五子棋游戏
body {
font-family: Arial, sans-serif;
background-color: #f0f0f0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
}
#game-container {
display: flex;
flex-direction: column;
align-items: center;
}
#game-board {
background-color: #f3d2b5;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
}
#info {
font-size: 20px;
margin-top: 20px;
text-align: center;
height: 30px;
}
#restart-btn {
margin-top: 20px;
padding: 8px 16px;
font-size: 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
#restart-btn:hover {
background-color: #45a049;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', () => {
const canvas = document.getElementById('game-board');
const ctx = canvas.getContext('2d');
const infoDiv = document.getElementById('info');
const restartBtn = document.getElementById('restart-btn');
const BOARD_SIZE = 15;
const CELL_SIZE = canvas.width / BOARD_SIZE;
const PIECE_RADIUS = CELL_SIZE * 0.4;
let board = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0));
let currentPlayer = 1; // 1: 黑棋(先手), 2: 白棋
let gameOver = false;
let playerColor = Math.random() < 0.5 ? 1 : 2;
let aiColor = playerColor === 1 ? 2 : 1;
// 棋型评分常量
const SCORE = {
FIVE: 100000,
LIVE_FOUR: 10000,
DIE_FOUR: 1000,
LIVE_THREE: 1000,
DIE_THREE: 100,
LIVE_TWO: 100,
DIE_TWO: 10,
SINGLE: 1
};
function initGame() {
board = Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0));
currentPlayer = 1;
playerColor = Math.random() < 0.5 ? 1 : 2;
aiColor = playerColor === 1 ? 2 : 1;
gameOver = false;
drawBoard();
updateInfo();
if (currentPlayer === 1 && aiColor === 1) {
setTimeout(aiMove, 500);
}
}
function drawBoard() {
ctx.fillStyle = '#f3d2b5';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
for (let i = 0; i < BOARD_SIZE; i++) {
ctx.beginPath();
ctx.moveTo(CELL_SIZE / 2, i * CELL_SIZE + CELL_SIZE / 2);
ctx.lineTo(canvas.width - CELL_SIZE / 2, i * CELL_SIZE + CELL_SIZE / 2);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(i * CELL_SIZE + CELL_SIZE / 2, CELL_SIZE / 2);
ctx.lineTo(i * CELL_SIZE + CELL_SIZE / 2, canvas.height - CELL_SIZE / 2);
ctx.stroke();
}
for (let i = 0; i < BOARD_SIZE; i++) {
for (let j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] === 1) {
drawPiece(j, i, 'black');
} else if (board[i][j] === 2) {
drawPiece(j, i, 'white');
}
}
}
}
function drawPiece(x, y, color) {
const centerX = x * CELL_SIZE + CELL_SIZE / 2;
const centerY = y * CELL_SIZE + CELL_SIZE / 2;
ctx.beginPath();
ctx.arc(centerX, centerY, PIECE_RADIUS, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
if (color === 'white') {
ctx.strokeStyle = '#000';
ctx.lineWidth = 1;
ctx.stroke();
}
}
function updateInfo() {
if (gameOver) return;
if (currentPlayer === playerColor) {
infoDiv.textContent = `你的回合 (${playerColor === 1 ? '黑棋' : '白棋'})`;
} else {
infoDiv.textContent = `AI思考中 (${aiColor === 1 ? '黑棋' : '白棋'})...`;
}
}
function placePiece(x, y) {
if (gameOver || currentPlayer !== playerColor) return;
const gridX = Math.round((x - CELL_SIZE / 2) / CELL_SIZE);
const gridY = Math.round((y - CELL_SIZE / 2) / CELL_SIZE);
if (gridX >= 0 && gridX < BOARD_SIZE && gridY >= 0 && gridY < BOARD_SIZE && board[gridY][gridX] === 0) {
board[gridY][gridX] = currentPlayer;
drawBoard();
if (checkWin(gridX, gridY, currentPlayer)) {
gameOver = true;
infoDiv.textContent = `恭喜你赢了! (${playerColor === 1 ? '黑棋' : '白棋'})`;
return;
}
currentPlayer = currentPlayer === 1 ? 2 : 1;
updateInfo();
setTimeout(aiMove, 500);
}
}
function aiMove() {
if (gameOver || currentPlayer !== aiColor) return;
// 1. 检查AI是否能直接获胜
const winMove = findWinningMove(aiColor);
if (winMove) {
makeAIMove(winMove.x, winMove.y);
return;
}
// 2. 检查是否需要防守玩家即将获胜
const blockWinMove = findWinningMove(playerColor);
if (blockWinMove) {
makeAIMove(blockWinMove.x, blockWinMove.y);
return;
}
// 3. 增强的活三检测和防守
const blockThreeMove = findBlockThreeMove();
if (blockThreeMove) {
makeAIMove(blockThreeMove.x, blockThreeMove.y);
return;
}
// 4. 尝试创建活四或冲四
const createFourMove = findBestScoreMove(aiColor, ['LIVE_FOUR', 'DIE_FOUR']);
if (createFourMove) {
makeAIMove(createFourMove.x, createFourMove.y);
return;
}
// 5. 阻止玩家形成活二
const blockTwoMove = findBestScoreMove(playerColor, ['LIVE_TWO']);
if (blockTwoMove) {
makeAIMove(blockTwoMove.x, blockTwoMove.y);
return;
}
// 6. 尝试创建活三
const createThreeMove = findBestScoreMove(aiColor, ['LIVE_THREE']);
if (createThreeMove) {
makeAIMove(createThreeMove.x, createThreeMove.y);
return;
}
// 7. 最佳落子点
const bestMove = findBestPosition();
if (bestMove) {
makeAIMove(bestMove.x, bestMove.y);
return;
}
}
// 增强的活三检测函数
function findBlockThreeMove() {
const directions = [
[1, 0], [0, 1], [1, 1], [1, -1]
];
let bestDefense = null;
let maxThreat = 0;
for (let y = 0; y < BOARD_SIZE; y++) {
for (let x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] === 0) {
let threatLevel = 0;
let defensePoints = [];
for (const [dx, dy] of directions) {
// 检查正方向
let count = 0;
let emptyCount = 0;
let blockCount = 0;
// 正方向
let i = 1;
while (true) {
const nx = x + i * dx;
const ny = y + i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
blockCount++;
break;
}
if (board[ny][nx] === playerColor) {
count++;
i++;
} else if (board[ny][nx] === 0) {
emptyCount++;
break;
} else {
blockCount++;
break;
}
}
// 负方向
i = 1;
while (true) {
const nx = x - i * dx;
const ny = y - i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
blockCount++;
break;
}
if (board[ny][nx] === playerColor) {
count++;
i++;
} else if (board[ny][nx] === 0) {
emptyCount++;
break;
} else {
blockCount++;
break;
}
}
// 判断是否是活三威胁
if (count === 3 && emptyCount >= 1) {
// 连活三
if (emptyCount === 2) {
threatLevel += SCORE.LIVE_THREE * 2;
defensePoints.push({x, y, direction: [dx, dy]});
}
// 跳活三
else {
// 检查是否是跳活三
const jumpThree = checkJumpThree(x, y, dx, dy, playerColor);
if (jumpThree) {
threatLevel += SCORE.LIVE_THREE;
defensePoints.push(jumpThree);
}
}
}
}
// 选择威胁最大的防守点
if (threatLevel > maxThreat) {
maxThreat = threatLevel;
if (defensePoints.length > 0) {
// 优先选择能同时防守多个方向的点
bestDefense = defensePoints[0];
}
}
}
}
}
return bestDefense;
}
// 检查跳活三
function checkJumpThree(x, y, dx, dy, color) {
// 检查正方向跳活三
let pattern1 = [
[0, 0],
[dx, dy],
[2*dx, 2*dy],
[3*dx, 3*dy]
];
let pattern2 = [
[0, 0],
[dx, dy],
[3*dx, 3*dy],
[4*dx, 4*dy]
];
// 检查第一种跳活三模式:_OO_O
let match1 = true;
for (const [px, py] of pattern1) {
const nx = x + px;
const ny = y + py;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
match1 = false;
break;
}
if ((px === 0 && py === 0) || (px === 3*dx && py === 3*dy)) {
if (board[ny][nx] !== 0) {
match1 = false;
break;
}
} else {
if (board[ny][nx] !== color) {
match1 = false;
break;
}
}
}
if (match1) {
return {x: x + 3*dx, y: y + 3*dy};
}
// 检查第二种跳活三模式:_O_OO
let match2 = true;
for (const [px, py] of pattern2) {
const nx = x + px;
const ny = y + py;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
match2 = false;
break;
}
if ((px === 0 && py === 0) || (px === dx && py === dy)) {
if (board[ny][nx] !== 0) {
match2 = false;
break;
}
} else {
if (board[ny][nx] !== color) {
match2 = false;
break;
}
}
}
if (match2) {
return {x: x + dx, y: y + dy};
}
return null;
}
function makeAIMove(x, y) {
board[y][x] = aiColor;
drawBoard();
if (checkWin(x, y, aiColor)) {
gameOver = true;
infoDiv.textContent = `AI赢了! (${aiColor === 1 ? '黑棋' : '白棋'})`;
return;
}
if (isDraw()) {
gameOver = true;
infoDiv.textContent = "平局!";
return;
}
currentPlayer = playerColor;
updateInfo();
}
function isDraw() {
for (let y = 0; y < BOARD_SIZE; y++) {
for (let x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] === 0) return false;
}
}
return true;
}
function findWinningMove(color) {
for (let y = 0; y < BOARD_SIZE; y++) {
for (let x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] === 0) {
board[y][x] = color;
if (checkWin(x, y, color)) {
board[y][x] = 0;
return {x, y};
}
board[y][x] = 0;
}
}
}
return null;
}
function findBestScoreMove(color, patterns) {
let bestScore = -1;
let bestMove = null;
for (let y = 0; y < BOARD_SIZE; y++) {
for (let x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] === 0) {
const score = evaluatePosition(x, y, color, patterns);
if (score > bestScore) {
bestScore = score;
bestMove = {x, y};
}
}
}
}
return bestMove;
}
function evaluatePosition(x, y, color, patterns) {
const directions = [
[1, 0], [0, 1], [1, 1], [1, -1]
];
let totalScore = 0;
for (const [dx, dy] of directions) {
const pattern = evaluateDirection(x, y, dx, dy, color);
if (patterns.includes(pattern.type)) {
totalScore += SCORE[pattern.type];
}
}
// 增加位置分(中心区域更高)
const centerX = BOARD_SIZE / 2;
const centerY = BOARD_SIZE / 2;
const distanceToCenter = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
totalScore += (BOARD_SIZE - distanceToCenter) * 2;
// 增加邻近棋子分
totalScore += getNearbyScore(x, y, color);
return totalScore;
}
function evaluateDirection(x, y, dx, dy, color) {
let count = 1;
let emptyEnds = 0;
let blockEnds = 0;
// 正方向
let i = 1;
while (true) {
const nx = x + i * dx;
const ny = y + i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
blockEnds++;
break;
}
if (board[ny][nx] === color) {
count++;
i++;
} else if (board[ny][nx] === 0) {
emptyEnds++;
break;
} else {
blockEnds++;
break;
}
}
// 负方向
i = 1;
while (true) {
const nx = x - i * dx;
const ny = y - i * dy;
if (nx < 0 || nx >= BOARD_SIZE || ny < 0 || ny >= BOARD_SIZE) {
blockEnds++;
break;
}
if (board[ny][nx] === color) {
count++;
i++;
} else if (board[ny][nx] === 0) {
emptyEnds++;
break;
} else {
blockEnds++;
break;
}
}
// 判断棋型
if (count >= 5) return {type: 'FIVE'};
if (count === 4) {
if (emptyEnds === 2) return {type: 'LIVE_FOUR'};
if (emptyEnds === 1) return {type: 'DIE_FOUR'};
}
if (count === 3) {
if (emptyEnds === 2) return {type: 'LIVE_THREE'};
if (emptyEnds === 1) return {type: 'DIE_THREE'};
}
if (count === 2) {
if (emptyEnds === 2) return {type: 'LIVE_TWO'};
if (emptyEnds === 1) return {type: 'DIE_TWO'};
}
return {type: 'SINGLE'};
}
function getNearbyScore(x, y, color) {
let score = 0;
const directions = [
[-1, -1], [-1, 0], [-1, 1],
[0, -1], [0, 1],
[1, -1], [1, 0], [1, 1]
];
for (const [dx, dy] of directions) {
const nx = x + dx;
const ny = y + dy;
if (nx >= 0 && nx < BOARD_SIZE && ny >= 0 && ny < BOARD_SIZE) {
if (board[ny][nx] === color) {
score += 5;
} else if (board[ny][nx] !== 0) {
score += 3;
}
}
}
return score;
}
function findBestPosition() {
let bestScore = -1;
let bestMove = null;
// 优先寻找靠近已有棋子的位置
for (let y = 0; y < BOARD_SIZE; y++) {
for (let x = 0; x < BOARD_SIZE; x++) {
if (board[y][x] === 0) {
// 评估位置价值
let score = 0;
// 1. 中心区域价值更高
const centerX = BOARD_SIZE / 2;
const centerY = BOARD_SIZE / 2;
const distanceToCenter = Math.sqrt(Math.pow(x - centerX, 2) + Math.pow(y - centerY, 2));
score += (BOARD_SIZE - distanceToCenter) * 2;
// 2. 靠近AI棋子的位置价值更高
score += getNearbyScore(x, y, aiColor) * 2;
// 3. 靠近玩家棋子的位置也有一定价值(防守)
score += getNearbyScore(x, y, playerColor);
if (score > bestScore) {
bestScore = score;
bestMove = {x, y};
}
}
}
}
return bestMove;
}
function checkWin(x, y, color) {
const directions = [
[1, 0], [0, 1], [1, 1], [1, -1]
];
for (const [dx, dy] of directions) {
let count = 1;
// 正方向
let i = 1;
while (x + i * dx >= 0 && x + i * dx < BOARD_SIZE &&
y + i * dy >= 0 && y + i * dy < BOARD_SIZE &&
board[y + i * dy][x + i * dx] === color) {
count++;
i++;
}
// 负方向
i = 1;
while (x - i * dx >= 0 && x - i * dx < BOARD_SIZE &&
y - i * dy >= 0 && y - i * dy < BOARD_SIZE &&
board[y - i * dy][x - i * dx] === color) {
count++;
i++;
}
if (count >= 5) {
return true;
}
}
return false;
}
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
placePiece(x, y);
});
restartBtn.addEventListener('click', initGame);
initGame();
});
</script>