Submission #4160: FractalFusion's NES Wheel of Fortune in 01:55.18

(Link to video)
Nintendo Entertainment System
baseline
FCEUX 2.2.0
6922
60.0988138974405
2096
! Savestate
! FM2 file begins from a savestate
Wheel of Fortune.nes
Submitted by FractalFusion on 1/14/2014 10:07:52 PM
Submission Comments
FractalFusion's NES Wheel of Fortune in 1:55.18 (6922 frames). An improvement over PikachuMan's previous submission by about 26 seconds.

Goals

  • Starts from savestate+reset.
  • Aims for fastest time from reset to selecting END on the bonus round which results in a bonus round win.
  • 2P input in a 1P game.
  • Manipulates luck.

Why from savestate+reset instead of power-on?

The game uses the RAM range 0x780-0x7FC as a bitfield for the status of its 1000 puzzles. Bit 0 means puzzle is available for selection; bit 1 means puzzle cannot be selected/has already been done. But the game never initializes this range before using it; as a result, the initial RAM power-on state affects this range. The RAM initialization of FCEUX arbitrarily restricts which puzzles are available at first. However, we can use a preparation movie file to manipulate this range, and then start from a reset. This allows much more flexibility.
The preparation movie file, being also a verification movie file, proves that this RAM setup can be reached from FCEUX's power-on state. Nothing special about FCEUX's power-on state; with the right preparation movie file, this setup can be reached from any power-on state whatsoever. This is because, if the entire RAM range 0x780-0x7FC is filled entirely with 1-bits, the game zeroes the entire range next puzzle.
The preparation movie file is in the resource link in the Other information section below.

Verifying the movie

  • Run Wheel of Fortune-verificationfile.fm2 . Pause at the last frame, when frame display says "692332/692332".
  • Stop the movie.
  • Record a new movie starting from savestate (Record from -> Now).
  • Check the header of the new movie. The savestate in Base64 encoding should be the same as for this submitted movie.

Optimizing the TAS

Optimal puzzles

First, I found out where the puzzles were stored in the ROM (ROM file 0x109DE-0x14617, NES address 04:89CE-05:C607). There are 1000 of them. (At first sight, there appears to be 1002 of them, but one of them is a fake, corresponding to the title screen's WHEEL OF FORTUNE, and the other is a lost puzzle that the game's puzzle selection routine will never hit, ever.)
I used a C++ program to calculate a "score" for each puzzle, representing how long the puzzle takes. It is based off both the time taken to enter the puzzle on the solve screen, and the time for Vanna to reveal the puzzle. Each puzzle is calculated as follows:
  • Let val('A')=1, val('B')=2, ... , val('Z')=26. Let k be the number of letters in the puzzle. Let a(0)=val('A')=1 and a(k)=val(kth letter of puzzle). Let b(k)=a(k)-a(k-1).
  • Then the score of the puzzle is:
score=  42*k
      + sum(1 to k)[min( 1+8*|b(k)| , 117-8*(b(k)-16) , 129-8*(-b(k)-16) )]
      + min( 129-8*(15-a(k)) , 121-8*(a(k)-16) )
