1 2
6 7 8 9
Player (79)
Joined: 8/5/2007
Posts: 865
FatRatKnight wrote:
Filling out the rest of the stuff. I hope it helps out in planning when to reset for encounters.
FatRatKnight wrote:
Step counts. Counting only the ones that increment the appropriate RNG.
Excellent, excellent, excellent! These will be very useful to us!
FatRatKnight wrote:
Also, suppose some path takes 210 steps to finally get through and meet the boss. Is it wiser to manipulate closer to the 93 step RNG per reset, or don't bother and go for approximately 70 each time? The 93 steps is still the way to go. Just because you finish some leg of the journey doesn't mean that, suddenly, the remaining 69 steps are completely worthless. You were at C33B:5E when you made the P-FROG bandit drop dead. The next encounter was at 7C. The difference between the two numbers, 0x5E and 0x7C is 30 steps. By resetting when you did, you effectively wasted 29 steps. Ouch. Keep that in mind. Just because you beat a boss doesn't mean the leftover RNG is suddenly bad.
You're half right. First, regarding my run, yes, you're entirely right. I don't know what I was thinking. I believe I thought it would be a lot harder to manipulate luck than it has ended up being. Going off the top of my head, I had a sense that manipulating a specific RNG value takes maybe 24 steps, manipulating it to one of two values takes 12 steps, and manipulating it to one of three values takes 8 steps. I think those numbers were a tad conservative, so when I saw I could get POWER when I could, I jumped on it (never mind that I could have put off learning POWER and never mind that I should have put off learning POWER...). However, you're mistaken in the general case. The P-FROG is a poor example because he was at the end of a long string of battles from which we were learning TELEPOR. Suppose we reach KINGSWD with the same surplus of 30 steps. Those steps won't "roll over" because we need to manipulate luck at KINGSWD to get a Mana bonus (unless you want to fight another battle later on-- I don't think you want that). That means any difficulty we went through and frames lost to luck manipulation en-route to KINGSWD were wasted. Provided we have to manipulate luck before a forced encounter, it's best to reach it by manipulating luck in as few frames as possible. We can theoretically reset as late as 15 frames after saving (remember, 16 frames per step... except in the glider or on the bike, but you know that), but we should rarely have to reset later than, say, four or five frames after saving (I think we can even consistently get it down to the zero to two range). Of course, that assumes we can go the distance in the same number of resets. Make sense now? This rule ceases to apply in the second half of the game when we're no longer manipulating luck for the purposes of stat bonuses and abilities. There it makes sense to go as many steps as possible without resetting. I'm currently trying to get SEI-RYU to kill C by analyzing the RNG. Sorry it's taking me a little bit to get into the run tonight. I still expect plenty of progress.
Player (79)
Joined: 8/5/2007
Posts: 865
Up to World 3. Not quite as far as I would have liked, but it'll do. There isn't a whole lot to report on. I decided to let D die and revive her. That cost 967 frames. I did not follow the correct encounter rate coming out of the SEI-RYU battle, so I wasted 17 steps. I had the most awful luck ever in the Tower and the RNG simply refused to throw me anything that wasted less than about 15 steps. I am currently having the damnedest time trying to manipulate luck for the MOSQUITOs in World 3. I've determined that the number of enemies in a group is decided by how many times C353 rolls before hitting a number below a certain threshold. In this instance (all instances?), that threshold is 64. I have A in slot 1, so the only value of C30B that allows me to get her a +5 Mana bonus is 137, and that's only if C353 is 148, which has just an eleven percent chance of happening. I was able to get it to happen twice, but in both cases, the RNG snuck out from under me. Prior to resetting, my script told me that C30B would be 137 while C353 would be 148. After resetting, C353 was 147, so I think the act of opening the menu caused it to shift. I'd noticed things like that happening before, but this is the first time it's been a problem and actually notable. For now, be aware that opening the menu seems to cause the RNG seeds to shift down ever so slightly. It can't have been by much. I may decide to shoot for the Mana bonus in another slot and reorder the party. Now, however, sleep.
Player (79)
Joined: 8/5/2007
Posts: 865
I tried shuffling the party and manipulating luck (some new findings showed that I would be best off with A in the fourth slot), but I had the same problem-- the RNG index dropped below its predicted value. I figure that the offset is dipping by only a tiny fraction, so I set my mind to creating a Lua script that would determine the "unknown fractional amount" in b (according to the definition on the resources page). At first I was intimidated and thought it would be a long guess-and-check procedure, but then I realized that when b increments by 1/64, exactly one RNG index is exactly 1 greater than its previous value. For example, suppose we compare b=0 with b=1/64. In that case C307 will be one greater than its previous value and every other RNG index will be the same as it was before. Therefore, all I had to do was tally up the number of RAM addresses between C300 and C33F that exceeded their expected values when b=0. Divide this number by 64 and you have your "fractional amount". But in case you don't care about any of that and only want the good stuff, here's my code:
--This function finds the "unknown fractional amount" that offsets the RNGs.  Useful for debugging.
local function getb()
	b=0
	for i=0,63 do
		b=b + (memory.readbyte(0xC300+i)-memory.readbyte(0xC300)-math.floor(9/64*i))%256
	end
	return b
end
In this case, b is not divided by 64 to obtain the fractional amount-- I did that so it would be consistent with your "RNG picking" script, which uses the same format. As far as I can tell, my script works marvelously. Just note that it doesn't give sensible results when the RNGs start changing; you should only interpret the output immediately after resetting. I'll be using my script to find out how much the RNG slips when I save and reset. If it's by a fairly consistent amount, then I can possibly adjust the "window" over which to reset. I'll edit this post to update you unless you reply in the meantime. Edit: I took 5 samples. The results are... not good.
Projected| Actual  |Difference
---------+---------+----------
41/64    |254 53/64|-1 52/64
16/64    |255 28/64|-52/64
9 35/64  |7 27/64  |-2 12/64
3 5/64   |2 17/64  |-52/64
3 58/64  |1 50/64  |-2 8/64
The difference column has a mean of -1.55 but a rather large standard deviation of 0.69. I don't know if it's significant that all of the difference column is divisible by 1/16. These results don't outright kill our chances of manipulating luck, but they do make it a lot harder. For now, I'll have to adjust my window 1.55 upward plus two standard deviations of tolerance, then try options until they work. Can you offer any insight here? Edit 2: Well, I made essentially no progress tonight. This RNG drifting is really worrisome. Is it going to get worse as the game progresses, or is it unique to World 3? Why did I not notice it before? What, if anything, can we do to influence it? If we don't figure something out, it will make the final run excruciating (I was hoping instead for a "paint by numbers" kind of straightforwardness). On the other hand, I think the shift can be subtly influenced. I haven't yet bothered to do a formal test, but I recall restarting at the same frame after saving two different times and getting slightly different results. Given how consistent the RNGs usually are, that indicates to me that it has to do with when and for what duration I pressed certain buttons. Tomorrow, I may try throwing together a short script that presses buttons for a certain duration-- say, ten frames-- then resets to compare the RNG. (Or, you know, I could just press the damn buttons for ten frames then reset. Why do I need a script? This is a good indication to me that I should go to bed...) If we can understand and control that, then we're back in business. That means we can force the RNG to drift by doing something as simple as holding the A button. I'm doubtful though. I think there's some monster chaos underneath it all. No urgency, but I'm basically waiting for you to bounce an idea this way. Edit 3: Well heck. Seeing that it would be extremely easy to test the RNG drift, I decided to run a little test before turning in. Here are the resulting RNG seeds when holding the specified button for ten frames:
Button|   RNG
------+---------
None  |217 10/64
A     |217 10/64
B     |217 10/64
Up    |217 8/64
Down  |217 8/64
Left  |217 16/64
Right |217 15/64
Select|217 22/64
Start |217 23/64
Chew on that for a little while. Edit 4: The same test in Base Town after a fresh hard reset:
Button|   RNG
------+---------
None  |243 12/64
A     |243 12/64
B     |243 12/64
Up    |243 12/64
Down  |243 12/64
Left  |243 18/64
Right |243 17/64
Select|243 26/64
Start |243 26/64
It's not unique to World 3.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
The RNG is affected by what the CPU is doing internally. Generally, the music itself has a significant impact on the RNG. So does many different actions like exiting the menu. Part of the reason why I was considering using the Sound Test, of all things, to manipulate luck. As for why I didn't bring this up earlier, it's simply that I don't think to say these details. It's quite important to know, and I forget. ... I have a few things I want to look at, though. I think I should look now... One test: Party of mutants, F F M F and see how the random battles go from there. At SEI-RYU, the M must die (two Fs will have POWER and would steal his HAMMER away from him). At monster trio, the fodder F must die. After BYAK-KO, anything goes after we get our manly man. Another test: Party of mutants, F M F F. No swap. Fight an extra battle to give extra Agility to the fourth F. Even a point or two will make the run feasible. The third test: Party of mutants, F M F F. Swap first with fourth so the GAZE user is the one with the extra Agility. What you've been doing, with several mistakes. Here's what I use to scan the RNG:
Language: lua

local str= "???.??" --***************************************************************************** local function RefreshRNG() --***************************************************************************** local addr= 0xC300 local Rn= MR(addr) str= string.format("%3d",Rn) .. "." local J= Rn while J == Rn do addr= addr+1 J= MR(addr) end if addr == 0xC308 then str= str .. " 0" return end local val= 64 - 9*(addr - 0xC300) Rn= J local count= 0 while Rn == ((J-count)%256) do count= count+1 J= MR(addr + count*7) end val= val + 9 - count str= str .. string.format("%2d",val) end
And the trigger that will detect resets for me:
Language: lua

