Posts for Bobo_the_King

Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I can't stand the idea of manual testing, so I am trying to incorporate this into a yet more sophisticated bot. It's intimidating, though. This bot will have to do just about everything. It will need to take note of frames where the music changes, backtrack to just before the music changed, create a savestate, move on, see how well it can manipulate luck, then if it's less than ideal, backtrack to one of those savestates and delay input by one frame at a time. I'd also like it to do some cost-benefit analysis on how many steps to shoot for before saving and resetting. It'll be tough, but if I can get it done, it will "solve" the game and give us nearly ideal input. Expect procrastination for a while. Edit: New useful addresses: C5BD and C5BE. These addresses appear to be zero when outside a text box, but inside dialogue they change to something else. The only caveat is that they also change when you "bump into" an NPC. That shouldn't be an issue, since we shouldn't be colliding with anyone in this run. I'll keep my eyes peeled for similar addresses. Edit 2: New useful address: D3EF. Battle option selection. Very useful for my battle bot. Edit 3: Sorry, D3EF is only for character 1. Here's the full list: Character 1: D3EF Character 2: D3F3 Character 3: D3F7 Character 4: D3FB Additionally, it appears that the subsequent addresses, D3F0, D3F4, D3F8, and D3FC, are the actions' targets. Those may specifically be enemies, with different addresses signifying friendly actions. I'll check it later.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Currently working on more advanced botting. Most important new finding is addresses FFE0 and FFE1 which signify the current music track playing. This is to (potentially) be used by a bot that implements FatRatKnight's MusicDelay parameter above. Edit: I've updated my walking script to do some more sophisticated guessing and checking. This isn't particularly useful (walking isn't a very difficult thing to do), but it uses a new subfunction that should be very versatile. Basically, if we need to find a specific frame where anything in particular happens, we can test it using the same subfunction. The subfunction is guessandcheck. It takes five arguments: • action-- Some array of controller inputs. For example, if you want to go left, action would be {{left=1},{left=1}} (remember that you have to hold a button for two frames to get a response). • condition-- A passed function* that checks the condition that's desired. In the case of holding left, it should check whether address CCC9 (the horizontal position address) decreases by 1. (In fact, in the current version of my script, it only checks if either of CCC8 or CCC9 changes, regardless of what direction is pressed. It may be a good idea to change this.) Note that condition takes a variable called oldstate that's just a vector of relevant addresses. As our conditions grow more complicated, oldstate will grow larger. Someone could probably come up with a way to clean it up. • framedelay-- How long to wait after action is executed before checking. In the case of stepping left (or right, or up, or down...), the coordinate addresses change on the next frame, so framedelay can be set to 1. I set it equal to 3 in my walking script because I didn't bother to check the actual delay. • lowguess and highguess-- Initial guesses for the first and last frames that should be checked. These guesses are refined. Note that framedelay, lowguess, and highguess have no effect on the script's success as long as they encompass the desired condition. Therefore, they should be set as tight as possible only in the interest of making the script execute more quickly. *Thanks to FatRatKnight, BrandonE, and this site for explaining passed functions to me. The strategy here is very simple and I'll explain it first with an analogy. Suppose you wish to find page 592 in a book. You open up the middle of the book, check if that page is greater than or less than 592, keep the half that encompasses the page you're looking for, open up the middle of that half, and repeat the process. This is more efficient than leafing through the book one page at a time (which is effectively what my old walking script did). The same thing is going on here. It checks the frame in the middle of lowguess and highguess (rounding down). If condition is satisfied, then the earliest frame that can give us the action we want is somewhere between lowguess and the current guess, so highguess is revised to be the current guess. If condition is not satisfied, then lowguess is revised upward to be the current guess, plus one frame. And that's pretty much it. It just executes that loop until lowguess is equal to highguess. For an action like walking, which takes no more than 16 frames, just four iterations of the loop should be necessary to find the optimal frame. What's nice about this subfunction is that it can be used for many purposes other than a walking script. It potentially can be used to automate almost any process. I'm currently considering making an automatic battle fighter. This function should serve as the core of any future bots. Heck, with just a little work, it could be used as a bot for any game (and it would surprise me if no one has come up with something similar already). Edit 2: Noticed a mistake in my bot. If the last guess was unsuccessful, lowguess was bumped up to meet highguess and the loop was broken without executing the correct action. Now it goes back and loads the savestate and executes the proper guess.
Language: lua

--Finds the earliest frame at which the RAM value at "address" is equal to "condition". local function guessandcheck(action,condition,framedelay,lowguess,highguess) local state2=savestate.create() local oldstate={memory.readbyte(0xCCC9), --x coordinate memory.readbyte(0xCCC8), --y coordinate memory.readbyte(0xFFCE), --cursor x memory.readbyte(0xFFCF), --cursor y memory.readbyte(0x994A)} --battle window local foundit=false savestate.save(state2) while not(foundit) do savestate.load(state2) guess=math.floor((highguess+lowguess)/2) for i=1,guess do emu.frameadvance() end for i=1,#action do joypad.set(1,action[i]) emu.frameadvance() end for i=1,framedelay do emu.frameadvance() end if condition(oldstate) then highguess=guess else lowguess=guess+1 end foundit = (highguess==lowguess) end savestate.load(state2) for i=1,lowguess do emu.frameadvance() end for i=1,#action do joypad.set(1,action[i]) emu.frameadvance() end while not(condition(oldstate)) do emu.frameadvance() end end local function tookstep(oldstate) if not((memory.readbyte(0xCCC9)==oldstate[1]) and (memory.readbyte(0xCCC8)==oldstate[2])) then steptaken=true else steptaken=false end return steptaken end --Apparently Lua doesn't have a built-in sign function... local function sign(x) if x>0 then return 1 elseif x<0>0 then for i = 1,n do vba.frameadvance() end end end --Walk in a given direction until ready to save. local function walk(go) local speed=memory.readbyte(0xC5B2) local framesperstep=16/speed for i=1,2 do joypad.set(1,go) vba.frameadvance() end for i=1, framesperstep-2 do vba.frameadvance() end 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 --Boilerplate... cango=false j=0 local state1=savestate.create() movie.rerecordcounting(true) --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.) -- 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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 wpnum=wpnumstart while wpnum<#waypointsx do go=setcourse(waypointsx,waypointsy,wpnum) sumcoords=memory.readbyte(0xCCC8)+memory.readbyte(0xCCC9) guessandcheck({go,go},tookstep,3,0,16) wpnum = incwpnum(waypointsx,waypointsy,wpnum) end emu.pause()
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
marzojr, you are much more learned in physics than I am. Nevertheless, I have to ask: what is wrong with interpreting a as dv/dt? Okay, if a is commonly understood to be the "constant proper acceleration", I suppose I can't argue with that, though physicists are generally more precise than most with their nomenclature. If we cannot discard the possibility of a=dv/dt (which seems quite sensible to me), then we must at least hint to our interpretation of acceleration by choice of units and words. I still maintain that velocity cannot increase monotonically indefinitely (and if it does not increase monotonically indefinitely, it technically does not increase monotonically at all). And as a physicist, I'm not afraid to be wrong or to concede valid points to someone who knows better than I do-- you're probably correct and I am out of my league. But please, no need to be so snarky about it.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Warp wrote:
The acceleration felt by the astronaut (which can be unambiguously measured by the astronaut using an accelerometer) can be constant forever, even under SR/GR, as far as I understand.
Said this way, yes, you are absolutely correct, though I think that speaking of the force felt by the astronaut is even more accurate. In fact, I think the key word there is "felt". The earlier problem came from ambiguity in the word "acceleration" and your choice of units, m/s^2, which, taken in a perfectly valid way, implies a constant dv/dt. In this interpretation, the astronaut would not feel a constant acceleration; he would instead feel an asymptotically increasing force as he approaches the speed of light, with death and annihilation of the spaceship surely occurring sometime before he reaches v=c (in a finite time). Hope that clears things up. I didn't have time to answer the problem, so I instead settled for a nitpick. I knew what you meant in the original statement, but decided I should point out a minor flaw/ambiguity. At first glance, p4wn3r's math looks at least plausible, but I'd still like to take a crack at the problem myself. But now, I have a pitiful E&M final to turn in.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
That's an interesting question and I'd really like to tackle it, but ironically, I'm studying for a physics test at the moment. Still, I will take the time to point out that to properly define the acceleration, you should refer to the force per unit mass. In particular, the problem is well-stated if you demand that the spaceship's momentum, not its velocity, increases monotonically. Your units imply that the velocity increases monotonically, which is obviously not possible. Like I said, I'm pretty busy, but I did take the time to calculate that c/g ~ 1 year, so I would guess that the answer to part 1 is roughly 4 years (2 years each way) and the answer to part 2 would be something like 15 years. These are very crude calculations, though, and only reflect ballpark estimates of the answer. I'd love to address this problem in full, but it'll be at least a few days before I have the chance. Someone else will probably beat me to it.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
There was a game I played maybe fifteen years ago with a friend that was very simple and generic, so I don't expect anyone to know it. Nevertheless, it was a total blast to play and I remember a lot of details about it. It was for PC and it had vector-esque graphics (not sure if they were "true" vector graphics, but that was the distinct style). You played as a red triangle/trapezoid shaped ship with a turret (line) in front. The game was played from an overhead view and there were simple obstacles (large gray squares) in the battlefield (the background was black). You had to stay within the battlefield while enemy ships attacked you. I believe you needed to destroy all the enemy ships to advance to the next level. With each level (or perhaps every two or three levels), they would introduce new enemies with new colors, new movements, new weapons, and new strategies. I think the early levels had you just fight other generic tanks that looked similar to you, while later levels pitted you against something like pink "spider robots" with homing missiles (probably not that exactly, but something along those lines). What made the game so much fun is that you started it with some number of points (ten?) that you could distribute among different attributes: speed, agility, and firepower, I think. Being little kids, my friend and I would generally devote a lot of points to firepower, but we learned one day that giving most of the points to speed was a lot more fun and successful. We'd scramble around the battlefield, barely outrunning the enemy bots, turning just barely enough to dodge their attacks. Of course, the levels took much longer to win that way, as we'd only get in a shot or two by painstakingly circling back around on our pursuers. I think (but I'm not sure) that you were offered one additional upgrade point between levels. Ring any bells? I think it's a long shot.
Post subject: What makes a game easy to speedrun, hard to TAS?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Quick topic while I put off studying for my finals. I would actually be surprised if this thread doesn't already exist, but I couldn't find anything. Feel free to merge this with an existing thread, as appropriate. Very simple question: What can make a game easy to speedrun (unassisted, of course) while hard to TAS? What games have these qualities? Obviously, the TAS is always going to have a faster time and more polished quality, but I imagine there are good examples of games where the unassisted speedrun was practiced and produced in a matter of months while the tool-assisted version took years of studying, decompiling, and redoing major segments just to meet the high standards we set. The one aspect that came to my mind was luck manipulation. The unassisted runner would accept the law of averages and not challenge their results too much while the tool-assisted runner would spend days just trying to manipulate the result he wants. I think a few RPGs could fit this profile (Fire Emblem, perhaps?). (Another possibility that just crossed my mind is most 3-D games. For a TASer, the addition of a third dimension increases the difficulty of optimizing movements by tenfold, while a conventional speedrunner just does the best he can. I think Mario 64 runs might fall under the "easy to speedrun, hard to TAS" category.) I don't know how good an example it is, but FatRatKnight and I are working on a Final Fantasy Legend TAS. While work has been off and on, we've devoted a lot of time and energy to decoding the RNG, going so far as to make a bot that will save and reset every frame to see how we can manipulate the RNG. Meanwhile, over at SDA, Poxnor has been able to produce a run that (from what I understand) he began to seriously practice in February (about two-and-a-half months' work). The same RNG that is giving FatRatKnight and me so much trouble has a period of just 256 and begins from a fixed state, so Poxnor just accepted the luck he was presented with and structured his run around making the most of it. What other games are like this? Additionally, what makes a game particularly easy to TAS but brutally difficult to speedrun unassisted? Here's a simple but diabolical scenario that comes to my mind: • Have a trick in the first level that the player has a 1 in 2 chance of pulling off and saves one second. • Have a trick in the second level that the player has a 1 in 4 chance of pulling off and saves two seconds. • Have a trick in the third level that the player has a 1 in 8 chance of pulling off and saves four seconds. ... • Have a trick in the nth level that the player has a 1 in 2^n chance of pulling off and saves 2^(n-1) seconds. This is known as the St. Petersburg lottery (Edit: Okay, not exactly, but the spirit is similar). While the TAS should be straightforward, it would be frustrating to an unassisted runner because they would be forced to keep sub-optimal runs in the desperate hope that they nail one of the later time-savers. Are there any games like this? What other qualities can make a run especially easy to TAS but cruelly difficult to speedrun conventionally?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Yeah, a while back, I was thinking of suggesting that we make an annotations script. Then I stumbled upon a topic with just such a script, authored by none other than FatRatKnight. He's rather prescient. As for my progress, I'm almost through the first chunk of the tower, but my luck-manipulation bot came up dry on the last floor. I may need help with that, if only briefly. Otherwise, I'll need to backtrack to the last luck manipulation, costing roughly 16 frames (as I recall, the second-best option reached one step farther post-reset, but was two steps back). On the positive side of things, I made a walking bot for when we don't need to manipulate luck. It's a little inefficient, but a welcome addition, nonetheless. Edit: Just watched Poxnor's speedrun (not yet published, but I'm a verifier). In World 3, after entering the Floating Castle when it's near Jeanne's hideout, he leaves without using the glider. That is, he just steps off the catwalk (plank?) and into the abyss. It's worth testing whether it's faster. My guess is that it isn't, but I can't say for sure as yet. I think it'd be an extra 4 frames to take one extra step in the glider, but 12 frames faster to move one tile down in the glider, rather than on foot. This may not be exact, since I've found that entering vehicles is a little "sticky"-- input is halted for two or three frames. Anyway, this edit is just a note to ourselves to test it. A few other things caught my eye. First, he somehow manages to trigger the initial confrontation with So-Cho while facing the counter, not the NPC next to it. If I recall, that doesn't really make sense and it may get him through that cutscene one step (16 frames) sooner. Second, he mysteriously stops to talk with the Creator before taking the elevator to ASHURA. This just didn't make sense to me at first, but now I wonder if it's equivalent to the NPC (who happens to look exactly like the Creator) we need to talk to in World 1. I'm going to look into it. If we can talk to him there, it will save us many steps. The rest was just random observations. Most of it was very good, with a few minor mistakes that were generally related to our discoveries. For example, he doesn't leap off the elevator during his descent in the factory, costing a few dozen frames. Nothing too notable. And as a side note, it is really weird watching a run of this game with the sound on. Edit 2: A quick RAM search seems to indicate that the address that flags whether the man in Base Town has been talked to is CC95 with a duplicate address at EC95 (for anyone idly reading this conversation, most or all relevant RAM addresses are in the C___ range with duplicates in the E___ range). I'm going to proceed and keep an eye on that flag throughout my earlier test run. Expect another edit shortly... Edit 3: Yes yes yes yes yes! It appears we need to talk to the Creator at any point in the game. I caught good opportunities on floors 5, 10, and 22, with Poxnor's strategy (floor 22) likely the best as the Creator only says, "Be careful! Ashura lives above." I may check the other possibilities as well, but I think he's chattier then. Normally, a revelation like this that requires extensive backtracking would get me down, but I like this one. It'll save us a lot of time (upwards of 100 frames) and it'll give me a second chance to do this portion, which was having really crappy luck. Besides, it's not as much backtracking as it seems, especially with my walking script. I'm really happy to have caught this! Much thanks to Poxnor! (We'll have to thank him in the submission text.) Edit 4: Last edit for the night, I swear. I just checked the Creator's dialogue on floors 5, 10, and 22. They were as follows. 5F: "Look for the old man!" 10F: "The ruler of this world is calling for guards. Being a guard is the quickest way to meet him." 22F: "Be careful! Ashura lives above." 5F is the shortest, but I discovered that we can't walk up to him smoothly. At the very least, he'll cost us about 4 steps, much more time than his dialogue would save. I thought we would pass by him on the way to the door to floor 6, but I realized we actually start at the door on the top edge of the room, not the left. 10F is along the way, but far too long. Nothing really to consider there. 22F is strongly looking like the best option. The only other possibility is on 15F, but his dialogue would have to be pretty short. I think he's actually out of the way, negating that possibility. I didn't check too carefully. Anyway, if we plan on talking to him on floor 22, we can always check floor 15 along the way. Bobo out.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Looks like we've now got an unassisted speedrun too. School has been tough lately, with a term paper due this Wednesday. After that, I have one final stack of papers to grade, a presentation to throw together, and a paper I'd like to write in addition to the usual homework. Things won't clear up completely, but after Wednesday, I expect I'll have enough free time to work on this game. Tell you what: I'll set an alarm for next Friday and I'll get on IRC, we'll chat, and I'll devote the day to TASing the game. It should be a fine way to celebrate a productive semester. After one day of TASing, we'll see where things go. We might burn through the game over the weekend or just do a little bit a few hours at a time on a weekly basis. This summer will be devoted to research, but thankfully, that'll be more like job. Once I get home, my time should be mine alone. So in short, we begin this Friday. No more putting it off. I've shafted you enough times already. The long term schedule will be more hazy, but I expect it should go smoothly with a good start.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Warp wrote:
Draak13 wrote:
Edit the video to fix the camera angles
I'm still wondering if it would be theoretically possible to change the camera angles without redoing the run, by using some script/program that changes the camera movements and compensates the joystick positions accordingly.
What about a cam hack? (Sorry if it's been suggested before.) We could turn this run into Super Mario 64: Resident Evil. As a bonus, if a cam hack can be made, we could view some really interesting camera angles. Imagine the Bowser fights from an overhead view or BitFS presented as the side-scroller it effectively is. If that could be done, the only thing that would count against this run would be the mildly annoying clicking whenever the authors attempt to change the camera angle.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I don't vote (in part, to avoid arguments like this), but could we perhaps make a guideline (but not an outright rule) that entertainment is a factor for improvements to existing runs that don't feature a significant route change? Seriously, that's what this seems to be about. If this TAS were executed differently from the previous run in any meaningful way, I think it would be accepted in a heartbeat, regardless of the camera angles.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Just thought I'd pipe in to say I saw the submission, saw it was submitted today, saw it was an improvement of just three frames, saw it had five pages of responses already, and immediately thought to myself, "Flame war". I haven't read everything yet, but it looks like I guessed right.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Still here, just not much to report. I survived my hardest week of school so far, so I'm starting to feel like I can take a break to TAS... but only after I grade 40 papers.
FatRatKnight wrote:
This is likely how we'll destroy SEI-RYU. Minus my goofing around in the menus in a most unTASlike way. As for the MOSQUITOs, a group of 3 instantly dies to GAZE. Strangely, encountering 1 or 2, quite possible while still grabbing the Mana+5, lets them attack first, but a group of 3 lets our GAZE mutant strike first. Additionally, the spot I loaded the game from is the near-optimal tile, as it lets the guard step towards us without any delay as demonstrated. Any closer, and we must wait a frame or two; Any farther, and we'll need to take an extra step, eating 16 frames. Finally, the last hurdle, the Monster Trio, couldn't have ended better even if I want it to. The second fodder dies (if she doesn't use ESP) without a fuss, and the monsters die without any time-wasting actions other than killing our fodder for us. This is pretty much perfection, right here.
Microstorage seems to be down currently, but I'll check these out as soon as reasonably possible. Tell me if the links work for you.
FatRatKnight wrote:
So you ended up busier than expected. That's fine. If you get nothing else done this week, at least find some time to suggest the names we should use. Blank names all the way through (fastest)? Single letter names with carefully thought anagrams (Don't forget -- we're getting a fifth member eventually)? Full-blown names? We still have time to think about this. When we finally start, changing the names become rather inconvenient.
Hmmm... I'm starting to think that the anagram thing may be too subtle and stupid. Have you determined the party order throughout the game? It will much more difficult to deduce a good anagram without knowing how the words rearrange themselves. Full names are just about out of the question because it would take too long and I can't think of five appropriate names. I guess we could use them as a way to thank contributors to this run. In addition to BOBO and RAT, we could include ALEX for Alex Jackson and SARA for... Sara. Who else? (Seems like an unusual way to thank someone-- name a character who's destined to die after them. At least they nobly manipulated the RNG.)
FatRatKnight wrote:
I forgot to mention I've been getting a few PMs from Kuwaga. The discussion is mainly about the RNG, and we concluded that there's no easy way to create a lua script that predicts the RNG values when we reset. Well, short of running the emulator and going through the reset process and recording what comes out. Address FF04 is based on a hardware timer, which is where the RNG is getting its source of "random" numbers when we reset. Directly reading it probably isn't going to give us the precision we need to unravel the RNG, and good luck trying to count opcodes.
Interesting, but you're right-- it probably won't help us much at this point. In any case, it's good to investigate (never know what you'll find). Send Kuwaga my thanks.
FatRatKnight wrote:
Incidentally, take a look at address CB00. In fact, take a look at the next one at CB01, too. Actually, Tools -> Debug -> Memory Viewer -> Address CB00 and friends, all the way out to CBFF. I wouldn't have expected the entire RNG table being right there in memory, of all places.
What the... ah crap! HAHAHAHA! We spent, what, five pages deducing the RNG? I swear I searched for it in the ROM, but I guess I just didn't look hard enough. Anyway, I'm going to go back to grading papers some more. After that, I have one homework assignment to work on, but I should be otherwise free (aside from stuff I choose to put off). If you're very lucky, I'll have the run started sometime this week, but hey, you know my track record for getting your hopes up. Still, I'm looking for an excuse to take a break. Edit: Nearly forgot! Be sure to check out Zoogelio's "Battle data?" thread on GameFAQs-- looks like Alex Jackson is back! Edit 2: Also, I've figured out how to use IRC. I'm currently on the TASVideos channel, for what it's worth. I don't think there's any need to chat in realtime at the moment, but when the run starts ramping up, we may want to communicate via IRC or AIM. Tell me if you use an IM service at all and I'll PM you my screenname(s) (eventually).
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Still here, though I don't need to tell you progress isn't going quite as planned. I've been busier than I've expected, and it looks like it's only going to get worse over the next couple days. The good news is that I'm pleased to see how simple the plans are. Of course there are intricacies, but I've got most of those written down in my notes. I had a sense that the run would be tricky, but I think most of our time will be spent just trying different RNG manipulations and seeing which one comes out on top. Even the movement is automated with my script, so that leaves... menu navigation, battles, and... gosh, I think that's all we have to do manually. So if I can just scrape together two or three hours, I could actually get this damn thing started. I'll give you a heads-up when I get fully into gear. I'm hoping to get something done by this weekend. Just be aware that it's other obligations, not a lack of interest or willingness that's keeping me from working at the moment. Edit: Oh yeah, and Sara at Tower Reversed caught sight of a GameFAQs topic and in response made an enemy encounter list. I don't think it will be particularly useful to us since we'll be manipulating away battles, but it's neat information. I'm not yet sure what the "freq" column indicates (the bottom number is the encounter rate, but that's all I know) or why there is an asterisk in the upper-right corner of some of the cells. What struck me about the encounter list is that it's not separately compiled for each map/terrain; instead, it seems to be one long list and every part of the map is given a "difficulty index", which dictates where in the list to start. If you don't know what I'm talking about, look at rows 15 to 1B and notice how the encounters (particularly PIRATE-GECKO-JAGUAR and SHRIMP-RED BONE-GUARD) just shift to the left with each row. It's an interesting way to set up the game (at least I think it is). Edit 2: According to Sara, the three numbers in each cell of the "freq" column are effectively the same information: the first number is the byte stored in the ROM, the second is the probability of an encounter based on that byte, and the third is the threshold for an encounter that you and I are familiar with. Of note, the glider's encounter rate isn't listed explicitly, so it appears that the glider effectively divides the encounter rate by 2. Also, the enemy encounters apparently aren't stored in list form with a "difficulty index", as I conjectured above. Sara informs me that although the enemy encounters are listed in order in one part of the ROM, each encounter is, in fact, called individually and explicitly. Finally, the asterisks indicate battles that are different in the Japanese version of the game. So add these to the growing pile of screwy programming habits: they stored the encounter rates as fractions when thresholds would have sufficed and they went through all the trouble of making the encounters table one long list only to ignore it entirely and program them individually.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
I'm going to set aside about two hours tonight to also get up to speed and maybe get started on the run. I remember now that you had extensively researched the best strategies for who to die in World 1. Because you're the better strategist between us and you're a little ahead of me anyway, could you remind me what the plan is and, if it's not too much trouble, summarize our other findings? I know we have a few old posts that outline the step-by-step strategy, so it should just be a matter of filling in the details we've amassed since then. Also, did we ever write down the fastest way to navigate menus? I remember a few strategies. For example, my luck manipulation bot can save and reset with perfect efficiency and I know that to use TELEPOR you have to press up for 3 frames before pressing A.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
Gear yourself up. Next week is Spring Break and I should have a little free time throughout the week. I'm shooting for two or three hours a day, which might be enough to finish one world at a time. I should be clear that I do have a fair amount of work on my plate as well-- principally, grading papers and researching for a term paper. With good time management, I should be able to cram it all in there. Ha! "Good time management..." What am I thinking?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
FatRatKnight wrote:
As I consider this a joint project with Bobo the King, I may find it difficult to get myself reorganized in an effort to complete this run if I go solo now. As far as I know, we already figured out everything there is, so no bursts of inspiration to save more frames had come up as of now. I do have something to TAS while waiting, so it's not like I'm completely trapped. It's just that this run is fairly dependent on the two team members coming together again.
I'm still here... sort of. Haven't been checking too often. But it's time to come clean-- I have some bad news... I was admitted to grad school. If you know anything about grad school, you know that it simply obliterates your free time. I can attest that's absolutely true-- between studying, doing homework, and grading papers, I have no free time to speak of at the moment. I don't anticipate it will be this way the entire semester, but I also can't say anything about when it might improve (except over spring break). I'm sure that my schedule will clear up by the summer, though. The big question in my mind, however, is how much I'll enjoy TASing in my free time. It's a lot of fun and a rewarding hobby, but it also requires a lot of time and effort, both of which I may be short of. I guess what I'm saying is that I'm willing, just not able at the moment. Just keep me updated if you find yourself in an FFL mood. I can probably squeeze out a few hours over weekends at this point, but I may be able to do more later. Spring break is from March 12th to 20th inclusive and the semester ends May 7th. I'm happy to do my part, it's just that I can no longer spontaneously say, "I'd like to get back into that FFL run again." We'll need to schedule things in advance. Edit: By the way, TASing Mario Golf had also crossed my mind as a fun, easy diversion. Great minds think alike, it seems. But did you also think of TASing Mario Tennis?
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
It'd be pretty bass-ackwards, but I'd drum up something in GlovePIE. I'm not familiar with GlovePIE's GUI capabilities (if any), but the tools are all there.
Experienced Forum User, Published Author, 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.
Experienced Forum User, Published Author, 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.
Experienced Forum User, Published Author, Player (79)
Joined: 8/5/2007
Posts: 865
With about 24,000 (29 choose 25) different choices of pentacubes to work with and millions of ways to stack them, I'm almost sure it can be done. I'm a little ashamed to say you've gotten me working on this all day. I've been looking for 5x5 tilings of the 12 extruded pentominos. After I'm done, I'll use the remaining pieces to fill in the 3x5x5 block. It's slow, but not especially difficult.
Experienced Forum User, Published Author, 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?
Experienced Forum User, Published Author, 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.
Experienced Forum User, Published Author, 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()
Experienced Forum User, Published Author, 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.