lc0_dart 0.1.2
lc0_dart: ^0.1.2 copied to clipboard
LC0 (Leela Chess Zero) classical CNN inference in pure Dart with a PUCT / virtual-loss MCTS and a UCI shell. Loads a .pb.gz LC0 network (LINEAR16 / FLOAT16 / BFLOAT16), folds BatchNorm at load time, r [...]
lc0_dart #
LC0 (Leela Chess Zero) classical CNN inference in pure Dart, packaged as a UCI-compatible chess engine that plays legal chess against any GUI (Arena, Cute Chess, Lichess board editor, ...).
The heavy lifting (Conv2d, Lc0Reader, Lc0Net, Lc0Input) lives in the
sibling dart_pytorch
package; this project depends on it plus dartchess for move
generation, and glues them together with the ported LC0 policy-index
tables and a batched PUCT search (virtual loss) that reaches
~1300 rollouts/sec on an RTX 3060 at B=32.
Install #
# pubspec.yaml
dependencies:
lc0_dart: ^0.1.0
The dart_pytorch dependency needs a native CUDA library
(native/lib/libmat_mul.so — Linux/WSL2 today; build with
nvcc --shared -Xcompiler -fPIC -o native/lib/libmat_mul.so lib/native/src/engine.cu from that package). Pass
device: Device.CPU when constructing Lc0Net to skip GPU
entirely.
Quick start #
# One-time model download
mkdir -p models/lc0 && cd models/lc0
curl -sSL -O \
"https://storage.lczero.org/files/networks-contrib/744706.pb.gz"
cd ../..
# One-shot inference (WDL + top 20 raw policy slots)
dart pub get
dart run bin/lc0_demo.dart # starting position
dart run bin/lc0_demo.dart 'rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1'
# UCI engine: pipe commands manually or plug into a chess GUI
dart run bin/lc0_uci.dart --rollouts 400 --batch 32 # ~300 ms/move on GPU
uci
isready
position startpos moves e2e4 c7c5
go
Once running you can change search depth on the fly:
setoption name Rollouts value 100
Sample UCI transcript — Fool's Mate defence, Black to move after
1. f3 e5 2. g4:
info string [mcts 10] nodes=41 Q=0.167 time=9980ms (4 n/s)
info string top: d8h4 n=8 Q=1.000 P=0.8% | g8e7 n=4 Q=0.080 P=37.9% ...
bestmove d8h4
Even though the policy prior for Qh4 is only 0.8%, MCTS seeds every
root child with one rollout, the resulting position is checkmate
(Q=+1 for the mating side), and PUCT commits to it immediately.
That's the whole reason MCTS matters: policy-only picked Ne7 here.
Scope #
- In scope: classical CNN body (128×10 with SE), WDL value head,
policy → 1858 legal-move ranking via the ported
kConvPolicyMap+kMoveStrstables, dartchess-backed move generation, batched PUCT search with virtual loss + root-child seeding for cheap mate-in-1 discovery, CPU / GPU tensor backends viadart_pytorch, UCI shell. - Out of scope: multi-thread search, Dirichlet noise, tablebases, ponder, tuned time management, attention-body networks (BT2, BT3, T78+), Mish activation, Smolgen.
At 400 rollouts / batch 32 on an RTX 3060 (~300 ms/move) this plays
qualitatively stronger chess than raw policy: sees mate-in-1
instantly, avoids most tactical blunders, and follows opening theory
correctly. It is still a tiny 128×10 network with a fixed-depth
search — this is a demonstration, not a Stockfish substitute. Larger
nets (e.g. t1-256x10, ~37 MB) drop straight into the same reader
and forward path.
What we ported #
See REFERENCE.md for the file-by-file mapping between
this Dart port and its LC0 C++ source (expected at
/mnt/c/www/cpp/lc0 — clone with
git clone --depth 1 https://github.com/LeelaChessZero/lc0.git).