local RsState= "Idle" --***************************************************************************** local function ScanReset2() --***************************************************************************** --The "read memory for joypad instead of the broken joypad.get" alternative. if RsState == "Idle" then local a= memory.readbyte(0xC3A0) if bit.band(a,0x0F) == 0x0F then RsState= "Rs?" end elseif RsState == "Rs?" then --Yes, elseif. local a= memory.readbyte(0xC3A1) if a == 0 then RsState= "Oh" else RsState= "Idle" end elseif RsState == "Oh" then local a= memory.readbyte(0xC300) if a == 0 then a= memory.readbyte(0xC320) end if a ~= 0 then RefreshRNG(); RsState= "Idle" end else --Oh, snap. An error. I typoed? RsState= "Idle" end end
Player (79)
Joined: 8/5/2007
Posts: 865
The agony is over! (Okay, not quite.) I've dispatched the Monster Trio. I normally wouldn't give an update without more progress, but this segment was especially nasty and there is plenty to report. Before the MOSQUITO fight, I manipulated C30B to be 229 (Mana +5 bonus in 4th slot) while C353 is 239 (one MOSQUITO). I now think that was a bad choice. I think what we should shoot for in the real run is C30B = 173 while C353 = 184. This is much harder to get (1/7th as frequent), but the chief difference is that it gives us a lot more steps (about 70) before we fight the next battle. In fact, there might be enough steps to manipulate that result back in the Tower, which would be really handy. Ah, you may say, but Jeanne's hideout is just 28 steps from the Floating Castle and we'd have to manipulate luck before the Monster Trio to get a Mana bonus anyway and you'd just made a big deal about how leftover steps don't carry over after a battle. Well, this is the exception. If we pick any other RNG value that gives us the Mana boost and one MOSQUITO, we must manipulate luck en route to Jeanne's hideout. Because the glider is so damn fast, our window to manipulate luck is extremely small-- I recall it was no more than 100 frames. If we can traverse the entire path from the castle to Jeanne's hideout without fighting a battle or resetting, not only can we use the transit to manipulate luck, but also the time when we're walking over to the monsters to save her. I think it'll be a lot harder to manipulate luck the first time, but it will more than pay for itself in how much easier it is to manipulate luck the following time. Regardless of whether you fully understood all that, I just wanted to put it in writing, since we should take it into account for the final run. I had to reorganize the party just a little bit before engaging the monsters. That cost a little bit of time, but it's better than letting D die and reviving her again. I need just two more TELEPORs out of her. My Monster Trio strategy differed slightly from what I believe is the prescribed method. As I recall, you intended to have our POWER user (B, in my case) pump himself up and take care of one of the monsters. Unfortunately, POWER doesn't quite cut it and it still takes two turns to defeat a monster. The only benefit I saw to this was that it saved one charge of GAZE to use on the SABERCAT at Mileille. Other than that, it costs an extra half-turn, involves more text, and is otherwise slow (HAMMER attacks take forever to animate and I don't know why). I decided instead to use up GAZE completely and head straight for the Hidden Town to pick up bSTONE after fighting the Trio. This is a slightly more circuitous path (maybe 20 steps max) than picking it up when the Floating Castle is next to Jeanne's hideout, but I figure the glider is so fast (4 frames per step) that it makes very little difference. Come to think of it, though, perhaps the plan was to give our POWER user the BATTLE sword. That should take care of one of the monsters in one hit. I still think it would take too long to unequip it and re-equip it, then wait for the text and animation as B POWERs up and swings the sword, but it's worth testing. I'll check on that eventually. Anyway, I'm currently contemplating how to get to BYAK-KO's castle and how to manipulate luck once I'm there. It's 70 steps from the Hidden Town to the castle, but I only have 44 steps before my next battle, which means another bout of manipulating luck on the fly. Worse, I don't know if I should try to manipulate luck for the SABERCAT battle now or put it off until I'm in the castle. Manipulating for it now might be tricky and it might cause the NPCs to conspire against me. I really don't know what to do about them. There are a few hallways where it would be great if they'd walk south, but you and I both know that's not easy to do. I might try to stop walking strategically to put C34F and C369 in a better spot, but I'm not confident I can do that smoothly. Oh wait. The only way I can get a +5 Mana bonus in the third slot while fighting one SABERCAT is by having C30B = 242 (I can get it for the second slot with C30B = 243, which is just as bad). That's not going to give me nearly enough steps to reach the castle, so it looks like I'll be manipulating luck just to avoid a random battle. At least that answers one question.
FatRatKnight wrote:
The RNG is affected by what the CPU is doing internally. Generally, the music itself has a significant impact on the RNG. So does many different actions like exiting the menu. Part of the reason why I was considering using the Sound Test, of all things, to manipulate luck.
Ah yes, I remember you saying something about that and not being clear why the Sound Test would help. It's a cute idea, but given the 60-odd frames just to reach the title screen after any soft reset, I think it would be more efficient just to reset twice. If you're still interested in manipulating luck with the Sound Test, I'll leave it to you.
FatRatKnight wrote:
One test: Party of mutants, F F M F and see how the random battles go from there. At SEI-RYU, the M must die (two Fs will have POWER and would steal his HAMMER away from him). At monster trio, the fodder F must die. After BYAK-KO, anything goes after we get our manly man.
Just curious: Why does the Mutant M need to die and why does the fodder need to die? (I know at least one of them has to.) Also, what potential advantages do you see to having a female POWER wielder? In any case, it's a cute idea. Oh, if you're looking for the POWER user to go sooner in battle, why not make it F F M M? Last I checked, our fodder doesn't actively do anything.
FatRatKnight wrote:
The third test: Party of mutants, F M F F. Swap first with fourth so the GAZE user is the one with the extra Agility. What you've been doing, with several mistakes.
Who? Me? What? Mistakes? No! Never! I'm perfect! I'm going to toy around with your Lua scripts. I wasn't able to decipher them by looking at them, so I'll just see what they can do for me. One more thing: if the game is too slow for you, set C5B2 and/or C5B3 equal to 4 for instant glider effect. Those are the bytes for walking speed (crazy things happen if you set it greater than 16). If you want the thrill of being inside the glider, change CCC1 to 2. I don't recommend it, though, as it was never meant to traverse the terrain in World 1 and therefore stays frozen in place.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Bobo the King wrote:
Just curious: Why does the Mutant M need to die and why does the fodder need to die? (I know at least one of them has to.) Also, what potential advantages do you see to having a female POWER wielder? In any case, it's a cute idea.
There is one chief reason for killing MUTANT M at SEI-RYU: The first party member will only go first against a higher speed enemy if the fourth member has lower Agility than the first member. If I don't give GAZE to the starter MUTANT F, I'm stuck with a slightly slower secondary MUTANT F. The only member that would be slower than this one would be the MUTANT M. When a party member dies, they go straight to the fourth slot. If someone with too high an agility is dead and thus forever stuck in slot 4 (reviving takes too long), this kills whatever chances I have of ever going first against the SABERCATs, BYAK-KO, and ATOM ANTs. The outcome is rather bleak for the survival of our heroes. The only real advantage in having two F POWER users instead giving one to the M is so I can kill the M off in this fashion. The remaining fodder is put to good use as a humanoid body shield at Monster Trio. After Monster Trio, our party should be: 1) GAZE gal, with Agility higher than member 4. 2) Spare POWER mutant. Useless beyond Monster Trio, though. 3) Dead fodder. Tragic loss at the Monster Trio. 4) Dead since SEI-RYU. Must have less Agility than member 1. There's 3 ways to have Member 4 be slower than Member 1: - The dead member is the slow MUTANT M, thus slower than anyone else. - The intended STONE book user is the starter MUTANT F, thus faster than anyone else. - Fight an extra battle so that Member 1 ultimately has more Agility than Member 4. Equal Agility won't work. Member 1 must have higher Agility than Member 4. This is to get around the strange turn order system the game uses. The second and third slots don't really matter, from what I recall of my basic tests. EDIT:
Bobo the King wrote:
I'm going to toy around with your Lua scripts. I wasn't able to decipher them by looking at them, so I'll just see what they can do for me.
Those two are just stand-alone functions. Stick them together under one file, call the "trigger" known as ScanReset2() every frame, and gui.text the variable str somewhere.
Bobo the King wrote:
My Monster Trio strategy differed slightly from what I believe is the prescribed method. As I recall, you intended to have our POWER user (B, in my case) pump himself up and take care of one of the monsters. Unfortunately, POWER doesn't quite cut it and it still takes two turns to defeat a monster. [...]
The plan for the Monster Trio is to hand the BATTLE sword to the spare POWER mutant. The GAZE mutant isn't using it, is she? On the other hand, I can't remember if even the BATTLE sword is enough...
Player (79)
Joined: 8/5/2007
Posts: 865
FatRatKnight wrote:
There is one chief reason for killing MUTANT M at SEI-RYU: The first party member will only go first against a higher speed enemy if the fourth member has lower Agility than the first member. If I don't give GAZE to the starter MUTANT F, I'm stuck with a slightly slower secondary MUTANT F. The only member that would be slower than this one would be the MUTANT M. When a party member dies, they go straight to the fourth slot. If someone with too high an agility is dead and thus forever stuck in slot 4 (reviving takes too long), this kills whatever chances I have of ever going first against the SABERCATs, BYAK-KO, and ATOM ANTs. The outcome is rather bleak for the survival of our heroes. The only real advantage in having two F POWER users instead giving one to the M is so I can kill the M off in this fashion.
You seem to be up on things, but as a reminder, I think you should equip the HAMMER as you're unequipping the SABER. Any known or projected disadvantages to the third strategy, the one I'm testing? The only one I can think of is that it requires shuffling party members before the first battle, but that shouldn't cost too much time.
FatRatKnight wrote:
Those two are just stand-alone functions. Stick them together under one file, call the "trigger" known as ScanReset2() every frame, and gui.text the variable str somewhere.
It's giving me an error because MR is undefined. I have this gut feeling I could probably deduce what it is, but I'm just not seeing it at the moment. I was able to make sense of your ScanReset2 function, however. That was pretty clever-- I didn't know there was a RAM address devoted to controller input (and I figured if there were one, it wouldn't update every frame, given how "sticky" the controls seem to be in this game).
FatRatKnight wrote:
The plan for the Monster Trio is to hand the BATTLE sword to the spare POWER mutant. The GAZE mutant isn't using it, is she? On the other hand, I can't remember if even the BATTLE sword is enough...
You've probably already researched it yourself, but according to this FAQ (which I recall you referenced in the past), the BATTLE sword should do fine against most of the enemies (I'd GAZE the GARLIC just to be on the safe side). After all, it did 230 damage against SEI-RYU and we know this trio has about 200 HP each, so unless their defense is significantly higher than SEI-RYU's, it should OHKO them. However, I'm standing by my strategy and I think it's time I put some numbers to it. Jeanne's hideout is at x=29, y=31 and the Hidden Town is at x=52, y=8. Both strategies start by moving down one tile to exit the hideout, down again to enter the glider, and down a third time to leave the forest enclave, so those steps don't need to be counted. From this point (x=29, y=34), the Hidden Town is 49 steps away for a total of 392 frames round trip. Finally, after escaping jail, the glider reappears at x=39, y=53. You travel north until you're aligned with the bottom edge of the castle (which is a 2x2 block at x=27-28, y=30-31), then take a sharp left traversing nine tiles to bring you to x=30, at which point you dismount the glider and enter the castle. Those last nine steps take an additional 36 frames, bringing the grand total to 428 frames. Note that the path I've listed doesn't make any literal sense, but it works fine if all you want is the step total. The alternative strategy is to put off buying bSTONE until the castle is closer. We don't need to take into account anything before the jailbreak (that was all accounted for above), but we do need to count steps after the paths diverge at x=39, y=31. From there, it's 36 steps to the Hidden Town, taking 144 frames to traverse. The return trip takes you to x=28, y=28 where you dismount and enter the castle. That's another 44 steps and 176 frames, bringing the grand total to 320 frames. The difference in transit time between the two strategies is 128 frames. That should be the exact amount-- in all respects other than what I've listed above, the two paths are identical. What this means, of course, is that my "depleting GAZE and getting bSTONE early" strategy has to save 128 frames elsewhere. One obvious place is in juggling the BATTLE sword. I ran a quick test, unequipping an item and re-equipping it on a second character. It took 160 frames to do so. The only other place where GAZE might save a little time is in its battle animation. I believe the GAZE animation is a little bit faster than bSTONE's. However, that does not take into account the much faster actions of the Mutant M during the Monster Trio battle (look at my run for evidence). All in all, I'd say it's pretty clear which strategy is faster. You're welcome to try whatever you want in your own test run(s), but I'll need some serious convincing to use the BATTLE sword in the final run. Meanwhile, back at my run... Stuck at Mileille. I keep doing tests and finding that my results in practice are one value off from what I thought they were from the test. Case in point, I thought that C353 = 252 would spawn one SABERCAT. Not so. It spawns three. That significantly depressed the odds of getting the result I wanted. No problem, I thought, I'll just shoot for a +5 Mana bonus in the second slot. As luck would have it, the SABERCAT struck first, taking out B (who had outlived his usefulness) and bringing A into the second slot for the purposes of picking up the boost. Too good to be true, yes? Of course, the RNG conspired against me to put an ability shuffle in slot 1, shuffling D's abilities. Care to hazard a guess as to what that did to her TELEPOR uses? I found myself in jail with no means to TELEPOR out. The only good news that comes to mind here is that this will not be an issue when we put TELEPOR on A or B (preferably A). I'm going to have to go back tomorrow and see if there's anywhere along the tower climb when I can manipulate the results I want. I think I'm going to have to shoot for a slot 1 Mana bonus, which should be quite difficult. By the way-- when you tested who goes first in battle, did you use a spell or a weapon? I mentioned at one point that using P-Blast in an earlier run caused my mutant to often strike last in battle. After buying and equipping bSTONE, I had the same problem against the SABERCAT. I don't know if it was just a fluke, but it does concern me, especially with the BYAK-KO battle approaching. Also, we have one "free" battle in World 1 from which we need a single additional Mana bonus for our GAZE user. I was thinking that if we can't get TELEPOR on her immediately, maybe we could fight that extra battle to get a fresh set of RNG values that might work better. My only concern is that we'd still have to learn TELEPOR by the time we beat P-FROG, so we can't waste too many steps. If the extra battle doesn't help us there, we can use it any time we're having trouble manipulating luck.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
MR? ... I have a habit of renaming functions and forgetting to include the rename... MR is short for Memory Read. That was my train of thought, anyway, and I'd rather not have to retype the entire function name every time. A single line at the top of the file can fix that: MR= memory.readbyte Note: No parentheses. I'm not calling the function. I'm assigning the function itself to MR, not whatever value the function spits out. Nowadays, I've gotten a new habit of using R1s, R1u, R2s, R2u, R4s, and R4u. Any guesses here? The fact your values are "off by one" for C353 is just fine, actually. Monsters are put in one of three slots, and even if the other slots are empty, the game still rolls for those slots. If the monster was placed in slot 1, the game's first roll is given to that monster, whereas if the monster was in slot 3, the third roll is given instead. It's not really visible which one of three slots the monster is in from the battle screen, though. If nothing else, eat the extra line of text and GAZE or STONE them anyway. The BATTLE sword, in my plans, are never given to the GAZE mutant in the first place -- She uses the HAMMER versus SEI-RYU. The spare POWER mutant gets the BATTLE sword. I was using the STONE book against BYAK-KO in my Agility test -- What else would I use? Have you tried putting the Arts & Crafts mutant (STONE book wielder. Just in case you haven't figured out what I mean by art by now) in slot 1 directly? There's no avoiding placing the mutant in slot 1 eventually. I'm not sure if that extra battle in W1 is so "free". The TELEPOR route requires that you switch twice to pick up the various Mana boosts. There's also the fact that TELEPOR is given to the third member, and you most certainly must swap the spare POWER mutant at least once to get that and POWER. Three swaps are required here, with three menu transitions likely. Not eating the extra swaps, we can spare a Mana+5 for a Mana+4, or even Mana+3 if we're giving GAZE to the starter MUTANT F. Even if we are still restricted to a Mana gain, we have more possible values to use.
Player (79)
Joined: 8/5/2007
Posts: 865
FatRatKnight wrote:
MR? ... I have a habit of renaming functions and forgetting to include the rename... MR is short for Memory Read. That was my train of thought, anyway, and I'd rather not have to retype the entire function name every time.
Oh, heh heh. I didn't expect it to be just one command! Okay, I've fixed it, and it works fine.
FatRatKnight wrote:
The fact your values are "off by one" for C353 is just fine, actually. Monsters are put in one of three slots, and even if the other slots are empty, the game still rolls for those slots. If the monster was placed in slot 1, the game's first roll is given to that monster, whereas if the monster was in slot 3, the third roll is given instead. It's not really visible which one of three slots the monster is in from the battle screen, though.
Ah, that makes sense! ... At least... it almost does... I wish they'd programmed this game in any sensible way. Glad to hear I'm not the one who's insane, at least.
FatRatKnight wrote:
The BATTLE sword, in my plans, are never given to the GAZE mutant in the first place -- She uses the HAMMER versus SEI-RYU. The spare POWER mutant gets the BATTLE sword.
That makes a lot more sense than what I was thinking.
FatRatKnight wrote:
I was using the STONE book against BYAK-KO in my Agility test -- What else would I use? Have you tried putting the Arts & Crafts mutant (STONE book wielder. Just in case you haven't figured out what I mean by art by now) in slot 1 directly? There's no avoiding placing the mutant in slot 1 eventually.
I was just being cautious, but it's good to know I need A in slot 1 for BYAK-KO. I was trying to put her in slot 2 for the SABERCAT battle and I didn't realize that would mean shuffling the party again later. Looks like I have to manipulate a Mana bonus for slot 1 unless I want my execution to be really sloppy.
FatRatKnight wrote:
I'm not sure if that extra battle in W1 is so "free". The TELEPOR route requires that you switch twice to pick up the various Mana boosts. There's also the fact that TELEPOR is given to the third member, and you most certainly must swap the spare POWER mutant at least once to get that and POWER. Three swaps are required here, with three menu transitions likely.
Hmmm... that's a very good point and I suppose it sheds some light on why you're interested in testing all three possibilities. I figured that the strategy in my current run doesn't sacrifice any net frames to pick up the Mana bonuses, but there may be excessive shuffling in there. However, I don't think it's more time than it takes to fight a battle. I'm interested in what you discover. For the record, though, the Mana bonus is indeed "free" in the sense that we can fight that battle almost anywhere and it may be best to do so when we're having trouble manipulating luck for another desired result (say, walking a large number of steps without a battle).
FatRatKnight wrote:
Not eating the extra swaps, we can spare a Mana+5 for a Mana+4, or even Mana+3 if we're giving GAZE to the starter MUTANT F. Even if we are still restricted to a Mana gain, we have more possible values to use.
Sorry, I don't follow this point. Is this because our starter Mutant F picks up a Mana +4 boost at some point (without shuffling) and we therefore don't have to manipulate +5 boosts consistently to reach 46 Mana before BYAK-KO? Run update: (Drumroll, please!) No progress. Well, not quite. I spent the morning writing a Lua script/bot that will walk, save, and reset exactly as we would if we were playing (unless NPCs get in the way). Not to boast, but I'm really proud of this one! It's my baby!
Language: lua

local function any(vector,value) local match=false for i=1, #vector do match=(match or vector[i]==value) end return match end --Apparently Lua doesn't have a built-in sign function... local function sign(x) if x>0 then return 1 elseif x<0 then return -1 elseif x==0 then return 0 else return "error" end end local function makerngtable() n={[0]=3,6,7,13,11,1,15,10,12,9,8,2,4,14,0,5} r={[0]=10,11,1,12,5,9,6,3,15,11,2,7,2,0,15,15} s={[0]=5,15,13,9,5,1,13,11,1,3,3,7,11,15,3,9} local RNGTable={} for c=0,15 do for i=0,7 do currrow=(r[c]+i*s[c])%16 currnum=n[c]+32*i RNGTable[16*currrow+c]=currnum end for j=0,7 do currrow=(r[c]+8+j*s[c])%16 currnum=255-n[c]-32*j RNGTable[16*currrow+c]=currnum end end return RNGTable end local function printcolumn(startx,starty,data) for i = 1,#data do gui.text(startx, 9*i + starty + 1, data[i]) end end local function setcourse(waypointsx,waypointsy,wpnum) local index=wpnum+1 local x=memory.readbyte(0xCCC9) local y=memory.readbyte(0xCCC8) local lr = sign(waypointsx[index]-x) local ud = sign(waypointsy[index]-y) if lr==1 and ud==0 then go = {right=1} elseif lr==-1 and ud==0 then go = {left=1} elseif lr==0 and ud==1 then go = {down=1} elseif lr==0 and ud==-1 then go = {up=1} else go = {} end return go end --This was tested to be the fastest method for saving. Hopefully, it holds everywhere. local function pauseandsave() for i = 0,1 do joypad.set(1,{start=1}) vba.frameadvance() end for i = 2,13 do vba.frameadvance() end joypad.set(1,{down=1}) vba.frameadvance() joypad.set(1,{down=1, A=1}) vba.frameadvance() joypad.set(1,{A=1}) vba.frameadvance() for i = 17,18 do vba.frameadvance() end for i = 19,20 do joypad.set(1,{A=1}) vba.frameadvance() end end --Increments wpnum only when the next waypoint is reached local function incwpnum(waypointsx,waypointsy,wpnum) local x=memory.readbyte(0xCCC9) local y=memory.readbyte(0xCCC8) if waypointsx[wpnum+1]==x and waypointsy[wpnum+1]==y then wpnum=wpnum+1 end return wpnum end local function reset() for i = 1,2 do joypad.set(1, {A = 1, B = 1, select = 1, start = 1}) vba.frameadvance() end end local function idleframes(n) for i = 1,n do vba.frameadvance() end end --Walk in a given direction until ready to save. local function walk(go) local speed=memory.readbyte(0xC5B2) local framesperstep=16/speed local startcoords=memory.readbyte(0xCCC8)+memory.readbyte(0xCCC9) while (memory.readbyte(0xCCC8)+memory.readbyte(0xCCC9))==startcoords do joypad.set(1,go) vba.frameadvance() end for i=1, framesperstep-2 do joypad.set(1,go) vba.frameadvance() end end local function condition(address1,address2,address3,RNGTable) local value1 = memory.readbyte(address1) local value2 = memory.readbyte(address2) local value3 = memory.readbyte(address3) local roll1=RNGTable[value1] local roll2=RNGTable[value2] local roll3=RNGTable[value3] --This is the super-duper important condition. Fiddle with this to test various conditions. local reportit=(any({23,137,203,161,230,243},value1) and roll3<64) return reportit end --Boilerplate... local firstrng = 0xC300 local nextbonus = 0xC30B local canrun = 0xC323 local abslot = 0xC36F local enctrgrp = 0xC343 local stepstobattle = 0xC33B local numenemies = 0xC353 local RNGTable=makerngtable() local state1=savestate.create() local state2=savestate.create() local state3=savestate.create() local rngvals={} local framevals={} local xcoords={} local ycoords={} local wpnumlist={} local enumerate={} local ind=1 movie.rerecordcounting(false) --Addresses to be luck manipulated. I figure we won't have to manipulate more than three at a time. local address1 = nextbonus local address2 = stepstobattle local address3 = numenemies --Decides what RAM address will be output in the end. local rngofinterest = nextbonus --Waypoints to be traversed. Because the player moves orthogonally, they should be staggered so only the x or y coordinate changes with each index *except* when the room changes. --(Might be possible to take into account the room-- byte CCC7-- but it's a little complicated.) local waypointsx={12,13,13,14,14,15,15,19,19, 7, 7,19} local waypointsy={11,11,12,12,13,13,14,14, 5, 5,14,14} --The waypoint number. Set to the index of the current waypoint or, if the first waypoint has yet to be traversed, just set it equal to 0. local wpnum=1 --The encounter rate and the duration in frames over which to manipulate luck. local encounterrate=4 local framesduration=1000 local maxlength=5 local startframe=vba.framecount() local nextbattlerng=memory.readbyte(0xC33B) savestate.save(state1) while (not (RNGTable[nextbattlerng]<encounterrate)) and ((vba.framecount()-startframe)<framesduration) and (wpnum<#waypointsx) and (#rngvals<maxlength) do savestate.save(state2) pauseandsave() for i=1,16 do savestate.save(state3) currframe=vba.framecount()%1000 currx=memory.readbyte(0xCCC9) curry=memory.readbyte(0xCCC8) reset() idleframes(5) if condition(address1,address2,address3,RNGTable) then rngvals[ind]=memory.readbyte(rngofinterest) framevals[ind]=currframe xcoords[ind]=currx ycoords[ind]=curry wpnumlist[ind]=wpnum enumerate[ind]=ind ind=ind+1 end savestate.load(state3) vba.frameadvance() end savestate.load(state2) local go = setcourse(waypointsx,waypointsy,wpnum) walk(go) wpnum = incwpnum(waypointsx,waypointsy,wpnum) nextbattlerng=memory.readbyte(0xC33B) end savestate.load(state1) gui.text(1,1,"#") gui.text(20,1,"RNG") gui.text(40,1,"Frame") gui.text(70,1,"x") gui.text(90,1,"y") gui.text(110,1,"Waypoint") printcolumn(1,1,enumerate) printcolumn(20,1,rngvals) printcolumn(40,1,framevals) printcolumn(70,1,xcoords) printcolumn(90,1,ycoords) printcolumn(110,1,wpnumlist) emu.pause()
In Matlab, I try to keep my variables at or very near the top of the code for easy changing if necessary, but Lua insists that functions come first, so most of the good stuff is in the middle. There are only a few lines you need to be actively concerned with. First is in the function "condition". This is the condition that is true if the luck was manipulated such that we got the result we wanted. For example, if you're looking for C30B = 23 while C33B = 161, you'd alter the line defining "reportit" accordingly. I have it set up so that we can check up to three addresses or their corresponding RNG rolls simultaneously (I figure that's the most we'll ever need to manipulate, though it can be very easily expanded). The other variables you might want to change come after the "Boilerplate" section. The variables "address1", "address2", and "address3" are the ones passed on to the condition function. Unfortunately, you have to scroll back and forth between the function and those variables, coordinating their addresses with the condition you set. I'm sorry I don't know how to keep them tidier-- I tried to at least keep them as close as I could to each other. Next is "rngofinterest". This is the address that's reported to you in the summary of the results. I may make it so that it can report multiple addresses or the fractional offset, but since the condition is already verified, what we're really interested in is the frame number at which to reset. After that come the "waypoint" variables. I encoded them as two vectors, but they should really be interpreted as a 2-by-n array. The first vector contains the x-coordinates of each waypoint while the second vector contains the corresponding y-coordinates. For the default example above, the player starts at coordinates (12,11), then moves one step right to (13,11), then one step down to (13,12), and so on. As you can see, moving more than one step at a time is supported, but you need to make sure that only the x or y coordinate (not both!) changes from element to element. The program might still work when changing "rooms", which would mean the waypoints vectors would break this pattern, but I haven't yet tested it. Along with the waypoint vectors comes "wpnum", the waypoint number, which is an index to the above vectors. This number corresponds to the last waypoint visited, including where the player is currently standing (if applicable). For example, if the player is at (19,10) according to the default vectors, wpnum should be something like 8, which corresponds to the coordinates (19,14). Choose a wpnum that isn't orthogonal to the next waypoint and the program doesn't do anything. Also, you can set wpnum to 0 if you haven't reached the first waypoint. Finally, there are the "encounterrate", "framesduration", and "maxlength" variables. These are fairly self-explanatory. I've set four conditions for halting the program and returning the results: 1) The next step will trigger a battle. (Can't very well save and manipulate luck if we're in the middle of a battle, hm?) Because "encounterrate" is just one value (I wasn't able to find a RAM address for terrain), the program will only work perfectly if we don't move from one terrain to another. I figure that exceptions will be rare and can be checked easily enough individually. 2) Too many frames have passed. I usually set framesduration to 1000 for the sake of sanity, but if you want to manipulate the hell out of luck, set it to something ridiculous like 100000. 3) We've reached the end of the waypoints. No point attempting to manipulate luck if there's nowhere else to move. 4) We've obtained enough results. This is especially useful if we want just one result and we don't care when we get it. For example, I'm currently climbing the Floating Castle and if I get a +5 Mana bonus in the first slot with one expected SABERCAT anywhere along the climb, I don't need to manipulate luck any further. In that case, I would set maxlength to 1. You may be wondering about whether this program works. I'm pretty sure it does. I've checked most important aspects of it and tested it in the field. You can too. The default numbers correspond to the player standing at the base of the tower. Just step outside the Base Town and run the script. The player should walk in a zigzag pattern down and right, then walk along the perimeter of the enclosure (if there isn't a random encounter in the meantime). Go ahead and run that and tell me how it works for you. Toy around with new waypoints and even use it in the glider or on the bike! You're my beta tester. If you have any questions, comments, or suggestions to make the script better, I'd be happy oblige. Edit: I updated my code slightly. It now reports the x position, y position, and last waypoint reached. You can pass these values on to a new script I wrote:
Language: lua

