AttoChess, a complete, playable chess program for 16-bit x86 DOS in 278 bytes

SeenNotHeard1 pts0 comments

AttoChess: a 278-byte chess program for 16-bit x86 DOS

01Lineage

AttoChess is a size-optimized descendant of LeanChess . It plays the same game, in the same environment, using the same 0x88-style board and recursive minimax search, yet assembles to 278 bytes: ten bytes smaller than the 288-byte program that previously held the record for the smallest working chess engine.

1K ZX Chess 672 bytes<br>1982 &middot; David Horne &middot; Sinclair ZX81

BootChess 487 bytes<br>2015 &middot; Olivier Poudade &middot; x86 boot sector

LeanChess 288 bytes<br>2019 &middot; Dmitry Shechtman &middot; x86 DOS (.COM)

AttoChess 278 bytes<br>2026 &middot; Nicholas Tanner &middot; x86 DOS (.COM)

0bars to scale672 bytes

Note<br>AttoChess is not a byte-count stunt that only sets up a board. It boots, draws the position, reads your move from the keyboard, searches with real 4-ply recursion, replies with a legal move, and loops. The 278-byte figure is the assembled size of that whole program, verified byte-for-byte on the produced .COM.

02Play AttoChess

Start game<br>Loads the 278-byte program in an emulated DOS machine. You play White.

Your move

Play move

Not started<br>K N B P Q R White &middot; k n b p q r Black

How it works

Press Start game and the real 278-byte program boots inside an emulated DOS machine, right here in the page. You play White; it plays Black.

Type your move as four characters, the square you are moving from and the square to, like e2e4, then press Enter or Play move . Your piece moves at once; the engine searches four plies deep and answers a moment later, with both squares of the last move highlighted.

Moves are trusted and given in plain coordinates: no click-to-move, no castling, en passant, or promotion. When the engine runs out of replies, the game is over.

03Prior work

This project stands on the work that came before it, and honoring that lineage is part of the point.

LeanChess, Dmitry Shechtman, 2019

The real breakthrough: it took the record from a 487-byte boot sector down to a 288-byte DOS .COM, with an elegant, genuinely readable design. A padded 0x88 board, a piece encoding that doubles as its own move-generation index, and a single recursive routine that both makes a move and finds the best reply. Every clever idea in AttoChess is one of these ideas taken a step further, and AttoChess is a derivative work that retains Shechtman's copyright and MIT license in full. If you find this interesting, read his source first.

BootChess, Olivier Poudade, 2015

Fit a chess game into a 512-byte boot sector (487 bytes of code) and reset public expectations of how small chess could go. Poudade's page still hosts it.

1K ZX Chess, David Horne, 1982

Squeezed a playable game into 672 bytes on a Sinclair ZX81 and stood as the benchmark for over thirty years.

04Verifying the size

The record claim is only worth anything if you can reproduce it. Assemble the source and measure the output:

tasm AttoChess<br>tlink /t AttoChess ; /t produces a .COM, not an .EXE<br>ls -l AttoChess.com ; 278 bytes<br>There is nothing else in the image: no data-section padding, no separate render buffer, no reserved board array baked into the file. Every byte in AttoChess.com is code or table data the program actually uses.

05Optimizations

AttoChess keeps the original's core search verbatim. All ten bytes come from rethinking the two things around the search, how the board is drawn and how the move is decoded, plus one change inside the search loop that frees a register.

01 &middot; Display<br>The board draws itself: no render buffer, no int 21h/09h

This accounts for the largest saving.

The original renders the position into a separate buffer: it walks the board, transforms each square into a printable character, stores it, appends a $ terminator, and prints the whole string with DOS function 09h (int 21h). That path costs a buffer pointer setup, the copy loop's store, the terminator write, and, because the buffer lives after the board, a reserved board array in the image.

AttoChess deletes all of it. The board's border columns are laid out as CR, LF, CR, LF (0Dh, 0Ah, 0Dh, 0Ah) instead of the previous 08h filler. Both CR and LF still have bit 3 set, so every existing border/color mask test fires exactly as before, but now the raw board bytes are already a printable frame. The display loop streams each byte straight to the console with int 29h (DOS fast console output): borders become newlines, empty squares become NULs, and only real pieces take the ASCII transform.

main_loop:<br>mov si, offset board_db + 24 ; row 2 (black back rank), col 0<br>mov cl, 98 ; 8 rank rows + final CR,LF (CH=0)<br>disp_loop:<br>lodsb ; read square contents<br>test al, 30h ; piece?<br>jz disp_cont ; no: emit raw (CR / LF / NUL)<br>inc ax ; zero-align king<br>and al, 27h ; isolate piece type + black/lowercase bit<br>add al, 4Bh ; K, N, B, P, Q, R (upper/lower by color)<br>disp_cont:<br>int 29h ; fast console output of AL<br>loop disp_loop<br>Removed in one move: the render buffer, its pointer setup,...

attochess bytes byte move board middot

Related Articles