Skip to content

Alphazero running in the browser - #373

Merged
adamantivm merged 40 commits into
mainfrom
jdb/javascript-nonsense
Aug 3, 2026
Merged

Alphazero running in the browser#373
adamantivm merged 40 commits into
mainfrom
jdb/javascript-nonsense

Conversation

@jonbinney

Copy link
Copy Markdown
Owner

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.

jonbinney and others added 30 commits July 5, 2026 18:28
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>
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>
…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>
jonbinney and others added 10 commits July 6, 2026 15:27
…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>
@jonbinney

Copy link
Copy Markdown
Owner Author

Create a directory for the model and config - call it $SERVER_DIR and add a subdirectory called "models". Download this config.yaml into $SERVER_DIR and download this onnx model into the models subdirectory. Then run the following commands from within you cloned deep_rabbit_hole directory.

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
jonbinney marked this pull request as ready for review August 2, 2026 22:19
@jonbinney

Copy link
Copy Markdown
Owner Author

ok @adamantivm this is ready for you to try

@adamantivm

Copy link
Copy Markdown
Collaborator

It worked! Merging

@adamantivm
adamantivm merged commit a6f1b55 into main Aug 3, 2026
6 checks passed
@adamantivm
adamantivm deleted the jdb/javascript-nonsense branch August 3, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants