Alphazero running in the browser - #373
Merged
Merged
Conversation
Moves the AlphaZero AI into the browser via Rust→WASM + onnxruntime-web (WebGPU), collapsing the server to a thin static host. Captures the architecture, component boundaries, testing, and deferred milestones agreed in brainstorming. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The server no longer touches the AI (MCTS+ONNX run in WASM), and the run directories, trainer, and config loaders are already Python — so a Python server reuses existing code for M3/M4. Retires the Rust play_server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bite-sized TDD plan to feature-gate quoridor-rs for wasm, add a portable async leaf-parallel batched MCTS driver, and build the quoridor-wasm crate with game + runSearch bindings. First of three plans for the browser-WASM play server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ult-features Make arrow/parquet/rayon/dashmap optional behind policy_db/parallel features, move rand_distr to always-on, gate the modules that use them, and split the ort OnnxEvaluator out of the always-compiled evaluator core. python/binary builds unchanged; quoridor-rs core now builds with --no-default-features for wasm. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…arch Code review found the driver duplicated expand_node when multiple leaves in a round collide on the unexpanded root (guaranteed round 1 with leaf_parallelism>1), producing duplicate children and undercounted visits. Pre-expand the root and guard expand_node; strengthen tests to assert distinct actions + visit sum ~n. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uplicate children
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Workspace member (cdylib+rlib) depending on quoridor-rs with default-features off. Verified: native workspace build excludes it (default-members=["."]), and `wasm-pack build --target web` compiles the core to wasm32 and emits pkg/. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ser test runner Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…notes Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plit_outputs runSearch's terminal guard reused only mechanics.is_game_over() (win), missing the step-cap draw that WasmGame.is_game_over() and apply_action() enforce — so a maxed-out game could return a move apply_action then rejects. Make is_game_over() pub and reuse it. Also reject non-multiple 'logits' lengths at the JS boundary. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Serves the SPA + wasm + model .onnx files with COOP/COEP + wasm MIME, exposes /api/config and /api/models reusing v2.config.load_user_config. Targets the real models/checkpoints/ layout. Second of three plans for the browser-WASM play server. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…A+models, COOP/COEP)
…EP-on-static Adds the play_server_web README (run instructions + API) and two coverage tests from review: /api and /models still resolve with the SPA catch-all mounted, and COOP/COEP headers are present on served model files. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The browser play app tying Plans 1+2 together: Svelte UI, a Web Worker running quoridor-wasm MCTS + onnxruntime-web (WebGPU) inference, ORT served same-origin under COEP, built by Vite and served by the Python server. Final milestone-1 piece. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…attern collision) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion, cleanup) Review of the worker+UI found the board/undo/new-game were clickable during the brief window after a human move where it's the AI's turn — a stray click could inject an AI-turn move and race the in-flight search. Fixes: - worker: `busy` guard drops overlapping game commands; state messages carry a `thinking` flag that stays true through the whole AI turn (no gap/flicker); undo replays then lets the AI reply; release the old ORT session and free the old Game on new game (no GPU/wasm leak); drop redundant casts; fix the real OrtLikeSession type mismatch. - UI: board gated on `awaitingHuman` (my turn, live, not thinking); Undo removes 2 plies (my move + AI reply) and is enabled only on my turn; initial config/model fetch wrapped in try/catch so a failure surfaces instead of hanging on "Loading…". Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Svelte 5 $state is a Proxy, which structuredClone/postMessage cannot clone (DataCloneError on newGame/setParams). Spread params into a plain object at the worker boundary so any $state proxy is de-proxied before it's posted. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…r, AI status - #1 orientation: vertical-flip the board so the human (player 0) is shown at the bottom moving up (blue pawn, ringed), AI at the top. New tested boardGrid.ts ports the old play-server's mirror + wall-group geometry. - #2/#3 walls: render a (2N-1) grid with wall gutters; place a wall by clicking its gutter cell, with a hover preview highlighting the full 3-cell wall. The old wall-button list is gone. - #4 thinking: prominent status line ("AI is thinking… d/total sims" with a pulsing dot / "Your move" / win). Added [ai]-prefixed worker console logs (newGame, session ready, search start/done, first eval batch, errors) to diagnose the AI-never-moves report from the browser console. Replaces board.ts/deriveBoard (superseded by boardGrid.ts). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ined
Root cause of "AI never moves": serde_wasm_bindgen serializes Option::None to
JS `undefined`, but the TS code checked `=== null`. Two effects, both fixed:
- boardGrid gated legal actions on `winner === null` -> undefined failed the
gate, so the human saw ZERO legal moves and could never move.
- the worker's aiMoveIfNeeded checked `winner !== null` -> undefined read as
"game over", so the AI never searched.
Fixes: serialize None as null in stateView() (Serializer.serialize_missing_as_null),
matching the declared `T | null` types; plus defensive `== null` / `!= null`
checks in boardGrid and the worker. Verified end-to-end in headless Chrome
(wasm-CPU fallback): human gets legal moves, AI searches and moves, turn cycles.
Also: inline favicon (no more /favicon.ico 404); dropped temporary debug logs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…egal slot The board tinted all 32 legal wall anchors persistently, so an empty board read as covered in walls. Remove that tint: a wall is now drawn only if placed, or as a hover preview over a placeable slot (legal_actions already excludes walls when the human has none left, so the "has available walls" condition holds). Verified in headless Chrome: fresh board shows 0 wall cells; hovering a slot shows exactly the 3-cell preview; leaving clears it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a "You play: First (P1) / Second (P2)" selector to the Setup panel, applied on New game. Choosing P2 makes the AI (player 0) open the game; the board already flips so the human is shown at the bottom moving up in either case. Verified in headless Chrome: P1 -> human moves first; P2 -> AI opens (Moves:1) then the human has legal moves, rendered below the AI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the run-directory layout (models under models/checkpoints/) with a simple play directory: a config.yaml plus a models/ dir of one or more .onnx files. Every model uses the settings in config.yaml (served via /api/config); the UI just picks which model to run. Renames the CLI arg run_dir -> play_dir and defaults --models-dir to <play_dir>/models. Tests + README updated; verified live (12 tests, curl, and a headless-Chrome game with a 2-model dropdown). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner
Author
|
Create a directory for the model and config - call it DRH_DIR=$(git rev-parse --show-toplevel)
cd $DRH_DIR
wasm-pack build deep_quoridor/rust/quoridor-wasm --target web --release
npm --prefix deep_quoridor/frontend install
npm --prefix deep_quoridor/frontend run build # -> deep_quoridor/frontend/dist/
echo "Serving config+models from ${SERVER_DIR}"
PYTHONPATH=deep_quoridor/src python deep_quoridor/src/run_play_server_web.py \
${SERVER_DIR} --static-dir deep_quoridor/frontend/dist --host 0.0.0.0 --port 8080
echo " open http://localhost:8080 (WebGPU-capable browser; falls back to wasm-CPU)" |
jonbinney
marked this pull request as ready for review
August 2, 2026 22:19
Owner
Author
|
ok @adamantivm this is ready for you to try |
Collaborator
|
It worked! Merging |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Runs the algorithm using rust compiled to webassembly and webgpu for the neural network.
@adamantivm tomorrow I'll add a script showing how to run the server.