mirror of
https://github.com/tiennm99/sokoban.git
synced 2026-05-23 10:25:19 +00:00
8a3d4b4a9d
Replace Phaser 3 with Svelte 5 as the rendering and UI layer. The framework-agnostic core (level parser, board model, progress store, microban level data) moves from src/game/core → src/lib/core with zero code changes. Scenes and the hand-rolled button factory are gone; in their place: - src/App.svelte root router (menu / levels / game) - src/views/MenuView title + play + progress + hints - src/views/LevelSelectView paginated 5x4 grid with native <button>s - src/views/GameView owns BoardModel, handles input, HUD, win - src/views/Board purely presentational DOM renderer - src/views/AppButton shared themed wrapper for native <button> - src/app.css Nord palette ported to CSS variables GameView uses a non-reactive BoardModel ref and syncs plain snapshot fields (player, boxes, moves, won) into $state after every mutation — Board consumes only plain props, so Svelte reactivity stays predictable and the core class stays framework-agnostic. GameView is keyed on levelIndex in App, so changing level remounts with fresh state. Native <button> everywhere kills the click-hitbox class of bugs. Animations are now CSS transform transitions (110ms) instead of tweens. Bundle shrinks from ~1.5 MB Phaser to ~65 kB JS / 23 kB gzipped — about 60x smaller. Removed: phaser, terser, src/game, log.js (analytics ping), phasermsg vite plugin, manual Phaser chunks, terser config, public/style.css. Scripts simplified to dev/build. Docs updated: codebase summary, architecture, code standards, changelog, roadmap, README.
3.2 KiB
3.2 KiB
Codebase Summary
Layout
src/
├── main.js # Mounts App.svelte into #app
├── App.svelte # Root router: menu / levels / game
├── app.css # Nord palette (CSS variables) + resets
├── lib/
│ ├── core/
│ │ ├── level-parser.js # XSB text → {walls, targets, boxes, player, floors}
│ │ ├── board-model.js # Pure game state + move/undo/win logic
│ │ └── progress-store.js # localStorage persistence (completed + best moves)
│ └── data/
│ └── microban-levels.js # 155 XSB level strings (Microban, D. W. Skinner)
└── views/
├── MenuView.svelte # Title, play, progress, hints
├── LevelSelectView.svelte # Paginated 5×4 grid (20/page × 8 pages)
├── GameView.svelte # Board + HUD + win overlay + input
├── Board.svelte # Presentational DOM board (div-per-tile)
└── AppButton.svelte # Shared themed button (wraps native <button>)
public/
├── style.css # Legacy file — theme now lives in src/app.css
├── favicon.png
└── assets/ # bg.png, logo.png (unused, reserved)
Data flow
src/main.jsmountsApp.svelte.App.svelteholdsviewandlevelIndexstate and renders one of three view components.MenuView→ callsonPlay()→ App switches toLevelSelectView.LevelSelectViewreads completion + best-move data fromprogressStore, renders a paginated grid, callsonSelect(i)on click.GameViewis keyed onlevelIndex({#key levelIndex}in App) so every level change remounts it with fresh state.- Inside
GameView:parseLevel(xsb) → new BoardModel(level), keyboard input callsmodel.tryMove()/model.undo(), after each mutationsyncFromModel()reassigns the$statesnapshots thatBoardreads as props. - On win:
progressStore.recordCompletion()+ overlay with NEXT / LEVELS actions.
Key design choices
- Framework-agnostic core.
level-parser.js,board-model.js,progress-store.js, andmicroban-levels.jscontain zero Svelte — they could be lifted into any other stack. - BoardModel as a non-reactive ref. Svelte reactivity is driven by plain
$statesnapshot fields (player,boxes,moves,won) thatsyncFromModel()reassigns after every mutation. Cleaner than making the class instance itself reactive. - Board is purely presentational. Takes plain props (
walls,targets,floors,player,boxes,tileSize) so Svelte reactivity is predictable. - Animations via CSS. Box and player use
transform: translate()withtransition: transform 110ms ease. No JS animation loop. - Responsive tile sizing.
GameView.computeTileSize()picks a tile size that fits the level inside the viewport, capped at 56 px. - Scoped CSS per component. Svelte SFCs keep markup, style, and logic co-located and isolate styles to the component that owns them.
File size
Every .js / .svelte file is under the 200-LOC budget, except microban-levels.js which is pure data (155 XSB strings) and exempt.