Voltorb Flip board generation explained

What to expect from this page

Voltorb Flip is a minigame in Pokémon HeartGold and SoulSilver that you either love or hate. As part of the research to make the manipulation tool, I also investigated how fields are generated. These are my results. Familiarity with RNG terminology is assumed (such as the term seed and what advancing it means).

I will be referring to the random number generator (RNG) several times. The RNG in question is a standard Mersenne Twister (commonly called the incubation RNG or IRNG in Pokémon RNG manipulation circles), namely MT19937. While Voltorb Flip is being played, nothing else accesses the IRNG.

IRNG initialization

The first thing to understand is the state of the RNG and how Voltorb Flip interacts with it. The RNG seed is a function of the current time (local time as recorded on the DS) and the VBlank interrupt counter (commonly referred to as the delay in Pokémon RNG manipulation circles). Given:

Y
The current year minus 2000. If today is 2023-04-20, then Y = 23.
M
The current month. If today is 2023-04-20, then M = 4.
D
The current day of the month. If today is 2023-04-20, then D = 20.
h
The current hour. If right now is 2023-04-20T10:15:00 (in local time), then h = 10.
m
The current minute. If right now is 2023-04-20T10:15:00 (in local time), then m = 15.
s
The current second. If right now is 2023-04-20T10:15:00 (in local time), then s = 00. The DS does not respect leap seconds.
v
The VBlank counter. This counts how many times a VBlank interrupt happened since the game started. This is practically equal to the amount of frames processed since the game started. The lower bound seems to be around 420.

Then the seed S = Y + 0x100 ⋅ M ⋅ 0x10000 ⋅ D + 0x10000 ⋅ h + 0x1000000 ⋅ (m + s) (mod 232). The current date, time and VBlank counter are processed at the moment that on the title screen, the A button is pressed to select the save file (where also presented with the option to instead start a new game or change options).

It is assumed the player starts Voltorb Flip immediately; otherwise, certain events such as random calls (even if not picked up!) may advance the IRNG state.

Board generation

Determining the level

Boards are generated before the menu is shown. This applies both for the very first board and all subsequent ones. In other words, when you open Voltorb Flip, a board has already been generated; when you win, lose or quit a round, the next board has already been generated.

First, the next level is calculated:

  1. If at level 8 and the previous round has been won, stay at level 8.
  2. Move to level 8 if:
    1. the previous round was played at level 5 or greater;
    2. the previous 5 rounds were either won or quit out of (i. e. not lost); and
    3. at least 8 tiles were flipped in all of the previous 5 rounds.
  3. If the previous round has been won, move to the next level. This does not apply at level 7; the criteria for advancing to level 8 are exhaustively checked above.
  4. Otherwise, move to level l, where l is the number of cards flipped and no greater than the previous level. For example, if in the previous round at level 5 only 2 tiles were flipped (regardless of whether you quit or whether a voltorb was flipped), then the new level is level 2.

Board configuration

Before a board is generated, the board configuration is selected. There are 80 board configurations, 10 configurations for each level. The board configuration is chosen by:

  1. rolling for one random number,
  2. reducing it modulo 100,
  3. rounding it up to the nearest 10,
  4. dividing it by 10 again, then finally
  5. adding the current level multiplied by 10.

For example, at level 3, a random number is rolled equalling 123456789. 123456789 reduced modulo is equal to 89. Rounding 89 up to the nearest multiple of 10, we get 90. Dividing 90 by 10 yields 9. Adding the current level multiplied by 10 (= 30), we get board configuration 39.

The board configuration contains:

  1. how many Voltorbs to place;
  2. how many tiles of value 2 to place;
  3. how many tiles of value 3 to place;
  4. how many value tiles (tiles of value 2 or 3) may be in a row or in a column with 0 Voltorbs (free value tile);
  5. how many free value tiles may be on the board in total.

Board generation proper

The next step is to actually generate the board given the board configuration as follows and in this particular order:

  1. Set all tiles on the board to be value 1 tiles.
  2. Place the Voltorbs.
  3. Place the value 2 tiles.
  4. Place the value 3 tiles.
  5. Check if the board is valid. A board is valid when the constraints of the board configuration in terms of free value tiles are not violated. If the board is not valid, repeat from the beginning.

If after 1000 attempts no valid board is generated, the invalid board is kept anyway.

Tiles are placed as follows:

  1. Roll the RNG for a number and reduce it modulo 25. This determines for which field the value is changed. The locations are assigned from 0 through 24 starting at the top left, going left to right, then top to bottom. The top left tile is located at 0, the top right tile is located at 4, the leftmost tile of the second row from the top is located at 5 and the tile in the bottom right is located at 24.
  2. If there is already any tile that is not a value 1 tile at the rolled location, repeat placing this tile from the top.

