Submission #4233: MrWint's GBC Pokémon: Silver Version "Coin Case glitch" in 30:39.49

Game Boy Color
(Submitted: Pokémon: Silver Version)
Coin Case glitch
BizHawk 1.5.3
109868
59.7275005696058
28174608
Unknown
Pokemon Silver Version (UE) [C][!].gbc
Submitted by MrWint on 3/29/2014 2:30:21 PM
Submission Comments

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

Most basic information can be found in my previous submission text.

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: Judging.
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.
Guga: Processing...
Last Edited by Memory on 1/8/2022 1:33 AM
Page History Latest diff List referrers