DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
FractalFusion wrote:
You can think of random(min,max) being calculated first to get some value, then the RNG updated with x←31821*x mod 65536. Sorry if I didn't make it clear.
Thanks for the clarification. I'm trying to work out the spreadsheets. EDIT: OK, I've got the RNG sequence spreadsheet updated to show what RNG value we need for a fast Arnoid. There are a LOT of options for potential RNG seeds that yield him arriving with a 2 second delay. Once the address is found I can update my starting seed spreadsheet so I know what initial RTC to set.
Active player (378)
Joined: 9/25/2011
Posts: 652
Okay, I think I've found it. Memory addresses 273824 and 1322400 seem to follow the RNG pattern. Use this code to track the memory values:
wordOld = {}
searchList={273824,1322400}

while true do
	a, b = jpcrr.wait_event();
	if a == "lock" then
		if nowTick ~= jpcrr.clock_time() then
			nowTick=jpcrr.clock_time()
			for i,v in ipairs(searchList) do
				wordValue = jpcrr.read_word(v)
				if wordOld[v] == nil or wordOld[v] ~= wordValue then
					print("@" .. nowTick .. ": " .. v .. " -> " .. wordValue )
					wordOld[v]=wordValue
				end
			end
		end
		jpcrr.release_vga();
	end
end
Note, because the seed removed the +1 from the calculation, interesting things can happen at certain numbers. Specifically, if the rng seed is 16384, the next seed will also be 16384 (and every seed after that). The rng seed generator also gets stuck on numbers 32768 and 49152. If any of these numbers is advantageous to you, it may be worth starting the game on one of these numbers, as you're guaranteed the seed will never change. I'm particularly curious what the robot battle would look like with the random number generator stuck on one number that always means you hit for damage on every blow.
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
c-square wrote:
Okay, I think I've found it. Memory addresses 273824 and 1322400 seem to follow the RNG pattern. Use this code to track the memory values:
wordOld = {}
searchList={273824,1322400}

while true do
	a, b = jpcrr.wait_event();
	if a == "lock" then
		if nowTick ~= jpcrr.clock_time() then
			nowTick=jpcrr.clock_time()
			for i,v in ipairs(searchList) do
				wordValue = jpcrr.read_word(v)
				if wordOld[v] == nil or wordOld[v] ~= wordValue then
					print("@" .. nowTick .. ": " .. v .. " -> " .. wordValue )
					wordOld[v]=wordValue
				end
			end
		end
		jpcrr.release_vga();
	end
