diff --git a/server/routes/games.ts b/server/routes/games.ts index 3e0b36f..90d7268 100755 --- a/server/routes/games.ts +++ b/server/routes/games.ts @@ -353,8 +353,13 @@ const distributeResources = (game: Game, roll: number): void => { }); const receives: Received = {} as Received; + /* Initialize all fields of 'receives' to zero so we can safely increment any + * matched tile */ PLAYER_COLORS.forEach((color) => { - receives[color] = { wood: 0, brick: 0, sheep: 0, wheat: 0, stone: 0, desert: 0 }; + receives[color] = {} as ResourceCount; + RESOURCE_TYPES.forEach((type) => { + receives[color]![type] = 0; + }); }); /* Find which corners are on each tile */ @@ -381,7 +386,11 @@ const distributeResources = (game: Game, roll: number): void => { const count = active.type === "settlement" ? 1 : 2; if (!tile.robber) { if (resource.type) { - receives[active.color][resource.type]! += count; + try { + receives[active.color][resource.type]! += count; + } catch (e) { + console.error("Error incrementing resources", { receives, active, resource, count }); + } } } else { const victim = game.players[active.color]; @@ -779,7 +788,9 @@ const loadGame = async (id: string): Promise => { // Check if we need to place a settlement (no action or place-settlement action) if (!game.turn.actions || game.turn.actions.length === 0 || game.turn.actions.indexOf("place-settlement") !== -1) { - setForSettlementPlacement(game, getValidCorners(game, currentColor)); + // During initial placement, settlements may be placed on any valid corner + // (they are not constrained by existing roads), so do not filter by color. + setForSettlementPlacement(game, getValidCorners(game)); console.log( `${info}: Set turn limits for settlement placement (${game.turn.limits?.corners?.length || 0} valid corners)` ); @@ -2820,7 +2831,9 @@ const placeRoad = (game: Game, session: Session, index: number): string | undefi name: game.players[nextColor].name, color: nextColor, } as unknown as Turn; - setForSettlementPlacement(game, getValidCorners(game, nextColor)); + // During initial placement forward pass, allow any valid corner + // (not restricted by the player's existing roads). + setForSettlementPlacement(game, getValidCorners(game)); addChatMessage(game, null, `It is ${game.turn.name}'s turn to place a settlement.`); } } diff --git a/server/routes/games/types.ts b/server/routes/games/types.ts index fd0f76f..b8573aa 100644 --- a/server/routes/games/types.ts +++ b/server/routes/games/types.ts @@ -105,7 +105,7 @@ export interface PersistentSessionData { } export type PlayerColor = "R" | "B" | "O" | "W" | "robber" | "unassigned"; -export const PLAYER_COLORS = ["R", "B", "O", "W", "robber"] as PlayerColor[]; +export const PLAYER_COLORS: PlayerColor[] = ["R", "B", "O", "W", "robber", "unassigned"]; /** * Runtime Session type = Persistent + Transient diff --git a/server/util/validLocations.ts b/server/util/validLocations.ts index 6edf034..5946b42 100644 --- a/server/util/validLocations.ts +++ b/server/util/validLocations.ts @@ -95,12 +95,16 @@ const getValidCorners = (game: Game, color: PlayerColor = "unassigned", type?: C // If the corner has a color set and it's not the explicit sentinel // "unassigned" then it's occupied and should be skipped. - if (placement.color !== "unassigned") { + // Note: placement.color may be undefined (initial state), treat that + // the same as unassigned. + if (placement.color && placement.color !== "unassigned") { return; } let valid; - if (!color) { + // Treat either a falsy color (""/undefined) or the explicit sentinel + // "unassigned" as meaning "do not filter by player". + if (!color || color === "unassigned") { valid = true; /* Not filtering based on current player */ } else { valid = false; @@ -132,7 +136,8 @@ const getValidCorners = (game: Game, color: PlayerColor = "unassigned", type?: C /* There is a settlement within one segment from this * corner, so it is invalid for settlement placement */ const cc = road.corners[c] as number; - if (game.placements.corners[cc]!.color !== "unassigned") { + const ccColor = game.placements.corners[cc]!.color; + if (ccColor && ccColor !== "unassigned") { valid = false; } }