Rebuild the board and reducer around the real content

shared/board.js: rebuilt from shared/tileInventory.js instead of the
sketch-inspired placeholder — one chain() per segment (same helper as
before), TEMP_SEGMENT_ORDER concatenating all 12 segments in the design
doc's own order since board-graph.json (the real segment connections/STOP
forks) doesn't exist yet — loudly commented as a placeholder to replace, not
a guess at real topology. A synthetic `finish` space is appended since no
tile in the source data has that type (win condition is itself an open
question). Movement is now one tile per turn (board.spaces[pos].next) —
no tile in the data implies a movement die, every `die` belongs to that
tile's own effect resolution, so walkForward() is gone.

shared/tileEffects.js (new): pure resolveTileEffect(space, rolls) covering
all 9 real tile types (payday, action_space, dice_space, roll_table_ref,
inline_table, cash_bonus, event, choice, stop) plus rollsNeededFor(space) so
the server knows what to pre-roll before constructing an action. Real
roll_table_ref/action_space/dice_space tiles resolve against the actual 48
placeholder tables; the two real content gaps (inline_table's 85 tiles,
payday/cash_bonus amounts) resolve against a synthesized generic banded
table per die size, every result clearly flagged `todo: true` so it's
visible in the game log, not just in docs.

shared/game.js: player state is now flat {cash, love, education, wealth,
age} fields (mirrors a roll-table effect object's shape directly). Landing
logic calls resolveTileEffect() instead of applying a single cash delta, and
only pauses for a decision when a space's `choices` array is actually
populated (true of nothing yet) — keeps the existing pending-choice
machinery intact for once board-graph.json exists, instead of needing
choice/stop tiles to block on options nobody has defined.

server/rooms.js: REQUEST_ROLL now looks up the player's next tile,
determines what it needs via rollsNeededFor(), and pre-rolls each die via
crypto.randomInt before constructing the ROLL action — same
randomness-happens-once-at-the-server-boundary pattern as before, just
covering N table rolls instead of one movement die. server/index.js:
default config now carries startingStats instead of startingCash, and
maxPlayers is 8.
This commit is contained in:
2026-07-28 10:06:59 -07:00
parent 220f70d342
commit 4d78937dca
5 changed files with 339 additions and 219 deletions
+8 -2
View File
@@ -28,10 +28,16 @@ const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, '..', 'data');
fs.mkdirSync(DATA_DIR, { recursive: true });
db.initDb(path.join(DATA_DIR, 'lifegame.db'));
const DEFAULT_CONFIG = { minPlayers: 2, maxPlayers: 6, diceSides: 6, startingCash: 0 };
const DEFAULT_CONFIG = {
minPlayers: 2,
maxPlayers: 8,
startingStats: { cash: 0, love: 0, education: 0, wealth: 0, age: 18 },
};
// Deliberately avoids the board's own semantic colors (green=start,
// marigold=finish, red=choice) so a player's token never blends into a space.
const PLAYER_COLORS = ['#2f6f9f', '#7a4fae', '#2f8f8f', '#c15fa0', '#a6752c', '#5a6b8c'];
const PLAYER_COLORS = [
'#2f6f9f', '#7a4fae', '#2f8f8f', '#c15fa0', '#a6752c', '#5a6b8c', '#4a4a9f', '#8a8a3f',
];
const AUTH_TIMEOUT_MS = 10_000;
const app = express();