Tiles now slide smoothly to new positions with 100ms CSS transitions.
New tile-tracker.js module manages tile identity across moves, enabling
Svelte to reuse DOM nodes for CSS transition animation. Input queuing
prevents moves during animation. Supports prefers-reduced-motion.
59 tests passing (10 new tile-tracker tests).
All Story 2.3 ACs already satisfied by Stories 1.2, 1.6, and 2.2.
Keep-playing mode works: flag persists across sessions, win overlay
suppressed, game over still triggers. Epic 2 done.
Game state saved after every move, restored on page load. Best score
persisted separately. New Game clears game state but preserves best
score. Schema validation prevents crashes from corrupted localStorage.
49 tests passing (11 new storage tests).
Epic 1 code review passed with 0 patches needed — all 6 stories
marked done. 6 deferred items logged for Epics 2-5. Created story
2-1-best-score-tracking as ready-for-dev to begin Epic 2.
ScoreBoard shows current/best score. Keyboard arrow keys control
gameplay via input-handler module. GameMessage overlay shows win/game
over states with Keep Going, Try Again, and New Game actions.
Grid.svelte renders 4x4 board with empty cell placeholders.
Tile.svelte renders colored numbered tiles with absolute positioning
and ARIA roles. App.svelte owns game state via Svelte 5 $state rune.
Pure JS game engine with initGame, move, canMove, isGameOver.
Implements leading-edge merge order, once-per-move rule, 90/10 tile
spawning, win detection (2048), and immutable state updates.
Initialize project with Svelte 5, Vite 8, Tailwind CSS v4, and Vitest.
Set up folder structure (src/lib, src/components, public/fonts),
constants module with game values and 12-tier tile colors, Clear Sans
font, and page background styling.
Add deploy.yml workflow that runs tests, builds, and deploys to
GitHub Pages on push to main. Add vite.config.js with base path
for subdirectory hosting. Include deployment spec.
The floor was centered on CANVAS_WIDTH/2 but the container is no longer
centered in the canvas (NEXT panel on right). This created a 26px gap
at the bottom-left corner. Center floor on CONTAINER_X + CONTAINER_WIDTH/2
to match the walls. Also display fruit tier numbers instead of names.
Derive canvas width from container + panel dimensions so the NEXT panel
fits with consistent padding. Extract drawFruitCircle helper so all
fruit rendering (in-game, cursor preview, NEXT panel) shows the name
label consistently. Update color palette for maximum visual distinction
between similar fruits (cherry/strawberry/apple, dekopon/persimmon).
Center score text over the container instead of the canvas.
Fix fruits falling through the floor by replacing single-step physics
with fixed-timestep sub-stepping (16.67ms steps, max 5 per frame).
Add escaped-body cleanup as safety net. Tune physics constants for
better feel (higher density, more bounce, less float).
Update fruit radii to match the community-standard ratios (TomboFry/
moonfloof clones) scaled to our 400px container. Update colors to
match real fruit appearances. Fix "Grapes" → "Grape" per wiki.
Add CLAUDE.md with architecture overview and dev commands for future
Claude Code sessions. Update README with tech stack, project structure,
and how-to-play instructions. Add spec for fixing collision physics
tuning issues.
Browser-based physics puzzle game where players drop fruits that merge
into larger fruits on collision, using Matter.js for 2D physics and
Canvas2D for rendering. Includes 11-fruit progression chain, scoring,
game-over detection, mouse/touch input, and Vitest test suite.
Phaser's game.destroy() calls shutdown() on scenes, not destroy().
The old destroy() override was never called, causing stale event
listeners to persist and throw errors in React StrictMode.