# 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) ``` ## 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`)