|
<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<title>Go Game</title> |
|
<style> |
|
.board { |
|
display: grid; |
|
grid-template-columns: repeat(19, 30px); |
|
grid-template-rows: repeat(19, 30px); |
|
gap: 1px; |
|
background-color: #dcb35c; |
|
border: 2px solid #000; |
|
width: fit-content; |
|
margin: 20px auto; |
|
} |
|
|
|
.intersection { |
|
width: 30px; |
|
height: 30px; |
|
position: relative; |
|
cursor: pointer; |
|
} |
|
|
|
.intersection::before { |
|
content: ''; |
|
position: absolute; |
|
left: 0; |
|
top: 50%; |
|
width: 100%; |
|
height: 1px; |
|
background: #000; |
|
z-index: 1; |
|
} |
|
|
|
.intersection::after { |
|
content: ''; |
|
position: absolute; |
|
top: 0; |
|
left: 50%; |
|
height: 100%; |
|
width: 1px; |
|
background: #000; |
|
z-index: 1; |
|
} |
|
|
|
.star-point::before { |
|
content: ''; |
|
position: absolute; |
|
width: 6px; |
|
height: 6px; |
|
background: #000; |
|
border-radius: 50%; |
|
top: 50%; |
|
left: 50%; |
|
transform: translate(-50%, -50%); |
|
z-index: 2; |
|
} |
|
|
|
.stone { |
|
width: 28px; |
|
height: 28px; |
|
border-radius: 50%; |
|
position: absolute; |
|
top: 1px; |
|
left: 1px; |
|
z-index: 3; |
|
transition: 0.2s ease; |
|
} |
|
|
|
.black { |
|
background: #000; |
|
box-shadow: inset -2px -2px 8px rgba(255,255,255,0.2); |
|
} |
|
|
|
.white { |
|
background: #fff; |
|
box-shadow: inset -2px -2px 8px rgba(0,0,0,0.2); |
|
} |
|
|
|
.controls { |
|
text-align: center; |
|
margin: 20px; |
|
} |
|
|
|
button { |
|
padding: 8px 16px; |
|
margin: 0 5px; |
|
font-size: 16px; |
|
border: none; |
|
border-radius: 4px; |
|
background: #4CAF50; |
|
color: white; |
|
cursor: pointer; |
|
transition: 0.3s; |
|
} |
|
|
|
button:hover { |
|
background: #45a049; |
|
} |
|
|
|
.game-info { |
|
text-align: center; |
|
margin: 20px; |
|
font-size: 18px; |
|
} |
|
|
|
.game-info div { |
|
margin: 10px 0; |
|
} |
|
|
|
.game-container { |
|
max-width: 800px; |
|
margin: 0 auto; |
|
padding: 20px; |
|
background: #f5f5f5; |
|
border-radius: 10px; |
|
box-shadow: 0 0 20px rgba(0,0,0,0.1); |
|
} |
|
|
|
.mode-select { |
|
text-align: center; |
|
margin-bottom: 20px; |
|
} |
|
|
|
select { |
|
padding: 8px; |
|
font-size: 16px; |
|
border-radius: 4px; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<div class="game-container"> |
|
<div class="mode-select"> |
|
<select id="gameMode"> |
|
<option value="pvp">Player vs Player</option> |
|
<option value="ai">Player vs AI</option> |
|
</select> |
|
<select id="difficulty"> |
|
<option value="easy">Easy</option> |
|
<option value="hard">Hard</option> |
|
</select> |
|
</div> |
|
|
|
<div id="board" class="board"> |
|
|
|
</div> |
|
|
|
<div class="game-info"> |
|
<div>Current Player: <span id="currentPlayer">Black</span></div> |
|
<div>Black Captures: <span id="blackCaptures">0</span></div> |
|
<div>White Captures: <span id="whiteCaptures">0</span></div> |
|
</div> |
|
|
|
<div class="controls"> |
|
<button id="passBtn">Pass</button> |
|
<button id="resetBtn">Reset</button> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
class GoGame { |
|
constructor() { |
|
this.size = 19; |
|
this.board = Array(this.size).fill().map(() => Array(this.size).fill(null)); |
|
this.currentPlayer = 'black'; |
|
this.captures = { black: 0, white: 0 }; |
|
this.lastMove = null; |
|
this.gameMode = 'pvp'; |
|
this.difficulty = 'easy'; |
|
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; |
|
|
|
if (this.isStarPoint(i, j)) { |
|
intersection.classList.add('star-point'); |
|
} |
|
|
|
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(); |
|
}); |
|
document.getElementById('difficulty').addEventListener('change', (e) => { |
|
this.difficulty = e.target.value; |
|
}); |
|
|
|
this.updateDisplay(); |
|
} |
|
|
|
isStarPoint(row, col) { |
|
const starPoints = [ |
|
[3, 3], [3, 9], [3, 15], |
|
[9, 3], [9, 9], [9, 15], |
|
[15, 3], [15, 9], [15, 15] |
|
]; |
|
return starPoints.some(point => point[0] === row && point[1] === col); |
|
} |
|
|
|
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 captures = this.checkCaptures(row, col); |
|
const suicide = this.isSuicideMove(row, col); |
|
|
|
this.board[row][col] = null; |
|
|
|
return !suicide || captures.length > 0; |
|
} |
|
|
|
placeStone(row, col) { |
|
this.board[row][col] = this.currentPlayer; |
|
this.renderStone(row, col); |
|
|
|
const captures = this.checkCaptures(row, col); |
|
this.removeCaptures(captures); |
|
|
|
this.lastMove = [row, col]; |
|
this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black'; |
|
this.updateDisplay(); |
|
} |
|
|
|
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); |
|
} |
|
|
|
makeStrategicMove() { |
|
let bestMove = null; |
|
let bestScore = -Infinity; |
|
|
|
for (let row = 0; row < this.size; row++) { |
|
for (let col = 0; col < this.size; col++) { |
|
if (this.isValidMove(row, col)) { |
|
const score = this.evaluateMove(row, col); |
|
if (score > bestScore) { |
|
bestScore = score; |
|
bestMove = [row, col]; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if (bestMove) { |
|
this.placeStone(...bestMove); |
|
} else { |
|
this.pass(); |
|
} |
|
} |
|
|
|
evaluateMove(row, col) { |
|
let score = 0; |
|
|
|
this.board[row][col] = this.currentPlayer; |
|
|
|
const captures = this.checkCaptures(row, col); |
|
score += captures.length * 10; |
|
|
|
const group = this.getGroup(row, col); |
|
const liberties = this.getGroupLiberties(group); |
|
score += liberties.length; |
|
|
|
if (this.isStarPoint(row, col)) { |
|
score += 5; |
|
} |
|
|
|
const neighbors = this.getNeighbors(row, col); |
|
for (const [nRow, nCol] of neighbors) { |
|
if (this.board[nRow][nCol] === this.currentPlayer) { |
|
score += 3; |
|
} |
|
} |
|
|
|
this.board[row][col] = null; |
|
return score; |
|
} |
|
|
|
getGroupLiberties(group) { |
|
const liberties = new Set(); |
|
for (const [row, col] of group) { |
|
const neighbors = this.getNeighbors(row, col); |
|
for (const [nRow, nCol] of neighbors) { |
|
if (this.board[nRow][nCol] === null) { |
|
liberties.add(`${nRow},${nCol}`); |
|
} |
|
} |
|
} |
|
return Array.from(liberties); |
|
} |
|
|
|
makeAIMove() { |
|
if (this.difficulty === 'easy') { |
|
this.makeRandomMove(); |
|
} else { |
|
this.makeStrategicMove(); |
|
} |
|
} |
|
|
|
pass() { |
|
this.currentPlayer = this.currentPlayer === 'black' ? 'white' : 'black'; |
|
this.updateDisplay(); |
|
|
|
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.captures = { black: 0, white: 0 }; |
|
this.lastMove = null; |
|
this.initialize(); |
|
} |
|
|
|
updateDisplay() { |
|
document.getElementById('currentPlayer').textContent = |
|
this.currentPlayer.charAt(0).toUpperCase() + this.currentPlayer.slice(1); |
|
document.getElementById('blackCaptures').textContent = this.captures.black; |
|
document.getElementById('whiteCaptures').textContent = this.captures.white; |
|
} |
|
} |
|
|
|
new GoGame(); |
|
</script> |
|
</body> |
|
</html> |
|
|
|
|