Luck manipulation (Generation I)

From Glitch City Wiki
Jump to navigation Jump to search

Pseudorandom number generation in Pokémon Red, Blue, and Yellow uses the rDIV function, which is stored in hardware and thus makes intentionally landing on a specific value in these games very difficult and unreliable. The only known emulators that emulate this function properly are BGB, Sameboy, and Gambatte-Speedrun. The function also behaves differently between the original Game Boy, Game Boy Color, Super Game Boy, Super Game Boy 2, and 3DS Virtual Console. However, certain series of timed inputs based on the DMG-001's rDIV function have been discovered for use in speedrun routes and work as intended nearly all of the time provided the player does not miss any input windows. The save file's in-game time also affects the initial seed, and at times can cause one of these procedures to fail. What is needed to fix this is simply saving again.

Encounter slots are determined by a variable known as the DSum (the sum of hRandomAdd + hRandomSub in random.asm in pret's disassembly), which can be predicted within a reasonable range once the current value is guessed through the encounter slot of the last wild Pokémon encountered.

Mechanics of the RNG

The RNG itself is a simple function:

Random_::
; Generate a random 16-bit value.
	ld a, [rDIV]
	ld b, a
	ld a, [hRandomAdd]
	adc b              ; add [rDIV] and the carry flag (either 0 or 1) to hRandomAdd
	ld [hRandomAdd], a
	ld a, [rDIV]
	ld b, a
	ld a, [hRandomSub]
	sbc b              ; subtract [rDIV] and the carry flag (which will come from the above addition) from hRandomSub
	ld [hRandomSub], a
	ret

Here, rDIV is the Divider Register, which is initialized to a fixed value during the booting process, and is incremented at a rate of 16384Hz (~16779Hz on SGB), or every 256 cycles.

(Note: In this article, the word "cycles" refers to T-cycles, where the CPU clock frequency is 4.194304 MHz. Since all instruction timings are divisible by 4, some people use M-cycles, which is defined as 4 T-cycles (or 1 nop). For the BGB debugger, the unit of all "CLKS" variables is 2 T-cycles (or 1 doublespeed nop), although their values may be slightly inaccurate when interrupts are involved.)

At the very beginning of the game's startup process (before the copyright screen), hRandomAdd ($FFD3) and hRandomSub ($FFD4) are zeroed with the entire HRAM; apart from that, Random_ is the only place in the code that directly changes them. If the exact timings and circumstances (actually only the carry flag matters) of those calls can be predicted (e.g. in a TAS or shortly after a hard reset), then the results can be predicted too. That's the principle behind luck manipulation.

Advancement of the RNG

Usually, the RNG is advanced (i.e. Random_ is called) whenever a random number is needed. In addition, it is also advanced once per frame during the V-Blank interrupt. Since the game doesn't need random numbers very often, the majority of RNG advancements come from the V-Blank.

Since the timing of the V-Blank interrupt itself is almost fixed (with one source of deviation mentioned below), the timing of those framely RNG advancements only depend on the few lines of code between the start of the VBlank function and the Random_ call. As a result, the processor time taken by many things that happen during the main loop doesn't actually change the timing of framely RNG advancements. This fact makes luck manipulation much simpler.

There is one complication to the timing of the V-Blank interrupt. Usually, the game executes a "halt" instruction each frame after the necessary processing, to put the console in "sleep mode", and wait until an interrupt (usually V-Blank) wakes it up. However, when there is too much processing to do, or when the game is busy-waiting for user input, the V-Blank interrupt may happen before the "halt" instruction. In this case, the game need to wait for the current instruction to finish before executing the V-Blank routine. This results in a deviation of up to 20 cycles for the timing of the V-Blank interrupt, which may be enough to affect the RNG. Everything that happens during the previous frame may affect this deviation, but thankfully the odds that it actually changes the RNG is low, because the value of rDIV is only increased every 256 cycles.

DSum manipulation

"DSum manipulation" is considered one of the easier RNG manipulation techniques because it doesn't require frame-level precision. The DSum is the sum of hRandomAdd and hRandomSub (mod 256). Since during any RNG advancement, the main part of their change is rDIV being added to the former and subtracted from the latter, the DSum remains nearly constant after a single RNG advancement.

Two factors cause the DSum to change:

  • A carry flag is added to hRandomAdd and subtracted from hRandomSub, but they are not the same carry flag. The carry flag added to hRandomAdd comes from some previously executed code, and the carry flag subtracted from hRandomSub comes from the addition to hRandomAdd.
  • Since the value of rDIV is read twice, it may change between those two reads.

Their effects can be evaluated as follows:

  • During the framely advancement (the most frequent type of advancement), the carry flag added into hRandomAdd comes from PrepareOAMData, which prepares an OAM buffer from the overworld sprites. It is activated in the overworld and deactivated during a battle. As a side effect, it always sets the carry flag to 0 when it's activated, and to 1 when it's deactivated. This means that during a battle, this factor increases DSum by 1 every frame.
  • The carry flag subtracted from hRandomSub comes from the previous addition. Since rDIV changes regularly in a range of [0, 255], the number added can be considered to be about 128 on average, so a carry happens about every two frames on average. This means that this factor always decreases DSum by about 0.5 every frame on average.
  • A change of rDIV between the two reads is comparatively rare, and doesn't affect the DSum much.

In summary, the DSum decreases in the overworld, and increases during a battle, by about 0.5 per frame on average.

Application

The main use of DSum manipulation is to control wild encounters, which depend on both hRandomAdd and hRandomSub:

  • For each step taken in an area with encounters, if the current value of hRandomAdd is less than the encounter rate, an encounter happens.
  • When an encounter happens, the species and level of the wild Pokémon depends on hRandomSub (without advancing the RNG), with each area having ten "encounter slots":
Encounter slot hRandomSub Range
1 0-50
2 51-101
3 102-140
4 141-165
5 166-190
6 191-215
7 216-228
8 229-241
9 242-252
10 253-255

Since in most areas the encounter rate is not very large, the range of hRandomAdd is already limited. If the DSum is known, then the range of hRandomSub becomes limited too. Therefore, taking steps in the grass only when DSum is in a certain range allows the player to manipulate the likely encounters.

The DSum manipulation can either be used directly, or it can be referred to when searching for more precise RNG manipulation methods to decrease the search space.

Hard-reset RNG manipulations

Most random events in the game depends only on hRandomAdd, which changes rapidly each frame, so manipulation of those events needs frame-level precision. Since it is impossible in real-time gameplay to do all inputs in a playthrough frame-perfectly, those manipulations usually begin by saving the game and hard-resetting the console, in order to reset the state of the RNG.

Even doing frame-perfect input for a short period of time may seem impossible at first. However, the key is that the game doesn't accept inputs on all frames. If the player holds down a button before the game begins to accept it, then it will be accepted on the first frame the game does, essentially becoming a frame-perfect input. This technique is known as "buffering" an input in the speedrunning community.

A factor that cannot be controlled in RTA, even with a hard-reset and perfect inputs, is the in-game timer (IGT), which goes into the save file. It is in an hours/minutes/seconds/frames format, and is advanced every frame. A carry from the "frames" byte to the "seconds" byte incurs a rather large delay of 72 cycles (or larger if there are also carries on higher bytes, but that's relatively rare). In Red and Blue, the IGT is increased after the RNG advancement every frame, but it can still affect the RNG if it precedes an in-game RNG call or if it slightly changes the timing of a VBlank. In Yellow, the IGT is increased before the framely RNG advancement, resulting in a larger effect. Manipulation failures due to a bad value of IGT is usually referred to as an "IGT0" failure.

Most manipulations used in RTA are found by brute-force searches. The search space consists of inputs with large frame windows, some of which are detailed below. The 60 possible value of IGT frames are enumerated in order to determine the success rate of each manipulation.

BIOS

Generation I Pokémon RTA usually are done on the GBA platform (or an emulation of it), with the Game Boy Color BIOS. The BIOS provides some RNG manipulation opportunities, because it accepts inputs to change the color palette. This subtly affects the timing of the V-Blank interrupts, changing the progression of the RNG.

Intro

It takes at least two inputs from a hard-reset to continue playing a saved game: One to open the title menu from the title screen, and one to select "continue". Furthermore, for the purpose of speedrunning, it's desirable to skip the long intro animation. The most common technique to get through the intro is:

  • When the copyright screen appears, hold Up + Select + B. This skips the Game Freak animation completely, and the Gengar vs. Nidorino (Red) / Jigglypuff (Blue) fight animation at the first opportunity.
  • When the screen fades to white from the Gengar fight, switch to holding Start. This opens the title menu at the first opportunity.
  • When the screen turns white again, switch to holding A. This will continue the saved game at the first opportunity.
  • When the title menu disappears and the screen turns white yet again, switch to holding the first overworld input (usually a direction).

There are more complex techniques to manipulate the RNG here with humanly possible inputs, but they are generally harder/takes more time than RNG manipulation with overworld inputs, so they are only used in specific cases (e.g. when manipulating the trainer ID, for which there is no opportunity for overworld inputs).

Overworld

This is the main component of most manipulations. For the purpose of keeping up frame-perfect inputs, the player should be constantly walking, which also manipulates the RNG slightly compared to staying put. At the beginning of each step, the tilemap is updated so that the new row or column that appears on the screen is displayed properly, which delays the framely RNG advancement slightly. The time delta is either 8.1875 (row) or 7.984375 (column) rDIV periods, so a step generally adds 8 to the RNG state (for one time only), but it can be either 9 or 7 depending on the frame on which this happens. Plus, at the end of each step, map loading will cause a "lag frame" where the game doesn't hit a halt instruction before V-Blank, which as mentioned above may slightly affect the RNG too.

Another main factor that affects overworld RNG is sprites. The running time of PrepareOAMData depends on how many sprites are visible on the screen (and some static properties of them), which affects the framely RNG advancement. In addition, for sprites currently on screen, the RNG is called to determine the direction the sprite would walk in (even if it cannot actually walk), and the delay until the next movement. This cycle begins when the sprite is first loaded on screen. Notice that the movement state of sprites persists when it becomes off-screen and even after saving and resetting, as long as the player doesn't leave the map. Therefore all manipulations assume that all sprites involved are initially unloaded.

The game also checks whether the A and Start buttons are pressed between steps. If the A button is pressed, and if it is not pressed the last time game checks for inputs, the game will take an "overworld frame" (2 frames) to check if the player can pick up a hidden item, which is a technique to delay 2 frames without releasing the directional button. Usually the exact tile on which A is pressed doesn't matter, but it may matter when there are sprites on screen, around the map boundary, or if the effects of a step ends up changing because of the delay.

Some manipulations (most notably Mt. Moon manipulations) require the player to actually pick up items. Since item balls are sprites, they must be picked up at the exact frame. Thankfully, the item pickup text box closes without an input, so the player can simply hold the direction to proceed.

Battle

When a manipulation extends into battle, it's usually to catch the wild Pokémon that has been manipulated to appear. It usually suffices to throw the Poké Ball on the correct frame. The usual procedure is as follows:

  • Press A or B to clear the "Wild <Pokémon> appeared!" text box. Release both keys when the text box becomes blank.
  • (For Red/Blue) Some time after the "Go! <Your lead Pokémon>!" text (usually during the Pokémon's cry), hold Down + A. This opens the item menu at the first opportunity.
    • This makes use of a quirk in the menu UI, where Up + A or Down + A can be handled in the same frame.
    • This trick doesn't work in Yellow, because in Yellow the A button has priority. Therefore the player must wait for the cursor to be on "ITEM" before pressing A (a very tight 3 frame window).
  • When the menu jingle plays, switch to holding A + B (or A + Up/Left/Right). Assuming Poké Ball is the first item in the inventory, this will throw the ball.
    • This makes use of another quirk in the menu UI, where all buttons currently pressed down are checked when any button is newly pressed.

The most difficult step is the first step, because the window to press A or B is only 4 frames. Mashing a button is not the best way to hit the window, because most people cannot mash at an interval of 4 frames. The recommended technique is to "stagger" an A press with a B press, which effectively extends the window to 8 frames, making it easier to reliably hit with practice.

Another argument against mashing is that pressing A or B during a text box may actually affect the RNG slightly. When a text is displaying, holding down A or B will make the game delay only 1 frame between characters, essentially displaying at the "fast" text speed. Although in speedruns the text speed is usually already set to "fast", the game still checks for an input during the 1 frame delay, in a busy-waiting loop. Pressing A or B breaks that loop, and thus has a tiny probability to affect the RNG.

Note that the RNG for a successful catch is checked after the "<Player> used Poké Ball!" message is displayed. Therefore the lead Pokémon's name length, species (affects the cry length) and the player's name length all affects the manipulation.

External links

  1. RNG manipulation FAQ on Pokémon Speedruns wiki
  2. DSum manipulation FAQ on Pokémon Speedruns wiki
  3. random.asm