This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
cw4:4rpl_tools [2025/06/23 18:45] – [Request Viable Move-Cell] kalli | cw4:4rpl_tools [2025/08/29 21:04] (current) – [Pseudo RNG based on Linear congruential generator (LCG)] kalli | ||
---|---|---|---|
Line 3551: | Line 3551: | ||
- | ===== Request Viable Move-Cell ===== | + | ===== Request Viable |
<hidden click here for source code> | <hidden click here for source code> | ||
- | This script allows for other scripts to SEND A MESSAGE TO REQUEST A VIABLE MOVE-CELL. It can also be used to request a viable build cell, but with limitations: | + | This script allows for other scripts to SEND A MESSAGE TO REQUEST A VIABLE |
- | + | The script is a compromise between high performance and high fidelity. The reason that it's setup to run as a separate script and not as a function, is so that it can take move/build requests from multiple sources into account: | |
- | The script is a compromise between high performance and high fidelity. The reason that it's setup to run as a separate script and not as a function, is so that it can take move/build requests from multiple sources into account: it won't recheck cells that were recently rejected + new searches will continue where recent previous queries stopped after finding a solution. A very high performance version exists for 3x3 units in my SQUADS map. | + | |
Line 3565: | Line 3564: | ||
- | If the units are special custom units (build in void/ | + | Use example for building miners: |
- | INPUT: | + | <code 4rpl example.4rpl>500 0 do |
- | <code 4rpl MultiObjective.4rpl>" | + | " |
- | OUTPUT: 2 global variables, see example: | + | <-*WxWviableCellFound if |
- | <code 4rpl MultiObjective.4rpl> | + | " |
- | < | + | # Unlike existing units that move, newly build units do not instantly occupy the cells that they were created on, so we force a refresh of that with SetUnitOccupiesLand. |
- | endif</ | + | endif |
+ | loop | ||
+ | |||
+ | 500 0 do | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | endif | ||
+ | loop</ | ||
INPUT variables explained: | INPUT variables explained: | ||
- | <-UID: The UID of the unit. Can be left 0, needs a UID to determine the unit type. | + | <-moveUID: can be left 0, but it's needed if you want to allow a unit to check if it's current location is viable. |
+ | |||
+ | < | ||
+ | |||
+ | < | ||
< | < | ||
- | < | + | < |
< | < | ||
Line 3587: | Line 3598: | ||
< | < | ||
- | <-unitWidth: the footprint witdth=length of the unit. This assumes square units. | + | <-likePlatform: the unit can be placed in the void. |
- | <-ignoreCooldown: set to 1 to ignore | + | <-likeBeacon: the unit can be placed in the void and on uneven terrain. |
+ | < | ||
- | <code 4rpl MultiObjective.4rpl> | + | <-likeNullifier: |
- | # --WxW-FindViableCell-NRP-Pre-- | + | |
- | # INPUTS: " | + | < |
+ | |||
+ | < | ||
+ | |||
+ | < | ||
+ | |||
+ | |||
+ | |||
+ | <code 4rpl LxW-FindViableCell-NRP-Pre.4rpl> | ||
+ | # --LxW-FindViableCell-NRP-Pre-- | ||
+ | |||
+ | # INPUTS: " | ||
# OUTPUT: global varialbles named < | # OUTPUT: global varialbles named < | ||
Line 3601: | Line 3623: | ||
# BASED ON SNAPPING TOOL CODE, but without the Indicator unit or the Mouse commands, and only for SQUARE units. | # BASED ON SNAPPING TOOL CODE, but without the Indicator unit or the Mouse commands, and only for SQUARE units. | ||
- | $radiusLandingSpot: | + | $radiusLandingSpot: |
- | $cellsWithinLandingRange: | + | $cellsWithinLandingRange: |
+ | |||
+ | $$defaultSearchRange: | ||
- | $$defaultSearchRange:20 # If no searchRange is send in the message, this is the range that will be used. | + | $groupDist:7 # Integer or float distance. Requests that are closer together than this distance, will be grouped together, so that later searches can continue on from previous searches. |
+ | $blockCellTimer:90 # INTEGER. Amount of frames under which queries will be grouped together & searches around cells that were found to be blocked, will not be done again. | ||
+ | $clearCellTimer: | ||
- | $$contSlot: | + | $$contSlot: |
- | $$resoSlot: | + | $$resoSlot: |
- | $$meshSlot: | + | $$meshSlot: |
- | $$unitBuildTypes:" | ||
- | # $$footprintWidth:" | ||
- | # $$footprintLength:" | ||
- | $$voidUnitTypes:" | ||
- | $$anywhereTypes:" | ||
- | $$resourceTypes:" | ||
- | $$nullifyTypes:" | ||
$$nullifyUnits:" | $$nullifyUnits:" | ||
: | : | ||
- | < | + | < |
- | <-_DATA[2] dup not if pop < | + | < |
- | < | + | |
- | < | + | |
< | < | ||
Line 3629: | Line 3646: | ||
# listening channels: | # listening channels: | ||
" | " | ||
- | 1 ->resetSnapIndex | + | 1 ->resetSearch |
+ | 1 -> | ||
- | # When the loop was ran through fully without finding a resolution, remember that cell for 150 frames before trying the same cell again. | + | # When the loop was ran through fully without finding a resolution, remember that cell for 90 frames before trying the same cell again. |
list -> | list -> | ||
list -> | list -> | ||
list -> | list -> | ||
- | # the ONCE code from the snapping tool script | ||
- | # < | ||
- | # < | ||
- | # < | ||
- | # < | ||
- | # < | ||
- | < | ||
- | < | ||
- | < | ||
- | < | ||
< | < | ||
- | # unitType and prevType must be different at the start of the map. | ||
- | < | ||
- | # < | ||
: | : | ||
# See vanilla snapping tool cpack for full commments. | # See vanilla snapping tool cpack for full commments. | ||
- | ->width | + | ->resetSearch |
+ | -> | ||
+ | -> | ||
+ | -> | ||
+ | -> | ||
+ | -> | ||
+ | ->placeV | ||
->noMesh | ->noMesh | ||
-> | -> | ||
+ | -> | ||
->cell | ->cell | ||
- | ->unitType | + | ->width |
- | + | ->length | |
- | <-unitType | + | pop # The UID has no further use. |
- | # <-buildList | + | |
- | # <-widthList[<-typeIndex] | + | # The following switch checks if the search should start from scratch (=1), or if it should continue (=0) on from a previous search. |
- | # <-lengthList[<-typeIndex] | + | switch |
- | <-resourceList | + | <-resetSearch case 1 endcase |
- | <-nullifierList | + | <-length < |
+ | <-width <-prevWidth neq case 1 endcase | ||
+ | <-checkN < | ||
+ | <-checkR | ||
+ | <-placeA < | ||
+ | <-placeV | ||
+ | <-cell ev2 < | ||
+ | < | ||
+ | <-ignoreCooldown if getgametickcount else getgameupdatecount endif dup <-prevSearchUpdateCount gt if | ||
+ | -> | ||
+ | 1 | ||
+ | else | ||
+ | pop | ||
+ | 0 | ||
+ | endif | ||
+ | endif | ||
+ | endswitch if # If the search starts from scratch, then the new settings have to be remembered for the next function call. | ||
+ | # Adjust the footprint array depending on the requested width in the function call. | ||
+ | < | ||
+ | dup | ||
+ | 1 add 2.0 div asint dup ->rHz 1 sub ->rLz # For the footprint loop. | ||
+ | else pop endif | ||
+ | <-length dup <-prevLength neq if | ||
+ | dup -> | ||
+ | 1 add 2.0 div asint dup ->rHx 1 sub ->rLx # For the footprint loop. | ||
+ | else pop endif | ||
- | <-anywhereList | + | <-checkN -> |
- | 1 ->resetSnapIndex | + | <-checkR -> |
- | 1 else 0 endif ->placeA | + | < |
+ | < | ||
+ | <-cell -> | ||
- | <-voidList | + | # Recreate the area to search: |
- | 1 ->resetSnapIndex | + | <-searchRange 1 sub dup dup mul swap add 2 mul 1 add -> |
- | 1 else 0 endif ->placeV | + | <-cell ev2 0 21 < |
+ | 0 ->startLoop | ||
+ | 0 ->skip | ||
- | # 0 -> | + | # If the search is being reset, then clear the blocked lists. |
- | endif | + | <-resetSearch If |
- | <-unitType | + | 1 ->ignoreCooldown |
- | + | <-blockedCellV2List clearlist | |
- | <-cell ev2 <-prevSearchCell ev2 distancecell 6 gt if | + | <-blockedCellTimeList clearlist |
- | <-cell -> | + | <-nearbyUnitsCountList clearlist |
- | 1 -> | + | endif |
else | else | ||
- | < | + | # If the search continues on from before, but the search range was changed, then the potential cells will need to be adjusted, without adjusting the start of the loop. |
- | getgameupdatecount | + | < |
- | ->prevSearchUpdateCount | + | dup -> |
- | 1 ->resetSnapIndex | + | <-cell ev2 0 21 < |
- | else pop endif | + | else pop endif |
endif | endif | ||
- | # If the new searchRange is larger than the old one, then do not check if the cell was blocked. | + | # If the new searchRange is larger than the old one, then do not check if the cell was recently found to be blocked. |
- | < | + | < |
# Check if the cell is not on the list of blocked cells. | # Check if the cell is not on the list of blocked cells. | ||
- | < | + | < |
-> | -> | ||
Line 3698: | Line 3739: | ||
# Try to purge a few old blocked cells from the lists, otherwise the list bloats. | # Try to purge a few old blocked cells from the lists, otherwise the list bloats. | ||
-1 <-index 1 sub do | -1 <-index 1 sub do | ||
- | < | + | < |
< | < | ||
< | < | ||
Line 3708: | Line 3749: | ||
switch | switch | ||
# The last check on this spot was less than 3 seconds ago, so we're not checking it again now. | # The last check on this spot was less than 3 seconds ago, so we're not checking it again now. | ||
- | < | + | < |
-1 -1 v2 false return | -1 -1 v2 false return | ||
endcase | endcase | ||
# The last check on this spot was longer than 9 seconds ago, so we can check it again now. | # The last check on this spot was longer than 9 seconds ago, so we can check it again now. | ||
- | < | + | < |
< | < | ||
< | < | ||
< | < | ||
endcase | endcase | ||
- | # Check the cell again if there are now less enemies | + | # Check the cell again if there are now less units nearby than when it was blocked. |
< | < | ||
< | < | ||
Line 3726: | Line 3767: | ||
-1 -1 v2 false return | -1 -1 v2 false return | ||
endswitch | endswitch | ||
- | else pop endif | + | endif |
- | endif | + | |
- | + | ||
- | < | + | |
- | < | + | |
- | <-cell ev2 0 21 < | + | |
- | 0 -> | + | |
- | 0 -> | + | |
- | else | + | |
- | # If the search range was changed, then the potentialcells will need to be adjusted, without adjusting the start of the loop. | + | |
- | < | + | |
- | dup -> | + | |
- | < | + | |
- | else pop endif | + | |
endif | endif | ||
Line 3748: | Line 3776: | ||
< | < | ||
@checkFootPrint if | @checkFootPrint if | ||
- | i < | + | i |
< | < | ||
endif | endif | ||
else | else | ||
- | < | + | <-noSkip if 0 else <-width 3 div asint 1 add endif ->skip # If the center cell was blocked, then also skip the next X potential cells. |
endif | endif | ||
endif | endif | ||
Line 3782: | Line 3810: | ||
< | < | ||
->cZ ->cX # Used in checkFootPrint. | ->cZ ->cX # Used in checkFootPrint. | ||
- | < | + | < |
+ | < | ||
+ | else | ||
+ | pop pop false return | ||
+ | endif endif | ||
+ | < | ||
+ | " | ||
+ | getunitcell <-cX <-cZ distancecell 2.828427 approximately not if pop pop false return endif | ||
+ | endif | ||
+ | < | ||
+ | < | ||
+ | endif | ||
endswitch | endswitch | ||
true | true | ||
- | # Adjust the footprint array depending on the requested width in the function call. | ||
- | <-width dup < | ||
- | dup -> | ||
- | 1 add 2.0 div asint dup ->rH 1 sub ->rL | ||
- | else pop endif | ||
: | : | ||
# See vanilla snapping tool cpack for full commments. | # See vanilla snapping tool cpack for full commments. | ||
- | <-cZ <-rH add <-cZ <-rL sub do | + | <-cZ <-rHz add <-cZ <-rLz sub do |
- | <-cX <-rH add <-cX <-rL sub do | + | <-cX <-rHx add <-cX <-rLx sub do |
switch | switch | ||
i j | i j | ||
Line 3809: | Line 3843: | ||
< | < | ||
pop pop | pop pop | ||
+ | < | ||
endswitch | endswitch | ||
- | < | ||
loop | loop | ||
loop | loop | ||
- | <-checkN if < | + | < |
- | <-checkR if < | + | |
- | # <-checkO if | + | |
- | # " | + | |
- | # getunitcell <-cX <-cZ distancecell 2.828427 approximately not if false return endif | + | |
- | # endif | + | |
true | true | ||
+ | |||
+ | </ | ||
+ | |||
+ | </ | ||
+ | |||
+ | ---- | ||
+ | |||
+ | |||
+ | ===== Pseudo Random Number Generator, based on sinus ===== | ||
+ | |||
+ | <hidden click here for source code> | ||
+ | |||
+ | Several basic functions based on Grabz' rng lite function of "< | ||
+ | |||
+ | |||
+ | Copy the functions directly into your script or run the below script in your cpack and have other scripts send messages to request a randfloat or randint. | ||
+ | |||
+ | |||
+ | Difference between the functions: | ||
+ | * " | ||
+ | * " | ||
+ | * " | ||
+ | * No prefix: these are sequenced seeds and they use index 0. | ||
+ | |||
+ | Example code with messages: | ||
+ | <code example.4rpl> | ||
+ | 12345 ->int | ||
+ | 1 ->index | ||
+ | 0 ->first | ||
+ | 100 ->last # The last integer itself will be excluded. | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | < | ||
+ | " | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | ---- | ||
+ | |||
+ | <code SinusRNG.4rpl> | ||
+ | :once | ||
+ | # Creating lists and a starting mapconstant for the seed sequences. | ||
+ | createlist -> | ||
+ | createlist -> | ||
+ | getmapsize 2 div swap 2 div swap dup2 getterrain 1 add dup 99999 floodfillterrain getlistcount -> | ||
+ | |||
+ | # Setting up the messages so that other scripts can request a random seed. | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | |||
+ | : | ||
+ | <-_DATA listtostack @sinRandInt -> | ||
+ | : | ||
+ | @sinRand01 -> | ||
+ | : | ||
+ | <-_DATA listtostack @indexSinRandInt -> | ||
+ | : | ||
+ | <-_DATA @indexSinRand01 -> | ||
+ | : | ||
+ | <-_DATA listtostack @spikedSinRandInt -> | ||
+ | : | ||
+ | @spikedSinRand01 -> | ||
+ | : | ||
+ | <-_DATA listtostack @seededSinRandInt -> | ||
+ | : | ||
+ | <-_DATA @seededSinRand01 -> | ||
+ | |||
+ | :sinRandInt # INPUT: integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. | ||
+ | ->last | ||
+ | ->first | ||
+ | 0 @indexSinRand01 <-last <-first sub mul <-first add asint | ||
+ | |||
+ | :sinRand01 # INPUT: none. | ||
+ | 0 @indexSinRand01 | ||
+ | |||
+ | : | ||
+ | ->last | ||
+ | ->first | ||
+ | @indexSinRand01 <-last <-first sub mul <-first add asint | ||
+ | |||
+ | : | ||
+ | # The random numbers will be generated with the previous seed that is stored under that index. | ||
+ | ->i | ||
+ | < | ||
+ | <-i < | ||
+ | 1 -> | ||
+ | else | ||
+ | < | ||
+ | endif | ||
+ | |||
+ | : | ||
+ | ->last | ||
+ | ->first | ||
+ | elapsedtime asint @seededSinRand01 <-last <-first sub mul <-first add asint | ||
+ | |||
+ | : | ||
+ | elapsedtime asint @seededSinRand01 | ||
+ | |||
+ | : | ||
+ | ->last | ||
+ | ->first | ||
+ | @seededSinRand01 <-last <-first sub mul <-first add asint | ||
+ | |||
+ | : | ||
+ | # abs <-power pow <-add add sin <-sinMul mul dup floor sub | ||
+ | 1.05 pow 99419 sub sin 619 mul dup floor sub | ||
+ | </ | ||
+ | |||
+ | </ | ||
+ | |||
+ | ---- | ||
+ | |||
+ | |||
+ | ===== Pseudo RNG based on Linear congruential generator (LCG) ===== | ||
+ | |||
+ | The downside of the above sinus based functions, is that they have few resulting digits. They are more than adequate to pick a random cell on a map (max 512 possibilities), | ||
+ | |||
+ | The below LCG functions should work for up to 6 digits = 10^6, but I've only done a test up to 10^5. Rolling 1 million random integers between 0 and 10^5 with @lcgRandInt, | ||
+ | |||
+ | <hidden click here for source code> | ||
+ | |||
+ | <code LinearCongruentialGenerator.4rpl> | ||
+ | # Quick Example. | ||
+ | 0 10000 @lcgRandInt | ||
+ | |||
+ | :lcgRandInt # INPUT: integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. | ||
+ | ->last | ||
+ | ->first | ||
+ | 0 @indexLcgRand01 <-last <-first sub mul <-first add asint | ||
+ | |||
+ | :lcgRand01 # INPUT: none. | ||
+ | 0 @indexLcgRand01 | ||
+ | |||
+ | : | ||
+ | ->last | ||
+ | ->first | ||
+ | @indexLcgRand01 <-last <-first sub mul <-first add asint | ||
+ | |||
+ | : | ||
+ | # The random numbers will be generated with the previous seed that is stored under that index. | ||
+ | ->i | ||
+ | < | ||
+ | <-i < | ||
+ | 1 -> | ||
+ | else | ||
+ | < | ||
+ | endif | ||
+ | |||
+ | : | ||
+ | < | ||
+ | < | ||
+ | |||
+ | : | ||
+ | ->last | ||
+ | ->first | ||
+ | elapsedtime dup 10 log ceil 7 sub neg 10 swap pow mul asint < | ||
+ | |||
+ | : | ||
+ | elapsedtime dup 10 log ceil 7 sub neg 10 swap pow mul asint < | ||
+ | |||
+ | : | ||
+ | ->last | ||
+ | ->first | ||
+ | @seededLcgRand01 <-last <-first sub mul <-first add asint | ||
+ | |||
+ | : | ||
+ | asint 1103515245 mul 12345 add abs asfloat 2147483647 div 10 mul dup floor sub | ||
+ | :once | ||
+ | # Creating lists and a starting mapconstant for the seed sequences. | ||
+ | createlist -> | ||
+ | createlist -> | ||
+ | getmapsize 2 div swap 2 div swap dup2 getterrain 1 add dup 99999 floodfillterrain getlistcount -> | ||
+ | # <-SEED -> | ||
</ | </ | ||