Skip to content

game-board WC: sticky win state leaks across games on reused instances (splash) #49

Description

@cablehead

Summary

<game-board> tracks win state (hasWon, movesSinceWin) and lastAppliedJson as sticky per-instance fields with no game-identity key. When a single element instance is reused across different games, that state leaks between games and mislabels the endgame badge.

Where it bites

The splash is one <game-board id="splash-board"> whose splashState signal is overwritten with different games as it cycles/seeks through all_states (serve.nu /sse/splash/:tabId + seed at the page route). The same element instance receives snapshots from many distinct games over its lifetime.

  • Once any splashed game crosses 2048, hasWon latches true for every subsequent game on that element. A later game that never won still renders the win path — "game over" + "you win!" instead of "you lost" (#applyBadge: showWin = over ? this.hasWon : ..., showLost = over && !this.hasWon).
  • movesSinceWin ticks on every distinct snapshot, so across game boundaries the "hide the win badge after 3 post-win moves" rule is meaningless.

State only resets when a fresh element is created (page reload).

/play and the /my/games / /by/<id> cards are unaffected — there it's one element ↔ one game. /watch would be exposed if it ever swaps games on a single element.

Root cause

game-board.js dedupes purely on JSON equality (lastAppliedJson) and has no notion of "this is now a different game," so #apply can't know to reset hasWon / movesSinceWin.

Suggested fix

Carry a game identity in the wire state (e.g. gameId) and reset hasWon, movesSinceWin, and lastAppliedJson in #apply when it changes — or expose a game-id attribute the splash sets per seek and reset in attributeChangedCallback.

Secondary (minor)

movesSinceWin++ keys off "distinct JSON," not "a move." Any non-move re-render (e.g. a playedMs-only change) would tick it. Latent today because playedMs only changes on a move, but the counter is coupled to serialization rather than to game events.


File: examples/2048/static/game-board.js (#tickWinCounter, #applyBadge, attributeChangedCallback).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions