Spaces:
Paused
Paused
| const PLAYERS = [ | |
| { symbol: 'X', color: '#FF4757', label: 'Player 1' }, | |
| { symbol: 'Circle', color: '#2ED7F8', label: 'Player 2' }, | |
| { symbol: 'Triangle',color: '#2ECC71', label: 'Player 3' }, | |
| { symbol: 'Square', color: '#FFD32A', label: 'Player 4' }, | |
| ]; | |
| function getPlayer(sym) { | |
| return PLAYERS.find(p => p.symbol === sym) || PLAYERS[0]; | |
| } | |
| function makeBoard(size) { | |
| const hLines = Array.from({ length: size }, () => Array(size - 1).fill(null)); | |
| const vLines = Array.from({ length: size - 1 }, () => Array(size).fill(null)); | |
| const boxes = Array.from({ length: size - 1 }, () => Array(size - 1).fill(null)); | |
| return { size, hLines, vLines, boxes }; | |
| } | |
| function getCompletedBoxes(hLines, vLines, boxes, row, col, isHorizontal) { | |
| const size = hLines.length; | |
| const completed = []; | |
| if (isHorizontal) { | |
| if (row > 0 && vLines[row-1][col] && vLines[row-1][col+1] && hLines[row-1][col]) { | |
| completed.push([row-1, col]); | |
| } | |
| if (row < size - 1 && vLines[row][col] && vLines[row][col+1] && hLines[row+1][col]) { | |
| completed.push([row, col]); | |
| } | |
| } else { | |
| if (col > 0 && hLines[row][col-1] && hLines[row+1][col-1] && vLines[row][col-1]) { | |
| completed.push([row, col-1]); | |
| } | |
| if (col < size - 1 && hLines[row][col] && hLines[row+1][col] && vLines[row][col+1]) { | |
| completed.push([row, col]); | |
| } | |
| } | |
| return completed.filter(([br, bc]) => !boxes[br][bc]); | |
| } | |
| function isGameOver(boxes) { | |
| return boxes.every(row => row.every(cell => cell !== null)); | |
| } | |
| function getWinner(boxes, players) { | |
| const counts = {}; | |
| players.forEach(p => counts[p.symbol] = 0); | |
| for (let r = 0; r < boxes.length; r++) | |
| for (let c = 0; c < boxes[r].length; c++) | |
| if (boxes[r][c]) counts[boxes[r][c]]++; | |
| const max = Math.max(...Object.values(counts)); | |
| const tops = Object.keys(counts).filter(k => counts[k] === max); | |
| if (tops.length === 1) return tops[0]; | |
| return null; | |
| } | |
| function getScores(boxes) { | |
| const counts = {}; | |
| for (let r = 0; r < boxes.length; r++) | |
| for (let c = 0; c < boxes[r].length; c++) | |
| if (boxes[r][c]) counts[boxes[r][c]] = (counts[boxes[r][c]] || 0) + 1; | |
| return counts; | |
| } | |
| function makeSVG(symbol, color, size) { | |
| const ns = 'http://www.w3.org/2000/svg'; | |
| const svg = document.createElementNS(ns, 'svg'); | |
| svg.setAttribute('viewBox', '0 0 48 48'); | |
| svg.setAttribute('width', size); | |
| svg.setAttribute('height', size); | |
| svg.innerHTML = makeSVGPath(symbol, color); | |
| return svg; | |
| } | |
| function makeSVGString(symbol, color, size) { | |
| return `<svg viewBox="0 0 48 48" width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">${makeSVGPath(symbol, color)}</svg>`; | |
| } | |
| function makeSVGPath(symbol, color) { | |
| switch (symbol) { | |
| case 'X': | |
| return ` | |
| <line x1="8" y1="8" x2="40" y2="40" stroke="${color}" stroke-width="6" stroke-linecap="round"/> | |
| <line x1="40" y1="8" x2="8" y2="40" stroke="${color}" stroke-width="6" stroke-linecap="round"/>`; | |
| case 'Circle': | |
| return `<circle cx="24" cy="24" r="16" fill="none" stroke="${color}" stroke-width="5"/>`; | |
| case 'Triangle': | |
| return `<polygon points="24,6 42,40 6,40" fill="none" stroke="${color}" stroke-width="4" stroke-linejoin="round"/>`; | |
| case 'Square': | |
| return `<rect x="8" y="8" width="32" height="32" rx="4" fill="none" stroke="${color}" stroke-width="5"/>`; | |
| default: | |
| return ''; | |
| } | |
| } | |
| function escapeHtml(str) { | |
| const d = document.createElement('div'); | |
| d.appendChild(document.createTextNode(str)); | |
| return d.innerHTML; | |
| } | |