{"slug": "show-hn-chesslm-tiny-chess-model-trained-on-stockfish", "title": "Show HN: Chesslm – Tiny chess model trained on Stockfish", "summary": "Developer skorotkiewicz released chesslm, an open-source chess engine that pairs a 100,353-parameter NumPy neural network with a fixed material evaluator and alpha-beta search, using Stockfish only to supply training labels and benchmark comparisons. The project ships a Tkinter desktop game and a localhost-only web server, runs on Python 3.10 or newer, and requires no Stockfish process at play time. The author states the included model.npz checkpoint's training history and playing strength have not been verified and claims no Elo rating.", "body_md": "A tiny neural chess evaluator with alpha-beta search and a desktop chess game.\n\n[Quick start](#quick-start) ·\n  [Desktop game](#desktop-game) ·\n  [Training](#training) ·\n  [Benchmarking](#benchmarking) ·\n  [Development](#development)\n\nchesslm combines a small NumPy network with a fixed material evaluator to choose\nchess moves. Play against the included `model.npz` in a Tkinter window, or request\na move from the command line. Playing uses your CPU and requires no Stockfish\nprocess. Stockfish supplies training labels and benchmark comparisons.\n\nThe network has 100,353 parameters and 401,412 bytes of float32 weights, about 392 KiB. Despite the name, it is not a language model. The included checkpoint's training history and playing strength have not been verified; no Elo is claimed.\n\nUse Python 3.10 or newer. Run these commands in a POSIX shell:\n\n```\ngit clone https://github.com/skorotkiewicz/chesslm.git\ncd chesslm\npython -m venv .venv\n. .venv/bin/activate\npython -m pip install -r requirements.txt\npython chess_game.py\n```\n\nThe desktop game needs Tkinter and a display. Some Linux distributions provide\nTkinter separately in a package named `python3-tk` or `tk`. On Windows, activate\nthe environment with `.venv\\Scripts\\Activate.ps1` in PowerShell.\n\nFor a terminal-only first move, run this from the project directory:\n\n```\nOPENBLAS_NUM_THREADS=1 python chesslm.py play --model model.npz\n```\n\nThe `OPENBLAS_NUM_THREADS=1` prefix limits OpenBLAS threading for the small\nnetwork. In PowerShell, set `$env:OPENBLAS_NUM_THREADS = \"1\"` before running the\nPython command. The desktop game sets this default automatically.\n\n```\npython chess_game.py\npython chess_game.py --color black\npython chess_game.py --model model.npz --depth 3 --max-nodes 20000\n```\n\nClick a piece, then its destination. Dots and rings mark legal moves, and buttons let you choose a promotion piece. Use arrow keys and Enter or Space to move with the keyboard; Tab reaches the buttons. Playing as Black flips the board.\n\nSearch runs in a background thread, so the window can redraw and close while the\nmodel thinks. New game becomes available after the search finishes. The default\ncheckpoint is `model.npz` beside the script.\n\nIf you use `uv`, you can also launch the game without setting up `.venv`:\n\n```\nuv run --python /usr/bin/python chess_game.py\nuv run --python /usr/bin/python chess_game.py --color black\n```\n\nThese examples use a Linux system Python with Tkinter installed. Choose a\nPython with Tkinter on your platform. `uv` installs the script's declared NumPy\nand python-chess dependencies in an isolated environment.\n\n```\npython chess_web.py\n# Or use an isolated uv environment:\nuv run --python /usr/bin/python chess_web.py\n```\n\nOpen **[http://127.0.0.1:8000](http://127.0.0.1:8000)** in your browser. Choose White or Black, then click\na piece and its destination. Legal moves are highlighted; promotions offer all\nfour pieces. Arrow keys and Enter work on the board. Each tab has its own game;\nrefreshing the page starts over.\n\nThe Python server loads `model.npz` beside the script and performs inference on\nCPU. The browser does not download the model. No Stockfish, training, external\nassets, or JavaScript packages are needed. Tkinter is not required.\n\nUse `--model /path/to/model.npz`, `--port 8080`, `--depth 3`, or\n`--max-nodes 20000` to change the defaults. The server preserves move history for\nrepetition draws and validates every move. Games are limited to 1000 submitted\nplies. One search runs at a time; another tab can retry if the model is busy.\n\nThis is a localhost-only server for personal play, not a public hosting setup.\nStop it with Ctrl+C. Keep `chess_web.html` beside `chess_web.py`.\n\nThe included [Pages workflow](https://github.com/skorotkiewicz/chesslm/blob/main/.github/workflows/pages.yml) builds and deploys the\nbrowser game when you push to `main`. In the repository, open **Settings > Pages**\nand select **GitHub Actions** as the source. Push these files, or run\n**Deploy chess game to GitHub Pages** from the Actions tab. For this repository,\nthe expected address is `https://skorotkiewicz.github.io/chesslm/`.\n\nPages cannot run a Python server. This build instead loads Pyodide and NumPy in\na Web Worker, then runs the same evaluator and search with the bundled\n`model.npz`. The checkpoint is copied unchanged; CI does not train or generate\ndata. Relative asset paths support project Pages URLs such as `/chesslm/`.\n\nThe first visit downloads the Python runtime and NumPy from jsDelivr and can take a minute on a slow connection. Browser inference uses depth 2 and a 2,000-node limit per move, below the local server's default budget. The UI stays responsive while the worker searches. Refreshing discards the game.\n\nTo preview the Pages build locally, use an environment with `requirements.txt`:\n\n```\npython build_pages.py\npython -m http.server 8001 --bind 127.0.0.1 --directory _site\n```\n\nOpen `http://127.0.0.1:8001`. Opening `index.html` as a `file://` URL will not work.\nThe build bundles python-chess with its GPL license in `chess.zip`.\n\n```\nOPENBLAS_NUM_THREADS=1 python chesslm.py play \\\n  --model model.npz --depth 3 --max-nodes 20000\n```\n\nThe command prints JSON with `move`, a UCI move string such as `e2e4`, and `nodes`,\nthe visited node count. When the game has ended, `move` is `null`. Add\n`--fen '...'` with a valid FEN to choose a move in another position.\n\nThis command chooses one move per invocation; it does not implement a UCI engine server. CLI model paths are relative to your working directory.\n\n| Component | Implementation | \n|---|---|\n| Input | 782 features: 12 piece planes, side to move, castling rights, legal en passant file, and halfmove clock | \n| Network | One 128-unit tanh hidden layer, learning a correction to a fixed material evaluator | \n| Target | White's evaluation, transformed with `tanh(centipawns / 600)` | \n| Training | Mean squared error with Adam; save the weights with the lowest validation loss | \n| Search | Iterative deepening, alpha-beta pruning, capture ordering, and four quiescence plies | \n| Runtime | NumPy and python-chess on CPU | \n\nThe weight size excludes the NPZ header, Python, NumPy, and process memory.\n\nA node limit can leave only a shallow completed search iteration. If none finishes, search returns a legal fallback move. Long tactics remain a limitation. The game and search recognize automatic draws but do not implement optional draw claims. A FEN does not include prior repetition history.\n\nTraining is optional. The included checkpoint is enough to play.\n\nData generation and benchmarking require a Stockfish executable. Stockfish is\nnot tracked in this repository. The default path is\n`stockfish/stockfish-linux-x86-64-universal`, relative to `chesslm.py`, for x86-64\nLinux. Supply `--engine /path/to/stockfish` to use another location or a binary\nfor your platform.\n\nRun these commands on your training machine. Output files must not already\nexist; `model-trained.npz` leaves the included checkpoint available.\n\n```\npython chesslm.py generate \\\n  --engine /path/to/stockfish \\\n  --positions 100000 --nodes 20000 --threads 2 \\\n  --output positions-train.jsonl\n\nOPENBLAS_NUM_THREADS=2 python chesslm.py train \\\n  --data positions-train.jsonl --epochs 30 --output model-trained.npz\n```\n\nStockfish labels self-play positions with White's centipawn evaluation using a three-line search. Opening choices vary among those lines, with occasional variation later. Mate labels use ±10,000 centipawns. The node budget bounds Stockfish's search work, not elapsed time.\n\nValidation holds out entire games and removes positions shared with those games from training. A dataset needs at least two games and some distinct positions. Training saves the initial weights if no epoch improves validation loss.\n\nThe dataset is loaded into memory. Input arrays need roughly 313 MB per 100,000 positions, plus JSON rows and temporary arrays. Larger datasets need more RAM.\n\nCopy `model-trained.npz` to your playing machine and select it explicitly:\n\n```\npython chess_game.py --model model-trained.npz\n```\n\nGenerate a separate corpus with a different seed. Do not train on this file. Use fresh output filenames if you repeat the workflow.\n\n```\npython chesslm.py generate \\\n  --engine /path/to/stockfish \\\n  --seed 9001 --positions 5000 --nodes 20000 \\\n  --output positions-benchmark.jsonl\n\nOPENBLAS_NUM_THREADS=1 python chesslm.py benchmark \\\n  --engine /path/to/stockfish \\\n  --model model-trained.npz --data positions-benchmark.jsonl \\\n  --positions 100 --nodes 50000\n```\n\nThe JSON report contains the position count, best-move agreement, and mean centipawn loss against Stockfish. These are noisy, node-limited estimates. Different seeds can still produce repeated openings, and match testing is needed to establish an Elo rating. A tiny distilled model should not be expected to match Stockfish.\n\nFrom an environment containing `requirements.txt`, run:\n\n```\nOPENBLAS_NUM_THREADS=1 python -m unittest -v\npython chesslm.py --help\npython chess_game.py --help\npython chess_web.py --help\n```\n\nTests cover encoding, checkpoint size and loading, gradient math, invalid positions, validation separation, node limits, promotions, check evasion, forced mates, a free queen capture, and desktop interactions. GUI tests skip when no display is available.\n\nThe tests do not run Stockfish, generate data, or optimize model weights. Search tests use hand-set zero weights; the derivative check uses random weights without optimization. Passing them does not measure the supplied checkpoint's strength. Generation and training still need end-to-end verification on the training machine.\n\n| File | Purpose | \n|---|---|\n| [`chesslm.py`](https://github.com/skorotkiewicz/chesslm/blob/main/chesslm.py) | Model, search, data generation, training, and benchmark CLI | \n| [`chess_game.py`](https://github.com/skorotkiewicz/chesslm/blob/main/chess_game.py) | Tkinter desktop game | \n| [`chess_web.py`](https://github.com/skorotkiewicz/chesslm/blob/main/chess_web.py) | Local HTTP server and validated game API | \n| [`chess_web.html`](https://github.com/skorotkiewicz/chesslm/blob/main/chess_web.html) | Browser chess board | \n| [`test_chess_web.py`](https://github.com/skorotkiewicz/chesslm/blob/main/test_chess_web.py) | HTTP, rule, request-validation, and static-build checks | \n| [`chess_position.py`](https://github.com/skorotkiewicz/chesslm/blob/main/chess_position.py) | Shared move validation and board responses | \n| [`chess_worker.js`](https://github.com/skorotkiewicz/chesslm/blob/main/chess_worker.js) | Browser-side model inference with Pyodide | \n| [`build_pages.py`](https://github.com/skorotkiewicz/chesslm/blob/main/build_pages.py) | Static site build, including the unchanged model | \n| [`model.npz`](https://github.com/skorotkiewicz/chesslm/blob/main/model.npz) | Included checkpoint for play | \n| [`requirements.txt`](https://github.com/skorotkiewicz/chesslm/blob/main/requirements.txt) | Runtime dependencies | \n| [`test_chesslm.py`](https://github.com/skorotkiewicz/chesslm/blob/main/test_chesslm.py) | Model and search checks | \n| [`test_chess_game.py`](https://github.com/skorotkiewicz/chesslm/blob/main/test_chess_game.py) | Desktop game checks | \n\nMIT.", "url": "https://wpnews.pro/news/show-hn-chesslm-tiny-chess-model-trained-on-stockfish", "canonical_source": "https://github.com/skorotkiewicz/chesslm", "published_at": "2026-09-20 15:13:44+00:00", "updated_at": "2026-09-20 15:23:01.228394+00:00", "lang": "en", "topics": ["machine-learning", "neural-networks", "ai-tools", "developer-tools"], "entities": ["chesslm", "Stockfish", "NumPy", "Tkinter", "python-chess", "GitHub Pages", "skorotkiewicz"], "alternates": {"html": "https://wpnews.pro/news/show-hn-chesslm-tiny-chess-model-trained-on-stockfish", "markdown": "https://wpnews.pro/news/show-hn-chesslm-tiny-chess-model-trained-on-stockfish.md", "text": "https://wpnews.pro/news/show-hn-chesslm-tiny-chess-model-trained-on-stockfish.txt", "jsonld": "https://wpnews.pro/news/show-hn-chesslm-tiny-chess-model-trained-on-stockfish.jsonld"}}