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 -> | ||
| </ | </ | ||