If after 100 attempts, not all tiles of this kind could be placed, stop trying to place this kind of tile. This does not prevent other kinds of tiles from being placed; e. g. if generating the second of three tiles of value 2 and 100 failures total (including the first of the three) were encountered, the second and third tiles of value 2 will not be placed, but it will be attempted to place tiles of value 3 as normal.

Other sources of RNG advancement

This concludes board generation and thus most of the IRNG advancements. However, user actions do influence the RNG: Flipping any tile – including Voltorbs – that is located in a row and column, in which the Voltorb count is non-zero (risky tile) advances the IRNG; the random number is entirely unused. For abusing the field generation, however, this must be kept in mind.

Appendix: Board configurations

These are the board configurations (recall that every 10 configurations belong to a level). Note that the maximum values are exclusive, i. e. the free value tiles being equal to the limit in the configuration is a reason to reject the board.

typedef struct {
    unsigned int voltorbs;
    unsigned int twos;
    unsigned int threes;
    unsigned int maxFreeValueTilesInRowOrColumn;
    unsigned int maxFreeValueTilesTotal;
} BoardConfiguration;
 
BoardConfiguration configs[80] = {
    {6, 3, 1, 3, 3},
    {6, 0, 3, 2, 2},
    {6, 5, 0, 3, 4},
    {6, 2, 2, 3, 3},
    {6, 4, 1, 3, 4},
    {6, 3, 1, 3, 3},
    {6, 0, 3, 2, 2},
    {6, 5, 0, 3, 4},
    {6, 2, 2, 3, 3},
    {6, 4, 1, 3, 4},
    {7, 1, 3, 2, 3},
    {7, 6, 0, 3, 4},
    {7, 3, 2, 2, 3},
    {7, 0, 4, 2, 3},
    {7, 5, 1, 3, 4},
    {7, 1, 3, 2, 2},
    {7, 6, 0, 3, 3},
    {7, 3, 2, 2, 2},
    {7, 0, 4, 2, 2},
    {7, 5, 1, 3, 3},
    {8, 2, 3, 2, 3},
    {8, 7, 0, 3, 4},
    {8, 4, 2, 3, 4},
    {8, 1, 4, 2, 3},
    {8, 6, 1, 4, 3},
    {8, 2, 3, 2, 2},
    {8, 7, 0, 3, 3},
    {8, 4, 2, 3, 3},
    {8, 1, 4, 2, 2},
    {8, 6, 1, 3, 3},
    {8, 3, 3, 4, 3},
    {8, 0, 5, 2, 3},
    {10, 8, 0, 4, 5},
    {10, 5, 2, 3, 4},
    {10, 2, 4, 3, 4},
    {8, 3, 3, 3, 3},
    {8, 0, 5, 2, 2},
    {10, 8, 0, 4, 4},
    {10, 5, 2, 3, 3},
    {10, 2, 4, 3, 3},
    {10, 7, 1, 4, 5},
    {10, 4, 3, 3, 4},
    {10, 1, 5, 3, 4},
    {10, 9, 0, 4, 5},
    {10, 6, 2, 4, 5},
    {10, 7, 1, 4, 4},
    {10, 4, 3, 3, 3},
    {10, 1, 5, 3, 3},
    {10, 9, 0, 4, 4},
    {10, 6, 2, 4, 4},
    {10, 3, 4, 3, 4},
    {10, 0, 6, 3, 4},
    {10, 8, 1, 4, 5},
    {10, 5, 3, 4, 5},
    {10, 2, 5, 3, 4},
    {10, 3, 4, 3, 3},
    {10, 0, 6, 3, 3},
    {10, 8, 1, 4, 4},
    {10, 5, 3, 4, 4},
    {10, 2, 5, 3, 3},
    {10, 7, 2, 4, 5},
    {10, 4, 4, 4, 5},
    {13, 1, 6, 3, 4},
    {13, 9, 1, 5, 6},
    {10, 6, 3, 4, 5},
    {10, 7, 2, 4, 4},
    {10, 4, 4, 4, 4},
    {13, 1, 6, 3, 3},
    {13, 9, 1, 5, 5},
    {10, 6, 3, 4, 4},
    {10, 0, 7, 3, 4},
    {10, 8, 2, 5, 6},
    {10, 5, 4, 4, 5},
    {10, 2, 6, 4, 5},
    {10, 7, 3, 5, 6},
    {10, 0, 7, 3, 3},
    {10, 8, 2, 5, 5},
    {10, 5, 4, 4, 4},
    {10, 2, 6, 4, 4},
    {10, 7, 3, 5, 5},
};