Puzzles with best results:
ScorePuzzle #Word
06420818TOOTSIE
06430806BEN HUR
06950340EUCLID
06970199BABE RUTH
07020451MONOCLE
07110442TUXEDO
07130138HONG KONG
07240618HIGH JINKS
07270686SO BE IT
07290010FORT KNOX
07290055BOOT HILL
07380456LULLABY
07450462CHESTNUT
07490922BIRTHDAY
07550803PSYCHO
07610688LOOK IT UP
07630953PAYDAY
07650850THE ILIAD
07660392COCONUT
07820122CAPE COD
07820366HAIRPIN
07820430HAIRPIN
07920969YOM KIPPUR
07930081HONOLULU
07930148ETHIOPIA
07970494SOUR NOTE
07980355GALILEO
(The full list is here. It is sorted by puzzle number though. You can sort by score in another program like Notepad++ . The list was generated using wofscript.cpp . See the resoure link in the Other information section below.)
These scores assume the standard first/second round structure.
Speed-up round is a bit different; you have to choose a letter in the puzzle before you are allowed to solve it. I quickly figured that choosing the U in EUCLID pretty much gives the best result; it saves the four seconds needed to go from E to U to C normally, and no other word comes close. It is also near the left side so Vanna doesn't have to walk far.
In bonus round, you choose five consonants and a vowel and if any of them are in the puzzle, they are turned over before you solve it. It is fastest to choose letters that aren't in the puzzle so Vanna doesn't have to do anything. Note that if the letters you choose uncover the whole puzzle (for example, if the puzzle is PSYCHO and you choose, well, PSYCHO), you still have to select END after the puzzle is uncovered, and no, timing out causes you to lose no matter what, in case you were thinking about abusing that.
The best bonus round puzzle happens to be the one with the best score above; that is, TOOTSIE. ZYXWVU can be selected while on the way to T. (The next couple best are MONOCLE, selecting BDFGHI along the way, and HONG KONG, selecting BCDFJI.)
This conveniently leaves BEN HUR and BABE RUTH for the first and second rounds, respectively. Checking shows that BEN HUR first and BABE RUTH second is a couple frames faster than the other way around.
There is the possibility of having a puzzle show up twice in a single game by zeroing the range 0x780-0x7FC during the game, I checked to see if it was feasible. However, EUCLID and TOOTSIE would be impossible for speed and bonus rounds in this case, and substituting other puzzles in their place would be too costly.
So, in conclusion, the puzzles I use are:
  • First round: BEN HUR (#806)
  • Second round: BABE RUTH (#199)
  • Speed-up round: EUCLID (#340)
  • Bonus round: TOOTSIE (#818)

Spin manipulation

It is possible to manipulate the speed-up round's spin. This is also where 2P input comes in.
I manipulate the spin meter to be as low as possible. It is impossible to manipulate the spin further with 1P input; however, with 2P input, the added delay variance is manipulated to be 0. The speed-up round's puzzle can also be manipulated with 2P input as well.

How to manipulate these puzzles (i.e. 0x780-0x7FC manipulation)

The RNG

The RNG (address 0x0A-0x0C) controls randomness in this game. It is influenced by input most of the time. The address 0x0A merely increments whereas 0x0B and 0x0C are chaotic. Usually, puzzle selection in this game (as well as speed-up round's spin) is determined by the RNG when address 0x0A is 200 (0xC8). At that time, only 38505 out of 65536 values for 0x0B-0x0C are possible.

Puzzle selection rules

As stated earlier, if the entire RAM range 0x780-0x7FC is filled entirely with 1-bits, the game zeroes the entire range next puzzle. (It also zeroes address 0x7FD for whatever reason.)
Now the game selects a puzzle using its puzzle select routine that is intricately connected to the RNG. If the routine gives a puzzle that already has its bit set to 1, it runs the routine again up to 7 more times with slightly different parameters. If that fails, the game stops trying to use its fancy routines. It simply takes a point in the range and finds the first puzzle from there that has bit 0.
Now, there are some limitations for puzzle selection. It is entirely possible that two given puzzles must occur in a particular order and never the other way around. Because of RNG timing, this restriction may be particular to the first/second/bonus rounds (the third speed-up round is less restricted). Puzzles 936-959 and 968-991 are extremely restricted. Fortunately, these limitations do not affect the choice of puzzles that I want.

For the puzzles used in this TAS

  • BEN HUR (#806) can be triggered directly; nothing else needed.
  • BABE RUTH (#199) requires all puzzles 96-198 to be done already (bit 1) as well as a sequence of puzzles for which there exists an RNG value causing the routine to try these puzzles before taking the first available puzzle from 96.
  • EUCLID (#340) requires (for optimal speed-up round spin manipulation) all puzzles 224-339 to be done already (bit 1) as well as a sequence of puzzles for which there exists an RNG value causing the routine to try these puzzles before taking the first available puzzle from 224.
  • TOOTSIE (#818) requires all puzzles 800-817 to be done already (bit 1), including BEN HUR (#806), as well as a sequence of puzzles for which there exists an RNG value causing the routine to try these puzzles before taking the first available puzzle from 800.
Note that the completion of BEN HUR in the first round satisfies the requirements for TOOTSIE in the bonus round.
The following RNG values are used, with the puzzle sequence that the puzzle selection algorithm tries:
  • BEN HUR: C8.C7FA (805 124 800 072 814 038 632 804 800 801 802 ... 806)
  • BABE RUTH: C8.6D39 (123 801 075 815 033 633 807 122 096 097 098 ... 199)
  • EUCLID: 99.2327 (333 918 252 672 998 165 999 251 224 225 226 ... 340)
  • TOOTSIE: C8.C7FA (805 124 800 072 814 038 632 804 800 801 802 ... 818)

Setting up the preparation movie file

The standard method for setting up bits in the 0x780-0x7FC range is to manipulate a particular puzzle for the first round and then immediately reset after the bit is set to 1. However, not all puzzles can be manipulated this way. Some puzzles require a manipulation in the third (speed-up) round. Thus, some puzzles will have to be solved in the first two rounds. As I was trying to optimize the preparation movie file as well, I chose the puzzles with the lowest possible scores.
The preparation movie, made with a lot of input file editing as well as a bunch of C++-generated information files, boils down to this:
(note that this preparation movie uses only one controller instead of two that the TAS uses)
  • Reach the first round and immediately reset after any one bit is set to 1 (no manipulation needed). Repeat this 511 more times. The range 0x780-0x7FC is now filled with 1-bits and will be zeroed out next puzzle.
  • Let the characters below denote the important RNG values to manipulate:
CharacterRNG value at C8.xxxx CharacterRNG value at C8.xxxx
06D39 c440F
1B4C8 d040C
21F30 e440C
3C7FA f040D
44434 gCD7E
58373 h8368
6C31A iC368
7C373 j8369
8C391 kC369
9040E m662A
a040F nD40A
b440E p4615
  • Perform the following manipulations in this order (brackets means solve the first two and manipulate the third for the speed-up round):
41(012)000000111(012)000011111111(012)00111115678(012)0(002)
000011(012)000111(012)0000000(002)(002)(002)00000001(012)
001111(012)00(002)001(012)0000(002)1(012)0011111111(012)
000(002)011111111(012)1(112)1(112)0000000001111111111(012)
(9a2)0000001bcdef(012)00011(012)00011(012)
00000011gg33333333hijk(m1n)m11(31p)

Memory range 0x780-0x7FC should now look like this. This is starting position for the TAS:
00000000 42000000 01900000 FFFFFFFF
FFFFFFFF FFFFFFFF FE000000 FFFFFFFF
FFFFFFFF FFFFFFFF FFFFF000 00000120
00000004 00000000 00000000 00000000
00000000 00000000 00040000 000000C0
00000000 80000000 00000000 00000000
00000000 FDFFC000 02000000 00000000
00000200 00000000 00000000 03

Other stuff

Can this TAS be played directly from power-on?

Sure. Just modify FCEUX's source code to write the above memory for 0x780-0x7FC at power-on and you're set.
OK, if you mean play directly from power-on on console, then probably not. Well, let's look at it closely. You'll need the following binary values for the following addresses ('.' means any bit value):
0x784: .1....1.
0x789: 1..1....
0x78C-0x797: 11111111
0x798: 11111110
0x79C-0x7A9: 11111111
0x7AA: 11110...
0x7CF: 11......
0x7D4: 1.......
0x7E4: 11111101
0x7E5: 11111111
0x7E6: 110.....
0x7F2: ......1.
0x7FC: ......11

Count them. That's 250 bits that must be manipulated at power-on. Assuming each bit can be set independently to 0 or 1 with 0.5 probability each, that's a probability of (0.5)^250 to get a RAM state that works. If you set your NES on fire with a laser beam and throw it into a pool of water, then maybe, just maybe it will give you a RAM state that works.
On the other hand, why bother?

How about console verification?

There is a method which may allow it to be verified as starting from reset (not from power-on) even though we have no idea of the NES power-on state. See the resource link in the Other information section below.
The file Wheel of Fortune-clearmem-e34f x 77.fm2 (which we call file A) sets 77 bits in 0x780-0x7FC using RNG value C8.E34F (resetting immediately after activating each puzzle, except the last one). Run this file over and over until, during running the file, the range 0x780-0x7FC is zeroed out after being filled completely with 1-bits.
To tell if this has happened, see the shape of the last puzzle and compare it with the 77 puzzles in wofconsoleverify-puzzleshapes.txt (#0512-0588). If the puzzle is not in there, then the zeroing out did not happen. If the puzzle is there and does not have a star, then the zeroing out happened (except see the last sentence below). If the puzzle is there and has a star, then run Wheel of Fortune-e34f.fm2 (sets only 1 bit in 0x780-0x7FC using RNG value C8.E34F) and check the displayed puzzle (which should be the next one in the puzzle order) to confirm whether or not it is one of these puzzles. Also, if this is the first time running file A and the displayed puzzle is the last one in the puzzle order (#0588), then the zeroing out cannot be confirmed to have happened.
Since there are only 1000 puzzles, file A needs only to be run at most 13 times. On average, it requires 6.5 times.
Once the range has zeroed out, run Wheel of Fortune-verifyfrom0mem.fm2 to set up the necessary RAM for this TAS. Whether or not any additional puzzles of 0512-0588 have been activated by file A after zeroing out the range does not matter; neither Wheel of Fortune-verifyfrom0mem.fm2 nor the actual TAS are affected.
Note that this whole set-up takes a long time (anywhere from 1h40m to 5h20m, though if power-on RAM is truly random, then it takes on average 3h30m).
Once everything has been set up, just run the TAS (should begin with a reset). The TAS itself requires two controllers, even though the setup files only require one.
I don't know if there are any other important details for sync on a real console, but this is how it works, theoretically.

Other information (disassembly, etc.)

Some more information regarding game mechanics and disassembly can be found at the Game Resources page.
The resource link contains:
  • The verification movie file Wheel of Fortune-verificationfile.fm2 .
  • The files for possible method of console verification Wheel of Fortune-clearmem-e34f x 77.fm2 , wofconsoleverify-puzzleshapes.txt , Wheel of Fortune-verifyfrom0mem.fm2 as described above.
  • The program to generate the full list of puzzles and calculate a score for each one wofscript.cpp . It requires a ROM for this game, renamed zzz.nes .
  • Files to help with RNG manipulation wofrandomtable1.txt , wofrandomtable2.txt wofrandomtable3.txt , wofrandomtable4.txt , as well as the programs to generate them wof.h , wofrandom1.cpp , wofrandom2.cpp , wofrandom3.cpp , wofrandom4.cpp and an intermediate file woftbl .
    • Given the current value of RNG as BC.xxxx, wofrandomtable1.txt gives the input values required to be pressed on 0x0A=BE, C0, C2 to manipulate the RNG from BC.xxxx to C2.0400.
    • Given the desired value of RNG to be C8.xxxx, wofrandomtable2.txt gives the input values required to be pressed on 0x0A=C4, C6, C8 to manipulate the RNG from C2.0400 to C8.xxxx.
    • Given the value of RNG as C8.xxxx, wofrandomtable3.txt reveals the sequence of puzzles that the routine tries, in a standard/bonus round, before taking the first available puzzle from the last number given.
    • Given the value of RNG as C8.xxxx, wofrandomtable4.txt reveals the sequence of puzzles that the routine tries, in a speed-up round, before taking the first available puzzle from the last number given.
There were other program files and tables involved when I figured out how to use 2P input to manipulate the speed-up round, but it is probably too confusing to include them. They pretty much work on the same idea as the above programs and tables anyway.

Thanks

Thanks to PikachuMan, MESHUGGAH, and everyone else in the discussion thread for the previous Wheel of Fortune submission.

Nach: Judging.
FractalFusion: Cancelling this submission as explained in the latter half of this post.
Last Edited by adelikat on 10/8/2023 3:21 PM
Page History Latest diff List referrers