diff --git a/src/components/DominoRetilingPuzzle.tsx b/src/components/DominoRetilingPuzzle.tsx index 65898b3..b58aa02 100644 --- a/src/components/DominoRetilingPuzzle.tsx +++ b/src/components/DominoRetilingPuzzle.tsx @@ -28,44 +28,62 @@ const DominoRetilingPuzzle: React.FC = ({ showSocialS const BOARD_ROWS = boardSize; const BOARD_COLS = boardSize + 1; // n + 1 extra square on top row - // Generate a random valid tiling of the n×n board + // Generate a random valid tiling of the n×n board using backtracking const generateRandomTiling = useCallback(() => { const newDominoes: Domino[] = []; const occupied = new Set(); let dominoId = 0; - // Fill the n×n board with random dominoes - for (let row = 0; row < boardSize; row++) { - for (let col = 0; col < boardSize; col++) { - const key = `${row},${col}`; - if (occupied.has(key)) continue; - - // Randomly choose horizontal or vertical - const canHorizontal = col + 1 < boardSize && !occupied.has(`${row},${col + 1}`); - const canVertical = row + 1 < boardSize && !occupied.has(`${row + 1},${col}`); - - if (!canHorizontal && !canVertical) continue; - - let isHorizontal: boolean; - if (canHorizontal && canVertical) { - isHorizontal = Math.random() > 0.5; - } else { - isHorizontal = canHorizontal; + // Helper to find first uncovered cell + const findFirstUncovered = (): { row: number; col: number } | null => { + for (let row = 0; row < boardSize; row++) { + for (let col = 0; col < boardSize; col++) { + if (!occupied.has(`${row},${col}`)) { + return { row, col }; + } } + } + return null; + }; - newDominoes.push({ - id: dominoId++, - row, - col, - isHorizontal - }); + // Greedy fill with backtracking if needed + while (true) { + const cell = findFirstUncovered(); + if (!cell) break; // All covered - occupied.add(key); - if (isHorizontal) { - occupied.add(`${row},${col + 1}`); - } else { - occupied.add(`${row + 1},${col}`); - } + const { row, col } = cell; + + // Try to place a domino here + const canHorizontal = col + 1 < boardSize && !occupied.has(`${row},${col + 1}`); + const canVertical = row + 1 < boardSize && !occupied.has(`${row + 1},${col}`); + + if (!canHorizontal && !canVertical) { + // Stuck - this shouldn't happen with even board sizes, but reset if it does + newDominoes.length = 0; + occupied.clear(); + dominoId = 0; + continue; + } + + let isHorizontal: boolean; + if (canHorizontal && canVertical) { + isHorizontal = Math.random() > 0.5; + } else { + isHorizontal = canHorizontal; + } + + newDominoes.push({ + id: dominoId++, + row, + col, + isHorizontal + }); + + occupied.add(`${row},${col}`); + if (isHorizontal) { + occupied.add(`${row},${col + 1}`); + } else { + occupied.add(`${row + 1},${col}`); } } @@ -174,6 +192,25 @@ const DominoRetilingPuzzle: React.FC = ({ showSocialS const renderBoard = () => { const cells: JSX.Element[] = []; + // Render the inaccessible shaded region (last column except top cell) + for (let row = 1; row < BOARD_ROWS; row++) { + cells.push( +
+ ); + } + // Render cells for (let row = 0; row < BOARD_ROWS; row++) { const maxCol = row === 0 ? BOARD_COLS : boardSize; @@ -193,15 +230,10 @@ const DominoRetilingPuzzle: React.FC = ({ showSocialS key={`cell-${row}-${col}`} className={`border border-border/50 ${ isExtraSquare - ? 'bg-blue-500/40' + ? 'bg-blue-400/50' : (row + col) % 2 === 0 ? 'bg-muted/30' : 'bg-muted/60' } ${selectedDomino !== null ? 'cursor-pointer' : ''}`} - style={{ - ...cellStyle, - ...(isExtraSquare ? { - backgroundImage: 'repeating-linear-gradient(45deg, transparent, transparent 4px, rgba(59, 130, 246, 0.3) 4px, rgba(59, 130, 246, 0.3) 8px)', - } : {}) - }} + style={cellStyle} onMouseEnter={() => handleCellHover(row, col)} onMouseLeave={() => setHoverPlacement(null)} onClick={() => {