Page 2 of 2
Posted: Sun Feb 07, 2010 7:13 am
A few helpers for lists. These were done one evening on IRC together with Taben (Betelgeuse). Much fun, if you are into that sort of thing

Code: Select all

``````;; reverse a list
(setq extListReverse (lambda (lst)
(block (result)
(enum lst el
(setq result (append el result))
)
)
))

;; Transform each element of a list by applying them
;; to a function
;;
;; @param lst: the list to map
;; @param fun: the function used to transform
;;             it must accept an element as param
;; @return: a new list of transformed values
(setq extListMap (lambda (lst fun)
(block (mapped)
(enum lst el
(setq mapped (append mapped (apply fun (list el))))
)
)
))

;; Reduce the list to one element by using a lambda
;;
;; @param lst: initial list
;; @param memo: initial value of memo
;; @param fun: the function used to reduce the list
;;             it must accept two params, el and memo
;; @return: the last value returned by the fun
(setq extListReduce (lambda (lst memo fun)
(enum lst el
(setq memo (apply fun (list el memo)))
)
))

;; Look through each value of a list and return the
;; first that passes truth test `fun`. Does not
;; loop further through the list after that
;;
;; @param lst: list to test
;; @param fun: the function used to test
;;             it must accept an element as param
;; @return: the first value that tests true
(setq extListDetect (lambda (lst fun)
(block (found)
(if (apply fun (list el)) (block Nil
(setq found True)
el
))
)
)
))

;; Insert an element into a list at a given index
;;
;; @param lst: list to insert into
;; @param el: element to insert
;; @param index: index to insert element at
;;
;; @return: new list with element inserted
(setq extListInsert (lambda (lst el index)
(if (gr index (count lst))
(lnkReplace Nil 1 1) ;; throw an error, ugh
(append (subset lst 0 index) el (subset lst index))
)
))

``````
A bit on extListMap:

Code: Select all

``````;; extListMap is handy when you want to apply the same
;; calculation to every element of a list.
;; eg. we can rewrite the extListToID function from a few posts
;; above like this
(setq extListToID (lambda (lst)
(extListMap lst (lambda (el) (objGetID el)))
))
``````
A bit on extListReduce

Code: Select all

``````;; extListReduce is used to reduce the content
;; of a list to a single value.
;; It is a bit tricky to use, so let me start with an example.

;; Say you have a list of strings, and what to know
;; the size of the all put together. Easy:

(extListReduce
(list "one" "two" "three")
0
(lambda (el memo) (add (count el) memo))
) -> 11

;; It may look like magic, but it is not so bad.
;; The lambda you define, takes two arguments,
;; el, which is the current element in this iteration,
;; and memo, which is the result returned
;; from the previous iteration.
;; Another, simpler example

(extListReduce (list 1 2 3) 0 (lambda (el mem) (add el mem))) -> 6

;;If you want a different initial values, here is another example

(extListReduce
(list "World" "of" "Transcendence")
"Hello"
(lambda (string memo) (cat memo " " string))
) -> "Hello World of Transcendence
``````

Posted: Fri Mar 05, 2010 2:38 am
A function that converts string representations of die rolls to actual die rolls

Code: Select all

