人工智能五子棋完整代码
新游戏黑棋 (玩家)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>人工智能五子棋</title>
<style>
body {
font-family: 'Microsoft YaHei', Arial, sans-serif;
margin: 0;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
}
.container {
background: white;
border-radius: 15px;
padding: 20px;
box-shadow: 0 10px 30px rgba(0,0,0,0.3);
max-width: 800px;
width: 100%;
}
h1 {
text-align: center;
color: #333;
margin-bottom: 20px;
font-size: 2.5em;
text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
}
.game-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
padding: 10px;
background: #f5f5f5;
border-radius: 10px;
}
.current-player {
font-size: 1.2em;
font-weight: bold;
}
.controls {
display: flex;
gap: 10px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 1em;
transition: all 0.3s ease;
}
.new-game-btn {
background: #4CAF50;
color: white;
}
.new-game-btn:hover {
background: #45a049;
}
.difficulty-select {
padding: 8px;
border-radius: 5px;
border: 1px solid #ddd;
}
.board-container {
display: flex;
justify-content: center;
margin: 20px 0;
}
.board {
background: #deb887;
border: 3px solid #8b4513;
border-radius: 10px;
padding: 20px;
box-shadow: 0 5px 15px rgba(0,0,0,0.2);
}
.board-grid {
display: grid;
grid-template-columns: repeat(15, 30px);
grid-template-rows: repeat(15, 30px);
gap: 1px;
background: #8b4513;
padding: 1px;
}
.cell {
width: 30px;
height: 30px;
background: #deb887;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
position: relative;
transition: background-color 0.2s ease;
}
.cell:hover {
background: #d2b48c;
}
.cell.occupied {
cursor: not-allowed;
}
.cell.occupied:hover {
background: #deb887;
}
.stone {
width: 26px;
height: 26px;
border-radius: 50%;
border: 2px solid #333;
position: absolute;
animation: placeStone 0.3s ease;
}
@keyframes placeStone {
0% {
transform: scale(0);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
.black-stone {
background: radial-gradient(circle at 30% 30%, #666, #000);
}
.white-stone {
background: radial-gradient(circle at 30% 30%, #fff, #ccc);
}
.winning-stone {
animation: winningPulse 1s infinite;
}
@keyframes winningPulse {
0%, 100% {
transform: scale(1);
box-shadow: 0 0 10px rgba(255, 215, 0, 0.8);
}
50% {
transform: scale(1.1);
box-shadow: 0 0 20px rgba(255, 215, 0, 1);
}
}
.game-status {
text-align: center;
font-size: 1.5em;
font-weight: bold;
margin-top: 20px;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.winner {
color: #ff6b6b;
animation: celebrate 2s ease-in-out;
}
@keyframes celebrate {
0%, 100% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
}
.thinking {
color: #4285f4;
}
.instructions {
background: #f0f8ff;
padding: 15px;
border-radius: 10px;
margin-top: 20px;
border-left: 4px solid #4285f4;
}
.instructions h3 {
margin-top: 0;
color: #333;
}
.instructions ul {
margin: 10px 0;
padding-left: 20px;
}
.instructions li {
margin: 5px 0;
}
@media (max-width: 600px) {
.board-grid {
grid-template-columns: repeat(15, 25px);
grid-template-rows: repeat(15, 25px);
}
.cell {
width: 25px;
height: 25px;
}
.stone {
width: 21px;
height: 21px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>🤖 人工智能五子棋</h1>
<div class="game-info">
<div class="current-player">
当前玩家: <span id="current-player">黑棋 (玩家)</span>
</div>
<div class="controls">
<select id="difficulty" class="difficulty-select">
<option value="easy">简单</option>
<option value="medium" selected>中等</option>
<option value="hard">困难</option>
</select>
<button class="new-game-btn" onclick="newGame()">新游戏</button>
</div>
</div>
<div class="board-container">
<div class="board">
<div id="board" class="board-grid"></div>
</div>
</div>
<div id="game-status" class="game-status">
点击棋盘开始游戏
</div>
<div class="instructions">
<h3>游戏说明:</h3>
<ul>
<li>目标: 率先在横、竖、斜任一方向连成五子获胜</li>
<li>玩家执黑棋先手,AI执白棋后手</li>
<li>可选择AI难度: 简单、中等、困难</li>
<li>点击棋盘格子下棋,AI会自动应对</li>
</ul>
</div>
</div>
<script>
class GomokuAI {
constructor() {
this.BOARD_SIZE = 15;
this.EMPTY = 0;
this.BLACK = 1; // 玩家
this.WHITE = 2; // AI
this.board = [];
this.currentPlayer = this.BLACK;
this.gameOver = false;
this.difficulty = 'medium';
this.lastMove = null;
this.initBoard();
this.createBoardUI();
// 评分权重
this.SCORES = {
FIVE: 100000,
FOUR: 10000,
BLOCKED_FOUR: 1000,
THREE: 1000,
BLOCKED_THREE: 100,
TWO: 100,
BLOCKED_TWO: 10,
ONE: 10
};
}
initBoard() {
this.board = [];
for (let i = 0; i < this.BOARD_SIZE; i++) {
this.board[i] = [];
for (let j = 0; j < this.BOARD_SIZE; j++) {
this.board[i][j] = this.EMPTY;
}
}
this.currentPlayer = this.BLACK;
this.gameOver = false;
this.lastMove = null;
this.updateGameStatus('点击棋盘开始游戏');
}
createBoardUI() {
const boardElement = document.getElementById('board');
boardElement.innerHTML = '';
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', () => this.handleCellClick(i, j));
boardElement.appendChild(cell);
}
}
}
handleCellClick(row, col) {
if (this.gameOver || this.board[row][col] !== this.EMPTY || this.currentPlayer !== this.BLACK) {
return;
}
this.makeMove(row, col, this.BLACK);
if (this.checkWin(row, col, this.BLACK)) {
this.gameOver = true;
this.updateGameStatus('🎉 恭喜!玩家获胜!', 'winner');
this.highlightWinningStones(row, col, this.BLACK);
return;
}
if (this.isBoardFull()) {
this.gameOver = true;
this.updateGameStatus('😐 平局!', 'winner');
return;
}
this.currentPlayer = this.WHITE;
this.updateCurrentPlayer();
this.updateGameStatus('🤖 AI思考中...', 'thinking');
// AI延迟移动,增加真实感
setTimeout(() => {
this.aiMove();
}, 500 + Math.random() * 1000);
}
makeMove(row, col, player) {
this.board[row][col] = player;
this.lastMove = { row, col };
this.updateCell(row, col, player);
}
updateCell(row, col, player) {
const cellIndex = row * this.BOARD_SIZE + col;
const cell = document.getElementById('board').children[cellIndex];
cell.classList.add('occupied');
const stone = document.createElement('div');
stone.className = `stone ${player === this.BLACK ? 'black-stone' : 'white-stone'}`;
cell.appendChild(stone);
}
aiMove() {
if (this.gameOver) return;
const move = this.getBestMove();
if (move) {
this.makeMove(move.row, move.col, this.WHITE);
if (this.checkWin(move.row, move.col, this.WHITE)) {
this.gameOver = true;
this.updateGameStatus('😤 AI获胜!再来一局?', 'winner');
this.highlightWinningStones(move.row, move.col, this.WHITE);
return;
}
if (this.isBoardFull()) {
this.gameOver = true;
this.updateGameStatus('😐 平局!', 'winner');
return;
}
this.currentPlayer = this.BLACK;
this.updateCurrentPlayer();
this.updateGameStatus('轮到你了!');
}
}
getBestMove() {
const difficultySettings = {
easy: { depth: 2, randomness: 0.3 },
medium: { depth: 3, randomness: 0.1 },
hard: { depth: 4, randomness: 0.05 }
};
const setting = difficultySettings[this.difficulty];
const moves = this.getValidMoves();
if (moves.length === 0) return null;
// 如果是第一步,随机选择中心附近位置
if (moves.length === this.BOARD_SIZE * this.BOARD_SIZE) {
const center = Math.floor(this.BOARD_SIZE / 2);
const firstMoves = [
{ row: center, col: center },
{ row: center - 1, col: center },
{ row: center + 1, col: center },
{ row: center, col: center - 1 },
{ row: center, col: center + 1 }
];
return firstMoves[Math.floor(Math.random() * firstMoves.length)];
}
// 优先检查关键位置
const criticalMove = this.findCriticalMove();
if (criticalMove) return criticalMove;
// 使用minimax算法
let bestScore = -Infinity;
let bestMoves = [];
// 只考虑有潜力的位置
const candidateMoves = this.getCandidateMoves();
for (const move of candidateMoves) {
this.board[move.row][move.col] = this.WHITE;
const score = this.minimax(setting.depth - 1, -Infinity, Infinity, false);
this.board[move.row][move.col] = this.EMPTY;
if (score > bestScore) {
bestScore = score;
bestMoves = [move];
} else if (score === bestScore) {
bestMoves.push(move);
}
}
// 添加随机性
if (Math.random() < setting.randomness && bestMoves.length > 1) {
return bestMoves[Math.floor(Math.random() * bestMoves.length)];
}
return bestMoves.length > 0 ? bestMoves[0] : moves[0];
}
findCriticalMove() {
// 检查AI能否立即获胜
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.board[i][j] === this.EMPTY) {
this.board[i][j] = this.WHITE;
if (this.checkWin(i, j, this.WHITE)) {
this.board[i][j] = this.EMPTY;
return { row: i, col: j };
}
this.board[i][j] = this.EMPTY;
}
}
}
// 检查是否需要阻止玩家获胜
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.board[i][j] === this.EMPTY) {
this.board[i][j] = this.BLACK;
if (this.checkWin(i, j, this.BLACK)) {
this.board[i][j] = this.EMPTY;
return { row: i, col: j };
}
this.board[i][j] = this.EMPTY;
}
}
}
return null;
}
getCandidateMoves() {
const moves = [];
const visited = new Set();
// 从已有棋子周围2格内搜索
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.board[i][j] !== this.EMPTY) {
for (let di = -2; di <= 2; di++) {
for (let dj = -2; dj <= 2; dj++) {
const ni = i + di;
const nj = j + dj;
const key = `${ni}-${nj}`;
if (ni >= 0 && ni < this.BOARD_SIZE &&
nj >= 0 && nj < this.BOARD_SIZE &&
this.board[ni][nj] === this.EMPTY &&
!visited.has(key)) {
moves.push({ row: ni, col: nj });
visited.add(key);
}
}
}
}
}
}
return moves.length > 0 ? moves : this.getValidMoves().slice(0, 50);
}
minimax(depth, alpha, beta, isMaximizing) {
if (depth === 0) {
return this.evaluateBoard();
}
const moves = this.getCandidateMoves().slice(0, 20); // 限制搜索范围
if (isMaximizing) {
let maxScore = -Infinity;
for (const move of moves) {
this.board[move.row][move.col] = this.WHITE;
const score = this.minimax(depth - 1, alpha, beta, false);
this.board[move.row][move.col] = this.EMPTY;
maxScore = Math.max(score, maxScore);
alpha = Math.max(alpha, score);
if (beta <= alpha) break;
}
return maxScore;
} else {
let minScore = Infinity;
for (const move of moves) {
this.board[move.row][move.col] = this.BLACK;
const score = this.minimax(depth - 1, alpha, beta, true);
this.board[move.row][move.col] = this.EMPTY;
minScore = Math.min(score, minScore);
beta = Math.min(beta, score);
if (beta <= alpha) break;
}
return minScore;
}
}
evaluateBoard() {
return this.evaluatePlayer(this.WHITE) - this.evaluatePlayer(this.BLACK);
}
evaluatePlayer(player) {
let score = 0;
// 检查所有方向
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.board[i][j] === player) {
for (const [dx, dy] of directions) {
score += this.evaluateLine(i, j, dx, dy, player);
}
}
}
}
return score;
}
evaluateLine(row, col, dx, dy, player) {
let count = 1;
let blocked = 0;
// 向前检查
let r = row + dx, c = col + dy;
while (r >= 0 && r < this.BOARD_SIZE && c >= 0 && c < this.BOARD_SIZE) {
if (this.board[r][c] === player) {
count++;
} else if (this.board[r][c] === this.EMPTY) {
break;
} else {
blocked++;
break;
}
r += dx;
c += dy;
}
// 向后检查
r = row - dx;
c = col - dy;
while (r >= 0 && r < this.BOARD_SIZE && c >= 0 && c < this.BOARD_SIZE) {
if (this.board[r][c] === player) {
count++;
} else if (this.board[r][c] === this.EMPTY) {
break;
} else {
blocked++;
break;
}
r -= dx;
c -= dy;
}
return this.getScore(count, blocked);
}
getScore(count, blocked) {
if (blocked === 2) return 0;
switch (count) {
case 5: return this.SCORES.FIVE;
case 4: return blocked === 0 ? this.SCORES.FOUR : this.SCORES.BLOCKED_FOUR;
case 3: return blocked === 0 ? this.SCORES.THREE : this.SCORES.BLOCKED_THREE;
case 2: return blocked === 0 ? this.SCORES.TWO : this.SCORES.BLOCKED_TWO;
case 1: return this.SCORES.ONE;
default: return 0;
}
}
checkWin(row, col, player) {
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
for (const [dx, dy] of directions) {
let count = 1;
// 检查正方向
for (let i = 1; i < 5; i++) {
const newRow = row + dx * i;
const newCol = col + dy * i;
if (newRow < 0 || newRow >= this.BOARD_SIZE ||
newCol < 0 || newCol >= this.BOARD_SIZE ||
this.board[newRow][newCol] !== player) {
break;
}
count++;
}
// 检查负方向
for (let i = 1; i < 5; i++) {
const newRow = row - dx * i;
const newCol = col - dy * i;
if (newRow < 0 || newRow >= this.BOARD_SIZE ||
newCol < 0 || newCol >= this.BOARD_SIZE ||
this.board[newRow][newCol] !== player) {
break;
}
count++;
}
if (count >= 5) {
this.winDirection = { dx, dy };
return true;
}
}
return false;
}
highlightWinningStones(row, col, player) {
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
for (const [dx, dy] of directions) {
const stones = [{ row, col }];
// 收集正方向的棋子
for (let i = 1; i < 5; i++) {
const newRow = row + dx * i;
const newCol = col + dy * i;
if (newRow < 0 || newRow >= this.BOARD_SIZE ||
newCol < 0 || newCol >= this.BOARD_SIZE ||
this.board[newRow][newCol] !== player) {
break;
}
stones.push({ row: newRow, col: newCol });
}
// 收集负方向的棋子
for (let i = 1; i < 5; i++) {
const newRow = row - dx * i;
const newCol = col - dy * i;
if (newRow < 0 || newRow >= this.BOARD_SIZE ||
newCol < 0 || newCol >= this.BOARD_SIZE ||
this.board[newRow][newCol] !== player) {
break;
}
stones.push({ row: newRow, col: newCol });
}
if (stones.length >= 5) {
stones.forEach(stone => {
const cellIndex = stone.row * this.BOARD_SIZE + stone.col;
const cell = document.getElementById('board').children[cellIndex];
const stoneElement = cell.querySelector('.stone');
if (stoneElement) {
stoneElement.classList.add('winning-stone');
}
});
return;
}
}
}
getValidMoves() {
const moves = [];
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.board[i][j] === this.EMPTY) {
moves.push({ row: i, col: j });
}
}
}
return moves;
}
isBoardFull() {
return this.getValidMoves().length === 0;
}
updateGameStatus(message, className = '') {
const statusElement = document.getElementById('game-status');
statusElement.textContent = message;
statusElement.className = `game-status ${className}`;
}
updateCurrentPlayer() {
const playerText = this.currentPlayer === this.BLACK ? '黑棋 (玩家)' : '白棋 (AI)';
document.getElementById('current-player').textContent = playerText;
}
setDifficulty(difficulty) {
this.difficulty = difficulty;
}
newGame() {
this.initBoard();
this.createBoardUI();
this.updateCurrentPlayer();
}
}
// 初始化游戏
let game = new GomokuAI();
// 游戏控制函数
function newGame() {
game.newGame();
}
// 难度选择
document.getElementById('difficulty').addEventListener('change', function() {
game.setDifficulty(this.value);
});
// 键盘快捷键
document.addEventListener('keydown', function(e) {
if (e.key === 'n' || e.key === 'N') {
newGame();
}
});
console.log('🎮 五子棋AI游戏已加载完成!');
console.log('⌨️ 按 N 键开始新游戏');
</script>
</body>
</html>
● 这个五子棋AI的核心算法实现基于以下几个关键技术:
1. Minimax算法 + Alpha-Beta剪枝
minimax(depth, alpha, beta, isMaximizing) {
if (depth === 0) {
return this.evaluateBoard(); // 递归终止条件
}
if (isMaximizing) { // AI回合,寻找最大值
let maxScore = -Infinity;
for (const move of moves) {
this.board[move.row][move.col] = this.WHITE;
const score = this.minimax(depth - 1, alpha, beta, false);
this.board[move.row][move.col] = this.EMPTY;
maxScore = Math.max(score, maxScore);
alpha = Math.max(alpha, score);
if (beta <= alpha) break; // Alpha-Beta剪枝
}
return maxScore;
}
}
工作原理:
- 递归搜索未来N步的所有可能走法
- AI层寻找对自己最有利的走法(maximizing)
- 玩家层假设玩家会选择对AI最不利的走法(minimizing)
- Alpha-Beta剪枝大幅减少搜索空间
2. 棋局评估算法
evaluatePlayer(player) {
let score = 0;
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]]; // 四个方向
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.board[i][j] === player) {
for (const [dx, dy] of directions) {
score += this.evaluateLine(i, j, dx, dy, player);
}
}
}
}
return score;
}
评分体系:
SCORES = {
FIVE: 100000, // 五连:必胜
FOUR: 10000, // 活四:下一步必胜
BLOCKED_FOUR: 1000, // 冲四:一端被堵
THREE: 1000, // 活三:可形成活四
BLOCKED_THREE: 100, // 眠三:一端被堵
TWO: 100, // 活二
BLOCKED_TWO: 10, // 眠二
ONE: 10 // 单子
}
3. 智能搜索优化
候选位置筛选:
getCandidateMoves() {
// 只搜索已有棋子周围2格内的位置
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.board[i][j] !== this.EMPTY) {
for (let di = -2; di <= 2; di++) {
for (let dj = -2; dj <= 2; dj++) {
// 收集有效候选位置
}
}
}
}
}
}
关键位置优先检查:
findCriticalMove() {
// 1. 检查AI能否立即获胜
// 2. 检查是否需要阻止玩家获胜
// 3. 优先处理威胁和机会
}
4. 难度分级系统
const difficultySettings = {
easy: { depth: 2, randomness: 0.3 }, // 搜索2步,30%随机性
medium: { depth: 3, randomness: 0.1 }, // 搜索3步,10%随机性
hard: { depth: 4, randomness: 0.05 } // 搜索4步,5%随机性
};
难度实现:
- 搜索深度:越深AI越聪明,但计算时间指数增长
- 随机性:偶尔选择次优解,避免AI过于完美
- 计算限制:限制候选位置数量控制响应时间
5. 核心算法流程
玩家下棋 → 检查获胜 → AI开始思考
↓
找关键位置(必胜/必防) → 找到则直接下棋
↓
启动Minimax搜索 → 评估所有候选位置
↓
选择最高分位置 → 应用难度调整 → AI下棋
6. 性能优化技巧
- 位置剪枝:只考虑棋子周围的有效位置
- 深度限制:根据难度控制搜索深度
- Alpha-Beta剪枝:跳过无意义的搜索分支
- 评估缓存:避免重复计算相同棋局
- 异步处理:UI不阻塞,增加思考延迟提升体验感
这个AI实现了从基础博弈论到实际应用的完整流程,能够在合理时间内做出智能决策。
更多推荐
所有评论(0)