hanabi: engine binding and UI, replacing the spike #4
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "hanabi/engine-binding"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The spike answered its question and is gone. What replaces it is the binding layer — spawning card objects from the committed spritesheet, laying hands out in front of each seat, hiding every hand from the person holding it, turning button clicks into
rules.ttsluacalls, and surviving a reload. No rule is reimplemented here.Design calls
Cards sit on the table, not in hand zones. A hand zone takes ownership of layout and reorders cards, and slot order is exactly what a clue refers to ("your second and fourth are red"), so it has to stay under the script control. Cards spawn locked: a player who can pick up their own card can look at it, and then there is no game.
Rendering is total and idempotent, not incremental — every position recomputed from state on every action. The incremental version would be faster and would drift, and a table that disagrees with the rules state is worse than a slow one. Sixty cards maximum; nothing to optimise for.
Illegal moves are reported privately, since they are nearly always misclicks. Legal moves are broadcast, because in Hanabi every action is public information.
Bug fixed before it could ship
The card-object map was keyed by integer card id.
JSON.encodeturns a sparse integer-keyed table into a JSON object, and JSON keys are strings — so every key went from7to"7"on reload, every lookup missed, and the next render would have spawned a second full set of cards on top of the first. Only ever visible after a reload. Keys are stringified on both sides now.Verification
A headless harness stubs enough of the TTS API to load the bundle and drive it through the same entry points a player uses, then plays complete games through the buttons. 300 seeded deals, ~25 assertions each, zero failures. Covers the save/load round trip, that every hand card ends up hidden from exactly one colour, that off-turn and empty-clue attempts are refused privately without spending a token, and that a new game clears the old one off the table. Rules verified separately at 945 assertions.
What this does not cover
Geometry. The numbers in
LAYOUTare reasoned, not observed, and nothing here has been run in TTS. That needs a real table and is the next step.The spike answered its question and is gone. What replaces it is the binding layer: spawning card objects from the committed spritesheet, laying hands out in front of each seat, hiding every hand from the person holding it, turning button clicks into rules.ttslua calls, and surviving a reload. No rule is reimplemented here -- rules.ttslua remains the only thing that decides whether a move is legal. Cards sit on the table in front of each seat rather than inside the hand zones. A hand zone takes ownership of layout and will reorder cards, and slot order is exactly what a clue refers to ("your second and fourth are red"), so it has to stay under the script's control. Cards spawn locked, because a player who can pick up their own card can simply look at it, and at that point there is no game left to play. Rendering is total and idempotent rather than incremental: every position is recomputed from state on every action. The incremental version would be faster and would drift, and a table that disagrees with the rules state is worse than a slow one. Hanabi has at most sixty cards, so there is nothing to optimise for. Illegal moves are reported privately to whoever attempted them, because they are nearly always misclicks and broadcasting them is noise. Legal moves are broadcast, because in Hanabi every action is public information and the table is entitled to see it. Fixes a bug that would only ever have shown up after a reload: the card-object map was keyed by integer card id, and JSON.encode turns a sparse integer-keyed table into a JSON object, whose keys are strings. Every key went from 7 to "7" on load, every lookup missed, and the next render would have spawned a second full set of cards on top of the first. Keys are stringified on both sides now. Verified headlessly. A harness stubs enough of the TTS API to load the bundle and drive it through the same entry points a player uses, then plays complete games through the buttons: 300 seeded deals, ~25 assertions each, zero failures. It covers the save/load round trip, that every hand card ends up hidden from exactly one colour, that off-turn and empty-clue attempts are refused privately without spending a token, and that starting a new game clears the old one off the table. Rules verified separately at 945 assertions. What it cannot cover is geometry. The numbers in LAYOUT are reasoned, not observed, and nothing in this commit has been run in TTS. That is the next step and it needs a real table. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>