ChessMap is a remote chess training app built around a human-like adaptive rival. A player can enter Practice with only a profile name, or join a host-created match with a six-character code. Maia-3 runs on the server GPU, the room server validates every move and clock, and a transparent opponent map changes how the AI plays across games. An anonymized combined memory also retrieves similar positions and outcomes from every completed ChessMap game.
- Practice mode: enter the same name on every visit; no room code is needed.
- Selectable challenge: practice targets range from 1700 to 2500 in 100-point
steps, with independent
1+0,3+0,5+0, and10+0clocks. - Live match mode: a host watches while a remote human joins by code or QR.
- Human interaction: tap a piece to see legal moves, tap a destination, or drag.
- Human-like AI: Maia-3 79M predicts plausible moves at a configured Elo instead of searching for a perfect engine move at every turn. Delivery pacing varies by rating, time control, game phase, candidate ambiguity, and clock pressure.
- Mistake recovery: active rooms warn before navigation and resume after an accidental refresh through a private browser-held reconnect token.
- Adaptive rival: profile confidence, estimated Elo, aggression, tactical risk, and clock behavior influence candidate selection and AI time usage.
- Combined memory: a local position graph and deterministic piece-square vectors aggregate anonymized move/outcome evidence across all completed games.
- Post-game coach: Maia replays up to eight representative decisions, identifies top-two human choices, suggests alternatives, and references the player's strongest stored prior game.
- Local-first history: PGN, move times, profile changes, and coaching summaries persist outside Git.
- Open the app.
- Enter a profile name.
- Choose a Maia target from 1700 to 2500 and a practice clock.
- Select Practice against your AI. The default is
1700at5+0.
A profile name is currently an identifier, not a secure account. The same spelling loads the same history. Authentication is intentionally left for the persistent cloud phase.
- The host selects Create match to watch.
- The app creates a room code and remote QR.
- The human enters a name and room code from another device.
- Both sides receive authoritative clock and board updates through Socket.IO.
flowchart LR
A[Human move] --> B[chess.js legal validation]
B --> C[Authoritative room and clock]
C --> D[Maia-3 79M on PyTorch]
D --> E[Five human candidate moves and WDL]
E --> F[Private opponent-map adaptation]
E --> M[Combined position-memory recall]
F --> G[Bounded candidate re-ranking]
M --> G
G --> J[AI move over Socket.IO]
J --> H[Finished-game Maia review]
H --> I[Persistent profile and PGN]
H --> K[Anonymized graph indexing]
Maia-3 is a Chessformer model trained to predict human chess decisions across skill levels. ChessMap runs it as one persistent UCI subprocess so the model stays warm on the GPU. For each AI turn it receives move history, AI Elo, and estimated opponent Elo, then returns likely human moves and predicted win/draw/loss outcomes.
The base model weights remain frozen. ChessMap performs online profile adaptation, not unsupported per-game neural fine-tuning:
- Low-confidence profiles mostly preserve Maia's normal human move distribution.
- Higher confidence gradually gives predicted outcome more weight while retaining a penalty for moves that are unlikely human choices.
- AI move timing follows a separate transparent human pacing model; Maia inference latency itself is not presented as thinking time.
- The estimated player rating is sent to Maia on every AI turn.
Maia4All research describes few-game personal embeddings, but its official prototype-enriched implementation and weights are not publicly released. This repo does not claim to reproduce it.
The current system has a real combined retrieval memory, but not a combined neural model. It stores exact position nodes, move-transition edges, aggregate outcomes, and deterministic 780-dimensional piece-square vectors for cosine similarity. It does not use a vector database, learned embeddings, or Maia weight updates. One frozen Maia model is shared by all rooms, private profiles remain isolated, and collective records contain no player names. See Architecture And Memory Model for current architecture, live sequence, coaching, storage, privacy boundaries, and future learned-memory options.
The map is updated after completed games:
| Signal | Current evidence |
|---|---|
| Aggression | Captures, checks, advanced pawns, and castling behavior |
| Tactical risk | Checks, queen activity, and capture frequency |
| Time pressure | Average human think time |
| Rating | Result update against the actual AI Elo for that game |
| Confidence | Saturating function of completed games, capped at 95% |
Profiles are keyed by normalized player name in data/opponents.json. Completed
games are appended to data/games.jsonl with PGN, move times, result, map snapshot,
and coaching summary. A resignation queues the partial PGN and profile write before
the server acknowledges it. The entire data/ directory is gitignored.
data/combined-memory.json is built automatically from existing PGNs on startup
and updated after each new game's review. It contains:
- canonical four-field FEN position nodes;
- directed move-transition edges and counts;
- per-move play counts and side-to-move outcome sums;
- up to three anonymized reference examples per move;
- SHA-256-derived game IDs for idempotent indexing;
- deterministic 780-dimensional piece-square/castling/en-passant vectors computed in memory for similar-position cosine retrieval.
During an AI turn, ChessMap queries only positions with the same side to move,
keeps the eight most similar nodes above 0.82 similarity, and checks evidence for
Maia's top five candidates. Historical evidence contributes at most 0.18 bounded
weight; without supported candidates it contributes exactly zero. Memory can
re-rank Maia candidates but cannot introduce an illegal or out-of-policy move.
After the game ends, the room immediately enters an analysis state. Maia replays up to eight evenly sampled human decisions at the player's estimated level:
- A played move ranked first or second becomes a What worked item.
- A move outside the top two becomes a Try next time item with Maia's leading human candidate and expected-score context.
- The review compares the game with the strongest prior reviewed game for the same profile. The first game becomes the baseline.
- Combined memory reports anonymized support counts and stored-game provenance for similar positions. The current game is indexed only after review, preventing self-reference.
This is Maia-based behavioral coaching, not a Stockfish proof that a move is objectively best. That distinction is deliberate: the product is teaching a human through realistic human candidates.
Maia-3 supplies skill-aware move choice through its documented SelfElo and
OppoElo options with one-node inference. ChessMap adds timing separately from a
measured sample of the CC0 Lichess open database.
The committed calibration/lichess-human-pacing-centered.json artifact contains only
aggregate quantiles and style rates, never usernames or games. It is stratified by:
- exact
1+0,3+0,5+0, and10+0controls; - nearest-100 rating bands centered on each target from 1700 through 2500;
- opening, middlegame, and endgame phase;
- normal, low, critical, and scramble clock pressure.
The artifact records its archive URL, actual sample byte count, byte-range request,
SHA-256, parser method, and deterministic reservoir seed. Likely tournament berserk
games are excluded when first observed clocks do not match the nominal control.
Regenerate it with the pinned dependencies in
scripts/requirements-calibration.txt and
scripts/calibrate_lichess_pacing.py. Raw compressed samples belong under the
gitignored data/research/ directory. If the artifact is missing or invalid,
ChessMap safely falls back to tested built-in pacing constants. ChessMap also
records both human and AI move times for future local recalibration without
changing Maia's frozen weights.
- Windows 10/11
- Node.js 20 or newer
- Python 3.10-3.12
- NVIDIA GPU recommended; CPU inference is supported by Maia but much slower
Install JavaScript dependencies:
npm installCreate a Python environment and install CUDA PyTorch appropriate for the machine, then install the pinned Maia-3 source revision:
conda create -n chessmap python=3.11 -y
conda activate chessmap
pip install torch --index-url https://download.pytorch.org/whl/cu121
pip install git+https://github.com/CSSLab/maia3.git@1e13597
python -m maia3.cache --model maia3-79mSet MAIA_PYTHON to that environment's interpreter. See .env.example.
The app reads environment variables from the launching shell; it does not load the
example file automatically.
Run locally:
npm run devnpm run shareOn Windows, the first run downloads the official cloudflared.exe release into the
gitignored tools/ directory, builds the production app, creates a temporary HTTPS
Quick Tunnel, and starts ChessMap with the public hostname embedded in room QR codes.
Keep the terminal and PC running. The random URL changes after every restart and has
no uptime guarantee.
For a stable hostname, use a named Cloudflare Tunnel and set PUBLIC_ORIGIN.
Deployment tradeoffs and the recommended cloud migration are documented in
docs/DEPLOYMENT.md.
No. A normal shutdown stops the website and Maia, but data/opponents.json,
data/games.jsonl, data/combined-memory.json, and the cached Maia checkpoint
remain on disk. The temporary
public URL stops working and will change on the next npm run share. Disk failure,
manual deletion, or an unsynced machine can still lose local history, so back up the
data/ directory or migrate it to a hosted database.
npm test # maps, graph/vector memory, policy, and coaching
npm run lint # ESLint
npm run build # optimized Next.js build and TypeScript
npm run dev # local development server
npm run share # build + temporary public tunnel + production server
node scripts/room_smoke.mjs # host/join/AI integration check
node scripts/practice_smoke.mjs # no-code practice/AI integration checksrc/app/ Next.js interface and responsive chessboard
server.mjs Socket.IO rooms, clocks, persistence, review orchestration
lib/maia-engine.mjs Serialized persistent UCI bridge to Maia-3
lib/opponent-map.mjs Profile extraction and adaptive candidate policy
lib/combined-memory.mjs Anonymized position graph and vector retrieval
lib/coach.mjs Review-position selection and coaching summary
scripts/ Tunnel setup, public launcher, and smoke tests
tests/ Deterministic Node test suite
data/ Local private profiles and games (gitignored)
- Game/profile data remains on the ChessMap host unless a cloud database is added.
- Combined memory omits names/profile keys and stores hashed game IDs, dates,
openings, results, positions, moves, and outcomes. PGNs still remain private in
games.jsonl. - Room codes are convenience identifiers, not authentication or authorization.
- Profile names are not accounts; do not use sensitive personal information.
- Quick Tunnels are intended for development and personal testing, not production.
- Model checkpoints, local data, Python environments, and tunnel binaries are not committed to Git.
- Maia-3 / Chessformer for human move modeling
- chess.js for chess rules
- react-chessboard for board input
- Socket.IO for realtime rooms
- Next.js for the web application
- Cloudflare Tunnel for remote testing
Review upstream licenses before distributing a hosted derivative. This repository does not currently declare its own license.