The engine speaks UCI, so a standard bridge (lichess-bot) can host it on Lichess. This directory holds the configuration and the setup script for that.
Development is finished, but the bot is meant to keep playing on demand. If it has been off for months, start here rather than at Setup, which assumes a fresh machine.
cd ~/PycharmProjects/PyCheckmate
git pull # the engine runs from the working tree, see below
./deploy/setup.sh # idempotent: re-pushes config, re-applies the patch
bot up && bot statusI checked all of the following on 2026-08-05, so if something here is no longer true, that is your broken thing:
| Thing | State | Why it matters |
|---|---|---|
| lichess-bot clone | on the KNOWN_GOOD pin, rate-limit patch applied |
upstream drift cannot surprise you |
<clone>/.token |
present, chmod 600 |
the likeliest thing to have decayed, since a revoked or deleted token fails at startup |
| clone virtualenv | Python 3.12 | setup.sh rebuilds it if missing |
engines/engine.sh |
cds into this repo and runs engine.uci under PyPy |
nothing is pinned, so the bot plays whatever is in your working tree and git pull is the entire upgrade |
| PyPy 3.11 | installed via uv | uv python install pypy3.11 if not |
Two startup failures have actually happened here. One is an expired or missing
token. The other is an HTTP 429 lockout from restarting repeatedly while editing
config, so batch every config edit and then do exactly one up/down cycle.
Orphaned multiprocessing children hold streams open where a normal process
check cannot see them, so run ps aux | grep "lichess-bot/.venv" and sweep them
before retrying. The full manual is
docs/LICHESS_BOT.md.
One change is committed but unplayed. polyglot.enabled went to false on
2026-08-05, so the next time the bot runs, books/book.json picks the opening
for the first time ever. The book is verified end to end under
python -m engine.uci, and book.py confirms the move is legal in the exact
position before returning it, so it cannot cause a forfeit. It has still never
played a rated game. Watch the first one. If you want the safer path for a demo,
set it back to true and re-run setup.sh.
setup.sh and the bot control script are POSIX shell, so this path is
macOS/Linux. On Windows, run them under WSL, or drive lichess-bot directly with
the config.yml here, since the engine itself is plain UCI and platform-neutral.
git clone https://github.com/pdloc06/PyCheckmate.git
cd PyCheckmate && uv sync
./deploy/setup.shsetup.sh clones lichess-bot, builds its virtualenv, applies the patch below,
installs the config, and generates the launcher that points it at this engine.
It is idempotent, so re-running it is safe, and it reports anything it cannot
supply itself. Set LICHESS_BOT_DIR to put the clone somewhere other than
~/PycharmProjects/lichess-bot.
Two things it cannot do for you:
- The API token. Create one at
lichess.org/account/oauth/token
with the
bot:playscope, on a dedicated BOT account, and save it to<clone>/.token(one line,chmod 600). It is a credential: keep it out of git. - A polyglot book, if you leave
polyglot.enabled: truein the config. Either drop a.bininto the clone'sengines/, or set it tofalse.
Then link the control script and start:
ln -sf "$PWD/bot" ~/.local/bin/bot
bot up| File | What it is |
|---|---|
config.yml |
The tuned lichess-bot config: matchmaking, rated play, opponent bounds, time controls. 91 lines of deliberate divergence from upstream's default, each commented. This repo is its source of truth, because setup.sh overwrites the clone's copy, so edit it here. |
rate-limit.patch |
Six lines on lib/lichess_bot.py: sleep out a 429 cooldown rather than retrying every second and burying the log in tracebacks. |
setup.sh |
The installer described above. |
bot up / bot down / bot status / bot log, from the control script at the
repo root. Two things happen automatically:
- Game records write themselves.
bot upalso startsengine/tools/sf_watch.py, which grades each finished game with Stockfish in the idle gap before the next one begins and appends a record to~/.local/share/pycheckmate/game_analysis.jsonl.bot downwaits for the current game and its analysis before stopping;bot down nowskips both. - The engine version stamps itself.
sf_watchreads the git revision at startup and labels every record with it, writing the cut toversion_cuts.json. Never average results across two labels. Override withBOT_ENGINE_VERSION=<rev> bot upwhen a run spans a docs-only commit; thebotscript explains exactly when that is correct.
Full operations manual, including the traps: docs/LICHESS_BOT.md.
config.yml used to set polyglot.enabled: true with engines/komodo.bin and
max_depth: 20. lichess-bot consults that book before it asks the engine, in
lib/engine_wrapper.py where get_book_move sits at the top of search, so for
the first 39 plies of every game the polyglot book chose the move and
books/book.json was never reached. The 13,230-position book this project built
for itself got shipped and then measured nothing, across every rated game played
up to 2026-08-05.
It went to false on 2026-08-05 while closing the project out. Re-run setup.sh
to push that to the clone. Whoever next brings the bot up gets the first games in
which our own book chooses a move, so take a fresh version cut when you do,
because it changes what the bot plays.
The shape of the mistake is worth keeping. A shadowed book and a useless book
produce identical evidence, so no amount of game-record analysis could have
surfaced this. What would have caught it is a question asked before collecting
anything: by what path does this code actually get executed in the thing I am
about to measure? The book was verified under python -m engine.uci, which is
precisely the path lichess-bot skips.