Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ead085a
Add design doc: browser-WASM play server (Milestone 1)
jonbinney Jul 5, 2026
f06c03c
Spec: use a Python (FastAPI) thin server instead of Rust
jonbinney Jul 5, 2026
52a99fa
Add Plan 1: quoridor-wasm crate implementation plan
jonbinney Jul 5, 2026
fd8df8e
refactor(rust): feature-gate heavy deps so core builds with --no-defa…
jonbinney Jul 6, 2026
29f8d0a
feat(mcts): portable async leaf-parallel batched search driver with i…
jonbinney Jul 6, 2026
f116339
Fix Plan 1 Task 3: root pre-expansion + collision guard in batched se…
jonbinney Jul 6, 2026
bc8b251
fix(mcts): pre-expand root + guard expand to prevent leaf-collision d…
jonbinney Jul 6, 2026
7d8aa8b
style(rust): rustfmt import/module ordering in alphazero module
jonbinney Jul 6, 2026
05d384c
feat(wasm): add quoridor-wasm crate skeleton building against the core
jonbinney Jul 6, 2026
cf583fd
feat(wasm): WasmGame session bindings (new/apply/undo/stateView) with…
jonbinney Jul 6, 2026
77a8fde
fix(wasm): rustfmt, view serialization/wall-boundary tests, reuse mec…
jonbinney Jul 6, 2026
0b2c4d5
feat(wasm): runSearch binding wiring async JS eval + progress to batc…
jonbinney Jul 6, 2026
39a1d60
Fix Plan 1 Task 5: drop uncompilable JsFuture line; note node vs brow…
jonbinney Jul 6, 2026
b476c2d
Fix Plan 1 Task 5: guard runSearch against terminal-game panic
jonbinney Jul 6, 2026
ac23ce0
fix(wasm): guard runSearch against panic on an already-finished game
jonbinney Jul 6, 2026
5ac7664
docs(wasm): quoridor-wasm README with build, JS API, and test-runner …
jonbinney Jul 6, 2026
12c8b66
fix(wasm): runSearch honors WasmGame's full game-over check; harden s…
jonbinney Jul 6, 2026
5d0eb1d
docs(plan): correct stale deferred note (root pre-expansion was imple…
jonbinney Jul 6, 2026
7cac431
Add Plan 2: Python FastAPI play server implementation plan
jonbinney Jul 6, 2026
eccf176
deps: add fastapi, uvicorn, httpx for the Python play server
jonbinney Jul 6, 2026
93ac68a
feat(play-server): model listing helper
jonbinney Jul 6, 2026
bbfaa7a
feat(play-server): config view (board dims + alphazero defaults)
jonbinney Jul 6, 2026
f5fc9b0
feat(play-server): FastAPI app factory (config/models APIs, static SP…
jonbinney Jul 6, 2026
e33f50f
feat(play-server): uvicorn CLI entrypoint
jonbinney Jul 6, 2026
feab00d
docs+test(play-server): README + lock in mount-precedence and COOP/CO…
jonbinney Jul 6, 2026
925dd38
Add Plan 3: Svelte + Worker + onnxruntime-web frontend
jonbinney Jul 6, 2026
eacf76d
feat(frontend): scaffold Vite+Svelte app (builds, vitest, ORT wasm copy)
jonbinney Jul 6, 2026
5b0cd1b
gitignore: don't ignore deep_quoridor/frontend/src/lib (Python lib/ p…
jonbinney Jul 6, 2026
4ddb3a4
feat(frontend): pure board model + eval marshalling with vitest
jonbinney Jul 6, 2026
d19331e
feat(frontend): API client for /api/config and /api/models
jonbinney Jul 6, 2026
d84ecc0
feat(frontend): AI web worker (wasm+ORT) and main-thread client
jonbinney Jul 6, 2026
9f00b46
feat(frontend): board, control rail, config drawer, app shell
jonbinney Jul 6, 2026
a42b59a
fix(frontend): harden interaction flow (turn-gating, worker serializa…
jonbinney Jul 6, 2026
2b1176d
docs(frontend): README with build + run instructions
jonbinney Jul 6, 2026
9b85724
fix(frontend): clone params to a plain object before postMessage
jonbinney Jul 6, 2026
d3d43d0
fix(frontend): human at bottom moving up, click-to-place walls + hove…
jonbinney Jul 16, 2026
7717998
fix(frontend/wasm): AI now moves — None serialized as null, not undef…
jonbinney Jul 16, 2026
b3903e1
fix(frontend): draw walls only when placed or hovered, not on every l…
jonbinney Jul 16, 2026
ca4edac
feat(frontend): let the human choose to play first (P1) or second (P2)
jonbinney Jul 16, 2026
82b2071
feat(play-server): serve from a flat play dir (config.yaml + models/)
jonbinney Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -194,4 +194,10 @@ target
Cargo.lock

# Mac crap
.DS_Store
.DS_Store

# Superpowers brainstorm companion scratch files
.superpowers/

# The broad Python lib/ pattern above must not swallow the frontend source dir.
!deep_quoridor/frontend/src/lib/
3 changes: 3 additions & 0 deletions deep_quoridor/ci_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ pyyaml
pydantic
pydantic-yaml
jax
fastapi
uvicorn
httpx
2 changes: 2 additions & 0 deletions deep_quoridor/frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
61 changes: 61 additions & 0 deletions deep_quoridor/frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Quoridor frontend (Svelte + Web Worker + onnxruntime-web)

The browser play app. A Web Worker runs the `quoridor-wasm` MCTS and evaluates the
net with onnxruntime-web (WebGPU, wasm-CPU fallback); the main thread renders the
board and streams the AI's "thinking" progress. Served by the Python play server
(`src/run_play_server_web.py`). Design: `docs/superpowers/specs/2026-07-05-browser-wasm-play-server-design.md`.

## Build

```
# 1. Build the wasm package (Plan 1):
wasm-pack build deep_quoridor/rust/quoridor-wasm --target web --release
# 2. Build the frontend:
npm --prefix deep_quoridor/frontend install
npm --prefix deep_quoridor/frontend run build # -> deep_quoridor/frontend/dist/
```

## Run (play a game)

```
# Point the server at a run directory (config.yaml + models/checkpoints/*.onnx)
# and the built SPA:
PYTHONPATH=deep_quoridor/src python deep_quoridor/src/run_play_server_web.py \
/path/to/runs/<run_id> --static-dir deep_quoridor/frontend/dist --port 8080
# open http://localhost:8080
```

Quick try with the bundled fixture model (5×5, 2 walls):
```
RUN=$(mktemp -d)
printf 'run_id: play\nquoridor:\n board_size: 5\n max_walls: 2\n max_steps: 50\nalphazero:\n mcts_n: 200\n mcts_c_puct: 1.4\nself_play:\n num_processes: 1\n games_per_process: 1\ntraining:\n games_per_training_step: 1.0\n learning_rate: 0.001\n batch_size: 64\n weight_decay: 0.0001\n replay_buffer_size: 1000\n' > "$RUN/config.yaml"
mkdir -p "$RUN/models/checkpoints"
cp deep_quoridor/rust/fixtures/alphazero_B5W2_mv1.onnx "$RUN/models/checkpoints/model_1.onnx"
PYTHONPATH=deep_quoridor/src python deep_quoridor/src/run_play_server_web.py "$RUN" \
--static-dir deep_quoridor/frontend/dist --port 8080
# open http://localhost:8080 (needs a WebGPU-capable browser, or it falls back to wasm-CPU)
```

Dev mode with HMR: `npm --prefix deep_quoridor/frontend run dev`. Vite serves the
COOP/COEP headers itself, but the `/api/*` and `/models/*` routes come from the
Python server, so for full play run the built app behind the Python server (above)
or add a Vite dev proxy to it.

## How it fits together

```
Browser
main thread (Svelte) ── postMessage ─▶ Web Worker (ai.worker.ts)
board, progress bar, ◀─ state/progress ─ quoridor-wasm (game + MCTS)
undo, config drawer onnxruntime-web (WebGPU) ── eval_batch
Python server: / (SPA) · /api/config · /api/models · /models/*.onnx · /ort/*.wasm
```

## Tests
```
npm --prefix deep_quoridor/frontend run test # vitest: board model, eval marshalling, api client
```
Unit tests cover the pure logic (board derivation, eval tensor marshalling, API
client). End-to-end gameplay (WebGPU inference, progress bar, clicking) is a manual
browser check — the server's `/api/*`, `/models/*`, and `/ort/*` routes are
integration-verified via curl, but actually playing needs a real browser.
14 changes: 14 additions & 0 deletions deep_quoridor/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Quoridor</title>
<!-- Inline favicon so the browser doesn't 404 on /favicon.ico. -->
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><rect width='16' height='16' rx='3' fill='%232b1a0c'/><circle cx='8' cy='11' r='2.4' fill='%231e3a8a'/><circle cx='8' cy='5' r='2.4' fill='%23b91c1c'/></svg>" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
Loading
Loading