<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8" />
<title>🎮 增强版五子棋 - 双人/电脑对战</title>
<style>
body {
background: #f4f4f4;
font-family: 'Microsoft Yahei', sans-serif;
text-align: center;
margin-top: 30px;
}
h1 {
color: #333;
}
canvas {
border: 2px solid #333;
background-color: #fdd;
display: block;
margin: 20px auto;
box-shadow: 0 0 15px rgba(0,0,0,0.2);
}
#status {
font-size: 18px;
margin-top: 10px;
color: green;
}
button {
padding: 8px 16px;
font-size: 16px;
cursor: pointer;
margin: 5px;
}
#score {
margin-top: 15px;
font-size: 16px;
}
select {
font-size: 16px;
padding: 5px;
}
</style>
</head>
<body>
<h1>🎮 增强版五子棋 - 玩家 vs 玩家 / 玩家 vs AI</h1>
<select id="gameMode">
<option value="local">本地双人对战</option>
<option value="ai">挑战电脑(黑先)</option>
</select>
<br/>
<canvas id="chessBoard" width="600" height="600"></canvas>
<div id="status">轮到黑棋落子</div>
<button onclick="restartGame()">重新开始</button>
<!-- 音效 -->
<audio id="placeSound">
<source src="https://cdn.pixabay.com/audio/2022/03/15/audio_4b7f9a1ba6.mp3" type="audio/mp3">
</audio>
<audio id="winSound">
<source src="https://cdn.pixabay.com/audio/2022/01/17/audio_c46dc4b0ac.mp3" type="audio/mp3">
</audio>
<div id="score">🎮 当前战绩:<br/>你(黑棋):0 胜 电脑(白棋):0 胜</div>
<script>
const canvas = document.getElementById("chessBoard");
const ctx = canvas.getContext("2d");
const statusDiv = document.getElementById("status");
const scoreDiv = document.getElementById("score");
const boardSize = 15;
const cellSize = canvas.width / (boardSize + 1);
let chessBoard = [];
let gameOver = false;
let currentPlayer = "black"; // 黑棋先手
let gameMode = "local"; // 默认本地双人
// 初始化胜负记录
function initScores() {
if (!localStorage.getItem("blackWins")) localStorage.setItem("blackWins", "0");
if (!localStorage.getItem("whiteWins")) localStorage.setItem("whiteWins", "0");
updateScoreDisplay();
}
function updateScoreDisplay() {
const blackWins = localStorage.getItem("blackWins");
const whiteWins = localStorage.getItem("whiteWins");
scoreDiv.innerHTML = `🎮 当前战绩:<br/>你(黑棋):${blackWins} 胜 电脑(白棋):${whiteWins} 胜`;
}
function playPlaceSound() {
document.getElementById("placeSound").play();
}
function playWinSound() {
document.getElementById("winSound").play();
}
function updateStatus(text) {
statusDiv.textContent = text;
}
function init() {
for (let i = 0; i < boardSize; i++) {
chessBoard[i] = [];
for (let j = 0; j < boardSize; j++) {
chessBoard[i][j] = "";
}
}
drawBoard();
gameOver = false;
currentPlayer = "black";
gameMode = document.getElementById("gameMode").value;
updateStatus(gameMode === "ai" ? "轮到你(黑棋)落子" : "轮到黑棋落子");
if (gameMode === "ai" && currentPlayer === "white") aiMove();
}
function restartGame() {
init();
}
function drawBoard() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 绘制网格线
ctx.strokeStyle = "#333";
for (let i = 1; i <= boardSize; i++) {
ctx.beginPath();
ctx.moveTo(cellSize, i * cellSize);
ctx.lineTo(boardSize * cellSize, i * cellSize);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(i * cellSize, cellSize);
ctx.lineTo(i * cellSize, boardSize * cellSize);
ctx.stroke();
}
// 绘制星位
ctx.fillStyle = "#333";
const stars = [
[3, 3], [11, 3],
[3, 11], [11, 11],
[7, 7]
];
stars.forEach(([x, y]) => {
ctx.beginPath();
ctx.arc((x + 1) * cellSize, (y + 1) * cellSize, 5, 0, Math.PI * 2);
ctx.fill();
});
// 绘制棋子
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
if (chessBoard[i][j] === "black") {
drawChess(i, j, "black");
} else if (chessBoard[i][j] === "white") {
drawChess(i, j, "white");
}
}
}
}
function drawChess(x, y, color) {
ctx.beginPath();
ctx.arc((x + 1) * cellSize, (y + 1) * cellSize, cellSize / 2 - 2, 0, Math.PI * 2);
ctx.fillStyle = color;
ctx.fill();
if (color === "white") {
ctx.strokeStyle = "#999";
ctx.stroke();
}
}
canvas.addEventListener("click", function (e) {
if (gameOver) return;
const rect = canvas.getBoundingClientRect();
const x = Math.round((e.clientX - rect.left) / cellSize) - 1;
const y = Math.round((e.clientY - rect.top) / cellSize) - 1;
if (
x >= 0 && x < boardSize &&
y >= 0 && y < boardSize &&
chessBoard[x][y] === ""
) {
chessBoard[x][y] = currentPlayer;
drawBoard();
playPlaceSound();
if (checkWin(x, y)) {
const winner = currentPlayer === "black" ? "黑棋" : "白棋";
updateStatus(`${winner} 获胜!🎉`);
playWinSound();
gameOver = true;
// 更新胜负记录
if (gameMode === "ai") {
let key = currentPlayer === "black" ? "blackWins" : "whiteWins";
let count = parseInt(localStorage.getItem(key));
localStorage.setItem(key, count + 1);
updateScoreDisplay();
}
return;
}
// 切换玩家
currentPlayer = currentPlayer === "black" ? "white" : "black";
if (gameMode === "ai" && currentPlayer === "white") {
updateStatus("电脑正在思考...");
setTimeout(aiMove, 600);
} else {
updateStatus(`轮到${currentPlayer === "black" ? "黑棋" : "白棋"}落子`);
}
}
});
function aiMove() {
if (gameOver) return;
let bestScore = -Infinity;
let bestMove = null;
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
if (chessBoard[i][j] === "") {
let score = evaluate(i, j, "white");
if (score > bestScore) {
bestScore = score;
bestMove = { x: i, y: j };
}
}
}
}
if (bestMove) {
chessBoard[bestMove.x][bestMove.y] = "white";
drawBoard();
playPlaceSound();
if (checkWin(bestMove.x, bestMove.y)) {
updateStatus("电脑(白棋)获胜了 😢");
playWinSound();
gameOver = true;
let key = "whiteWins";
let count = parseInt(localStorage.getItem(key));
localStorage.setItem(key, count + 1);
updateScoreDisplay();
return;
}
currentPlayer = "black";
updateStatus("轮到你(黑棋)落子");
}
}
// 棋型权重
const patternScores = {
live5: 100000,
live4: 10000,
doubleLive3: 5000,
live3: 1000,
live2: 100,
block4: 800,
block3: 50,
dead: 0
};
function evaluate(x, y, player) {
let score = 0;
const opponent = player === "white" ? "black" : "white";
const directions = [
[[1, 0], [-1, 0]],
[[0, 1], [0, -1]],
[[1, 1], [-1, -1]],
[[1, -1], [-1, 1]]
];
for (const dir of directions) {
let line = "";
for (let step = -4; step <= 4; step++) {
let tx = x + dir[0][0] * step;
let ty = y + dir[0][1] * step;
if (tx >= 0 && tx < boardSize && ty >= 0 && ty < boardSize) {
line += chessBoard[tx][ty] || "_";
} else {
line += "#";
}
}
line = line.replace(/_/i, "o"); // 替换当前点为中心
if (line.includes("ooooo")) return patternScores.live5;
if (/(_|^)oooo_/.test(line) || /_oooo($|_)/.test(line)) score += patternScores.live4;
else if (/oo_o_o|o_oo_o|o_o_oo|_ooo_/.test(line)) score += patternScores.live3;
else if (/oo_oo/.test(line)) score += patternScores.block4;
else if (/oo_/.test(line)) score += patternScores.live2;
}
return score;
}
function checkWin(x, y) {
const directions = [
[[1, 0], [-1, 0]],
[[0, 1], [0, -1]],
[[1, 1], [-1, -1]],
[[1, -1], [-1, 1]]
];
const player = chessBoard[x][y];
for (const dir of directions) {
let count = 1;
for (const d of dir) {
let tx = x + d[0];
let ty = y + d[1];
while (
tx >= 0 && tx < boardSize &&
ty >= 0 && ty < boardSize &&
chessBoard[tx][ty] === player
) {
count++;
tx += d[0];
ty += d[1];
}
}
if (count >= 5) return true;
}
return false;
}
// 页面加载初始化
document.getElementById("gameMode").addEventListener("change", init);
initScores();
init();
</script>
</body>
</html>
index.html
index.html