doubletwelve 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../src/app/pipGrid.ts","../src/app/Pip.tsx","../src/app/pipLayouts.ts","../src/app/pipColors.ts","../src/app/PipPattern.tsx","../src/app/DominoHalf.tsx","../src/app/DoubleTwelve.tsx","../src/app/trainLayout.ts","../src/harness/layoutValidation.ts","../src/app/DominoTrain.tsx","../src/app/DominoHub.tsx","../src/rules/dominoSet.ts","../src/game/generateSampleTrains.ts","../src/app/MexicanTrainGame.tsx","../src/app/trainBends.ts","../src/app/viewportMath.ts","../src/app/Viewport.tsx","../src/harness/trainFixtures.ts","../src/rules/rulesConfig.ts","../src/rules/placement.ts"],"sourcesContent":["export type PipGridSize = '3x3' | '3x4' | '4x3';\n\nexport interface PipLayoutCell {\n row: number;\n col: number;\n gridSize: PipGridSize;\n top?: string;\n left?: string;\n}\n\nconst GRID_POSITIONS: Record<\n PipGridSize,\n { rows: number[]; cols: number[]; size: string }\n> = {\n '3x3': { rows: [20, 50, 80], cols: [20, 50, 80], size: '18%' },\n '3x4': { rows: [24, 50, 76], cols: [22, 40, 60, 78], size: '12%' },\n '4x3': { rows: [15, 38, 62, 85], cols: [20, 50, 80], size: '14%' },\n};\n\nexport function resolvePipPosition(cell: PipLayoutCell): {\n top: string;\n left: string;\n width: string;\n height: string;\n} {\n const grid = GRID_POSITIONS[cell.gridSize];\n\n return {\n top: cell.top ?? `${grid.rows[cell.row]}%`,\n left: cell.left ?? `${grid.cols[cell.col]}%`,\n width: grid.size,\n height: grid.size,\n };\n}\n","import { FC } from 'react';\nimport { PipGridSize, resolvePipPosition } from './pipGrid';\n\nexport interface PipProps {\n row: number;\n col: number;\n gridSize: PipGridSize;\n color: string;\n hollow?: boolean;\n top?: string;\n left?: string;\n}\n\nexport const Pip: FC<PipProps> = ({\n row,\n col,\n gridSize,\n color,\n hollow,\n top,\n left,\n}) => {\n const positionStyle = resolvePipPosition({ row, col, gridSize, top, left });\n\n return (\n <div\n data-testid=\"pip\"\n data-row={row}\n data-col={col}\n data-grid={gridSize}\n style={{\n position: 'absolute',\n backgroundColor: hollow ? 'transparent' : color,\n border: hollow ? '2px solid #888' : undefined,\n borderRadius: '50%',\n transform: 'translate(-50%, -50%)',\n boxShadow: hollow ? undefined : '1px 2px 3px rgba(0,0,0,0.3)',\n ...positionStyle,\n }}\n />\n );\n};\n\nexport default Pip;\n","import { PipLayoutCell } from './pipGrid';\n\n/** Canonical double-12 pip layouts (0–12). */\nexport const PIP_LAYOUTS: Record<number, readonly PipLayoutCell[]> = {\n 0: [],\n 1: [{ row: 1, col: 1, gridSize: '3x3' }],\n 2: [\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n ],\n 3: [\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n ],\n 4: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 5: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 6: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 7: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 8: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 1, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 1, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 9: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 1, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 1, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 10: [\n { row: 0, col: 0, gridSize: '3x4' },\n { row: 0, col: 1, gridSize: '3x4' },\n { row: 0, col: 2, gridSize: '3x4' },\n { row: 0, col: 3, gridSize: '3x4' },\n { row: 1, col: 0, gridSize: '3x4' },\n { row: 1, col: 3, gridSize: '3x4' },\n { row: 2, col: 0, gridSize: '3x4' },\n { row: 2, col: 1, gridSize: '3x4' },\n { row: 2, col: 2, gridSize: '3x4' },\n { row: 2, col: 3, gridSize: '3x4' },\n ],\n 11: [\n { row: 0, col: 0, gridSize: '4x3' },\n { row: 1, col: 0, gridSize: '4x3' },\n { row: 2, col: 0, gridSize: '4x3' },\n { row: 3, col: 0, gridSize: '4x3' },\n { row: 0, col: 1, gridSize: '4x3' },\n { row: 2, col: 1, gridSize: '4x3', top: '50%' },\n { row: 3, col: 1, gridSize: '4x3' },\n { row: 0, col: 2, gridSize: '4x3' },\n { row: 1, col: 2, gridSize: '4x3' },\n { row: 2, col: 2, gridSize: '4x3' },\n { row: 3, col: 2, gridSize: '4x3' },\n ],\n 12: [\n { row: 0, col: 0, gridSize: '4x3' },\n { row: 1, col: 0, gridSize: '4x3' },\n { row: 2, col: 0, gridSize: '4x3' },\n { row: 3, col: 0, gridSize: '4x3' },\n { row: 0, col: 1, gridSize: '4x3' },\n { row: 1, col: 1, gridSize: '4x3' },\n { row: 2, col: 1, gridSize: '4x3' },\n { row: 3, col: 1, gridSize: '4x3' },\n { row: 0, col: 2, gridSize: '4x3' },\n { row: 1, col: 2, gridSize: '4x3' },\n { row: 2, col: 2, gridSize: '4x3' },\n { row: 3, col: 2, gridSize: '4x3' },\n ],\n};\n\nexport function getPipLayout(value: number): readonly PipLayoutCell[] {\n return PIP_LAYOUTS[value] ?? [];\n}\n","export interface PipColorStyle {\n color: string;\n hollow?: boolean;\n}\n\n/** Partial map of domino values (0–12) to pip styles. */\nexport type PipColorMap = Partial<Record<number, PipColorStyle>>;\n\n/** Standard double-12 domino pip colors by value. */\nexport const DEFAULT_PIP_COLORS: PipColorMap = {\n 0: { color: 'transparent' },\n 1: { color: '#1a1a1a' },\n 2: { color: '#8B1A1A' },\n 3: { color: '#E6B800' },\n 4: { color: '#e8e8e8', hollow: true },\n 5: { color: '#2E8B57' },\n 6: { color: '#2563EB' },\n 7: { color: '#E8A87C' },\n 8: { color: '#DC2626' },\n 9: { color: '#1E3A8A' },\n 10: { color: '#EA580C' },\n 11: { color: '#166534' },\n 12: { color: '#DC2626' },\n};\n\n/** @deprecated Use DEFAULT_PIP_COLORS instead. */\nexport const PIP_COLORS = DEFAULT_PIP_COLORS;\n\n/** Merge custom overrides onto the default double-12 color set. */\nexport function mergePipColors(overrides?: PipColorMap): PipColorMap {\n return { ...DEFAULT_PIP_COLORS, ...overrides };\n}\n\n/** Resolve the pip style for a value when colored pips are enabled. */\nexport function resolvePipStyle(\n value: number,\n pipColors?: PipColorMap\n): PipColorStyle | undefined {\n if (pipColors === undefined) {\n return undefined;\n }\n\n return (\n pipColors[value] ??\n DEFAULT_PIP_COLORS[value] ?? { color: '#1a1a1a' }\n );\n}\n\n/** @deprecated Use resolvePipStyle instead. */\nexport function getPipStyle(value: number): PipColorStyle {\n return resolvePipStyle(value, DEFAULT_PIP_COLORS)!;\n}\n","import { FC } from 'react';\nimport { Pip } from './Pip';\nimport { getPipLayout } from './pipLayouts';\nimport { PipColorMap, resolvePipStyle } from './pipColors';\n\ninterface PipPatternProps {\n value: number;\n pipColor: string;\n pipColors?: PipColorMap;\n}\n\nconst pipProps = (\n value: number,\n pipColor: string,\n pipColors?: PipColorMap\n): { color: string; hollow?: boolean } => {\n const style = resolvePipStyle(value, pipColors);\n if (style) {\n return { color: style.color, hollow: style.hollow };\n }\n return { color: pipColor };\n};\n\nexport const PipPattern: FC<PipPatternProps> = ({\n value,\n pipColor,\n pipColors,\n}) => {\n const { color, hollow } = pipProps(value, pipColor, pipColors);\n const layout = getPipLayout(value);\n\n return (\n <>\n {layout.map((cell, index) => (\n <Pip\n key={index}\n row={cell.row}\n col={cell.col}\n gridSize={cell.gridSize}\n color={color}\n hollow={hollow}\n top={cell.top}\n left={cell.left}\n />\n ))}\n </>\n );\n};\n\nexport default PipPattern;\n","import { FC } from 'react';\nimport { PipPattern } from './PipPattern';\nimport { PipColorMap } from './pipColors';\n\ninterface DominoHalfProps {\n value: number;\n pipColor: string;\n pipColors?: PipColorMap;\n}\n\nexport const DominoHalf: FC<DominoHalfProps> = ({\n value,\n pipColor,\n pipColors,\n}) => {\n return (\n <div\n style={{\n width: '100%',\n height: '100%',\n position: 'relative',\n padding: '0',\n overflow: 'hidden',\n }}\n >\n <PipPattern value={value} pipColor={pipColor} pipColors={pipColors} />\n </div>\n );\n};\n\nexport default DominoHalf;\n","import { FC } from 'react';\nimport { DominoHalf } from './DominoHalf';\nimport { PipColorMap } from './pipColors';\n\nexport interface DoubleTwelveProps {\n /** Pip count on the top half (0–12). Defaults to 0 (blank). */\n value1?: number;\n /** Pip count on the bottom half (0–12). Defaults to 0 (blank). */\n value2?: number;\n width?: number;\n height?: number;\n backgroundColor?: string;\n /** Fallback pip color when pipColors is not set. */\n pipColor?: string;\n /** Per-value pip colors. Pass DEFAULT_PIP_COLORS or a custom/merged map. */\n pipColors?: PipColorMap;\n borderColor?: string;\n rotation?: number;\n}\n\nexport const DoubleTwelve: FC<DoubleTwelveProps> = ({\n value1 = 0,\n value2 = 0,\n width = 100,\n height = 200,\n backgroundColor = 'white',\n pipColor = 'black',\n pipColors,\n borderColor = 'black',\n rotation = 0,\n}) => {\n // Validate input values\n const val1 = Math.min(Math.max(value1, 0), 12);\n const val2 = Math.min(Math.max(value2, 0), 12);\n\n return (\n <div\n style={{\n width: `${width}px`,\n height: `${height}px`,\n backgroundColor,\n borderColor,\n borderWidth: '1px',\n borderStyle: 'solid',\n borderRadius: '10px',\n transform: `rotate(${rotation}deg)`,\n transformOrigin: 'center center',\n boxShadow: '0 1px 2px rgba(0,0,0,0.2)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n >\n <div\n style={{\n flex: 1,\n position: 'relative',\n borderBottomWidth: '1px',\n borderBottomStyle: 'solid',\n borderBottomColor: borderColor,\n }}\n >\n <DominoHalf value={val1} pipColor={pipColor} pipColors={pipColors} />\n </div>\n <div\n style={{\n flex: 1,\n position: 'relative',\n }}\n >\n <DominoHalf value={val2} pipColor={pipColor} pipColors={pipColors} />\n </div>\n </div>\n );\n};\n\nexport default DoubleTwelve;\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBend, TrainBranch } from '@/game/TrainData';\n\nexport const DOMINO_WIDTH = 60;\nexport const DOMINO_HEIGHT = 120;\n\n/**\n * Side-toe angles (degrees) relative to the branch direction for a chicken-foot\n * double. The 0° center toe is the straight main-line continuation and is not\n * listed here; these are the two angled toes that fan off the double's open end.\n */\nexport const CHICKEN_FOOT_TOE_ANGLES = [-45, 45] as const;\n\nexport type TrainLayoutStyle = 'offset' | 'linear';\n\nexport interface TrainLayoutEntry {\n x: number;\n y: number;\n rotation: number;\n isDouble: boolean;\n value1: number;\n value2: number;\n}\n\nexport interface ComputeTrainLayoutInput {\n startX: number;\n startY: number;\n angle: number;\n dominoes: readonly DominoValue[];\n layoutStyle: TrainLayoutStyle;\n dominoWidth?: number;\n dominoHeight?: number;\n /**\n * Distance from (startX, startY) to the center of the first tile, along the\n * train direction. Defaults to a small hub gap; chicken-foot toes pass half a\n * domino-height so the first toe tile butts against the host double's far end.\n */\n leadGap?: number;\n /**\n * Which side the offset zigzag seeds on (+1 / -1). Defaults to the natural\n * outward side for `angle`. Chicken-foot toes override this so each toe's\n * zigzag starts toward the outside of the foot, clear of the center row.\n */\n outwardSign?: number;\n /**\n * Offset mode only: index of a chicken-foot double that should act as a\n * centered hub. The double and the tile feeding into it are snapped onto the\n * train axis (perp 0) so the inbound tile reads as centered on the double and\n * the offset center toe fans out symmetrically — which lets the two angled\n * toes sit at equal, close distances on either side.\n */\n hubIndex?: number;\n /**\n * Pivots that fold this run's path into Ls, Us, or snakes. When present, the\n * run is split into straight sub-runs at each bend index and chained corner to\n * corner. Hub-centering is skipped (corners relax centering by design).\n */\n bends?: readonly TrainBend[];\n}\n\nexport function halfExtentAlongTrain(\n isDouble: boolean,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): number {\n return isDouble ? dominoWidth / 2 : dominoHeight / 2;\n}\n\nexport function stepAlongTrain(\n fromIsDouble: boolean,\n toIsDouble: boolean,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): number {\n return (\n halfExtentAlongTrain(fromIsDouble, dominoWidth, dominoHeight) +\n halfExtentAlongTrain(toIsDouble, dominoWidth, dominoHeight)\n );\n}\n\nexport function trainDirection(angle: number): { dirX: number; dirY: number } {\n const angleRad = (angle * Math.PI) / 180;\n return {\n dirX: Math.cos(angleRad),\n dirY: Math.sin(angleRad),\n };\n}\n\nexport function trainPerpendicular(angle: number): { perpX: number; perpY: number } {\n const { dirX, dirY } = trainDirection(angle);\n return { perpX: -dirY, perpY: dirX };\n}\n\n/**\n * Orients a value chain for rendering so each tile's connecting value (`value1`,\n * the near end) faces the previous tile. A tile is flipped only when it is\n * stored reversed (its `value2`, not `value1`, is the one that matches the\n * previous tile's open end). A correctly-stored chain is left untouched, and\n * doubles are never flipped. This is identical for linear and offset layouts —\n * the connection rule doesn't depend on spacing.\n */\nexport function orientDominoValues(dominoes: DominoValue[]): DominoValue[] {\n const oriented = dominoes.map((domino) => ({ ...domino }));\n\n for (let i = 1; i < oriented.length; i++) {\n const domino = oriented[i];\n const prevValue = oriented[i - 1].value2;\n const isDouble = domino.value1 === domino.value2;\n\n if (!isDouble && domino.value1 !== prevValue && domino.value2 === prevValue) {\n oriented[i] = { value1: domino.value2, value2: domino.value1 };\n }\n }\n\n return oriented;\n}\n\nexport function outwardPerpSign(angle: number): number {\n const { dirX, dirY } = trainDirection(angle);\n\n if (Math.abs(dirX) >= Math.abs(dirY)) {\n return dirX >= 0 ? 1 : -1;\n }\n\n return dirY >= 0 ? 1 : -1;\n}\n\nexport function nextPerpOffset(current: number, outwardSign: number): number {\n if (current === 0) {\n return outwardSign;\n }\n\n return current === outwardSign ? -outwardSign : outwardSign;\n}\n\ninterface PlaceOrientedRunInput {\n orientedDominoes: readonly DominoValue[];\n startX: number;\n startY: number;\n angle: number;\n layoutStyle: TrainLayoutStyle;\n dominoWidth: number;\n dominoHeight: number;\n leadGap: number;\n outwardSign: number;\n hubIndex?: number;\n}\n\n/**\n * Places an already value-oriented run of dominoes along a single straight\n * heading. This is the geometric core shared by straight runs and by each\n * sub-run of a bent (folded) path; it never re-orients tiles, so callers that\n * split a run at bends can orient the whole value-chain once and still keep\n * like-values touching across every corner.\n */\nfunction placeOrientedRun({\n orientedDominoes,\n startX,\n startY,\n angle,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign,\n hubIndex,\n}: PlaceOrientedRunInput): TrainLayoutEntry[] {\n const layout: TrainLayoutEntry[] = [];\n const { dirX, dirY } = trainDirection(angle);\n const { perpX, perpY } = trainPerpendicular(angle);\n const isHub = layoutStyle === 'offset' && hubIndex != null;\n // Lane (perpStep units) of each placed tile, used to recenter the inbound run\n // onto the hub double afterward.\n const laneByIndex: number[] = [];\n\n let currentX = startX + dirX * leadGap;\n let currentY = startY + dirY * leadGap;\n let perpOffset = 0;\n // Lane (in perpStep units) of the current tile. Regular tiles brick by\n // flipping lanes; a double stays in the lane of the tile it connects to, and\n // the tile coming out of the double stays in that lane too — so the run out\n // of a double mirrors the run into it and the train holds two fixed rows.\n let laneSign = 0;\n\n // Regular tiles alternate half a domino-width to each side of the centerline\n // so the two rows touch along the spine (no gap) and interlock cleanly.\n const perpStep = dominoWidth / 2;\n\n // perpOffset is the current net perpendicular position in units of perpStep.\n // Moving to a new lane steps by the delta.\n const setPerpOffset = (target: number) => {\n const delta = (target - perpOffset) * perpStep;\n currentX += perpX * delta;\n currentY += perpY * delta;\n perpOffset = target;\n };\n\n for (let i = 0; i < orientedDominoes.length; i++) {\n const domino = orientedDominoes[i];\n const isDouble = domino.value1 === domino.value2;\n const prevIsDouble =\n i > 0 &&\n orientedDominoes[i - 1].value1 === orientedDominoes[i - 1].value2;\n\n if (layoutStyle === 'linear') {\n if (i > 0) {\n if (isDouble) {\n currentX += dirX * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n } else if (prevIsDouble) {\n currentX += dirX * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n } else {\n currentX += dirX * dominoHeight;\n currentY += dirY * dominoHeight;\n }\n }\n } else if (isDouble) {\n // A double aligns with the tile it connects to: it stays in the current\n // lane (no perpendicular move) and only advances along the train.\n if (i > 0) {\n currentX += dirX * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n }\n } else {\n // Regular tile.\n if (i === 0) {\n laneSign = outwardSign;\n } else if (prevIsDouble) {\n // First tile out of a double: stay in the double's lane (centered on\n // it), so the outro mirrors the intro. Advance along only.\n currentX += dirX * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n } else {\n // Brick against the previous regular: flip lanes, overlap 50% along.\n currentX += dirX * (dominoHeight / 2);\n currentY += dirY * (dominoHeight / 2);\n laneSign = nextPerpOffset(laneSign, outwardSign);\n }\n\n setPerpOffset(laneSign);\n }\n\n laneByIndex.push(perpOffset);\n\n layout.push({\n x: currentX,\n y: currentY,\n rotation: isDouble ? angle + 180 : angle - 90,\n isDouble,\n value1: domino.value1,\n value2: domino.value2,\n });\n }\n\n // Center the hub double on the train axis by rigidly sliding the whole run\n // perpendicular. Because the inbound tile, the double, and the outgoing tile\n // all share the double's lane, this lands all three on the axis (each reads as\n // centered on the double) while the offset zigzag — and the no-overlap\n // guarantee of the original lattice — is preserved. The center toe still fans\n // off-axis, so the two angled toes end up symmetric and close on either side.\n if (isHub && hubIndex != null) {\n const shift = -laneByIndex[hubIndex] * perpStep;\n if (shift !== 0) {\n for (let i = 0; i < layout.length; i++) {\n layout[i] = {\n ...layout[i],\n x: layout[i].x + perpX * shift,\n y: layout[i].y + perpY * shift,\n };\n }\n }\n }\n\n return layout;\n}\n\n/**\n * Normalizes a run's bends: integer indices strictly inside the run, one per\n * index (last wins), sorted. Index 0 is dropped — a run can't bend before its\n * first tile. Returns the cleaned, sorted list.\n */\nexport function normalizeBends(\n bends: readonly TrainBend[] | undefined,\n tileCount: number\n): TrainBend[] {\n if (!bends || bends.length === 0) return [];\n const byIndex = new Map<number, number>();\n for (const bend of bends) {\n if (!Number.isInteger(bend.index)) continue;\n if (bend.index <= 0 || bend.index >= tileCount) continue;\n byIndex.set(bend.index, bend.turn);\n }\n return [...byIndex.entries()]\n .map(([index, turn]) => ({ index, turn }))\n .sort((a, b) => a.index - b.index);\n}\n\n/**\n * Local heading (degrees) of the tile at `index` in a (possibly bent) run: the\n * base `angle` plus every bend turn at or before that index. With no bends this\n * is just `angle`. Used to anchor chicken-foot toes off a double's *actual*\n * heading when the double sits in a turned section of the path.\n */\nexport function headingAtIndex(\n angle: number,\n bends: readonly TrainBend[] | undefined,\n index: number,\n tileCount = Infinity\n): number {\n const cleaned = normalizeBends(bends, Number.isFinite(tileCount) ? tileCount : index + 1);\n let heading = angle;\n for (const bend of cleaned) {\n if (bend.index <= index) heading += bend.turn;\n else break;\n }\n return heading;\n}\n\n/**\n * Lays out a run that folds at one or more bends. The whole value-chain is\n * oriented once (so like-values keep touching), then split into straight\n * sub-runs at each bend index. Each post-bend sub-run is anchored at the open\n * end of the previous sub-run's last tile and turned by the bend's angle.\n *\n * Corner handling: a perpendicular tile butted straight onto the prior tile's\n * end would overlap it by a quarter-tile, so each post-bend sub-run is nudged\n * half a tile-width along the *previous* heading. That converts the would-be\n * overlap into a clean edge/point touch at the corner while the connecting pips\n * still meet. Centering relaxes at corners (tiles bunch) — by design.\n */\nfunction placeBentRun(\n orientedDominoes: readonly DominoValue[],\n input: Required<\n Pick<\n ComputeTrainLayoutInput,\n 'startX' | 'startY' | 'angle' | 'layoutStyle' | 'dominoWidth' | 'dominoHeight' | 'leadGap' | 'outwardSign'\n >\n >,\n bends: readonly TrainBend[],\n hubIndex?: number\n): TrainLayoutEntry[] {\n const { startX, startY, angle, layoutStyle, dominoWidth, dominoHeight, leadGap, outwardSign } = input;\n const boundaries = [0, ...bends.map((b) => b.index), orientedDominoes.length];\n\n const result: TrainLayoutEntry[] = [];\n let heading = angle;\n let subStartX = startX;\n let subStartY = startY;\n let subLeadGap = leadGap;\n\n for (let s = 0; s < boundaries.length - 1; s++) {\n const slice = orientedDominoes.slice(boundaries[s], boundaries[s + 1]);\n if (slice.length === 0) continue;\n\n // Keep the hub double centered as long as it lives in the first (pre-bend)\n // sub-run: this preserves the straight-run vertical position so a bend\n // elsewhere doesn't shift the whole train and spuriously collide. Later\n // sub-runs chain off this centered run, so they follow along.\n const subHubIndex =\n s === 0 && hubIndex != null && hubIndex < boundaries[1] ? hubIndex : undefined;\n\n const sub = placeOrientedRun({\n orientedDominoes: slice,\n startX: subStartX,\n startY: subStartY,\n angle: heading,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap: subLeadGap,\n outwardSign,\n hubIndex: subHubIndex,\n });\n result.push(...sub);\n\n const isLastSub = s >= boundaries.length - 2;\n if (isLastSub) break;\n\n // Chain the next sub-run off this one's open end, turned by the bend angle.\n const last = sub[sub.length - 1];\n const prevDir = trainDirection(heading);\n const halfPrev = halfExtentAlongTrain(last.isDouble, dominoWidth, dominoHeight);\n\n heading += bends[s].turn;\n const nextFirst = orientedDominoes[boundaries[s + 1]];\n const nextIsDouble = nextFirst.value1 === nextFirst.value2;\n const halfNext = halfExtentAlongTrain(nextIsDouble, dominoWidth, dominoHeight);\n const nextDir = trainDirection(heading);\n const nextPerp = trainPerpendicular(heading);\n const perpStep = dominoWidth / 2;\n\n // Land the next sub-run's first tile so its connecting half sits edge-flush\n // against the previous tile's open half — a clean L corner where the matching\n // pips fully touch. Pull a half-width back along the previous heading (into\n // the corner) and advance an extra half-width along the new heading so the\n // turning tile clears the previous tile's body instead of overlapping it.\n const targetX =\n last.x + prevDir.dirX * (halfPrev - perpStep) + nextDir.dirX * (halfNext + perpStep);\n const targetY =\n last.y + prevDir.dirY * (halfPrev - perpStep) + nextDir.dirY * (halfNext + perpStep);\n\n // In offset mode placeOrientedRun seeds a regular first tile a half-width\n // into its outward lane; pre-cancel that so the tile lands exactly on the\n // target. Linear runs and doubles get no perpendicular seed.\n const seeds = layoutStyle === 'offset' && !nextIsDouble;\n const seedX = seeds ? nextPerp.perpX * perpStep * outwardSign : 0;\n const seedY = seeds ? nextPerp.perpY * perpStep * outwardSign : 0;\n\n subStartX = targetX - seedX;\n subStartY = targetY - seedY;\n subLeadGap = 0;\n }\n\n return result;\n}\n\nexport function computeTrainLayout({\n startX,\n startY,\n angle,\n dominoes,\n layoutStyle,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT,\n leadGap = dominoHeight * 0.3,\n outwardSign: outwardSignInput,\n hubIndex,\n bends,\n}: ComputeTrainLayoutInput): TrainLayoutEntry[] {\n const orientedDominoes = orientDominoValues([...dominoes]);\n const outwardSign = outwardSignInput ?? outwardPerpSign(angle);\n\n const cleanedBends = normalizeBends(bends, orientedDominoes.length);\n if (cleanedBends.length > 0) {\n // Hub-centering still applies to the pre-bend sub-run (so a bend doesn't\n // shift the whole train); corners past it relax centering by design.\n return placeBentRun(\n orientedDominoes,\n {\n startX,\n startY,\n angle,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign,\n },\n cleanedBends,\n hubIndex\n );\n }\n\n return placeOrientedRun({\n orientedDominoes,\n startX,\n startY,\n angle,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign,\n hubIndex,\n });\n}\n\n/** The four world-space corners of a tile (its rotated rectangle). */\nexport function tileCorners(\n entry: TrainLayoutEntry,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): Array<{ x: number; y: number }> {\n const r = (entry.rotation * Math.PI) / 180;\n const cos = Math.cos(r);\n const sin = Math.sin(r);\n const hw = dominoWidth / 2;\n const hh = dominoHeight / 2;\n return [\n [-hw, -hh],\n [hw, -hh],\n [hw, hh],\n [-hw, hh],\n ].map(([x, y]) => ({\n x: entry.x + x * cos - y * sin,\n y: entry.y + x * sin + y * cos,\n }));\n}\n\nfunction projectionGap(\n a: Array<{ x: number; y: number }>,\n b: Array<{ x: number; y: number }>,\n axis: { x: number; y: number }\n): number {\n let aMin = Infinity;\n let aMax = -Infinity;\n let bMin = Infinity;\n let bMax = -Infinity;\n for (const p of a) {\n const d = p.x * axis.x + p.y * axis.y;\n aMin = Math.min(aMin, d);\n aMax = Math.max(aMax, d);\n }\n for (const p of b) {\n const d = p.x * axis.x + p.y * axis.y;\n bMin = Math.min(bMin, d);\n bMax = Math.max(bMax, d);\n }\n return Math.min(aMax, bMax) - Math.max(aMin, bMin);\n}\n\n/**\n * True when two tiles physically overlap (separating-axis test on their rotated\n * rectangles). Tiles that merely touch (within `epsilon`) are not overlapping,\n * so legitimately adjacent dominoes — bricked, end-to-end, or butted against a\n * double — pass cleanly while real collisions are caught.\n */\nexport function tilesOverlap(\n a: TrainLayoutEntry,\n b: TrainLayoutEntry,\n epsilon = 1,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): boolean {\n const ca = tileCorners(a, dominoWidth, dominoHeight);\n const cb = tileCorners(b, dominoWidth, dominoHeight);\n for (const corners of [ca, cb]) {\n for (let i = 0; i < 4; i++) {\n const p = corners[i];\n const q = corners[(i + 1) % 4];\n const ex = q.x - p.x;\n const ey = q.y - p.y;\n const len = Math.hypot(ex, ey) || 1;\n const axis = { x: -ey / len, y: ex / len };\n if (projectionGap(ca, cb, axis) <= epsilon) {\n return false;\n }\n }\n }\n return true;\n}\n\nfunction overlapsAny(\n tile: TrainLayoutEntry,\n others: readonly TrainLayoutEntry[],\n dominoWidth: number,\n dominoHeight: number\n): boolean {\n return others.some((other) =>\n tilesOverlap(tile, other, 1, dominoWidth, dominoHeight)\n );\n}\n\n/**\n * True when any tile of `layout` overlaps any tile in `obstacles` — i.e. this\n * path would physically intersect another path. Used to forbid a bend that\n * would cross another train.\n */\nexport function layoutsCollide(\n layout: readonly TrainLayoutEntry[],\n obstacles: readonly TrainLayoutEntry[],\n epsilon = 1,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): boolean {\n return layout.some((tile) =>\n obstacles.some((other) =>\n tilesOverlap(tile, other, epsilon, dominoWidth, dominoHeight)\n )\n );\n}\n\n/**\n * True when a path crosses itself — any two of its own tiles overlap. Adjacent\n * tiles that merely touch are fine (tilesOverlap ignores contact), so this only\n * fires when a fold (e.g. a too-tight U-turn) makes the path collide with itself.\n */\nexport function layoutSelfIntersects(\n layout: readonly TrainLayoutEntry[],\n epsilon = 1,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): boolean {\n for (let i = 0; i < layout.length; i++) {\n for (let j = i + 1; j < layout.length; j++) {\n if (tilesOverlap(layout[i], layout[j], epsilon, dominoWidth, dominoHeight)) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * A single straight run of dominoes within a chicken-foot tree: the main line\n * or one toe. `depth` is 0 for the main line, 1 for its toes, and so on.\n */\nexport interface TrainSegment {\n angle: number;\n depth: number;\n layoutStyle: TrainLayoutStyle;\n /** Outward side this segment's zigzag seeds on (needed to re-derive layout). */\n outwardSign: number;\n dominoes: readonly DominoValue[];\n layout: TrainLayoutEntry[];\n /** Anchor point this segment hangs off (host double's open end), if any. */\n anchor?: { x: number; y: number };\n}\n\nexport interface ComputeTrainTreeInput {\n startX: number;\n startY: number;\n angle: number;\n branch: TrainBranch;\n layoutStyle: TrainLayoutStyle;\n dominoWidth?: number;\n dominoHeight?: number;\n leadGap?: number;\n depth?: number;\n anchor?: { x: number; y: number };\n outwardSign?: number;\n /**\n * Accumulator of every tile already placed in the tree. Toes are nudged\n * outward until they clear everything in here, so no two dominoes overlap.\n * Callers normally omit this; the recursion threads it through.\n */\n placed?: TrainLayoutEntry[];\n /**\n * Unit direction a toe may be nudged along (outward, parallel to the host\n * double's open edge) to resolve overlaps. The trunk passes none.\n */\n pushAxis?: { x: number; y: number };\n /**\n * Minimum number of nudge steps to apply before checking for clearance. Both\n * toes of a foot share this so they stay symmetric about the double even when\n * only one side is crowded by the offset center toe.\n */\n minPushSteps?: number;\n}\n\n/** Outward nudge increment and cap used to space chicken-foot toes apart. */\nconst TOE_PUSH_STEP = DOMINO_WIDTH / 4;\nconst TOE_PUSH_MAX_STEPS = 24;\n\n/**\n * Lays out a branch and, recursively, the chicken-foot side toes hanging off any\n * of its doubles. Returns a flat list of segments (main line first, then toes in\n * depth-first order) so callers can render every tile and validate each run.\n */\nexport function computeTrainTree({\n startX,\n startY,\n angle,\n branch,\n layoutStyle,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT,\n leadGap,\n depth = 0,\n anchor,\n outwardSign,\n placed = [],\n pushAxis,\n minPushSteps = 0,\n}: ComputeTrainTreeInput): TrainSegment[] {\n const segmentOutward = outwardSign ?? outwardPerpSign(angle);\n\n // The first double that sprouts a foot becomes a centered hub so its inbound\n // tile reads centered and its two angled toes stay symmetric.\n const hubIndex = branch.feet\n ? Object.keys(branch.feet)\n .map(Number)\n .filter((index) => {\n const tile = branch.dominoes[index];\n return tile && tile.value1 === tile.value2;\n })\n .sort((a, b) => a - b)[0]\n : undefined;\n\n const buildLayout = (originX: number, originY: number) =>\n computeTrainLayout({\n startX: originX,\n startY: originY,\n angle,\n dominoes: branch.dominoes,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign: segmentOutward,\n hubIndex,\n bends: branch.bends,\n });\n\n // Nudge this run outward (only toes get a pushAxis) until none of its tiles\n // overlap anything already placed, so dominoes never sit on top of each other.\n // Start from minPushSteps so a foot's two toes share a nudge and stay symmetric.\n let layout = buildLayout(\n startX + (pushAxis?.x ?? 0) * TOE_PUSH_STEP * minPushSteps,\n startY + (pushAxis?.y ?? 0) * TOE_PUSH_STEP * minPushSteps\n );\n let appliedAnchor =\n anchor && pushAxis\n ? {\n x: anchor.x + pushAxis.x * TOE_PUSH_STEP * minPushSteps,\n y: anchor.y + pushAxis.y * TOE_PUSH_STEP * minPushSteps,\n }\n : anchor;\n if (pushAxis && placed.length > 0) {\n for (let k = minPushSteps; k <= TOE_PUSH_MAX_STEPS; k++) {\n const originX = startX + pushAxis.x * TOE_PUSH_STEP * k;\n const originY = startY + pushAxis.y * TOE_PUSH_STEP * k;\n const trial = buildLayout(originX, originY);\n const clear = !trial.some((tile) =>\n overlapsAny(tile, placed, dominoWidth, dominoHeight)\n );\n if (clear || k === TOE_PUSH_MAX_STEPS) {\n layout = trial;\n appliedAnchor = anchor\n ? {\n x: anchor.x + pushAxis.x * TOE_PUSH_STEP * k,\n y: anchor.y + pushAxis.y * TOE_PUSH_STEP * k,\n }\n : anchor;\n break;\n }\n }\n }\n\n placed.push(...layout);\n\n const segments: TrainSegment[] = [\n {\n angle,\n depth,\n layoutStyle,\n outwardSign: segmentOutward,\n dominoes: branch.dominoes,\n layout,\n anchor: appliedAnchor,\n },\n ];\n\n if (branch.feet) {\n const perpStep = dominoWidth / 2;\n const leadGap = dominoHeight / 2;\n\n for (const key of Object.keys(branch.feet)) {\n const hostIndex = Number(key);\n const host = layout[hostIndex];\n const toes = branch.feet[hostIndex];\n if (!host || !host.isDouble || !toes) {\n continue;\n }\n\n // Anchor toes off the double's LOCAL heading so a double inside a bent\n // section fans its toes relative to the turned path, not the base angle.\n const hostAngle = headingAtIndex(\n angle,\n branch.bends,\n hostIndex,\n branch.dominoes.length\n );\n const { dirX, dirY } = trainDirection(hostAngle);\n const { perpX, perpY } = trainPerpendicular(hostAngle);\n\n for (let toeIndex = 0; toeIndex < toes.length; toeIndex++) {\n const toe = toes[toeIndex];\n const toeOffset = CHICKEN_FOOT_TOE_ANGLES[toeIndex] ?? 0;\n const sideSign = Math.sign(toeOffset);\n const toeAngle = hostAngle + toeOffset;\n const toePerp = trainPerpendicular(toeAngle);\n // Seed the zigzag on the toe's INNER lane so each toe splays outward\n // (away from the center toe) as it extends rather than curling in.\n const outward = -sideSign;\n\n // The double's open corner on this toe's side: half a domino-width out\n // along the train, half a domino-height across to the corner.\n const cornerX =\n host.x + dirX * (dominoWidth / 2) + perpX * (dominoHeight / 2) * sideSign;\n const cornerY =\n host.y + dirY * (dominoWidth / 2) + perpY * (dominoHeight / 2) * sideSign;\n\n // Snug placement: the first toe tile butts its inner edge midpoint\n // against that corner (its center lands at corner + toeDir * leadGap).\n // Pick the origin so the offset seed cancels and that lands exactly.\n const originX = cornerX - toePerp.perpX * outward * perpStep;\n const originY = cornerY - toePerp.perpY * outward * perpStep;\n\n segments.push(\n ...computeTrainTree({\n startX: originX,\n startY: originY,\n angle: toeAngle,\n branch: toe,\n // Toes inherit the main style so they zigzag in offset mode.\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign: outward,\n depth: depth + 1,\n anchor: { x: originX, y: originY },\n placed,\n // If the snug spot is still blocked (the offset center toe leans into\n // one side), slide this toe along the double's open edge, away from\n // center, until it clears. This keeps it butted against the double\n // while stepping past the obstacle — independent per toe, so a foot\n // ends up snug and only as asymmetric as the obstruction requires.\n pushAxis: { x: perpX * sideSign, y: perpY * sideSign },\n })\n );\n }\n }\n }\n\n return segments;\n}\n\n/** Flattens a list of segments into a single list of tiles for rendering. */\nexport function flattenSegments(\n segments: readonly TrainSegment[]\n): TrainLayoutEntry[] {\n return segments.flatMap((segment) => segment.layout);\n}\n\nexport interface TrainLayoutBounds {\n width: number;\n height: number;\n offsetX: number;\n offsetY: number;\n}\n\n/** Bounding box for rendering a train layout on a felt canvas. */\nexport function getTrainLayoutBounds(\n layout: readonly TrainLayoutEntry[],\n padding = 24,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): TrainLayoutBounds {\n const halfExtent = Math.hypot(dominoWidth, dominoHeight) / 2;\n\n if (layout.length === 0) {\n return {\n width: padding * 2 + dominoWidth,\n height: padding * 2 + dominoHeight,\n offsetX: padding,\n offsetY: padding,\n };\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const entry of layout) {\n minX = Math.min(minX, entry.x - halfExtent);\n minY = Math.min(minY, entry.y - halfExtent);\n maxX = Math.max(maxX, entry.x + halfExtent);\n maxY = Math.max(maxY, entry.y + halfExtent);\n }\n\n return {\n width: Math.ceil(maxX - minX + padding * 2),\n height: Math.ceil(maxY - minY + padding * 2),\n offsetX: padding - minX,\n offsetY: padding - minY,\n };\n}\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBranch } from '@/game/TrainData';\nimport {\n DOMINO_HEIGHT,\n DOMINO_WIDTH,\n TrainLayoutEntry,\n TrainLayoutStyle,\n TrainSegment,\n nextPerpOffset,\n outwardPerpSign,\n stepAlongTrain,\n tilesOverlap,\n trainDirection,\n trainPerpendicular,\n} from '@/app/trainLayout';\n\nexport interface LayoutValidationIssue {\n code: string;\n message: string;\n index?: number;\n}\n\nexport interface LayoutValidationResult {\n valid: boolean;\n issues: LayoutValidationIssue[];\n}\n\nconst DEFAULT_TOLERANCE = 1;\n\nexport function projectOnTrainAxis(\n dx: number,\n dy: number,\n angle: number\n): number {\n const { dirX, dirY } = trainDirection(angle);\n return dx * dirX + dy * dirY;\n}\n\nexport function projectOnPerpendicularAxis(\n dx: number,\n dy: number,\n angle: number\n): number {\n const { perpX, perpY } = trainPerpendicular(angle);\n return dx * perpX + dy * perpY;\n}\n\nexport function validateDominoChain(dominoes: readonly DominoValue[]): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n\n for (let i = 1; i < dominoes.length; i++) {\n if (dominoes[i].value1 !== dominoes[i - 1].value2) {\n issues.push({\n code: 'chain-break',\n message: `Domino ${i} does not connect to domino ${i - 1}`,\n index: i,\n });\n }\n }\n\n for (let i = 1; i < dominoes.length; i++) {\n const prevIsDouble = dominoes[i - 1].value1 === dominoes[i - 1].value2;\n const currentIsDouble = dominoes[i].value1 === dominoes[i].value2;\n if (prevIsDouble && currentIsDouble) {\n issues.push({\n code: 'consecutive-doubles',\n message: `Consecutive doubles at index ${i - 1} and ${i}`,\n index: i,\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport interface AxisPosition {\n along: number;\n perp: number;\n}\n\n/**\n * Reconstructs the expected position of every tile in train-axis space\n * (along the train and perpendicular to it), mirroring computeTrainLayout.\n * Positions are relative to the first tile, so only deltas are meaningful.\n */\nexport function expectedAxisLayout(\n layout: readonly TrainLayoutEntry[],\n layoutStyle: TrainLayoutStyle,\n outwardSign: number,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): AxisPosition[] {\n const perpStep = dominoWidth / 2;\n const isDoubleArr = layout.map((entry) => entry.isDouble);\n\n const positions: AxisPosition[] = [];\n let along = 0;\n let perp = 0;\n let laneSign = 0;\n\n for (let i = 0; i < layout.length; i++) {\n const isDouble = isDoubleArr[i];\n const prevIsDouble = i > 0 && isDoubleArr[i - 1];\n\n if (layoutStyle === 'linear') {\n if (i > 0) {\n along += stepAlongTrain(prevIsDouble, isDouble, dominoWidth, dominoHeight);\n }\n perp = 0;\n } else if (isDouble) {\n // Double stays in the current lane (aligned with the tile it connects to).\n if (i > 0) {\n along += stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n }\n // perp unchanged\n } else if (i === 0) {\n laneSign = outwardSign;\n perp = laneSign * perpStep;\n } else if (prevIsDouble) {\n // First tile out of a double stays in the double's lane (mirror).\n along += stepAlongTrain(true, false, dominoWidth, dominoHeight);\n // perp unchanged\n } else {\n along += dominoHeight / 2;\n laneSign = nextPerpOffset(laneSign, outwardSign);\n perp = laneSign * perpStep;\n }\n\n positions.push({ along, perp });\n }\n\n return positions;\n}\n\nexport function validateConsecutiveSpacing(\n layout: readonly TrainLayoutEntry[],\n angle: number,\n layoutStyle: TrainLayoutStyle,\n tolerance = DEFAULT_TOLERANCE,\n outwardSignOverride?: number\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n const outwardSign = outwardSignOverride ?? outwardPerpSign(angle);\n const expected = expectedAxisLayout(layout, layoutStyle, outwardSign);\n\n for (let i = 1; i < layout.length; i++) {\n const prev = layout[i - 1];\n const current = layout[i];\n const dx = current.x - prev.x;\n const dy = current.y - prev.y;\n const along = projectOnTrainAxis(dx, dy, angle);\n const perp = projectOnPerpendicularAxis(dx, dy, angle);\n const expectedAlong = expected[i].along - expected[i - 1].along;\n const expectedPerp = expected[i].perp - expected[i - 1].perp;\n\n if (Math.abs(along - expectedAlong) > tolerance) {\n issues.push({\n code: 'spacing-along-train',\n message: `Along-train spacing between domino ${i - 1} and ${i} is ${along.toFixed(2)}px (expected ${expectedAlong}px)`,\n index: i,\n });\n }\n\n if (Math.abs(perp - expectedPerp) > tolerance) {\n issues.push({\n code: 'spacing-perpendicular',\n message: `Perpendicular spacing between domino ${i - 1} and ${i} is ${perp.toFixed(2)}px (expected ${expectedPerp}px)`,\n index: i,\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport function validateNoPairOverlap(\n layout: readonly TrainLayoutEntry[],\n angle: number,\n layoutStyle: TrainLayoutStyle,\n tolerance = DEFAULT_TOLERANCE,\n outwardSignOverride?: number\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n const outwardSign = outwardSignOverride ?? outwardPerpSign(angle);\n const expected = expectedAxisLayout(layout, layoutStyle, outwardSign);\n\n for (let i = 1; i < layout.length; i++) {\n const prev = layout[i - 1];\n const current = layout[i];\n const dx = current.x - prev.x;\n const dy = current.y - prev.y;\n const distance = Math.hypot(dx, dy);\n const expectedAlong = expected[i].along - expected[i - 1].along;\n const expectedPerp = expected[i].perp - expected[i - 1].perp;\n const minDistance = Math.hypot(expectedAlong, expectedPerp) * 0.9;\n\n if (distance + tolerance < minDistance) {\n issues.push({\n code: 'overlap',\n message: `Domino ${i - 1} and ${i} centers are ${distance.toFixed(2)}px apart (minimum ${minDistance.toFixed(2)}px)`,\n index: i,\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport function validateTrainLayout(\n layout: readonly TrainLayoutEntry[],\n dominoes: readonly DominoValue[],\n angle: number,\n layoutStyle: TrainLayoutStyle,\n tolerance = DEFAULT_TOLERANCE,\n outwardSignOverride?: number\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [\n ...validateDominoChain(dominoes).issues,\n ...validateConsecutiveSpacing(\n layout,\n angle,\n layoutStyle,\n tolerance,\n outwardSignOverride\n ).issues,\n ...validateNoPairOverlap(\n layout,\n angle,\n layoutStyle,\n tolerance,\n outwardSignOverride\n ).issues,\n ];\n\n if (layout.length !== dominoes.length) {\n issues.push({\n code: 'layout-length',\n message: `Layout length ${layout.length} does not match domino count ${dominoes.length}`,\n });\n }\n\n return { valid: issues.length === 0, issues };\n}\n\n/**\n * Validates the chicken-foot branch tree as data: every chain links up, every\n * foot hangs off a real double, and every toe's first tile matches the double's\n * value. Recurses into nested feet.\n */\nexport function validateChickenFootChain(\n branch: TrainBranch\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n\n const walk = (current: TrainBranch, path: string) => {\n issues.push(\n ...validateDominoChain(current.dominoes).issues.map((issue) => ({\n ...issue,\n message: `[${path}] ${issue.message}`,\n }))\n );\n\n if (!current.feet) {\n return;\n }\n\n for (const key of Object.keys(current.feet)) {\n const hostIndex = Number(key);\n const host = current.dominoes[hostIndex];\n const toes = current.feet[hostIndex] ?? [];\n\n if (!host) {\n issues.push({\n code: 'foot-host-missing',\n message: `[${path}] Foot references missing tile ${hostIndex}`,\n });\n continue;\n }\n\n if (host.value1 !== host.value2) {\n issues.push({\n code: 'foot-host-not-double',\n message: `[${path}] Foot host tile ${hostIndex} is not a double`,\n });\n }\n\n if (toes.length > 2) {\n issues.push({\n code: 'foot-too-many-toes',\n message: `[${path}] Double ${hostIndex} has ${toes.length} side toes (max 2; the center toe is the main line)`,\n });\n }\n\n toes.forEach((toe, toeIndex) => {\n const first = toe.dominoes[0];\n if (first && first.value1 !== host.value1) {\n issues.push({\n code: 'foot-connection',\n message: `[${path}] Toe ${toeIndex} on double ${hostIndex} starts with ${first.value1} but the double is ${host.value1}`,\n });\n }\n walk(toe, `${path}.${hostIndex}.${toeIndex}`);\n });\n }\n };\n\n walk(branch, 'main');\n return { valid: issues.length === 0, issues };\n}\n\n/**\n * Validates a laid-out chicken-foot tree. Each run's tiles must link up by value\n * and match its tile count; each toe must start the right distance out from its\n * host double (measured along the toe's axis); and — the hard physical rule — no\n * two tiles anywhere in the tree may overlap.\n *\n * The center toe is a centered linear run while the inbound spine is offset, so\n * a single run can mix layout styles; the per-tile overlap test below checks the\n * real constraint directly rather than reconstructing each style's spacing.\n */\nexport function validateTrainTree(\n segments: readonly TrainSegment[],\n tolerance = DEFAULT_TOLERANCE\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n\n segments.forEach((segment, segmentIndex) => {\n issues.push(\n ...validateDominoChain(segment.dominoes).issues.map((issue) => ({\n ...issue,\n message: `[segment ${segmentIndex} @${segment.angle}°] ${issue.message}`,\n }))\n );\n\n if (segment.layout.length !== segment.dominoes.length) {\n issues.push({\n code: 'layout-length',\n message: `[segment ${segmentIndex}] Layout length ${segment.layout.length} does not match domino count ${segment.dominoes.length}`,\n });\n }\n\n if (segment.anchor && segment.layout.length > 0) {\n const first = segment.layout[0];\n const along = projectOnTrainAxis(\n first.x - segment.anchor.x,\n first.y - segment.anchor.y,\n segment.angle\n );\n const expected = DOMINO_HEIGHT / 2;\n if (Math.abs(along - expected) > tolerance) {\n issues.push({\n code: 'foot-anchor',\n message: `[segment ${segmentIndex}] First toe tile sits ${along.toFixed(2)}px from the double along the toe (expected ${expected}px)`,\n index: 0,\n });\n }\n }\n });\n\n // Physical rule: dominoes are solid, so no two tiles may overlap anywhere.\n const tiles = segments.flatMap((segment) => segment.layout);\n for (let i = 0; i < tiles.length; i++) {\n for (let j = i + 1; j < tiles.length; j++) {\n if (tilesOverlap(tiles[i], tiles[j])) {\n issues.push({\n code: 'tile-overlap',\n message: `Tiles ${i} and ${j} overlap`,\n index: j,\n });\n }\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport function dominoCorners(\n entry: TrainLayoutEntry,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): { x: number; y: number }[] {\n const rotation = (entry.rotation * Math.PI) / 180;\n const halfW = dominoWidth / 2;\n const halfH = dominoHeight / 2;\n const localCorners = [\n { x: -halfW, y: -halfH },\n { x: halfW, y: -halfH },\n { x: halfW, y: halfH },\n { x: -halfW, y: halfH },\n ];\n\n return localCorners.map(({ x, y }) => {\n const rotatedX = x * Math.cos(rotation) - y * Math.sin(rotation);\n const rotatedY = x * Math.sin(rotation) + y * Math.cos(rotation);\n return { x: entry.x + rotatedX, y: entry.y + rotatedY };\n });\n}\n","import { FC, useEffect, useMemo } from 'react';\nimport { DoubleTwelve } from '@/app/DoubleTwelve';\nimport {\n DOMINO_HEIGHT,\n DOMINO_WIDTH,\n computeTrainTree,\n flattenSegments,\n} from '@/app/trainLayout';\nimport { validateChickenFootChain } from '@/harness/layoutValidation';\nimport { TrainData } from '@/game/TrainData';\nimport { PipColorMap } from '@/app/pipColors';\n\n/** True in dev/test, false in the production library bundle (Vite inlines it). */\nconst IS_DEV = (() => {\n try {\n return Boolean(import.meta.env?.DEV);\n } catch {\n return false;\n }\n})();\n\ninterface DominoTrainProps {\n startX: number;\n startY: number;\n angle: number;\n trainData: TrainData;\n layoutStyle: 'offset' | 'linear';\n tableWidth: number;\n tableHeight: number;\n centerX: number;\n centerY: number;\n pipColors?: PipColorMap;\n}\n\nexport const DominoTrain: FC<DominoTrainProps> = ({\n startX,\n startY,\n angle,\n trainData,\n layoutStyle,\n tableWidth,\n tableHeight,\n centerX,\n centerY,\n pipColors,\n}) => {\n // Guard rail: a train must be a sequentially-correct chain (like-values\n // touching, toes connecting to their double). Rather than silently drawing a\n // broken train, surface the rule violations in dev. Never throws, so it can't\n // crash a host app's render in production.\n useEffect(() => {\n if (!IS_DEV) return;\n const result = validateChickenFootChain({\n dominoes: trainData.dominoes,\n feet: trainData.feet,\n });\n if (!result.valid) {\n console.warn(\n `DominoTrain: player ${trainData.playerId} train does not follow the rules:`,\n result.issues.map((issue) => issue.message)\n );\n }\n }, [trainData.dominoes, trainData.feet, trainData.playerId]);\n\n const trainLayout = useMemo(\n () =>\n flattenSegments(\n computeTrainTree({\n startX,\n startY,\n angle,\n branch: { dominoes: trainData.dominoes, feet: trainData.feet },\n layoutStyle,\n })\n ),\n [\n startX,\n startY,\n angle,\n trainData.dominoes,\n trainData.feet,\n layoutStyle,\n tableWidth,\n tableHeight,\n ]\n );\n\n return (\n <>\n {trainLayout.map((entry, index) => {\n const showMarker = trainData.isPublic;\n\n return (\n <div\n key={`main-train-${trainData.playerId}-${index}`}\n style={{\n position: 'absolute',\n left: `${entry.x - DOMINO_WIDTH / 2}px`,\n top: `${entry.y - DOMINO_HEIGHT / 2}px`,\n zIndex: 5,\n }}\n >\n <DoubleTwelve\n value1={entry.value1}\n value2={entry.value2}\n width={DOMINO_WIDTH}\n height={DOMINO_HEIGHT}\n backgroundColor=\"white\"\n pipColor=\"black\"\n pipColors={pipColors}\n borderColor={showMarker ? 'red' : 'black'}\n rotation={entry.rotation}\n />\n </div>\n );\n })}\n </>\n );\n};\n\nexport default DominoTrain;\n","import { FC } from 'react';\nimport { DominoTrain } from '@/app/DominoTrain';\nimport { DoubleTwelve } from '@/app/DoubleTwelve';\nimport { TrainData } from '@/game/TrainData';\nimport { PipColorMap } from '@/app/pipColors';\n\ninterface DominoHubProps {\n playerCount: number;\n centerX: number;\n centerY: number;\n radius: number;\n engineValue: number;\n trains: TrainData[];\n layoutStyle: 'offset' | 'linear';\n tableWidth: number;\n tableHeight: number;\n pipColors?: PipColorMap;\n}\n\n/**\n * Distance from the hub center at which a train should start so its first tile\n * clears its neighbours. Trains fan out 360/slots° apart, so the neighbour gap\n * at distance d is ~2πd/slots; it must exceed a tile's footprint. Offset trains\n * zigzag wider (a perpendicular half-tile seed) and need a bigger ring; linear\n * trains are skinny and stay near the hub. Never smaller than `radius + 20`.\n */\nexport function hubTrainStartDistance(\n slots: number,\n radius: number,\n dominoWidth: number,\n layoutStyle: 'offset' | 'linear'\n): number {\n const minNeighborGap = dominoWidth * (layoutStyle === 'offset' ? 2.5 : 1.3);\n return Math.max(radius + 20, Math.ceil((minNeighborGap * slots) / (2 * Math.PI)));\n}\n\nexport const DominoHub: FC<DominoHubProps> = ({\n playerCount,\n centerX,\n centerY,\n radius,\n engineValue,\n trains,\n layoutStyle,\n tableWidth,\n tableHeight,\n pipColors,\n}) => {\n // Ensure we have at least 8 player slots\n const slots = Math.max(8, playerCount);\n const hubSize = 120; // Increased to fit the standard domino size\n const dominoWidth = 60;\n const dominoHeight = 120; // Standard domino size to match the rest of the dominoes\n\n const startDistance = hubTrainStartDistance(slots, radius, dominoWidth, layoutStyle);\n\n return (\n <div style={{ position: 'relative', width: '100%', height: '100%' }}>\n {/* Central hub */}\n <div\n style={{\n position: 'absolute',\n width: `${hubSize}px`,\n height: `${hubSize}px`,\n left: `${centerX - hubSize / 2}px`,\n top: `${centerY - hubSize / 2}px`,\n backgroundColor: '#d1d5db',\n borderWidth: '3px',\n borderStyle: 'solid',\n borderColor: '#6b7280',\n borderRadius: '50%',\n boxShadow: '0 4px 6px rgba(0,0,0,0.1)',\n zIndex: 10,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n }}\n >\n {/* Engine domino in the center */}\n <div style={{ transform: 'rotate(0deg)' }}>\n <DoubleTwelve\n value1={engineValue}\n value2={engineValue}\n width={dominoWidth}\n height={dominoHeight}\n backgroundColor=\"white\"\n pipColor=\"black\"\n pipColors={pipColors}\n borderColor=\"#333\"\n />\n </div>\n </div>\n\n {/* Trains radiating from hub */}\n {Array.from({ length: slots }).map((_, index) => {\n const angle = (index * 360) / slots;\n const radians = (angle * Math.PI) / 180;\n\n // Calculate starting point for the train\n const startX = centerX + startDistance * Math.cos(radians);\n const startY = centerY + startDistance * Math.sin(radians);\n\n // Get train data for this position (if exists)\n const trainData = trains.find((t) => t.playerId === index) || {\n dominoes: [],\n playerId: index,\n isPublic: false,\n };\n\n return (\n <DominoTrain\n key={index}\n startX={startX}\n startY={startY}\n angle={angle}\n trainData={trainData}\n layoutStyle={layoutStyle}\n tableWidth={tableWidth}\n tableHeight={tableHeight}\n centerX={centerX}\n centerY={centerY}\n pipColors={pipColors}\n />\n );\n })}\n </div>\n );\n};\n\nexport default DominoHub;\n","import { DominoValue } from '@/game/DominoValue';\n\n/**\n * Canonical, order-independent key for a tile. `6-3` and `3-6` are the same\n * physical domino, so they share a key. Used to enforce tile uniqueness.\n */\nexport function tileKey(value1: number, value2: number): string {\n return value1 <= value2 ? `${value1}:${value2}` : `${value2}:${value1}`;\n}\n\nexport function dominoKey(tile: DominoValue): string {\n return tileKey(tile.value1, tile.value2);\n}\n\nexport function isDouble(tile: DominoValue): boolean {\n return tile.value1 === tile.value2;\n}\n\nexport function tileHasValue(tile: DominoValue, value: number): boolean {\n return tile.value1 === value || tile.value2 === value;\n}\n\n/** The pip value on the opposite end from `value`, or null if it doesn't touch. */\nexport function otherEnd(tile: DominoValue, value: number): number | null {\n if (tile.value1 === value) return tile.value2;\n if (tile.value2 === value) return tile.value1;\n return null;\n}\n\n/**\n * Orients a tile so its `value1` is the end that connects to `connectingValue`.\n * Returns null if the tile has no such end.\n */\nexport function orientForConnection(\n tile: DominoValue,\n connectingValue: number\n): DominoValue | null {\n if (tile.value1 === connectingValue) {\n return { value1: tile.value1, value2: tile.value2 };\n }\n if (tile.value2 === connectingValue) {\n return { value1: tile.value2, value2: tile.value1 };\n }\n return null;\n}\n\n/** Every unique tile in a double-`maxPips` set (e.g. maxPips=12 → 91 tiles). */\nexport function generateDominoSet(maxPips: number): DominoValue[] {\n const tiles: DominoValue[] = [];\n for (let a = 0; a <= maxPips; a++) {\n for (let b = a; b <= maxPips; b++) {\n tiles.push({ value1: a, value2: b });\n }\n }\n return tiles;\n}\n\n/** Count of tiles in a double-`maxPips` set: (n+1)(n+2)/2 where n = maxPips. */\nexport function dominoSetSize(maxPips: number): number {\n const n = maxPips + 1;\n return (n * (n + 1)) / 2;\n}\n","import { DominoValue } from './DominoValue';\nimport { TrainBranch, TrainData } from './TrainData';\nimport { tileKey } from '@/rules/dominoSet';\n\nexport { tileKey };\n\nexport interface GenerateSampleTrainsOptions {\n /** Attach chicken-foot side toes (±45°) to every double. */\n chickenFeet?: boolean;\n}\n\n/** Demo train generator that respects double-12 tile uniqueness constraints. */\nexport function generateSampleTrains(\n playerCount: number,\n engineValue = 12,\n options: GenerateSampleTrainsOptions = {}\n): TrainData[] {\n const usedTiles = new Set<string>([tileKey(engineValue, engineValue)]);\n const trains: TrainData[] = [];\n\n for (let playerId = 0; playerId < playerCount; playerId++) {\n const dominoCount = 4 + Math.floor(Math.random() * 7);\n const dominoes: DominoValue[] = [];\n let openValue = engineValue;\n let prevWasDouble = false;\n\n for (let j = 0; j < dominoCount; j++) {\n const value2 = pickNextValue(\n openValue,\n prevWasDouble,\n j === 0,\n engineValue,\n usedTiles\n );\n\n // No tile left that keeps the chain legal and unique: stop this train.\n if (value2 === null) {\n break;\n }\n\n const isDouble = value2 === openValue;\n usedTiles.add(tileKey(openValue, value2));\n\n dominoes.push({ value1: openValue, value2 });\n prevWasDouble = isDouble;\n openValue = value2;\n }\n\n const feet = options.chickenFeet\n ? buildFeet(dominoes, usedTiles)\n : undefined;\n\n trains.push({\n playerId,\n dominoes,\n isPublic: Math.random() > 0.7,\n ...(feet ? { feet } : {}),\n });\n }\n\n return trains;\n}\n\n/**\n * Builds chicken-foot side toes for every double in a chain. Each toe is a short\n * straight run (no doubles, so no nested feet) starting from the double's value.\n */\nfunction buildFeet(\n dominoes: readonly DominoValue[],\n usedTiles: Set<string>\n): Record<number, TrainBranch[]> | undefined {\n const feet: Record<number, TrainBranch[]> = {};\n\n for (let i = 0; i < dominoes.length; i++) {\n if (dominoes[i].value1 !== dominoes[i].value2) {\n continue;\n }\n\n const doubleValue = dominoes[i].value1;\n const toes: TrainBranch[] = [];\n for (let t = 0; t < 2; t++) {\n const toe = buildToe(doubleValue, usedTiles);\n if (toe) {\n toes.push(toe);\n }\n }\n\n if (toes.length) {\n feet[i] = toes;\n }\n }\n\n return Object.keys(feet).length ? feet : undefined;\n}\n\nfunction buildToe(\n startValue: number,\n usedTiles: Set<string>\n): TrainBranch | null {\n const length = 1 + Math.floor(Math.random() * 2);\n const dominoes: DominoValue[] = [];\n let openValue = startValue;\n\n for (let j = 0; j < length; j++) {\n const next = pickNonDouble(openValue, usedTiles);\n if (next === null) {\n break;\n }\n usedTiles.add(tileKey(openValue, next));\n dominoes.push({ value1: openValue, value2: next });\n openValue = next;\n }\n\n return dominoes.length ? { dominoes } : null;\n}\n\nfunction pickNonDouble(\n openValue: number,\n usedTiles: Set<string>\n): number | null {\n const candidates: number[] = [];\n for (let value = 0; value < 13; value++) {\n if (value === openValue) {\n continue;\n }\n if (usedTiles.has(tileKey(openValue, value))) {\n continue;\n }\n candidates.push(value);\n }\n\n if (candidates.length === 0) {\n return null;\n }\n\n return candidates[Math.floor(Math.random() * candidates.length)];\n}\n\nfunction pickNextValue(\n openValue: number,\n prevWasDouble: boolean,\n isFirstDomino: boolean,\n engineValue: number,\n usedTiles: Set<string>\n): number | null {\n const candidates = Array.from({ length: 13 }, (_, value) => value).filter(\n (value) =>\n isValidNextValue(\n openValue,\n value,\n prevWasDouble,\n isFirstDomino,\n engineValue,\n usedTiles\n )\n );\n\n if (candidates.length === 0) {\n return null;\n }\n\n return candidates[Math.floor(Math.random() * candidates.length)];\n}\n\nfunction isValidNextValue(\n openValue: number,\n value: number,\n prevWasDouble: boolean,\n isFirstDomino: boolean,\n engineValue: number,\n usedTiles: Set<string>\n): boolean {\n const isDouble = value === openValue;\n\n if (isFirstDomino && isDouble && openValue === engineValue) {\n return false;\n }\n\n if (isDouble && prevWasDouble) {\n return false;\n }\n\n if (usedTiles.has(tileKey(openValue, value))) {\n return false;\n }\n\n return true;\n}\n","import { FC, useState, useEffect } from 'react';\nimport { DominoHub } from './DominoHub';\nimport { GameState } from '@/game/GameState';\nimport { generateSampleTrains } from '@/game/generateSampleTrains';\nimport { DEFAULT_PIP_COLORS, PipColorMap } from './pipColors';\n\ninterface MexicanTrainGameProps {\n initialState?: GameState;\n width?: number;\n height?: number;\n pipColors?: PipColorMap;\n onPipColorsChange?: (pipColors: PipColorMap | undefined) => void;\n}\n\nconst defaultGameState: GameState = {\n playerCount: 8,\n trains: [],\n engineValue: 12,\n};\n\nexport const MexicanTrainGame: FC<MexicanTrainGameProps> = ({\n initialState = defaultGameState,\n width = 1200,\n height = 800,\n pipColors: pipColorsProp,\n onPipColorsChange,\n}) => {\n const [gameState, setGameState] = useState<GameState>(initialState);\n const [layoutStyle, setLayoutStyle] = useState<'offset' | 'linear'>('offset');\n const [chickenFeet, setChickenFeet] = useState(false);\n const [pipColorsInternal, setPipColorsInternal] = useState<\n PipColorMap | undefined\n >(undefined);\n const pipColors = pipColorsProp ?? pipColorsInternal;\n const setPipColors = onPipColorsChange ?? setPipColorsInternal;\n const pipColorsEnabled = pipColors !== undefined;\n\n // Center coordinates for the hub\n const centerX = width / 2;\n const centerY = height / 2;\n\n // Generate sample data for better visualization\n const regenerateTrains = (chickenFeetEnabled = chickenFeet) => {\n const sampleTrains = generateSampleTrains(\n gameState.playerCount,\n gameState.engineValue,\n { chickenFeet: chickenFeetEnabled }\n );\n setGameState((prevState) => ({\n ...prevState,\n trains: sampleTrains,\n }));\n };\n\n useEffect(() => {\n regenerateTrains();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const toggleChickenFeet = () => {\n const next = !chickenFeet;\n setChickenFeet(next);\n regenerateTrains(next);\n };\n\n return (\n <div\n style={{\n width: `${width}px`,\n height: `${height}px`,\n position: 'relative',\n backgroundColor: '#1f8a55', // Classic green felt background\n borderRadius: '8px',\n boxShadow: '0 4px 12px rgba(0,0,0,0.2)',\n overflow: 'hidden',\n }}\n >\n <div\n style={{ position: 'absolute', top: '10px', left: '10px', zIndex: 100 }}\n >\n <button\n onClick={() => regenerateTrains()}\n style={{\n padding: '8px 12px',\n backgroundColor: '#fff',\n border: '1px solid #ccc',\n borderRadius: '4px',\n marginRight: '10px',\n cursor: 'pointer',\n }}\n >\n New trains\n </button>\n <button\n onClick={() =>\n setLayoutStyle(layoutStyle === 'offset' ? 'linear' : 'offset')\n }\n style={{\n padding: '8px 12px',\n backgroundColor: '#fff',\n border: '1px solid #ccc',\n borderRadius: '4px',\n marginRight: '10px',\n cursor: 'pointer',\n }}\n >\n Layout: {layoutStyle === 'offset' ? 'Offset' : 'Linear'}\n </button>\n <button\n onClick={toggleChickenFeet}\n style={{\n padding: '8px 12px',\n backgroundColor: chickenFeet ? '#fef3c7' : '#fff',\n border: `1px solid ${chickenFeet ? '#f59e0b' : '#ccc'}`,\n borderRadius: '4px',\n marginRight: '10px',\n cursor: 'pointer',\n }}\n >\n Chicken Feet: {chickenFeet ? 'On' : 'Off'}\n </button>\n <button\n onClick={() =>\n setPipColors(pipColorsEnabled ? undefined : DEFAULT_PIP_COLORS)\n }\n style={{\n padding: '8px 12px',\n backgroundColor: pipColorsEnabled ? '#fef3c7' : '#fff',\n border: `1px solid ${pipColorsEnabled ? '#f59e0b' : '#ccc'}`,\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n >\n Pip Colors: {pipColorsEnabled ? 'On' : 'Off'}\n </button>\n </div>\n\n {/* Game information */}\n <div\n style={{\n position: 'absolute',\n top: '10px',\n right: '10px',\n zIndex: 100,\n backgroundColor: 'rgba(255,255,255,0.8)',\n padding: '8px',\n borderRadius: '4px',\n fontSize: '14px',\n }}\n >\n <div>Engine: Double-{gameState.engineValue}</div>\n <div>Players: {gameState.playerCount}</div>\n </div>\n\n <DominoHub\n playerCount={gameState.playerCount}\n centerX={centerX}\n centerY={centerY}\n radius={80}\n engineValue={gameState.engineValue}\n trains={gameState.trains}\n layoutStyle={layoutStyle}\n tableWidth={width}\n tableHeight={height}\n pipColors={pipColors}\n />\n </div>\n );\n};\n\nexport default MexicanTrainGame;\n","import { TrainBend, TrainBranch } from '@/game/TrainData';\nimport {\n TrainLayoutEntry,\n TrainLayoutStyle,\n computeTrainTree,\n flattenSegments,\n layoutSelfIntersects,\n layoutsCollide,\n outwardPerpSign,\n trainPerpendicular,\n} from './trainLayout';\n\nexport type TurnSide = 'left' | 'right';\n\n/** Default pivot magnitude. The interactive UI only produces square corners. */\nexport const TURN_DEGREES = 90;\n\n/**\n * Signed turn (degrees) for a side. Headings use the screen convention\n * (0° = +x, +90° = +y / downward), so a `+90` turn rotates +x toward +y, which\n * reads as a clockwise/\"right\" turn on screen.\n */\nexport function sideToTurn(side: TurnSide, degrees = TURN_DEGREES): number {\n return side === 'right' ? degrees : -degrees;\n}\n\nexport function oppositeSide(side: TurnSide): TurnSide {\n return side === 'right' ? 'left' : 'right';\n}\n\n/**\n * Default turn side in offset mode: fold toward the empty side — the one\n * opposite the lane the zigzag biases into (`outwardSign`). A `+90` turn heads\n * toward the heading's `+perp`; outwardSign is measured on that same perp axis,\n * so the empty side is `-outwardSign`, i.e. side = outwardSign >= 0 ? 'left' : 'right'.\n */\nexport function offsetDefaultSide(angle: number, outwardSign?: number): TurnSide {\n const bias = outwardSign ?? outwardPerpSign(angle);\n return bias >= 0 ? 'left' : 'right';\n}\n\nexport interface TableBounds {\n width: number;\n height: number;\n}\n\n/**\n * Default turn side in linear mode: fold toward whichever perpendicular side has\n * more open table from the bend point. Distance is measured from `point` along\n * each perpendicular until it exits the table rectangle; the roomier side wins.\n * Ties (e.g. dead-center) fall back to 'right'.\n */\nexport function linearDefaultSide(\n point: { x: number; y: number },\n angle: number,\n bounds: TableBounds\n): TurnSide {\n const { perpX, perpY } = trainPerpendicular(angle);\n const distanceToExit = (sx: number, sy: number): number => {\n // Largest t >= 0 with point + t*(sx,sy) still inside [0,w] x [0,h].\n let t = Infinity;\n if (sx > 0) t = Math.min(t, (bounds.width - point.x) / sx);\n else if (sx < 0) t = Math.min(t, (0 - point.x) / sx);\n if (sy > 0) t = Math.min(t, (bounds.height - point.y) / sy);\n else if (sy < 0) t = Math.min(t, (0 - point.y) / sy);\n return Number.isFinite(t) ? Math.max(0, t) : Infinity;\n };\n\n // +90 turn heads toward +perp ('right'); -90 toward -perp ('left').\n const rightRoom = distanceToExit(perpX, perpY);\n const leftRoom = distanceToExit(-perpX, -perpY);\n return rightRoom >= leftRoom ? 'right' : 'left';\n}\n\nexport interface BuildTrainTilesInput {\n startX: number;\n startY: number;\n angle: number;\n layoutStyle: TrainLayoutStyle;\n}\n\n/** Flattens a branch (with feet and bends) to its world-space tiles. */\nexport function buildBranchTiles(\n branch: TrainBranch,\n input: BuildTrainTilesInput\n): TrainLayoutEntry[] {\n return flattenSegments(\n computeTrainTree({\n startX: input.startX,\n startY: input.startY,\n angle: input.angle,\n branch,\n layoutStyle: input.layoutStyle,\n })\n );\n}\n\n/** Replaces (or removes) the bend at `index`, returning a new bends array. */\nexport function withBendAt(\n bends: readonly TrainBend[] | undefined,\n index: number,\n turn: number | null\n): TrainBend[] {\n const rest = (bends ?? []).filter((bend) => bend.index !== index);\n if (turn === null) return rest;\n return [...rest, { index, turn }].sort((a, b) => a.index - b.index);\n}\n\nexport interface ResolveBendResult {\n /** The legal turn to apply, or null when no side is collision-free. */\n turn: number | null;\n /** Why null: 'blocked' = both sides collide; never set on success. */\n reason?: 'blocked';\n}\n\nexport interface ResolveBendInput {\n branch: TrainBranch;\n index: number;\n build: BuildTrainTilesInput;\n /** Tiles belonging to every OTHER path; a bend may not intersect these. */\n obstacles: readonly TrainLayoutEntry[];\n /** Preferred side to try first (from the mode's heuristic). */\n preferredSide: TurnSide;\n /** Turn magnitude in degrees (default 90). */\n degrees?: number;\n}\n\n/**\n * Picks a collision-free turn for a new bend at `index`. Tries the preferred\n * side first, then the opposite; a candidate is rejected if the resulting path\n * crosses itself or any obstacle path. Returns `{ turn: null, reason: 'blocked' }`\n * when neither side is legal, so the caller can refuse the bend.\n */\nexport function resolveBend({\n branch,\n index,\n build,\n obstacles,\n preferredSide,\n degrees = TURN_DEGREES,\n}: ResolveBendInput): ResolveBendResult {\n const candidates: TurnSide[] = [preferredSide, oppositeSide(preferredSide)];\n\n for (const side of candidates) {\n const turn = sideToTurn(side, degrees);\n const candidateBranch: TrainBranch = {\n ...branch,\n bends: withBendAt(branch.bends, index, turn),\n };\n const tiles = buildBranchTiles(candidateBranch, build);\n if (layoutSelfIntersects(tiles)) continue;\n if (layoutsCollide(tiles, obstacles)) continue;\n return { turn };\n }\n\n return { turn: null, reason: 'blocked' };\n}\n\n/**\n * Cycles a tile's bend on repeated clicks: none → preferred legal side →\n * opposite legal side → none. Skips sides that collide. Returns the next bends\n * array, or the unchanged input when no legal bend exists.\n */\nexport function cycleBendAt(\n branch: TrainBranch,\n index: number,\n build: BuildTrainTilesInput,\n obstacles: readonly TrainLayoutEntry[],\n preferredSide: TurnSide,\n degrees = TURN_DEGREES\n): { bends: TrainBend[]; changed: boolean; blocked: boolean } {\n const current = (branch.bends ?? []).find((bend) => bend.index === index);\n const preferredTurn = sideToTurn(preferredSide, degrees);\n const oppositeTurn = sideToTurn(oppositeSide(preferredSide), degrees);\n\n const isLegal = (turn: number): boolean => {\n const candidate: TrainBranch = {\n ...branch,\n bends: withBendAt(branch.bends, index, turn),\n };\n const tiles = buildBranchTiles(candidate, build);\n return !layoutSelfIntersects(tiles) && !layoutsCollide(tiles, obstacles);\n };\n\n // Cycle order by current state. `null` means \"straighten\" (remove the bend),\n // which is always legal — but it's only a cycle target when a bend already\n // exists. Starting from straight with both sides blocked reports `blocked`\n // rather than performing a no-op removal.\n let order: (number | null)[];\n if (!current) {\n order = [preferredTurn, oppositeTurn];\n } else if (current.turn === preferredTurn) {\n order = [oppositeTurn, null];\n } else if (current.turn === oppositeTurn) {\n order = [null];\n } else {\n order = [preferredTurn, oppositeTurn, null];\n }\n\n for (const turn of order) {\n if (turn === null) {\n return { bends: withBendAt(branch.bends, index, null), changed: true, blocked: false };\n }\n if (isLegal(turn)) {\n return { bends: withBendAt(branch.bends, index, turn), changed: true, blocked: false };\n }\n }\n\n return { bends: branch.bends ?? [], changed: false, blocked: true };\n}\n","/** Pan/zoom transform: content is scaled by `scale` then translated by (x, y). */\nexport interface ViewportTransform {\n scale: number;\n x: number;\n y: number;\n}\n\nexport interface Size {\n width: number;\n height: number;\n}\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport function clampScale(scale: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, scale));\n}\n\n/**\n * Zooms by `factor` about a fixed screen `pivot` so the content point under the\n * pivot stays put. Scale is clamped to [min, max]; the translation is adjusted\n * by the *effective* factor after clamping so panning can't drift at the limits.\n */\nexport function zoomAt(\n view: ViewportTransform,\n factor: number,\n pivot: Point,\n min: number,\n max: number\n): ViewportTransform {\n const scale = clampScale(view.scale * factor, min, max);\n const effective = scale / view.scale;\n return {\n scale,\n x: pivot.x - (pivot.x - view.x) * effective,\n y: pivot.y - (pivot.y - view.y) * effective,\n };\n}\n\n/**\n * Centers `content` within `viewport` at the largest scale that fits inside the\n * given padding (clamped to [min, max]). Use this for a \"fit / reset\" control.\n */\nexport function fitToBounds(\n content: Size,\n viewport: Size,\n padding: number,\n min: number,\n max: number\n): ViewportTransform {\n const safeW = Math.max(1, content.width);\n const safeH = Math.max(1, content.height);\n const raw = Math.min(\n (viewport.width - padding * 2) / safeW,\n (viewport.height - padding * 2) / safeH\n );\n const scale = clampScale(raw, min, max);\n return {\n scale,\n x: (viewport.width - safeW * scale) / 2,\n y: (viewport.height - safeH * scale) / 2,\n };\n}\n\n/** Converts a screen point inside the viewport to content coordinates. */\nexport function screenToContent(view: ViewportTransform, screen: Point): Point {\n return {\n x: (screen.x - view.x) / view.scale,\n y: (screen.y - view.y) / view.scale,\n };\n}\n","import {\n CSSProperties,\n FC,\n ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport {\n ViewportTransform,\n clampScale,\n fitToBounds,\n zoomAt,\n} from './viewportMath';\n\nexport interface ViewportProps {\n /** Visible viewport size in pixels. */\n width: number;\n height: number;\n /** Content (world) size, used by the fit/reset control. */\n contentWidth: number;\n contentHeight: number;\n children: ReactNode;\n minScale?: number;\n maxScale?: number;\n /** Multiplier applied per wheel notch / zoom-button press. */\n zoomStep?: number;\n padding?: number;\n background?: string;\n /** Show the built-in zoom/reset control overlay. Default true. */\n showControls?: boolean;\n testId?: string;\n}\n\nconst PAN_THRESHOLD = 3;\n\n/**\n * A pan/zoom canvas for content larger than the screen. Drag to slide, wheel or\n * the on-screen buttons to zoom (zoom centers on the cursor for the wheel).\n * Clicks pass through to children unless the pointer actually dragged, so\n * interactive content (e.g. click-to-bend tiles) keeps working.\n */\nexport const Viewport: FC<ViewportProps> = ({\n width,\n height,\n contentWidth,\n contentHeight,\n children,\n minScale = 0.2,\n maxScale = 4,\n zoomStep = 1.15,\n padding = 40,\n background = '#1f8a55',\n showControls = true,\n testId = 'viewport',\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const fit = useCallback(\n (): ViewportTransform =>\n fitToBounds(\n { width: contentWidth, height: contentHeight },\n { width, height },\n padding,\n minScale,\n maxScale\n ),\n [contentWidth, contentHeight, width, height, padding, minScale, maxScale]\n );\n\n const [view, setView] = useState<ViewportTransform>(fit);\n\n // Re-fit when the content or viewport size changes meaningfully.\n useEffect(() => {\n setView(fit());\n }, [fit]);\n\n const pan = useRef<\n | { pointerX: number; pointerY: number; startX: number; startY: number; moved: boolean }\n | null\n >(null);\n\n const localPoint = (clientX: number, clientY: number) => {\n const rect = containerRef.current?.getBoundingClientRect();\n return { x: clientX - (rect?.left ?? 0), y: clientY - (rect?.top ?? 0) };\n };\n\n const onPointerDown = (event: React.PointerEvent) => {\n pan.current = {\n pointerX: event.clientX,\n pointerY: event.clientY,\n startX: view.x,\n startY: view.y,\n moved: false,\n };\n };\n\n const onPointerMove = (event: React.PointerEvent) => {\n const current = pan.current;\n if (!current) return;\n const dx = event.clientX - current.pointerX;\n const dy = event.clientY - current.pointerY;\n if (!current.moved && Math.hypot(dx, dy) < PAN_THRESHOLD) return;\n current.moved = true;\n containerRef.current?.setPointerCapture?.(event.pointerId);\n setView((prev) => ({ ...prev, x: current.startX + dx, y: current.startY + dy }));\n };\n\n const endPan = (event: React.PointerEvent) => {\n if (pan.current?.moved) {\n // Swallow the click that follows a drag so children don't treat it as a tap.\n event.preventDefault();\n }\n pan.current = null;\n };\n\n const onWheel = (event: React.WheelEvent) => {\n event.preventDefault();\n const pivot = localPoint(event.clientX, event.clientY);\n const factor = event.deltaY < 0 ? zoomStep : 1 / zoomStep;\n setView((prev) => zoomAt(prev, factor, pivot, minScale, maxScale));\n };\n\n const zoomButton = (factor: number) =>\n setView((prev) =>\n zoomAt(prev, factor, { x: width / 2, y: height / 2 }, minScale, maxScale)\n );\n\n const buttonStyle: CSSProperties = {\n width: 32,\n height: 32,\n fontSize: 18,\n lineHeight: '30px',\n textAlign: 'center',\n cursor: 'pointer',\n background: '#fff',\n border: '1px solid #d1d5db',\n borderRadius: 6,\n userSelect: 'none',\n };\n\n return (\n <div\n ref={containerRef}\n data-testid={testId}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endPan}\n onPointerLeave={endPan}\n onWheel={onWheel}\n style={{\n position: 'relative',\n width,\n height,\n overflow: 'hidden',\n background,\n borderRadius: 8,\n cursor: 'grab',\n touchAction: 'none',\n }}\n >\n <div\n data-testid={`${testId}-content`}\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n transformOrigin: '0 0',\n transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,\n }}\n >\n {children}\n </div>\n\n {showControls && (\n <div\n data-testid={`${testId}-controls`}\n style={{\n position: 'absolute',\n right: 12,\n bottom: 12,\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n }}\n >\n <div style={buttonStyle} role=\"button\" aria-label=\"Zoom in\" onClick={() => zoomButton(zoomStep)}>\n +\n </div>\n <div style={buttonStyle} role=\"button\" aria-label=\"Zoom out\" onClick={() => zoomButton(1 / zoomStep)}>\n −\n </div>\n <div\n style={{ ...buttonStyle, fontSize: 12, lineHeight: '30px' }}\n role=\"button\"\n aria-label=\"Reset view\"\n onClick={() => setView(fit())}\n >\n ⤢\n </div>\n <div\n data-testid={`${testId}-zoom-readout`}\n style={{ ...buttonStyle, fontSize: 11, cursor: 'default' }}\n >\n {Math.round(clampScale(view.scale, minScale, maxScale) * 100)}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport default Viewport;\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBranch } from '@/game/TrainData';\nimport { TrainLayoutStyle } from '@/app/trainLayout';\n\nexport interface TrainFixture {\n id: string;\n name: string;\n description: string;\n angle: number;\n dominoes: DominoValue[];\n layoutStyles: TrainLayoutStyle[];\n}\n\nexport interface ChickenFootFixture {\n id: string;\n name: string;\n description: string;\n angle: number;\n branch: TrainBranch;\n layoutStyles: TrainLayoutStyle[];\n}\n\nexport const TRAIN_FIXTURES: TrainFixture[] = [\n {\n id: 'regular-after-double',\n name: 'Regular after double',\n description: 'Double followed by a two-tile offset run',\n angle: 0,\n dominoes: [\n { value1: 12, value2: 6 },\n { value1: 6, value2: 6 },\n { value1: 6, value2: 3 },\n { value1: 3, value2: 1 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'double-after-regular',\n name: 'Double after regular',\n description: 'Offset run, a double, then another offset run',\n angle: 0,\n dominoes: [\n { value1: 12, value2: 9 },\n { value1: 9, value2: 4 },\n { value1: 4, value2: 4 },\n { value1: 4, value2: 2 },\n { value1: 2, value2: 7 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'double-after-double',\n name: 'Double after double',\n description: 'Offset runs at the head, middle, and tail around two doubles',\n angle: 90,\n dominoes: [\n { value1: 12, value2: 7 },\n { value1: 7, value2: 8 },\n { value1: 8, value2: 8 },\n { value1: 8, value2: 3 },\n { value1: 3, value2: 5 },\n { value1: 5, value2: 5 },\n { value1: 5, value2: 2 },\n { value1: 2, value2: 1 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'offset-zigzag',\n name: 'Offset zigzag',\n description: 'Alternating perpendicular tiles without doubles',\n angle: 0,\n dominoes: [\n { value1: 12, value2: 5 },\n { value1: 5, value2: 9 },\n { value1: 9, value2: 2 },\n { value1: 2, value2: 7 },\n { value1: 7, value2: 1 },\n ],\n layoutStyles: ['offset'],\n },\n {\n id: 'horizontal-open',\n name: 'Horizontal train',\n description: 'Rightward train: offset head, double, offset tail',\n angle: 0,\n dominoes: [\n { value1: 5, value2: 12 },\n { value1: 12, value2: 11 },\n { value1: 11, value2: 11 },\n { value1: 11, value2: 6 },\n { value1: 6, value2: 2 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'vertical-open',\n name: 'Vertical train',\n description: 'Downward train: offset head, double, offset tail',\n angle: 90,\n dominoes: [\n { value1: 3, value2: 12 },\n { value1: 12, value2: 10 },\n { value1: 10, value2: 10 },\n { value1: 10, value2: 4 },\n { value1: 4, value2: 1 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n];\n\nexport function getTrainFixture(id: string): TrainFixture | undefined {\n return TRAIN_FIXTURES.find((fixture) => fixture.id === id);\n}\n\nexport const CHICKEN_FOOT_FIXTURES: ChickenFootFixture[] = [\n {\n id: 'single-foot',\n name: 'Single foot',\n description:\n 'A double fans two angled toes (±45°) while the main line continues straight as the center toe',\n angle: 0,\n branch: {\n dominoes: [\n { value1: 12, value2: 6 },\n { value1: 6, value2: 6 },\n { value1: 6, value2: 3 },\n { value1: 3, value2: 1 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 6, value2: 2 },\n { value1: 2, value2: 5 },\n ],\n },\n {\n dominoes: [\n { value1: 6, value2: 4 },\n { value1: 4, value2: 0 },\n ],\n },\n ],\n },\n },\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'foot-no-center',\n name: 'Foot at the tail',\n description:\n 'Double ends the main line, so both side toes are present with no straight continuation',\n angle: 0,\n branch: {\n dominoes: [\n { value1: 9, value2: 7 },\n { value1: 7, value2: 7 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 7, value2: 3 },\n { value1: 3, value2: 8 },\n ],\n },\n {\n dominoes: [\n { value1: 7, value2: 5 },\n { value1: 5, value2: 0 },\n ],\n },\n ],\n },\n },\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'nested-foot',\n name: 'Nested foot',\n description:\n 'A side toe contains its own double, which sprouts a second-level foot',\n angle: 90,\n branch: {\n dominoes: [\n { value1: 12, value2: 8 },\n { value1: 8, value2: 8 },\n { value1: 8, value2: 3 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 8, value2: 5 },\n { value1: 5, value2: 5 },\n { value1: 5, value2: 2 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 5, value2: 9 },\n { value1: 9, value2: 1 },\n ],\n },\n {\n dominoes: [\n { value1: 5, value2: 4 },\n { value1: 4, value2: 6 },\n ],\n },\n ],\n },\n },\n {\n dominoes: [\n { value1: 8, value2: 1 },\n { value1: 1, value2: 7 },\n ],\n },\n ],\n },\n },\n layoutStyles: ['linear', 'offset'],\n },\n];\n\nexport function getChickenFootFixture(\n id: string\n): ChickenFootFixture | undefined {\n return CHICKEN_FOOT_FIXTURES.find((fixture) => fixture.id === id);\n}\n","/**\n * How a played double must be answered before play continues.\n * - `none`: doubles are ordinary tiles.\n * - `cover`: the double must be answered by one tile on its open end.\n * - `chicken-foot`: the double must grow a full foot (`chickenFoot.toeCount`\n * answers: the straight center continuation plus the angled side toes).\n */\nexport type DoubleObligation = 'none' | 'cover' | 'chicken-foot';\n\nexport interface ChickenFootConfig {\n /**\n * Total toes a double must grow to be satisfied, counting the straight center\n * continuation as one. Stays 3 by default (center + two ±angle side toes).\n */\n toeCount: number;\n /** Angles (degrees, relative to the train) of the side toes (toeCount - 1). */\n sideToeAngles: number[];\n}\n\n/**\n * Every knob that governs legal play. Pass a partial override to {@link resolveRules}\n * to get a fully-populated config; unspecified fields fall back to DEFAULT_RULES.\n */\nexport interface RulesConfig {\n /** Highest pip value in the set (12 → double-12, 91 tiles). */\n maxPips: number;\n /** Value of the starting engine double (defaults to maxPips). */\n engineValue: number;\n /** Forbid a double immediately following a double in a chain. */\n allowConsecutiveDoubles: boolean;\n /** Each physical tile may be placed at most once across all play. */\n requireUniqueTiles: boolean;\n /** A tile may only attach where one of its ends matches the open value. */\n requireSequential: boolean;\n /** Obligation imposed by playing a double. */\n doubleObligation: DoubleObligation;\n chickenFoot: ChickenFootConfig;\n}\n\nexport const DEFAULT_RULES: RulesConfig = {\n maxPips: 12,\n engineValue: 12,\n allowConsecutiveDoubles: false,\n requireUniqueTiles: true,\n requireSequential: true,\n doubleObligation: 'cover',\n chickenFoot: {\n toeCount: 3,\n sideToeAngles: [-45, 45],\n },\n};\n\n/** Number of answers a double needs to be satisfied under the given rules. */\nexport function requiredDoubleAnswers(config: RulesConfig): number {\n switch (config.doubleObligation) {\n case 'chicken-foot':\n return Math.max(1, config.chickenFoot.toeCount);\n case 'cover':\n return 1;\n case 'none':\n default:\n return 0;\n }\n}\n\n/** Number of angled side-toe slots a double exposes (center is the main line). */\nexport function sideToeSlots(config: RulesConfig): number {\n if (config.doubleObligation === 'chicken-foot') {\n return Math.max(0, config.chickenFoot.toeCount - 1);\n }\n // Outside chicken-foot, doubles do not branch; covers go on the main line.\n return 0;\n}\n\n/** Fills in any missing fields from DEFAULT_RULES (engineValue tracks maxPips). */\nexport function resolveRules(overrides: Partial<RulesConfig> = {}): RulesConfig {\n const maxPips = overrides.maxPips ?? DEFAULT_RULES.maxPips;\n return {\n ...DEFAULT_RULES,\n ...overrides,\n maxPips,\n engineValue: overrides.engineValue ?? maxPips,\n chickenFoot: {\n ...DEFAULT_RULES.chickenFoot,\n ...(overrides.chickenFoot ?? {}),\n },\n };\n}\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBranch } from '@/game/TrainData';\nimport {\n dominoKey,\n isDouble,\n orientForConnection,\n} from '@/rules/dominoSet';\nimport {\n RulesConfig,\n requiredDoubleAnswers,\n sideToeSlots,\n} from '@/rules/rulesConfig';\n\n/**\n * Locates a branch inside a chicken-foot tree. Empty path = the main line; each\n * step descends into the `toeIndex`-th side toe hanging off the double at\n * `doubleIndex` of the current branch.\n */\nexport type BranchPath = ReadonlyArray<{\n doubleIndex: number;\n toeIndex: number;\n}>;\n\nexport interface OpenEnd {\n path: BranchPath;\n attach: 'run-tail' | 'side-toe';\n /** Pip value a tile must match to attach here. */\n value: number;\n /** For side-toe ends: which double in the branch, and which toe slot. */\n doubleIndex?: number;\n toeSlot?: number;\n /** The tile being attached to is a double (for the no-consecutive rule). */\n attachToDouble: boolean;\n /** This end exists only because an unanswered double must be satisfied. */\n obligation: boolean;\n}\n\nexport interface Move {\n end: OpenEnd;\n tile: DominoValue;\n}\n\nexport type PlacementViolation =\n | 'value-mismatch'\n | 'duplicate-tile'\n | 'consecutive-doubles';\n\nexport interface PlacementResult {\n legal: boolean;\n violations: PlacementViolation[];\n}\n\nexport function getBranchAt(\n root: TrainBranch,\n path: BranchPath\n): TrainBranch | undefined {\n let current: TrainBranch | undefined = root;\n for (const step of path) {\n current = current?.feet?.[step.doubleIndex]?.[step.toeIndex];\n if (!current) return undefined;\n }\n return current;\n}\n\ninterface DoubleStatus {\n path: BranchPath;\n doubleIndex: number;\n value: number;\n hasCenter: boolean;\n sideToes: number;\n answers: number;\n}\n\nfunction walkBranches(\n branch: TrainBranch,\n path: BranchPath,\n visit: (branch: TrainBranch, path: BranchPath) => void\n): void {\n visit(branch, path);\n if (!branch.feet) return;\n for (const key of Object.keys(branch.feet)) {\n const doubleIndex = Number(key);\n branch.feet[doubleIndex].forEach((toe, toeIndex) => {\n walkBranches(toe, [...path, { doubleIndex, toeIndex }], visit);\n });\n }\n}\n\nfunction collectDoubles(root: TrainBranch): DoubleStatus[] {\n const out: DoubleStatus[] = [];\n walkBranches(root, [], (branch, path) => {\n branch.dominoes.forEach((domino, doubleIndex) => {\n if (domino.value1 !== domino.value2) return;\n const hasCenter = doubleIndex < branch.dominoes.length - 1;\n const sideToes = branch.feet?.[doubleIndex]?.length ?? 0;\n out.push({\n path,\n doubleIndex,\n value: domino.value1,\n hasCenter,\n sideToes,\n answers: (hasCenter ? 1 : 0) + sideToes,\n });\n });\n });\n return out;\n}\n\n/** Doubles that still owe answers under the current rules. */\nexport function getUnsatisfiedDoubles(\n root: TrainBranch,\n config: RulesConfig\n): DoubleStatus[] {\n const required = requiredDoubleAnswers(config);\n if (required <= 0) return [];\n return collectDoubles(root).filter((d) => d.answers < required);\n}\n\n/** Every key of every tile already placed in the tree (for uniqueness checks). */\nexport function collectPlayedKeys(root: TrainBranch): Set<string> {\n const keys = new Set<string>();\n walkBranches(root, [], (branch) => {\n for (const domino of branch.dominoes) {\n keys.add(dominoKey(domino));\n }\n });\n return keys;\n}\n\n/**\n * All places a tile may legally attach to this train, honoring double\n * obligations. When a double is unanswered (and the rules require answers),\n * only that double's open slots are offered until it is satisfied.\n */\nexport function getOpenEnds(\n root: TrainBranch,\n startValue: number,\n config: RulesConfig\n): OpenEnd[] {\n if (root.dominoes.length === 0) {\n return [\n {\n path: [],\n attach: 'run-tail',\n value: startValue,\n attachToDouble: true, // a train starts off the engine double\n obligation: false,\n },\n ];\n }\n\n const unsatisfied = getUnsatisfiedDoubles(root, config);\n\n if (config.doubleObligation !== 'none' && unsatisfied.length > 0) {\n const slots = sideToeSlots(config);\n const ends: OpenEnd[] = [];\n\n for (const status of unsatisfied) {\n const branch = getBranchAt(root, status.path);\n if (!branch) continue;\n\n // Center continuation: only available if the double is the run's tail.\n if (!status.hasCenter && status.doubleIndex === branch.dominoes.length - 1) {\n ends.push({\n path: status.path,\n attach: 'run-tail',\n value: status.value,\n attachToDouble: true,\n obligation: true,\n });\n }\n\n if (status.sideToes < slots) {\n ends.push({\n path: status.path,\n attach: 'side-toe',\n value: status.value,\n doubleIndex: status.doubleIndex,\n toeSlot: status.sideToes,\n attachToDouble: true,\n obligation: true,\n });\n }\n }\n\n return ends;\n }\n\n // No active obligation: the growing tip of every branch is open.\n const ends: OpenEnd[] = [];\n walkBranches(root, [], (branch, path) => {\n const last = branch.dominoes[branch.dominoes.length - 1];\n if (!last) return;\n ends.push({\n path,\n attach: 'run-tail',\n value: last.value2,\n attachToDouble: isDouble(last),\n obligation: false,\n });\n });\n return ends;\n}\n\nexport function evaluatePlacement(\n tile: DominoValue,\n end: OpenEnd,\n playedKeys: ReadonlySet<string>,\n config: RulesConfig\n): PlacementResult {\n const violations: PlacementViolation[] = [];\n\n const oriented = orientForConnection(tile, end.value);\n if (config.requireSequential && !oriented) {\n violations.push('value-mismatch');\n }\n\n if (config.requireUniqueTiles && playedKeys.has(dominoKey(tile))) {\n violations.push('duplicate-tile');\n }\n\n if (!config.allowConsecutiveDoubles && end.attachToDouble && isDouble(tile)) {\n violations.push('consecutive-doubles');\n }\n\n return { legal: violations.length === 0, violations };\n}\n\n/** Every legal (open end × hand tile) move for this train. */\nexport function getLegalMoves(\n root: TrainBranch,\n startValue: number,\n hand: readonly DominoValue[],\n playedKeys: ReadonlySet<string>,\n config: RulesConfig\n): Move[] {\n const ends = getOpenEnds(root, startValue, config);\n const moves: Move[] = [];\n for (const end of ends) {\n for (const tile of hand) {\n if (evaluatePlacement(tile, end, playedKeys, config).legal) {\n moves.push({ end, tile });\n }\n }\n }\n return moves;\n}\n\nfunction updateBranchAt(\n branch: TrainBranch,\n path: BranchPath,\n updater: (branch: TrainBranch) => TrainBranch\n): TrainBranch {\n if (path.length === 0) {\n return updater(branch);\n }\n const [step, ...rest] = path;\n const toes = branch.feet?.[step.doubleIndex] ?? [];\n const updatedToes = toes.map((toe, index) =>\n index === step.toeIndex ? updateBranchAt(toe, rest, updater) : toe\n );\n return {\n ...branch,\n feet: { ...branch.feet, [step.doubleIndex]: updatedToes },\n };\n}\n\n/**\n * Returns a new tree with `move` applied. The tile is oriented so its matching\n * end connects. Does not validate; call {@link evaluatePlacement} first (or use\n * {@link playMove}).\n */\nexport function applyMove(\n root: TrainBranch,\n move: Move,\n _config?: RulesConfig\n): TrainBranch {\n const oriented =\n orientForConnection(move.tile, move.end.value) ?? { ...move.tile };\n\n return updateBranchAt(root, move.end.path, (branch) => {\n if (move.end.attach === 'run-tail') {\n return { ...branch, dominoes: [...branch.dominoes, oriented] };\n }\n\n const doubleIndex = move.end.doubleIndex ?? 0;\n const slot = move.end.toeSlot ?? branch.feet?.[doubleIndex]?.length ?? 0;\n const existing = branch.feet?.[doubleIndex]\n ? [...branch.feet[doubleIndex]]\n : [];\n existing[slot] = { dominoes: [oriented] };\n return {\n ...branch,\n feet: { ...branch.feet, [doubleIndex]: existing },\n };\n });\n}\n\nexport interface PlayMoveResult {\n ok: boolean;\n board: TrainBranch;\n violations: PlacementViolation[];\n}\n\n/** Validates a move against the rules and applies it only if legal. */\nexport function playMove(\n root: TrainBranch,\n move: Move,\n config: RulesConfig\n): PlayMoveResult {\n const result = evaluatePlacement(\n move.tile,\n move.end,\n collectPlayedKeys(root),\n config\n );\n if (!result.legal) {\n return { ok: false, board: root, violations: result.violations };\n }\n return { ok: true, board: applyMove(root, move, config), violations: [] };\n}\n"],"names":["GRID_POSITIONS","resolvePipPosition","cell","grid","Pip","row","col","gridSize","color","hollow","top","left","positionStyle","jsx","PIP_LAYOUTS","getPipLayout","value","DEFAULT_PIP_COLORS","PIP_COLORS","mergePipColors","overrides","resolvePipStyle","pipColors","getPipStyle","pipProps","pipColor","style","PipPattern","layout","Fragment","index","DominoHalf","DoubleTwelve","value1","value2","width","height","backgroundColor","borderColor","rotation","val1","val2","jsxs","DOMINO_WIDTH","DOMINO_HEIGHT","CHICKEN_FOOT_TOE_ANGLES","halfExtentAlongTrain","isDouble","dominoWidth","dominoHeight","stepAlongTrain","fromIsDouble","toIsDouble","trainDirection","angle","angleRad","trainPerpendicular","dirX","dirY","orientDominoValues","dominoes","oriented","domino","i","prevValue","outwardPerpSign","nextPerpOffset","current","outwardSign","placeOrientedRun","orientedDominoes","startX","startY","layoutStyle","leadGap","hubIndex","perpX","perpY","isHub","laneByIndex","currentX","currentY","perpOffset","laneSign","perpStep","setPerpOffset","target","delta","prevIsDouble","shift","normalizeBends","bends","tileCount","byIndex","bend","turn","a","b","headingAtIndex","cleaned","heading","placeBentRun","input","boundaries","result","subStartX","subStartY","subLeadGap","s","slice","subHubIndex","sub","last","prevDir","halfPrev","nextFirst","nextIsDouble","halfNext","nextDir","nextPerp","targetX","targetY","seeds","seedX","seedY","computeTrainLayout","outwardSignInput","cleanedBends","tileCorners","entry","r","cos","sin","hw","hh","x","y","projectionGap","axis","aMin","aMax","bMin","bMax","p","d","tilesOverlap","epsilon","ca","cb","corners","q","ex","ey","len","overlapsAny","tile","others","other","layoutsCollide","obstacles","layoutSelfIntersects","j","TOE_PUSH_STEP","TOE_PUSH_MAX_STEPS","computeTrainTree","branch","depth","anchor","placed","pushAxis","minPushSteps","segmentOutward","buildLayout","originX","originY","appliedAnchor","k","trial","segments","key","hostIndex","host","toes","hostAngle","toeIndex","toe","toeOffset","sideSign","toeAngle","toePerp","outward","cornerX","cornerY","flattenSegments","segment","getTrainLayoutBounds","padding","halfExtent","minX","minY","maxX","maxY","DEFAULT_TOLERANCE","projectOnTrainAxis","dx","dy","projectOnPerpendicularAxis","validateDominoChain","issues","currentIsDouble","expectedAxisLayout","isDoubleArr","positions","along","perp","validateConsecutiveSpacing","tolerance","outwardSignOverride","expected","prev","expectedAlong","expectedPerp","validateNoPairOverlap","distance","minDistance","validateTrainLayout","validateChickenFootChain","walk","path","issue","first","validateTrainTree","segmentIndex","tiles","IS_DEV","DominoTrain","trainData","tableWidth","tableHeight","centerX","centerY","useEffect","trainLayout","useMemo","showMarker","hubTrainStartDistance","slots","radius","minNeighborGap","DominoHub","playerCount","engineValue","trains","hubSize","startDistance","_","radians","t","tileKey","dominoKey","tileHasValue","otherEnd","orientForConnection","connectingValue","generateDominoSet","maxPips","dominoSetSize","n","generateSampleTrains","options","usedTiles","playerId","dominoCount","openValue","prevWasDouble","pickNextValue","feet","buildFeet","doubleValue","buildToe","startValue","length","next","pickNonDouble","candidates","isFirstDomino","isValidNextValue","defaultGameState","MexicanTrainGame","initialState","pipColorsProp","onPipColorsChange","gameState","setGameState","useState","setLayoutStyle","chickenFeet","setChickenFeet","pipColorsInternal","setPipColorsInternal","setPipColors","pipColorsEnabled","regenerateTrains","chickenFeetEnabled","sampleTrains","prevState","toggleChickenFeet","TURN_DEGREES","sideToTurn","side","degrees","oppositeSide","offsetDefaultSide","linearDefaultSide","point","bounds","distanceToExit","sx","sy","rightRoom","leftRoom","buildBranchTiles","withBendAt","rest","resolveBend","build","preferredSide","candidateBranch","cycleBendAt","preferredTurn","oppositeTurn","isLegal","candidate","order","clampScale","scale","min","max","zoomAt","view","factor","pivot","effective","fitToBounds","content","viewport","safeW","safeH","raw","screenToContent","screen","PAN_THRESHOLD","Viewport","contentWidth","contentHeight","children","minScale","maxScale","zoomStep","background","showControls","testId","containerRef","useRef","fit","useCallback","setView","pan","localPoint","clientX","clientY","rect","onPointerDown","event","onPointerMove","endPan","onWheel","zoomButton","buttonStyle","TRAIN_FIXTURES","CHICKEN_FOOT_FIXTURES","DEFAULT_RULES","requiredDoubleAnswers","config","sideToeSlots","resolveRules","getBranchAt","root","step","walkBranches","visit","doubleIndex","collectDoubles","out","hasCenter","sideToes","getUnsatisfiedDoubles","required","collectPlayedKeys","keys","getOpenEnds","unsatisfied","ends","status","evaluatePlacement","end","playedKeys","violations","getLegalMoves","hand","moves","updateBranchAt","updater","updatedToes","applyMove","move","_config","slot","existing","playMove"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../src/app/dominoTheme.ts","../src/app/DominoThemeContext.tsx","../src/app/DefaultPip.tsx","../src/app/pipGrid.ts","../src/app/pipLayouts.ts","../src/app/pipColors.ts","../src/app/PipPattern.tsx","../src/app/DominoHalf.tsx","../src/app/DoubleTwelve.tsx","../src/app/trainLayout.ts","../src/harness/layoutValidation.ts","../src/app/DominoTrain.tsx","../src/app/DominoHub.tsx","../src/rules/dominoSet.ts","../src/game/generateSampleTrains.ts","../src/app/MexicanTrainGame.tsx","../src/app/trainBends.ts","../src/app/viewportMath.ts","../src/app/Viewport.tsx","../src/harness/trainFixtures.ts","../src/rules/rulesConfig.ts","../src/rules/placement.ts","../src/ai/types.ts","../src/ai/candidate-generator.ts","../src/ai/heuristics.ts","../src/ai/skill-profiles.ts","../src/ai/policy.ts","../src/ai/create-ai-player.ts","../src/ai/search.ts"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react';\n\nimport type { PipGridSize } from './pipGrid';\n\nexport interface PipRenderContext {\n value: number;\n row: number;\n col: number;\n gridSize: PipGridSize;\n color: string;\n hollow?: boolean;\n top?: string;\n left?: string;\n positionStyle: CSSProperties;\n}\n\nexport interface TileRenderContext {\n value1: number;\n value2: number;\n width: number;\n height: number;\n backgroundColor: string;\n borderColor: string;\n rotation: number;\n}\n\n/** Optional presentation hooks for domino tiles and pips. */\nexport interface DominoTheme {\n /** Root class on each tile — use for app-specific CSS modules. */\n tileClassName?: string;\n /** Extra data attributes for CSS selectors, e.g. holographic toggles. */\n tileDataAttributes?: Record<string, string | boolean | number | undefined>;\n tileStyle?: (ctx: TileRenderContext) => CSSProperties;\n halfDividerStyle?: (ctx: TileRenderContext) => CSSProperties;\n /** Merged onto each pip after layout positioning. */\n pipStyle?: (ctx: PipRenderContext) => CSSProperties;\n /** Replace the default pip element entirely. */\n renderPip?: (ctx: PipRenderContext) => ReactNode;\n}\n\nexport const DEFAULT_DOMINO_THEME: DominoTheme = {};\n\nexport function mergeDominoTheme(\n base: DominoTheme,\n patch?: DominoTheme\n): DominoTheme {\n if (!patch) {\n return base;\n }\n\n return {\n ...base,\n ...patch,\n tileDataAttributes: {\n ...base.tileDataAttributes,\n ...patch.tileDataAttributes,\n },\n };\n}\n\n/** Convert theme tileDataAttributes to React data-* props. */\nexport function themeDataAttributes(\n attrs?: DominoTheme['tileDataAttributes']\n): Record<string, string> {\n if (!attrs) {\n return {};\n }\n\n const result: Record<string, string> = {};\n for (const [key, value] of Object.entries(attrs)) {\n if (value === undefined || value === false) {\n continue;\n }\n result[`data-${key}`] = value === true ? 'true' : String(value);\n }\n return result;\n}\n","import {\n createContext,\n useContext,\n type FC,\n type ReactNode,\n} from 'react';\n\nimport {\n DEFAULT_DOMINO_THEME,\n mergeDominoTheme,\n type DominoTheme,\n} from './dominoTheme';\n\nconst DominoThemeContext = createContext<DominoTheme>(DEFAULT_DOMINO_THEME);\n\nexport interface DominoThemeProviderProps {\n theme?: DominoTheme;\n children: ReactNode;\n}\n\nexport const DominoThemeProvider: FC<DominoThemeProviderProps> = ({\n theme,\n children,\n}) => {\n const parent = useContext(DominoThemeContext);\n const resolved = mergeDominoTheme(parent, theme);\n\n return (\n <DominoThemeContext.Provider value={resolved}>\n {children}\n </DominoThemeContext.Provider>\n );\n};\n\nexport function useDominoTheme(override?: DominoTheme): DominoTheme {\n const fromContext = useContext(DominoThemeContext);\n return mergeDominoTheme(fromContext, override);\n}\n","import { FC } from 'react';\n\nimport type { PipRenderContext } from './dominoTheme';\nimport type { DominoTheme } from './dominoTheme';\n\nexport interface DefaultPipProps {\n ctx: PipRenderContext;\n theme?: DominoTheme;\n}\n\n/** Stock domino pip — solid or hollow circle using the resolved pip color. */\nexport const DefaultPip: FC<DefaultPipProps> = ({ ctx, theme }) => {\n const { row, col, gridSize, color, hollow, positionStyle } = ctx;\n const themed = theme?.pipStyle?.(ctx) ?? {};\n\n return (\n <div\n data-testid=\"pip\"\n data-row={row}\n data-col={col}\n data-grid={gridSize}\n data-pip-value={ctx.value}\n data-hollow={hollow ? 'true' : undefined}\n style={{\n position: 'absolute',\n borderRadius: '50%',\n transform: 'translate(-50%, -50%)',\n backgroundColor: hollow ? 'transparent' : color,\n border: hollow ? `2px solid ${color}` : undefined,\n boxShadow: hollow ? undefined : '1px 2px 3px rgba(0,0,0,0.3)',\n ...positionStyle,\n ...themed,\n }}\n />\n );\n};\n\nexport default DefaultPip;\n","export type PipGridSize = '3x3' | '3x4' | '4x3';\n\nexport interface PipLayoutCell {\n row: number;\n col: number;\n gridSize: PipGridSize;\n top?: string;\n left?: string;\n}\n\nconst GRID_POSITIONS: Record<\n PipGridSize,\n { rows: number[]; cols: number[]; size: string }\n> = {\n '3x3': { rows: [20, 50, 80], cols: [20, 50, 80], size: '18%' },\n '3x4': { rows: [24, 50, 76], cols: [22, 40, 60, 78], size: '12%' },\n '4x3': { rows: [15, 38, 62, 85], cols: [20, 50, 80], size: '14%' },\n};\n\nexport function resolvePipPosition(cell: PipLayoutCell): {\n top: string;\n left: string;\n width: string;\n height: string;\n} {\n const grid = GRID_POSITIONS[cell.gridSize];\n\n return {\n top: cell.top ?? `${grid.rows[cell.row]}%`,\n left: cell.left ?? `${grid.cols[cell.col]}%`,\n width: grid.size,\n height: grid.size,\n };\n}\n","import { PipLayoutCell } from './pipGrid';\n\n/** Canonical double-12 pip layouts (0–12). */\nexport const PIP_LAYOUTS: Record<number, readonly PipLayoutCell[]> = {\n 0: [],\n 1: [{ row: 1, col: 1, gridSize: '3x3' }],\n 2: [\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n ],\n 3: [\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n ],\n 4: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 5: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 6: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 7: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 8: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 1, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 1, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 9: [\n { row: 0, col: 0, gridSize: '3x3' },\n { row: 0, col: 1, gridSize: '3x3' },\n { row: 0, col: 2, gridSize: '3x3' },\n { row: 1, col: 0, gridSize: '3x3' },\n { row: 1, col: 1, gridSize: '3x3' },\n { row: 1, col: 2, gridSize: '3x3' },\n { row: 2, col: 0, gridSize: '3x3' },\n { row: 2, col: 1, gridSize: '3x3' },\n { row: 2, col: 2, gridSize: '3x3' },\n ],\n 10: [\n { row: 0, col: 0, gridSize: '3x4' },\n { row: 0, col: 1, gridSize: '3x4' },\n { row: 0, col: 2, gridSize: '3x4' },\n { row: 0, col: 3, gridSize: '3x4' },\n { row: 1, col: 0, gridSize: '3x4' },\n { row: 1, col: 3, gridSize: '3x4' },\n { row: 2, col: 0, gridSize: '3x4' },\n { row: 2, col: 1, gridSize: '3x4' },\n { row: 2, col: 2, gridSize: '3x4' },\n { row: 2, col: 3, gridSize: '3x4' },\n ],\n 11: [\n { row: 0, col: 0, gridSize: '4x3' },\n { row: 1, col: 0, gridSize: '4x3' },\n { row: 2, col: 0, gridSize: '4x3' },\n { row: 3, col: 0, gridSize: '4x3' },\n { row: 0, col: 1, gridSize: '4x3' },\n { row: 2, col: 1, gridSize: '4x3', top: '50%' },\n { row: 3, col: 1, gridSize: '4x3' },\n { row: 0, col: 2, gridSize: '4x3' },\n { row: 1, col: 2, gridSize: '4x3' },\n { row: 2, col: 2, gridSize: '4x3' },\n { row: 3, col: 2, gridSize: '4x3' },\n ],\n 12: [\n { row: 0, col: 0, gridSize: '4x3' },\n { row: 1, col: 0, gridSize: '4x3' },\n { row: 2, col: 0, gridSize: '4x3' },\n { row: 3, col: 0, gridSize: '4x3' },\n { row: 0, col: 1, gridSize: '4x3' },\n { row: 1, col: 1, gridSize: '4x3' },\n { row: 2, col: 1, gridSize: '4x3' },\n { row: 3, col: 1, gridSize: '4x3' },\n { row: 0, col: 2, gridSize: '4x3' },\n { row: 1, col: 2, gridSize: '4x3' },\n { row: 2, col: 2, gridSize: '4x3' },\n { row: 3, col: 2, gridSize: '4x3' },\n ],\n};\n\nexport function getPipLayout(value: number): readonly PipLayoutCell[] {\n return PIP_LAYOUTS[value] ?? [];\n}\n","export interface PipColorStyle {\n color: string;\n hollow?: boolean;\n}\n\n/** Partial map of domino values (0–12) to pip styles. */\nexport type PipColorMap = Partial<Record<number, PipColorStyle>>;\n\n/** Standard double-12 domino pip colors by value. */\nexport const DEFAULT_PIP_COLORS: PipColorMap = {\n 0: { color: 'transparent' },\n 1: { color: '#1a1a1a' },\n 2: { color: '#8B1A1A' },\n 3: { color: '#E6B800' },\n 4: { color: '#e8e8e8', hollow: true },\n 5: { color: '#2E8B57' },\n 6: { color: '#2563EB' },\n 7: { color: '#E8A87C' },\n 8: { color: '#DC2626' },\n 9: { color: '#1E3A8A' },\n 10: { color: '#EA580C' },\n 11: { color: '#166534' },\n 12: { color: '#DC2626' },\n};\n\n/** @deprecated Use DEFAULT_PIP_COLORS instead. */\nexport const PIP_COLORS = DEFAULT_PIP_COLORS;\n\n/** Merge custom overrides onto the default double-12 color set. */\nexport function mergePipColors(overrides?: PipColorMap): PipColorMap {\n return { ...DEFAULT_PIP_COLORS, ...overrides };\n}\n\n/** Resolve the pip style for a value when colored pips are enabled. */\nexport function resolvePipStyle(\n value: number,\n pipColors?: PipColorMap\n): PipColorStyle | undefined {\n if (pipColors === undefined) {\n return undefined;\n }\n\n return (\n pipColors[value] ??\n DEFAULT_PIP_COLORS[value] ?? { color: '#1a1a1a' }\n );\n}\n\n/** @deprecated Use resolvePipStyle instead. */\nexport function getPipStyle(value: number): PipColorStyle {\n return resolvePipStyle(value, DEFAULT_PIP_COLORS)!;\n}\n","import { FC } from 'react';\n\nimport { DefaultPip } from './DefaultPip';\nimport { useDominoTheme } from './DominoThemeContext';\nimport type { PipRenderContext } from './dominoTheme';\nimport { resolvePipPosition } from './pipGrid';\nimport { getPipLayout } from './pipLayouts';\nimport { PipColorMap, resolvePipStyle } from './pipColors';\n\ninterface PipPatternProps {\n value: number;\n pipColor: string;\n pipColors?: PipColorMap;\n}\n\nconst pipProps = (\n value: number,\n pipColor: string,\n pipColors?: PipColorMap\n): { color: string; hollow?: boolean } => {\n const style = resolvePipStyle(value, pipColors);\n if (style) {\n return { color: style.color, hollow: style.hollow };\n }\n return { color: pipColor };\n};\n\nexport const PipPattern: FC<PipPatternProps> = ({\n value,\n pipColor,\n pipColors,\n}) => {\n const theme = useDominoTheme();\n const { color, hollow } = pipProps(value, pipColor, pipColors);\n const layout = getPipLayout(value);\n\n return (\n <>\n {layout.map((cell, index) => {\n const ctx: PipRenderContext = {\n value,\n row: cell.row,\n col: cell.col,\n gridSize: cell.gridSize,\n color,\n hollow,\n top: cell.top,\n left: cell.left,\n positionStyle: resolvePipPosition(cell),\n };\n\n if (theme.renderPip) {\n return <span key={index}>{theme.renderPip(ctx)}</span>;\n }\n\n return <DefaultPip key={index} ctx={ctx} theme={theme} />;\n })}\n </>\n );\n};\n\nexport default PipPattern;\n","import { FC } from 'react';\nimport { PipPattern } from './PipPattern';\nimport { PipColorMap } from './pipColors';\n\ninterface DominoHalfProps {\n value: number;\n pipColor: string;\n pipColors?: PipColorMap;\n}\n\nexport const DominoHalf: FC<DominoHalfProps> = ({\n value,\n pipColor,\n pipColors,\n}) => {\n return (\n <div\n style={{\n width: '100%',\n height: '100%',\n position: 'relative',\n padding: '0',\n overflow: 'hidden',\n }}\n >\n <PipPattern value={value} pipColor={pipColor} pipColors={pipColors} />\n </div>\n );\n};\n\nexport default DominoHalf;\n","import { FC } from 'react';\n\nimport { useDominoTheme } from './DominoThemeContext';\nimport { DominoHalf } from './DominoHalf';\nimport type { DominoTheme, TileRenderContext } from './dominoTheme';\nimport { themeDataAttributes } from './dominoTheme';\nimport { PipColorMap } from './pipColors';\n\nexport interface DoubleTwelveProps {\n /** Pip count on the top half (0–12). Defaults to 0 (blank). */\n value1?: number;\n /** Pip count on the bottom half (0–12). Defaults to 0 (blank). */\n value2?: number;\n width?: number;\n height?: number;\n backgroundColor?: string;\n /** Fallback pip color when pipColors is not set. */\n pipColor?: string;\n /** Per-value pip colors. Pass DEFAULT_PIP_COLORS or a custom/merged map. */\n pipColors?: PipColorMap;\n borderColor?: string;\n rotation?: number;\n /** Presentation overrides — also available via DominoThemeProvider. */\n theme?: DominoTheme;\n}\n\nexport const DoubleTwelve: FC<DoubleTwelveProps> = ({\n value1 = 0,\n value2 = 0,\n width = 100,\n height = 200,\n backgroundColor = 'white',\n pipColor = 'black',\n pipColors,\n borderColor = 'black',\n rotation = 0,\n theme: themeOverride,\n}) => {\n const theme = useDominoTheme(themeOverride);\n const val1 = Math.min(Math.max(value1, 0), 12);\n const val2 = Math.min(Math.max(value2, 0), 12);\n\n const tileCtx: TileRenderContext = {\n value1: val1,\n value2: val2,\n width,\n height,\n backgroundColor,\n borderColor,\n rotation,\n };\n\n const tileThemed = theme.tileStyle?.(tileCtx) ?? {};\n const dividerThemed = theme.halfDividerStyle?.(tileCtx) ?? {};\n\n return (\n <div\n className={theme.tileClassName}\n {...themeDataAttributes(theme.tileDataAttributes)}\n style={{\n width: `${width}px`,\n height: `${height}px`,\n backgroundColor,\n borderColor,\n borderWidth: '1px',\n borderStyle: 'solid',\n borderRadius: '10px',\n transform: `rotate(${rotation}deg)`,\n transformOrigin: 'center center',\n boxShadow: '0 1px 2px rgba(0,0,0,0.2)',\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n ...tileThemed,\n }}\n >\n <div\n style={{\n flex: 1,\n position: 'relative',\n borderBottomWidth: '1px',\n borderBottomStyle: 'solid',\n borderBottomColor: borderColor,\n ...dividerThemed,\n }}\n >\n <DominoHalf value={val1} pipColor={pipColor} pipColors={pipColors} />\n </div>\n <div\n style={{\n flex: 1,\n position: 'relative',\n }}\n >\n <DominoHalf value={val2} pipColor={pipColor} pipColors={pipColors} />\n </div>\n </div>\n );\n};\n\nexport default DoubleTwelve;\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBend, TrainBranch } from '@/game/TrainData';\n\nexport const DOMINO_WIDTH = 60;\nexport const DOMINO_HEIGHT = 120;\n\n/**\n * Side-toe angles (degrees) relative to the branch direction for a chicken-foot\n * double. The 0° center toe is the straight main-line continuation and is not\n * listed here; these are the two angled toes that fan off the double's open end.\n */\nexport const CHICKEN_FOOT_TOE_ANGLES = [-45, 45] as const;\n\nexport type TrainLayoutStyle = 'offset' | 'linear';\n\nexport interface TrainLayoutEntry {\n x: number;\n y: number;\n rotation: number;\n isDouble: boolean;\n value1: number;\n value2: number;\n}\n\nexport interface ComputeTrainLayoutInput {\n startX: number;\n startY: number;\n angle: number;\n dominoes: readonly DominoValue[];\n layoutStyle: TrainLayoutStyle;\n dominoWidth?: number;\n dominoHeight?: number;\n /**\n * Distance from (startX, startY) to the center of the first tile, along the\n * train direction. Defaults to a small hub gap; chicken-foot toes pass half a\n * domino-height so the first toe tile butts against the host double's far end.\n */\n leadGap?: number;\n /**\n * Which side the offset zigzag seeds on (+1 / -1). Defaults to the natural\n * outward side for `angle`. Chicken-foot toes override this so each toe's\n * zigzag starts toward the outside of the foot, clear of the center row.\n */\n outwardSign?: number;\n /**\n * Offset mode only: index of a chicken-foot double that should act as a\n * centered hub. The double and the tile feeding into it are snapped onto the\n * train axis (perp 0) so the inbound tile reads as centered on the double and\n * the offset center toe fans out symmetrically — which lets the two angled\n * toes sit at equal, close distances on either side.\n */\n hubIndex?: number;\n /**\n * Pivots that fold this run's path into Ls, Us, or snakes. When present, the\n * run is split into straight sub-runs at each bend index and chained corner to\n * corner. Hub-centering is skipped (corners relax centering by design).\n */\n bends?: readonly TrainBend[];\n}\n\nexport function halfExtentAlongTrain(\n isDouble: boolean,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): number {\n return isDouble ? dominoWidth / 2 : dominoHeight / 2;\n}\n\nexport function stepAlongTrain(\n fromIsDouble: boolean,\n toIsDouble: boolean,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): number {\n return (\n halfExtentAlongTrain(fromIsDouble, dominoWidth, dominoHeight) +\n halfExtentAlongTrain(toIsDouble, dominoWidth, dominoHeight)\n );\n}\n\nexport function trainDirection(angle: number): { dirX: number; dirY: number } {\n const angleRad = (angle * Math.PI) / 180;\n return {\n dirX: Math.cos(angleRad),\n dirY: Math.sin(angleRad),\n };\n}\n\nexport function trainPerpendicular(angle: number): { perpX: number; perpY: number } {\n const { dirX, dirY } = trainDirection(angle);\n return { perpX: -dirY, perpY: dirX };\n}\n\n/**\n * Orients a value chain for rendering so each tile's connecting value (`value1`,\n * the near end) faces the previous tile. A tile is flipped only when it is\n * stored reversed (its `value2`, not `value1`, is the one that matches the\n * previous tile's open end). A correctly-stored chain is left untouched, and\n * doubles are never flipped. This is identical for linear and offset layouts —\n * the connection rule doesn't depend on spacing.\n */\nexport function orientDominoValues(dominoes: DominoValue[]): DominoValue[] {\n const oriented = dominoes.map((domino) => ({ ...domino }));\n\n for (let i = 1; i < oriented.length; i++) {\n const domino = oriented[i];\n const prevValue = oriented[i - 1].value2;\n const isDouble = domino.value1 === domino.value2;\n\n if (!isDouble && domino.value1 !== prevValue && domino.value2 === prevValue) {\n oriented[i] = { value1: domino.value2, value2: domino.value1 };\n }\n }\n\n return oriented;\n}\n\nexport function outwardPerpSign(angle: number): number {\n const { dirX, dirY } = trainDirection(angle);\n\n if (Math.abs(dirX) >= Math.abs(dirY)) {\n return dirX >= 0 ? 1 : -1;\n }\n\n return dirY >= 0 ? 1 : -1;\n}\n\nexport function nextPerpOffset(current: number, outwardSign: number): number {\n if (current === 0) {\n return outwardSign;\n }\n\n return current === outwardSign ? -outwardSign : outwardSign;\n}\n\ninterface PlaceOrientedRunInput {\n orientedDominoes: readonly DominoValue[];\n startX: number;\n startY: number;\n angle: number;\n layoutStyle: TrainLayoutStyle;\n dominoWidth: number;\n dominoHeight: number;\n leadGap: number;\n outwardSign: number;\n hubIndex?: number;\n}\n\n/**\n * Places an already value-oriented run of dominoes along a single straight\n * heading. This is the geometric core shared by straight runs and by each\n * sub-run of a bent (folded) path; it never re-orients tiles, so callers that\n * split a run at bends can orient the whole value-chain once and still keep\n * like-values touching across every corner.\n */\nfunction placeOrientedRun({\n orientedDominoes,\n startX,\n startY,\n angle,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign,\n hubIndex,\n}: PlaceOrientedRunInput): TrainLayoutEntry[] {\n const layout: TrainLayoutEntry[] = [];\n const { dirX, dirY } = trainDirection(angle);\n const { perpX, perpY } = trainPerpendicular(angle);\n const isHub = layoutStyle === 'offset' && hubIndex != null;\n // Lane (perpStep units) of each placed tile, used to recenter the inbound run\n // onto the hub double afterward.\n const laneByIndex: number[] = [];\n\n let currentX = startX + dirX * leadGap;\n let currentY = startY + dirY * leadGap;\n let perpOffset = 0;\n // Lane (in perpStep units) of the current tile. Regular tiles brick by\n // flipping lanes; a double stays in the lane of the tile it connects to, and\n // the tile coming out of the double stays in that lane too — so the run out\n // of a double mirrors the run into it and the train holds two fixed rows.\n let laneSign = 0;\n\n // Regular tiles alternate half a domino-width to each side of the centerline\n // so the two rows touch along the spine (no gap) and interlock cleanly.\n const perpStep = dominoWidth / 2;\n\n // perpOffset is the current net perpendicular position in units of perpStep.\n // Moving to a new lane steps by the delta.\n const setPerpOffset = (target: number) => {\n const delta = (target - perpOffset) * perpStep;\n currentX += perpX * delta;\n currentY += perpY * delta;\n perpOffset = target;\n };\n\n for (let i = 0; i < orientedDominoes.length; i++) {\n const domino = orientedDominoes[i];\n const isDouble = domino.value1 === domino.value2;\n const prevIsDouble =\n i > 0 &&\n orientedDominoes[i - 1].value1 === orientedDominoes[i - 1].value2;\n\n if (layoutStyle === 'linear') {\n if (i > 0) {\n if (isDouble) {\n currentX += dirX * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n } else if (prevIsDouble) {\n currentX += dirX * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n } else {\n currentX += dirX * dominoHeight;\n currentY += dirY * dominoHeight;\n }\n }\n } else if (isDouble) {\n // A double aligns with the tile it connects to: it stays in the current\n // lane (no perpendicular move) and only advances along the train.\n if (i > 0) {\n currentX += dirX * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n }\n } else {\n // Regular tile.\n if (i === 0) {\n laneSign = outwardSign;\n } else if (prevIsDouble) {\n // First tile out of a double: stay in the double's lane (centered on\n // it), so the outro mirrors the intro. Advance along only.\n currentX += dirX * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n currentY += dirY * stepAlongTrain(true, false, dominoWidth, dominoHeight);\n } else {\n // Brick against the previous regular: flip lanes, overlap 50% along.\n currentX += dirX * (dominoHeight / 2);\n currentY += dirY * (dominoHeight / 2);\n laneSign = nextPerpOffset(laneSign, outwardSign);\n }\n\n setPerpOffset(laneSign);\n }\n\n laneByIndex.push(perpOffset);\n\n layout.push({\n x: currentX,\n y: currentY,\n rotation: isDouble ? angle + 180 : angle - 90,\n isDouble,\n value1: domino.value1,\n value2: domino.value2,\n });\n }\n\n // Center the hub double on the train axis by rigidly sliding the whole run\n // perpendicular. Because the inbound tile, the double, and the outgoing tile\n // all share the double's lane, this lands all three on the axis (each reads as\n // centered on the double) while the offset zigzag — and the no-overlap\n // guarantee of the original lattice — is preserved. The center toe still fans\n // off-axis, so the two angled toes end up symmetric and close on either side.\n if (isHub && hubIndex != null) {\n const shift = -laneByIndex[hubIndex] * perpStep;\n if (shift !== 0) {\n for (let i = 0; i < layout.length; i++) {\n layout[i] = {\n ...layout[i],\n x: layout[i].x + perpX * shift,\n y: layout[i].y + perpY * shift,\n };\n }\n }\n }\n\n return layout;\n}\n\n/**\n * Normalizes a run's bends: integer indices strictly inside the run, one per\n * index (last wins), sorted. Index 0 is dropped — a run can't bend before its\n * first tile. Returns the cleaned, sorted list.\n */\nexport function normalizeBends(\n bends: readonly TrainBend[] | undefined,\n tileCount: number\n): TrainBend[] {\n if (!bends || bends.length === 0) return [];\n const byIndex = new Map<number, number>();\n for (const bend of bends) {\n if (!Number.isInteger(bend.index)) continue;\n if (bend.index <= 0 || bend.index >= tileCount) continue;\n byIndex.set(bend.index, bend.turn);\n }\n return [...byIndex.entries()]\n .map(([index, turn]) => ({ index, turn }))\n .sort((a, b) => a.index - b.index);\n}\n\n/**\n * Local heading (degrees) of the tile at `index` in a (possibly bent) run: the\n * base `angle` plus every bend turn at or before that index. With no bends this\n * is just `angle`. Used to anchor chicken-foot toes off a double's *actual*\n * heading when the double sits in a turned section of the path.\n */\nexport function headingAtIndex(\n angle: number,\n bends: readonly TrainBend[] | undefined,\n index: number,\n tileCount = Infinity\n): number {\n const cleaned = normalizeBends(bends, Number.isFinite(tileCount) ? tileCount : index + 1);\n let heading = angle;\n for (const bend of cleaned) {\n if (bend.index <= index) heading += bend.turn;\n else break;\n }\n return heading;\n}\n\n/**\n * Lays out a run that folds at one or more bends. The whole value-chain is\n * oriented once (so like-values keep touching), then split into straight\n * sub-runs at each bend index. Each post-bend sub-run is anchored at the open\n * end of the previous sub-run's last tile and turned by the bend's angle.\n *\n * Corner handling: a perpendicular tile butted straight onto the prior tile's\n * end would overlap it by a quarter-tile, so each post-bend sub-run is nudged\n * half a tile-width along the *previous* heading. That converts the would-be\n * overlap into a clean edge/point touch at the corner while the connecting pips\n * still meet. Centering relaxes at corners (tiles bunch) — by design.\n */\nfunction placeBentRun(\n orientedDominoes: readonly DominoValue[],\n input: Required<\n Pick<\n ComputeTrainLayoutInput,\n 'startX' | 'startY' | 'angle' | 'layoutStyle' | 'dominoWidth' | 'dominoHeight' | 'leadGap' | 'outwardSign'\n >\n >,\n bends: readonly TrainBend[],\n hubIndex?: number\n): TrainLayoutEntry[] {\n const { startX, startY, angle, layoutStyle, dominoWidth, dominoHeight, leadGap, outwardSign } = input;\n const boundaries = [0, ...bends.map((b) => b.index), orientedDominoes.length];\n\n const result: TrainLayoutEntry[] = [];\n let heading = angle;\n let subStartX = startX;\n let subStartY = startY;\n let subLeadGap = leadGap;\n\n for (let s = 0; s < boundaries.length - 1; s++) {\n const slice = orientedDominoes.slice(boundaries[s], boundaries[s + 1]);\n if (slice.length === 0) continue;\n\n // Keep the hub double centered as long as it lives in the first (pre-bend)\n // sub-run: this preserves the straight-run vertical position so a bend\n // elsewhere doesn't shift the whole train and spuriously collide. Later\n // sub-runs chain off this centered run, so they follow along.\n const subHubIndex =\n s === 0 && hubIndex != null && hubIndex < boundaries[1] ? hubIndex : undefined;\n\n const sub = placeOrientedRun({\n orientedDominoes: slice,\n startX: subStartX,\n startY: subStartY,\n angle: heading,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap: subLeadGap,\n outwardSign,\n hubIndex: subHubIndex,\n });\n result.push(...sub);\n\n const isLastSub = s >= boundaries.length - 2;\n if (isLastSub) break;\n\n // Chain the next sub-run off this one's open end, turned by the bend angle.\n const last = sub[sub.length - 1];\n const prevDir = trainDirection(heading);\n const halfPrev = halfExtentAlongTrain(last.isDouble, dominoWidth, dominoHeight);\n\n heading += bends[s].turn;\n const nextFirst = orientedDominoes[boundaries[s + 1]];\n const nextIsDouble = nextFirst.value1 === nextFirst.value2;\n const halfNext = halfExtentAlongTrain(nextIsDouble, dominoWidth, dominoHeight);\n const nextDir = trainDirection(heading);\n const nextPerp = trainPerpendicular(heading);\n const perpStep = dominoWidth / 2;\n\n // Land the next sub-run's first tile so its connecting half sits edge-flush\n // against the previous tile's open half — a clean L corner where the matching\n // pips fully touch. Pull a half-width back along the previous heading (into\n // the corner) and advance an extra half-width along the new heading so the\n // turning tile clears the previous tile's body instead of overlapping it.\n const targetX =\n last.x + prevDir.dirX * (halfPrev - perpStep) + nextDir.dirX * (halfNext + perpStep);\n const targetY =\n last.y + prevDir.dirY * (halfPrev - perpStep) + nextDir.dirY * (halfNext + perpStep);\n\n // In offset mode placeOrientedRun seeds a regular first tile a half-width\n // into its outward lane; pre-cancel that so the tile lands exactly on the\n // target. Linear runs and doubles get no perpendicular seed.\n const seeds = layoutStyle === 'offset' && !nextIsDouble;\n const seedX = seeds ? nextPerp.perpX * perpStep * outwardSign : 0;\n const seedY = seeds ? nextPerp.perpY * perpStep * outwardSign : 0;\n\n subStartX = targetX - seedX;\n subStartY = targetY - seedY;\n subLeadGap = 0;\n }\n\n return result;\n}\n\nexport function computeTrainLayout({\n startX,\n startY,\n angle,\n dominoes,\n layoutStyle,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT,\n leadGap = dominoHeight * 0.3,\n outwardSign: outwardSignInput,\n hubIndex,\n bends,\n}: ComputeTrainLayoutInput): TrainLayoutEntry[] {\n const orientedDominoes = orientDominoValues([...dominoes]);\n const outwardSign = outwardSignInput ?? outwardPerpSign(angle);\n\n const cleanedBends = normalizeBends(bends, orientedDominoes.length);\n if (cleanedBends.length > 0) {\n // Hub-centering still applies to the pre-bend sub-run (so a bend doesn't\n // shift the whole train); corners past it relax centering by design.\n return placeBentRun(\n orientedDominoes,\n {\n startX,\n startY,\n angle,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign,\n },\n cleanedBends,\n hubIndex\n );\n }\n\n return placeOrientedRun({\n orientedDominoes,\n startX,\n startY,\n angle,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign,\n hubIndex,\n });\n}\n\n/** The four world-space corners of a tile (its rotated rectangle). */\nexport function tileCorners(\n entry: TrainLayoutEntry,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): Array<{ x: number; y: number }> {\n const r = (entry.rotation * Math.PI) / 180;\n const cos = Math.cos(r);\n const sin = Math.sin(r);\n const hw = dominoWidth / 2;\n const hh = dominoHeight / 2;\n return [\n [-hw, -hh],\n [hw, -hh],\n [hw, hh],\n [-hw, hh],\n ].map(([x, y]) => ({\n x: entry.x + x * cos - y * sin,\n y: entry.y + x * sin + y * cos,\n }));\n}\n\nfunction projectionGap(\n a: Array<{ x: number; y: number }>,\n b: Array<{ x: number; y: number }>,\n axis: { x: number; y: number }\n): number {\n let aMin = Infinity;\n let aMax = -Infinity;\n let bMin = Infinity;\n let bMax = -Infinity;\n for (const p of a) {\n const d = p.x * axis.x + p.y * axis.y;\n aMin = Math.min(aMin, d);\n aMax = Math.max(aMax, d);\n }\n for (const p of b) {\n const d = p.x * axis.x + p.y * axis.y;\n bMin = Math.min(bMin, d);\n bMax = Math.max(bMax, d);\n }\n return Math.min(aMax, bMax) - Math.max(aMin, bMin);\n}\n\n/**\n * True when two tiles physically overlap (separating-axis test on their rotated\n * rectangles). Tiles that merely touch (within `epsilon`) are not overlapping,\n * so legitimately adjacent dominoes — bricked, end-to-end, or butted against a\n * double — pass cleanly while real collisions are caught.\n */\nexport function tilesOverlap(\n a: TrainLayoutEntry,\n b: TrainLayoutEntry,\n epsilon = 1,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): boolean {\n const ca = tileCorners(a, dominoWidth, dominoHeight);\n const cb = tileCorners(b, dominoWidth, dominoHeight);\n for (const corners of [ca, cb]) {\n for (let i = 0; i < 4; i++) {\n const p = corners[i];\n const q = corners[(i + 1) % 4];\n const ex = q.x - p.x;\n const ey = q.y - p.y;\n const len = Math.hypot(ex, ey) || 1;\n const axis = { x: -ey / len, y: ex / len };\n if (projectionGap(ca, cb, axis) <= epsilon) {\n return false;\n }\n }\n }\n return true;\n}\n\nfunction overlapsAny(\n tile: TrainLayoutEntry,\n others: readonly TrainLayoutEntry[],\n dominoWidth: number,\n dominoHeight: number\n): boolean {\n return others.some((other) =>\n tilesOverlap(tile, other, 1, dominoWidth, dominoHeight)\n );\n}\n\n/**\n * True when any tile of `layout` overlaps any tile in `obstacles` — i.e. this\n * path would physically intersect another path. Used to forbid a bend that\n * would cross another train.\n */\nexport function layoutsCollide(\n layout: readonly TrainLayoutEntry[],\n obstacles: readonly TrainLayoutEntry[],\n epsilon = 1,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): boolean {\n return layout.some((tile) =>\n obstacles.some((other) =>\n tilesOverlap(tile, other, epsilon, dominoWidth, dominoHeight)\n )\n );\n}\n\n/**\n * True when a path crosses itself — any two of its own tiles overlap. Adjacent\n * tiles that merely touch are fine (tilesOverlap ignores contact), so this only\n * fires when a fold (e.g. a too-tight U-turn) makes the path collide with itself.\n */\nexport function layoutSelfIntersects(\n layout: readonly TrainLayoutEntry[],\n epsilon = 1,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): boolean {\n for (let i = 0; i < layout.length; i++) {\n for (let j = i + 1; j < layout.length; j++) {\n if (tilesOverlap(layout[i], layout[j], epsilon, dominoWidth, dominoHeight)) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * A single straight run of dominoes within a chicken-foot tree: the main line\n * or one toe. `depth` is 0 for the main line, 1 for its toes, and so on.\n */\nexport interface TrainSegment {\n angle: number;\n depth: number;\n layoutStyle: TrainLayoutStyle;\n /** Outward side this segment's zigzag seeds on (needed to re-derive layout). */\n outwardSign: number;\n dominoes: readonly DominoValue[];\n layout: TrainLayoutEntry[];\n /** Anchor point this segment hangs off (host double's open end), if any. */\n anchor?: { x: number; y: number };\n}\n\nexport interface ComputeTrainTreeInput {\n startX: number;\n startY: number;\n angle: number;\n branch: TrainBranch;\n layoutStyle: TrainLayoutStyle;\n dominoWidth?: number;\n dominoHeight?: number;\n leadGap?: number;\n depth?: number;\n anchor?: { x: number; y: number };\n outwardSign?: number;\n /**\n * Accumulator of every tile already placed in the tree. Toes are nudged\n * outward until they clear everything in here, so no two dominoes overlap.\n * Callers normally omit this; the recursion threads it through.\n */\n placed?: TrainLayoutEntry[];\n /**\n * Unit direction a toe may be nudged along (outward, parallel to the host\n * double's open edge) to resolve overlaps. The trunk passes none.\n */\n pushAxis?: { x: number; y: number };\n /**\n * Minimum number of nudge steps to apply before checking for clearance. Both\n * toes of a foot share this so they stay symmetric about the double even when\n * only one side is crowded by the offset center toe.\n */\n minPushSteps?: number;\n}\n\n/** Outward nudge increment and cap used to space chicken-foot toes apart. */\nconst TOE_PUSH_STEP = DOMINO_WIDTH / 4;\nconst TOE_PUSH_MAX_STEPS = 24;\n\n/**\n * Lays out a branch and, recursively, the chicken-foot side toes hanging off any\n * of its doubles. Returns a flat list of segments (main line first, then toes in\n * depth-first order) so callers can render every tile and validate each run.\n */\nexport function computeTrainTree({\n startX,\n startY,\n angle,\n branch,\n layoutStyle,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT,\n leadGap,\n depth = 0,\n anchor,\n outwardSign,\n placed = [],\n pushAxis,\n minPushSteps = 0,\n}: ComputeTrainTreeInput): TrainSegment[] {\n const segmentOutward = outwardSign ?? outwardPerpSign(angle);\n\n // The first double that sprouts a foot becomes a centered hub so its inbound\n // tile reads centered and its two angled toes stay symmetric.\n const hubIndex = branch.feet\n ? Object.keys(branch.feet)\n .map(Number)\n .filter((index) => {\n const tile = branch.dominoes[index];\n return tile && tile.value1 === tile.value2;\n })\n .sort((a, b) => a - b)[0]\n : undefined;\n\n const buildLayout = (originX: number, originY: number) =>\n computeTrainLayout({\n startX: originX,\n startY: originY,\n angle,\n dominoes: branch.dominoes,\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign: segmentOutward,\n hubIndex,\n bends: branch.bends,\n });\n\n // Nudge this run outward (only toes get a pushAxis) until none of its tiles\n // overlap anything already placed, so dominoes never sit on top of each other.\n // Start from minPushSteps so a foot's two toes share a nudge and stay symmetric.\n let layout = buildLayout(\n startX + (pushAxis?.x ?? 0) * TOE_PUSH_STEP * minPushSteps,\n startY + (pushAxis?.y ?? 0) * TOE_PUSH_STEP * minPushSteps\n );\n let appliedAnchor =\n anchor && pushAxis\n ? {\n x: anchor.x + pushAxis.x * TOE_PUSH_STEP * minPushSteps,\n y: anchor.y + pushAxis.y * TOE_PUSH_STEP * minPushSteps,\n }\n : anchor;\n if (pushAxis && placed.length > 0) {\n for (let k = minPushSteps; k <= TOE_PUSH_MAX_STEPS; k++) {\n const originX = startX + pushAxis.x * TOE_PUSH_STEP * k;\n const originY = startY + pushAxis.y * TOE_PUSH_STEP * k;\n const trial = buildLayout(originX, originY);\n const clear = !trial.some((tile) =>\n overlapsAny(tile, placed, dominoWidth, dominoHeight)\n );\n if (clear || k === TOE_PUSH_MAX_STEPS) {\n layout = trial;\n appliedAnchor = anchor\n ? {\n x: anchor.x + pushAxis.x * TOE_PUSH_STEP * k,\n y: anchor.y + pushAxis.y * TOE_PUSH_STEP * k,\n }\n : anchor;\n break;\n }\n }\n }\n\n placed.push(...layout);\n\n const segments: TrainSegment[] = [\n {\n angle,\n depth,\n layoutStyle,\n outwardSign: segmentOutward,\n dominoes: branch.dominoes,\n layout,\n anchor: appliedAnchor,\n },\n ];\n\n if (branch.feet) {\n const perpStep = dominoWidth / 2;\n const leadGap = dominoHeight / 2;\n\n for (const key of Object.keys(branch.feet)) {\n const hostIndex = Number(key);\n const host = layout[hostIndex];\n const toes = branch.feet[hostIndex];\n if (!host || !host.isDouble || !toes) {\n continue;\n }\n\n // Anchor toes off the double's LOCAL heading so a double inside a bent\n // section fans its toes relative to the turned path, not the base angle.\n const hostAngle = headingAtIndex(\n angle,\n branch.bends,\n hostIndex,\n branch.dominoes.length\n );\n const { dirX, dirY } = trainDirection(hostAngle);\n const { perpX, perpY } = trainPerpendicular(hostAngle);\n\n for (let toeIndex = 0; toeIndex < toes.length; toeIndex++) {\n const toe = toes[toeIndex];\n const toeOffset = CHICKEN_FOOT_TOE_ANGLES[toeIndex] ?? 0;\n const sideSign = Math.sign(toeOffset);\n const toeAngle = hostAngle + toeOffset;\n const toePerp = trainPerpendicular(toeAngle);\n // Seed the zigzag on the toe's INNER lane so each toe splays outward\n // (away from the center toe) as it extends rather than curling in.\n const outward = -sideSign;\n\n // The double's open corner on this toe's side: half a domino-width out\n // along the train, half a domino-height across to the corner.\n const cornerX =\n host.x + dirX * (dominoWidth / 2) + perpX * (dominoHeight / 2) * sideSign;\n const cornerY =\n host.y + dirY * (dominoWidth / 2) + perpY * (dominoHeight / 2) * sideSign;\n\n // Snug placement: the first toe tile butts its inner edge midpoint\n // against that corner (its center lands at corner + toeDir * leadGap).\n // Pick the origin so the offset seed cancels and that lands exactly.\n const originX = cornerX - toePerp.perpX * outward * perpStep;\n const originY = cornerY - toePerp.perpY * outward * perpStep;\n\n segments.push(\n ...computeTrainTree({\n startX: originX,\n startY: originY,\n angle: toeAngle,\n branch: toe,\n // Toes inherit the main style so they zigzag in offset mode.\n layoutStyle,\n dominoWidth,\n dominoHeight,\n leadGap,\n outwardSign: outward,\n depth: depth + 1,\n anchor: { x: originX, y: originY },\n placed,\n // If the snug spot is still blocked (the offset center toe leans into\n // one side), slide this toe along the double's open edge, away from\n // center, until it clears. This keeps it butted against the double\n // while stepping past the obstacle — independent per toe, so a foot\n // ends up snug and only as asymmetric as the obstruction requires.\n pushAxis: { x: perpX * sideSign, y: perpY * sideSign },\n })\n );\n }\n }\n }\n\n return segments;\n}\n\n/** Flattens a list of segments into a single list of tiles for rendering. */\nexport function flattenSegments(\n segments: readonly TrainSegment[]\n): TrainLayoutEntry[] {\n return segments.flatMap((segment) => segment.layout);\n}\n\nexport interface TrainLayoutBounds {\n width: number;\n height: number;\n offsetX: number;\n offsetY: number;\n}\n\n/** Bounding box for rendering a train layout on a felt canvas. */\nexport function getTrainLayoutBounds(\n layout: readonly TrainLayoutEntry[],\n padding = 24,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): TrainLayoutBounds {\n const halfExtent = Math.hypot(dominoWidth, dominoHeight) / 2;\n\n if (layout.length === 0) {\n return {\n width: padding * 2 + dominoWidth,\n height: padding * 2 + dominoHeight,\n offsetX: padding,\n offsetY: padding,\n };\n }\n\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n\n for (const entry of layout) {\n minX = Math.min(minX, entry.x - halfExtent);\n minY = Math.min(minY, entry.y - halfExtent);\n maxX = Math.max(maxX, entry.x + halfExtent);\n maxY = Math.max(maxY, entry.y + halfExtent);\n }\n\n return {\n width: Math.ceil(maxX - minX + padding * 2),\n height: Math.ceil(maxY - minY + padding * 2),\n offsetX: padding - minX,\n offsetY: padding - minY,\n };\n}\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBranch } from '@/game/TrainData';\nimport {\n DOMINO_HEIGHT,\n DOMINO_WIDTH,\n TrainLayoutEntry,\n TrainLayoutStyle,\n TrainSegment,\n nextPerpOffset,\n outwardPerpSign,\n stepAlongTrain,\n tilesOverlap,\n trainDirection,\n trainPerpendicular,\n} from '@/app/trainLayout';\n\nexport interface LayoutValidationIssue {\n code: string;\n message: string;\n index?: number;\n}\n\nexport interface LayoutValidationResult {\n valid: boolean;\n issues: LayoutValidationIssue[];\n}\n\nconst DEFAULT_TOLERANCE = 1;\n\nexport function projectOnTrainAxis(\n dx: number,\n dy: number,\n angle: number\n): number {\n const { dirX, dirY } = trainDirection(angle);\n return dx * dirX + dy * dirY;\n}\n\nexport function projectOnPerpendicularAxis(\n dx: number,\n dy: number,\n angle: number\n): number {\n const { perpX, perpY } = trainPerpendicular(angle);\n return dx * perpX + dy * perpY;\n}\n\nexport function validateDominoChain(dominoes: readonly DominoValue[]): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n\n for (let i = 1; i < dominoes.length; i++) {\n if (dominoes[i].value1 !== dominoes[i - 1].value2) {\n issues.push({\n code: 'chain-break',\n message: `Domino ${i} does not connect to domino ${i - 1}`,\n index: i,\n });\n }\n }\n\n for (let i = 1; i < dominoes.length; i++) {\n const prevIsDouble = dominoes[i - 1].value1 === dominoes[i - 1].value2;\n const currentIsDouble = dominoes[i].value1 === dominoes[i].value2;\n if (prevIsDouble && currentIsDouble) {\n issues.push({\n code: 'consecutive-doubles',\n message: `Consecutive doubles at index ${i - 1} and ${i}`,\n index: i,\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport interface AxisPosition {\n along: number;\n perp: number;\n}\n\n/**\n * Reconstructs the expected position of every tile in train-axis space\n * (along the train and perpendicular to it), mirroring computeTrainLayout.\n * Positions are relative to the first tile, so only deltas are meaningful.\n */\nexport function expectedAxisLayout(\n layout: readonly TrainLayoutEntry[],\n layoutStyle: TrainLayoutStyle,\n outwardSign: number,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): AxisPosition[] {\n const perpStep = dominoWidth / 2;\n const isDoubleArr = layout.map((entry) => entry.isDouble);\n\n const positions: AxisPosition[] = [];\n let along = 0;\n let perp = 0;\n let laneSign = 0;\n\n for (let i = 0; i < layout.length; i++) {\n const isDouble = isDoubleArr[i];\n const prevIsDouble = i > 0 && isDoubleArr[i - 1];\n\n if (layoutStyle === 'linear') {\n if (i > 0) {\n along += stepAlongTrain(prevIsDouble, isDouble, dominoWidth, dominoHeight);\n }\n perp = 0;\n } else if (isDouble) {\n // Double stays in the current lane (aligned with the tile it connects to).\n if (i > 0) {\n along += stepAlongTrain(prevIsDouble, true, dominoWidth, dominoHeight);\n }\n // perp unchanged\n } else if (i === 0) {\n laneSign = outwardSign;\n perp = laneSign * perpStep;\n } else if (prevIsDouble) {\n // First tile out of a double stays in the double's lane (mirror).\n along += stepAlongTrain(true, false, dominoWidth, dominoHeight);\n // perp unchanged\n } else {\n along += dominoHeight / 2;\n laneSign = nextPerpOffset(laneSign, outwardSign);\n perp = laneSign * perpStep;\n }\n\n positions.push({ along, perp });\n }\n\n return positions;\n}\n\nexport function validateConsecutiveSpacing(\n layout: readonly TrainLayoutEntry[],\n angle: number,\n layoutStyle: TrainLayoutStyle,\n tolerance = DEFAULT_TOLERANCE,\n outwardSignOverride?: number\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n const outwardSign = outwardSignOverride ?? outwardPerpSign(angle);\n const expected = expectedAxisLayout(layout, layoutStyle, outwardSign);\n\n for (let i = 1; i < layout.length; i++) {\n const prev = layout[i - 1];\n const current = layout[i];\n const dx = current.x - prev.x;\n const dy = current.y - prev.y;\n const along = projectOnTrainAxis(dx, dy, angle);\n const perp = projectOnPerpendicularAxis(dx, dy, angle);\n const expectedAlong = expected[i].along - expected[i - 1].along;\n const expectedPerp = expected[i].perp - expected[i - 1].perp;\n\n if (Math.abs(along - expectedAlong) > tolerance) {\n issues.push({\n code: 'spacing-along-train',\n message: `Along-train spacing between domino ${i - 1} and ${i} is ${along.toFixed(2)}px (expected ${expectedAlong}px)`,\n index: i,\n });\n }\n\n if (Math.abs(perp - expectedPerp) > tolerance) {\n issues.push({\n code: 'spacing-perpendicular',\n message: `Perpendicular spacing between domino ${i - 1} and ${i} is ${perp.toFixed(2)}px (expected ${expectedPerp}px)`,\n index: i,\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport function validateNoPairOverlap(\n layout: readonly TrainLayoutEntry[],\n angle: number,\n layoutStyle: TrainLayoutStyle,\n tolerance = DEFAULT_TOLERANCE,\n outwardSignOverride?: number\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n const outwardSign = outwardSignOverride ?? outwardPerpSign(angle);\n const expected = expectedAxisLayout(layout, layoutStyle, outwardSign);\n\n for (let i = 1; i < layout.length; i++) {\n const prev = layout[i - 1];\n const current = layout[i];\n const dx = current.x - prev.x;\n const dy = current.y - prev.y;\n const distance = Math.hypot(dx, dy);\n const expectedAlong = expected[i].along - expected[i - 1].along;\n const expectedPerp = expected[i].perp - expected[i - 1].perp;\n const minDistance = Math.hypot(expectedAlong, expectedPerp) * 0.9;\n\n if (distance + tolerance < minDistance) {\n issues.push({\n code: 'overlap',\n message: `Domino ${i - 1} and ${i} centers are ${distance.toFixed(2)}px apart (minimum ${minDistance.toFixed(2)}px)`,\n index: i,\n });\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport function validateTrainLayout(\n layout: readonly TrainLayoutEntry[],\n dominoes: readonly DominoValue[],\n angle: number,\n layoutStyle: TrainLayoutStyle,\n tolerance = DEFAULT_TOLERANCE,\n outwardSignOverride?: number\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [\n ...validateDominoChain(dominoes).issues,\n ...validateConsecutiveSpacing(\n layout,\n angle,\n layoutStyle,\n tolerance,\n outwardSignOverride\n ).issues,\n ...validateNoPairOverlap(\n layout,\n angle,\n layoutStyle,\n tolerance,\n outwardSignOverride\n ).issues,\n ];\n\n if (layout.length !== dominoes.length) {\n issues.push({\n code: 'layout-length',\n message: `Layout length ${layout.length} does not match domino count ${dominoes.length}`,\n });\n }\n\n return { valid: issues.length === 0, issues };\n}\n\n/**\n * Validates the chicken-foot branch tree as data: every chain links up, every\n * foot hangs off a real double, and every toe's first tile matches the double's\n * value. Recurses into nested feet.\n */\nexport function validateChickenFootChain(\n branch: TrainBranch\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n\n const walk = (current: TrainBranch, path: string) => {\n issues.push(\n ...validateDominoChain(current.dominoes).issues.map((issue) => ({\n ...issue,\n message: `[${path}] ${issue.message}`,\n }))\n );\n\n if (!current.feet) {\n return;\n }\n\n for (const key of Object.keys(current.feet)) {\n const hostIndex = Number(key);\n const host = current.dominoes[hostIndex];\n const toes = current.feet[hostIndex] ?? [];\n\n if (!host) {\n issues.push({\n code: 'foot-host-missing',\n message: `[${path}] Foot references missing tile ${hostIndex}`,\n });\n continue;\n }\n\n if (host.value1 !== host.value2) {\n issues.push({\n code: 'foot-host-not-double',\n message: `[${path}] Foot host tile ${hostIndex} is not a double`,\n });\n }\n\n if (toes.length > 2) {\n issues.push({\n code: 'foot-too-many-toes',\n message: `[${path}] Double ${hostIndex} has ${toes.length} side toes (max 2; the center toe is the main line)`,\n });\n }\n\n toes.forEach((toe, toeIndex) => {\n const first = toe.dominoes[0];\n if (first && first.value1 !== host.value1) {\n issues.push({\n code: 'foot-connection',\n message: `[${path}] Toe ${toeIndex} on double ${hostIndex} starts with ${first.value1} but the double is ${host.value1}`,\n });\n }\n walk(toe, `${path}.${hostIndex}.${toeIndex}`);\n });\n }\n };\n\n walk(branch, 'main');\n return { valid: issues.length === 0, issues };\n}\n\n/**\n * Validates a laid-out chicken-foot tree. Each run's tiles must link up by value\n * and match its tile count; each toe must start the right distance out from its\n * host double (measured along the toe's axis); and — the hard physical rule — no\n * two tiles anywhere in the tree may overlap.\n *\n * The center toe is a centered linear run while the inbound spine is offset, so\n * a single run can mix layout styles; the per-tile overlap test below checks the\n * real constraint directly rather than reconstructing each style's spacing.\n */\nexport function validateTrainTree(\n segments: readonly TrainSegment[],\n tolerance = DEFAULT_TOLERANCE\n): LayoutValidationResult {\n const issues: LayoutValidationIssue[] = [];\n\n segments.forEach((segment, segmentIndex) => {\n issues.push(\n ...validateDominoChain(segment.dominoes).issues.map((issue) => ({\n ...issue,\n message: `[segment ${segmentIndex} @${segment.angle}°] ${issue.message}`,\n }))\n );\n\n if (segment.layout.length !== segment.dominoes.length) {\n issues.push({\n code: 'layout-length',\n message: `[segment ${segmentIndex}] Layout length ${segment.layout.length} does not match domino count ${segment.dominoes.length}`,\n });\n }\n\n if (segment.anchor && segment.layout.length > 0) {\n const first = segment.layout[0];\n const along = projectOnTrainAxis(\n first.x - segment.anchor.x,\n first.y - segment.anchor.y,\n segment.angle\n );\n const expected = DOMINO_HEIGHT / 2;\n if (Math.abs(along - expected) > tolerance) {\n issues.push({\n code: 'foot-anchor',\n message: `[segment ${segmentIndex}] First toe tile sits ${along.toFixed(2)}px from the double along the toe (expected ${expected}px)`,\n index: 0,\n });\n }\n }\n });\n\n // Physical rule: dominoes are solid, so no two tiles may overlap anywhere.\n const tiles = segments.flatMap((segment) => segment.layout);\n for (let i = 0; i < tiles.length; i++) {\n for (let j = i + 1; j < tiles.length; j++) {\n if (tilesOverlap(tiles[i], tiles[j])) {\n issues.push({\n code: 'tile-overlap',\n message: `Tiles ${i} and ${j} overlap`,\n index: j,\n });\n }\n }\n }\n\n return { valid: issues.length === 0, issues };\n}\n\nexport function dominoCorners(\n entry: TrainLayoutEntry,\n dominoWidth = DOMINO_WIDTH,\n dominoHeight = DOMINO_HEIGHT\n): { x: number; y: number }[] {\n const rotation = (entry.rotation * Math.PI) / 180;\n const halfW = dominoWidth / 2;\n const halfH = dominoHeight / 2;\n const localCorners = [\n { x: -halfW, y: -halfH },\n { x: halfW, y: -halfH },\n { x: halfW, y: halfH },\n { x: -halfW, y: halfH },\n ];\n\n return localCorners.map(({ x, y }) => {\n const rotatedX = x * Math.cos(rotation) - y * Math.sin(rotation);\n const rotatedY = x * Math.sin(rotation) + y * Math.cos(rotation);\n return { x: entry.x + rotatedX, y: entry.y + rotatedY };\n });\n}\n","import { FC, useEffect, useMemo } from 'react';\nimport { DoubleTwelve } from '@/app/DoubleTwelve';\nimport {\n DOMINO_HEIGHT,\n DOMINO_WIDTH,\n computeTrainTree,\n flattenSegments,\n} from '@/app/trainLayout';\nimport { validateChickenFootChain } from '@/harness/layoutValidation';\nimport { TrainData } from '@/game/TrainData';\nimport { PipColorMap } from '@/app/pipColors';\n\n/** True in dev/test, false in the production library bundle (Vite inlines it). */\nconst IS_DEV = (() => {\n try {\n return Boolean(import.meta.env?.DEV);\n } catch {\n return false;\n }\n})();\n\ninterface DominoTrainProps {\n startX: number;\n startY: number;\n angle: number;\n trainData: TrainData;\n layoutStyle: 'offset' | 'linear';\n tableWidth: number;\n tableHeight: number;\n centerX: number;\n centerY: number;\n pipColors?: PipColorMap;\n}\n\nexport const DominoTrain: FC<DominoTrainProps> = ({\n startX,\n startY,\n angle,\n trainData,\n layoutStyle,\n tableWidth,\n tableHeight,\n centerX,\n centerY,\n pipColors,\n}) => {\n // Guard rail: a train must be a sequentially-correct chain (like-values\n // touching, toes connecting to their double). Rather than silently drawing a\n // broken train, surface the rule violations in dev. Never throws, so it can't\n // crash a host app's render in production.\n useEffect(() => {\n if (!IS_DEV) return;\n const result = validateChickenFootChain({\n dominoes: trainData.dominoes,\n feet: trainData.feet,\n });\n if (!result.valid) {\n console.warn(\n `DominoTrain: player ${trainData.playerId} train does not follow the rules:`,\n result.issues.map((issue) => issue.message)\n );\n }\n }, [trainData.dominoes, trainData.feet, trainData.playerId]);\n\n const trainLayout = useMemo(\n () =>\n flattenSegments(\n computeTrainTree({\n startX,\n startY,\n angle,\n branch: { dominoes: trainData.dominoes, feet: trainData.feet },\n layoutStyle,\n })\n ),\n [\n startX,\n startY,\n angle,\n trainData.dominoes,\n trainData.feet,\n layoutStyle,\n tableWidth,\n tableHeight,\n ]\n );\n\n return (\n <>\n {trainLayout.map((entry, index) => {\n const showMarker = trainData.isPublic;\n\n return (\n <div\n key={`main-train-${trainData.playerId}-${index}`}\n style={{\n position: 'absolute',\n left: `${entry.x - DOMINO_WIDTH / 2}px`,\n top: `${entry.y - DOMINO_HEIGHT / 2}px`,\n zIndex: 5,\n }}\n >\n <DoubleTwelve\n value1={entry.value1}\n value2={entry.value2}\n width={DOMINO_WIDTH}\n height={DOMINO_HEIGHT}\n backgroundColor=\"white\"\n pipColor=\"black\"\n pipColors={pipColors}\n borderColor={showMarker ? 'red' : 'black'}\n rotation={entry.rotation}\n />\n </div>\n );\n })}\n </>\n );\n};\n\nexport default DominoTrain;\n","import { FC } from 'react';\nimport { DominoTrain } from '@/app/DominoTrain';\nimport { DoubleTwelve } from '@/app/DoubleTwelve';\nimport { TrainData } from '@/game/TrainData';\nimport { PipColorMap } from '@/app/pipColors';\n\ninterface DominoHubProps {\n playerCount: number;\n centerX: number;\n centerY: number;\n radius: number;\n engineValue: number;\n trains: TrainData[];\n layoutStyle: 'offset' | 'linear';\n tableWidth: number;\n tableHeight: number;\n pipColors?: PipColorMap;\n}\n\n/**\n * Distance from the hub center at which a train should start so its first tile\n * clears its neighbours. Trains fan out 360/slots° apart, so the neighbour gap\n * at distance d is ~2πd/slots; it must exceed a tile's footprint. Offset trains\n * zigzag wider (a perpendicular half-tile seed) and need a bigger ring; linear\n * trains are skinny and stay near the hub. Never smaller than `radius + 20`.\n */\nexport function hubTrainStartDistance(\n slots: number,\n radius: number,\n dominoWidth: number,\n layoutStyle: 'offset' | 'linear'\n): number {\n const minNeighborGap = dominoWidth * (layoutStyle === 'offset' ? 2.5 : 1.3);\n return Math.max(radius + 20, Math.ceil((minNeighborGap * slots) / (2 * Math.PI)));\n}\n\nexport const DominoHub: FC<DominoHubProps> = ({\n playerCount,\n centerX,\n centerY,\n radius,\n engineValue,\n trains,\n layoutStyle,\n tableWidth,\n tableHeight,\n pipColors,\n}) => {\n // Ensure we have at least 8 player slots\n const slots = Math.max(8, playerCount);\n const hubSize = 120; // Increased to fit the standard domino size\n const dominoWidth = 60;\n const dominoHeight = 120; // Standard domino size to match the rest of the dominoes\n\n const startDistance = hubTrainStartDistance(slots, radius, dominoWidth, layoutStyle);\n\n return (\n <div style={{ position: 'relative', width: '100%', height: '100%' }}>\n {/* Central hub */}\n <div\n style={{\n position: 'absolute',\n width: `${hubSize}px`,\n height: `${hubSize}px`,\n left: `${centerX - hubSize / 2}px`,\n top: `${centerY - hubSize / 2}px`,\n backgroundColor: '#d1d5db',\n borderWidth: '3px',\n borderStyle: 'solid',\n borderColor: '#6b7280',\n borderRadius: '50%',\n boxShadow: '0 4px 6px rgba(0,0,0,0.1)',\n zIndex: 10,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n }}\n >\n {/* Engine domino in the center */}\n <div style={{ transform: 'rotate(0deg)' }}>\n <DoubleTwelve\n value1={engineValue}\n value2={engineValue}\n width={dominoWidth}\n height={dominoHeight}\n backgroundColor=\"white\"\n pipColor=\"black\"\n pipColors={pipColors}\n borderColor=\"#333\"\n />\n </div>\n </div>\n\n {/* Trains radiating from hub */}\n {Array.from({ length: slots }).map((_, index) => {\n const angle = (index * 360) / slots;\n const radians = (angle * Math.PI) / 180;\n\n // Calculate starting point for the train\n const startX = centerX + startDistance * Math.cos(radians);\n const startY = centerY + startDistance * Math.sin(radians);\n\n // Get train data for this position (if exists)\n const trainData = trains.find((t) => t.playerId === index) || {\n dominoes: [],\n playerId: index,\n isPublic: false,\n };\n\n return (\n <DominoTrain\n key={index}\n startX={startX}\n startY={startY}\n angle={angle}\n trainData={trainData}\n layoutStyle={layoutStyle}\n tableWidth={tableWidth}\n tableHeight={tableHeight}\n centerX={centerX}\n centerY={centerY}\n pipColors={pipColors}\n />\n );\n })}\n </div>\n );\n};\n\nexport default DominoHub;\n","import { DominoValue } from '@/game/DominoValue';\n\n/**\n * Canonical, order-independent key for a tile. `6-3` and `3-6` are the same\n * physical domino, so they share a key. Used to enforce tile uniqueness.\n */\nexport function tileKey(value1: number, value2: number): string {\n return value1 <= value2 ? `${value1}:${value2}` : `${value2}:${value1}`;\n}\n\nexport function dominoKey(tile: DominoValue): string {\n return tileKey(tile.value1, tile.value2);\n}\n\nexport function isDouble(tile: DominoValue): boolean {\n return tile.value1 === tile.value2;\n}\n\nexport function tileHasValue(tile: DominoValue, value: number): boolean {\n return tile.value1 === value || tile.value2 === value;\n}\n\n/** The pip value on the opposite end from `value`, or null if it doesn't touch. */\nexport function otherEnd(tile: DominoValue, value: number): number | null {\n if (tile.value1 === value) return tile.value2;\n if (tile.value2 === value) return tile.value1;\n return null;\n}\n\n/**\n * Orients a tile so its `value1` is the end that connects to `connectingValue`.\n * Returns null if the tile has no such end.\n */\nexport function orientForConnection(\n tile: DominoValue,\n connectingValue: number\n): DominoValue | null {\n if (tile.value1 === connectingValue) {\n return { value1: tile.value1, value2: tile.value2 };\n }\n if (tile.value2 === connectingValue) {\n return { value1: tile.value2, value2: tile.value1 };\n }\n return null;\n}\n\n/** Every unique tile in a double-`maxPips` set (e.g. maxPips=12 → 91 tiles). */\nexport function generateDominoSet(maxPips: number): DominoValue[] {\n const tiles: DominoValue[] = [];\n for (let a = 0; a <= maxPips; a++) {\n for (let b = a; b <= maxPips; b++) {\n tiles.push({ value1: a, value2: b });\n }\n }\n return tiles;\n}\n\n/** Count of tiles in a double-`maxPips` set: (n+1)(n+2)/2 where n = maxPips. */\nexport function dominoSetSize(maxPips: number): number {\n const n = maxPips + 1;\n return (n * (n + 1)) / 2;\n}\n","import { DominoValue } from './DominoValue';\nimport { TrainBranch, TrainData } from './TrainData';\nimport { tileKey } from '@/rules/dominoSet';\n\nexport { tileKey };\n\nexport interface GenerateSampleTrainsOptions {\n /** Attach chicken-foot side toes (±45°) to every double. */\n chickenFeet?: boolean;\n}\n\n/** Demo train generator that respects double-12 tile uniqueness constraints. */\nexport function generateSampleTrains(\n playerCount: number,\n engineValue = 12,\n options: GenerateSampleTrainsOptions = {}\n): TrainData[] {\n const usedTiles = new Set<string>([tileKey(engineValue, engineValue)]);\n const trains: TrainData[] = [];\n\n for (let playerId = 0; playerId < playerCount; playerId++) {\n const dominoCount = 4 + Math.floor(Math.random() * 7);\n const dominoes: DominoValue[] = [];\n let openValue = engineValue;\n let prevWasDouble = false;\n\n for (let j = 0; j < dominoCount; j++) {\n const value2 = pickNextValue(\n openValue,\n prevWasDouble,\n j === 0,\n engineValue,\n usedTiles\n );\n\n // No tile left that keeps the chain legal and unique: stop this train.\n if (value2 === null) {\n break;\n }\n\n const isDouble = value2 === openValue;\n usedTiles.add(tileKey(openValue, value2));\n\n dominoes.push({ value1: openValue, value2 });\n prevWasDouble = isDouble;\n openValue = value2;\n }\n\n const feet = options.chickenFeet\n ? buildFeet(dominoes, usedTiles)\n : undefined;\n\n trains.push({\n playerId,\n dominoes,\n isPublic: Math.random() > 0.7,\n ...(feet ? { feet } : {}),\n });\n }\n\n return trains;\n}\n\n/**\n * Builds chicken-foot side toes for every double in a chain. Each toe is a short\n * straight run (no doubles, so no nested feet) starting from the double's value.\n */\nfunction buildFeet(\n dominoes: readonly DominoValue[],\n usedTiles: Set<string>\n): Record<number, TrainBranch[]> | undefined {\n const feet: Record<number, TrainBranch[]> = {};\n\n for (let i = 0; i < dominoes.length; i++) {\n if (dominoes[i].value1 !== dominoes[i].value2) {\n continue;\n }\n\n const doubleValue = dominoes[i].value1;\n const toes: TrainBranch[] = [];\n for (let t = 0; t < 2; t++) {\n const toe = buildToe(doubleValue, usedTiles);\n if (toe) {\n toes.push(toe);\n }\n }\n\n if (toes.length) {\n feet[i] = toes;\n }\n }\n\n return Object.keys(feet).length ? feet : undefined;\n}\n\nfunction buildToe(\n startValue: number,\n usedTiles: Set<string>\n): TrainBranch | null {\n const length = 1 + Math.floor(Math.random() * 2);\n const dominoes: DominoValue[] = [];\n let openValue = startValue;\n\n for (let j = 0; j < length; j++) {\n const next = pickNonDouble(openValue, usedTiles);\n if (next === null) {\n break;\n }\n usedTiles.add(tileKey(openValue, next));\n dominoes.push({ value1: openValue, value2: next });\n openValue = next;\n }\n\n return dominoes.length ? { dominoes } : null;\n}\n\nfunction pickNonDouble(\n openValue: number,\n usedTiles: Set<string>\n): number | null {\n const candidates: number[] = [];\n for (let value = 0; value < 13; value++) {\n if (value === openValue) {\n continue;\n }\n if (usedTiles.has(tileKey(openValue, value))) {\n continue;\n }\n candidates.push(value);\n }\n\n if (candidates.length === 0) {\n return null;\n }\n\n return candidates[Math.floor(Math.random() * candidates.length)];\n}\n\nfunction pickNextValue(\n openValue: number,\n prevWasDouble: boolean,\n isFirstDomino: boolean,\n engineValue: number,\n usedTiles: Set<string>\n): number | null {\n const candidates = Array.from({ length: 13 }, (_, value) => value).filter(\n (value) =>\n isValidNextValue(\n openValue,\n value,\n prevWasDouble,\n isFirstDomino,\n engineValue,\n usedTiles\n )\n );\n\n if (candidates.length === 0) {\n return null;\n }\n\n return candidates[Math.floor(Math.random() * candidates.length)];\n}\n\nfunction isValidNextValue(\n openValue: number,\n value: number,\n prevWasDouble: boolean,\n isFirstDomino: boolean,\n engineValue: number,\n usedTiles: Set<string>\n): boolean {\n const isDouble = value === openValue;\n\n if (isFirstDomino && isDouble && openValue === engineValue) {\n return false;\n }\n\n if (isDouble && prevWasDouble) {\n return false;\n }\n\n if (usedTiles.has(tileKey(openValue, value))) {\n return false;\n }\n\n return true;\n}\n","import { FC, useState, useEffect } from 'react';\nimport { DominoHub } from './DominoHub';\nimport { GameState } from '@/game/GameState';\nimport { generateSampleTrains } from '@/game/generateSampleTrains';\nimport { DEFAULT_PIP_COLORS, PipColorMap } from './pipColors';\n\ninterface MexicanTrainGameProps {\n initialState?: GameState;\n width?: number;\n height?: number;\n pipColors?: PipColorMap;\n onPipColorsChange?: (pipColors: PipColorMap | undefined) => void;\n}\n\nconst defaultGameState: GameState = {\n playerCount: 8,\n trains: [],\n engineValue: 12,\n};\n\nexport const MexicanTrainGame: FC<MexicanTrainGameProps> = ({\n initialState = defaultGameState,\n width = 1200,\n height = 800,\n pipColors: pipColorsProp,\n onPipColorsChange,\n}) => {\n const [gameState, setGameState] = useState<GameState>(initialState);\n const [layoutStyle, setLayoutStyle] = useState<'offset' | 'linear'>('offset');\n const [chickenFeet, setChickenFeet] = useState(false);\n const [pipColorsInternal, setPipColorsInternal] = useState<\n PipColorMap | undefined\n >(undefined);\n const pipColors = pipColorsProp ?? pipColorsInternal;\n const setPipColors = onPipColorsChange ?? setPipColorsInternal;\n const pipColorsEnabled = pipColors !== undefined;\n\n // Center coordinates for the hub\n const centerX = width / 2;\n const centerY = height / 2;\n\n // Generate sample data for better visualization\n const regenerateTrains = (chickenFeetEnabled = chickenFeet) => {\n const sampleTrains = generateSampleTrains(\n gameState.playerCount,\n gameState.engineValue,\n { chickenFeet: chickenFeetEnabled }\n );\n setGameState((prevState) => ({\n ...prevState,\n trains: sampleTrains,\n }));\n };\n\n useEffect(() => {\n regenerateTrains();\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const toggleChickenFeet = () => {\n const next = !chickenFeet;\n setChickenFeet(next);\n regenerateTrains(next);\n };\n\n return (\n <div\n style={{\n width: `${width}px`,\n height: `${height}px`,\n position: 'relative',\n backgroundColor: '#1f8a55', // Classic green felt background\n borderRadius: '8px',\n boxShadow: '0 4px 12px rgba(0,0,0,0.2)',\n overflow: 'hidden',\n }}\n >\n <div\n style={{ position: 'absolute', top: '10px', left: '10px', zIndex: 100 }}\n >\n <button\n onClick={() => regenerateTrains()}\n style={{\n padding: '8px 12px',\n backgroundColor: '#fff',\n border: '1px solid #ccc',\n borderRadius: '4px',\n marginRight: '10px',\n cursor: 'pointer',\n }}\n >\n New trains\n </button>\n <button\n onClick={() =>\n setLayoutStyle(layoutStyle === 'offset' ? 'linear' : 'offset')\n }\n style={{\n padding: '8px 12px',\n backgroundColor: '#fff',\n border: '1px solid #ccc',\n borderRadius: '4px',\n marginRight: '10px',\n cursor: 'pointer',\n }}\n >\n Layout: {layoutStyle === 'offset' ? 'Offset' : 'Linear'}\n </button>\n <button\n onClick={toggleChickenFeet}\n style={{\n padding: '8px 12px',\n backgroundColor: chickenFeet ? '#fef3c7' : '#fff',\n border: `1px solid ${chickenFeet ? '#f59e0b' : '#ccc'}`,\n borderRadius: '4px',\n marginRight: '10px',\n cursor: 'pointer',\n }}\n >\n Chicken Feet: {chickenFeet ? 'On' : 'Off'}\n </button>\n <button\n onClick={() =>\n setPipColors(pipColorsEnabled ? undefined : DEFAULT_PIP_COLORS)\n }\n style={{\n padding: '8px 12px',\n backgroundColor: pipColorsEnabled ? '#fef3c7' : '#fff',\n border: `1px solid ${pipColorsEnabled ? '#f59e0b' : '#ccc'}`,\n borderRadius: '4px',\n cursor: 'pointer',\n }}\n >\n Pip Colors: {pipColorsEnabled ? 'On' : 'Off'}\n </button>\n </div>\n\n {/* Game information */}\n <div\n style={{\n position: 'absolute',\n top: '10px',\n right: '10px',\n zIndex: 100,\n backgroundColor: 'rgba(255,255,255,0.8)',\n padding: '8px',\n borderRadius: '4px',\n fontSize: '14px',\n }}\n >\n <div>Engine: Double-{gameState.engineValue}</div>\n <div>Players: {gameState.playerCount}</div>\n </div>\n\n <DominoHub\n playerCount={gameState.playerCount}\n centerX={centerX}\n centerY={centerY}\n radius={80}\n engineValue={gameState.engineValue}\n trains={gameState.trains}\n layoutStyle={layoutStyle}\n tableWidth={width}\n tableHeight={height}\n pipColors={pipColors}\n />\n </div>\n );\n};\n\nexport default MexicanTrainGame;\n","import { TrainBend, TrainBranch } from '@/game/TrainData';\nimport {\n TrainLayoutEntry,\n TrainLayoutStyle,\n computeTrainTree,\n flattenSegments,\n layoutSelfIntersects,\n layoutsCollide,\n outwardPerpSign,\n trainPerpendicular,\n} from './trainLayout';\n\nexport type TurnSide = 'left' | 'right';\n\n/** Default pivot magnitude. The interactive UI only produces square corners. */\nexport const TURN_DEGREES = 90;\n\n/**\n * Signed turn (degrees) for a side. Headings use the screen convention\n * (0° = +x, +90° = +y / downward), so a `+90` turn rotates +x toward +y, which\n * reads as a clockwise/\"right\" turn on screen.\n */\nexport function sideToTurn(side: TurnSide, degrees = TURN_DEGREES): number {\n return side === 'right' ? degrees : -degrees;\n}\n\nexport function oppositeSide(side: TurnSide): TurnSide {\n return side === 'right' ? 'left' : 'right';\n}\n\n/**\n * Default turn side in offset mode: fold toward the empty side — the one\n * opposite the lane the zigzag biases into (`outwardSign`). A `+90` turn heads\n * toward the heading's `+perp`; outwardSign is measured on that same perp axis,\n * so the empty side is `-outwardSign`, i.e. side = outwardSign >= 0 ? 'left' : 'right'.\n */\nexport function offsetDefaultSide(angle: number, outwardSign?: number): TurnSide {\n const bias = outwardSign ?? outwardPerpSign(angle);\n return bias >= 0 ? 'left' : 'right';\n}\n\nexport interface TableBounds {\n width: number;\n height: number;\n}\n\n/**\n * Default turn side in linear mode: fold toward whichever perpendicular side has\n * more open table from the bend point. Distance is measured from `point` along\n * each perpendicular until it exits the table rectangle; the roomier side wins.\n * Ties (e.g. dead-center) fall back to 'right'.\n */\nexport function linearDefaultSide(\n point: { x: number; y: number },\n angle: number,\n bounds: TableBounds\n): TurnSide {\n const { perpX, perpY } = trainPerpendicular(angle);\n const distanceToExit = (sx: number, sy: number): number => {\n // Largest t >= 0 with point + t*(sx,sy) still inside [0,w] x [0,h].\n let t = Infinity;\n if (sx > 0) t = Math.min(t, (bounds.width - point.x) / sx);\n else if (sx < 0) t = Math.min(t, (0 - point.x) / sx);\n if (sy > 0) t = Math.min(t, (bounds.height - point.y) / sy);\n else if (sy < 0) t = Math.min(t, (0 - point.y) / sy);\n return Number.isFinite(t) ? Math.max(0, t) : Infinity;\n };\n\n // +90 turn heads toward +perp ('right'); -90 toward -perp ('left').\n const rightRoom = distanceToExit(perpX, perpY);\n const leftRoom = distanceToExit(-perpX, -perpY);\n return rightRoom >= leftRoom ? 'right' : 'left';\n}\n\nexport interface BuildTrainTilesInput {\n startX: number;\n startY: number;\n angle: number;\n layoutStyle: TrainLayoutStyle;\n}\n\n/** Flattens a branch (with feet and bends) to its world-space tiles. */\nexport function buildBranchTiles(\n branch: TrainBranch,\n input: BuildTrainTilesInput\n): TrainLayoutEntry[] {\n return flattenSegments(\n computeTrainTree({\n startX: input.startX,\n startY: input.startY,\n angle: input.angle,\n branch,\n layoutStyle: input.layoutStyle,\n })\n );\n}\n\n/** Replaces (or removes) the bend at `index`, returning a new bends array. */\nexport function withBendAt(\n bends: readonly TrainBend[] | undefined,\n index: number,\n turn: number | null\n): TrainBend[] {\n const rest = (bends ?? []).filter((bend) => bend.index !== index);\n if (turn === null) return rest;\n return [...rest, { index, turn }].sort((a, b) => a.index - b.index);\n}\n\nexport interface ResolveBendResult {\n /** The legal turn to apply, or null when no side is collision-free. */\n turn: number | null;\n /** Why null: 'blocked' = both sides collide; never set on success. */\n reason?: 'blocked';\n}\n\nexport interface ResolveBendInput {\n branch: TrainBranch;\n index: number;\n build: BuildTrainTilesInput;\n /** Tiles belonging to every OTHER path; a bend may not intersect these. */\n obstacles: readonly TrainLayoutEntry[];\n /** Preferred side to try first (from the mode's heuristic). */\n preferredSide: TurnSide;\n /** Turn magnitude in degrees (default 90). */\n degrees?: number;\n}\n\n/**\n * Picks a collision-free turn for a new bend at `index`. Tries the preferred\n * side first, then the opposite; a candidate is rejected if the resulting path\n * crosses itself or any obstacle path. Returns `{ turn: null, reason: 'blocked' }`\n * when neither side is legal, so the caller can refuse the bend.\n */\nexport function resolveBend({\n branch,\n index,\n build,\n obstacles,\n preferredSide,\n degrees = TURN_DEGREES,\n}: ResolveBendInput): ResolveBendResult {\n const candidates: TurnSide[] = [preferredSide, oppositeSide(preferredSide)];\n\n for (const side of candidates) {\n const turn = sideToTurn(side, degrees);\n const candidateBranch: TrainBranch = {\n ...branch,\n bends: withBendAt(branch.bends, index, turn),\n };\n const tiles = buildBranchTiles(candidateBranch, build);\n if (layoutSelfIntersects(tiles)) continue;\n if (layoutsCollide(tiles, obstacles)) continue;\n return { turn };\n }\n\n return { turn: null, reason: 'blocked' };\n}\n\n/**\n * Cycles a tile's bend on repeated clicks: none → preferred legal side →\n * opposite legal side → none. Skips sides that collide. Returns the next bends\n * array, or the unchanged input when no legal bend exists.\n */\nexport function cycleBendAt(\n branch: TrainBranch,\n index: number,\n build: BuildTrainTilesInput,\n obstacles: readonly TrainLayoutEntry[],\n preferredSide: TurnSide,\n degrees = TURN_DEGREES\n): { bends: TrainBend[]; changed: boolean; blocked: boolean } {\n const current = (branch.bends ?? []).find((bend) => bend.index === index);\n const preferredTurn = sideToTurn(preferredSide, degrees);\n const oppositeTurn = sideToTurn(oppositeSide(preferredSide), degrees);\n\n const isLegal = (turn: number): boolean => {\n const candidate: TrainBranch = {\n ...branch,\n bends: withBendAt(branch.bends, index, turn),\n };\n const tiles = buildBranchTiles(candidate, build);\n return !layoutSelfIntersects(tiles) && !layoutsCollide(tiles, obstacles);\n };\n\n // Cycle order by current state. `null` means \"straighten\" (remove the bend),\n // which is always legal — but it's only a cycle target when a bend already\n // exists. Starting from straight with both sides blocked reports `blocked`\n // rather than performing a no-op removal.\n let order: (number | null)[];\n if (!current) {\n order = [preferredTurn, oppositeTurn];\n } else if (current.turn === preferredTurn) {\n order = [oppositeTurn, null];\n } else if (current.turn === oppositeTurn) {\n order = [null];\n } else {\n order = [preferredTurn, oppositeTurn, null];\n }\n\n for (const turn of order) {\n if (turn === null) {\n return { bends: withBendAt(branch.bends, index, null), changed: true, blocked: false };\n }\n if (isLegal(turn)) {\n return { bends: withBendAt(branch.bends, index, turn), changed: true, blocked: false };\n }\n }\n\n return { bends: branch.bends ?? [], changed: false, blocked: true };\n}\n","/** Pan/zoom transform: content is scaled by `scale` then translated by (x, y). */\nexport interface ViewportTransform {\n scale: number;\n x: number;\n y: number;\n}\n\nexport interface Size {\n width: number;\n height: number;\n}\n\nexport interface Point {\n x: number;\n y: number;\n}\n\nexport function clampScale(scale: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, scale));\n}\n\n/**\n * Zooms by `factor` about a fixed screen `pivot` so the content point under the\n * pivot stays put. Scale is clamped to [min, max]; the translation is adjusted\n * by the *effective* factor after clamping so panning can't drift at the limits.\n */\nexport function zoomAt(\n view: ViewportTransform,\n factor: number,\n pivot: Point,\n min: number,\n max: number\n): ViewportTransform {\n const scale = clampScale(view.scale * factor, min, max);\n const effective = scale / view.scale;\n return {\n scale,\n x: pivot.x - (pivot.x - view.x) * effective,\n y: pivot.y - (pivot.y - view.y) * effective,\n };\n}\n\n/**\n * Centers `content` within `viewport` at the largest scale that fits inside the\n * given padding (clamped to [min, max]). Use this for a \"fit / reset\" control.\n */\nexport function fitToBounds(\n content: Size,\n viewport: Size,\n padding: number,\n min: number,\n max: number\n): ViewportTransform {\n const safeW = Math.max(1, content.width);\n const safeH = Math.max(1, content.height);\n const raw = Math.min(\n (viewport.width - padding * 2) / safeW,\n (viewport.height - padding * 2) / safeH\n );\n const scale = clampScale(raw, min, max);\n return {\n scale,\n x: (viewport.width - safeW * scale) / 2,\n y: (viewport.height - safeH * scale) / 2,\n };\n}\n\n/** Converts a screen point inside the viewport to content coordinates. */\nexport function screenToContent(view: ViewportTransform, screen: Point): Point {\n return {\n x: (screen.x - view.x) / view.scale,\n y: (screen.y - view.y) / view.scale,\n };\n}\n","import {\n CSSProperties,\n FC,\n ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from 'react';\nimport {\n ViewportTransform,\n clampScale,\n fitToBounds,\n zoomAt,\n} from './viewportMath';\n\nexport interface ViewportProps {\n /** Visible viewport size in pixels. */\n width: number;\n height: number;\n /** Content (world) size, used by the fit/reset control. */\n contentWidth: number;\n contentHeight: number;\n children: ReactNode;\n minScale?: number;\n maxScale?: number;\n /** Multiplier applied per wheel notch / zoom-button press. */\n zoomStep?: number;\n padding?: number;\n background?: string;\n /** Show the built-in zoom/reset control overlay. Default true. */\n showControls?: boolean;\n testId?: string;\n}\n\nconst PAN_THRESHOLD = 3;\n\n/**\n * A pan/zoom canvas for content larger than the screen. Drag to slide, wheel or\n * the on-screen buttons to zoom (zoom centers on the cursor for the wheel).\n * Clicks pass through to children unless the pointer actually dragged, so\n * interactive content (e.g. click-to-bend tiles) keeps working.\n */\nexport const Viewport: FC<ViewportProps> = ({\n width,\n height,\n contentWidth,\n contentHeight,\n children,\n minScale = 0.2,\n maxScale = 4,\n zoomStep = 1.15,\n padding = 40,\n background = '#1f8a55',\n showControls = true,\n testId = 'viewport',\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const fit = useCallback(\n (): ViewportTransform =>\n fitToBounds(\n { width: contentWidth, height: contentHeight },\n { width, height },\n padding,\n minScale,\n maxScale\n ),\n [contentWidth, contentHeight, width, height, padding, minScale, maxScale]\n );\n\n const [view, setView] = useState<ViewportTransform>(fit);\n\n // Re-fit when the content or viewport size changes meaningfully.\n useEffect(() => {\n setView(fit());\n }, [fit]);\n\n const pan = useRef<\n | { pointerX: number; pointerY: number; startX: number; startY: number; moved: boolean }\n | null\n >(null);\n\n const localPoint = (clientX: number, clientY: number) => {\n const rect = containerRef.current?.getBoundingClientRect();\n return { x: clientX - (rect?.left ?? 0), y: clientY - (rect?.top ?? 0) };\n };\n\n const onPointerDown = (event: React.PointerEvent) => {\n pan.current = {\n pointerX: event.clientX,\n pointerY: event.clientY,\n startX: view.x,\n startY: view.y,\n moved: false,\n };\n };\n\n const onPointerMove = (event: React.PointerEvent) => {\n const current = pan.current;\n if (!current) return;\n const dx = event.clientX - current.pointerX;\n const dy = event.clientY - current.pointerY;\n if (!current.moved && Math.hypot(dx, dy) < PAN_THRESHOLD) return;\n current.moved = true;\n containerRef.current?.setPointerCapture?.(event.pointerId);\n setView((prev) => ({ ...prev, x: current.startX + dx, y: current.startY + dy }));\n };\n\n const endPan = (event: React.PointerEvent) => {\n if (pan.current?.moved) {\n // Swallow the click that follows a drag so children don't treat it as a tap.\n event.preventDefault();\n }\n pan.current = null;\n };\n\n const onWheel = (event: React.WheelEvent) => {\n event.preventDefault();\n const pivot = localPoint(event.clientX, event.clientY);\n const factor = event.deltaY < 0 ? zoomStep : 1 / zoomStep;\n setView((prev) => zoomAt(prev, factor, pivot, minScale, maxScale));\n };\n\n const zoomButton = (factor: number) =>\n setView((prev) =>\n zoomAt(prev, factor, { x: width / 2, y: height / 2 }, minScale, maxScale)\n );\n\n const buttonStyle: CSSProperties = {\n width: 32,\n height: 32,\n fontSize: 18,\n lineHeight: '30px',\n textAlign: 'center',\n cursor: 'pointer',\n background: '#fff',\n border: '1px solid #d1d5db',\n borderRadius: 6,\n userSelect: 'none',\n };\n\n return (\n <div\n ref={containerRef}\n data-testid={testId}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={endPan}\n onPointerLeave={endPan}\n onWheel={onWheel}\n style={{\n position: 'relative',\n width,\n height,\n overflow: 'hidden',\n background,\n borderRadius: 8,\n cursor: 'grab',\n touchAction: 'none',\n }}\n >\n <div\n data-testid={`${testId}-content`}\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n transformOrigin: '0 0',\n transform: `translate(${view.x}px, ${view.y}px) scale(${view.scale})`,\n }}\n >\n {children}\n </div>\n\n {showControls && (\n <div\n data-testid={`${testId}-controls`}\n style={{\n position: 'absolute',\n right: 12,\n bottom: 12,\n display: 'flex',\n flexDirection: 'column',\n gap: 6,\n }}\n >\n <div style={buttonStyle} role=\"button\" aria-label=\"Zoom in\" onClick={() => zoomButton(zoomStep)}>\n +\n </div>\n <div style={buttonStyle} role=\"button\" aria-label=\"Zoom out\" onClick={() => zoomButton(1 / zoomStep)}>\n −\n </div>\n <div\n style={{ ...buttonStyle, fontSize: 12, lineHeight: '30px' }}\n role=\"button\"\n aria-label=\"Reset view\"\n onClick={() => setView(fit())}\n >\n ⤢\n </div>\n <div\n data-testid={`${testId}-zoom-readout`}\n style={{ ...buttonStyle, fontSize: 11, cursor: 'default' }}\n >\n {Math.round(clampScale(view.scale, minScale, maxScale) * 100)}\n </div>\n </div>\n )}\n </div>\n );\n};\n\nexport default Viewport;\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBranch } from '@/game/TrainData';\nimport { TrainLayoutStyle } from '@/app/trainLayout';\n\nexport interface TrainFixture {\n id: string;\n name: string;\n description: string;\n angle: number;\n dominoes: DominoValue[];\n layoutStyles: TrainLayoutStyle[];\n}\n\nexport interface ChickenFootFixture {\n id: string;\n name: string;\n description: string;\n angle: number;\n branch: TrainBranch;\n layoutStyles: TrainLayoutStyle[];\n}\n\nexport const TRAIN_FIXTURES: TrainFixture[] = [\n {\n id: 'regular-after-double',\n name: 'Regular after double',\n description: 'Double followed by a two-tile offset run',\n angle: 0,\n dominoes: [\n { value1: 12, value2: 6 },\n { value1: 6, value2: 6 },\n { value1: 6, value2: 3 },\n { value1: 3, value2: 1 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'double-after-regular',\n name: 'Double after regular',\n description: 'Offset run, a double, then another offset run',\n angle: 0,\n dominoes: [\n { value1: 12, value2: 9 },\n { value1: 9, value2: 4 },\n { value1: 4, value2: 4 },\n { value1: 4, value2: 2 },\n { value1: 2, value2: 7 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'double-after-double',\n name: 'Double after double',\n description: 'Offset runs at the head, middle, and tail around two doubles',\n angle: 90,\n dominoes: [\n { value1: 12, value2: 7 },\n { value1: 7, value2: 8 },\n { value1: 8, value2: 8 },\n { value1: 8, value2: 3 },\n { value1: 3, value2: 5 },\n { value1: 5, value2: 5 },\n { value1: 5, value2: 2 },\n { value1: 2, value2: 1 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'offset-zigzag',\n name: 'Offset zigzag',\n description: 'Alternating perpendicular tiles without doubles',\n angle: 0,\n dominoes: [\n { value1: 12, value2: 5 },\n { value1: 5, value2: 9 },\n { value1: 9, value2: 2 },\n { value1: 2, value2: 7 },\n { value1: 7, value2: 1 },\n ],\n layoutStyles: ['offset'],\n },\n {\n id: 'horizontal-open',\n name: 'Horizontal train',\n description: 'Rightward train: offset head, double, offset tail',\n angle: 0,\n dominoes: [\n { value1: 5, value2: 12 },\n { value1: 12, value2: 11 },\n { value1: 11, value2: 11 },\n { value1: 11, value2: 6 },\n { value1: 6, value2: 2 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'vertical-open',\n name: 'Vertical train',\n description: 'Downward train: offset head, double, offset tail',\n angle: 90,\n dominoes: [\n { value1: 3, value2: 12 },\n { value1: 12, value2: 10 },\n { value1: 10, value2: 10 },\n { value1: 10, value2: 4 },\n { value1: 4, value2: 1 },\n ],\n layoutStyles: ['linear', 'offset'],\n },\n];\n\nexport function getTrainFixture(id: string): TrainFixture | undefined {\n return TRAIN_FIXTURES.find((fixture) => fixture.id === id);\n}\n\nexport const CHICKEN_FOOT_FIXTURES: ChickenFootFixture[] = [\n {\n id: 'single-foot',\n name: 'Single foot',\n description:\n 'A double fans two angled toes (±45°) while the main line continues straight as the center toe',\n angle: 0,\n branch: {\n dominoes: [\n { value1: 12, value2: 6 },\n { value1: 6, value2: 6 },\n { value1: 6, value2: 3 },\n { value1: 3, value2: 1 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 6, value2: 2 },\n { value1: 2, value2: 5 },\n ],\n },\n {\n dominoes: [\n { value1: 6, value2: 4 },\n { value1: 4, value2: 0 },\n ],\n },\n ],\n },\n },\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'foot-no-center',\n name: 'Foot at the tail',\n description:\n 'Double ends the main line, so both side toes are present with no straight continuation',\n angle: 0,\n branch: {\n dominoes: [\n { value1: 9, value2: 7 },\n { value1: 7, value2: 7 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 7, value2: 3 },\n { value1: 3, value2: 8 },\n ],\n },\n {\n dominoes: [\n { value1: 7, value2: 5 },\n { value1: 5, value2: 0 },\n ],\n },\n ],\n },\n },\n layoutStyles: ['linear', 'offset'],\n },\n {\n id: 'nested-foot',\n name: 'Nested foot',\n description:\n 'A side toe contains its own double, which sprouts a second-level foot',\n angle: 90,\n branch: {\n dominoes: [\n { value1: 12, value2: 8 },\n { value1: 8, value2: 8 },\n { value1: 8, value2: 3 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 8, value2: 5 },\n { value1: 5, value2: 5 },\n { value1: 5, value2: 2 },\n ],\n feet: {\n 1: [\n {\n dominoes: [\n { value1: 5, value2: 9 },\n { value1: 9, value2: 1 },\n ],\n },\n {\n dominoes: [\n { value1: 5, value2: 4 },\n { value1: 4, value2: 6 },\n ],\n },\n ],\n },\n },\n {\n dominoes: [\n { value1: 8, value2: 1 },\n { value1: 1, value2: 7 },\n ],\n },\n ],\n },\n },\n layoutStyles: ['linear', 'offset'],\n },\n];\n\nexport function getChickenFootFixture(\n id: string\n): ChickenFootFixture | undefined {\n return CHICKEN_FOOT_FIXTURES.find((fixture) => fixture.id === id);\n}\n","/**\n * How a played double must be answered before play continues.\n * - `none`: doubles are ordinary tiles.\n * - `cover`: the double must be answered by one tile on its open end.\n * - `chicken-foot`: the double must grow a full foot (`chickenFoot.toeCount`\n * answers: the straight center continuation plus the angled side toes).\n */\nexport type DoubleObligation = 'none' | 'cover' | 'chicken-foot';\n\nexport interface ChickenFootConfig {\n /**\n * Total toes a double must grow to be satisfied, counting the straight center\n * continuation as one. Stays 3 by default (center + two ±angle side toes).\n */\n toeCount: number;\n /** Angles (degrees, relative to the train) of the side toes (toeCount - 1). */\n sideToeAngles: number[];\n}\n\n/**\n * Every knob that governs legal play. Pass a partial override to {@link resolveRules}\n * to get a fully-populated config; unspecified fields fall back to DEFAULT_RULES.\n */\nexport interface RulesConfig {\n /** Highest pip value in the set (12 → double-12, 91 tiles). */\n maxPips: number;\n /** Value of the starting engine double (defaults to maxPips). */\n engineValue: number;\n /** Forbid a double immediately following a double in a chain. */\n allowConsecutiveDoubles: boolean;\n /** Each physical tile may be placed at most once across all play. */\n requireUniqueTiles: boolean;\n /** A tile may only attach where one of its ends matches the open value. */\n requireSequential: boolean;\n /** Obligation imposed by playing a double. */\n doubleObligation: DoubleObligation;\n chickenFoot: ChickenFootConfig;\n}\n\nexport const DEFAULT_RULES: RulesConfig = {\n maxPips: 12,\n engineValue: 12,\n allowConsecutiveDoubles: false,\n requireUniqueTiles: true,\n requireSequential: true,\n doubleObligation: 'cover',\n chickenFoot: {\n toeCount: 3,\n sideToeAngles: [-45, 45],\n },\n};\n\n/** Number of answers a double needs to be satisfied under the given rules. */\nexport function requiredDoubleAnswers(config: RulesConfig): number {\n switch (config.doubleObligation) {\n case 'chicken-foot':\n return Math.max(1, config.chickenFoot.toeCount);\n case 'cover':\n return 1;\n case 'none':\n default:\n return 0;\n }\n}\n\n/** Number of angled side-toe slots a double exposes (center is the main line). */\nexport function sideToeSlots(config: RulesConfig): number {\n if (config.doubleObligation === 'chicken-foot') {\n return Math.max(0, config.chickenFoot.toeCount - 1);\n }\n // Outside chicken-foot, doubles do not branch; covers go on the main line.\n return 0;\n}\n\n/** Fills in any missing fields from DEFAULT_RULES (engineValue tracks maxPips). */\nexport function resolveRules(overrides: Partial<RulesConfig> = {}): RulesConfig {\n const maxPips = overrides.maxPips ?? DEFAULT_RULES.maxPips;\n return {\n ...DEFAULT_RULES,\n ...overrides,\n maxPips,\n engineValue: overrides.engineValue ?? maxPips,\n chickenFoot: {\n ...DEFAULT_RULES.chickenFoot,\n ...(overrides.chickenFoot ?? {}),\n },\n };\n}\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainBranch } from '@/game/TrainData';\nimport {\n dominoKey,\n isDouble,\n orientForConnection,\n} from '@/rules/dominoSet';\nimport {\n RulesConfig,\n requiredDoubleAnswers,\n sideToeSlots,\n} from '@/rules/rulesConfig';\n\n/**\n * Locates a branch inside a chicken-foot tree. Empty path = the main line; each\n * step descends into the `toeIndex`-th side toe hanging off the double at\n * `doubleIndex` of the current branch.\n */\nexport type BranchPath = ReadonlyArray<{\n doubleIndex: number;\n toeIndex: number;\n}>;\n\nexport interface OpenEnd {\n path: BranchPath;\n attach: 'run-tail' | 'side-toe';\n /** Pip value a tile must match to attach here. */\n value: number;\n /** For side-toe ends: which double in the branch, and which toe slot. */\n doubleIndex?: number;\n toeSlot?: number;\n /** The tile being attached to is a double (for the no-consecutive rule). */\n attachToDouble: boolean;\n /** This end exists only because an unanswered double must be satisfied. */\n obligation: boolean;\n}\n\nexport interface Move {\n end: OpenEnd;\n tile: DominoValue;\n}\n\nexport type PlacementViolation =\n | 'value-mismatch'\n | 'duplicate-tile'\n | 'consecutive-doubles';\n\nexport interface PlacementResult {\n legal: boolean;\n violations: PlacementViolation[];\n}\n\nexport function getBranchAt(\n root: TrainBranch,\n path: BranchPath\n): TrainBranch | undefined {\n let current: TrainBranch | undefined = root;\n for (const step of path) {\n current = current?.feet?.[step.doubleIndex]?.[step.toeIndex];\n if (!current) return undefined;\n }\n return current;\n}\n\ninterface DoubleStatus {\n path: BranchPath;\n doubleIndex: number;\n value: number;\n hasCenter: boolean;\n sideToes: number;\n answers: number;\n}\n\nfunction walkBranches(\n branch: TrainBranch,\n path: BranchPath,\n visit: (branch: TrainBranch, path: BranchPath) => void\n): void {\n visit(branch, path);\n if (!branch.feet) return;\n for (const key of Object.keys(branch.feet)) {\n const doubleIndex = Number(key);\n branch.feet[doubleIndex].forEach((toe, toeIndex) => {\n walkBranches(toe, [...path, { doubleIndex, toeIndex }], visit);\n });\n }\n}\n\nfunction collectDoubles(root: TrainBranch): DoubleStatus[] {\n const out: DoubleStatus[] = [];\n walkBranches(root, [], (branch, path) => {\n branch.dominoes.forEach((domino, doubleIndex) => {\n if (domino.value1 !== domino.value2) return;\n const hasCenter = doubleIndex < branch.dominoes.length - 1;\n const sideToes = branch.feet?.[doubleIndex]?.length ?? 0;\n out.push({\n path,\n doubleIndex,\n value: domino.value1,\n hasCenter,\n sideToes,\n answers: (hasCenter ? 1 : 0) + sideToes,\n });\n });\n });\n return out;\n}\n\n/** Doubles that still owe answers under the current rules. */\nexport function getUnsatisfiedDoubles(\n root: TrainBranch,\n config: RulesConfig\n): DoubleStatus[] {\n const required = requiredDoubleAnswers(config);\n if (required <= 0) return [];\n return collectDoubles(root).filter((d) => d.answers < required);\n}\n\n/** Every key of every tile already placed in the tree (for uniqueness checks). */\nexport function collectPlayedKeys(root: TrainBranch): Set<string> {\n const keys = new Set<string>();\n walkBranches(root, [], (branch) => {\n for (const domino of branch.dominoes) {\n keys.add(dominoKey(domino));\n }\n });\n return keys;\n}\n\n/**\n * All places a tile may legally attach to this train, honoring double\n * obligations. When a double is unanswered (and the rules require answers),\n * only that double's open slots are offered until it is satisfied.\n */\nexport function getOpenEnds(\n root: TrainBranch,\n startValue: number,\n config: RulesConfig\n): OpenEnd[] {\n if (root.dominoes.length === 0) {\n return [\n {\n path: [],\n attach: 'run-tail',\n value: startValue,\n attachToDouble: true, // a train starts off the engine double\n obligation: false,\n },\n ];\n }\n\n const unsatisfied = getUnsatisfiedDoubles(root, config);\n\n if (config.doubleObligation !== 'none' && unsatisfied.length > 0) {\n const slots = sideToeSlots(config);\n const ends: OpenEnd[] = [];\n\n for (const status of unsatisfied) {\n const branch = getBranchAt(root, status.path);\n if (!branch) continue;\n\n // Center continuation: only available if the double is the run's tail.\n if (!status.hasCenter && status.doubleIndex === branch.dominoes.length - 1) {\n ends.push({\n path: status.path,\n attach: 'run-tail',\n value: status.value,\n attachToDouble: true,\n obligation: true,\n });\n }\n\n if (status.sideToes < slots) {\n ends.push({\n path: status.path,\n attach: 'side-toe',\n value: status.value,\n doubleIndex: status.doubleIndex,\n toeSlot: status.sideToes,\n attachToDouble: true,\n obligation: true,\n });\n }\n }\n\n return ends;\n }\n\n // No active obligation: the growing tip of every branch is open.\n const ends: OpenEnd[] = [];\n walkBranches(root, [], (branch, path) => {\n const last = branch.dominoes[branch.dominoes.length - 1];\n if (!last) return;\n ends.push({\n path,\n attach: 'run-tail',\n value: last.value2,\n attachToDouble: isDouble(last),\n obligation: false,\n });\n });\n return ends;\n}\n\nexport function evaluatePlacement(\n tile: DominoValue,\n end: OpenEnd,\n playedKeys: ReadonlySet<string>,\n config: RulesConfig\n): PlacementResult {\n const violations: PlacementViolation[] = [];\n\n const oriented = orientForConnection(tile, end.value);\n if (config.requireSequential && !oriented) {\n violations.push('value-mismatch');\n }\n\n if (config.requireUniqueTiles && playedKeys.has(dominoKey(tile))) {\n violations.push('duplicate-tile');\n }\n\n if (!config.allowConsecutiveDoubles && end.attachToDouble && isDouble(tile)) {\n violations.push('consecutive-doubles');\n }\n\n return { legal: violations.length === 0, violations };\n}\n\n/** Every legal (open end × hand tile) move for this train. */\nexport function getLegalMoves(\n root: TrainBranch,\n startValue: number,\n hand: readonly DominoValue[],\n playedKeys: ReadonlySet<string>,\n config: RulesConfig\n): Move[] {\n const ends = getOpenEnds(root, startValue, config);\n const moves: Move[] = [];\n for (const end of ends) {\n for (const tile of hand) {\n if (evaluatePlacement(tile, end, playedKeys, config).legal) {\n moves.push({ end, tile });\n }\n }\n }\n return moves;\n}\n\nfunction updateBranchAt(\n branch: TrainBranch,\n path: BranchPath,\n updater: (branch: TrainBranch) => TrainBranch\n): TrainBranch {\n if (path.length === 0) {\n return updater(branch);\n }\n const [step, ...rest] = path;\n const toes = branch.feet?.[step.doubleIndex] ?? [];\n const updatedToes = toes.map((toe, index) =>\n index === step.toeIndex ? updateBranchAt(toe, rest, updater) : toe\n );\n return {\n ...branch,\n feet: { ...branch.feet, [step.doubleIndex]: updatedToes },\n };\n}\n\n/**\n * Returns a new tree with `move` applied. The tile is oriented so its matching\n * end connects. Does not validate; call {@link evaluatePlacement} first (or use\n * {@link playMove}).\n */\nexport function applyMove(\n root: TrainBranch,\n move: Move,\n _config?: RulesConfig\n): TrainBranch {\n const oriented =\n orientForConnection(move.tile, move.end.value) ?? { ...move.tile };\n\n return updateBranchAt(root, move.end.path, (branch) => {\n if (move.end.attach === 'run-tail') {\n return { ...branch, dominoes: [...branch.dominoes, oriented] };\n }\n\n const doubleIndex = move.end.doubleIndex ?? 0;\n const slot = move.end.toeSlot ?? branch.feet?.[doubleIndex]?.length ?? 0;\n const existing = branch.feet?.[doubleIndex]\n ? [...branch.feet[doubleIndex]]\n : [];\n existing[slot] = { dominoes: [oriented] };\n return {\n ...branch,\n feet: { ...branch.feet, [doubleIndex]: existing },\n };\n });\n}\n\nexport interface PlayMoveResult {\n ok: boolean;\n board: TrainBranch;\n violations: PlacementViolation[];\n}\n\n/** Validates a move against the rules and applies it only if legal. */\nexport function playMove(\n root: TrainBranch,\n move: Move,\n config: RulesConfig\n): PlayMoveResult {\n const result = evaluatePlacement(\n move.tile,\n move.end,\n collectPlayedKeys(root),\n config\n );\n if (!result.legal) {\n return { ok: false, board: root, violations: result.violations };\n }\n return { ok: true, board: applyMove(root, move, config), violations: [] };\n}\n","import { DominoValue } from '@/game/DominoValue';\nimport { TrainData } from '@/game/TrainData';\nimport { RulesConfig } from '@/rules/rulesConfig';\nimport { Move } from '@/rules/placement';\nimport { GenericHeuristic, Rng, SkillProfile } from './policy';\n\n// The skill/RNG primitives live in the model-agnostic policy core.\nexport type { Rng, SkillProfile } from './policy';\n\n/**\n * Base shape every action shares. Games extend the action space by declaring\n * their own `kind` literals (e.g. Warp12's `'deploy-beacon'`) and unioning them\n * with {@link AiAction}; the scoring pipeline treats unknown kinds opaquely.\n */\nexport interface AiActionBase {\n readonly kind: string;\n}\n\n/** Attach `tile` at `move.end` of the train at `trainIndex` in the observation. */\nexport interface AiPlayAction extends AiActionBase {\n readonly kind: 'play';\n readonly trainIndex: number;\n readonly move: Move;\n}\n\nexport interface AiDrawAction extends AiActionBase {\n readonly kind: 'draw';\n}\n\nexport interface AiPassAction extends AiActionBase {\n readonly kind: 'pass';\n}\n\n/** The base action space shared by all double-N variants. */\nexport type AiAction = AiPlayAction | AiDrawAction | AiPassAction;\n\n/** Narrows any action to a play action (kind discriminant on the base is widened). */\nexport function isPlayAction(action: AiActionBase): action is AiPlayAction {\n return action.kind === 'play';\n}\n\n/**\n * Everything the bot is allowed to see this turn. Game-specific extras (beacon\n * flags, fracture state, scores, turn order…) ride along in {@link meta} so\n * custom heuristics can read them without changing this interface.\n */\nexport interface AiObservation {\n readonly selfPlayerId: number;\n readonly hand: readonly DominoValue[];\n readonly rules: RulesConfig;\n readonly trains: readonly TrainData[];\n readonly engineValue: number;\n /** Tiles left to draw; omit for \"unlimited/unknown\". 0 forbids drawing. */\n readonly drawPileSize?: number;\n /**\n * Set by the host once this player has already taken their single draw this\n * turn. Standard Mexican Train allows exactly one draw when you can't play;\n * if the drawn tile still can't be played you must pass (which marks your\n * train public). When true the generator stops offering `draw`, so the bot\n * falls through to `pass` instead of draining the pile.\n */\n readonly turnDrawUsed?: boolean;\n readonly meta?: Readonly<Record<string, unknown>>;\n}\n\n/** Shared, pre-computed turn data handed to every heuristic (built once per decision). */\nexport interface EvalContext {\n readonly obs: AiObservation;\n /** Canonical keys of every tile already on the table (global uniqueness). */\n readonly playedKeys: ReadonlySet<string>;\n readonly candidates: readonly AiActionBase[];\n readonly playCandidates: readonly AiPlayAction[];\n /** Tiles neither played nor in hand — the basis for tile-counting heuristics. */\n readonly unseen: readonly DominoValue[];\n readonly rng: Rng;\n}\n\n/**\n * A single, pure rule-of-thumb over the domino action space. Higher score =\n * more attractive; return 0 when it doesn't apply so it stays weight-neutral.\n * This is the domino specialization of the generic {@link GenericHeuristic}.\n */\nexport type Heuristic = GenericHeuristic<AiActionBase, EvalContext>;\n\n/** Produces the legal/considered actions for a turn. Override to change rules access. */\nexport type CandidateGenerator<TAction extends AiActionBase = AiAction> = (\n obs: AiObservation\n) => TAction[];\n\nexport interface AiPlayer<TAction extends AiActionBase = AiAction> {\n decide(obs: AiObservation): TAction;\n}\n\nexport interface CreateAiPlayerOptions<TAction extends AiActionBase = AiAction> {\n skill: SkillProfile;\n /** Defaults to {@link DEFAULT_HEURISTICS}. Append your own to extend behavior. */\n heuristics?: Heuristic[];\n /** Defaults to {@link defaultCandidateGenerator}. */\n generateCandidates?: CandidateGenerator<TAction>;\n /** Defaults to `Math.random`. Inject a seeded RNG for reproducible games/tests. */\n rng?: Rng;\n}\n","import { TrainData } from '@/game/TrainData';\nimport { collectPlayedKeys, getLegalMoves } from '@/rules/placement';\nimport {\n AiAction,\n AiObservation,\n AiPlayAction,\n CandidateGenerator,\n} from './types';\n\n/**\n * Indices (into `obs.trains`) the player may legally build on under standard\n * Mexican Train access: your own train, plus any train flagged public. Games\n * with richer access rules (Warp12's Distress Beacon, locked fractures, …)\n * supply their own {@link CandidateGenerator}.\n */\nexport function getAccessibleTrainIndices(obs: AiObservation): number[] {\n const indices: number[] = [];\n obs.trains.forEach((train, index) => {\n if (train.playerId === obs.selfPlayerId || train.isPublic) {\n indices.push(index);\n }\n });\n return indices;\n}\n\n/** Union of every played tile's key across all trains (uniqueness is global). */\nexport function collectAllPlayedKeys(\n trains: readonly TrainData[]\n): Set<string> {\n const keys = new Set<string>();\n for (const train of trains) {\n for (const key of collectPlayedKeys(train)) {\n keys.add(key);\n }\n }\n return keys;\n}\n\n/** Every legal placement of a hand tile onto an accessible train. */\nexport function generatePlayActions(obs: AiObservation): AiPlayAction[] {\n const playedKeys = collectAllPlayedKeys(obs.trains);\n const actions: AiPlayAction[] = [];\n\n for (const trainIndex of getAccessibleTrainIndices(obs)) {\n const train = obs.trains[trainIndex];\n const moves = getLegalMoves(\n train,\n obs.engineValue,\n obs.hand,\n playedKeys,\n obs.rules\n );\n for (const move of moves) {\n actions.push({ kind: 'play', trainIndex, move });\n }\n }\n\n return actions;\n}\n\nexport interface CandidateGeneratorOptions {\n /**\n * Offer `draw` even when legal plays exist. Off by default (canonical \"must\n * play if you can\"). Turn on for variants where drawing is always optional —\n * combined with a high blunder rate this is what makes a beginner draw when\n * they didn't have to.\n */\n allowOptionalDraw?: boolean;\n}\n\n/**\n * Builds the standard candidate set: all legal plays, plus `draw` when the pile\n * isn't empty (and either there are no plays, or optional drawing is enabled),\n * falling back to `pass` only when nothing else is possible.\n */\nexport function createCandidateGenerator(\n options: CandidateGeneratorOptions = {}\n): CandidateGenerator<AiAction> {\n return (obs) => {\n const plays = generatePlayActions(obs);\n const actions: AiAction[] = [...plays];\n\n const pileHasTiles = (obs.drawPileSize ?? Number.POSITIVE_INFINITY) > 0;\n // One draw per turn: once the host marks the draw used, the bot must pass\n // (which is what flips its own train public) rather than draw again.\n const canDraw = pileHasTiles && !obs.turnDrawUsed;\n if (canDraw && (plays.length === 0 || options.allowOptionalDraw)) {\n actions.push({ kind: 'draw' });\n }\n if (actions.length === 0) {\n actions.push({ kind: 'pass' });\n }\n return actions;\n };\n}\n\nexport const defaultCandidateGenerator = createCandidateGenerator();\n","import {\n dominoKey,\n orientForConnection,\n tileHasValue,\n} from '@/rules/dominoSet';\nimport { AiPlayAction, Heuristic, isPlayAction } from './types';\n\n/** Stable ids so skill profiles and overrides can reference heuristics by name. */\nexport const HEURISTIC_IDS = {\n preferPlay: 'prefer-play',\n dumpPips: 'dump-pips',\n doublesEarly: 'play-doubles-early',\n ownTrain: 'own-train',\n obligationRelief: 'obligation-relief',\n handFlexibility: 'hand-flexibility',\n defensivePublic: 'defensive-public',\n} as const;\n\n/** Pip value the open end will expose after this tile is oriented and placed. */\nfunction newOpenEndValue(action: AiPlayAction): number {\n const oriented = orientForConnection(action.move.tile, action.move.end.value);\n return oriented ? oriented.value2 : action.move.tile.value2;\n}\n\n/**\n * Strongly favors playing over drawing, and drawing over passing. This is the\n * baseline that keeps every competent profile playing whenever it legally can;\n * mistakes come from the blunder rate, not from declining a free play.\n */\nconst preferPlay: Heuristic = {\n id: HEURISTIC_IDS.preferPlay,\n score(action) {\n if (action.kind === 'play') return 100;\n if (action.kind === 'draw') return 0;\n return -50;\n },\n};\n\n/** Offload weight: shed the heaviest tiles first to minimize end-of-round penalty. */\nconst dumpPips: Heuristic = {\n id: HEURISTIC_IDS.dumpPips,\n score(action) {\n if (!isPlayAction(action)) return 0;\n const tile = action.move.tile;\n return tile.value1 + tile.value2;\n },\n};\n\n/** Doubles are hard to get rid of late; nudge the bot to play them while its hand is full. */\nconst playDoublesEarly: Heuristic = {\n id: HEURISTIC_IDS.doublesEarly,\n score(action, ctx) {\n if (!isPlayAction(action)) return 0;\n const tile = action.move.tile;\n if (tile.value1 !== tile.value2) return 0;\n return Math.min(ctx.obs.hand.length, 12);\n },\n};\n\n/** Playing on your own train keeps it active (shields up) and under your control. */\nconst ownTrain: Heuristic = {\n id: HEURISTIC_IDS.ownTrain,\n score(action, ctx) {\n if (!isPlayAction(action)) return 0;\n const train = ctx.obs.trains[action.trainIndex];\n return train && train.playerId === ctx.obs.selfPlayerId ? 8 : 0;\n },\n};\n\n/** Clearing an outstanding obligation (e.g. covering a double) unblocks the board. */\nconst obligationRelief: Heuristic = {\n id: HEURISTIC_IDS.obligationRelief,\n score(action) {\n if (!isPlayAction(action)) return 0;\n return action.move.end.obligation ? 10 : 0;\n },\n};\n\n/**\n * Rewards leaving yourself a follow-up: how many of your remaining tiles can\n * attach to the new open end this move creates. Encourages building runs you\n * can actually continue rather than stranding yourself.\n */\nconst handFlexibility: Heuristic = {\n id: HEURISTIC_IDS.handFlexibility,\n score(action, ctx) {\n if (!isPlayAction(action)) return 0;\n const endValue = newOpenEndValue(action);\n const playedKey = dominoKey(action.move.tile);\n\n let skipped = false;\n let matches = 0;\n for (const tile of ctx.obs.hand) {\n if (!skipped && dominoKey(tile) === playedKey) {\n skipped = true; // don't count the tile we're about to play\n continue;\n }\n if (tileHasValue(tile, endValue)) matches++;\n }\n return matches * 3;\n },\n};\n\n/**\n * Defensive play on shared/opponent trains: prefer leaving an open end that is\n * hard for others to extend (few unseen tiles match it). Neutral on your own\n * train, where flow is desirable instead.\n */\nconst defensivePublic: Heuristic = {\n id: HEURISTIC_IDS.defensivePublic,\n score(action, ctx) {\n if (!isPlayAction(action)) return 0;\n const train = ctx.obs.trains[action.trainIndex];\n if (!train || train.playerId === ctx.obs.selfPlayerId) return 0;\n\n const endValue = newOpenEndValue(action);\n let openCount = 0;\n for (const tile of ctx.unseen) {\n if (tileHasValue(tile, endValue)) openCount++;\n }\n return -openCount;\n },\n};\n\n/** The stock, game-agnostic heuristic set. Append/replace by `id` to customize. */\nexport const DEFAULT_HEURISTICS: Heuristic[] = [\n preferPlay,\n dumpPips,\n playDoublesEarly,\n ownTrain,\n obligationRelief,\n handFlexibility,\n defensivePublic,\n];\n","import { HEURISTIC_IDS } from './heuristics';\nimport { SkillProfile } from './types';\n\nconst H = HEURISTIC_IDS;\n\n/**\n * Stock skill tiers. Each is just a configuration of the same engine:\n *\n * - **beginner** — only cares about playing and lightly about dumping pips, with\n * high temperature and a real blunder rate: erratic, often suboptimal plays.\n * - **intermediate** — adds doubles-early and own-train sense, low noise.\n * - **advanced** — full heuristic suite (obligations, flexibility, defense),\n * near-deterministic, no blunders.\n *\n * Clone and tweak (`{ ...SKILL_PRESETS.advanced, temperature: 0.3 }`) for any\n * point on the spectrum.\n */\nexport const SKILL_PRESETS: Record<'beginner' | 'intermediate' | 'advanced', SkillProfile> = {\n beginner: {\n id: 'beginner',\n temperature: 2.5,\n blunderRate: 0.25,\n lookaheadDepth: 0,\n enabled: new Set([H.preferPlay, H.dumpPips]),\n weights: {\n [H.preferPlay]: 1,\n [H.dumpPips]: 0.2,\n },\n },\n intermediate: {\n id: 'intermediate',\n temperature: 0.6,\n blunderRate: 0.05,\n lookaheadDepth: 0,\n enabled: new Set([H.preferPlay, H.dumpPips, H.doublesEarly, H.ownTrain]),\n weights: {\n [H.preferPlay]: 1,\n [H.dumpPips]: 1,\n [H.doublesEarly]: 1,\n [H.ownTrain]: 1,\n },\n },\n advanced: {\n id: 'advanced',\n temperature: 0.15,\n blunderRate: 0,\n lookaheadDepth: 0,\n enabled: new Set([\n H.preferPlay,\n H.dumpPips,\n H.doublesEarly,\n H.ownTrain,\n H.obligationRelief,\n H.handFlexibility,\n H.defensivePublic,\n ]),\n weights: {\n [H.preferPlay]: 1,\n [H.dumpPips]: 1.2,\n [H.doublesEarly]: 1.5,\n [H.ownTrain]: 1,\n [H.obligationRelief]: 1.5,\n [H.handFlexibility]: 1,\n [H.defensivePublic]: 1.5,\n },\n },\n};\n\nexport type SkillLevel = keyof typeof SKILL_PRESETS;\n\nexport function getSkillProfile(level: SkillLevel): SkillProfile {\n return SKILL_PRESETS[level];\n}\n","/**\n * Model-agnostic decision core shared by every game built on this library.\n *\n * Nothing here knows about dominoes, trains, or any specific rule set: it only\n * knows how to turn a set of candidate actions into one chosen action, given a\n * skill profile and a way to score actions. The domino-specific player\n * (`createAiPlayer`) and downstream variants (e.g. Warp12) are thin adapters\n * over {@link createPolicyPlayer}.\n */\n\n/** Pseudo-random source in [0, 1). Inject a seeded one for deterministic play. */\nexport type Rng = () => number;\n\n/**\n * The dials that define \"skill\". The same engine spans beginner→advanced purely\n * by changing which heuristics are active, their weights, and how sharply (or\n * randomly) the policy commits to the highest-scoring action.\n */\nexport interface SkillProfile {\n readonly id: string;\n /** Softmax temperature over candidate scores. 0 = argmax; higher = noisier. */\n readonly temperature: number;\n /** Probability of ignoring the policy and picking a uniformly random action. */\n readonly blunderRate: number;\n /** Plies of simulation (0 = greedy). Reserved; greedy-only in this release. */\n readonly lookaheadDepth: number;\n readonly weights: Readonly<Record<string, number>>;\n readonly enabled: ReadonlySet<string>;\n}\n\n/**\n * A single, pure rule-of-thumb over actions of type `TAction`, given a turn\n * context of type `TCtx`. Higher score = more attractive; return 0 when the\n * heuristic doesn't apply so it stays weight-neutral.\n */\nexport interface GenericHeuristic<TAction, TCtx> {\n readonly id: string;\n score(action: TAction, ctx: TCtx): number;\n}\n\n/** Weighted sum of the enabled heuristics for one action. */\nexport function scoreWithHeuristics<TAction, TCtx>(\n action: TAction,\n ctx: TCtx,\n byId: ReadonlyMap<string, GenericHeuristic<TAction, TCtx>>,\n skill: SkillProfile\n): number {\n let total = 0;\n for (const id of skill.enabled) {\n const heuristic = byId.get(id);\n if (!heuristic) continue;\n const weight = skill.weights[id] ?? 1;\n total += weight * heuristic.score(action, ctx);\n }\n return total;\n}\n\n/** Index of the max score, breaking ties uniformly at random. */\nexport function argmaxIndex(scores: readonly number[], rng: Rng): number {\n let best = Number.NEGATIVE_INFINITY;\n let tied: number[] = [];\n scores.forEach((score, index) => {\n if (score > best) {\n best = score;\n tied = [index];\n } else if (score === best) {\n tied.push(index);\n }\n });\n return tied[Math.floor(rng() * tied.length)];\n}\n\n/** Sample an index proportional to exp(score / temperature). */\nexport function softmaxIndex(\n scores: readonly number[],\n temperature: number,\n rng: Rng\n): number {\n const max = Math.max(...scores);\n const weights = scores.map((score) => Math.exp((score - max) / temperature));\n const sum = weights.reduce((acc, value) => acc + value, 0);\n\n let threshold = rng() * sum;\n for (let i = 0; i < weights.length; i++) {\n threshold -= weights[i];\n if (threshold <= 0) return i;\n }\n return weights.length - 1;\n}\n\n/** Temperature-controlled choice: argmax at 0, softmax sampling above it. */\nexport function chooseActionIndex(\n scores: readonly number[],\n skill: SkillProfile,\n rng: Rng\n): number {\n if (scores.length <= 1) return 0;\n if (skill.temperature <= 0) return argmaxIndex(scores, rng);\n return softmaxIndex(scores, skill.temperature, rng);\n}\n\nexport interface PolicyPlayerConfig<TObs, TAction, TCtx> {\n skill: SkillProfile;\n heuristics: ReadonlyArray<GenericHeuristic<TAction, TCtx>>;\n generateCandidates: (obs: TObs) => TAction[];\n buildContext: (obs: TObs, candidates: readonly TAction[]) => TCtx;\n /** Returned when the generator yields no candidates at all. */\n fallback: (obs: TObs) => TAction;\n rng?: Rng;\n}\n\nexport interface PolicyPlayer<TObs, TAction> {\n decide(obs: TObs): TAction;\n}\n\n/**\n * The reusable decision engine. Per turn:\n *\n * observation → candidates → (blunder?) → weighted heuristics → policy → action\n *\n * Context is built once per decision and shared across heuristics. A single\n * candidate short-circuits scoring; an empty set returns `fallback`.\n */\nexport function createPolicyPlayer<TObs, TAction, TCtx>(\n config: PolicyPlayerConfig<TObs, TAction, TCtx>\n): PolicyPlayer<TObs, TAction> {\n const { skill, generateCandidates, buildContext, fallback } = config;\n const rng = config.rng ?? Math.random;\n const byId = new Map(config.heuristics.map((heuristic) => [heuristic.id, heuristic] as const));\n\n return {\n decide(obs) {\n const candidates = generateCandidates(obs);\n if (candidates.length === 0) return fallback(obs);\n if (candidates.length === 1) return candidates[0];\n if (skill.blunderRate > 0 && rng() < skill.blunderRate) {\n return candidates[Math.floor(rng() * candidates.length)];\n }\n\n const ctx = buildContext(obs, candidates);\n const scores = candidates.map((candidate) =>\n scoreWithHeuristics(candidate, ctx, byId, skill)\n );\n return candidates[chooseActionIndex(scores, skill, rng)];\n },\n };\n}\n","import { dominoKey, generateDominoSet } from '@/rules/dominoSet';\nimport { collectAllPlayedKeys, defaultCandidateGenerator } from './candidate-generator';\nimport { DEFAULT_HEURISTICS } from './heuristics';\nimport { GenericHeuristic, Rng, createPolicyPlayer } from './policy';\nimport {\n AiAction,\n AiActionBase,\n AiObservation,\n AiPlayer,\n CandidateGenerator,\n CreateAiPlayerOptions,\n EvalContext,\n isPlayAction,\n} from './types';\n\nfunction buildEvalContext(\n obs: AiObservation,\n candidates: readonly AiActionBase[],\n rng: Rng\n): EvalContext {\n const playedKeys = collectAllPlayedKeys(obs.trains);\n\n const seen = new Set<string>(playedKeys);\n for (const tile of obs.hand) {\n seen.add(dominoKey(tile));\n }\n const unseen = generateDominoSet(obs.rules.maxPips).filter(\n (tile) => !seen.has(dominoKey(tile))\n );\n\n return {\n obs,\n playedKeys,\n candidates,\n playCandidates: candidates.filter(isPlayAction),\n unseen,\n rng,\n };\n}\n\n/**\n * Builds an offline, heuristic-driven domino player over the standard double-N\n * model. The decision flow per turn:\n *\n * observation → candidate generator → weighted heuristics → policy → action\n *\n * Every stage is injectable: swap the generator to change rules access, append\n * heuristics (including ones that read custom `kind`s or `obs.meta`) to teach it\n * variant-specific tactics, and pick/clone a {@link SkillProfile} to set strength.\n * Pass a seeded {@link Rng} for fully reproducible games. Under the hood this is\n * a thin adapter over the model-agnostic {@link createPolicyPlayer}.\n */\nexport function createAiPlayer<TAction extends AiActionBase = AiAction>(\n options: CreateAiPlayerOptions<TAction>\n): AiPlayer<TAction> {\n const heuristics = options.heuristics ?? DEFAULT_HEURISTICS;\n const generate: CandidateGenerator<TAction> =\n options.generateCandidates ??\n (defaultCandidateGenerator as unknown as CandidateGenerator<TAction>);\n const rng = options.rng ?? Math.random;\n\n return createPolicyPlayer<AiObservation, TAction, EvalContext>({\n skill: options.skill,\n heuristics: heuristics as ReadonlyArray<GenericHeuristic<TAction, EvalContext>>,\n generateCandidates: generate,\n buildContext: (obs, candidates) => buildEvalContext(obs, candidates, rng),\n fallback: () => ({ kind: 'pass' } as unknown as TAction),\n rng,\n });\n}\n","/**\n * Model-agnostic lookahead (\"gaming it out\").\n *\n * The greedy policy ({@link createPolicyPlayer}) scores the *current* options\n * with handcrafted heuristics. Search instead *simulates*: it applies an action\n * to a forward model, lets the game continue for a few plies, and evaluates the\n * resulting position. With imperfect information (hidden hands, an unknown draw\n * order) we can't do plain minimax, so we use **determinized depth-limited\n * search** (a.k.a. Perfect-Information Monte Carlo):\n *\n * 1. sample a plausible full world consistent with what we can see,\n * 2. run depth-limited paranoid minimax in that now-perfect-information world,\n * 3. average each root action's value over several sampled worlds.\n *\n * Nothing here knows about any specific game — a caller supplies a\n * {@link SearchModel}. Warp12 plugs its engine in to get real lookahead; the\n * same core could drive any turn-based game with a forward model.\n */\n\nimport { Rng } from './policy';\n\nexport type PlayerRef = number | string;\n\n/**\n * The forward model the search drives. Implement these over your engine:\n * `applyAction` is the transition function, `evaluate` is the leaf heuristic\n * (higher = better for `perspective`), and `determinize` samples the hidden\n * state so the search isn't allowed to peek at information a player shouldn't\n * have. `orderActions` is an optional breadth control (good move ordering lets\n * `maxBranch` prune to the promising moves).\n */\nexport interface SearchModel<TState, TAction> {\n legalActions(state: TState): TAction[];\n applyAction(state: TState, action: TAction): TState;\n isTerminal(state: TState): boolean;\n currentPlayer(state: TState): PlayerRef;\n /** Position value from `perspective`'s point of view (higher is better). */\n evaluate(state: TState, perspective: PlayerRef): number;\n /** Sample a concrete world consistent with `perspective`'s knowledge. */\n determinize?(state: TState, perspective: PlayerRef, rng: Rng): TState;\n /** Reorder actions best-first; the search expands only the first `maxBranch`. */\n orderActions?(state: TState, actions: TAction[]): TAction[];\n}\n\nexport interface SearchOptions {\n /** Plies to look ahead, including the root action itself (>= 1). */\n depth: number;\n perspective: PlayerRef;\n rng?: Rng;\n /** Worlds to sample for imperfect-information averaging (default 1). */\n determinizations?: number;\n /** Cap candidates expanded per node (default unlimited). */\n maxBranch?: number;\n}\n\nexport interface ScoredAction<TAction> {\n readonly action: TAction;\n readonly value: number;\n}\n\nfunction limitedActions<TState, TAction>(\n state: TState,\n model: SearchModel<TState, TAction>,\n maxBranch: number\n): TAction[] {\n let actions = model.legalActions(state);\n if (model.orderActions) {\n actions = model.orderActions(state, actions);\n }\n if (Number.isFinite(maxBranch) && actions.length > maxBranch) {\n actions = actions.slice(0, maxBranch);\n }\n return actions;\n}\n\n/**\n * Paranoid minimax in a (now perfect-information) world: the perspective player\n * maximizes their own evaluation; everyone else is assumed to minimize it. This\n * is pessimistic but cheap and stable for multi-player games.\n */\nfunction minimaxValue<TState, TAction>(\n state: TState,\n model: SearchModel<TState, TAction>,\n depth: number,\n perspective: PlayerRef,\n maxBranch: number\n): number {\n if (depth <= 0 || model.isTerminal(state)) {\n return model.evaluate(state, perspective);\n }\n\n const actions = limitedActions(state, model, maxBranch);\n if (actions.length === 0) {\n return model.evaluate(state, perspective);\n }\n\n const maximizing = model.currentPlayer(state) === perspective;\n let best = maximizing ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY;\n for (const action of actions) {\n const value = minimaxValue(\n model.applyAction(state, action),\n model,\n depth - 1,\n perspective,\n maxBranch\n );\n best = maximizing ? Math.max(best, value) : Math.min(best, value);\n }\n return best;\n}\n\n/**\n * Value every root action by simulating it forward. For each action we average\n * its minimax value across `determinizations` sampled worlds. Returns one entry\n * per (ordered, breadth-capped) root action; the caller turns these values into\n * a choice (e.g. skill-scaled softmax via {@link chooseActionIndex}).\n */\nexport function searchActionValues<TState, TAction>(\n rootState: TState,\n model: SearchModel<TState, TAction>,\n options: SearchOptions\n): ScoredAction<TAction>[] {\n const rng = options.rng ?? Math.random;\n const samples = Math.max(1, options.determinizations ?? 1);\n const maxBranch = options.maxBranch ?? Number.POSITIVE_INFINITY;\n const depth = Math.max(1, options.depth);\n\n const rootActions = limitedActions(rootState, model, maxBranch);\n\n return rootActions.map((action) => {\n let total = 0;\n for (let sample = 0; sample < samples; sample++) {\n const world = model.determinize\n ? model.determinize(rootState, options.perspective, rng)\n : rootState;\n total += minimaxValue(\n model.applyAction(world, action),\n model,\n depth - 1,\n options.perspective,\n maxBranch\n );\n }\n return { action, value: total / samples };\n });\n}\n"],"names":["DEFAULT_DOMINO_THEME","mergeDominoTheme","base","patch","themeDataAttributes","attrs","result","key","value","DominoThemeContext","createContext","DominoThemeProvider","theme","children","parent","useContext","resolved","useDominoTheme","override","fromContext","DefaultPip","ctx","row","col","gridSize","color","hollow","positionStyle","themed","jsx","GRID_POSITIONS","resolvePipPosition","cell","grid","PIP_LAYOUTS","getPipLayout","DEFAULT_PIP_COLORS","PIP_COLORS","mergePipColors","overrides","resolvePipStyle","pipColors","getPipStyle","pipProps","pipColor","style","PipPattern","layout","Fragment","index","DominoHalf","DoubleTwelve","value1","value2","width","height","backgroundColor","borderColor","rotation","themeOverride","val1","val2","tileCtx","tileThemed","dividerThemed","jsxs","DOMINO_WIDTH","DOMINO_HEIGHT","CHICKEN_FOOT_TOE_ANGLES","halfExtentAlongTrain","isDouble","dominoWidth","dominoHeight","stepAlongTrain","fromIsDouble","toIsDouble","trainDirection","angle","angleRad","trainPerpendicular","dirX","dirY","orientDominoValues","dominoes","oriented","domino","i","prevValue","outwardPerpSign","nextPerpOffset","current","outwardSign","placeOrientedRun","orientedDominoes","startX","startY","layoutStyle","leadGap","hubIndex","perpX","perpY","isHub","laneByIndex","currentX","currentY","perpOffset","laneSign","perpStep","setPerpOffset","target","delta","prevIsDouble","shift","normalizeBends","bends","tileCount","byIndex","bend","turn","a","b","headingAtIndex","cleaned","heading","placeBentRun","input","boundaries","subStartX","subStartY","subLeadGap","s","slice","subHubIndex","sub","last","prevDir","halfPrev","nextFirst","nextIsDouble","halfNext","nextDir","nextPerp","targetX","targetY","seeds","seedX","seedY","computeTrainLayout","outwardSignInput","cleanedBends","tileCorners","entry","r","cos","sin","hw","hh","x","y","projectionGap","axis","aMin","aMax","bMin","bMax","p","d","tilesOverlap","epsilon","ca","cb","corners","q","ex","ey","len","overlapsAny","tile","others","other","layoutsCollide","obstacles","layoutSelfIntersects","j","TOE_PUSH_STEP","TOE_PUSH_MAX_STEPS","computeTrainTree","branch","depth","anchor","placed","pushAxis","minPushSteps","segmentOutward","buildLayout","originX","originY","appliedAnchor","k","trial","segments","hostIndex","host","toes","hostAngle","toeIndex","toe","toeOffset","sideSign","toeAngle","toePerp","outward","cornerX","cornerY","flattenSegments","segment","getTrainLayoutBounds","padding","halfExtent","minX","minY","maxX","maxY","DEFAULT_TOLERANCE","projectOnTrainAxis","dx","dy","projectOnPerpendicularAxis","validateDominoChain","issues","currentIsDouble","expectedAxisLayout","isDoubleArr","positions","along","perp","validateConsecutiveSpacing","tolerance","outwardSignOverride","expected","prev","expectedAlong","expectedPerp","validateNoPairOverlap","distance","minDistance","validateTrainLayout","validateChickenFootChain","walk","path","issue","first","validateTrainTree","segmentIndex","tiles","IS_DEV","DominoTrain","trainData","tableWidth","tableHeight","centerX","centerY","useEffect","trainLayout","useMemo","showMarker","hubTrainStartDistance","slots","radius","minNeighborGap","DominoHub","playerCount","engineValue","trains","hubSize","startDistance","_","radians","t","tileKey","dominoKey","tileHasValue","otherEnd","orientForConnection","connectingValue","generateDominoSet","maxPips","dominoSetSize","n","generateSampleTrains","options","usedTiles","playerId","dominoCount","openValue","prevWasDouble","pickNextValue","feet","buildFeet","doubleValue","buildToe","startValue","length","next","pickNonDouble","candidates","isFirstDomino","isValidNextValue","defaultGameState","MexicanTrainGame","initialState","pipColorsProp","onPipColorsChange","gameState","setGameState","useState","setLayoutStyle","chickenFeet","setChickenFeet","pipColorsInternal","setPipColorsInternal","setPipColors","pipColorsEnabled","regenerateTrains","chickenFeetEnabled","sampleTrains","prevState","toggleChickenFeet","TURN_DEGREES","sideToTurn","side","degrees","oppositeSide","offsetDefaultSide","linearDefaultSide","point","bounds","distanceToExit","sx","sy","rightRoom","leftRoom","buildBranchTiles","withBendAt","rest","resolveBend","build","preferredSide","candidateBranch","cycleBendAt","preferredTurn","oppositeTurn","isLegal","candidate","order","clampScale","scale","min","max","zoomAt","view","factor","pivot","effective","fitToBounds","content","viewport","safeW","safeH","raw","screenToContent","screen","PAN_THRESHOLD","Viewport","contentWidth","contentHeight","minScale","maxScale","zoomStep","background","showControls","testId","containerRef","useRef","fit","useCallback","setView","pan","localPoint","clientX","clientY","rect","onPointerDown","event","onPointerMove","endPan","onWheel","zoomButton","buttonStyle","TRAIN_FIXTURES","CHICKEN_FOOT_FIXTURES","DEFAULT_RULES","requiredDoubleAnswers","config","sideToeSlots","resolveRules","getBranchAt","root","step","walkBranches","visit","doubleIndex","collectDoubles","out","hasCenter","sideToes","getUnsatisfiedDoubles","required","collectPlayedKeys","keys","getOpenEnds","unsatisfied","ends","status","evaluatePlacement","end","playedKeys","violations","getLegalMoves","hand","moves","updateBranchAt","updater","updatedToes","applyMove","move","_config","slot","existing","playMove","isPlayAction","action","getAccessibleTrainIndices","obs","indices","train","collectAllPlayedKeys","generatePlayActions","actions","trainIndex","createCandidateGenerator","plays","defaultCandidateGenerator","HEURISTIC_IDS","newOpenEndValue","preferPlay","dumpPips","playDoublesEarly","ownTrain","obligationRelief","handFlexibility","endValue","playedKey","skipped","matches","defensivePublic","openCount","DEFAULT_HEURISTICS","H","SKILL_PRESETS","getSkillProfile","level","scoreWithHeuristics","byId","skill","total","id","heuristic","weight","argmaxIndex","scores","rng","best","tied","score","softmaxIndex","temperature","weights","sum","acc","threshold","chooseActionIndex","createPolicyPlayer","generateCandidates","buildContext","fallback","buildEvalContext","seen","unseen","createAiPlayer","heuristics","generate","limitedActions","state","model","maxBranch","minimaxValue","perspective","maximizing","searchActionValues","rootState","samples","sample","world"],"mappings":"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"}