end
Note, because the seed removed the +1 from the calculation, interesting things can happen at certain numbers. Specifically, if the rng seed is 16384, the next seed will also be 16384 (and every seed after that). The rng seed generator also gets stuck on numbers 32768 and 49152. If any of these numbers is advantageous to you, it may be worth starting the game on one of these numbers, as you're guaranteed the seed will never change. I'm particularly curious what the robot battle would look like with the random number generator stuck on one number that always means you hit for damage on every blow.
Awesome. I'll work with this as soon as I can and try to determine an ideal starting RTC. Unfortunately none of the unchanging RNG seeds yield Arnoid showing up for the Pods (unless my math is wrong).
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
c-square wrote:
If the rng seed is 16384, the next seed will also be 16384 (and every seed after that). The rng seed generator also gets stuck on numbers 32768 and 49152. If any of these numbers is advantageous to you, it may be worth starting the game on one of these numbers, as you're guaranteed the seed will never change.
All of these are multiples of 16384. As such, dividing 16384/2 yields 8192. This seed results in a revolving seed of 2 numbers (8192 and 40960). 40960 is 8192 less than 49152. Adding 8192 to 16394 yields 24576 which also results in a revolving 2 value sequence of RNG seeds. Continuing this example: 4096 yields a revolving 4 value sequence 2048 yields a revolving 8 value sequence 1024 yields a revolving 16 value sequence and so on. Basically an even numbered seed is much more predictable and possibly more useful than an odd numbered seed.
Active player (378)
Joined: 9/25/2011
Posts: 652
Looks like the 2048 set gives you two shots at a 2/2 combo (Terminator appears and delay of 2 seconds). You just need to then adjust your starting value to line up one of the combo.
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
c-square wrote:
Looks like the 2048 set gives you two shots at a 2/2 combo (Terminator appears and delay of 2 seconds). You just need to then adjust your starting value to line up one of the combo.
I noticed that as well, I just haven't had a chance to play around with anything else yet. I've got to try and find where the first random is called and then adjust my RTC spreadsheet to see if I can even set a starting RTC to yield 2048. (or any of the other values which are also in the cycle) 26624 18432 43008 34816 59392 51200 10240 With the variety of options, it hopefully won't be very hard. I am also curious to see if it will still take a minimum of 1000 millisecond RTC change to produce a change in the RNG seed value, or if that was specific to the AGI system. Something that may be affected by RNG that I haven't had a chance to confirm yet is the timing of the buckets on the conveyor lift in the garbage freighter.
Editor, Expert player (2073)
Joined: 6/15/2005
Posts: 3282
DrD2k9 wrote:
Something that may be affected by RNG that I haven't had a chance to confirm yet is the timing of the buckets on the conveyor lift in the garbage freighter.
I'm pretty sure the timing on the buckets is the same every time.
c-square wrote:
I'm particularly curious what the robot battle would look like with the random number generator stuck on one number that always means you hit for damage on every blow.
I got one once where Elmo would walk up and punch air until he died, even though I never moved from the starting spot. No clue if that is possible with a seed that also works with Arnoid, or if it is any faster.
Active player (378)
Joined: 9/25/2011
Posts: 652
I can confirm that the only thing random about the buckets is which graphic it uses (dirt or metal garbage). Everything else is the same. I checked out the enemy robot actions for the 2048 rng loop:
  RNG    Action
 2048 -> Back up
26624 -> Punch
18432 -> Back up
43008 -> Back up
34816 -> Punch
59392 -> Back up
51200 -> Punch
10240 -> Punch
Note that every time a punch lands, it calls another random call for the damage. Oh, and an interesting thing happens at the robot battle if you set the rng seed to 16384: Link to video It's a zero input win! EDIT: Also interestingly, it seems the space battle at the end has no random calls in it at all.
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
c-square wrote:
Oh, and an interesting thing happens at the robot battle if you set the rng seed to 16384: It's a zero input win!
Unfortunately we can't use this unless we chose to kill Arnoid in the machine instead of the pods. (And that assumes there doesn't need RNG to get him to arrive there...i haven't checked) EDIT: Even if every other place RNG was used yielded the fastest options, I doubt it would make up for the extra time to use the machine to kill Arnoid.
EDIT: Also interestingly, it seems the space battle at the end has no random calls in it at all.
This I was already pretty confident of.
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
  RNG    Action
 2048 -> Back up
