// Grid dimensions for each piece count (cols × rows) const GRID_SIZES = { 20: { cols: 5, rows: 4 }, 40: { cols: 8, rows: 5 }, 60: { cols: 10, rows: 6 }, 100: { cols: 10, rows: 10 } }; class PuzzleGenerator { /** * Generate puzzle piece data for the given piece count. * @param {number} pieceCount — 40, 60, or 100 * @returns {{ cols, rows, pieces: PieceData[], horizConnectors: string[][], vertConnectors: string[][] }} * * horizConnectors[r][c] = 'tab'|'blank' → right edge of cell [r][c] * (left edge of [r][c+1] is the opposite) * vertConnectors[r][c] = 'tab'|'blank' → bottom edge of cell [r][c] * (top edge of [r+1][c] is the opposite) */ static generate(pieceCount) { const { cols, rows } = GRID_SIZES[pieceCount] || GRID_SIZES[40]; // Build connector tables const horizConnectors = Array.from({ length: rows }, () => Array.from({ length: cols - 1 }, () => Math.random() < 0.5 ? 'tab' : 'blank') ); const vertConnectors = Array.from({ length: rows - 1 }, () => Array.from({ length: cols }, () => Math.random() < 0.5 ? 'tab' : 'blank') ); // Build PieceData array const pieces = []; for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { pieces.push({ id: r * cols + c, gridRow: r, gridCol: c, edges: { top: r === 0 ? 'flat' : (vertConnectors[r - 1][c] === 'tab' ? 'blank' : 'tab'), bottom: r === rows - 1 ? 'flat' : vertConnectors[r][c], left: c === 0 ? 'flat' : (horizConnectors[r][c - 1] === 'tab' ? 'blank' : 'tab'), right: c === cols - 1 ? 'flat' : horizConnectors[r][c] }, x: 0, y: 0 }); } } return { cols, rows, pieces, horizConnectors, vertConnectors }; } }