local function any(vector,value) local function sign(x) local function makerngtable() local function setcourse(waypointsx,waypointsy,wpnum) local function pauseandsave() local function incwpnum(waypointsx,waypointsy,wpnum) local function reset() local function idleframes(n) local function walk(go) local function condition(address1,address2,address3,RNGTable) --Boilerplate... local firstrng = 0xC300 local nextbonus = 0xC30B local canrun = 0xC323 local abslot = 0xC36F local enctrgrp = 0xC343 local stepstobattle = 0xC33B local numenemies = 0xC353 local RNGTable=makerngtable() local ind=1 --Addresses to be luck manipulated. I figure we won't have to manipulate more than three at a time. local address1 = nextbonus local address2 = stepstobattle local address3 = numenemies --Waypoints to be traversed. Because the player moves orthogonally, they should be staggered so only the x or y coordinate changes with each index *except* when the room changes. --(Might be possible to take into account the room-- byte CCC7-- but it's a little complicated.) local waypointsx={24,24,22,22,52,52} local waypointsy={12,13,13, 6, 6, 2} --The starting waypoint number. Set to the index of the current waypoint or, if the first waypoint has yet to be traversed, just set it equal to 0. local wpnumstart=2 --The four variables that indicate when to reset. These should be obtained from the luck manipulation bot. local frame=60647 local wpnumfinish=3 local x=22 local y=10 local wpnum=wpnumstart while wpnum<wpnumfinish do go = setcourse(waypointsx,waypointsy,wpnum) walk(go) wpnum = incwpnum(waypointsx,waypointsy,wpnum) end while math.abs(memory.readbyte(0xCCC9)-x)+math.abs(memory.readbyte(0xCCC8)-y)>0 do go = setcourse(waypointsx,waypointsy,wpnum) walk(go) end pauseandsave() while vba.framecount()<frame do vba.frameadvance() end reset() idleframes(62) for i=1,2 do joypad.set(1,{A = 1}) vba.frameadvance() end idleframes(10) if condition(address1,address2,address3,RNGTable) then gui.text(1,1,"Success!") else gui.text(1,1,"Failure?") end vba.pause()
I've omitted the subroutines (or whatever you want to call them) but left their names in. They should be exactly the same ones from the first Lua script in this post. This script is pretty simple-- it just executes the specified save and reset from the previous script, then checks that the result was correct. I really wanted to make it so that you could select a particular result and execute it all in one script (so we don't have to juggle values between scripts), but I apparently don't have the know-how. Perhaps you could be of assistance? I think I've got 90% of the necessary code already. Edit 2: I screwed up my example in fiddling with my program. It should be fixed now.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Sorry, I don't follow this point. Is this because our starter Mutant F picks up a Mana +4 boost at some point (without shuffling) and we therefore don't have to manipulate +5 boosts consistently to reach 46 Mana before BYAK-KO?
The Mana growths are as follows: Starting Mana: 6 or 5 +5 from GAZE manipulation +2 from TELEPOR manipulation, no swaps +5 from KINGSWRD +5 from cronies +5 from STEWARD +5 from SEI-RYU +5 from MOSQUITO +5 from Monster Trio +5 from SABERCAT 48 or 47 Mana. Target: 46 The starting Mana of 6 would be the starter mutant's, while the starting Mana of 5 would be from some other mutant. Now, since we will ultimately have 2 or 1 more Mana than we actually need, we can replace one of these +5s in the list with a +4. In the case where we have 2 extra Mana, we can replace two of these +5s each with a +4, or replace one +5 with a +3. We still can't get rid of any Mana bonuses. We can only replace a full +5 with a lesser bonus. There's probably plenty of +4s out there that, in turn, lets us choose different RNG values for other things, like number of enemies to fight. This is what I mean. As for lua scripting... I'm good with informational HUD scripts and have some capability with cheat scripts, but botting isn't my strong point. I'll be taking a look. You want to tie it all into a single script, I take it? Not sure what I can do, but no one gets anywhere without trying first, right?
Player (79)
Joined: 8/5/2007
Posts: 865
I was thinking we could do it the bass-ackwards way and have it all manipulated by the controller input. For example, hold B and press a direction to select a result, then release B to choose it. I tried halfheartedly to make it all one script, but the problem was that I ended up on the last frame, which is paused. Since it's paused, you can't input anything, but I didn't want it to unpause in case the user didn't want to pick any of the options. I feel "none of the above, return control to the user" should be an option. Edit: Also, I've been compiling a list of waypoints so that we can just copy and paste them into the script as needed. So far, I'm most of the way through World 1. Here's what I've got:
Language: lua

