8df4859696
Adds public/boardRender.js: a layout algorithm that derives every space's position purely from shared/board.js (column = longest-path distance from Start, lane = branch offset that fans out at a choice space and re-centers wherever branches rejoin), so it keeps working unmodified as the board data grows — no hand-placed coordinates to maintain. Renders spaces as styled SVG nodes (color/shape by type), connects them with curved path lines, and animates player tokens between positions. Pending choices glow and are clickable directly on the board, in addition to the existing text buttons. Game view widens the card on desktop for the board and scrolls horizontally on narrow viewports. Also repicks the player color palette (server/index.js) to avoid the board's own semantic colors (green/gold/red), after a token nearly disappeared into the same-colored Start space during visual testing. Verified with a headless-browser run (Playwright, off-screen — not a desktop screenshot): two players through lobby -> live join update -> board -> a fork choice, no console errors, reducer tests still green.
85 lines
3.4 KiB
Markdown
85 lines
3.4 KiB
Markdown
# Life Journey — Phase 1
|
|
|
|
An async multiplayer, Game-of-Life-style board game. Phase 0 proved the
|
|
deployment path (Docker → Nginx Proxy Manager → HTTPS → WebSocket). Phase 1
|
|
adds the actual game: a pure shared reducer, SQLite persistence, and
|
|
rooms with shareable invite links, so a few people can play together across
|
|
devices and tab closes.
|
|
|
|
The server is the sole authority over game state. It generates the only
|
|
source of randomness (the dice roll) and applies it through the exact same
|
|
reducer (`shared/game.js`) the browser imports — client and server can never
|
|
disagree about the rules.
|
|
|
|
## What's here
|
|
|
|
```
|
|
lifegame/
|
|
├── docker-compose.yml
|
|
├── Dockerfile
|
|
├── package.json / package-lock.json
|
|
├── shared/
|
|
│ ├── board.js # the Phase 1 board graph + movement helper
|
|
│ ├── game.js # pure reducer: reduce(state, action) -> newState
|
|
│ └── game.test.js # node --test coverage of the whole rules engine
|
|
├── server/
|
|
│ ├── index.js # REST + WebSocket, static hosting
|
|
│ ├── rooms.js # in-memory room registry, applies/broadcasts actions
|
|
│ ├── db.js # SQLite schema + data access (games/players/tokens)
|
|
│ └── ids.js # id/token/join-code generation
|
|
└── public/
|
|
├── index.html # lobby / waiting room / game UI
|
|
├── client.js # REST wrappers + NetworkTransport (WebSocket)
|
|
└── boardRender.js # SVG board, laid out from board.js graph data
|
|
```
|
|
|
|
## Running locally
|
|
|
|
```bash
|
|
npm install
|
|
npm test # reducer unit tests — no server needed
|
|
npm run dev # starts on :3000, creates data/lifegame.db on first game
|
|
```
|
|
|
|
Open two browser tabs at `http://localhost:3000`. Create a game in one tab,
|
|
copy the invite link, open it in the other tab, join, and start the game once
|
|
both players are in the lobby.
|
|
|
|
## The board (Phase 1 subset)
|
|
|
|
The full hand-drawn board (`assets/game_board.png`) has ~150 spaces across two
|
|
thematic passes (Career, Education, Gap Year, Relationship/Family,
|
|
Investment, High Risk). Phase 1 encodes a small subset with the same shape —
|
|
a Career-vs-Education fork, a Relationship-vs-Investment fork, a
|
|
High-Risk-vs-Safe fork, converging to Finish — enough to prove the reducer,
|
|
persistence, and rooms all work end to end. More spaces can be inserted into
|
|
any branch later without touching the reducer or database schema.
|
|
|
|
## Deploying on the homelab
|
|
|
|
Same as Phase 0 — see `homelab-config.md` for the full infrastructure
|
|
reference. Set `PUBLIC_URL` in `.env` (or the compose environment) to your
|
|
public domain so invite links generated by the server are shareable rather
|
|
than pointing at an internal address:
|
|
|
|
```bash
|
|
cd ~/homelab/lifegame
|
|
docker compose up -d --build
|
|
docker compose logs -f # expect: "Life Journey (Phase 1) listening on :3000"
|
|
```
|
|
|
|
## Data & backups
|
|
|
|
The `lifegame-data` volume (mounted at `/app/data`) holds `lifegame.db` —
|
|
every game, player, and token. Back it up like your other self-hosted data;
|
|
losing it loses every in-progress and finished game.
|
|
|
|
## What's next
|
|
|
|
- Expand `shared/board.js` toward the full sketched board
|
|
- Richer board rendering (the current UI is a functional list view, not the
|
|
illustrated board)
|
|
- Tighter reconnection/presence handling (who's online right now, not just
|
|
who's joined)
|
|
- Admin page (`ADMIN_PASSWORD`, already stubbed in `.env.example`)
|