feat(levels): rename the "smart" animal tile to "acrobatic" (#48)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
84cceb3
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(levels): rename a tile and drop the lion target on the animal board (#47)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
0f9f5dc
Swap the "flying" tile for "smart" and remove "lion" from the animal
board's targets/labels (now 9). Sync the CLAUDE.md target count.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(levels): add the countries board with the column-shuffle mechanic (#44)
Ben Blaker
Unverified signature
3663d53
## What & why
Board four — **`countries`** (`order=3`, reached after the subjects board). The
judge reads which country a sentence sounds like: USA, Russia, Italy, France,
India, China, Korea, England.
### New mechanic — the `shift` / column-shuffle tile
The user's `[swap]` tile, named **`shift`** in code so it doesn't collide with
the existing off-edge board-navigation *swap tiles*.
- Shift tiles render as hand-drawn keycaps stamped with **two stacked accent ⇨
arrows** (wordless, dashed "press me" rim).
- The top row is all structural: a blank `start` in the middle flanked by four
shift tiles. Hopping onto one **rotates every row UNDER the top row one column
to the right**; the rightmost cap of each row **wraps around to become the
leftmost**, arcing up-and-over the caps that slide in beneath it. The top row
never moves.
- Landing on a shift tile costs one budget hop and appends no word (consistent
with `wings` / every land, and routed through #42's new `settleIdle()` …
feat(portal): spinning-spiral teleport tiles on the animal board (#42)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
8d3ebb8
Three spinning ink-spiral portals on the animal board (board 2), each linking to
the next clockwise (top→right→bottom→top). Hopping onto one shrinks the doodle
into the source spiral and expands it out of the destination. "portal" is a new
reserved tile word (wordless, never reaches the judge); the landing hop costs
budget. Spirals spin in the render loop (mesh spin, no texture redraw).
Rebased onto #41 (the wings board): #41 reserved the word "wings", which collided
with the literal "wings" the layout placed at animal (3,2) — renamed that cell to
"winged" so the animal board stays portal-only.
Verified in-browser (Playwright): all three clockwise links walked live, spirals
spinning, shrink-in/expand-out teleport, "winged" renders as a normal word, 0
console errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(levels): add the subjects board with the wings flight mechanic (#41)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
45107cc
Board three (subjects, order=2), reached after the animal board. The judge
reads which academic subject a sentence sounds like (maths, science,
philosophy, economics, literature, languages, art, music).
New mechanic — wings:
- `[wings]` tiles render as hand-drawn accent wing keycaps (wordless, like
start/⏎).
- Hopping onto one lifts the doodle into the air: it sprouts wings (new "fly"
pose) and rises to cruise altitude, wings slowly flapping on their own timer
(independent of the idle "boil", so they keep beating mid-hop).
- The next two hops glide FREE — airborne, no word appended, no budget spent.
- The third hop descends "down from the sky" and lands for real, e.g. onto a
⏎ to press submit. Wings are a clean exit: lock in the sentence you've
written, then soar over the rest of the keys to the button without typing
them.
Flight state resets on board change, death, fresh attempt, and swap-tile
transitions. Adds takeoff (soar) and wing-beat (flap) sounds.
Verified locall…
feat(emotion): replace sarcastic and surprised targets with scared (#39)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
9cebd73
Drop the "sarcastic" and "surprised" emotion targets/labels from the
home board and add a single "scared" target in their place, taking the
list from 8 targets to 7. The grid's "yikes"/"hurt"/"disturb"/"angry"
words make a scared read reachable.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(emotion): restore the "!" punctuation tile at (1,3)
Swap the right-hand "little" back to "!" so the feelings board keeps a
punctuation tile that attaches to words (e.g. "great!"). start (1,1) and
the ⏎ submit (2,3) are unchanged, so the parity that keeps two-word reads
reachable still holds.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(emotion): reshape the feelings board and expand to 8 targets (#36)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
3402747
Redraw the home board's grid (slowly→yikes, the old "!" tile → little) and
grow the collectible set from 3 to 8 emotions: happy, elated, irked, sarcastic,
distraught, furious, betrayed, surprised. The judge's candidate labels track
the targets.
Update the solution-path tests that referenced the removed "!" tile and refresh
the judge live-eval's board sentences for the same reason. All level invariants
still hold — grid rectangular, start blank, every target scoreable, solvable
within budget.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(judge): simplify prompt to a direct similarity instruction
Replace the few-shot "<sentence> is the same as" -> "<label>" framing with
a single system instruction: name the targets and ask for the one most
similar to the player's message, sent the bare sentence as the user turn.
Drops the per-board few-shot examples and the Level.examples field along
with all the "is the same as" suffix plumbing.
The Modal endpoint is prompt-agnostic (it scores whatever messages arrive),
so no redeploy is needed for this to take effect in the app.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(levels): retheme the home board to happy/betrayed/surprised (#23)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
aa58329
Swap the home board's targets from happy/sad/angry/scared to
happy/betrayed/surprised, change the (1,0) tile from "apple" to "yikes",
and recalibrate the judge few-shot examples to the new labels
(hurt→betrayed, yikes→surprised). The start tile and ⏎ submit tile keep
their positions, so grid parity is unchanged.
The live board-sentence eval was coupled to the old labels and stripped
the few-shot examples to measure zero-shot discrimination — which never
surfaces "betrayed"/"surprised" as a first answer token. Rework it to
mirror the actual in-game judge() call (passing level.examples), which is
the discrimination players really get and what makes those labels
reachable.
Verified: full suite green (30 passed incl. live HF eval); rendered the
board in-app and confirmed grid + target chips match.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(ui): restore the ⏎ submit tile and a solid start outline (#20)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
48c6837
Revert the submit tile's label from "=" back to "⏎" (the carriage-return
glyph) across the level grids, board engine, tests, and docs. The judge
prompt is "<sentence> is the same as" now, so the tile is a purely visual
marker — the rename touches no scoring logic.
Also drop the dashed rim on the start tile so it reads as a normal solid
keycap; only the submit tile keeps its dashed "press me" cue.
23 fast tests pass; verified both boards render in-browser (fake judge).
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(judge): enrich prompt with targets and per-level few-shot examples (#17)
Ben Blaker+1: Claude Opus 4.8 (1M context)
Unverified signature
d39c87a
Reshape the judge prompt that scores a board sentence against the level's
candidate labels:
- Add a system message listing the still-needed `targets`, threaded through
judge() -> score_labels().
- Replace the bare "=" in the user message with "is the same as", reading as
an equivalence while still ending right where the answer token is predicted
(constrained-decoding logprob mechanism unchanged).
- New optional `Level.examples` field: per-level (sentence, label) pairs
expanded into few-shot user/assistant turns to calibrate the judge. Kept
server-side only (not in client_value) so they don't spoil the puzzle.
emotion and animal boards each get four board-vocabulary examples.
Update the check_logprobs diagnostic to mirror the new prompt shape, and the
live eval test to pass all labels as unbiased targets. All non-slow tests pass.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
refactor(judge): simplify to a "<sentence> =" prompt; rename ⏎ tile to =
The judge prompt is now just the assembled sentence followed by " =", and the
board's candidate labels alone constrain the answer (emotions on one board,
animals on another). The submit tile changes from ⏎ to = so the tile you hop
onto is literally the prompt's operator.
- levels: Level drops category/few_shot/question, keeping grid + labels. Grids
use "=" for submit tiles.
- judge: score_labels(sentence, labels) sends one message, no examples.
- engine / tests / README / logprobs probe: ⏎ -> =.
23 fast tests pass; verified both boards submit onto "=" in-browser (fake judge).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat(levels): modular drop-in level API + animal board
Make adding a board a one-file drop-in, and ship a second level.
- levels/ package: each board is a self-contained Level (grid + its own judge
spec — category, few-shot, labels), auto-discovered from its file; order/home
set play order and the boot board. Replaces the single level.py.
- judge is now category-agnostic: score_labels takes a Level, and judge()
resolves the board from the level_id the client sends (labels/few-shot stay
server-side).
- engine: empty "" tiles are walkable but wordless; board build factored into
buildTiles/buildTargets/loadLevel so boards swap client-side; camera reframes
to board size; checked targets persist per board.
- nav: hand-drawn ink arrows walk between boards — "next" unlocks after the
first target is collected, "back" returns to the previous board.
- animal board: 5x5, central start, four ⏎ corners, walkable blanks, ten
animals to collect.
25 fast tests pass; verified both boards in-browser with the offline judge.
Co-Authored-By:…