--Start to guild. local waypointsx={18,18,21,21} local waypointsy={36,26,26,25} --Guild to field. local waypointsx={21,21,18,18} local waypointsy={25,26,26,37} --Tower to Bandit Cave. local waypointsx={12,19,19,22,22,46,46,42,42,45,45,43,43,44,44,42,42,38,38} local waypointsy={11,11, 7, 7,25,25,39,39,43,43,45,45,50,50,56,56,58,58,52} --First floor of Bandit Cave. local waypointsx={16,16,18,18,26,26,30,30} local waypointsy={13,14,14, 8, 8,21,21,23} --Second floor of Bandit Cave. local waypointsx={18,18, 8, 8} local waypointsy={36,51,51,50} --Third floor of Bandit Cave (to P-FROG). local waypointsx={61,61,48,48} local waypointsy={54,53,53,43} --(Base Town to field checked manually.) --Tower to Castle Armor. local waypointsx={12,19,19,22,22,46,46,37,37,44} local waypointsy={11,11, 7, 7,22,22,17,17,13,13} --First floor of Castle Armor (no NPC manipulation). local waypointsx={15,15,17,17,16,16,20,20,23,23} local waypointsy={30,22,22,17,17,13,13,11,11, 8} --Second floor of Castle Armor. local waypointsx={53,53,50,50,49,49,46,46} local waypointsy={16,14,14,22,22,29,29,28} --Third floor of Castle Armor. local waypointsx={45,45} local waypointsy={50,43} --(Base Town to field checked manually.) --Tower to Castle Sword. local waypointsx={12,19,19,21,21, 1, 1, 2, 2, 7, 7,14,14,18,18,27,27,26} local waypointsy={11,11, 7, 7,21,21,36,36,41,41,53,53,57,57,61,61,44,44} --First floor of Castle Sword. local waypointsx={12,12,18,18,16,16,11,11,14,14,12,12} local waypointsy={32,29,29,21,21,12,12,16,16,21,21,19} --Second floor of Castle Sword (no NPC manipulation). local waypointsx={36,36,38,38,40,40,36,36} local waypointsy={12,11,11, 6, 6,23,23,21} --Third floor of Castle Sword. local waypointsx={53,53} local waypointsy={17,10} --Outside WHITKEY room to Mileille's door. local waypointsx={24,24,22,22,52,52} local waypointsy={12,13,13, 6, 6, 2} --Test pattern. Use just outside tower, zigzags right and down, then marches around perimeter of enclosure. local waypointsx={12,13,13,14,14,15,15,19,19, 7, 7,19} local waypointsy={11,11,12,12,13,13,14,14, 5, 5,14,14}
Tell me if you want to chip in some of the work. I've pretty much got it covered, but in case you want to help, I just want to make sure we're not both going for the same ones.
Player (79)
Joined: 8/5/2007
Posts: 865
Through BYAK-KO! That was a chore! I wanted to manipulate it so that I got a +5 Mana bonus in the second slot and there was one SABERCAT. I was able to do it, but one of the NPCs stepped in my way. Then I realized I forgot to unequip the BATTLE sword and equip bSTONE, so I tried another option while taking the time to equip it. Fortunately, it didn't throw off the RNGs and I just barely squeaked by the NPC. After that, things were much more straightforward. I screwed up luck manipulation before entering the castle the second time-- I shot for the maximum number of steps in the glider instead of the maximum number of steps in the tower (where I was going to end up anyway in about three steps). I'm currently debating whether I should go back and redo that segment. In any case, rejoice! The hard part of my test run is over! I know it's not perfect, but there was really only one significant mistake (giving TELEPOR to D). And now that I've got that new luck manipulation bot, we can manipulate luck the professional way. Things are really shaping up now! We have a few more questions to resolve in World 4. After answering them, I think we should start to lay out our final plans with special attention to detail regarding party shuffling. We should be aware when we need to shuffle, how much we need to shuffle, when we can get away without shuffling, and how difficult it will be to manipulate it so. Except for a few open questions/disputes, I don't think we waste any time in battles, on the overall route, or even much time on luck manipulation, so that leaves party rearranging as the largest remaining time-waster. As for combining those two scripts, I was thinking of giving it a second shot. I might be able to do it without too much trouble, though GUIs have never been my strong suit. If you're working on it, by all means continue-- what you come up with will probably be better than what I do. I just thought I'd give you a heads-up if you've been slaving away at it. Edit: And we should also think about how best to avoid meat in our few forced encounters. Some meat can't be avoided (SEI-RYU and encounters that require a very specific RNG), but I don't think we've given it any broader consideration.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Alright. I'm getting myself confused over things. For now, I'll back away and clear my thoughts. Thing is, I got myself entangled on what each function should do and when they should be called. It's very easy to complicate things, so now I want to back away and go down another path that I can more easily see. As for user interface, one of my favorite functions for handling the user buttons would be like so:
Language: lua

