Introduction
Generation II of the Pokémon games seen a lot of TASes over the last few month (
1,
2,
3,
4,
5), and none of them lasted very long before being beaten.
The fact that only one of them made it to a publication at all is a good indicator on how fast the TAS strategies for this game evolved lately.
So, here comes another attempt I guess.
This submission uses the same basic route I used in my
previous run, but with several improvements, some of which
FractalFusion introduced in
his submission, and some of which are new.
Most of the basic game and route information is explained in detail in my previous submission text and remains valid for this run, I encourage you to read it if you haven't seen a run using the Coin Case glitch before.
In this submission text, I will merely point out the differences and improvements compared to FractalFusion's submission, which is the fastest submitted route at the time of writing.
Categories
- Aims for the fastest completion time
- Heavy luck manipulation
- Executes arbitrary code
Used emulator: BizHawk 1.5.3
About the run
Version choice
There is virtually no inherent time difference between the Gold and Silver version.
The largest inherent time difference that I'm aware of is the title screen loading 2 frames slower in Silver compared to Gold.
That means whether Gold or Silver is faster depends completely on the route.
This run uses the Silver Version for a couple of reasons:
- Faster luck manipulation of the Trainer ID: This run depends on having a specific Trainer ID, which is faster to get in Silver by 14 frames.
- Different sprites: Pokémon sprites are different in Gold and Silver, which has no real time implications (some load a frame faster than others, but nothing significant), but helps making the run more enjoyable to watch for everyone who saw the previous submissions and doesn't want to see the exact same images all over again.
- "Because I can": Silver has been ignored to an extend where some people asked whether the Coin Case glitch is even possible in Silver. Here is your answer.
Coin Case Glitch improvements
While the Coin Case glitch setup is pretty much fixed up to the point where your program counter lands in the party Pokémon data, you have multiple options from there on.
This run uses the same basic idea as FractalFusion's run, namely using the move Tackle, which conveniently corresponds to ld hl,d16
, to load a jump to the Box names which are set up to spell out our program.
It uses some improvements, though, the biggest one is skip teaching Mud-Slap, but instead exploit that an empty move slot has the value 00.
That means, instead of
Move 3 Move 4 Trainer ID Code
21 BD D8 E9 ld hl,D8BD; jp (hl)
we use
21 00 D9 E9 ld hl,D900; jp (hl)
which puts us in Box 8 instead. The time saved by not learning Mud-Slap outweighs the additional time needed to scroll down when renaming the boxes.
Another improvement is the written program itself, now only requiring two box names instead of three.
Disclaimer: The explanation of the inner workings of the arbitrary code execution follows, which is lengthy, technical an maybe boring to you. So if you're not interested you can just call it magic and skip the rest of this section.
The program itself is a simple interpreter loop, it reads joypad inputs and executes them as opcodes:
Bytes | Instruction | Comment |
---|
Box 7 | | |
aa | xor d | d stores last joypad input: find out differences to current input |
ea fd f8 | ld (f8fd),a | Write difference; will be executed as opcode later in the cycle |
aa | xor d | Restore current joypad input value |
f5 | push af | Copy current joypad input from a... |
d1 | pop de | ... to d (store it as last joypad input) |
f1 | pop af | Restore a and f from the previous cycle |
50 ($f8fd) | (any) | Execute opcode written earlier this cycle |
Box 8 | | |
f5 | push af | Save a and f for next cycle |
a4 | and h | Clears carry flag, needed for the jump |
fa a6 ff | ld a,(ffa6) | Entry point; reads current joypad inputs into a |
d2 f5 f8 | jp nc,f8f5 | Loop back to start of Box 7; carry will never be set |
Since this loop will run hundreds of times per frame, but we can only give it one input per frame, we need to prevent it from running the same opcode over and over again.
This is done by not using the input directly as the opcode, but the difference of the input from the previous one.
This means that for all cycles in a frame after the first one, the executed opcode is 0, which conveniently is nop
.
Note that there is no way to give the opcodes additional data, so you are limited to using opcodes that don't require any (using other opcodes doesn't break the program, but messes with the stack since the push af
is not executed but used as data instead).
This may seem quite limiting at first, since there is no way to set a register to a value you want directly, but it's possible to circumvent this using the properties of the xor operation.
It's easiest to see in an example from the actual inputs used in this run:
Our goal is to set l to 0x22 (the current value is 0x83), and the last input (stored in d) is set to 0xB3.
This can be done using only 2 frame cycles:
Input opcode operation comment
0x48 (0xb3 xor 0x48 = ) 0xfb ei Does nothing important.
0x22 (0x48 xor 0x22 = ) 0x6a ld l,d loads d=0x22 into l
The trick is to set it up so that the pressed buttons are your data while the difference is your desired opcode.
Because of the properties of xor, there is always such a number to achieve this and it takes only 1 frame to set it up.
The opcode executed during the setup cycle is irrelevant, as long is does not crash the program or corrupt the values you currently care about.
The inputs used in this run make use this technique extensively:
Joypad Command
e1 // Initial value of d when entering the loop
e8 09 (add hl,bc) // d900 + 00a4 = d9a4
cd 25 (dec h)
e0 2d (dec l)
97 77 (ld (hl),a) // d8a3 <- 0; Enable Red in Mt. Silver
b3 24 (inc h)
48 fb (ei) // setup cycle
22 6a (ld l,d)
06 24 (inc h)
71 77 (ld (hl),a) // da22 <- 0; Having no Pokémon in your party wins all battles instantly
b0 c1 (pop bc) // setup cycle
d2 62 (ld h,d)
61 b3 (or e) // setup cycle
0b 6a (ld l,d)
04 0f (rrca) // setup cycle
76 72 (ld (hl),d) // d20b <- 76; Change tile the character stands on, needed for warping
63 15 (dec d) // setup cycle; This temporarily corrupts d, but it is fixed in the next cycle
19 7a (ld a,d)
bb a2 (and d) // setup cycle; 0xbb and 0x19 = 0x19
d9 62 (ld h,d)
fb 22 (ld (hl+),a) // d90b <- 19; Y coordinate of the character position
79 82 (add d) // setup cycle
03 7a (ld a,d)
21 22 (ld (hl+),a) // d90c <- 03; X coordinate of the character position
65 44 (ld b,h) // setup cycle
17 72 (ld (hl),d) // d90d <- 17; (Glitch) map entrance 0x17, directly in front of Red
34 23 (inc hl)
16 22 (ld (hl+),a) // d90e <- 03; Map group
0c 1a (ld a,(de) ) // setup cycle
46 4a (ld c,d)
36 70 (ld (hl),b) // setup cycle
44 72 (ld (hl),d) // d90f <- 44; Map index: In Mt. Silver
2d 69 (ld l,c)
71 5c (ld e,h) // setup cycle
0b 7a (ld a,d)
29 22 (ld (hl+),a) // d946 <- 0b; Set warp pointer to our fake warp data
5d 74 (ld (hl),h) // d947 <- d9
79 24 (inc h) // setup cycle
81 f8 (ld hl, sp+f5)
eb 6a (ld l,d)
12 f9 (ld sp,hl) // Fix the stack pointer; leave directly to the overworld
db c9 (ret) // Return to normal game code
Route details and improvements
Intro
- I set the text speed to fast, turn off battle animations and set the battle style to "SET" to make the overall game play faster.
- The Trainer ID is manipulated to be 0xD9E9, which is important for the Coin Case glitch setup.
- Date and time are irrelevant to the route, using the defaults is the fastest.
- I name the player "A". Like in Gen I, each character printed on the screen cost one frame, making shorter names faster. Also, "A" and all characters which are only one button press away (namely "B", "I", "J" and "a") cost no extra frames to input.
New Bark Town
- Totodile's DVs don't need to be very specific for this run, 14 Attack, 6 Defense, 8 Speed and 15 Special suffice to kill everything as fast as possible. I got 10 Defense and 15 Speed instead, oh well.
Cherrygrove City
- Rival's Chikorita has 21 HP, Scratch does 5 HP and a critical hit 8 HP of damage. That means it can be killed with two crits and a non-crit (doing non-crits is faster since the "Critcal Hit!" message costs time). Like in Gen I, however, getting max damage out of a move has a significantly lower chance (1/39) than any other value, due to the way integer rounding is done. That means that max crits have a 1/624 (= 1/16 * 1/39) chance of happening. Being able to perform these max damage attacks will save time in multiple fights throughout the run.
- Poison warping is not used, since is costs more time than it gains. It saves 00:03.72s to do, but you miss out on getting Pokéballs, which are important and take longer to pick up (00:05.76s) later or buy in a mart (00:13.56s).
Violet City
- The AI of Bird Keepers forces them to use a damaging move whenever possible, so taking damage from Spearow's Peck and Pidgeotto's Gust is unavoidable.
- Falkner's Pidgey could be killed using only two Rage crits, but using three noncrits allows a OHKO on Pidgeotto and is therefore faster.
- After beating Falkner, the Boxes are renamed to spell out the program for the Coin Case glitch.
- Togepi's DVs are manipulated to be 0x18A9, which sets up a relative jump (
jr a9
) needed in the Coin Case glitch. There are plenty of other values that would work as well, since it's not important where we land exactly.
- I skip visiting the mart; again, it costs more time than using X-Attack in battles save. I also don't need to buy a Pokéball since I skipped poison warping.
Route 32
- After beating Albert, a wild Bellsprout is encoutered to create its PokéDex entry. It is later needed to trigger the Coin Case glitch.
Union Cave
- Using Rage is the fastest way to beat Russell's Geodudes without X-Attacks.
Slowpoke Well
- I catch a Slowpoke in Slowpoke Well, since it (along with Rattata) knows exactly 3 moves including Tackle, which is needed for the Coin Case glitch setup. Because of very technical details (a carry flag being set or not at the right time), Rattata does not work with the setup I'm using.
- The final Rocket's Koffing is another example of saving a crit and therefore time by hitting max crits.
Azalea City
- Without X-Attacks, beating the arena before the rival is way faster (it may even be faster when using X-Attacks as well).
Ilex Forest
- When hunting Farfetch'd, the route used here is not the shortest route in terms of steps, but it is faster by around 1.5 seconds (assuming you get no encounters of course).
- Cut is taught to Croconaw. Ideally, you would want to teach it to your caught Pokémon which hasn't got four moves already, but there is no convenient Pokémon that can learn both Tackle and Cut (Sentret is the closest to it, but it has only one move when caught and teaching it 2 moves takes longer than teaching Cut to Croconaw).
- Slowpoke's moves are swapped and it is switched with Togepi while we are in the menu for using Cut anyway, which saves time.
Goldenrod City
- Super Nerd Eric is the last trainer before finally getting the Coin Case and performing the glitch. Cut is used to OHKO the Grimers, its shorter name makes it faster than the other moves.
- The text box for picking up the Coin Case resets the BG map window, so taking 3 steps to the left puts us in the right position to enable the needed jump when activating the Coin Case.
- Bellsprout's cry is played by checking the PokéDex and the Coin Case glitch is triggered.
- Due to the executed code, there now is a warp to Mt. Silver directly to our left, so by walking to the left we end up in Mt. Silver right in front of Red. Also, because we returned directly to the overworld without leaving the item menu first, the screen is not redrawn and we see the item menu instead of the overworld until the map transition is done.
- When talking to Red, the fight is skipped since the game thinks we have no Pokémon in our party, and the credits roll.
Route frame differences
Here is an exact section-by-section breakdown of the amount of frames gained or lost, compared to FractalFusion's run.
Section | Time difference | Comment |
---|
Title screen | -00:00.75s | Faster luck manipulation of the Trainer ID |
Oak's introduction | -00:02.38s | Leaving the default time of day and using a faster name |
Get starter | -00:01.12s | Faster DV manipulation |
Cherrygrove Rival | -00:00.52s | Using 2 max crits + max noncrit instead of three crits |
Finish Mystery Egg errant | +00:03.72s | Not using the poison warp |
Youngster Mikey | -00:00.37s | |
Violet City Gym | -00:00.37s | |
Leave Violet City | -00:16.02s | Not visiting the mart |
Enter Union Cave | -00:00.50s | |
Hiker Russell | +00:01.66s | No X-Items results in a slower fight |
Slowpoke Well | +00:29.35s | Catching a Slowpoke |
Slowpoke Well cleared | -00:00.52s | Max crit + max noncrit against Koffing |
Azalea Gym and Rival | +00:00.40s | No X-Items results in a slower fight |
Enter Goldenrod City | -00:23.44s | Skip catching Rattata; doing Coin Case glitch setup
|
Coin Case used | -00:14.67s | All Coin Case glitch setup already done |
Sum | -00:25.53s | |
Noxxa: It's time to see if we finally can get a really good glitched Gold/Silver run published. Accepting as an improvement to the
published movie.