Difference between revisions of "Score Manipulation"

From Super Mario World Speedrunning Wiki
Jump to: navigation, search
(Created page with "=Overview= '''Score Manipulation''' is a very high-level technique that involves planning out exactly how many points you get in every level, so that your total score is a val...")
 
(added runtime)
Line 40: Line 40:
  
 
The game will also have to replace the leading zero with an empty tile, so that the score shows 386750 instead of 0386750.
 
The game will also have to replace the leading zero with an empty tile, so that the score shows 386750 instead of 0386750.
 +
 +
==Runtime==
 +
 +
Here is a formula for the amount of time (in microseconds) the system needs to display a given <tt>score</tt> with the digits of <tt>ABCDEF0</tt>, where <tt>M</tt> is the number of leading zeros that turn into empty tiles. Note that the formula is slightly different on different versions of the game (due to ROM alignment):
 +
 +
<pre>
 +
Japanese version: cycles = 2948 + 154 * M + 484 * (A + B + C + D + E + F)
 +
PAL v1.1 version: cycles = 2948 + 154 * M + 484 * A + 496 * (B + C + D + E + F)
 +
All other versions: cycles = 2948 + 154 * M + 484 * A + 490 * B + 496 * (C + D + E + F)
 +
 +
subtract 6 cycles if score is 0
 +
 +
NTSC systems: time_in_us = cycles / 21.47727
 +
PAL systems: time_in_us = cycles / 21.28137
 +
</pre>
 +
 +
Here are some various scores with their runtime on various versions of the game:
 +
{| class="wikitable"
 +
|-
 +
! Score !! (J) Cycle Count !! (J) Runtime (us) !! (U) Cycle Count !! (U) Runtime (us) !! (E0) Cycle Count !! (E0) Runtime (us) !! (E1) Cycle Count !! (E1) Runtime (us)
 +
|-
 +
| 0 || 3866 || 180.0 || 3866 || 180.0 || 3866 || 181.7 || 3866 || 181.7
 +
|-
 +
| 99950 || 18744 || 872.7 || 19128 || 890.6 || 19128 || 898.8 || 19128 || 898.8
 +
|-
 +
| 100150 || 6490 || 302.2 || 6568 || 305.8 || 6568  || 308.6 || 6574 || 308.9
 +
|-
 +
| 1000000 || 3432 || 159.8 || 3432 || 159.8 || 3432 || 161.3 || 3432 || 161.3
 +
|-
 +
| 9999990 || 29084 || 1354.2 || 29570 || 1376.8 || 29570 || 1389.5 || 29624 || 1392.0
 +
|}
 +
 +
For reference, one frame is 16639.3 microseconds long (19997.2 us in PAL regions).
  
 
=Theory=
 
=Theory=

Revision as of 00:29, 16 February 2023

Overview

Score Manipulation is a very high-level technique that involves planning out exactly how many points you get in every level, so that your total score is a value that takes a minimal time for the game to calculate and draw to the screen. Managing the player's score is a small portion of all of the things the game needs to do every frame, but if the score is not optimal, it can cause the game to lag more often during laggy portions of the run. This mainly includes the spotlight and fadeout at the end of every goal tape march, as well as every keyhole animation. However, certain levels are particularly laggy on their own, such as Back Door, Forest of Illusion 2, and Sunken Ghost Ship. Having an optimal score while playing these levels will reduce lag.

A good score manipulation route will have the player collect or purposefully *not* collect certain items without losing any time (for example, whether to collect a Dragon Coin or to jump over it). However, due to how little of time score manipulation actually saves and how much extra effort it takes to pay attention to your score at all times, most runners forego extremely optimal score manipulation. A couple more general rules are easier to follow to get the most beneficial improvements with the least amount of effort:

Score Calculation Algorithm

The player's score is stored as a 24-bit hexadecimal number in memory. This number is converted into a 6-digit decimal number every frame and displayed on the status bar (the last zero in the score is basically just for looks). There is no hardware in the Super Nintendo to manage numbers this large, so this conversion is done in software. It's not a slow algorithm, but it isn't fast by any means. To make matters worse, it is run every single frame. This means, if you have a score that makes this algorithm take a long time, there is less time for the game to process other game things, like physics, collisions, etc. This makes the game more likely to lag.

Pseudocode

Here is a simplified version of the code that runs to convert the score from hexadecimal to decimal. You can find the original code here.

let tempScore <- score
for i from 5 to 0 do
   let digit = 0
   while tempScore - 10^i > 0 do
      tempScore <- tempScore - 10^i
      digit <- digit + 1
   end
   set status bar score at the 10^i's place to digit
end
let k <- 5
while status bar score at the 10^k's place is 0
   set status bar score at the 10^k's place to an empty tile
   k <- k - 1
end

Essentially, for each of the digits in the score, that number in the score determines how many times the temporary score will be adjusted by a power of 10. For example, a score of 386750 will:

  • (0) subtract 100000 from the score 1 time
  • (3) subtract 10000 from the score 4 times
  • (8) subtract 1000 from the score 9 times
  • (6) subtract 100 from the score 7 times
  • (7) subtract 10 from the score 8 times
  • (5) subtract 1 from the score 6 times.

Notice that there will always be one subtraction first, so the amount of times is that digit plus one.

The game will also have to replace the leading zero with an empty tile, so that the score shows 386750 instead of 0386750.

Runtime

Here is a formula for the amount of time (in microseconds) the system needs to display a given score with the digits of ABCDEF0, where M is the number of leading zeros that turn into empty tiles. Note that the formula is slightly different on different versions of the game (due to ROM alignment):

Japanese version: cycles = 2948 + 154 * M + 484 * (A + B + C + D + E + F)
PAL v1.1 version: cycles = 2948 + 154 * M + 484 * A + 496 * (B + C + D + E + F)
All other versions: cycles = 2948 + 154 * M + 484 * A + 490 * B + 496 * (C + D + E + F)

subtract 6 cycles if score is 0

NTSC systems: time_in_us = cycles / 21.47727
PAL systems: time_in_us = cycles / 21.28137

Here are some various scores with their runtime on various versions of the game:

Score (J) Cycle Count (J) Runtime (us) (U) Cycle Count (U) Runtime (us) (E0) Cycle Count (E0) Runtime (us) (E1) Cycle Count (E1) Runtime (us)
0 3866 180.0 3866 180.0 3866 181.7 3866 181.7
99950 18744 872.7 19128 890.6 19128 898.8 19128 898.8
100150 6490 302.2 6568 305.8 6568 308.6 6574 308.9
1000000 3432 159.8 3432 159.8 3432 161.3 3432 161.3
9999990 29084 1354.2 29570 1376.8 29570 1389.5 29624 1392.0

For reference, one frame is 16639.3 microseconds long (19997.2 us in PAL regions).

Theory

As you can see, the higher a digit is in the score, the more times the algorithm has to loop and subtract a power of ten from the score. This means that scores with low digits take the least amount of time to convert and draw. However, the empty tiles that precede smaller scores also take extra time to draw. This means the most optimal score is exactly 1000000. The least optimal score is the maximum score of 9999990. In general, you can estimate how "good" of a score you have by summing all of the digits of the score together. For example, the score of 386750 has a value of about 29. From this, you can tell that a score of 100000 is much better than a score of 99950, since the values for those scores are 1 and 32, which are vastly different. If you had a score of 99950, you can spin jump on an enemy to kill it, gaining 200 points. This would put you at 100150, with a value of 7, which is still much better than 32.