local keys, lastkeys= input.get(), input.get() --So we have some default state --***************************************************************************** local function UpdateKeys() lastkeys= keys; keys= input.get() end local function Press(k) return keys[k] and not lastkeys[k] end --***************************************************************************** while true do UpdateKeys() if Press("numpad1") then SomeFunction() end if Press("numpad2") then SomeOtherFunction() end -- ... And so forth emu.frameadvance() end
It's too bad that VBA has no form of "run-while-paused" code, or else we could simply just tap the key without need of frame advance or leaving the emulation unpaused. And if you're frame advancing, the Press function loses some of its value of only returning true once for a held key. Kind of a problem, isn't it? For now, though, resets. What is involved in a reset, anyway? - Saving the game, of course. Resets are pointless if we lose our progress. - Resetting, naturally. - - How we reset may differ, in order to get a different set of RNGs. - Checking to see whether we have the right values. Saving the game. That can generally be assigned as a sort of a macro. Not much to do other than a fairly predictable sequence of key presses. Resetting. We can trigger a reset fairly easily, as a reset macro would simply consist of four buttons being held for two frames. But the timing of a reset might need to change, or some conditions leading up to the reset might also be desirable to change. Part of the reason why we want a bot to handle this, right? We could go with a straight "delay x frames", or we could have the bot do other actions. Although, a straight "delay x frames" isn't the fastest possible course some of the time, but how we might be able to deal with adding other actions? Checking conditions. Naturally, after our first reset, we want to actually scan the RNG to make sure it's what we want. Typically, we only need two addresses if it's confined to some range, but maybe we want something a little more at times. Finally, there's a little bit of overhead we may want to tie these together. A master function that figures out what to do. The reset function should have only one task, and that's to reset the game in some fashion. The checking function shouldn't even know that a reset took place, all it should do is look at memory and report whether it matches. The overhead function tracks these things for us and makes the decision whether to call these other functions or go through another loop. But I'm kind of moving off in one direction (I... Kind of froze up looking at the botting). Generally, we should check ourselves if some RNG path can work. Will this party of F M F F be able to fight this battle given this RNG? If so, leave it up to the bot to find out how to get that RNG for us. We have a perfectly good cheat script that can modify the RNG for some checking ourselves without first going through the process of getting said RNG. Do we need to automate such actions of walking from one place to another? What about battles, even though we should already know the outcomes? There isn't a lot that botting could do that we can't do manually with little trouble, other than RNG manipulation, unless you believe there is something I'm missing. Incidentally, I just checked F F M F party. I cheated the RNGs up the way I desire them in my F F M F party. The second F dies to an ALBATROS in the Bandit Cave without any shuffling, so out with that plan. I can switch a pair of Fs so the GAZE mutant picks up TELEPOR instead of the spare POWER mutant, but this means a Mana+1 instead of a Mana+2, allowing no more than zero error for further Mana+5 gains. F M F F party seems to stand up just fine, with the middle two taking a beating but still breathing. I haven't checked the desirable RNG path for when we switch the starter F with the fourth F. I'll be sure to let you know what I find in manually checking things. I'll write down my observations of each run and let you know how things go. Most of it will be real time, until NPCs involve themselves, so I spend as little time as possible reworking the route.
Player (79)
Joined: 8/5/2007
Posts: 865
Personally, I find it kind of funny that you say botting is your weak point. I'm no expert at it, but it strikes me as really straightforward-- just construct a tree and every time a path branches off, save the game, check the result, then reload. At least, that's what I did for my bot. I'm sure there's formal computer science terms for it all, but I'm not a computer scientist. My favorite thing about botting, however, is that in principle, I only need to know savestate.save and savestate.load. Everything else is just reading memory, basic loops and conditionals, and frame advancing-- no obscure functions that I need to look up. Anyway, as long as one of us knows what to do at any given time, that's the important thing.
FatRatKnight wrote:
As for user interface, one of my favorite functions for handling the user buttons would be like so:
Yeah, I caught those functions in your RNG scanning script. I wanted to use them, but...
FatRatKnight wrote:
It's too bad that VBA has no form of "run-while-paused" code, or else we could simply just tap the key without need of frame advance or leaving the emulation unpaused.
...I ran into this exact problem. I guess I could have done something like hold down a key while advancing a frame, but that struck me as inelegant. Too many fingers involved.
FatRatKnight wrote:
Saving the game. That can generally be assigned as a sort of a macro. Not much to do other than a fairly predictable sequence of key presses.
You mean... like this?
Language: lua

