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.
48 lines
3.2 KiB
Markdown
48 lines
3.2 KiB
Markdown
# 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
|
||
1. `src/main.js` mounts `App.svelte`.
|
||
2. `App.svelte` holds `view` and `levelIndex` state and renders one of three view components.
|
||
3. `MenuView` → calls `onPlay()` → App switches to `LevelSelectView`.
|
||
4. `LevelSelectView` reads completion + best-move data from `progressStore`, renders a paginated grid, calls `onSelect(i)` on click.
|
||
5. `GameView` is keyed on `levelIndex` (`{#key levelIndex}` in App) so every level change remounts it with fresh state.
|
||
6. Inside `GameView`: `parseLevel(xsb) → new BoardModel(level)`, keyboard input calls `model.tryMove()` / `model.undo()`, after each mutation `syncFromModel()` reassigns the `$state` snapshots that `Board` reads as props.
|
||
7. On win: `progressStore.recordCompletion()` + overlay with NEXT / LEVELS actions.
|
||
|
||
## Key design choices
|
||
- **Framework-agnostic core.** `level-parser.js`, `board-model.js`, `progress-store.js`, and `microban-levels.js` contain zero Svelte — they could be lifted into any other stack.
|
||
- **BoardModel as a non-reactive ref.** Svelte reactivity is driven by plain `$state` snapshot fields (`player`, `boxes`, `moves`, `won`) that `syncFromModel()` 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()` with `transition: 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.
|