import { layout } from './layout.js'; const isRuleEnabled = (game: any, rule: string): boolean => { return rule in game.rules && game.rules[rule].enabled; }; const getValidRoads = (game: any, color: string): number[] => { const limits: number[] = []; /* For each road, if the road is set, skip it. * If no color is set, check the two corners. If the corner * has a matching color, add this to the set. Otherwise skip. */ layout.roads.forEach((road, roadIndex) => { if (!game.placements || !game.placements.roads || game.placements.roads[roadIndex]?.color) { return; } let valid = false; for (let c = 0; !valid && c < road.corners.length; c++) { const cornerIndex = road.corners[c] as number; if (cornerIndex == null || (layout as any).corners[cornerIndex] == null) { continue; } const corner = (layout as any).corners[cornerIndex]; const cornerColor = (game as any).placements && (game as any).placements.corners && (game as any).placements.corners[cornerIndex] && (game as any).placements.corners[cornerIndex].color; /* Roads do not pass through other player's settlements */ if (cornerColor && cornerColor !== color) { continue; } for (let r = 0; !valid && r < (corner.roads || []).length; r++) { /* This side of the corner is pointing to the road being validated. Skip it. */ if (!corner.roads || corner.roads[r] === roadIndex) { continue; } const rr = corner.roads[r]; if (rr == null) { continue; } const placementsRoads = (game as any).placements && (game as any).placements.roads; if (placementsRoads && placementsRoads[rr] && placementsRoads[rr].color === color) { valid = true; } } } if (valid) { limits.push(roadIndex); } }); return limits; } const getValidCorners = (game: any, color: string, type?: string): number[] => { const limits: number[] = []; /* For each corner, if the corner already has a color set, skip it if type * isn't set. If type is set, if it is a match, and the color is a match, * add it to the list. * * If we are limiting based on active player, a corner is only valid * if it connects to a road that is owned by that player. * * If no color is set, walk each road that leaves that corner and * check to see if there is a settlement placed at the end of that road * * If so, this location cannot have a settlement. * * If still valid, and we are in initial settlement placement, and if * Volcano is enabled, verify the tile is not the Volcano. */ layout.corners.forEach((corner, cornerIndex) => { const placement = game.placements.corners[cornerIndex]; if (type) { if (placement.color === color && placement.type === type) { limits.push(cornerIndex); } return; } if (placement.color) { return; } let valid; if (!color) { valid = true; /* Not filtering based on current player */ } else { valid = false; for (let r = 0; !valid && r < (corner.roads || []).length; r++) { const rr = corner.roads[r]; if (rr == null) { continue; } const placementsRoads = (game as any).placements && (game as any).placements.roads; valid = !!(placementsRoads && placementsRoads[rr] && placementsRoads[rr].color === color); } } for (let r = 0; valid && r < (corner.roads || []).length; r++) { if (!corner.roads) { break; } const ridx = corner.roads[r] as number; if (ridx == null || (layout as any).roads[ridx] == null) { continue; } const road = (layout as any).roads[ridx]; for (let c = 0; valid && c < (road.corners || []).length; c++) { /* This side of the road is pointing to the corner being validated. * Skip it. */ if (road.corners[c] === cornerIndex) { continue; } /* 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 as any).placements && (game as any).placements.corners && (game as any).placements.corners[cc] && (game as any).placements.corners[cc].color) { valid = false; } } } if (valid) { /* During initial placement, if volcano is enabled, do not allow * placement on a corner connected to the volcano (robber starts * on the volcano) */ if (!(game.state === 'initial-placement' && isRuleEnabled(game, 'volcano') && (layout as any).tiles && (layout as any).tiles[(game as any).robber] && Array.isArray((layout as any).tiles[(game as any).robber].corners) && (layout as any).tiles[(game as any).robber].corners.indexOf(cornerIndex as number) !== -1 )) { limits.push(cornerIndex); } } }); return limits; } export { getValidCorners, getValidRoads, isRuleEnabled };