The public lists still stopped at the MEGA65. README, the recipe index, the seed table, the journal and the constraint notes now name the A500.
cc65 Chess
A chess program in C, originally for ~1 MHz 6502 machines — Commodore 64, Apple II, Oric, Plus/4, Atari. Faster 6502s came later: the Commander X16 and the Picocomputer, at up to 8 MHz. Other toolchains cover ZX Spectrum, Macintosh 68k, TI-99/4A, Color Computer 3, Agon Light, Acorn A3010, Amiga 500 and native MEGA65. Each port is built with whichever compiler that machine needs. There is also a terminal build for development.
It used to be a cc65-only project, which is why it is called cc65 Chess. Newer ports target machines cc65 does not, but the name is what it is.
It is also, now, a guided tour of how a chess engine works. The engine was rewritten from
scratch, the old one is still in the history, and both are documented in full — including a
measurement of exactly how strong the result is and exactly why the original was weak. If
you have ever wanted to read a complete, small, real chess engine rather than a description
of one, that is what doc/ is for.
Written with AI assistance. The original 2014 program was written by hand; the engine
that replaced it in 2026, and the documentation and test harness around it, were produced
in collaboration with an AI. This is stated up front rather than left to be inferred, and
doc/rework-log.md is the unedited working journal if you want to see how that went —
including the parts that went wrong.
How strong is it?
Measured over roughly 40,000 games against Stockfish, using two independent match runners:
| Menu level | Search budget | Time per move on a stock C64 | Approximate rating |
|---|---|---|---|
| 1 — Very Easy | 400 nodes | 11 seconds | ~1240 |
| 2 — Easy | 1,200 nodes | 40 seconds | ~1430 |
| 3 — Harder | 18,000 nodes | ~11 minutes | ~1720 |
| 4 — Very Hard | 65,000 nodes | ~48 minutes | ~1950 |
Ratings are on Stockfish's own scale, ±150 — and that uncertainty is honest rather than
statistical. At its strongest setting the engine scores 70% against Stockfish restricted to a
single search node and draws level with it at a hundred. Against Sargon II — a 1978
program, on an Apple II — Harder scored 57% at Sargon's level 4. doc/strength.md explains
what the numbers mean, what they do not mean, and how to reproduce them.
Emulator speed-up is a free multiplier: strength is measured in positions searched, not seconds, so an accelerated machine plays the same game sooner. Roughly 60 rating points per doubling of thinking time — which the Picocomputer collects in hardware, since its 65C02 runs at up to 8 MHz.
Playing it
Built images for every platform are in the releases tab. There is a video of the game here.
Keys. Cursor keys move the cursor; RETURN selects a piece and then its destination, or
deselects it. M (or RUN/STOP with nothing selected) opens the menu. U and R undo and
redo — the stack holds the last 127 moves, and in a game against the AI an undo takes back
both plies so you can play something else.
The cursor colour tells you what you are on: green — selectable, red — a piece with no legal moves, purple — an empty square or an enemy piece, blue — the piece you have selected, cyan — a square it can move to.
Three keys show you the attacks, and they are the feature the whole program is built
around. B overlays every square with how many white and black pieces attack it. A
highlights every enemy piece attacking the square under the cursor. D highlights every
friendly piece defending it. Attackers in cyan, defenders in red.
Building
make help is the verb list. make list says which ports this machine can compile — a
missing compiler is a skip, not an error.
make # every default port whose compiler is here
make c64 # one port
make apple2 po # Apple II + ProDOS image
make spectrum # ZX Spectrum, tap + sna
make spectrum test # build and run it
make term # host curses binary → build/term/chessterm
make coco3 # Color Computer 3, CMOC → build/coco3/chess.bin
make agon # Agon Light, agondev → build/agon/chess.bin
make archie # Acorn A3010, ArchieSDK → RISC OS app + ZIPs
make archie test # private Arculator copy under build/archie/
make mega65 # native 45GS02, LLVM-MOS → build/mega65/chess.prg
make mega65 test # native MEGA65 under Xemu
make amiga # Amiga 500 hunk → build/amiga/boot/
make amiga test # boot under FS-UAE
Products land in build/<port>/. Objects land in build/obj/<port>/. make tidy sweeps
leftover binaries out of the repo root.
The default ports are apple2 atari atmos c64 c64.chr plus4 cx16. spectrum joins that
list when its compiler is on PATH. mac68k joins when Retro68
is found. rp6502 is built only when you ask for it — it needs the
Picocomputer fork of cc65. Every 6502 port that
shares a compiler is built at the same optimisation setting, optsize — the Atari does
not fit at optspeed, and a port built differently is a port that behaves differently.
archie joins the default build when ArchieSDK is found. Set ARCHIESDK to its
installation directory. The port targets an A3010 with 2 MB RAM and RISC OS 3.11,
with the C64 board and colours in fullscreen Mode 9. Mouse hover and left click work
alongside the original keys. Build, packaging and emulator instructions are in
recipes/archie-archiesdk.md.
mega65 is opt-in and uses LLVM_MOS_SDK. It runs in native MEGA65 mode,
with the same engine and budgets, compiled for the 45GS02 with size optimisation.
Xemu measurements average 0.12 / 0.44 / 3.35 / 20.7 seconds per search at the
four levels over six fixed middlegames. The completed C64 comparison at levels
1/2 is about 121x slower; both sides return identical nodes and results. These
are emulator figures, combining CPU and compiler effects. An A3010 Arculator
comparison matches all four levels and is about 1.8x slower than native Xemu;
physical-machine timing has not been measured. Details and instructions are in
recipes/mega65-llvm-mos.md.
amiga joins the default build when Bartman's m68k-amiga-elf-gcc and
elf2hunk are found, usually inside the Amiga debug extension. Set
AMIGA_GCC and AMIGA_ELF2HUNK to point somewhere else. The port targets
an Amiga 500 with Kickstart 1.3: a 320×256 low-resolution screen, the C64
board and colours, and the mouse. Build and emulator instructions are in
recipes/amiga-fs-uae.md.
Disk / tape / program images are a second goal, not a second invocation:
make apple2 po
make atari atr
make c64 d64 # also: prg cprg cxprg tap rom dsk
make rp6502 rom # build/rp6502/cc65-Chess.rp6502
d64 needs C1541, po needs cadius and atr needs dir2atr; rom needs Python, and
the tool it runs is in rp6502/. The rest need nothing extra. cx16 needs a reasonably
current cc65: it uses c_sp in inline assembly, which older versions called sp.
The documentation
| doc/engine.md | How the engine works. The main event. Board representation, move generation, evaluation, alpha-beta, quiescence — every idea tied to the function that implements it. Assumes you know how the pieces move and nothing else. |
| doc/original-engine.md | How the engine that was replaced worked, and why it was weak. Two structural decisions, both of them traps that are well known inside computer chess and nearly invisible from outside it. |
| doc/strength.md | How strong it is and how that was established. The measurement, the methodology, and an honest account of what the numbers are worth. |
| doc/measuring.md | The instruments. What to run, which question each answers, and the workflow for changing the engine without breaking it. |
| doc/rework-log.md | The working journal, published unsanitised. Its value is the measurements that demolished the plan around them. Closed search and evaluation portfolios live at the end of it. |
| doc/readme-2014.txt | The original readme, kept verbatim as a historical document. Its description of the AI describes the old engine, which is the point. |
Contributors and agents should also read AGENTS.md — the constraints that are easy to violate by accident, and the traps that have already caught someone.
Tests
make check # or: cd tests && make test
35 seconds, exits non-zero on failure. Move generation is verified against perft — the standard correctness test — exactly to depth 5 on all five reference positions, and to depth 6 on the two that publish one. Beyond that there is a game fuzzer, a match harness for measuring whether a change actually made the engine stronger, on-target benchmarks that run under VICE, and a UCI adapter so the engine can be played against other engines or opened in any chess GUI. doc/measuring.md covers all of it.
Porting
Everything platform-specific is behind plat.h, and the board is presented to it as squares
0–63 regardless of what the engine does internally. When the terminal port was written it took
about an hour. The engine rewrite touched no platform file at all; the only addition to that
interface since is plat_GetSeed(), three lines a port that read a free-running counter for
the opening randomiser.
A new machine is a plat.h implementation plus a thin file in make/ports/. make help
says how those files are laid out. If you port it somewhere new, please say so — that is
the best part of putting this online.
History and credits
Started in February 2014, three months after the author learned to play chess, as a "for the fun of it" project for the Commodore 64. The engine was replaced in 2026; everything else — the display, the menus, the undo stack, the attack visualizer, the ports — is the original program.
- Stefan Wessels — the program, and all of it before 2026.
- Oliver Schmidt — the Apple II port, and the generic cc65 Makefile (with Patryk "Silver Dream !" Łogiewa) this tree grew from.
- [raxiss] — the Oric-1 / Atmos / Telestrat port.
- Ullrich von Bassewitz and the cc65 team — the compiler for the original 6502 ports.
- The LLVM-MOS team — the compiler and native MEGA65 SDK.
The tag v1-original-engine marks the last commit before the engine rewrite, if you want to
check out the original and compare.
Licence
Public domain — see LICENSE. Do anything you like with it.