| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Go Game</title> |
| <style> |
| * { |
| margin: 0; |
| padding: 0; |
| box-sizing: border-box; |
| } |
| |
| body { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| min-height: 100vh; |
| background: #f0f0f0; |
| font-family: Arial, sans-serif; |
| padding: 20px; |
| } |
| |
| .game-container { |
| background: #DEB887; |
| padding: 20px; |
| border-radius: 10px; |
| box-shadow: 0 0 20px rgba(0,0,0,0.1); |
| } |
| |
| .board { |
| display: grid; |
| grid-template-columns: repeat(19, 30px); |
| grid-template-rows: repeat(19, 30px); |
| gap: 0px; |
| background: #DEB887; |
| border: 2px solid #000; |
| position: relative; |
| } |
| |
| .intersection { |
| width: 30px; |
| height: 30px; |
| position: relative; |
| cursor: pointer; |
| } |
| |
| .intersection::before { |
| content: ''; |
| position: absolute; |
| top: 50%; |
| left: 0; |
| width: 100%; |
| height: 1px; |
| background: #000; |
| } |
| |
| .intersection::after { |
| content: ''; |
| position: absolute; |
| left: 50%; |
| top: 0; |
| height: 100%; |
| width: 1px; |
| background: #000; |
| } |
| |
| .stone { |
| position: absolute; |
| width: 26px; |
| height: 26px; |
| border-radius: 50%; |
| top: 2px; |
| left: 2px; |
| z-index: 1; |
| transition: all 0.2s ease; |
| } |
| |
| .black { |
| background: #000; |
| box-shadow: 2px 2px 2px rgba(0,0,0,0.2); |
| } |
| |
| .white { |
| background: #fff; |
| box-shadow: 2px 2px 2px rgba(0,0,0,0.2); |
| } |
| |
| .controls { |
| margin-top: 20px; |
| display: flex; |
| gap: 10px; |
| } |
| |
| button { |
| padding: 10px 20px; |
| font-size: 16px; |
| border: none; |
| border-radius: 5px; |
| cursor: pointer; |
| background: #4CAF50; |
| color: white; |
| transition: background 0.3s; |
| } |
| |
| button:hover { |
| background: #45a049; |
| } |
| |
| .score { |
| margin-top: 20px; |
| font-size: 18px; |
| display: flex; |
| gap: 20px; |
| } |
| |
| .mode-select { |
| margin-bottom: 20px; |
| } |
| |
| select { |
| padding: 8px; |
| font-size: 16px; |
| border-radius: 5px; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="mode-select"> |
| <select id="gameMode"> |
| <option value="pvp">Player vs Player</option> |
| <option value="ai">Player vs AI</option> |
| </select> |
| </div> |
|
|
| <div class="game-container"> |
| <div class="board" id="board"></div> |
| </div> |
|
|
| <div class="score"> |
| <div>Black Score: <span id="blackScore">0</span></div> |
| <div>White Score: <span id="whiteScore">0</span></div> |
| </div> |
|
|
| <div class="controls"> |
| <button id="passBtn">Pass</button> |
| <button id="resetBtn">Reset</button> |
| </div> |
|
|
| <script> |
| class GoGame { |
| constructor() { |
| this.size = 19; |
| this.board = Array(this.size).fill().map(() => Array(this.size).fill(null)); |
| this.currentPlayer = 'black'; |
| this.lastMove = null; |
| this.passes = 0; |
| this.gameMode = 'pvp'; |
| this.score = { black: 0, white: 0 }; |
| this.capturedStones = { black: 0, white: 0 }; |
| |
| this.initialize(); |
| } |
| |
| initialize() { |
| const boardElement = document.getElementById('board'); |
| boardElement.innerHTML = ''; |
| |
| for(let i = 0; i < this.size; i++) { |
| for(let j = 0; j < this.size; j++) { |
| const intersection = document.createElement('div'); |
| intersection.className = 'intersection'; |
| intersection.dataset.row = i; |
| intersection.dataset.col = j; |
| intersection.addEventListener('click', (e) => this.handleMove(e)); |
| boardElement.appendChild(intersection); |
| } |
| } |
| |
| document.getElementById('passBtn').addEventListener('click', () => this.pass()); |
| document.getElementById('resetBtn').addEventListener('click', () => this.reset()); |
| document.getElementById('gameMode').addEventListener('change', (e) => { |
| this.gameMode = e.target.value; |
| this.reset(); |
| }); |
| } |
| |
| handleMove(e) { |
| const row = parseInt(e.target.dataset.row); |
| const col = parseInt(e.target.dataset.col); |
| |
| if(this.isValidMove(row, col)) { |
| this.placeStone(row, col); |
| |
| if(this.gameMode === 'ai' && this.currentPlayer === 'white') { |
| setTimeout(() => this.makeAIMove(), 500); |
| } |
| } |
| } |
| |
| isValidMove(row, col) { |
| if(this.board[row][col] !== null) return false; |
| |
| |
| this.board[row][col] = this.currentPlayer; |
| const group = this.getGroup(row, col); |
| const liberties = this.countLiberties(group); |
| |
| |
| this.board[row][col] = null; |
| |
| return liberties > 0; |
| } |
| |
| placeStone(row, col) { |
| if(this.board[row][col] === null) { |
| this.board[row][col] = this.currentPlayer; |
| this.renderStone(row, col); |
| this.captures(); |
| this.passes = 0; |
| this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black'; |
| this.updateScore(); |
| } |
| } |
| |
| renderStone(row, col) { |
| const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`); |
| const stone = document.createElement('div'); |
| stone.className = `stone ${this.currentPlayer}`; |
| intersection.appendChild(stone); |
| } |
| |
| getGroup(row, col) { |
| const color = this.board[row][col]; |
| const group = new Set(); |
| const stack = [[row, col]]; |
| |
| while(stack.length > 0) { |
| const [r, c] = stack.pop(); |
| const key = `${r},${c}`; |
| |
| if(!group.has(key)) { |
| group.add(key); |
| const neighbors = this.getNeighbors(r, c); |
| |
| for(const [nr, nc] of neighbors) { |
| if(this.board[nr][nc] === color) { |
| stack.push([nr, nc]); |
| } |
| } |
| } |
| } |
| |
| return group; |
| } |
| |
| countLiberties(group) { |
| const liberties = new Set(); |
| |
| for(const pos of group) { |
| const [row, col] = pos.split(',').map(Number); |
| const neighbors = this.getNeighbors(row, col); |
| |
| for(const [nr, nc] of neighbors) { |
| if(this.board[nr][nc] === null) { |
| liberties.add(`${nr},${nc}`); |
| } |
| } |
| } |
| |
| return liberties.size; |
| } |
| |
| getNeighbors(row, col) { |
| const neighbors = []; |
| const directions = [[-1,0], [1,0], [0,-1], [0,1]]; |
| |
| for(const [dr, dc] of directions) { |
| const newRow = row + dr; |
| const newCol = col + dc; |
| |
| if(newRow >= 0 && newRow < this.size && newCol >= 0 && newCol < this.size) { |
| neighbors.push([newRow, newCol]); |
| } |
| } |
| |
| return neighbors; |
| } |
| |
| captures() { |
| for(let i = 0; i < this.size; i++) { |
| for(let j = 0; j < this.size; j++) { |
| if(this.board[i][j] !== null) { |
| const group = this.getGroup(i, j); |
| if(this.countLiberties(group) === 0) { |
| for(const pos of group) { |
| const [row, col] = pos.split(',').map(Number); |
| const capturedColor = this.board[row][col]; |
| this.board[row][col] = null; |
| const intersection = document.querySelector(`[data-row="${row}"][data-col="${col}"]`); |
| intersection.innerHTML = ''; |
| this.capturedStones[this.currentPlayer]++; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| makeAIMove() { |
| const validMoves = []; |
| for(let i = 0; i < this.size; i++) { |
| for(let j = 0; j < this.size; j++) { |
| if(this.isValidMove(i, j)) { |
| validMoves.push([i, j]); |
| } |
| } |
| } |
| |
| if(validMoves.length > 0) { |
| const [row, col] = validMoves[Math.floor(Math.random() * validMoves.length)]; |
| this.placeStone(row, col); |
| } else { |
| this.pass(); |
| } |
| } |
| |
| pass() { |
| this.passes++; |
| if(this.passes === 2) { |
| alert('Game Over!'); |
| return; |
| } |
| this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black'; |
| if(this.gameMode === 'ai' && this.currentPlayer === 'white') { |
| setTimeout(() => this.makeAIMove(), 500); |
| } |
| } |
| |
| reset() { |
| this.board = Array(this.size).fill().map(() => Array(this.size).fill(null)); |
| this.currentPlayer = 'black'; |
| this.passes = 0; |
| this.score = { black: 0, white: 0 }; |
| this.capturedStones = { black: 0, white: 0 }; |
| const intersections = document.querySelectorAll('.intersection'); |
| intersections.forEach(intersection => intersection.innerHTML = ''); |
| this.updateScore(); |
| } |
| |
| updateScore() { |
| document.getElementById('blackScore').textContent = |
| this.score.black + this.capturedStones.black; |
| document.getElementById('whiteScore').textContent = |
| this.score.white + this.capturedStones.white; |
| } |
| } |
| |
| const game = new GoGame(); |
| </script> |
| </body> |
| </html><script async data-explicit-opt-in="true" data-cookie-opt-in="true" src="https://vercel.live/_next-live/feedback/feedback.js"></script> |