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.

Atari random data header

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
Example dynamic ROM outputAnother ROM outputROM output 3ROM output 4ROM output 5ROM output 6

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 line

This 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 nothing

Heuristics

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:

MetricMin5th %ileMedianMaxAverage
Valid Opcodes (%)42.1%76.0%90.7%74.8%
TIA Accesses12932822,847341
RIOT Accesses334111891134
RIOT Timer1227872395
RIOT I/O082820133
Branch Commands281773645,928457
Jump Commands3371111,495142
Unique Opcodes29125143151141

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, 0xFE

Branch 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.

Interesting ROM discovery

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.

ROM 51014 proto-game

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.