devlog.

AtlasGB · Corrections

TACKLE is 95 per cent and we said 94

A section of ours literally headed "two things the cartridge settled" recorded that TACKLE's accuracy byte is 240 — 94%, where the community usually quotes 95. It is 242. The claim had propagated into a policy document, a Rust module's doc comments and a project memory file.

242
The accuracy byte
94.9%
What that is
240
What we published
0
Runs affected

No run was affected — the code was reading the cartridge all along. Only the prose was wrong.

It was found by re-reading the move table while writing a page about discoveries, on the grounds that a page about discoveries which repeats a number without checking it is not worth having.

Read the table, and check the rows nobody disputes first

Six bytes per move, straight out of the cartridge:

MoveIdPowerAccuracy byteAs a percentage
POUND140255100%
SCRATCH1040255100%
VINE WHIP2235255100%
TACKLE333524294.9%
HYPER BEAM6315022989.8%
HYDRO PUMP5612020480.0%

Every row but the one under investigation matches the values Generation 1 is known for. That is what says the table is being read at the right offset with the right stride.

python3 - "$ROM" <<'PY'
import sys
rom = open(sys.argv[1], 'rb').read()
e = 0x0E * 0x4000 + (33 - 1) * 6   # Moves, 0E:4000, TACKLE is move 33
print("power %d accuracy %d (%.1f%%)" % (rom[e+2], rom[e+4], rom[e+4] * 100 / 255))
PY
# power 35 accuracy 242 (94.9%)
The whole correction, against your own cartridge. No ROM is distributed by the project; you point $ROM at yours.

Where 240 came from

94% of 255 is 239.7, which rounds to 240. It is the byte you get by computing it from a percentage you already believed, instead of reading it.

When you write down a number the cartridge holds, write down the command that reads it.

And the accuracy byte is not the accuracy anyway

The hit calculation scales a move's accuracy byte by the attacker's accuracy stage and the inverse of the defender's evasion stage. A policy reading the ROM value is reading a number that has not been true since the first SAND-ATTACK landed.

A Pidgey's SAND-ATTACK takes a 95% TACKLE to 62% and then 47%. Both ratios come out of a stage-modifier table read from the cartridge, located by searching for its own opening bytes — a signature that occurs exactly once — and re-checked on every read against two properties: the first pair must be 0.25, and the middle pair must be exactly 1:1, because a table read from the wrong place almost never has a neutral stage in its middle.

Found only at N = 3,000

Three of 3,000 attempts at one scenario died in the same shape: at 2 HP against a Pokémon on 1 HP, the driver swung rather than escaped, because a good roll would end it first. Two separate 600-attempt runs were clean. The bug was not rare in the mechanic, it was rare in the route — it needs a Pidgey that has had time to use SAND-ATTACK twice.

There is a second trap underneath it, and the atlas carries its own warning about it: in the stat-stage block, 7 is neutral, not 0, and the block is zeroed work RAM until the battle-init routine writes the sevens. A sample taken on the first frames of a battle reads 0 everywhere and looks like every stat six stages down.


The move table and the stage-modifier ratios were read off a retail Pokémon Blue cartridge with the project's own tools. No ROM is distributed; the reproduction command points at your own.

← Back to devlog