Chess (terminal)
Chess against the computer, in a terminal. It is the second version of the
game, beside web/. It runs on the
magmacrunch engine's
terminal backend, either as a cabinet in the magmacrunch arcade or as its own
command, and plays the same either way.
pipx install magmacrunch-chess
chess # the title, where the level and clock are set
chess --play --level hard --clock blitz # straight into a game
For working on it, from this folder:
pip install -e ".[dev]"
python -m neon_chess
The command is chess. The import package is neon_chess, after the page's
Neon Edition, because chess is python-chess's and a player may well have it
installed.
Keys
| key | |
|---|---|
← ↑ ↓ → |
move the cursor |
ENTER |
pick up the piece under the cursor; on a marked square, put it down |
Q R B N |
when a pawn promotes, what it becomes (or ← → and ENTER) |
Esc |
put the piece back; with nothing picked up, the title (and from there, the arcade) |
R |
new game, same level and clock |
Q |
quit |
Your pieces are cyan capitals, the computer's pink small letters, as notation
writes them: K Q R B N P. So the two sides stay apart with no colour at all,
and under --ascii. Once a piece is picked up, a green · marks every empty
square it can go to, and a square it can take on is tinted green. The last
move is tinted yellow and a king in check red. The cursor is bracketed as well
as shaded, so it shows under NO_COLOR. On the title, ← → change the level
(Easy, Medium, Hard) and the clock (none, Blitz 5, Rapid 15, Classical 30).
The panel shows the clocks, whose turn it is, and the move list in the page's
notation.
The computer starts thinking 0.3 seconds after your move, as on the page. It thinks on a thread of its own, so the screen and the clocks carry on while it does: under a second at Easy and Medium, one or two at Hard. The page freezes while it thinks, and its clock does too, so there the computer's clock hardly moves. Here it runs.
Needs a 68x19 terminal.
Playing people online (a prototype)
PLAY ONLINE on the title, or chess --online, connects to the arcade's own
multiplayer server, the one behind the page's PLAY ONLINE button, so a
terminal can play a browser. Type a name, then press C to create a room and
read its four-letter code to your opponent, or J to join one. The first in
plays white; the host presses Enter to start once both are in. Black sees the
board from black's side, as on the page. You move as against the computer, a
promotion asks for its piece, and X then Y resigns.
pip install "magmacrunch-chess[online]" # websockets, only for this
chess --online
chess --online --server ws://127.0.0.1:8769 # a web/server.py on this machine
The server keeps the rules. This client sends the square a piece leaves and
the square it lands on, and the piece for a promotion straight after, and
draws the board the server sends back. It works out the legal moves only to
mark them, with rules.py, the page's own rules, which
tests/test_server_parity.py holds to
the server's own, piece by piece, in over 20,000 positions with castling, en
passant and promotion. The server sends no notation, so the move list is
written here, from the positions before and after, as chess writes it.
neon_chess/online.py holds the protocol, split into Table (the server's
state, with no I/O) and Connection (the WebSocket, on its own thread).
online_scenes.py reuses the computer game's screen with the server's state
in place of a local game.
tests/test_online.py runs web/server.py in-process on a free port and
plays it over real sockets. It covers:
- a hundred moves of random play, promotions to every piece included.
After every move both clients' boards, castling rights and en passant must
match a shadow kept with
rules.py, and the notation must match; - checkmate, a promotion to a knight, a resignation, out-of-turn and illegal moves, a wrong room code, a player leaving and a dead address;
- two complete terminal sessions, by keys alone: the lobby, a move each (black's on the turned board) and a resignation.
It never contacts the live server. By hand, the real page in headless Chromium
played a terminal client both ways through a local web/server.py: as white
for eight moves, and as black for eleven with a capture, a check and both
kings moving. Every board on both sides matched the position the moves give.
What the page gets wrong online, seen in that run:
- The page used to castle across a swept square, and then hang. Fixed in both; see below.
- The page's move list online has only your own moves. It writes a move
when you make it and nothing when the server reports your opponent's: the
page as white listed
e4 Nf3 g3 Bh3 O-O. - The host's START button shows before anyone has joined. Pressed early, it starts nothing, and says nothing.
The server's rules, which a prototype can only follow:
- The lobby's clock does nothing. The host may pick a time control; the server stores it and runs no clock, and neither does the page. So this client offers none.
- Nobody can take a leaver's place. A player leaving ends the game, and the one left is told they win, as on the page; the server records no result.
What a prototype has not done yet:
- Nobody has played the live server from a terminal, only a copy of it run
locally. The default address is
wss://magmacrunch.duckdns.org/chess, which is whatmpServerFor('chess', 8769)gives the public site. - No reconnect. A dropped connection says so, and the game is lost.
- No chat, and no watching, which the browser has.
It is the browser's game, and the browser's opponent
neon_chess/rules.py is web/js/config.js, board.js, ai.js and
game.js, ported function for function: the squares searched in the same
order, the moves listed in the same order, the same evaluation and the same
alpha-beta, so the computer picks the same move as the page's in every
position. Two tests hold it there:
-
tests/test_oracle.py.tools/js_oracle.mjsloads those files unmodified and plays two kinds of game through them:- 12 games against the computer, through
Game's own API asmain.jsdrives it: 6 at Easy, 4 at Medium and 2 at Hard. Half have a random player; half a careful one, who opens at random and then takes the biggest capture it can. The computer won every game that finished. - 200 games of random moves for both sides, through
Boardalone, up to 300 moves each. They end in 21 checkmates and 12 stalemates, and cover 360 promotions to every piece, 15 castlings and en passant.
The test replays all 56,000 moves through the port. After each one it requires the same board, castling rights, en passant square and check, and the same list of legal moves in the page's order. In the computer games the game state, turn, move list and result must match too. Changing the bishop's value from 330 to 320 fails it, at Easy, move 34.
- 12 games against the computer, through
-
tests/test_rules.py. It sets up by hand the cases worth pinning, including the page's own rules below, so that fixing one shows as a change.
The port is quicker than a straight copy. It looks outward from a square for an attacker rather than asking every enemy piece, undoes a trial move rather than copying the board, and adds up the evaluation tables once. Each gives the page's answer, which the oracle checks.
The computer's promotions, fixed in both
The page's computer would not promote a free pawn, at any level. Inside its
search a pawn reaching the last row stayed a pawn: Board.applyMove promotes
only to a piece the move names, and a move names none until it is played. The
pawn table rates a pawn one row short at 50 and one on the last row at 0, so
the search preferred to hold a pawn back. With a pawn on h2 and nothing to stop
it, Easy, Medium and Hard all moved their king instead. Nor could it see your
promotions coming: with your pawn on b7, Easy played Kg8 and let it queen for
nothing.
Now a promotion tried inside the search is a queen, which is what the computer
plays when it makes the move. All three levels queen the h2 pawn, and every
level stops yours, Easy with a check and Medium and Hard with a rook behind it.
This is fixed in web/js/ai.js (searchMove) and here (_search_move)
together. tests/test_oracle.py asks the page's own ai.js both positions at
every level and requires the port's answers. The ai.js from before the fix
fails that test: it moves its king. In headless Chromium, the fixed page played
a game opening with no errors, and its AI.chooseMove queened the pawn at all
three levels.
Castling across an attacked square, fixed in both
The page's bishops, rooks and queens attacked only occupied squares.
getSlidingAttacks in board.js listed the first piece in each direction and
none of the empty squares before it. Check was unaffected, since a king stands
on its square, but you or the computer could castle across a square a slider
swept. web/server.py gets this right and refuses that castle.
Online, that hung the game. The page moves its king before the server answers, and the server ignores a move it refuses without a word, so the page waited on its opponent for good while the server and the opponent waited on it. In headless Chromium against a terminal client, after 1.e4 b6 2.Nf3 Ba6 3.g3 Nc6 4.Bh3 Nf6, the page offered O-O across f1, which the bishop on a6 sweeps. It drew its king on g1 and said "Waiting for opponent...", with the server's king still on e1 and white to move.
Now getSlidingAttacks lists every square up to and including the first
piece. In the same game the fixed page does not offer the castle. Clicking g1
leaves it on "Select a piece to move", with its board still the server's and
white still to move. The port matches it (Board.attacked), so against the
computer the rule is chess's too, and the online client needs no rules of its
own: tests/test_server_parity.py compares the port with the server directly.
tests/test_oracle.py asks the page's own board.js for the king's moves with
a rook sweeping f1, and the old board.js fails it by offering O-O.
tests/test_rules.py covers a rook, a bishop and a queen sweeping f1, and a
knight covering it.
The page's rules
These are the page's own, kept because the oracle requires them. Each is a place where the page is not chess:
- The computer's moves are written without their piece.
formatMovelooks for the piece on the square it has just left, so the move list hasc6forNc6andxe5forBxe5. Your moves are never disambiguated, because it looks for a second piece that could reach the square after yours has taken it: two rooks to d1 is writtenRd1. - There is no draw but stalemate. No repetition, no fifty-move rule, no
insufficient material: a king against a king plays on until
R. - Game over is asked of you first. After your move, if you would have no legal move, the game ends in stalemate, though it is the computer's turn.
Two of the page's clicks work differently here. On the page, a second piece can only be picked up once the first is put back; here Enter on it picks it up. And the page's clock stops while the computer thinks, as above.
There is no high-score table, because the page keeps none.
Licence
PolyForm Noncommercial 1.0.0, as for the rest of this repository. See LICENSE
and NOTICE.
Metadata
Release files for magmacrunch-chess 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| magmacrunch_chess-0.1.0.tar.gz | 46.5 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| magmacrunch_chess-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 83.1 kB
Release files / magmacrunch_chess-0.1.0.tar.gz
| Download URL | magmacrunch_chess-0.1.0.tar.gz |
|---|---|
| Size | 46.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b604e8076c794b9621f856abd0bde6c385f7a4fa8043156fb94810a9b74023b5
|
|
BLAKE2b-256 checksum How to use checksums |
228c670345b7949e6d7b6a01424f3ee193bf0dfdf78439de251f2eb40cb11602
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logRelease files / magmacrunch_chess-0.1.0-py3-none-any.whl
| Download URL | magmacrunch_chess-0.1.0-py3-none-any.whl |
|---|---|
| Size | 36.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fce014a07d10f9d55a787ed14e65ef4f32168c3b4d709561bbd3d7b7097d60bf
|
|
BLAKE2b-256 checksum How to use checksums |
8f38f1e03ea466d683ec0f4014db098e1cb33585ea1b92bfa422d35d3dc877f7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logSecurity for magmacrunch-chess 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for magmacrunch_chess-0.1.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.