local function pauseandsave() for i = 0,1 do joypad.set(1,{start=1}) vba.frameadvance() end for i = 2,13 do vba.frameadvance() end joypad.set(1,{down=1}) vba.frameadvance() joypad.set(1,{down=1, A=1}) vba.frameadvance() joypad.set(1,{A=1}) vba.frameadvance() for i = 17,18 do vba.frameadvance() end for i = 19,20 do joypad.set(1,{A=1}) vba.frameadvance() end end
All this function does is pause and save as quickly as possible. You can see its full of vba.frameadvance() and button presses. I tested this sequence of buttons and it couldn't be done any faster.
FatRatKnight wrote:
We could go with a straight "delay x frames", or we could have the bot do other actions. Although, a straight "delay x frames" isn't the fastest possible course some of the time, but how we might be able to deal with adding other actions?
What do you mean by "other actions"? Like various keypresses in the meantime? I don't think that would affect anything while we're in the menu. Please clarify if I'm not getting it.
FatRatKnight wrote:
Do we need to automate such actions of walking from one place to another? What about battles, even though we should already know the outcomes? There isn't a lot that botting could do that we can't do manually with little trouble, other than RNG manipulation, unless you believe there is something I'm missing.
I agree with you there. The second script I wrote (the one that executes the bot's output) can walk for us, but I see no reason to rely exclusively on it. It seems cumbersome to write up a script that will walk for us when we can just press keys or buttons to get where we're going. Battles are a little different. I think we can wing it, but there's something to be said for automating them. In particular, I'm looking to get the input perfect to the frame (from what I've seen, you share this goal) and that's going to mean selecting stuff in very predictable, tedious ways. The game's frame rules seem extremely consistent (I checked the lag frames and there are virtually none), so it's fertile ground for botting. The one drawback-- and it's a biggie-- is that we need a reference point. We need some RAM address that changes at the start of battle at a very consistent frame so that all subsequent actions can be chosen from it. From there, we would need to find RAM addresses that correspond to our selections so we can scroll through the battle menus efficiently. Once we've got all that set up, I figure we just tell the script to pick, say, the first option for the first character, the fourth option for the second character, and so on. I say we fight a couple battles manually (perfect to the frame), then decide if we want to write up a bot.
FatRatKnight wrote:
Incidentally, I just checked F F M F party. I cheated the RNGs up the way I desire them in my F F M F party. The second F dies to an ALBATROS in the Bandit Cave without any shuffling, so out with that plan. I can switch a pair of Fs so the GAZE mutant picks up TELEPOR instead of the spare POWER mutant, but this means a Mana+1 instead of a Mana+2, allowing no more than zero error for further Mana+5 gains.
Hmmm, in my run, my GAZE-wielding Mutant had no margin for error for Mana+5 bonuses, but I was actively shuffling her to pick them up. Did you pick up another +1 Mana gain somewhere? Since you can't upload your test (what with your script and all), how about listing the Mana gains you picked up and when? I want to know if you're fighting more battles than I did in my test run. I'm generally not too concerned about manipulating for Mana+5 bonuses over Mana+4 bonuses. They seem to be easy to pick up and as long as we can get either flavor, a Mana+4 bonus could only save as many as 15 frames. I guess it might save more if you can travel the next segment with one less reset, but just trying to account for that makes my head spin. Now, you'd expressed some confusion over how my script works. I'd like to walk you through the functions I wrote. A computer science professor at my college once told us his mantra was to write subfunctions such that any program can be coded in less than ten lines. As I said before, I'm no computer scientist, but I still took his advice somewhat to heart, hence the fragmented nature of my script. Also, I was able to successfully merge my two scripts into one bot that seems to work pretty well. The downside is that it's gone from two somewhat elegantly written programs to one monster script. I'll be describing the functions in my first script since there aren't a whole lot of fundamental changes in my new script (mostly copying and pasting, piecing together a UI, etc.), so you should be able to get a grip on the whole thing just from looking at the two old ones. The "any" function is just something that I dragged over from Matlab. All it does is check whether any element of the input "vector" is equal to the argument "value". You don't need to be too concerned with this-- I just wanted it so that the success condition can be more easily determined. I wrote "sign" because Lua doesn't have a sign function built in. If you can't follow my five lines of code for that function, you might as well stop reading now... I cannibalized "makerngtable" from an existing script. It just makes the RNG table that we know and love. I like it because it doesn't clog the code with a big table that takes up 17 lines. The "printcolumn" function just prints the elements of a vector in a column. Nothing fancy there. This is obviously used in the graphical output. (In my merged script, I've added an argument that highlights one of the rows by changing the color of the text if that index is equal to the specified input.) Things start to get interesting at "setcourse". This function compares our current x and y coordinates with where we're headed, then outputs a d-pad direction based on it. The output "go" is in a format that's ready to be used by VBA's built-in joypad.set. I already explained "pauseandsave" above. It's just 20 frames of frame advancing and pressing buttons. You can copy it to its own script and execute it frame by frame to see how it works. You can also press the buttons yourself to try to beat it (I bet you can't). The "wpnum" function is one I wrote near the very end. I realized that "wpnum" (the waypoint number) was incrementing too often and only needed to be incremented when the waypoint is reached. This function compares the x and y coordinates with the coordinates of the next waypoint. If they match, "wpnum" increments so that the program will know to walk to the next waypoint. After that come "reset" and "idleframes". You can see that "reset" just holds the required four buttons for two frames while "idleframes" advances n frames. Neither of these should be confusing. The "walk" function is a little tricky. The simplest explanation is that given "go" (from the "setcourse" function), walk just executes the corresponding button press. What's not so simple is when it stops. I discovered that when on foot, I could pause 14 frames after one of our coordinates incremented. Therefore, this function walks until one of the coordinates changes, then continues to walk for 14 more frames until returning to the main function. The only thing that complicates this rule is the different speeds you reach on the bike or in the glider. That's why I isolate the speed and the number of frames per step in the function. I have it set up so that it returns to the main function two frames before the coordinates will increment again, just when it is ready to be paused. Finally, there's "condition", which reads the addresses in question and tests some condition (that should be changed every time the script is executed). I've included the ability to test either the value of the RAM address (useful for things like the steps to next encounter RNG where we want it as low as possible above a threshold) or the roll itself (useful for things like the RNG for the number of enemies in a group). The condition itself is set on the line defining "reportit". Next are the variables. The first 20 or so lines just preset most of the variables. You should be pretty familiar with how that's done. Next up are the addresses that are to be manipulated. I gave all the addresses variable names in the previous section because I get really tired of looking up which address corresponds to what RNG. The following variable is "rngofinterest" which does not affect how the code executes but is merely the address that's displayed to the user after the script has executed. The two waypoints vectors may be a little hard to interpret. The first vector contains the x coordinate of each waypoint and the second vector contains the corresponding y coordinate. The waypoints should be read column by column. For the example I included in the script, the player starts at (12,11), then walks right to (13,11), then down to (13,12), and so on until reaching (15,14). He then walks right four steps to (19,14), then up to (19,5), left to (7,5), and so on. Since most of the walking we'll do is in straight lines, it makes a lot of sense to only encode the points at which we turn. The "wpnum" variable stands for the last waypoint we've been to. Since we might be somewhere in the middle of the group of waypoints we're covering, I made it so that we could just offset the index at which we start. If we've already been to the first three waypoints, then set "wpnum" to 3. Last among the variables are "encounterrate", "framesduratoin", and "maxlength". All three are used to determine when the script should stop. Now for the good stuff: the script that puts it all together. The basic idea is that there are three levels of branching. The first branch is right at the very start. We need to create a savepoint before anything executes so we can go back to the original state in case we don't like any of the options presented. After that comes a branch with each step we take. With every step, we need to determine whether we want to pause and save or continue walking. Thus, the script saves a savestate with each step, reloading it after it's explored whether it can manipulate luck then. Finally, after pausing and saving, it needs to determine how many frames it should wait before resetting. It saves a state each frame, resets, checks the results, loads the state, then advances one frame before doing it again. I have it do this sixteen times because it takes sixteen frames per step; if we get the result we want on the sixteenth frame, it makes sense to instead take another step and reset on the zeroth frame of the next step (assuming we're not in the glider). To start, the script establishes the starting frame, the RNG for when the next battle will take place, and saves the original state. The following "while" loop executes the meat of the program-- when the loop is finished executing, the results are output to the user. The loop will break when •the next step will be a random encounter (based on "encounterrate" and the RNG table) •too many frames (more than "framesduration") have passed •the player has reached the last of the waypoints •the maximum number of successful results ("maxlength") have been acquired. Until one of those conditions is satisfied, the player will keep walking (and saving, and resetting, and checking...). Before taking the first step, the script saves a state and pauses and saves. This is because we may wish to manipulate luck before our first step. Having saved, it creates a new savestate. The relevant data are acquired (the frame at which the game was reset and the x and y coordinates), then the game is reset. Five idle frames are inserted so that the new RNGs load. If these RNGs meet the conditions set by the user, the results are appended to a series of vectors to be referenced later and the index is incremented so the next result ends up in the right place. The program then loads the last state, then advances one frame and does it all over again. In this loop, it does this sixteen times, once for each frame it takes to walk one step. Now that the first sixteen opportunities to manipulate luck have been checked, it's time to take a step. The script reloads the state it made before it paused the game, then obtains the direction the player is supposed to go from this point from the "setcourse" function. This result is immediately fed to the "walk" function, taking the physical steps and effectively advancing the emulation 16 frames (if on foot). If a waypoint is reached, "wpnum" is incremented. The steps to next battle RNG is updated to see if the next step will trigger a battle. That pretty much does it for the program. At this point, the program will loop, saving over the second savestate, pausing and saving, then attempting to manipulate luck for another 16 frames. If the loop ends, the first savestate is loaded so that the player is back where they started (presumably where we want them). The last functions are straightforward-- they just display the results that have been obtained. Of course, it pauses the emulation after it all so that the user can ponder over the results. I hope that explains away any confusion you might have. If you're still getting hung up on basic points, ask away. I understand it can be really difficult to make sense of someone else's programs (I still can't decipher some of yours). As for my new script that combines the two old ones, I should at least give a short explanation of how it's used. After it executes, you end up at the usual table of frames and RNG values. If you hold B (the Game Boy button, not on the keyboard...) and resume emulation, one of the options will be highlighted. You can then press up or down (again, the Game Boy buttons) to select one of the options. Releasing B executes that option and tells you if it's been successful (I don't anticipate many any failures). If you don't hold B and just advance the frame, the script ends. Here it is-- it's a monster:
Language: lua

local function any(vector,value) local match=false for i=1, #vector do match=(match or vector[i]==value) end return match end --Apparently Lua doesn't have a built-in sign function... local function sign(x) if x>0 then return 1 elseif x<0 then return -1 elseif x==0 then return 0 else return "error" end end local function makerngtable() n={[0]=3,6,7,13,11,1,15,10,12,9,8,2,4,14,0,5} r={[0]=10,11,1,12,5,9,6,3,15,11,2,7,2,0,15,15} s={[0]=5,15,13,9,5,1,13,11,1,3,3,7,11,15,3,9} local RNGTable={} for c=0,15 do for i=0,7 do currrow=(r[c]+i*s[c])%16 currnum=n[c]+32*i RNGTable[16*currrow+c]=currnum end for j=0,7 do currrow=(r[c]+8+j*s[c])%16 currnum=255-n[c]-32*j RNGTable[16*currrow+c]=currnum end end return RNGTable end --Prints a vector's contents in a column. Additionally makes the nth row green to indicate selection. local function printcolumn(startx,starty,data,n) for i = 1,#data do if i==n then color = "green" else color = "white" end gui.text(startx, 9*i + starty + 1, data[i], color) end end local function setcourse(waypointsx,waypointsy,wpnum) local index=wpnum+1 local x=memory.readbyte(0xCCC9) local y=memory.readbyte(0xCCC8) local lr = sign(waypointsx[index]-x) local ud = sign(waypointsy[index]-y) if lr==1 and ud==0 then go = {right=1} elseif lr==-1 and ud==0 then go = {left=1} elseif lr==0 and ud==1 then go = {down=1} elseif lr==0 and ud==-1 then go = {up=1} else go = {} end return go end --This was tested to be the fastest method for saving. Hopefully, it holds everywhere. local function pauseandsave() for i = 0,1 do joypad.set(1,{start=1}) vba.frameadvance() end for i = 2,13 do vba.frameadvance() end joypad.set(1,{down=1}) vba.frameadvance() joypad.set(1,{down=1, A=1}) vba.frameadvance() joypad.set(1,{A=1}) vba.frameadvance() for i = 17,18 do vba.frameadvance() end for i = 19,20 do joypad.set(1,{A=1}) vba.frameadvance() end end --Increments wpnum only when the next waypoint is reached local function incwpnum(waypointsx,waypointsy,wpnum) local x=memory.readbyte(0xCCC9) local y=memory.readbyte(0xCCC8) if waypointsx[wpnum+1]==x and waypointsy[wpnum+1]==y then wpnum=wpnum+1 end return wpnum end local function reset() for i = 1,2 do joypad.set(1, {A = 1, B = 1, select = 1, start = 1}) vba.frameadvance() end end local function idleframes(n) for i = 1,n do vba.frameadvance() end end --Walk in a given direction until ready to save. local function walk(go) local speed=memory.readbyte(0xC5B2) local framesperstep=16/speed local startcoords=memory.readbyte(0xCCC8)+memory.readbyte(0xCCC9) while (memory.readbyte(0xCCC8)+memory.readbyte(0xCCC9))==startcoords do joypad.set(1,go) vba.frameadvance() end for i=1, framesperstep-2 do joypad.set(1,go) vba.frameadvance() end end function modall(vector,n) local output={} for i=1,#vector do output[i]=vector[i]%n end return output end --Checks if a particular button is pressed. I wanted to make it just read B, but I noticed I also needed up and down, so I decided to make it arbitrary. Sorry if it's more confusing. --A is 1, B is 2, select is 4, start is 8, right is 16, left is 32, up is 64, down is 128. --Only B, up, and down are used in this script. local function isbuttonpressed(n,when) if when=="now" then t=0 end if when=="lastframe" then t=1 end local input=memory.readbyte(0xC3A0+t) Bstate=((math.floor(input/n)%2) == 1) return Bstate end local function scroll(selection,n) if selection==0 then selection=1 end if isbuttonpressed(64,"now") and (not isbuttonpressed(64,"lastframe")) then selection = (selection-2)%n+1 end if isbuttonpressed(128,"now") and (not isbuttonpressed(128,"lastframe")) then selection = (selection)%n+1 end return selection end local function printall(enumerate,rngvals,framevals,xcoords,ycoords,wpnumlist,selection) gui.text(1,1,"#",selection) gui.text(20,1,"RNG",selection) gui.text(40,1,"Frame",selection) gui.text(70,1,"x",selection) gui.text(90,1,"y",selection) gui.text(110,1,"Waypoint",selection) printcolumn(1,1,enumerate,selection) printcolumn(20,1,rngvals,selection) printcolumn(40,1,framevals,selection) printcolumn(70,1,xcoords,selection) printcolumn(90,1,ycoords,selection) printcolumn(110,1,wpnumlist,selection) end local function makeitso(wpnumstart,wpnumfinish,waypointsx,waypointsy,frame,x,y) local wpnum=wpnumstart while wpnum<wpnumfinish>0 do go = setcourse(waypointsx,waypointsy,wpnum) walk(go) end pauseandsave() while vba.framecount()<frame do vba.frameadvance() end reset() idleframes(62) for i=1,2 do joypad.set(1,{A = 1}) vba.frameadvance() end idleframes(10) end local function checkit(address1,address2,address3,RNGTable) if condition(address1,address2,address3,RNGTable) then gui.text(1,1,"Success!") else gui.text(1,1,"Failure?") end end function condition(address1,address2,address3,RNGTable) local value1 = memory.readbyte(address1) local value2 = memory.readbyte(address2) local value3 = memory.readbyte(address3) local roll1=RNGTable[value1] local roll2=RNGTable[value2] local roll3=RNGTable[value3] --This is the super-duper important condition. Fiddle with this to test various conditions. local reportit=(any({255,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20},value2)) return reportit end --Boilerplate... local firstrng = 0xC300 local nextbonus = 0xC30B local canrun = 0xC323 local abslot = 0xC36F local enctrgrp = 0xC343 local stepstobattle = 0xC33B local numenemies = 0xC353 local RNGTable=makerngtable() local state1=savestate.create() local state2=savestate.create() local state3=savestate.create() local rngvals={} local framevals={} local xcoords={} local ycoords={} local wpnumlist={} local enumerate={} local ind=1 local selection=0 movie.rerecordcounting(false) --Addresses to be luck manipulated. I figure we won't have to manipulate more than three at a time. local address1 = nextbonus local address2 = stepstobattle local address3 = numenemies --Decides what RAM address will be output in the end. local rngofinterest = stepstobattle --Waypoints to be traversed. Because the player moves orthogonally, they should be staggered so only the x or y coordinate changes with each index *except* when the room changes. --(Might be possible to take into account the room-- byte CCC7-- but it's a little complicated.) local waypointsx={12,13,13,14,14,15,15,19,19, 7, 7,19} local waypointsy={11,11,12,12,13,13,14,14, 5, 5,14,14} --The waypoint number. Set to the index of the current waypoint or, if the first waypoint has yet to be traversed, just set it equal to 0. local wpnumstart=1 --The encounter rate and the duration in frames over which to manipulate luck. local encounterrate=4 local framesduration=10000 local maxlength=10 local startframe=vba.framecount() local wpnum = wpnumstart local nextbattlerng=memory.readbyte(0xC33B) savestate.save(state1) while (not (RNGTable[nextbattlerng]<encounterrate)) and ((vba.framecount()-startframe)<framesduration) and (wpnum<#waypointsx) and (#rngvals<maxlength) do savestate.save(state2) pauseandsave() for i=1,16 do savestate.save(state3) currframe=vba.framecount() currx=memory.readbyte(0xCCC9) curry=memory.readbyte(0xCCC8) reset() idleframes(5) if condition(address1,address2,address3,RNGTable) then rngvals[ind]=memory.readbyte(rngofinterest) framevals[ind]=currframe xcoords[ind]=currx ycoords[ind]=curry wpnumlist[ind]=wpnum enumerate[ind]=ind ind=ind+1 end savestate.load(state3) vba.frameadvance() end savestate.load(state2) local go = setcourse(waypointsx,waypointsy,wpnum) walk(go) wpnum = incwpnum(waypointsx,waypointsy,wpnum) nextbattlerng=memory.readbyte(0xC33B) end savestate.load(state1) local framesshort=modall(framevals,1000) printall(enumerate,rngvals,framesshort,xcoords,ycoords,wpnumlist,selection) emu.pause() local numoptions=#enumerate while isbuttonpressed(2,"now") do selection = scroll(selection,numoptions) printall(enumerate,rngvals,framesshort,xcoords,ycoords,wpnumlist,selection) vba.frameadvance() end local wpnumend=wpnumlist[selection] local frame=framevals[selection] local x=xcoords[selection] local y=ycoords[selection] if (not (selection==0)) then savestate.load(state1) makeitso(wpnumstart,wpnumend,waypointsx,waypointsy,frame,x,y) checkit(address1,address2,address3,RNGTable) end emu.pause()
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
I should know what a monster script is -- I'm currently holding on to at least one that is over 1000 lines in length, written by myself, though this counts the comment lines and blank lines. Yours is a mere 345 lines. I will spend some time optimizing the functions. At least make things a bit more compact while I'm reading through and trying to get a better idea.
What do you mean by "other actions"? Like various keypresses in the meantime? I don't think that would affect anything while we're in the menu. Please clarify if I'm not getting it.
Do edits 3 & 4 in this post seem familiar? Admittedly, I do not know where the tests have taken place in, but fact is, it's possible to tweak the RNG according to your own tests. ... It is harder than I thought it would be to keep myself interested in staying with the run. The tests I should try might be simple enough, but I keep getting myself on other things. Since we were talking about botting, I ended up trying to produce one that plays Yoshi's Cookie, versus mode, and actually have some decent progress on it -- It can move the cursor to any coordinate it wants in frame perfection. ... Which is not about this game. Poor attention span, perhaps. EDIT: Test 1, party of F F M F. (Edit2: checked difficulty of RNG; EDIT3: Added RNG constraints) Summary: One party swap was made. One reset specifically for dodging an enemy encounter is made. Ending GP was 1004, should be enough for the BATTLE sword with a spare margin of 15. I picked out some exact RNG values and cheated for them, with the worst possible luck requiring us to hit one of only four values at the reset prior to cronies battle. There are a total of three apparently difficult rolls to make. My notes:
HP St Df Ag Mn     Ending party
40  7  5  8  6  STEALTH ....... ARMOR   ....... Starter MUTANT F
45  8  3  3  7  ESP     BARRIER ....... ....... Fodder MUTANT M, will die at SEI-RYU
33  8  3 15  6  TELEPOR POWER   ARMOR   ....... Spare POWER MUTANT F
20  3  0  7 26  ....... POWER   GAZE    STEALTH Main GAZE MUTANT F

reset (_79.37 ~ _80.24) [52]  C343:58  C36F:5F
GOBLIN   | 1A:SABER(18) 4D:RAPIER(4)
ALBATROS | 1A:SABER(20)
reset (233.29 ~ 233.36) [_8]  C30B:E8  C343:F2
GOBLIN   | 1A:SABER(18) 4D:GAZE (meat)
GOBLIN   | 2B:RAPIER(4) 1A:SABER(17)
ALBATROS | (First strike) 4D:GAZE (meat)
P-FROG   | 4D:GAZE (meat)
reset (145.37 ~ 146.28) [56]  C30B:93  C343:9B
ALBATROS | 1A:SABER(21)  (meat)
reset (146.37 ~ 147.28) [56]  C30B:94  C343:9C
LIZARD   | 1A:ARMOR 4D:GAZE
reset (172.22 ~ 172.28) [_7]  C30B:AD  C35A:B9
Swap B with C: New party of ACBD  (Reason: Agility+4 to third slot)
KINGSWRD | 1A:SABER(12) to1A:sKING(38) 4D:GAZE
reset (Few lousy extra steps. Blegh) [Lots!]
reset (_15.21 ~ _15.24) [_4]  C353:1B  C36F:1E
SKELETON | 1A:SABER(11) 4D:RAPIER(4) 3B:RAPIER(7) (meat)
reset (171.29 ~ 172.28) [64]  C30B:AD
STEWARD  | 1A:SABER(23)
Player (79)
Joined: 8/5/2007
Posts: 865
FatRatKnight wrote:
Do edits 3 & 4 in this post seem familiar? Admittedly, I do not know where the tests have taken place in, but fact is, it's possible to tweak the RNG according to your own tests.
Yeah... yeahhhhhhh... but here's the thing: I question how useful those findings will be. I strongly suspect that the RNG shift is only affected by changes in the game's state. When I held right for 10 frames, it caused the character to take one step to the right. I think it was the stepping right that altered the RNG, not the action of holding right. In other words, I could have held right for two frames, then up for the following eight frames, and gotten the exact same result as holding right for 10 frames. If that's the case, we can't just insert random ups and downs and pauses to alter the RNG. Furthermore, that means we can't affect the RNG without actually pausing the game or actually taking a step out of the desired path. Even though the select and start buttons had a large impact on the RNG seed, they're pretty much out of the question because it will take too long to enter and exit those menus; it would take a few dozen frames and my bot can already pare most resets down to within a few frames of optimal. That leaves the d-pad. I say taking a step out of the prescribed path is unacceptable. If we're on foot, that'd be 32 "wasted" frames. The only thing we can possibly do is change the order in which we move from place to place. In some cases, we'll be hampered by narrow hallways and we won't be able to stray from a specific path. Let me offer a scenario that gives this strategy the best possible chance of working. Suppose we're in a wide open field and we start at coordinates (0,0) while our next goal is at (40,40) and, as luck would have it, we wish to manipulate luck after 40 steps. The number of different paths we can take is 80 choose 40 or about 10^23. I'll offer three paths. We could zigzag down and right, which would increment the RNG seed almost monotonically, or we could walk 40 steps right, then 40 steps down, or we could walk 40 steps down, then 40 steps right. According to the numbers I came up with in the post you cited, the latter two paths' RNGs would differ by (approximately?) 40*7/64 or about 4.4. It should come as no surprise that the two will differ from the zigzag path by about 2.2. As we approach the goal (assuming we don't manipulate luck in the meantime), the two paths' RNGs will converge back to the same value. 4.4 is a healthy amount, but we can only get a number like that with a large number of steps and a wide open area. Assuming we have an x by y rectangle within which to move, the most the RNGs will diverge from each other is min(x,y)*7/64 (or min(x,y)*8/64 if we happen to be going left). If we wish to manipulate luck on step n, that divergence drops to min(n,x+y-n)*7/64. I can't say it would do nothing, but our options are limited. If we're in a relatively open space and are having trouble manipulating luck, it's worth looking into. All this analysis was a waste of time if it doesn't hold up to experiment. First I tested that it really is changes in the game's state that affects the RNG, not button presses. I just tried it out and holding various buttons after action was taken had no effect. I can write up a table summarizing the results if you'd like. For the second test, I stood at the lower-left hand corner of Base Town (which gives a nice ~25 step runway) and attempted to manipulate luck by walking either right or up. Using my luck manipulation script, both attempts resulted in positive results on frames (mod 1000) 915, 958, and 140. That's a little surprising, since they should have diverged more than that. I checked the result at frame 140, since that involved walking 17 steps and therefore gave us the best chance of divergence. The RNG seed for both was 247.54, so there was absolutely no divergence. Taking alternate paths has absolutely no effect on the RNG state after resetting. What's going on here? This is a very rare instance in which my answer is, "Who cares?" If we can't affect the RNG by taking a different path, that's that.
FatRatKnight wrote:
It is harder than I thought it would be to keep myself interested in staying with the run. The tests I should try might be simple enough, but I keep getting myself on other things. Since we were talking about botting, I ended up trying to produce one that plays Yoshi's Cookie, versus mode, and actually have some decent progress on it -- It can move the cursor to any coordinate it wants in frame perfection.
I noticed your interest seemed to be waning. How about we take a break? I'm satisfied with my recent results and most of the pieces are now in place. I propose we take a week or two off, then reconvene and finish this. There isn't a whole lot left to do, so I think there's something to be said for either getting it done now, or putting it off until later. It's up to you. One more finding: I found the RAM addresses for menu choices. In the pause menu, you can scroll both horizontally and vertically. These are covered by addresses FFCE and FFCF respectively. Basically, these determine the position of the cursor on screen. Additionally, in some menus, the window scrolls up and down (for example, you must scroll down to see the trash can and spheres in the item menu). The scroll offset is given by FFF2. The battle menu selection is covered by different addresses. I don't know if there's a horizontal selection address (perhaps for selecting different enemies), but the vertical selection is FF92. Curiously, when selecting an attack/ability, this address increments by two at a time from 10 to 16. The "scroll offset", as defined above, is at address C424. I still don't know if we should make a bot that will fight our battles for us, but these addresses will be handy to know should we decide we want one. Edit: I keep noticing minor mistakes that I'm making. They're not that important, but they bug me. I just don't want you to think there's any new content even though a little footnote says I've edited this post 3 times (and counting) in total.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Bobo the King wrote:
I noticed your interest seemed to be waning. How about we take a break? [...]
We stop now, and I'll never recover. It will simply become a project that only exists in my past and I will never regain what little interest I have now. An untended confined flame will simply burn itself out, and no amount of extra kindling will restore this flame once gone. My "burning passion" may be just a flicker, but leave it alone with nothing more to fuel it, and it will simply die out. ... Why am I using a metaphor now? So, no extended breaks. Won't help us complete this run. Might even be harmful. I want to look at more World 1 stuff, at the least, before I drop this run for a while. I've made a recent edit to my last post with some analysis on a particular RNG path we could take. I will check this party and make sure it can defeat SEI-RYU handily and with pinpoint killing of the fodder. Not strongly for the idea of botting battles. I've actually noticed, on occasion, the game has a sort of an "anti-lag" in that I'm able to access some menu item a frame earlier than expected. There aren't a lot of battles that we actually fight at this point, with the bulk of it going towards World 1, and I don't feel it would take too much time to handle manually.
Player (79)
Joined: 8/5/2007
Posts: 865
FatRatKnight wrote:
I've made a recent edit to my last post with some analysis on a particular RNG path we could take. I will check this party and make sure it can defeat SEI-RYU handily and with pinpoint killing of the fodder.
Any projected or confirmed advantages to this party over the current standard one (F M F F or GAZE, POWER, fodder, fodder)? Just keep me updated. I just spent a few hours working my way from World 3 to World 4. When I played back my movie, it desynced back in World 3. "Disappointing" is one word I'd use to describe it... (Edit: No, wait, I made it through about one-and-a-half floors of the Tower. Then it reset without saving. The reset brought me back to World 3. It still sucks.) Anything for me to work on at the moment? I'm happy to work on my run to find any quirks that may turn up (not to mention to continue compiling waypoints). I'm under the impression that wouldn't be good use of my time, though. Other than these last few party tests, what's holding us back from the final run at this point?
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Names: Star - MUTANT F destined to die at Monster Trio. The starter Fodd - Mutant destined to die at SEI-RYU, Pow - Spare mutant who supplements the fights of SEI-RYU and Monster Trio with POWER. Gaz - Main MUTANT F who wields the power of GAZE and will hold the STONE book. SEI-RYU bites party member 1, followed by zapping party member 2, using my favorite RNG that leaves us with plenty of steps (159.29 ~ 160.28) and a Mana+5 given to the second mutant. In order to survive the bite in round 1, ESP or ARMOR must be used. In the case of ESP, turn order does not matter. If it's ARMOR, it must be used before SEI-RYU attacks. A member in slot 1 will only go first if the slot 4 member is slower. The follow-up THUNDER is unavoidable; Member 2 will surely die. I need spare fodder for Monster Trio later, so I can only accept one death. With one death, Gaz must be in slot 3 in order to catch the Mana+5. In order for Gaz to be fast enough later on, Fodd must die here, so into slot 2. Pow can't take the hit and still beat SEI-RYU dead by round 3, so basically, slot 4. Star must sit in slot 1. Here's the problem I crash into: The Pow, in slot 4, has 15 Agility. Star has 8. I could put Fodd in front, but when Star dies, she's placed in slot 4, with 8 Agility versus the all-important Gaz's 7 Agility. Bad, there's no going first later. I checked the RNGs that put Mana+5 on track for the third mutant. I either get a bad step count for the random encounter or SEI-RYU targets slot 3 or 4 with the bite. The bad step count means another reset, but at least its workable. I'm wondering if I should eat another swap in World I and give an Agility+4 that the spare POWER mutant would have gotten. The Agility values would then be 12 and 11, allowing the starter MUTANT F to go first against SEI-RYU and get that ARMOR up in time. Or would it be better to leave the Agility alone and go for the bad step count win and take an extra reset?
Bobo the King wrote:
Any projected or confirmed advantages to this party over the current standard one (F M F F or GAZE, POWER, fodder, fodder)? Just keep me updated.
Lets see... World I would have some differences. For one thing, your fastest mutant is in another spot. Since your GAZE mutant is now the fastest one, the KINGSWRD battle will finish without him ever attacking. For another, we have an extra swap right from the start. But a new RNG route may be fairly unpredictable, it's hard to say. The later worlds would act pretty much the same, though. Only thing that might have a chance of being different would be SEI-RYU, but I don't believe we want it to change much. I may want to look at that F M F F group for World I for a bit.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
Test 2: Party of F M F F, with swap of first and last F. (EDIT: Added RNG analysis; EDIT2: Added RNG constraints) Summary: One swap was made, at the very start. One reset specifically for dodging a random encounter was made. Ending GP was 1023, a generous 34 above what we need for the BATTLE sword. As usual, I cheated some particular values to speed up my run. But of note, the KINGSWRD battle is easier to manipulate due to the fact I no longer must force him to pick a durable target -- The GAZE is fired first. Still, there are 2 tough rolls to make, regardless.
HP St Df Ag Mn     Ending party
20  6  5  7  5  ....... ....... ARMOR   ....... Fodder MUTANT F. Die at SEI-RYU
33 12  3  3  6  TELEPOR POWER   ....... ....... Spare POWER MUTANT M
45  4  3 15  7  ESP     BARRIER ARMOR   ....... Fodder MUTANT F for Monster Trio
40  4  0  8 27  STEALTH POWER   GAZE    STEALTH Main GAZE MUTANT F

