clone

a takuzu puzzle field for agents to explore tile by tile, with a referee and a replayable spectator view

docs: fields, chunks, growth by reveal, the nightly on slate; no packings

dami committed bc22ec8
commit
bc22ec864fbc412d77a330bd20aa812e05c29221
parent
4b5c7070fe07b7a07c0b65295d66afb94ad0691c
author
dami <gazey55@gmail.com>
3 files changed, +60 -61
MREADME.md47++++++++++++--------
Mdeploy.md72+++++++++++++-----------------
Msite/scripts/solve.ts2+-
M README.md +28 -19
@@ -33,25 +33,32 @@ cd site && deno install && deno task dev # prints the agent prompt, serves
3333 3434 Open `http://127.0.0.1:8000/`, paste the printed prompt into Claude Code or3535 Codex, and watch. Replay a finished game at `/?replay=<game_id>`; its log is36-at `/log/<game_id>`.36+at `/log/<game_id>`. The hosted daily is at https://mosaic-daily.fly.dev/37+(one field per UTC day, calendar and leaderboards; see `deploy.md`).3738 38-Scripted client (also the protocol smoke test; both boards below have been39-played end to end, logs under `site/data/games/`):39+Scripted client (also the protocol smoke test):4040 4141 ```42-cd site && deno task solve -- --seed a --G 27 --kind grid --n 14 # the phase-1 2x243-cd site && deno task solve -- --seed a --G 60 --kind big-first # packed, mixed sizes42+cd site && deno task solve -- --seed a --G 27 --kind grid --n 14 # the phase-1 2x243+cd site && deno task solve -- --seed a --G 60 --kind scatter # a scatter field44+cd site && deno task solve -- --seed a --kind field --chunks 3 # a chunked field that grows45+cd site && deno task solve -- --url https://mosaic-daily.fly.dev/mcp --join day-<date> --agent me --model my-model4446 ```4547 46-Boards are scattered fields of shapes (manifest v3, see PLAN.md):47-`mosaic-engine pack --kind scatter --G N` drops shapes at seeded random48-positions with rectangular overlaps and small holes (`--coverage`,49-`--compound`, `--max-hole`), `mosaic-engine gen --seed S --G N --kind50-scatter` fills it. `grid` (X-by-X squares of side n, `--X --n`) is kept51-for the 2x2 standard and the X=15 envelope; `big-first` and `bands` are52-square packings. Generation is a batch job cached under53-`site/data/cache/`, keyed by generator version (`mosaic-engine version`),54-seed, G, kind and n. Measured timings are in PLAN.md ("Measured").48+Boards are fields of shapes (manifest v3, see PLAN.md): `mosaic-engine49+pack --kind scatter --G N` drops shapes at seeded random positions with50+rectangular overlaps and small holes (`--coverage`, `--compound`,51+`--max-hole`); `mosaic-engine gen --seed S --G N --kind scatter` fills52+one. A daily-sized field is chunked: `mosaic-engine field --seed S --G53+1000 --chunks 3 --chunk-G 60 --rings all` generates the centre cluster54+and every ring around it (checkerboard of chunks, each a process); the55+site reveals rings during the day as `grow` events. `grid` (X-by-X56+squares of side n, `--X --n`) is kept for the 2x2 standard and the X=1557+envelope. Generation is a batch job cached under `site/data/cache/`,58+keyed by generator version (`mosaic-engine version`), seed and59+parameters; the nightly runs on slate (`.woodpecker/nightly.yaml`) and60+pushes the next day's field to the hosted app. Measured timings are in61+PLAN.md.5562 5663 ## Tests5764
@@ -64,11 +71,13 @@ seed, G, kind and n. Measured timings are in PLAN.md ("Measured").
6471 validity, the overlap acceptance rule, hole statistics), the fill order,6572 generator determinism and every per-tile assertion (re-verified from6673 the JSON) on the 2x2 and X=15 fixtures and on scatter fields, and the67- judge (classification and the <50 ms budget). `MOSAIC_SLOW=1` also regenerates the 2x268- fixture from its seed and compares.69-- `site`: `deno task test` covers geometry and hue assignment at X=2 and70- X=15, fixed-lightness colour mixing, delta coding, referee gating, the71- guess policy, live-fold == replay-fold at every revision, and that every74+ judge (classification and the <50 ms budget). `MOSAIC_SLOW=1` also75+ regenerates the 2x2 fixture from its seed and compares.76+- `site`: `deno task test` covers geometry and hue assignment, colour77+ mixing, delta coding, referee gating (frontier, territory, revision),78+ agents and meets, region-scoped observe under fog, growth, the79+ analyzer, live-fold == replay-fold at every revision, the daily loop80+ (rollovers, archives, leaderboards, honours, restart), and that every7281 finished game log on disk replays to a solved state with the right7382 reward.7483
M deploy.md +31 -41
@@ -1,52 +1,41 @@
11 # Deploying the daily22 3-Two machines on Fly.io, one app:4-5-- **`app`** (shared-cpu-2x, 2 GB, the `/data` volume): serves the site,6- judges (milliseconds), runs the daily loop. It generates nothing except7- a small emergency field when a day has no field at rollover8- (`MOSAIC_GENERATE=1`, `MOSAIC_DAILY_PREPARE=0`). Measured on it:9- ring 1 of the daily took 40 min at 181 MB peak RSS — far too slow for10- the nightly, fine for judging.3+One app on Fly.io, one machine, and a nightly job on slate:4+5+- **`app`** (`mosaic-daily`, dfw, shared-cpu-2x, 2 GB, the `/data`6+ volume): serves the site, judges (milliseconds), runs the daily loop.7+ It generates nothing except a small emergency field when a day has no8+ pushed field at rollover (`MOSAIC_GENERATE=1`), which `/` announces.9+ Measured on it: ring 1 of the daily took 40 min — far too slow for the10+ nightly, fine for judging.1111 - **The nightly runs on slate** (the user's Woodpecker CI,1212 `.woodpecker/nightly.yaml`): cron `nightly` at 05:00 UTC (and the1313 manual event) builds the engine with `golang:1.27` and runs1414 `deno task push-day --tomorrow --host https://mosaic-daily.fly.dev1515 --rings all --jobs $(nproc) --band 24` with the `mosaic_push_token`1616 secret; caches live under `/var/lib/mosaic-nightly` on the agent (repo17- is trusted for volumes). Woodpecker repo id 4, cron id 9.18-- **If slate is down**, the paid fallback is a Fly scheduled machine19- (performance-4x, ~$0.15 per run, ~50 min for the whole G=1000 field):20-21- ```22- IMAGE=$(fly status -a mosaic-daily | grep -o 'mosaic-daily:deployment-[A-Z0-9]*' | head -1)23- fly machine run "registry.fly.io/$IMAGE" -a mosaic-daily --name mosaic-nightly --region dfw \24- --vm-size performance-4x --schedule daily --restart no \25- -e MOSAIC_PUSH_TOKEN="$(cat ~/.config/mosaic/push-token)" \26- -e MOSAIC_DATA=/tmp/nightly/ -e MOSAIC_GOMEMLIMIT=512MiB -e MOSAIC_DAILY= \27- -e MOSAIC_ROLE=nightly -e MOSAIC_NIGHTLY_ARGS='--host https://mosaic-daily.fly.dev --rings all --jobs 4 --band 24'28- fly machine start mosaic-nightly -a mosaic-daily # run it now29- fly machine destroy mosaic-nightly --force -a mosaic-daily # when slate is back30- ```31-32- It runs once a day at about its creation time and stops on exit; it33- is not touched by `fly deploy` (update its image with `fly machine34- update mosaic-nightly --image … -y`).35-- **Or by hand from the Mac:** `cd site && deno task push-day <date>`36- (token in `~/.config/mosaic/push-token`).37-38-Rollover prefers the pushed field, then one the app prepared itself39-(only if `MOSAIC_DAILY_PREPARE=1`), then the emergency field, which `/`40-announces. `/status` reports whether tomorrow's field is pushed.41-42-The Mac can still push by hand (`deno task push-day <date>`, token in43-`~/.config/mosaic/push-token`).17+ is trusted for volumes). Woodpecker repo id 4, cron id 9. `/status`18+ on the app reports whether tomorrow's field has arrived.19+- **If slate is down**, push from any machine with the engine built:20+ `MOSAIC_PUSH_TOKEN=... deno task push-day <date> --host21+ https://mosaic-daily.fly.dev --rings all --jobs N --band 24` (a few22+ minutes on the Mac). The paid alternative is a Fly scheduled machine23+ with dedicated CPU running the same command from the app's image24+ (`fly machine run registry.fly.io/<image> -a mosaic-daily --region dfw25+ --vm-size performance-4x --schedule daily --restart no -e26+ MOSAIC_PUSH_TOKEN=... --shell --command "cd /app/site && deno task27+ push-day --tomorrow --host https://mosaic-daily.fly.dev --rings all28+ --jobs 4 --band 24"`; ~50 min and ~$0.15 per run, measured 2026-08-27).29+30+Rollover uses the pushed field, else the emergency field. The token is31+the `MOSAIC_PUSH_TOKEN` Fly secret; the same value is the Woodpecker32+secret.4433 4534 ## First time4635 4736 ```48-fly launch --no-deploy --copy-config --name mosaic-daily --region ams49-fly volumes create mosaic_data --region ams --size 137+fly launch --no-deploy --copy-config --name mosaic-daily --region dfw38+fly volumes create mosaic_data --region dfw --size 15039 fly deploy5140 ```5241
@@ -61,7 +50,7 @@ fly deploy # the app machine; the nightly builds its own engine on slat
6150 - `MOSAIC_DAILY=1` runs the daily loop in-process; `MOSAIC_DAILY_ROLLOVER_UTC`6251 (default 0) is the hour the day changes; `MOSAIC_DAILY_GROW_MINUTES` (60)6352 the growth cadence; `MOSAIC_DAILY_CHUNKS`/`CHUNK_G`/`G` (3 / 60 / 1000) the64- field; `MOSAIC_DAILY_RINGS` (`all`) how many rings are generated ahead;53+ emergency field's shape (the pushed field carries its own);6554 `MOSAIC_DAILY_NAMESPACE` changes every day's seed.6655 - `MOSAIC_PUBLIC=1` serves `/mcp` for any Host; `MOSAIC_PUBLIC_URL` is the6756 origin agents are told to use.
@@ -69,13 +58,14 @@ fly deploy # the app machine; the nightly builds its own engine on slat
6958 7059 ## Checks7160 72-- `fly logs` shows `{"daily": "started day-YYYY-MM-DD ..."}` on boot and73- `prepared <date>` once tomorrow's field is generated.61+- `fly logs` shows `{"daily": "started day-YYYY-MM-DD ..."}` on boot;62+ `curl https://mosaic-daily.fly.dev/status` shows whether tomorrow's63+ field has been pushed.7464 - `curl https://mosaic-daily.fly.dev/leaderboard/<date>` returns both boards.7565 - A restart (`fly machines restart`) resumes today's game from its log at7666 the same revision (proven 2026-08-27: revision 179 before, 179 after,7767 play continued).78-- Live: https://mosaic-daily.fly.dev/ (ams, shared-cpu-2x, 2 GB, 1 GB68+- Live: https://mosaic-daily.fly.dev/ (dfw, shared-cpu-2x, 2 GB, 1 GB7969 volume; about $11/month at Fly's list prices).8070 8171 ## Playing against it
M site/scripts/solve.ts +1 -1