$ lonely-chess

Lonely Chess

A legal PGN chess game, read as source code.

Every move in the game file below is an instruction: a variable declaration, a binary digit, a loop, an arithmetic step, or a print. The runner on this page is a real, independent implementation of the language — not a recording of the Python original.

87654321
abcdefgh

white to move

a playable board — ordinary chess rules. it does not drive the interpreter; paste a PGN program below for that.

Four things worth knowing before you run one

The mechanism, in short.

A PGN file is the program
Standard Portable Game Notation — the same format Chess.com and Lichess export — is parsed directly. No custom file format, no preprocessing.
The board is memory
Interpreter state lives in named slots like p_h2 and p_g2, one per pawn's home square.
Pieces carry syntax
Knights, rooks, bishops, kings, and queens each perform a fixed programming role — declaration, encoding, looping, branching, printing.
Two implementations, one language
A Python reference implementation ships in the repository; this page is a from-scratch TypeScript port of the same rules, running entirely in your browser.

Run it yourself

Paste a PGN program, or load one of the samples below. This parses moves, tracks interpreter state, and prints only when the program reaches an explicit print instruction — the same contract as the Python runtime.

Program output

Run program

Variables at end

{ }

How the language reads

Every chess move either changes the interpreter's mode, writes data, performs arithmetic, controls flow, or prints — never more than one at a time.

Na3

Integer mode

White Knight to a3 begins an integer declaration.

Nc3

String mode

White Knight to c3 begins a string declaration instead.

h2→h3

Variable name

A pawn move selects a variable by file — the h-file pawn becomes p_h2.

b6–h6

Binary encoding

The Black rook walks rank 6 to write 7-bit values, most significant bit first.

Ke2 / Ke1

Loop

King to e2 starts or advances the loop; king to e1 prints the current iteration and clears the buffer.

Bh3…Bf1

If + modulo

Bishop opens the if-block, the a-rook opens modulo, the black rook bounces to count the divisor, and the a-rook returns to a1 to evaluate the match.

Real programs, unabridged

Five real sample files from the repository, shown in full — no shortcut ellipses.

» sample_int100.pgn
[Event "Live Chess"]
[Site "Chess.com"]
[Date "2026.04.29"]
[Round "?"]
[White "Lessrtrer"]
[Black "6nicor"]
[Result "0-1"]
[TimeControl "600"]
[WhiteElo "507"]
[BlackElo "998"]
[Termination "6nicor won - game abandoned"]
[ECO "A00"]
[EndTime "1:11:17 GMT+0000"]
[Link "https://www.chess.com/game/live/167970268718"]

{ === LONELY CHESS: int p_h2 = 100 then print(p_h2) === }

{ --- SETUP PHASE --- }
{ Move 1: W Knight b1->a3  signals: begin INTEGER instantiation mode }
{ Move 1: B pawn a7->a5    filler move }
1. Na3 a5

{ Move 2: W pawn h2->h3    declares variable name p_h2 (based on pawn home square h2) }
{ Move 2: B pawn a5->a4    filler move }
2. h3 a4

{ Move 3: W pawn h3->h4    second pawn push, no-op (var already declared) }
{ Move 3: B rook a8->a6    arms encoding mode (rook reaches 6th rank) }
3. h4 Ra6

{ --- ENCODING PHASE: writing 1100100 = 100 in 7-bit binary --- }
{ b6=bit6, c6=bit5, d6=bit4, e6=bit3, f6=bit2, g6=bit1, h6=bit0 }

{ Move 4: W Knight a3->c4  no-op wait }
{ Move 4: B rook a6->b6    sets bit6 -> acc = 1000000 }
4. Nc4 Rb6

{ Move 5: W Knight c4->a3  no-op wait }
{ Move 5: B rook b6->c6    sets bit5 -> acc = 1100000 }
5. Na3 Rc6

{ Move 6: W Knight a3->c4  no-op wait }
{ Move 6: B rook c6->f6    sets bit2 -> acc = 1100100 = 100 }
6. Nc4 Rf6

{ Move 7: W Knight c4->a3  no-op wait }
{ Move 7: B rook f6->a6    finalises value: 1100100 = 100 }
7. Na3 Ra6

{ --- FINALISE PHASE --- }
{ Move 8: W Knight a3->b1  commits p_h2 = 100 }
{ Move 8: B rook a6->a8    encoding complete, resets to start position }
8. Nb1 Ra8

{ --- PRINT PHASE: print(p_h2) --- }
{ Move 9:  W pawn h4->h5   selects p_h2 for printing (pawn home = h2) }
{ Move 9:  B pawn e7->e6   filler }
9. h5 e6

{ Move 10: W Queen d1->d2  initiates print (path now clear after pawn moved) }
{ Move 10: B pawn e6->e5   filler }
10. Qd2 e5

{ Move 11: W Queen d2->d1  finalises print -> OUTPUT: p_h2 = 100 }
{ Move 11: B pawn e5->e4   filler }
11. Qd1 e4

0-1

Run it locally

Pure Python 3.6+, no dependencies.

» terminal
python3 lonely_chess_runtime.py fizzbuzz_100.pgn

python3 lonely_chess_interpreter.py sample_int100.pgn

Runtime vs. interpreter

lonely_chess_runtime.py prints only final program output. lonely_chess_interpreter.py annotates every move — better for debugging and for watching each chess move change the program state.