Predicting the randomness in Wonderland (Theory)

Discuss the games (no level solutions or off-topic, please).

Moderators: ~xpr'd~, tyteen4a03, Stinky, Emerald141, Qloof234, jdl

Post Reply
User avatar
nasko222
Rainbow Master
Posts: 591
Joined: Sat Jul 07, 2018 1:22 pm

Predicting the randomness in Wonderland (Theory)

Post by nasko222 » Sat Jun 24, 2023 9:10 am

Recently I've been tweaking around with the Blitz3D random and I found out a possible way to predict the randomization in Wonderland

The Blitz3D randomness is very predictable due to the randomness seed being set to a static number by default and it always yields the same results. After the randomness method has been executed, the random seed shifts to a new, again predictable random seed.

Code: Select all

a = Rand(10) ; integer
b = Rand(20) ; integer
c# = Rnd(50) ; float
Print(a) ; 4
Print(b) ; 15
Print(c#) ; 34.1473
Delay 1000
As long as this snippet of code is executed before any other random functions it will always yield the same numbers. Changing any of the parameters won't affect the randomness of the rest of them.

Code: Select all

a = Rand(999) ; integer (This one got changed)
; these stay the same
b = Rand(20) ; integer
c# = Rnd(50) ; float
Print(a) ; 356
; these one are unchanged
Print(b) ; 15
Print(c#) ; 34.1473
Delay 1000

There are two random functions in Blitz3D Rnd and Rand, Rnd is for floating points and Rand is for integers. Both functions scramble the random generator for the next call. They both require one parameter which is a number either integer or float, and this number is used to divide and modulo the actual random number that gets generated. This is unnoticable with comparably small numbers (0 - 65535) but after that the number becomes less precise to the point it generates the same number

Code: Select all

mode = 0
If (mode = 0) Then
a = Rand(2147483646)
Else
a = Rand(2147483647)
EndIf
Print(a)
Delay 1000
Running the program in both mode 0 and mode 1 will yield the same number here and if we use the 2^24 which is 16777216 the number difference is 1

Something else that can be done is to use SeedRnd(Millisecs()) which will put a random seed on the program where Millisecs() is the time in Unix of the device, that pretty much solves most of the problems except that if you know the exact Millisecs() you can put it in your own program and continue to predict the randomization

Code: Select all

; pre prediction
x = MilliSecs()
SeedRnd(x)
a = Rand(100)
Print(x)
Delay 1000

; post prediction

x = 1219170 ; the time I started the program
SeedRnd(x) ; seed that and it will give the number that was given last time on the pre-prediction phase
a = Rand(100)
Print(a)
Delay 1000
After I described how broken and goofy the randomness in Blitz3D is, let's go to Wonderland
For the sake of simplicity for the remainder of this post I will be exploring the WA3 POTZ v1.02 code

Unfortunately for us MS thought of our sneaky tricks and he put a randomizer scrambler code that looks like this

Code: Select all

; Randomize
For bla= 1 To MilliSecs() Mod 100
	blabla=Rand(0,44)
Next
But because Millisecs() gets moduled by 100, that means there are 100 possible random seeds for our Wonderland game, the game will run the Rand function between 1 and 100 times and after that we can use the simple scripts from the examples above. MS could've made it even more random with the SeedRnd example I described but thankfully for us he didn't and we can do a little bit of trolling in Wonderland

Code: Select all

; Randomize
PatrickRandomizer = MilliSecs() Mod 100
Print (PatrickRandomizer )
For bla= 1 To PatrickRandomizer
	blabla=Rand(0,44)
Next

a = Rand(1000)
Print(a)
Delay(1000)
With this example we can run the randomizer with whatever number we want, Let's say that PatrickRandomizer will be equal to Seed 69, as long as thats the seed, no matter what are bla and blabla numbers, the number "a" at the end will always equal to 139

With all that, there are 100 possible outcomes for randomness after the application has started. From there we can check the code for simple random events such as scritter or coily movement and run it in our program with the appropriate seed and know the outcomes.

This still needs expanding because there are small particles random stuff that can scramble the generator to oblivion and you can get lost in the code, that's why I am leaving this article unfinished for now, but it's something I've been thinking for a month or so to share around just for the fun nerdy moments in life

Video soon if I don't forget and move on :stinkyhappy:
User avatar
nasko222
Rainbow Master
Posts: 591
Joined: Sat Jul 07, 2018 1:22 pm

Re: Predicting the randomness in Wonderland (Theory)

Post by nasko222 » Sat Jun 24, 2023 9:18 am

Update: Just checked TOW and it doesn't have any randomizer seeds, and the seed is always the same. So I made this small video. As long as you start the application and don't run any levels beforehand the coily movement will be EXACTLY THE SAME. I'm pretty sure this can be used for speedruns for learning particular setup or something (I'm not that creative) :D

Video: https://www.youtube.com/watch?v=wwEMFCH ... l=nasko222

I run the level Fish in a Barrel 4 times in this video. The first 3 times is when the application never ran levels in the session that has been ran, the last time I ran the level twice and the randomization finally took place, eventhough again it could be predictable if you pause before the exact coily movement and then rinse and repeat.
User avatar
Wonderman109
Rainbow MegaStar
Posts: 3530
Joined: Thu Jun 28, 2012 11:25 pm

Re: Predicting the randomness in Wonderland (Theory)

Post by Wonderman109 » Wed Jun 28, 2023 12:52 am

I've used this to get through coily sections in WSW since forever. Coilies and broken z-bots are annoying (although they look cool).
User avatar
nasko222
Rainbow Master
Posts: 591
Joined: Sat Jul 07, 2018 1:22 pm

Re: Predicting the randomness in Wonderland (Theory)

Post by nasko222 » Sun Jul 02, 2023 6:34 pm

Ok so I (totally didnt look at how WASharp uses the Blitz3D random) cracked the randomization math and its completely reversable and allat

Code: Select all

	// Apparently the default seed for Blitz3D
	private static int rnd_state = 4660;
	// For these two you don't have to know the math but just the fact that I explained above, Rnd is for integers, Rand is for floats
	public static float Rnd(float from, float to = 0f) return GameMath.rnd() * (to - from) + from;
	public static int Rand(int from, int to = 1){if (to < from) {int num = from; from = to; to = num;} return (int)(GameMath.rnd() * (float)(to - from + 1)) + from;}
	
	
	// This piece of code is the entire Blitz3D math algorithm, if it gets below 0 it will be set to 2147483647 (The 32 bit integer limit)
	private static float rnd()
		{
			GameMath.rnd_state = 48271 * (GameMath.rnd_state % 44488) - 3399 * (GameMath.rnd_state / 44488);
			if (GameMath.rnd_state < 0)
			{
				GameMath.rnd_state += int.MaxValue;
			}
			return (float)(GameMath.rnd_state & 65535) / 65536f + 7.62939453E-06f;
		}
		
		
		
The seed apparently resets after one use to AGAIN, PREDICTABLE NUMBER. And if you know what a seed has been set to, You can know what the next seed will be and run the equasion again, which means you can actually predict the outcomes for every possible seed

Now that we know this, we can calculate all 100 seeds that are getting randomized at start of WA3/OpenWA

Here they are

Code: Select all

1: 4660, 2: 224942860, 3: 539475828, 4: 650989866, 5: 1951098782, 6: 1446483090, 7: 2049422479, 8: 1690801107, 9: 1544231762, 10: 306512485, 11: 1649319252, 12: 628368061, 13: 895642303, 14: 408826709, 15: 1246837856, 16: 733456154, 17: 1246605292, 18: 244777545, 19: 201848901, 20: 314993732, 21: 878216612, 22: 1066886072, 23: 852242805, 24: 1415698223, 25: 2091791246, 26: 321637373, 27: 1598347920, 28: 1207460551, 29: 474594094, 30: 1923448925, 31: 347580630, 32: 1922340366, 33: 523420316, 34: 876966681, 35: 861008887, 36: 1508963986, 37: 850229260, 38: 856631643, 39: 668416268, 40: 1327360100, 41: 677295208, 42: 425943440, 43: 707355862, 44: 1932310949, 45: 777095381, 46: 1074274102, 47: 997553533, 48: 2028258409, 49: 134710462, 50: 28228086, 51: 1093307108, 52: 616785243, 53: 127182845, 54: 1734847869, 55: 1716669734, 56: 413243125, 57: 1830773539, 58: 22459725, 59: 1821627387, 60: 910187815, 61: 308083892, 62: 193295257, 63: 1886388079, 64: 237361315, 65: 842779620, 66: 2032311899, 67: 379714375, 68: 419668480, 69: 603955929, 70: 1466140734, 71: 1755784029, 72: 861251357, 73: 328331474, 74: 459266594, 75: 784070993, 76: 639108375, 77: 1797780470, 78: 846892100, 79: 829854808, 80: 908969477, 81: 1627232410, 82: 1773790438, 83: 317743161, 84: 451917757, 85: 383161921, 86: 1479920627, 87: 1205068462, 88: 970182913, 89: 1523503294, 90: 450013159, 91: 788108684, 92: 121478759, 93: 1270819379, 94: 851867154, 95: 462517978, 96: 965321826, 97: 949690240, 98: 164162531, 99: 74876471, 100: 147153740
With that, lets run some theoretical example math:

Example 1, using coily:

Whenever coily moves, it has 4 random possible outcomes (It runs Rand(0,3)) up down left and right as you can probably guess.

Running all 100 seeds for first time brings back the following results:

Code: Select all

Left: 27% of seeds
Up: 25% of seeds
Right: 32% of seeds
Down: 31% of seeds
Example 2, using scritter:
By default, the scritter wants to always move once right and once down, if the scritter is left in question, it runs a 50% random chance whether to move to the opposite direction

Code: Select all

v1=1
v2=1
			If Rand(0,100)<50 Then
				v1=-1
			End If
			If Rand(0,100)<50 Then
				v2=-1
			End If
So lets make it easier for us to understand right? Imagine that there is a scritter right next to a wall in front of you, and if you move up the scritter has two choices, left or right, when that happens Rand(0,100) gets ran, and if its 0-49 it will move left (non default) and if its 50-100 it will move right (default)

Running all 100 seeds with this math gives the following results:

Code: Select all

Left: 37% of seeds
Right: 63% of seeds
This should also apply for up and down, where the non-default choice (up) is the same as the non-default choice (left) and the default choice (down) is the same as the default choice (right)

Code: Select all

Up: 37% of seeds
Down: 63% of seeds







But wait? Why stop at 100 seeds? The game will keep generating seeds after the first time has been ran, we can use this to calculate even more chances. Lets run both examples with 1000 seeds

Example 1:

Code: Select all

Left: 22.8% of seeds
Up: 25.4% of seeds
Right: 26.4% of seeds
Down: 27.7% of seeds
Example 2:

Code: Select all

Left/Up: 49.8% of seeds
Right/Down: 50.2% of seeds
Looks like things are slowing starting to evaluate, yeah this random blitz3d math is goofy :D i like it
Post Reply