``````(setq extStringToDieRoll (lambda (string)
(block (pos)
(switch
;; we actually got passed a number
(isInt string)
string
;; normal die roll, ie: 4d12 or 4d12+2
(setq pos (find string 'd))
(block (die sides bpos bonus)
(setq die (subset string 0 pos))
(setq sides (subset string (add pos 1)))
(if (or (setq bpos (find string "+")) (setq bpos (find string "-"))) (block Nil
(setq sides (subset string (add pos 1) (subtract bpos (add pos 1))))
(setq bonus (subset string bpos))
))
(rollDice (int die) (int sides) (int bonus))
)
;; range, ie: 1-4
(setq pos (find string '-))
(random (int (subset string 0 pos)) (int (subset string (add pos 1))))
;; fixnum?
(int string)
)
)
))
``````
It accepts strings like "2d6", "8d4+5", "2-10", "1"
"2d6" is two rolls of a six sided die
"8d4+5" is eight rolls of a four sided die with five added to the final result
"2-10" is a number between two and ten
"1" is just one

Don't pass it junk strings, as there is no error checking :)

Updated to handle "3d5-1"

Posted: Tue Jun 01, 2010 12:37 am
I am making a few functions that print game information, and will post them here:

The first one prints rarity and level distributions when using itmCreateRandom.
An example of how to call it would be:

Code: Select all

``````(extItmCreateRandomStats '("c" "ucu") "*" 100)
``````
And the output produced would look like this:

Code: Select all

``````[Iterations] 100 [Criteria] * [Level String] c

LEVEL: 1  TOTAL: 100

COMMON:   54
UNCOMMON: 39
RARE:     7
VERYRARE: 0

[Iterations] 100 [Criteria] * [Level String] ucr

LEVEL: 1  TOTAL: 32

COMMON:   15
UNCOMMON: 10
RARE:     7
VERYRARE: 0

LEVEL: 2  TOTAL: 53

COMMON:   26
UNCOMMON: 24
RARE:     3
VERYRARE: 0

LEVEL: 3  TOTAL: 15

COMMON:   6
UNCOMMON: 6
RARE:     3
VERYRARE: 0
``````
Here is the function itself:

Code: Select all

``````(setq extItmCreateRandomStats (lambda (frequencies criteria iterations)
(block Nil
(enum frequencies frequency (block ((data (list)))
(for i 1 iterations (block (itm lvl lvlData freq index value)
(setq itm (itmCreateRandom criteria frequency))
(setq lvl (itmGetLevel itm))
(if (not (setq lvlData (lookup data lvl 0)))
(lnkAppend data (setq lvlData (list lvl 0 0 0 0)))
)
(setq freq (itmGetFrequency itm))
(switch
(eq freq 20) (setq index 1)
(eq freq 10) (setq index 2)
(eq freq 4) (setq index 3)
(eq freq 1) (setq index 4)
)
(setq lvlData (lnkReplace lvlData index (add (item lvlData index) 1)))
(setq index (find data lvl 0))
(setq data (lnkReplace data index lvlData))
))
;; pretty print
(block (out (lvl 0) (cnt (count data)) (i 0))
(dbgLog "") (dbgOutput "")
(setq out (cat "[Iterations] " iterations " [Criteria] " criteria " [Level String] " frequency))
(dbgLog out) (dbgOutput out)
(loop (ls i cnt) (block (lvlData)
(if (setq lvlData (lookup data lvl 0))
(block Nil
(dbgLog "") (dbgOutput "")
(setq out (cat "LEVEL: " (item lvlData 0) "  TOTAL: " (add
(item lvlData 1) (item lvlData 2) (item lvlData 3) (item lvlData 4)
)))
(dbgLog out) (dbgOutput out)
(dbgLog "") (dbgOutput "")
(setq out (cat "  COMMON:   " (item lvlData 1)))
(dbgLog out) (dbgOutput out)
(setq out (cat "  UNCOMMON: " (item lvlData 2)))
(dbgLog out) (dbgOutput out)
(setq out (cat "  RARE:     " (item lvlData 3)))
(dbgLog out) (dbgOutput out)
(setq out (cat "  VERYRARE: " (item lvlData 4)))
(dbgLog out) (dbgOutput out)
(dbgLog "") (dbgOutput "")
)
)
))
)
))
)
))
``````

Posted: Tue Dec 07, 2010 6:14 am
Decimal Shifting for Floating Point Strings

Code: Select all

``````; This is a little function I cooked up for displaying all those values
; that are stored in smaller units than they're displayed in -- like
; maneuver and maxSpeed -- as if they were floating point values. Works
; out a division by 10^shift using string functions.
; -- Star Weaver, Dec 2010

(setq wvrDecShift (lambda (number shift) (block nil
; Make number a string off the bat or bad things happen
(setq number (cat number))

(for i (count number) (subtract shift 1)
(setq number (cat 0 number))
)

; Count back from the end of the string
(setq shift (subtract (count number) shift))

(cat (or (subset number 0 shift) "0") "." (or (subset number shift) "0"))
)))
``````
As an example, this is an excerpt from the main chunk of code I built this function for:

Code: Select all

``````(setq deviceMass 0) (setq armorMass 0) (setq cargoMass 0)
(objEnumItems theShip "*~aI" it
(setq deviceMass
(add deviceMass (multiply (itmGetMass it) (itmGetCount it)) )) )
(objEnumItems theShip "aI" it (...))
(objEnumItems theShip "*U" it (...))
(setq frameMass
(subtract (multiply 1000 (objGetMass theShip))
) )
.
.
(cat
; These values are stored as hundredth units, divide by 10^2
"Max Speed:" (wvrDecShift (shpGetDataField theShip "maxSpeed") 2) "c, "
"Maneuver:"  (wvrDecShift (shpGetDataField theShip "maneuver") 2) ", "
"TtW: "      (wvrDecShift (shpGetDataField theShip "thrustToWeight") 2)
"\n\n"

; To show KG in decimal tons (for consistancy), divide by 10^3
"Mass: " (objGetMass theShip) " tons; "
"Cargo: "      (wvrDecShift cargoMass 3)
" (of " (shpGetDataField theShip "cargoSpace") ")"
"\n"
"Devices: "    (wvrDecShift deviceMass 3) ", "
"Armor: "      (wvrDecShift armorMass 3)  ", "
; Normal divide here as that's how the value is stored
; plus finicky tweak due to truncating issues
"Spaceframe: " (add 1 (divide frameMass 1000))
.
.
``````

Re: General Helper functions

Posted: Wed Feb 22, 2012 1:53 am
I've been reminded of the existence of this thread so here's my current stock of helper functions (and a dockscreen and short example item to demonstrate the use of some of them.)

These functions swap weapons, armor, and shields. As currently written, they must be called where gSource is defines (eg in an invoke, ship event, or device event)

Code: Select all

``````		; Define the function for switching weapon.
(setq switchweapon (lambda (theweapon)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhweapon theitem)
(setq theitem gItem)
(setq status (ItmIsEnhanced theitem))
(setq enhweapon (itmSetEnhanced theweapon status))
; even worse kludge to handle launchers
(if(itmMatches theitem "l")
(block (nil)
(objEnumItems gSource "Il" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))

(shpInstallDevice gSource enhweapon)

; kludge for new item weirdness
(shpEnhanceItem gSource theitem status)

; Remove previous weapon
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)

))

; Define the function for switching shields.
(setq switchshield (lambda (theshield)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhshield theitem shieldHP)
(setq theitem gItem)
(setq shieldHP (subtract (shpGetShieldMaxHitPoints gSource) (shpGetShieldDamage gSource)))
(setq status (ItmIsEnhanced theitem))
(setq enhshield (itmSetEnhanced theshield status))

; shields require the same kludge as launchers
(if(itmMatches theitem "s")
(block (nil)
(objEnumItems gSource "Is" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))

; Install shield and set HP
(shpInstallDevice gSource enhshield)
(shpRechargeShield gSource shieldHP)

; Remove previous shield
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)

))

; Define the function for switching armor
(setq switcharmor (lambda (thearmor)
(block (oldinventory newinventory count)
(setq count 0)
; list the inventory
(setq oldinventory (objGetItems gsource "*U"))
(setq armorcount (shpGetArmorCount gsource))
(for armorposition 0 (subtract armorcount 1)
(block (armoriterator)
(setq armoriterator (shpGetArmor gsource armorposition))
(if (eq (itmGetUnid gitem) (itmGetUnid armoriterator))
(block (status enharmor armordamage armorhp)
; get HP
(setq armorHP (subtract (shpGetArmorMaxHitPoints gSource armorposition) (objGetArmorDamage gSource armorposition)))
; store enhancements
(setq status (itmIsEnhanced armoriterator))
; enhance the new armor
(setq enharmor (itmSetEnhanced thearmor status))
; install the armor
(shpInstallArmor gsource enharmor armorposition)
; damage the new armor to 0 HP
(shpDamageArmor gsource armorposition 15 (multiply (shpGetArmorMaxHitPoints gsource armorposition) 10))
; repair the armor
(objRepairArmor gsource armorposition armorHP)
))
))

; get the inventory again
(setq newinventory (objGetItems gsource "*U"))
; remove anything new.  May remove items that match the old armor, but I don't see a way to avoid that.  It shouldn't come up much anyways.
(enum newinventory outeriterator
(block (isitold)
(setq isitold nil)
(enum oldinventory inneriterator
(if (eq inneriterator outeriterator) (setq isitold TRUE))
)
(if (not isitold) (objRemoveItem gSource outeriterator count))
))
)

))``````
This pair of functions is for trig. AngleNormalize is going to be deprecated in 1.08 because of improvements to the builtin modulo function.

Code: Select all

``````		; Define the function for switching weapon.  (NB-- This function is an update to 0.99c of a function of Digdug's, renamed to avoid conflicts.)
(setq nwfswitchweapon (lambda (theweapon)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhweapon theitem)
(setq theitem gItem)
(setq status (ItmIsEnhanced theitem))
(setq enhweapon (itmSetEnhanced theweapon status))
; even worse kludge to handle launchers
(if(itmMatches theitem "l")
(block (nil)
(objEnumItems gSource "Il" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))

(shpInstallDevice gSource enhweapon)

; kludge for new item weirdness
(shpEnhanceItem gSource theitem status)

; Remove previous weapon
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)

))

; Define the function for switching shields.
(setq nwfswitchshield (lambda (theshield)
(block (nil)
(if (eq (itmIsDamaged theitem) Nil)
(block (status enhshield theitem shieldHP)
(setq theitem gItem)
(setq shieldHP (subtract (shpGetShieldMaxHitPoints gSource) (shpGetShieldDamage gSource)))
(setq status (ItmIsEnhanced theitem))
(setq enhshield (itmSetEnhanced theshield status))

; shields require the same kludge as launchers
(if(itmMatches theitem "s")
(block (nil)
(objEnumItems gSource "Is" variable
(block (nil)
(shpEnhanceItem gSource theitem status)
))
))

; Install shield and set HP
(shpInstallDevice gSource enhshield)
(shpRechargeShield gSource shieldHP)

; Remove previous shield
(shpRemoveDevice gSource (itmSetEnhanced theitem status))
(objRemoveItem gSource (itmSetEnhanced (itmCreate (itmGetUNID theitem) 1) status) 1)
)
(objSendMessage gSource Nil "Device non-functional: Error!")
)
)

))

; Define the function for switching armor
(setq nwfswitcharmor (lambda (thearmor)
(block (oldinventory newinventory count)
(setq count 0)
; list the inventory
(setq oldinventory (objGetItems gsource "*U"))
(setq armorcount (shpGetArmorCount gsource))
(for armorposition 0 (subtract armorcount 1)
(block (armoriterator)
(setq armoriterator (shpGetArmor gsource armorposition))
(if (eq (itmGetUnid gitem) (itmGetUnid armoriterator))
(block (status enharmor armordamage armorhp)
; get HP
(setq armorHP (subtract (shpGetArmorMaxHitPoints gSource armorposition) (objGetArmorDamage gSource armorposition)))
; store enhancements
(setq status (itmIsEnhanced armoriterator))
; enhance the new armor
(setq enharmor (itmSetEnhanced thearmor status))
; install the armor
(shpInstallArmor gsource enharmor armorposition)
; damage the new armor to 0 HP
(shpDamageArmor gsource armorposition 15 (multiply (shpGetArmorMaxHitPoints gsource armorposition) 10))
; repair the armor
(objRepairArmor gsource armorposition armorHP)
))
))

; get the inventory again
(setq newinventory (objGetItems gsource "*U"))
; remove anything new.  May remove items that match the old armor, but I don't see a way to avoid that.  It shouldn't come up much anyways.
(enum newinventory outeriterator
(block (isitold)
(setq isitold nil)
(enum oldinventory inneriterator
(if (eq inneriterator outeriterator) (setq isitold TRUE))
)
(if (not isitold) (objRemoveItem gSource outeriterator count))
))
)

))``````
This function is a kludge for real number division. The average return is the real number quotient of two integers but each individual return is an integer, either rounded up or down at random. It is useful where a calculation will be made many times such as in a short timer and regular integer division produces unacceptable bias.

Code: Select all

``````(setq probabilisticDivide (lambda (dividend divisor)
(divide dividend divisor)
(if (leq (modulo dividend divisor) (random 1 divisor))
0
1
)
)
))``````
These functions are the heart of the virtual ammo system for making non-launcher weapons that support multiple ammo types or weapons that are intended to get more than one shot per item of ammo.

Code: Select all

``````(setq VirtualAmmoUpdate (lambda nil
(block (ammolist realshots shots virtualUNID)
(setq ammolist (itmGetData gItem "ammolist"))
(setq virtualUnid (typGetDataField gItem "ammoType"))

(setq realshots 0)
; count ammo
(objEnumItems gSource "m" ammo
(if (find ammolist (itmGetType ammo))
(setq realshots (add realshots (itmGetCount ammo)))
)
)
; count shots
(objEnumItems gSource "*V" theItem
(if (eq (itmGetType theItem) virtualUNID)
(setq shots (itmGetCount theItem))
)
)
(switch
(gr realshots shots)
(objAddItem gSource (itmCreate virtualUNID (subtract realshots shots)))
(ls realshots shots)
(objRemoveItem gSource (itmCreate virtualUNID (subtract shots realshots)))
)
)
))

(setq fireUnlauncher (lambda nil
(block (ammolist launcher shot speed munition)
(setq ammolist (itmGetData gItem "ammolist"))

(setq launcher (typGetStaticData (itmGetUnid gItem) 'VirtualLauncher))

; determine ammo type
(enum ammolist ammo
(if (not munition)
(if (objHasItem gSource (itmCreate ammo 1))
(setq munition ammo)
)
)
)

(if munition
(block nil
(setq speed (typGetDataField munition "speed"))
(if (objRemoveItem gSource (itmCreate munition 1))
(block nil
(setq shot (sysCreateWeaponFire munition gSource aFirePos aFireAngle speed aTargetObj nil aWeaponBonus))
(objIncVel shot (objGetVel gSource))
)
)
)
;; else don't fire!
)
true
)
))

(setq initializeUnlauncher (lambda nil
(block (launcher ammonumber ammolist)
; construct ammo list
(setq launcher (typGetStaticData (itmGetType gItem) 'VirtualLauncher))
(setq ammonumber (typGetDataField launcher 'variantCount))
(setq ammolist (list))
(for i 0 ammonumber
(setq ammolist (append (typGetDataField launcher (cat "ammoType:" i)) ammolist))
)
; store ammo list
(objSetItemData gSource gItem "ammolist" ammolist)
)
))``````
These support the virtual ammo system ammo priority selection dockscreen.

Code: Select all

``````    (setq reorderAction (lambda (direction)
(block (ammolist ammo index)
(setq ammolist (itmGetData weItem "ammolist"))
(setq ammo (item (scrGetListEntry gScreen) 3))
;; save for keeping out position in the list
(setq weItem (objSetItemData weSource weItem "current-ammo" ammo))

(setq index (find ammolist ammo))
(lnkRemove ammolist index)
(setq ammolist (append (subset ammolist 0 index) (list ammo) (subset ammolist index)))

;; update ammolist
(setq weItem (objSetItemData weSource weItem "ammolist" ammolist))
)
))

;when the screen is refreshed between switching ammo this is evaluated for each item
;it will keep the cursor on the ammo type that it was on before changing the list order
(setq dsInitialAmmo (lambda nil
(block (entry ammo)
(setq entry (scrGetListEntry gScreen))
(setq ammo (itmGetData weItem "current-ammo"))
(or (not ammo) (eq (item entry 3) ammo))
)
))``````
And this is that dockscreen.

Code: Select all

``````  <DockScreen UNID="&WE_dsAmmoPriority;"
name=             "ammo list"
type=             "customPicker"
backgroundID=     "&rsItemListScreen;"
>
<List   initialItem="=(dsInitialAmmo)">
(block (ammolist (custom (list)))
(setq ammolist (itmGetData weItem "ammolist"))

(enum ammolist ammo
(block (itm)
(setq itm (itmCreate ammo 1))
(lnkAppend custom (list
(itmGetName itm 4)
(itmGetImageDesc itm)
"item description (perhaps an amount here as well?)"
ammo
))
)
)
custom
)
</List>
<Panes>
<Default>
<OnPaneInit>
(block (ammolist index length)
(setq ammolist (itmGetData weItem "ammolist"))
(setq length (count ammolist))
(setq index (find ammolist (item (scrGetListEntry gScreen) 3)))

(if (eq length 1) (block nil
(scrEnableAction gScreen 0 nil)
(scrEnableAction gScreen 1 nil)
))

(if (eq index 0) (scrEnableAction gScreen 0 nil))
(if (eq (add index 1) length) (scrEnableAction gScreen 1 nil))

(scrSetDesc gScreen "Choose the priority of your ammo")
)
</OnPaneInit>
<Actions>
<Action name="Higher" key="H">
(block nil
(reorderAction -1)

;refresh the screen so any changes are immediately visible
(scrShowScreen gScreen &WE_dsAmmoPriority;)
)
</Action>

<Action name="Lower" key="L">
(block nil
(reorderAction 1)

;refresh the screen so any changes are immediately visible
(scrShowScreen gScreen &WE_dsAmmoPriority;)
)
</Action>

<Action name="Done" key="d" cancel="1">
(block (uid ammolist)
;; clear our globals vars
(objSetItemData weSource weItem "current-ammo" nil)
(setq weSource nil)
(setq weItem nil)

(scrExitScreen gScreen)
)
</Action>
</Actions>
</Default>
</Panes>
</DockScreen>``````
And this is an example of the weapon looks like. The virtual launcher referenced in staticdata would normally be a virtual item. The ammotype is, of course, a virtual item with no need of any attributes except a name, a level and 'virtual="true"'.

Code: Select all

``````  <ItemType UNID="&itNAMIMissileLauncher2;"
name=       "NAMI missile unlauncher"
level=        "3"
value=        "1000"
mass=       "1000"
frequency=      "common"
attributes=     "MajorItem; NAMI"

description=    "This launcher is compatible with a full range of popular missiles including the KM100 Longbows and the XM900 Lucifers."
>

<Image imageID="&rsItemsNAMI2;" imageX="0" imageY="0" imageWidth="96" imageHeight="96"/>

<Weapon
fireRate=     "30"
powerUse=     "10"
>
<Missiles>
<Missile ammoID="&itNAMIVirtualAmmo;"
type=     "missile"

damage=     "kinetic:0"
missileSpeed= "0"

>
</Missile>
</Missiles>
</Weapon>

<StaticData>
<VirtualLauncher>
&itNAMIMissileLauncher;
</VirtualLauncher>
</StaticData>

<Invoke installedOnly="true">
(block nil
; save the source and the item
(setq weSource gSource)
(setq weItem gItem)
(scrShowScreen gScreen "&dsAmmoPriority;")
)
</Invoke>
<Events>
<OnInstall>
(WE_initializeUnlauncher)
<OnInstall>
<OnFireWeapon>
(WE_fireUnlauncher)
</OnFireWeapon>
<OnUpdate>
(WE_VirtualAmmoUpdate)
</OnUpdate>
<Events>
</ItemType>``````

Re: General Helper functions

Posted: Mon Nov 26, 2012 7:57 pm
Name vcmRFTrim (change it for your personal use)
Syntax (vcmRFTrim list number) -> list
Argument List list: any list
number: the number of list elements you want to extract from the input list (must be less than (count list))
Returns list: a list made by the defined number of elements picked at random from the input list.
Category list manipulation
Description Returns a sublist made by randomly picking elements from the input list.

Code: Select all

``````(setq vcmRFTrim (lambda (theList finalItemsNumber)
(block (trimmedList)

(for a 1 finalItemsNumber
(block (randomItem randomNumber)
(setq randomNumber (random 0 (subtract (count theList) 1)))
(setq randomItem (item theList randomNumber))
(setq trimmedList (lnkappend trimmedList randomItem))
(setq theList (lnkRemove theList randomNumber))
)
)
trimmedList
)))``````
Examples:
(vcmRFTrim '(1 2 3 4 5) 2) --> '(2 5)
(vcmRFTrim '(1 2 3 4 5) 2) --> '(4 1)
(vcmRFTrim '(1 2 3 4 5) 2) --> '(3 2)

After working on it for 2 hours, I just realised that I can do the same by doing:
(subset (shuffle '(1 2 3 4 5)) 0 2) --> '(5 2)
LOL
Still, I think I learned in the process.