AI 五子棋网站
Grok-3思考版二轮对话生成
对话展示
第一轮对话
第二轮对话
源码文件
index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI五子棋</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h1>五子棋</h1>
<div id="board"></div>
<div class="controls">
<label for="difficulty">难度:</label>
<select id="difficulty">
<option value="easy">简单</option>
<option value="medium" selected>普通</option>
<option value="hard">困难</option>
</select>
<button id="newGame">新游戏</button>
<button id="undo">悔棋</button>
<div id="status"></div>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
style.css
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.container {
text-align: center;
}
h1 {
color: #333;
}
#board {
display: grid;
grid-template-columns: repeat(15, 30px);
grid-template-rows: repeat(15, 30px);
gap: 1px;
background-color: #ccc;
margin: 20px auto;
border: 2px solid #333;
padding: 5px;
}
.cell {
width: 30px;
height: 30px;
background-color: #fff;
position: relative;
cursor: pointer;
transition: background-color 0.2s;
}
.cell:hover {
background-color: #e0e0e0;
}
.cell.black::after {
content: '';
position: absolute;
top: 5px;
left: 5px;
width: 20px;
height: 20px;
background-color: black;
border-radius: 50%;
}
.cell.white::after {
content: '';
position: absolute;
top: 5px;
left: 5px;
width: 20px;
height: 20px;
background-color: white;
border-radius: 50%;
border: 1px solid #333;
}
.controls {
margin-top: 20px;
}
label {
margin-right: 10px;
font-size: 18px;
}
select {
padding: 5px;
font-size: 16px;
margin-right: 10px;
}
button {
padding: 10px 20px;
margin: 0 10px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.2s;
}
button:hover {
background-color: #45a049;
}
#status {
margin-top: 10px;
font-size: 18px;
color: #333;
}
script.js
const boardSize = 15;
const board = Array(boardSize).fill().map(() => Array(boardSize).fill(0));
let currentPlayer = 1; // 1为黑棋(玩家),2为白棋(AI)
let gameOver = false;
let history = [];
const boardElement = document.getElementById('board');
const statusElement = document.getElementById('status');
const newGameButton = document.getElementById('newGame');
const undoButton = document.getElementById('undo');
const difficultySelect = document.getElementById('difficulty');
// 初始化棋盘
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', () => handleClick(i, j));
boardElement.appendChild(cell);
}
}
// 处理玩家点击
function handleClick(row, col) {
if (gameOver || board[row][col] !== 0) return;
board[row][col] = currentPlayer;
history.push({ row, col, player: currentPlayer });
updateBoard();
if (checkWin(row, col, currentPlayer)) {
gameOver = true;
statusElement.textContent = currentPlayer === 1 ? '黑棋获胜!' : '白棋获胜!';
return;
}
currentPlayer = 3 - currentPlayer;
statusElement.textContent = currentPlayer === 1 ? '轮到黑棋' : '轮到白棋';
if (currentPlayer === 2) {
setTimeout(aiMove, 500); // AI延迟0.5秒落子
}
}
// 更新棋盘显示
function updateBoard() {
const cells = document.querySelectorAll('.cell');
cells.forEach(cell => {
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
cell.classList.remove('black', 'white');
if (board[row][col] === 1) cell.classList.add('black');
else if (board[row][col] === 2) cell.classList.add('white');
});
}
// 检查胜利
function checkWin(row, col, player) {
const directions = [
{ dr: 0, dc: 1 }, // 水平
{ dr: 1, dc: 0 }, // 垂直
{ dr: 1, dc: 1 }, // 对角线
{ dr: 1, dc: -1 } // 反对角线
];
for (const { dr, dc } of directions) {
let count = 1;
for (let i = 1; i < 5; i++) {
const r = row + i * dr;
const c = col + i * dc;
if (r < 0 || r >= boardSize || c < 0 || c >= boardSize || board[r][c] !== player) break;
count++;
}
for (let i = 1; i < 5; i++) {
const r = row - i * dr;
const c = col - i * dc;
if (r < 0 || r >= boardSize || c < 0 || c >= boardSize || board[r][c] !== player) break;
count++;
}
if (count >= 5) return true;
}
return false;
}
// 获取候选位置
function getCandidates() {
const candidates = [];
const directions = [
{ dr: -1, dc: 0 }, { dr: 1, dc: 0 }, { dr: 0, dc: -1 }, { dr: 0, dc: 1 },
{ dr: -1, dc: -1 }, { dr: -1, dc: 1 }, { dr: 1, dc: -1 }, { dr: 1, dc: 1 }
];
const visited = Array(boardSize).fill().map(() => Array(boardSize).fill(false));
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
if (board[i][j] !== 0) {
for (const { dr, dc } of directions) {
const ni = i + dr;
const nj = j + dc;
if (ni >= 0 && ni < boardSize && nj >= 0 && nj < boardSize && board[ni][nj] === 0 && !visited[ni][nj]) {
candidates.push({ row: ni, col: nj });
visited[ni][nj] = true;
}
}
}
}
}
return candidates.length > 0 ? candidates : getAllEmptyCells();
}
// 获取所有空位(备用)
function getAllEmptyCells() {
const emptyCells = [];
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
if (board[i][j] === 0) emptyCells.push({ row: i, col: j });
}
}
return emptyCells;
}
// 评估函数评分表
const scoreTable = [
[], // 长度0
[0, 0, 0], // 长度1
[0, 1, 10], // 长度2:[0开放端,1开放端,2开放端]
[1, 10, 100], // 长度3
[10, 100, 1000], // 长度4
[10000, 10000, 10000] // 长度5及以上
];
// 评估单条线的得分
function scoreLine(line, player) {
let score = 0;
let j = 0;
while (j < line.length) {
if (line[j] === player) {
let k = j;
while (k < line.length && line[k] === player) k++;
const length = k - j;
if (length >= 2) {
let openEnds = 0;
if (j > 0 && line[j - 1] === 0) openEnds++;
if (k < line.length && line[k] === 0) openEnds++;
const effectiveLength = Math.min(length, 5);
score += scoreTable[effectiveLength][openEnds];
}
j = k;
} else {
j++;
}
}
return score;
}
// 获取玩家总得分
function getScore(player) {
let score = 0;
// 水平
for (let i = 0; i < boardSize; i++) {
score += scoreLine(board[i], player);
}
// 垂直
for (let j = 0; j < boardSize; j++) {
const column = board.map(row => row[j]);
score += scoreLine(column, player);
}
// 对角线(左上到右下)
for (let k = 0; k < 2 * boardSize - 1; k++) {
const line = [];
for (let i = 0; i < boardSize; i++) {
const j = k - i;
if (j >= 0 && j < boardSize) line.push(board[i][j]);
}
if (line.length >= 5) score += scoreLine(line, player);
}
// 反对角线(右上到左下)
for (let k = -boardSize + 1; k < boardSize; k++) {
const line = [];
for (let i = 0; i < boardSize; i++) {
const j = i - k;
if (j >= 0 && j < boardSize) line.push(board[i][j]);
}
if (line.length >= 5) score += scoreLine(line, player);
}
return score;
}
// 局面评估
function evaluate() {
return getScore(2) - getScore(1); // AI(白棋)得分 - 玩家(黑棋)得分
}
// Minimax算法
function minimax(depth, alpha, beta, isMaximizing) {
if (depth === 0) return evaluate();
const candidates = getCandidates();
if (isMaximizing) { // AI回合
let maxEval = -Infinity;
for (const { row, col } of candidates) {
board[row][col] = 2;
if (checkWin(row, col, 2)) {
board[row][col] = 0;
return 100000 - depth; // 优先更快获胜
}
const eval = minimax(depth - 1, alpha, beta, false);
board[row][col] = 0;
maxEval = Math.max(maxEval, eval);
alpha = Math.max(alpha, eval);
if (beta <= alpha) break;
}
return maxEval;
} else { // 玩家回合
let minEval = Infinity;
for (const { row, col } of candidates) {
board[row][col] = 1;
if (checkWin(row, col, 1)) {
board[row][col] = 0;
return -100000 + depth; // 延缓失败
}
const eval = minimax(depth - 1, alpha, beta, true);
board[row][col] = 0;
minEval = Math.min(minEval, eval);
beta = Math.min(beta, eval);
if (beta <= alpha) break;
}
return minEval;
}
}
// AI落子
function aiMove() {
const difficulty = difficultySelect.value;
if (difficulty === 'easy') {
const emptyCells = getAllEmptyCells();
if (emptyCells.length > 0) {
const randomIndex = Math.floor(Math.random() * emptyCells.length);
const { row, col } = emptyCells[randomIndex];
handleClick(row, col);
}
} else {
let depth = difficulty === 'medium' ? 3 : 4;
const candidates = getCandidates();
let bestMove = null;
let bestValue = -Infinity;
for (const { row, col } of candidates) {
board[row][col] = 2;
const moveValue = minimax(depth - 1, -Infinity, Infinity, false);
board[row][col] = 0;
if (moveValue > bestValue) {
bestValue = moveValue;
bestMove = { row, col };
}
}
if (bestMove) handleClick(bestMove.row, bestMove.col);
}
}
// 新游戏
newGameButton.addEventListener('click', () => {
board.forEach(row => row.fill(0));
history = [];
currentPlayer = 1;
gameOver = false;
updateBoard();
statusElement.textContent = '轮到黑棋';
});
// 悔棋
undoButton.addEventListener('click', () => {
if (history.length < 2 || gameOver) return; // 回退玩家和AI一步
history.pop(); // 移除AI一步
const lastMove = history.pop(); // 移除玩家一步
board[lastMove.row][lastMove.col] = 0;
currentPlayer = lastMove.player;
gameOver = false;
updateBoard();
statusElement.textContent = '轮到黑棋';
});
// 初始化状态
statusElement.textContent = '轮到黑棋';