Searching for Atari Games in Random Data
The author generated approximately 30 billion random 4KB files and used heuristic filters plus CUDA acceleration to find sequences that function as playable Atari 2600 ROMs, discovering 11 that produce dynamic video output.

What if we put a billion monkeys with GPUs and forced them to write an Atari 2600 game? Well, that's essentially what this project does — we generate approximately 30 billion random 4KB files and search for anything resembling a functional Atari 2600 game.
First, the Results
Before diving into the methodology, here's what we found. From 1.8 billion generated ROMs:
- 456 passed heuristic filters
- 16 produced static video output
- 11 exhibited dynamic, animated content






The Scale of the Problem
An Atari 2600 cartridge is 4KB — 4,096 bytes, or 32,768 bits. The total number of possible ROMs is 2^32,768 — a number with approximately 10,159 digits. For perspective:
- There are about 10^20 grains of sand on Earth
- There are about 10^80 protons in the observable universe
- The number of possible Atari games is 10^10,159
Brute-force testing every possible ROM is fundamentally impossible. Instead, we use intelligent filtering — heuristics based on what makes real Atari games tick.
Why Atari 2600?
The Atari 2600 is stupidly simple. There are no memory mappers, no copy protection, no operating system. The CPU reads instructions directly from the cartridge ROM at address $F000-$FFFF. No headers, no checksums, no handshakes. Plug in 4KB of data and the machine will try to execute it.
This contrasts sharply with the NES or Game Boy, which require sophisticated hardware interaction, memory mapping, and specific initialization sequences.
Understanding Atari Hardware
A simple Atari program looks like this:
; Program starts at $F000
F000: A9 84 ; LDA #$84 - Load color value
F002: 85 09 ; STA $09 - Store in COLUBK (background color)
F004: 85 02 ; STA $02 - Store in WSYNC (wait for sync)
F006: 4C 04 F0 ; JMP $F004 - Return to WSYNC lineThis minimal program sets a background color and synchronizes with the TV. The simplest possible program is just an infinite loop:
; Program starts at $F000
F000: 4C 00 F0 ; JMP $F000 — infinite loop doing nothingHeuristics
Rather than test all possible ROMs, we screen candidates using characteristics found in the 1,530 commercial Atari games we analyzed. Here are our filtering criteria:
Valid Opcodes
The 6502 processor has 151 valid opcodes out of 256 possible byte values. In random data, about 59% of bytes will happen to be valid opcodes. But in real games, more than 75% of the first kilobyte consists of valid opcodes.
TIA Register Access
The TIA (Television Interface Adapter) is the graphics and sound chip. Real games constantly access TIA registers to draw graphics. The most frequently accessed TIA registers:
- WSYNC ($02): 18.8% of all TIA operations — constant TV synchronization
- GRP0 ($1B): 8.4%
- GRP1 ($1C): 7.0%
- HMOVE ($2A): 4.9%
- PF1 ($0E): 4.1%
- PF2 ($0F): 3.7%
RIOT Operations
The RIOT (RAM-I/O-Timer) chip handles input and timing. Of all RIOT operations, 78% are timer operations and 22% are I/O operations.
Command Distribution
In real games, store operations dominate:
- STA (Store A): 71.8%
- STX (Store X): 9.3%
- STY (Store Y): 8.5%
- LDA (Load A): 6.7%
- Other: 3.7%
Addressing Modes
- Zero page: 82.1%
- Indexed zero page: 13.2%
- Absolute: 4.7%
Calibration Against Reality
We analyzed 1,530 commercial Atari ROMs to establish baselines:
| Metric | Min | 5th %ile | Median | Max | Average |
|---|---|---|---|---|---|
| Valid Opcodes (%) | 42.1% | — | 76.0% | 90.7% | 74.8% |
| TIA Accesses | 12 | 93 | 282 | 2,847 | 341 |
| RIOT Accesses | 3 | 34 | 111 | 891 | 134 |
| RIOT Timer | 1 | 22 | 78 | 723 | 95 |
| RIOT I/O | 0 | 8 | 28 | 201 | 33 |
| Branch Commands | 28 | 177 | 364 | 5,928 | 457 |
| Jump Commands | 3 | 37 | 111 | 1,495 | 142 |
| Unique Opcodes | 29 | 125 | 143 | 151 | 141 |
Composite Score
We combine all metrics into a single score:
Score = (Valid_Opcodes_Count × 0.25) +
(min(TIA_Accesses/150, 1.0) × 0.30) +
(min(RIOT_Accesses/50, 1.0) × 0.20) +
(min(Branch_Commands/200, 1.0) × 0.15) +
(min(Jump_Commands/40, 1.0) × 0.10)Real game scores range from 0.393 to 1.004, with an average of 0.853.
Filtering Thresholds
Based on the 5th percentile of real games, we set our minimum thresholds:
- OPCODE_THRESHOLD: 0.58
- TIA_THRESHOLD: 50
- RIOT_THRESHOLD: 13
- BRANCH_THRESHOLD: 150
- JUMP_THRESHOLD: 37
- INSTRUCTION_VARIETY: 100
- MIN_SCORE: 0.52
Mining Atari Games with GPU
Using CUDA with CuPy on an NVIDIA GTX 1070 (1,920 CUDA cores), we achieved a processing rate of approximately 62,150 ROMs per second.
The GPU handles random generation and heuristic analysis in parallel, sending only the promising candidates to the CPU for emulation verification using the MAME emulator.
The valid 6502 opcodes used for filtering:
0x00, 0x01, 0x05, 0x06, 0x08, 0x09, 0x0A, 0x0D, 0x0E, 0x10, 0x11, 0x15, 0x16, 0x18, 0x19, 0x1D, 0x1E, 0x20, 0x21, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2A, 0x2C, 0x2D, 0x2E, 0x30, 0x31, 0x35, 0x36, 0x38, 0x39, 0x3D, 0x3E, 0x40, 0x41, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4C, 0x4D, 0x4E, 0x50, 0x51, 0x55, 0x56, 0x58, 0x59, 0x5D, 0x5E, 0x60, 0x61, 0x65, 0x66, 0x68, 0x69, 0x6A, 0x6C, 0x6D, 0x6E, 0x70, 0x71, 0x75, 0x76, 0x78, 0x79, 0x7D, 0x7E, 0x81, 0x84, 0x85, 0x86, 0x88, 0x8A, 0x8C, 0x8D, 0x8E, 0x90, 0x91, 0x94, 0x95, 0x96, 0x98, 0x99, 0x9A, 0x9D, 0xA0, 0xA1, 0xA2, 0xA4, 0xA5, 0xA6, 0xA8, 0xA9, 0xAA, 0xAC, 0xAD, 0xAE, 0xB0, 0xB1, 0xB4, 0xB5, 0xB6, 0xB8, 0xB9, 0xBA, 0xBC, 0xBD, 0xBE, 0xC0, 0xC1, 0xC4, 0xC5, 0xC6, 0xC8, 0xC9, 0xCA, 0xCC, 0xCD, 0xCE, 0xD0, 0xD1, 0xD5, 0xD6, 0xD8, 0xD9, 0xDD, 0xDE, 0xE0, 0xE1, 0xE4, 0xE5, 0xE6, 0xE8, 0xE9, 0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF5, 0xF6, 0xF8, 0xF9, 0xFD, 0xFEBranch opcodes: 0x10, 0x30, 0x50, 0x70, 0x90, 0xB0, 0xD0, 0xF0
Jump opcodes: 0x4C, 0x6C, 0x20
Emulation Validation
ROMs that pass heuristic filtering are tested using the MAME emulator with a Lua snapshot script:
local s = manager.machine.screens[":screen"]
local frame_count = 0
emu.register_frame_done(function ()
frame_count = frame_count + 1
if frame_count == 1 then s:snapshot("first.png")
elseif frame_count == 60 then s:snapshot("second.png");
manager.machine:exit() end
end, "snapper")This captures the first frame and the 60th frame (roughly 1 second of gameplay). If the two frames differ, we have dynamic content. If either contains non-black pixels, we have visual output.
First Results and Why Machine Learning Failed
An initial attempt used a Random Forest classifier trained on 1,500 commercial games and random ROMs. It failed because it optimized for "ROMs that execute" rather than "ROMs that produce interesting behavior." The classifier selected programs containing only infinite loops with minimal graphical activity — technically running programs, but nothing visually interesting.
More Results: Finding Everything Interesting
After switching to the heuristic approach, the success rate improved dramatically. Out of every 2.59 million generated ROMs, approximately one produced interesting visual output.
What I Discovered
The most interesting finds included ROMs with colorful horizontal bars, shifting patterns, and even one that changed its display based on timing.

A Real Proto-Game
One particularly exciting discovery — ROM 51014 — displayed a yellow background with red bars and actually responded to joystick input. Moving the joystick changed the visual output, making this essentially a functional proto-game that emerged from pure randomness.

Probability Calculations
How likely is it to randomly generate a meaningful program? For a specific 6-byte word to appear at a specific location: 1/256^6 = 1/281,474,976,710,656. For our simple Atari program (7 bytes): approximately 1/36,893,488,147,419,103,232.
But we're not looking for specific programs — we're looking for any program that produces interesting output. This is a much larger haystack, but we're also looking for a much larger class of needles.
Future Work
Several avenues for improvement remain:
- Moving to larger GPU clusters for faster generation
- Improving heuristics based on the successful finds
- Exploring ROM sizes beyond 4KB
- Trying genetic algorithms to evolve interesting ROMs from the best candidates
Conclusion
This project demonstrates that functional programs can emerge from pure randomness if the platform is simple enough and the search is intelligent enough. The Atari 2600's extreme simplicity — direct ROM execution with no OS, no headers, no protection — makes it the ideal platform for this kind of exploration. The discovery of ROM 51014, a genuinely interactive proto-game generated from random data, suggests that the space of "interesting programs" may be larger than we intuitively think.