26624 -> Punch
18432 -> Back up
43008 -> Back up
34816 -> Punch
59392 -> Back up
51200 -> Punch
10240 -> Punch
Note that every time a punch lands, it calls another random call for the damage.
This is beautiful. If we punch at the right time, we should alter the RNG seed for damage calculation and may be able to make him not punch at all (after the first punch). EDIT: Which random values yield what actions for the robot battle? EDIT #2: Trying to use the lua script causes JPC-rr to freeze until I terminate the lua; then the emulator continues running. Ideas? Never mind...got it to work (not that I know how i accomplished this)
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
Ok I'm struggling to put together my RTC spreadsheet. The changes are more complicated than with SQ1, but I'm not sure why. Here's a spreadsheet with the first 52 seeds. As you can see, The difference between one seed and the next mostly follows a cycle, but there are some anomalies that I don't understand. This is making it much more difficult to complete the list of 65535 potential seeds as I did with SQ1. (There were anomalies there as well, but much easier to overcome.) If it helps, the first seed is set at game time 1448 ms. So in my mind, the time in milliseconds-from-midnight when the seed is set should be (the starting RTC time + 1448). EDIT: Updated Spreadsheet EDIT 2: I found some errors in the spreadsheet...starting from scratch on this one.
Editor, Expert player (2073)
Joined: 6/15/2005
Posts: 3282
Does anything different happen if you try RTC times that aren't multiples of 1000?
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
FractalFusion wrote:
Does anything different happen if you try RTC times that aren't multiples of 1000?
No. As with my testing in SQ1, it took at least a change of 1000 to elicit a different RNG seed. EDIT: For clarification.... All values from 0-999 in the initial RTC yield the same RNG seed when it is first set.
Active player (378)
Joined: 9/25/2011
Posts: 652
DrD2k9 wrote:
EDIT: Which random values yield what actions for the robot battle?
It's a little complicated to tell:
	(method (changeState newState &tmp [temp0 50] temp50 temp51)
		(switch (= state newState)
			(0
				(cond 
					((<badGuy> (badGuy y?) (gEgo y?))
							(badGuy posn: (badGuy x?) (- (badGuy y?) 1))
						else
							(badGuy posn: (badGuy x?) (+ (badGuy y?) 1))
						)
					)
					((> (badGuy x?) (gEgo x?)) (badGuy posn: (- (badGuy x?) 1) (badGuy y?)))
					(else (badGuy posn: (+ (badGuy x?) 1) (badGuy y?)))
				)
				(badGuy setMotion: Chase gEgo 47 self)
			)
			(1 (self changeState: 2))
			(2
				(switch (Random 1 2)
					(1 (self changeState: 3))
					(2 (self changeState: 11))
				)
			)
			(3
				(localproc_1ed2 badGuy gEgo)
				(= local5 1)
				(badGuy view: 205 setCel: 0 setMotion: 0)
				(proc0_10)
				(badGuy setCycle: End self)
			)
			(4
				(= local17 4)
				(if
				(and (localproc_1efe badGuy gEgo) (not local1))
					(if (not local0)
						(clang1 play:)
						(= local17 -8)
						(= local16 4)
						(global2 setScript: EgoBump)
					else
						(clang2 play:)
					)
				)
				(= cycles 2)
			)
			(5
				(= local5 0)
				(if (<Random>
							(GetDistance
								(gEgo x?)
								(gEgo y?)
								(badGuy x?)
								(badGuy y?)
							)
							55
						)
					)
					(self changeState: 14)
				else
					(= cycles (Random 2 7))
				)
			)
			(7
				(= local3 1)
				(localproc_1ed2 badGuy gEgo)
				(badGuy view: 207 setCel: 0 setMotion: 0)
				(proc0_10)
				(badGuy setCycle: End self)
			)
			(8 (= cycles 5))
			(9 (badGuy setCycle: Beg self))
			(10
				(= local3 0)
				(= local17 2)
				(self changeState: 11)
			)
			(11
				(= local4 1)
				(bgLegs view: 206 setCycle: Fwd)
				(badGuy
					view: (if local9 198 else 203)
					setLoop: (badGuy loop?)
					setCel: (if local9 1 else -1)
					setCycle: (if local9 0 else Walk)
				)
				(= cycles 2)
			)
			(12
				(switch (badGuy loop?)
					(0 (= temp50 -20) (= temp51 0))
					(1 (= temp50 20) (= temp51 0))
					(2 (= temp50 0) (= temp51 -10))
					(3 (= temp50 0) (= temp51 10))
				)
				(badGuy
					setMotion: MoveTo (+ (badGuy x?) temp50) (+ (badGuy y?) temp51) self
				)
			)
			(13
				(= local9 0)
				(self changeState: 14)
			)
			(14
				(bgLegs view: 204 setCycle: 0)
				(badGuy view: 203 setLoop: -1 setCel: -1 setCycle: Walk)
				(= local5 0)
				(= local4 0)
				(= local9 0)
				(self changeState: 0)
			)
			(15 (badGuy setScript: 0))
		)
	)
)
I think state 3 is the start of his punching sequence, state 6 is blocking, state 11 is walking backwards, state 14 is walking forwards. From what I can tell, at the start (state 2) is a 1 in 2 chance of punching (the other is being walking backwards). After a punch, state 5 has a 21 in 100 chance that the bad guy will punch again immediately.
Player (26)
Joined: 8/29/2011
Posts: 1206
Location: Amsterdam
c-square wrote:
After a punch, state 5 has a 21 in 100 chance that the bad guy will punch again immediately.
It appears this chance depends on the distance between badguy and EGO.
Post subject: More digging into initial RNG seed.
DrD2k9
He/Him
Editor, Judge, Expert player (2213)
Joined: 8/21/2016
Posts: 1088
Location: US
So I've disassembled SCIV.EXE in IDA and found three subroutines where the program pulls the time from DOS.
seg000:8714 ; =============== S U B R O U T I N E =======================================
seg000:8714
seg000:8714 ; Attributes: bp-based frame
seg000:8714
seg000:8714 sub_18714       proc near               ; CODE XREF: seg000:loc_157C4 p
seg000:8714                 push    si
seg000:8715                 push    di
seg000:8716                 push    bp
seg000:8717                 mov     bp, sp
seg000:8719                 mov     bx, ds:750h
seg000:871D
seg000:871D loc_1871D:                              ; CODE XREF: sub_18714+13 j
seg000:871D                 or      bx, bx
seg000:871F                 jnz     short loc_18729
seg000:8721                 mov     ah, 2Ch
seg000:8723                 int     21h             ; DOS - GET CURRENT TIME
seg000:8723                                         ; Return: CH = hours, CL = minutes, DH = seconds
seg000:8723                                         ; DL = hundredths of seconds
seg000:8725                 mov     bx, dx
seg000:8727                 jmp     short loc_1871D
seg000:8729 ; ---------------------------------------------------------------------------
seg000:8729
seg000:8729 loc_18729:                              ; CODE XREF: sub_18714+B j
seg000:8729                 mov     ax, 7C4Dh
seg000:872C                 mul     bx
seg000:872E                 mov     ds:750h, ax
seg000:8731                 mov     al, ah
seg000:8733                 mov     ah, dl
seg000:8735                 pop     bp
seg000:8736                 pop     di
seg000:8737                 pop     si
seg000:8738                 retn
This first one is the one that updates the the RNG seed based on the previous (lines 8729 onward. It calls the DOS time if the RNG seed is 0 (line 871d-8727) The next two are other subroutines that call for the DOS time.
seg000:9B03 ; =============== S U B R O U T I N E =======================================
seg000:9B03
seg000:9B03 ; Attributes: bp-based frame
seg000:9B03
seg000:9B03 sub_19B03       proc near               ; CODE XREF: seg000:5205 p
seg000:9B03
seg000:9B03 arg_0           = word ptr  8
seg000:9B03
seg000:9B03                 push    si
seg000:9B04                 push    di
seg000:9B05                 push    bp
seg000:9B06                 mov     bp, sp
seg000:9B08                 cmp     [bp+arg_0], 3
seg000:9B0C                 jz      short loc_19B3F
seg000:9B0E                 mov     ah, 2Ch
seg000:9B10                 int     21h             ; DOS - GET CURRENT TIME
seg000:9B10                                         ; Return: CH = hours, CL = minutes, DH = seconds
seg000:9B10                                         ; DL = hundredths of seconds
seg000:9B12                 mov     ah, cl
seg000:9B14                 xor     al, al
seg000:9B16                 shr     ax, 1
seg000:9B18                 shr     ax, 1
seg000:9B1A                 or      al, dh
seg000:9B1C                 cmp     [bp+arg_0], 2
seg000:9B20                 jnz     short loc_19B28
seg000:9B22                 shr     ax, 1
seg000:9B24                 mov     cl, 3
seg000:9B26                 jmp     short loc_19B39
seg000:9B28 ; ---------------------------------------------------------------------------
seg000:9B28
seg000:9B28 loc_19B28:                              ; CODE XREF: sub_19B03+1D j
seg000:9B28                 or      ch, ch
seg000:9B2A                 jnz     short loc_19B2F
seg000:9B2C                 add     ch, 0Ch
seg000:9B2F
seg000:9B2F loc_19B2F:                              ; CODE XREF: sub_19B03+27 j
seg000:9B2F                 cmp     ch, 0Ch
seg000:9B32                 jle     short loc_19B37
seg000:9B34                 sub     ch, 0Ch
seg000:9B37
seg000:9B37 loc_19B37:                              ; CODE XREF: sub_19B03+2F j
seg000:9B37                 mov     cl, 4
seg000:9B39
seg000:9B39 loc_19B39:                              ; CODE XREF: sub_19B03+23 j
seg000:9B39                 shl     ch, cl
seg000:9B3B                 or      ah, ch
seg000:9B3D                 jmp     short loc_19B53
seg000:9B3F ; ---------------------------------------------------------------------------
seg000:9B3F
seg000:9B3F loc_19B3F:                              ; CODE XREF: sub_19B03+9 j
seg000:9B3F                 mov     ah, 2Ah
seg000:9B41                 int     21h             ; DOS - GET CURRENT DATE
seg000:9B41                                         ; Return: DL = day, DH = month, CX = year
seg000:9B41                                         ; AL = day of the week (0=Sunday, 1=Monday, etc.)
seg000:9B43                 mov     ah, dh
seg000:9B45                 xor     al, al
seg000:9B47                 shr     ax, 1
seg000:9B49                 shr     ax, 1
seg000:9B4B                 shr     ax, 1
seg000:9B4D                 or      al, dl
seg000:9B4F                 shl     cx, 1
seg000:9B51                 or      ah, cl
seg000:9B53
seg000:9B53 loc_19B53:                              ; CODE XREF: sub_19B03+3A j
seg000:9B53                 pop     bp
seg000:9B54                 pop     di
seg000:9B55                 pop     si
seg000:9B56                 retn
And the last...
seg000:8684 ; =============== S U B R O U T I N E =======================================
seg000:8684
seg000:8684 ; Attributes: bp-based frame
seg000:8684
seg000:8684 sub_18684       proc near               ; CODE XREF: seg000:4E09 p
seg000:8684                                         ; seg000:59F3 p ...
seg000:8684
seg000:8684 arg_0           = word ptr  8
seg000:8684 arg_2           = word ptr  0Ah
seg000:8684
seg000:8684                 push    si
seg000:8685                 push    di
seg000:8686                 push    bp
seg000:8687                 mov     bp, sp
seg000:8689                 mov     ax, [bp+arg_0]
seg000:868C                 mov     dx, [bp+arg_2]
seg000:868F                 or      dx, dx
seg000:8691                 jnz     short loc_186A7
seg000:8693                 or      ah, ah
seg000:8695                 jz      short loc_1869E
seg000:8697                 mov     al, ah
seg000:8699                 mov     si, 772h
seg000:869C                 jmp     short loc_186B7
seg000:869E ; ---------------------------------------------------------------------------
seg000:869E
seg000:869E loc_1869E:                              ; CODE XREF: sub_18684+11 j
seg000:869E                 or      al, al
seg000:86A0                 jz      short loc_186EE
seg000:86A2                 mov     si, 762h
seg000:86A5                 jmp     short loc_186B7
seg000:86A7 ; ---------------------------------------------------------------------------
seg000:86A7
seg000:86A7 loc_186A7:                              ; CODE XREF: sub_18684+D j
seg000:86A7                 mov     ax, dx
seg000:86A9                 or      ah, ah
seg000:86AB                 jnz     short loc_186B2
seg000:86AD                 mov     si, 782h
seg000:86B0                 jmp     short loc_186B7
seg000:86B2 ; ---------------------------------------------------------------------------
seg000:86B2
seg000:86B2 loc_186B2:                              ; CODE XREF: sub_18684+27 j
seg000:86B2                 mov     al, ah
seg000:86B4                 mov     si, 792h
seg000:86B7
seg000:86B7 loc_186B7:                              ; CODE XREF: sub_18684+18 j
seg000:86B7                                         ; sub_18684+21 j ...
seg000:86B7                 mov     cx, 9
seg000:86BA
seg000:86BA loc_186BA:                              ; CODE XREF: sub_18684+38 j
seg000:86BA                 shr     al, 1
seg000:86BC                 loopne  loc_186BA
seg000:86BE                 shl     cx, 1
seg000:86C0                 sub     si, cx
seg000:86C2                 lodsw
seg000:86C3                 mov     bx, ax
seg000:86C5                 mov     ax, [bp+arg_0]
seg000:86C8                 mov     cx, bx
seg000:86CA                 div     bx
seg000:86CC                 xor     dx, dx
seg000:86CE                 add     ax, cx
seg000:86D0                 adc     dx, 0
seg000:86D3                 shr     dx, 1
seg000:86D5                 rcr     ax, 1
seg000:86D7                 mov     bx, ax
seg000:86D9                 mov     ax, [bp+arg_0]
seg000:86DC                 mov     dx, [bp+arg_2]
seg000:86DF                 mov     cx, bx
seg000:86E1                 div     bx
seg000:86E3                 xor     dx, dx
seg000:86E5                 add     ax, cx
seg000:86E7                 adc     dx, 0
seg000:86EA                 shr     dx, 1
seg000:86EC                 rcr     ax, 1
seg000:86EE
seg000:86EE loc_186EE:                              ; CODE XREF: sub_18684+1C j
seg000:86EE                 pop     bp
seg000:86EF                 pop     di
seg000:86F0                 pop     si
seg000:86F1                 retn
seg000:86F1 sub_18684       endp
seg000:86F1
seg000:86F2 ; ---------------------------------------------------------------------------
seg000:86F2                 push    si
seg000:86F3                 push    di
seg000:86F4                 push    bp
seg000:86F5                 mov     bp, sp
seg000:86F7                 mov     ax, ds:74Eh
seg000:86FA
seg000:86FA loc_186FA:                              ; CODE XREF: seg000:8704 j
seg000:86FA                 or      ax, ax
seg000:86FC                 jnz     short loc_18706
seg000:86FE                 mov     ah, 2Ch
seg000:8700                 int     21h             ; DOS - GET CURRENT TIME
seg000:8700                                         ; Return: CH = hours, CL = minutes, DH = seconds
seg000:8700                                         ; DL = hundredths of seconds
seg000:8702                 mov     ax, dx
seg000:8704                 jmp     short loc_186FA
seg000:8706 ; ---------------------------------------------------------------------------
seg000:8706
seg000:8706 loc_18706:                              ; CODE XREF: seg000:86FC j
seg000:8706                 shr     ax, 1
seg000:8708                 jnb     short loc_1870D
seg000:870A                 xor     ax, 0B400h
seg000:870D
seg000:870D loc_1870D:                              ; CODE XREF: seg000:8708 j
seg000:870D                 mov     ds:74Eh, ax
seg000:8710                 pop     bp
seg000:8711                 pop     di
seg000:8712                 pop     si
seg000:8713                 retn
Using 0 as initial RTC: Unless my math is wrong, neither of the last two subroutines yield 29317 for the initial seed that results according to the lua script. (I tried calculating through using the following values for the DOS time when the RNG is first called: 14469 ms, 14470 ms, and 14483) Any help or suggestions on the next direction to look would be appreciated. I've begun brute force testing and gotten the first 33 seeds thus far, but have found no lasting pattern. I'll keep working on this unless someone can figure out the assembly better than I did.