I tested my theory of changing the initial RTC within the savestate file. It didn't work. It took a change of at least 1000 to yield a different initial seed at the beginning of room 2.
Further, that seed did not change equally to the change in RTC timer. For example:
According to c-squares lua script. The number of 'changes' when the initial is set varies drastically with different initial RTC values as well.
Any ideas?
EDIT: With further testing...
Starting RTC of 86400000 = Starting of 0
Starting RTC of 86401000 = Starting of 1000
So it seems that a change of at least 1 whole second in RTC value is necessary to change the initial seed....though that still doesn't explain why the initial seed changes at a different rate than the milliseconds after midnight.
Well, yes. First, although the initial seed depends on the amount of milliseconds since midnight, the game's internal framerate is only about 12 frames per second. That means that every batch of (1000 / 12) = 83 milliseconds will yield the same seed.
Second, when the seed is initialized, it is immediately advanced once. The way the random algorithm works, adjacent values end up being non-adjacent after one step.
But it doesn't. It takes a change of 1000 milliseconds in the RTC setting to yield a different seed.
EDIT: And it recycles every 86400000 milliseconds (1 day).
So it seems that we're limited to 86,400 potential options for our initial seed. What I'm trying to figure out is a way to calculate what RTC time will yield the initial seed we want for the game. I don't particularly want to manually test 86,400 options.
Oh, I see: milliseconds is divided by thousand for some reason.
The initial seed is (time in seconds) * 20 + ((time in milliseconds) / 1000) * 20
BUT note that creating the seed will immediately advance it once (i.e. to generate the first random number in logic 2, which is then ignored since guards can't spawn the first time you enter the room). So the value you'll see in your lua panel is the seed AFTER one advancement.
That pattern continues all the way through RTC 58000 but then breaks down.
And, no, I did not brute force test all 60 possibilities. I wrote a formula, but it only worked up to that point.
RTC of 59000 has a 0xC649 difference the previous seed. That shouldn't be expected until RTC 60000.
Then RTC of 60000 returns to the 0x4296 difference.
My formula worked for a while, but then broke down...I'm missing something.
Thanks DrD2k9 for the information.
According to Radiant, the RNG is advanced once immediately after creating the seed. So I took your values and looked at the previous seed:
(for each of these, the first number is the index, where index(0)=0, and the second number is its corresponding seed value)
It looks like the sequence of initial seeds (before advancing it) starts at 4 for 0ms, then goes 22 (for 1000ms), 40 (for 2000ms), 58, 76, 95, ...
Normally the seed goes up by 18, but in some cases the number jumps by 19. It appears to occur every five numbers, but, as you said, at 59000 milliseconds it jumps by 19 instead of 18. Assuming that you mean that 59000ms is the first exception to this rule, the average seed increase appears to be between 18.203 and 18.204. Notably, this number is approximately the clock frequency of MS-DOS, normally cited to be 18.2Hz.
My conjecture is that the initial seed is the DOS clock frequency times RTC time (in seconds) plus 4, mod 65536.
Could you send the seed value for RTC 86399000ms? (This is one second before 86400000ms, which you said starts the cycle over).
Unfortunately, I haven't been able to come up with a predictive way of determining the next rock positions and have the skimmer move in time to avoid them. There's a lot of randoms going on that have nothing to do with the rock placement, and it's been very difficult taking them all into account.
I'll try using automated saving and loading of save states next to find a way, but I'm starting to think this may not be doable.
I just had another thought: how about using joystick input to get around the clunky frame limitation of typing? It could well make for better routes, and may make tackling the skimmer sequence easier.
They're about the time delay between rocks. But yeah, makes it more complicated. Since randomness advances each frame when you're moving, it should be doable to make all rocks spawn to the side while you stick in the middle.
So far I've been trying to dodge the rocks, to little success. Manipulating the rocks might work, however there are two problems that need to be overcome:
1) Figuring out when we need to manipulate the rocks
I've written a function to figure out what the next rocks are going to be, but it's not yet very accurate:
function getNextRock1(rockseed)
nextrock1 = calcRnd(rockseed, 80, 136)
return nextrock1
end
function getNextRock2(rockseed)
nextrock2 = calcRnd(rockseed, 20, 70)
return nextrock2
end
function getNextSeed(seed)
return slotRNG[seed]
end
function getNextRocks()
v0 = 240153
seedAddr = 246039
rock1 = jpcrr.read_byte(v0 + 30)
rock1Countdown = jpcrr.read_byte(v0 + 34)
rock2 = jpcrr.read_byte(v0 + 32)
rock2Countdown = jpcrr.read_byte(v0 + 35)
rockseed = jpcrr.read_word(seedAddr)
if (rock2Countdown > rock1Countdown) then
if rock1Countdown > 1 then
rockseed = getNextSeed(rockseed)
nextrock1 = getNextRock1(rockseed)
end
if rock2Countdown > 1 then
rockseed = getNextSeed(rockseed)
nextrock2 = getNextRock2(rockseed)
end
if rock2Countdown > 1 and 5 >= rock2Countdown - rock1Countdown then
rockseed = getNextSeed(rockseed)
nextrock1 = getNextRock1(rockseed)
end
if rock1Countdown > 1 and rock1Countdown - rock2Countdown < 5 then
rockseed = getNextSeed(rockseed)
end
end
if rock1Countdown <= 1 then
rockseed = getNextSeed(rockseed)
end
if rock2Countdown <= 1 then
rockseed = getNextSeed(rockseed)
end
if rock1Countdown == 0 and rock2Countdown == 0 then
countSeed=jpcrr.read_word(seedAddr)
countSeed=getNextSeed(countSeed)
count1 = calcRnd(countSeed,3,30)
countSeed=getNextSeed(countSeed)
count2 = calcRnd(countSeed,3,30)
rock1Pos = jpcrr.read_byte(v0+31)
rock2Pos = jpcrr.read_byte(v0+33)
if (rock2Pos < rock1Pos and count1 <= count2) or (rock1Pos < rock2Pos and count2 <= count1) then
rockseed = getNextSeed(rockseed)
nextrock1 = getNextRock1(rockseed)
rockseed = getNextSeed(rockseed)
nextrock2 = getNextRock2(rockseed)
else
rockseed = getNextSeed(rockseed)
nextrock2 = getNextRock2(rockseed)
rockseed = getNextSeed(rockseed)
nextrock1 = getNextRock1(rockseed)
end
elseif rock1Countdown <= 1 and rock2Countdown <= 1 then
if rock1Countdown <= rock2Countdown then
rockseed = getNextSeed(rockseed)
nextrock1 = getNextRock1(rockseed)
rockseed = getNextSeed(rockseed)
nextrock2 = getNextRock2(rockseed)
else
rockseed = getNextSeed(rockseed)
nextrock2 = getNextRock2(rockseed)
rockseed = getNextSeed(rockseed)
nextrock1 = getNextRock1(rockseed)
end
elseif rock1Countdown <= 1 then
rockseed = getNextSeed(rockseed)
nextrock1 = getNextRock1(rockseed)
elseif rock2Countdown <= 1 then
rockseed = getNextSeed(rockseed)
nextrock2 = getNextRock2(rockseed)
end
return rock1, rock2, nextrock1, nextrock2
end
(I apologize for the lack of comments.) It works fine if the rock countdowns are both large, but has trouble once they're 1 or 0, which is why there's so much code trying to adjust it. Feel free to check it out and correct it.
2) Manipulating seeds on the frames we want to
It's possible to use up or down to enter a direction without actually moving the skimmer, so manipulating seeds is possible while staying in one place. The problem is that because we're using text input, although it's possible to stop moving on a desired frame, you then have to wait a number of frames before the engine will pick up your command to start moving again. This makes seed manipulation unpredictable. I'm wondering if joystick input is instantaneous and gets around the keyboard input limitation.
For what it's worth, I don't mind doing the skimmer sequence manually. It just won't give us as much control of the random seed heading into Ulence Flats if I do so.
Speaking of randomization in Ulence Flats. Once we decide on a final process for the skimmer sequence, I'll see what I can do about not having to take the key out of the skimmer to save a few frames.
SUCCESS!
I've got a spreadsheet that works for choosing what starting seed I want.
It took a combination of calculations and brute force to complete the spreadsheet, but it's done. I can now set the initial RTC to yield a desired seed value upon Room 2.
Now to see if I can find a starting RTC that yields the longest spider droid delay as well as no guards on the Arcada.
I've found an initial RTC that yields no guards on the Arcada and only requires one 'y' input to advance the random seed to yield the longest time for the spider droid to appear.
I've run through the game up through exiting the escape pod and was able to confirm that variable v93 was set to 250, which I believe is the longest possible.
Now then...Do we want to try and manipulate acid drops, or just deal with the random seed where we are when we get past them?
Also on the note of randomness...
Does the guy who buys the skimmer work on an RNG timer? Does the blue droid in the Deltaur airlock work on one?
Looks like there's no delay for joystick input. It should be easy to update JPC-rr so you can use the number keys to control the joystick, if you'd like.
There is one problem I'm running into: I can send an AXISA command with a positive number, and Roger goes left. Same with AXISB and up. However, I haven't figured a way to go right or down yet. The input doesn't take negative numbers. Anyone else have an idea? I'll play with it more tomorrow.
Even without TAS'ing, I find that reasonably often I can enter the room and walk straight to the right without hitting anything. I'll check on the probability of that.
No to both. The skimmer guy appears immediately when you get off the skimmer. The blue droid appears 249 ticks after you enter the room.
Right, so those acid drops are pretty straightforward.
When entering the room, random() is called three times for their delays: 9-71, 1-71, and 1-71 ticks respectively. After that amount of ticks, they spawn.
When they do, an immediate is made check on EGO's X coordinate. This is done only once when the drop appears, not while it falls (note that this makes the sequence entirely unfair to a human player, as there is no way to see the drops coming!) If EGO is at X = 84 through 91, he'll get hit by the first drop. 94 through 101, the second drop. 109 to 116, the third drop.
I've tested a bunch of times and find that at fastest speed, moving straight through the room gets me past the drops 66% of the time. So this should be easy to manipulate; the odds are good that the seed you already have will get you past them.
Good to know. What I originally meant with my question was, do we want to try and manipulate the random calls to control what seed we get coming out of that room?
Or do we just want to see where it's at when entering the skimmer sequence?
My first question: Is that much work necessary? What is the potential range of seed changes that can happen through the skimmer sequence?
Second question: Might it be simpler to just make a run with my current RTC time and see where the seed is at the end of the skimmer sequence when performed manually; compare that number with the seed spreadsheet and see where it leaves us for the slots; then use that information to determine how much manipulation we actually need to worry about during the skimmer sequence?
If we end up with a relatively good starting point for the slots with this method, we don't have to try and automate so much of the skimmer sequence.
No, it's not. If we're searching for the perfect run, then we'll have to do that exercise, but it's very possible to get a quick, decent run without it. In the few passes I've done, the skimmer sequence has called random anywhere between 300 and 600 times, making it hard to target a specific range manually. However, if you want to limit the number of random calls, press "up" when you're still. That will prevent the shadow hop random calls.
I still want to try to get an autopilot working, as I feel I'm close now. But if you want to go ahead and do the run manually, feel free. You likely have enough info to do a fast run, and I'll be happy to help out if or when you need it.
Looks like there's no delay for joystick input. It should be easy to update JPC-rr so you can use the number keys to control the joystick, if you'd like.
There is one problem I'm running into: I can send an AXISA command with a positive number, and Roger goes left. Same with AXISB and up. However, I haven't figured a way to go right or down yet. The input doesn't take negative numbers. Anyone else have an idea? I'll play with it more tomorrow.
What are the specifics of joystick input in JPC-rr? Are there commands other than AXISA and AXISB, and if so, what do they do? What are the possible values for AXISA and AXISB? Did you try a number at the top of its possible range?
DrD2k9 wrote:
Does the guy who buys the skimmer work on an RNG timer?
Not really, but there is one situation in that room where random() is called, which Radiant already mentioned on a previous page. If you leave the skimmer key in the skimmer and walk off and come back, there is a 34% chance of it being stolen. If you can manipulate it so that it won't be stolen, then you don't need to take out the key. (Also, there is no need to say "no" to that guy the first time. Just walk off after he gives his first offer.)
Edit: The alien waiting to be killed by the slot machine runs on an RNG timer. It takes 71+54*(x-2) ticks for him to be killed, where x is a random number between 2 and 7.
What are the specifics of joystick input in JPC-rr? Are there commands other than AXISA and AXISB, and if so, what do they do? What are the possible values for AXISA and AXISB? Did you try a number at the top of its possible range?
I figured it out. The starting value (center) for the joystick axes is 10,000. Left and up is zero, right and down is 20,000.
Of course, when things start looking good, something else comes up:
My simple autopilot is still too slow to react after the rock placements are set. You have three frames to get out of the way of a rock. Even with instantaneous movement, that's not always possible. However, my (semi)predictive autopilot is faring better, and is getting out of the way of the rocks.... until the game freezes for some unknown reason and just hangs.
Still more to look into...