Both sides previous revisionPrevious revisionNext revision | Previous revision |
cw4:4rpl_tools [2025/06/28 18:36] – [Next UseFul Function goes here] kalli | cw4:4rpl_tools [2025/08/29 21:04] (current) – [Pseudo RNG based on Linear congruential generator (LCG)] kalli |
---|
| |
| |
===== Pseudo Random Number Generator, type LCG ===== | ===== Pseudo Random Number Generator, based on sinus ===== |
| |
<hidden click here for source code> | <hidden click here for source code> |
| |
A basic function to request a pseudo random number, based on the Linear Congruential Generator algorithm: https://en.wikipedia.org/wiki/Linear_congruential_generator. | Several basic functions based on Grabz' rng lite function of "<-seed sinus 10000 mul dup floor sub". |
| |
| |
Input is an integer seed + the desired amount of digits. | 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. |
| |
| |
If you want no truncation, then use 0 for the desired amount of digits. If you request more digits than your seed would generate, then the too short result is returned without truncation. | Difference between the functions: |
| * "seeded": provide a seed to the generator. The generated number will always be the same for the same seed. |
| * "index": these seeds are sequenced. In the same map for the same index, the generated number 1,2,3,... will be the same after map restart. |
| * "spiked": the generator is seeded with the elapsedtime of the gaming session, making the outcome uncontrollable. |
| * No prefix: these are sequenced seeds and they use index 0. |
| |
<code 4rpl file name.4rpl> | Example code with messages: |
1111 3 @linearPRNG trace | <code example.4rpl> |
| 12345 ->int |
| 1 ->index |
| 0 ->first |
| 100 ->last # The last integer itself will be excluded. |
| "sinRandInt" <-first <-last 2 listN sendmsg |
| <-*sinRandInt trace |
| "sinRand01" 0 sendmsg |
| <-*sinRand01 trace |
| "indexSinRandInt" <-index <-first <-last 3 listN sendmsg |
| <-*sinRandInt trace |
| "indexSinRand01" <-index sendmsg |
| <-*sinRand01 trace |
| "spikedSinRandInt" <-first <-last 2 listN sendmsg |
| <-*sinRandInt trace |
| "spikedSinRand01" 0 sendmsg |
| <-*sinRand01 trace |
| "seededSinRandInt" <-seed <-first <-last 3 listN sendmsg |
| <-*sinRandInt trace |
| "seededSinRand01" <-seed sendmsg |
| <-*sinRand01 trace |
| </code> |
| |
:linearPRNG | ---- |
# INPUT: INTEGER SEED + INTEGER for desired digit length. 0 ->digits = no truncating of the output. | |
# OUTPUT: A pseudo random integer number of the desired length. | <code SinusRNG.4rpl> |
# https://en.wikipedia.org/wiki/Linear_congruential_generator | :once |
dup ->digits if | # Creating lists and a starting mapconstant for the seed sequences. |
16807 mul 2 31 pow 1 sub mod2 dup log10 ceil <-digits dup2 gt if sub 10 swap pow div asint else pop pop endif | createlist ->prevSeedList |
else | createlist ->seedCountList |
16807 mul 2 31 pow 1 sub mod2 | getmapsize 2 div swap 2 div swap dup2 getterrain 1 add dup 99999 floodfillterrain getlistcount ->mapConstant |
endif | |
| # Setting up the messages so that other scripts can request a random seed. |
| "sinRandInt" "sinRandInt_CALL" registerformsg |
| "sinRand01" "sinRand01_CALL" registerformsg |
| "indexSinRandInt" "indexSinRandInt_CALL" registerformsg |
| "indexSinRand01" "indexSinRand01_CALL" registerformsg |
| "spikedSinRandInt" "spikedSinRandInt_CALL" registerformsg |
| "spikedSinRand01" "spikedSinRand01_CALL" registerformsg |
| "seededSinRandInt" "seededSinRandInt_CALL" registerformsg |
| "seededSinRand01" "seededSinRand01_CALL" registerformsg |
| |
| :sinRandInt_CALL |
| <-_DATA listtostack @sinRandInt ->*sinRandInt |
| :sinRand01_CALL |
| @sinRand01 ->*sinRand01 |
| :indexSinRandInt_CALL |
| <-_DATA listtostack @indexSinRandInt ->*sinRandInt |
| :indexSinRand01_CALL |
| <-_DATA @indexSinRand01 ->*sinRand01 |
| :spikedSinRandInt_CALL |
| <-_DATA listtostack @spikedSinRandInt ->*sinRandInt |
| :spikedSinRand01_CALL |
| @spikedSinRand01 ->*sinRand01 |
| :seededSinRandInt_CALL |
| <-_DATA listtostack @seededSinRandInt ->*sinRandInt |
| :seededSinRand01_CALL |
| <-_DATA @seededSinRand01 ->*sinRand01 |
| |
| :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 |
| |
| :indexSinRandInt # INPUT: the index of the seed sequence + integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. |
| ->last |
| ->first |
| @indexSinRand01 <-last <-first sub mul <-first add asint |
| |
| :indexSinRand01 # INPUT: the "index" of the seed sequence. OUTPUT: the next seed from that sequence. |
| # The random numbers will be generated with the previous seed that is stored under that index. |
| ->i |
| <-prevSeedList[<-i] eq0 if |
| <-i <-mapConstant add 2357 mul @seededSinRand01 dup 10000000 mul 1 add asint ->prevSeedList[<-i] |
| 1 ->seedCountList[<-i] |
| else |
| <-prevSeedList[<-i] @seededSinRand01 dup 10000000 mul <-seedCountList[<-i] 1 add dup ->seedCountList[<-i] add asint ->prevSeedList[<-i] |
| endif |
| |
| :spikedSinRandInt # INPUT: integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. |
| ->last |
| ->first |
| elapsedtime asint @seededSinRand01 <-last <-first sub mul <-first add asint |
| |
| :spikedSinRand01 |
| elapsedtime asint @seededSinRand01 |
| |
| :seededSinRandInt # INPUT: integer seed + integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. |
| ->last |
| ->first |
| @seededSinRand01 <-last <-first sub mul <-first add asint |
| |
| :seededSinRand01 |
| # abs <-power pow <-add add sin <-sinMul mul dup floor sub |
| 1.05 pow 99419 sub sin 619 mul dup floor sub |
| </code> |
| |
| </hidden> |
| |
| ---- |
| |
| |
| ===== 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), but unsuited for generating random numbers larger than a few thousand. |
| |
| 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, missed only 8 possible results. The same test with @sinRandInt missed 42458 possible results. |
| |
| <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 |
| |
| :indexLcgRandInt # INPUT: the index of the seed sequence + integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. |
| ->last |
| ->first |
| @indexLcgRand01 <-last <-first sub mul <-first add asint |
| |
| :indexLcgRand01 # INPUT: the "index" of the seed sequence. OUTPUT: the next seed from that sequence. |
| # The random numbers will be generated with the previous seed that is stored under that index. |
| ->i |
| <-prevSeedList[<-i] eq0 if |
| <-i <-mapConstant add 13 mul @seededLcgRand01 dup 10000000 mul 1 add asint ->prevSeedList[<-i] |
| 1 ->seedCountList[<-i] |
| else |
| <-prevSeedList[<-i] @seededLcgRand01 dup 10000000 mul <-seedCountList[<-i] 1 add dup ->seedCountList[<-i] add asint ->prevSeedList[<-i] |
| endif |
| |
| :clearIndexSinLcgRand |
| <-seedCountList clearlist |
| <-prevSeedList clearlist |
| |
| :spikedLcgRandInt # INPUT: integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. |
| ->last |
| ->first |
| elapsedtime dup 10 log ceil 7 sub neg 10 swap pow mul asint <-spikedLcgAdd 1 add dup ->spikedLcgAdd mul @seededLcgRand01 <-last <-first sub mul <-first add asint |
| |
| :spikedLcgRand01 |
| elapsedtime dup 10 log ceil 7 sub neg 10 swap pow mul asint <-spikedLcgAdd 1 add dup ->spikedLcgAdd mul @seededLcgRand01 |
| |
| :seededLcgRandInt # INPUT: integer seed + integer first randInt + integer last randInt. OUTPUT: an integer in between the first and last randInt, excluding the last randInt. |
| ->last |
| ->first |
| @seededLcgRand01 <-last <-first sub mul <-first add asint |
| |
| :seededLcgRand01 |
| 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 ->prevSeedList |
| createlist ->seedCountList |
| getmapsize 2 div swap 2 div swap dup2 getterrain 1 add dup 99999 floodfillterrain getlistcount ->mapConstant |
| # <-SEED ->mapconstant # Alternative for map constant. |
</code> | </code> |
| |