Swap A with D: Order is DBCA
reset ( 79.37 ~  80.24) [52]  C343:58  C36F:5F
GOBLIN   | 4A:SABER(18) 1D:RAPIER(4)
ALBATROS | 3C:RAPIER(7) To1D:BEAK(19) 4A:SABER(20)
reset (233.29 ~ 233.36) [ 8]  C30B:E8  C343:F2
GOBLIN   | 4A:GAZE
GOBLIN   | 4A:GAZE
ALBATROS | 4A:SABER(21)
P-FROG   | 3C:RAPIER(-) 1D:ARMOR 4A:GAZE
reset (145.37 ~ 146.28) [56]  C30B:93  C343:9B
ALBATROS | 1A:SABER(21)
reset (146.37 ~ 147.28) [56]  C30B:94  C343:9C
LIZARD   | 4A:GAZE
reset (171.29 ~ 172.28) [64]  C30B:AD
KINGSWRD | 4A:GAZE
reset (for those few extra steps) [Lots!]
reset ( 15.21 ~  15.28) [ 8]  C30B:10  C353:1B
SKELETON | 4A:SABER(11) 1D:RAPIER(4) 3C:RAPIER(7)
reset (171.29 ~ 172.28) [64]  C30B:AD
STEWARD  | 4A:SABER(23)
Player (79)
Joined: 8/5/2007
Posts: 865
I had a piano recital yesterday and my internet has been wonky, so I wasn't able to get anything done. I'll be a little busy today, but I hope to at least find out how long it takes to bike or walk to the Skyscraper. Just wanted to let you know I'm still here.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
I'm more interested in whether MUTANT M or HUMAN M is the way to go. By the end, it won't matter, but the process of reaching ideal SAW Strength is different. ... I should review the basics between the two. HUMAN M deals with his STRONG addiction before leaving town, where the MUTANT M would probably need an extra battle to get optimal Strength, or deal with SU-ZAKU somewhat slower. I'm pretty sure walking the way is better than fishing for the bike in town and getting to the skyscraper with it. Never hurts to double-check, though. Don't forget there are some no-encounter steps that only the bike can make use of. I'm almost certain of the World I plan now. Switching the starter MUTANT F so she can get GAZE is the best plan, as we can use my favorite RNG against SEI-RYU that way without any excess switching. Just a matter of looking at Worlds II & III battles and making sure things go smoothly before we commit ourselves to this group.
Player (79)
Joined: 8/5/2007
Posts: 865
Sorry, man. I know you were against the idea of taking a break, but it looks like I've kind of forced the issue anyway. I hope to be back in the saddle tomorrow, but I admittedly have a poor track record. I'm eager to work on the final run, not toy around with further tests, no matter how simple they may be. I guess I just have to devote one more day to testing before we move on.
Editor, Skilled player (1172)
Joined: 9/27/2008
Posts: 1085
My TASing mood died. Two months later, it looks as though it's being revived, as I caught myself putting some effort into TASing Mario Golf, both the ones for GBC and GBA. A casual attempt, anyway. So my interests in TASing once more is in the process of getting revived. I can't predict when I'll be back fully into it, but I am hopeful that we can finally finish this one.
Ambassador, Experienced player (697)
Joined: 7/17/2004
Posts: 985
Location: The FLOATING CASTLE
The progress you guys had made so far on this game was very promising. So I hope you get back into it. About motivation, I don't know how the really prolific TASers do it. But I go through different periods of motivation and laziness and I think that taking breaks can be very helpful. You come back to the project and suddenly come up with several new ideas and realize the problems you were struggling with weren't so bad after all.
1 2
6 7 8 9