借助DeepSeek编写的一个HTML网页版小游戏--AI对弈五子棋
这是一个网页版五子棋小游戏,提供四种AI难度选择(声闻、圆觉、菩萨、佛陀)。游戏采用15×15棋盘,玩家执黑子先手,AI执白子。主要功能包括:背景音乐控制、音效开关、悔棋、提示、胜负统计等。AI采用启发式算法评估位置得分,不同难度对应不同的思考策略和时间。游戏界面美观,包含动态效果和响应式设计,支持键盘快捷键操作。
一个网页版五子棋小游戏,四种难度供您选择对弈。
游戏地址: AI对弈五子棋 https://amitofo.icu/game2.html
网页源代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>五子棋</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #0f2027 0%, #203a43 50%, #2c5364 100%);
color: #f0f0f0;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
overflow-x: hidden;
}
.header {
text-align: center;
margin-bottom: 20px;
width: 100%;
max-width: 1000px;
}
h1 {
color: #4dc3ff;
font-size: 2.8rem;
margin-bottom: 10px;
text-shadow: 0 0 15px rgba(77, 195, 255, 0.7);
letter-spacing: 2px;
}
.subtitle {
color: #a9d1e8;
font-size: 1.2rem;
margin-bottom: 25px;
}
.music-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 15px;
margin-top: 10px;
}
.music-btn {
background: rgba(77, 195, 255, 0.2);
border: 2px solid rgba(77, 195, 255, 0.5);
color: #4dc3ff;
padding: 8px 16px;
border-radius: 20px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9rem;
transition: all 0.3s;
}
.music-btn:hover {
background: rgba(77, 195, 255, 0.3);
transform: translateY(-2px);
}
.game-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 40px;
width: 100%;
max-width: 1200px;
}
.board-section {
flex: 1;
min-width: 520px;
max-width: 600px;
}
.info-section {
flex: 1;
min-width: 320px;
max-width: 450px;
background-color: rgba(20, 30, 40, 0.85);
border-radius: 15px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.6);
border: 1px solid rgba(77, 195, 255, 0.3);
}
.board-container {
position: relative;
background-color: #c19a6b;
border-radius: 10px;
padding: 20px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.8);
border: 8px solid #8b5a2b;
}
#game-board {
display: grid;
grid-template-columns: repeat(15, 1fr);
grid-template-rows: repeat(15, 1fr);
gap: 0;
width: 100%;
aspect-ratio: 1 / 1;
background-color: #e8c9a1;
position: relative;
}
.cell {
border-right: 1px solid #8b5a2b;
border-bottom: 1px solid #8b5a2b;
position: relative;
cursor: pointer;
}
.cell:hover {
background-color: rgba(139, 90, 43, 0.15);
}
.cell:nth-child(15n) {
border-right: none;
}
.cell:nth-child(n+211) {
border-bottom: none;
}
.stone {
position: absolute;
width: 90%;
height: 90%;
top: 5%;
left: 5%;
border-radius: 50%;
z-index: 10;
box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.6), 3px 3px 8px rgba(0, 0, 0, 0.4);
transition: all 0.3s;
animation: stoneAppear 0.3s ease-out;
}
@keyframes stoneAppear {
0% { transform: scale(0); opacity: 0; }
70% { transform: scale(1.1); opacity: 0.8; }
100% { transform: scale(1); opacity: 1; }
}
.black {
background: radial-gradient(circle at 30% 30%, #555, #000 70%);
}
.white {
background: radial-gradient(circle at 30% 30%, #fff, #aaa 70%);
}
.last-move {
box-shadow: 0 0 0 4px rgba(255, 50, 50, 0.9);
}
.hint-cell {
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { background-color: rgba(255, 215, 0, 0.3); }
50% { background-color: rgba(255, 215, 0, 0.7); }
100% { background-color: rgba(255, 215, 0, 0.3); }
}
.game-info {
margin-bottom: 25px;
}
.status-panel {
display: flex;
align-items: center;
justify-content: space-between;
background-color: rgba(30, 40, 50, 0.9);
padding: 15px;
border-radius: 10px;
margin-bottom: 25px;
border: 2px solid rgba(77, 195, 255, 0.2);
}
.player-info {
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
flex: 1;
}
.player-name {
font-size: 1.3rem;
font-weight: bold;
}
.human-player .player-name {
color: #4dc3ff;
}
.ai-player .player-name {
color: #ff6b6b;
}
.player-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
}
.human-icon {
background: radial-gradient(circle at 30% 30%, #4dc3ff, #1a8cff);
color: white;
}
.ai-icon {
background: radial-gradient(circle at 30% 30%, #ff6b6b, #ff3333);
color: white;
}
.active-player {
transform: scale(1.1);
box-shadow: 0 0 20px rgba(77, 195, 255, 0.9);
transition: all 0.3s;
}
.vs-text {
font-size: 1.8rem;
font-weight: bold;
color: #ffd700;
margin: 0 20px;
}
.game-controls {
margin-bottom: 30px;
}
.control-title {
font-size: 1.3rem;
margin-bottom: 15px;
color: #4dc3ff;
}
.difficulty-selector {
display: flex;
gap: 10px;
margin-bottom: 20px;
flex-wrap: wrap;
}
.difficulty-btn {
flex: 1;
min-width: 80px;
padding: 10px 15px;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: bold;
cursor: pointer;
transition: all 0.3s;
background-color: rgba(60, 80, 100, 0.8);
color: #ccc;
}
.difficulty-btn:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.difficulty-btn.active {
background-color: #4dc3ff;
color: white;
box-shadow: 0 0 15px rgba(77, 195, 255, 0.7);
}
.btn-group {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.btn {
flex: 1;
min-width: 120px;
padding: 12px 20px;
border: none;
border-radius: 8px;
font-size: 1.1rem;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
transition: all 0.3s;
}
.btn:hover {
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
}
.btn:active {
transform: translateY(0);
}
.btn-new-game {
background: linear-gradient(to right, #00b09b, #96c93d);
color: white;
}
.btn-undo {
background: linear-gradient(to right, #ff9966, #ff5e62);
color: white;
}
.btn-hint {
background: linear-gradient(to right, #ffcc00, #ff9900);
color: white;
}
.btn-settings {
background: linear-gradient(to right, #9d50bb, #6e48aa);
color: white;
}
.game-stats {
background-color: rgba(30, 40, 50, 0.9);
padding: 20px;
border-radius: 10px;
border: 2px solid rgba(77, 195, 255, 0.2);
}
.stats-title {
font-size: 1.3rem;
margin-bottom: 15px;
color: #ffd700;
border-bottom: 1px solid #555;
padding-bottom: 8px;
}
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
.stat-item {
display: flex;
flex-direction: column;
}
.stat-label {
font-size: 0.9rem;
color: #aaa;
margin-bottom: 5px;
}
.stat-value {
font-size: 1.5rem;
font-weight: bold;
color: #fff;
}
.ai-thinking {
display: none;
text-align: center;
margin-top: 15px;
padding: 10px;
background-color: rgba(255, 107, 107, 0.2);
border-radius: 8px;
color: #ffcc00;
font-style: italic;
}
.thinking-dots {
display: inline-block;
animation: dots 1.5s infinite;
}
@keyframes dots {
0%, 20% { content: "."; }
40% { content: ".."; }
60%, 100% { content: "..."; }
}
.winner-message {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.95);
color: white;
padding: 40px 60px;
border-radius: 15px;
text-align: center;
z-index: 100;
display: none;
box-shadow: 0 0 50px rgba(77, 195, 255, 0.8);
border: 3px solid #4dc3ff;
animation: popIn 0.5s;
min-width: 400px;
}
@keyframes popIn {
0% { transform: translate(-50%, -50%) scale(0.5); opacity: 0; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 1; }
}
.winner-title {
font-size: 2.5rem;
color: #ffd700;
margin-bottom: 20px;
}
.winner-desc {
font-size: 1.3rem;
margin-bottom: 30px;
}
.winner-buttons {
display: flex;
gap: 15px;
justify-content: center;
}
.btn-close {
background-color: #4dc3ff;
color: #000;
font-weight: bold;
padding: 12px 30px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1.1rem;
flex: 1;
}
.btn-rematch {
background-color: #ffd700;
color: #000;
font-weight: bold;
padding: 12px 30px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1.1rem;
flex: 1;
}
.footer {
margin-top: 40px;
text-align: center;
color: #88aabb;
font-size: 0.9rem;
width: 100%;
max-width: 1000px;
}
.rules {
margin-top: 15px;
font-size: 0.9rem;
color: #aaccdd;
line-height: 1.5;
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.volume-control {
display: flex;
align-items: center;
gap: 10px;
margin-top: 10px;
justify-content: center;
}
.volume-slider {
width: 150px;
}
@media (max-width: 1100px) {
.game-container {
flex-direction: column;
align-items: center;
}
.board-section, .info-section {
min-width: 90%;
}
}
@media (max-width: 600px) {
h1 {
font-size: 2rem;
}
.board-section {
min-width: 95%;
}
#game-board {
grid-template-columns: repeat(15, 1fr);
grid-template-rows: repeat(15, 1fr);
}
.status-panel {
flex-direction: column;
gap: 20px;
}
.vs-text {
margin: 10px 0;
}
.winner-message {
min-width: 90%;
padding: 30px 20px;
}
.btn {
min-width: 100%;
}
.music-controls {
flex-direction: column;
gap: 10px;
}
}
</style>
</head>
<body>
<div class="header">
<h1><i class="fas fa-robot"></i> 五子棋 </h1>
<p class="subtitle">享受沉浸式AI对弈体验</p>
<div class="music-controls">
<button class="music-btn" id="toggle-music">
<i class="fas fa-music"></i> <span id="music-text">背景音乐: 开</span>
</button>
<button class="music-btn" id="toggle-sound">
<i class="fas fa-volume-up"></i> <span id="sound-text">音效: 开</span>
</button>
<div class="volume-control">
<i class="fas fa-volume-down"></i>
<input type="range" min="0" max="100" value="70" class="volume-slider" id="volume-slider">
<i class="fas fa-volume-up"></i>
</div>
</div>
</div>
<div class="game-container">
<div class="board-section">
<div class="board-container">
<div id="game-board"></div>
</div>
</div>
<div class="info-section">
<div class="game-info">
<h2>对战状态</h2>
<div class="status-panel">
<div class="player-info human-player">
<div class="player-icon human-icon active-player" id="human-icon">
<i class="fas fa-user"></i>
</div>
<div class="player-name">玩家 (黑子)</div>
<div class="player-status" id="human-status">你的回合</div>
</div>
<div class="vs-text">VS</div>
<div class="player-info ai-player">
<div class="player-icon ai-icon" id="ai-icon">
<i class="fas fa-robot"></i>
</div>
<div class="player-name" id="ai-name">电脑 (白子)</div>
<div class="player-status" id="ai-status">等待中</div>
</div>
</div>
<div class="ai-thinking" id="ai-thinking">
<i class="fas fa-cog fa-spin"></i> AI思考中<span class="thinking-dots"></span>
</div>
</div>
<div class="game-controls">
<h3 class="control-title">AI等级</h3>
<div class="difficulty-selector">
<button class="difficulty-btn active" data-level="easy">声闻</button>
<button class="difficulty-btn" data-level="medium">圆觉</button>
<button class="difficulty-btn" data-level="hard">菩萨</button>
<button class="difficulty-btn" data-level="expert">佛陀</button>
</div>
<div class="btn-group">
<button class="btn btn-new-game" id="new-game">
<i class="fas fa-play-circle"></i> 新游戏
</button>
<button class="btn btn-undo" id="undo">
<i class="fas fa-undo"></i> 悔棋
</button>
<button class="btn btn-hint" id="hint">
<i class="fas fa-lightbulb"></i> 提示
</button>
<button class="btn btn-settings" id="toggle-first">
<i class="fas fa-chess-board"></i> 玩家先手
</button>
</div>
</div>
<div class="game-stats">
<h3 class="stats-title">游戏三昧</h3>
<div class="stats-grid">
<div class="stat-item">
<div class="stat-label">功德数</div>
<div class="stat-value" id="move-count">0</div>
</div>
<div class="stat-item">
<div class="stat-label">入定时间</div>
<div class="stat-value" id="game-time">00:00</div>
</div>
<div class="stat-item">
<div class="stat-label">玩家胜局</div>
<div class="stat-value" id="human-wins">0</div>
</div>
<div class="stat-item">
<div class="stat-label">AI胜局</div>
<div class="stat-value" id="ai-wins">0</div>
</div>
</div>
</div>
</div>
</div>
<div class="winner-message" id="winner-message">
<h2 class="winner-title" id="winner-title">游戏结束!</h2>
<p class="winner-desc" id="winner-text">恭喜您获得胜利!</p>
<div class="winner-buttons">
<button class="btn-rematch" id="rematch">再来一局</button>
<button class="btn-close" id="close-winner">关闭</button>
</div>
</div>
<div class="footer">
<p>© 2026 五子棋</p>
<div class="rules">
<p><strong>游戏说明:</strong> 玩家执黑子,AI执白子。轮流在棋盘上放置棋子,先形成横、竖、斜方向连续五个或以上相同棋子的一方获胜。</p>
<p><strong>音效说明:</strong> 游戏开始时自动播放背景音乐,可在上方控制面板调节音量。</p>
</div>
</div>
<!-- 音频元素 - 使用本地文件 -->
<audio id="background-music" loop>
<source src="beijing.ogg" type="audio/ogg">
<source src="beijing.mp3" type="audio/mpeg">
您的浏览器不支持音频元素。
</audio>
<audio id="move-sound">
<source src="xiaochu.mp3" type="audio/mpeg">
您的浏览器不支持音频元素。
</audio>
<audio id="ai-move-sound">
<source src="ai.mp3" type="audio/mpeg">
您的浏览器不支持音频元素。
</audio>
<audio id="win-sound">
<source src="win.mp3" type="audio/mpeg">
您的浏览器不支持音频元素。
</audio>
<script>
// 游戏状态变量
let board = [];
let currentPlayer = 'human'; // 'human' 或 'ai'
let gameOver = false;
let moveHistory = [];
let scores = { human: 0, ai: 0 };
let moveCount = 0;
let gameStartTime = null;
let gameTimer = null;
let lastMoveCell = null;
let hintCell = null;
let playerFirst = true;
let aiDifficulty = 'medium';
let aiThinking = false;
// 音效控制变量
let musicEnabled = true;
let soundEnabled = true;
let backgroundMusic = document.getElementById('background-music');
let moveSound = document.getElementById('move-sound');
let aiMoveSound = document.getElementById('ai-move-sound');
let winSound = document.getElementById('win-sound');
// 音频播放状态
let userInteracted = false;
// DOM元素
const gameBoard = document.getElementById('game-board');
const humanWinsEl = document.getElementById('human-wins');
const aiWinsEl = document.getElementById('ai-wins');
const moveCountEl = document.getElementById('move-count');
const gameTimeEl = document.getElementById('game-time');
const humanIcon = document.getElementById('human-icon');
const aiIcon = document.getElementById('ai-icon');
const humanStatus = document.getElementById('human-status');
const aiStatus = document.getElementById('ai-status');
const aiName = document.getElementById('ai-name');
const aiThinkingEl = document.getElementById('ai-thinking');
const newGameBtn = document.getElementById('new-game');
const undoBtn = document.getElementById('undo');
const hintBtn = document.getElementById('hint');
const toggleFirstBtn = document.getElementById('toggle-first');
const difficultyBtns = document.querySelectorAll('.difficulty-btn');
const winnerMessage = document.getElementById('winner-message');
const winnerTitle = document.getElementById('winner-title');
const winnerText = document.getElementById('winner-text');
const closeWinnerBtn = document.getElementById('close-winner');
const rematchBtn = document.getElementById('rematch');
const toggleMusicBtn = document.getElementById('toggle-music');
const toggleSoundBtn = document.getElementById('toggle-sound');
const volumeSlider = document.getElementById('volume-slider');
const musicText = document.getElementById('music-text');
const soundText = document.getElementById('sound-text');
// 初始化游戏
function initGame() {
// 清空棋盘
board = [];
for (let i = 0; i < 15; i++) {
board[i] = [];
for (let j = 0; j < 15; j++) {
board[i][j] = '';
}
}
// 清空游戏板
gameBoard.innerHTML = '';
// 创建棋盘格子
for (let i = 0; i < 15 * 15; i++) {
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = Math.floor(i / 15);
cell.dataset.col = i % 15;
cell.addEventListener('click', () => makeMove(cell));
gameBoard.appendChild(cell);
}
// 重置游戏状态
currentPlayer = playerFirst ? 'human' : 'ai';
gameOver = false;
moveHistory = [];
moveCount = 0;
moveCountEl.textContent = moveCount;
aiThinking = false;
aiThinkingEl.style.display = 'none';
// 清除提示
if (hintCell) {
hintCell.classList.remove('hint-cell');
hintCell = null;
}
// 重置计时器
if (gameTimer) {
clearInterval(gameTimer);
}
gameStartTime = new Date();
gameTimer = setInterval(updateGameTime, 1000);
updateGameTime();
// 更新玩家指示器
updatePlayerIndicator();
// 隐藏获胜消息
winnerMessage.style.display = 'none';
// 清除最后一步标记
if (lastMoveCell) {
lastMoveCell.classList.remove('last-move');
lastMoveCell = null;
}
// 更新AI名称
updateAIName();
// 预加载音频
preloadAudio();
// 尝试播放背景音乐
setTimeout(() => {
playBackgroundMusic();
}, 500);
// 如果AI先手,则AI先走
if (currentPlayer === 'ai') {
setTimeout(() => aiMakeMove(), 1000);
}
}
// 预加载音频
function preloadAudio() {
try {
backgroundMusic.load();
moveSound.load();
aiMoveSound.load();
winSound.load();
} catch (e) {
console.log("音频预加载失败:", e);
}
}
// 播放背景音乐
function playBackgroundMusic() {
if (!musicEnabled || !userInteracted) return;
try {
const volume = volumeSlider.value / 100;
backgroundMusic.volume = volume;
// 重置并播放
backgroundMusic.currentTime = 0;
const playPromise = backgroundMusic.play();
if (playPromise !== undefined) {
playPromise.catch(error => {
console.log("背景音乐播放失败:", error);
});
}
} catch (e) {
console.log("播放背景音乐时出错:", e);
}
}
// 播放落子音效(玩家)
function playMoveSound() {
if (!soundEnabled || !userInteracted) return;
try {
const volume = volumeSlider.value / 100;
moveSound.volume = volume;
moveSound.currentTime = 0;
moveSound.play().catch(e => {
console.log("落子音效播放失败:", e);
});
} catch (e) {
console.log("播放落子音效时出错:", e);
}
}
// 播放AI落子音效
function playAiMoveSound() {
if (!soundEnabled || !userInteracted) return;
try {
const volume = volumeSlider.value / 100;
aiMoveSound.volume = volume;
aiMoveSound.currentTime = 0;
aiMoveSound.play().catch(e => {
console.log("AI落子音效播放失败:", e);
});
} catch (e) {
console.log("播放AI落子音效时出错:", e);
}
}
// 播放胜利音效
function playWinSound() {
if (!soundEnabled || !userInteracted) return;
try {
const volume = volumeSlider.value / 100;
winSound.volume = volume;
winSound.currentTime = 0;
winSound.play().catch(e => {
console.log("胜利音效播放失败:", e);
});
} catch (e) {
console.log("播放胜利音效时出错:", e);
}
}
// 更新AI名称
function updateAIName() {
const difficultyNames = {
'easy': '声闻AI',
'medium': '圆觉AI',
'hard': '菩萨AI',
'expert': '佛陀AI'
};
aiName.textContent = `${difficultyNames[aiDifficulty]} (白子)`;
}
// 更新玩家指示器
function updatePlayerIndicator() {
if (currentPlayer === 'human') {
humanIcon.classList.add('active-player');
aiIcon.classList.remove('active-player');
humanStatus.textContent = '你的回合';
aiStatus.textContent = '等待中';
} else {
aiIcon.classList.add('active-player');
humanIcon.classList.remove('active-player');
humanStatus.textContent = '等待中';
aiStatus.textContent = '思考中';
}
}
// 更新游戏时间
function updateGameTime() {
if (!gameStartTime) return;
const now = new Date();
const diff = Math.floor((now - gameStartTime) / 1000);
const minutes = Math.floor(diff / 60).toString().padStart(2, '0');
const seconds = (diff % 60).toString().padStart(2, '0');
gameTimeEl.textContent = `${minutes}:${seconds}`;
}
// 玩家落子
function makeMove(cell) {
if (gameOver || currentPlayer !== 'human' || aiThinking) return;
const row = parseInt(cell.dataset.row);
const col = parseInt(cell.dataset.col);
// 如果该位置已有棋子,则忽略
if (board[row][col] !== '') return;
// 放置棋子
board[row][col] = 'black';
moveHistory.push({row, col, player: 'human'});
moveCount++;
moveCountEl.textContent = moveCount;
// 创建棋子元素
const stone = document.createElement('div');
stone.classList.add('stone', 'black');
cell.appendChild(stone);
// 清除提示
if (hintCell) {
hintCell.classList.remove('hint-cell');
hintCell = null;
}
// 标记最后一步
if (lastMoveCell) {
lastMoveCell.classList.remove('last-move');
}
cell.classList.add('last-move');
lastMoveCell = cell;
// 播放落子音效
playMoveSound();
// 检查胜负
if (checkWin(row, col, 'black')) {
endGame('human');
return;
}
// 检查平局
if (moveCount >= 15 * 15) {
endGame('draw');
return;
}
// 切换玩家
currentPlayer = 'ai';
updatePlayerIndicator();
// AI思考
setTimeout(() => aiMakeMove(), 500);
}
// AI落子
function aiMakeMove() {
if (gameOver || currentPlayer !== 'ai') return;
aiThinking = true;
aiThinkingEl.style.display = 'block';
// 根据难度设置不同的思考时间
const thinkTime = aiDifficulty === 'easy' ? 300 :
aiDifficulty === 'medium' ? 600 :
aiDifficulty === 'hard' ? 900 : 1200;
setTimeout(() => {
const move = getAIMove();
if (!move) return;
const {row, col} = move;
board[row][col] = 'white';
moveHistory.push({row, col, player: 'ai'});
moveCount++;
moveCountEl.textContent = moveCount;
// 创建棋子元素
const cellIndex = row * 15 + col;
const cell = gameBoard.children[cellIndex];
const stone = document.createElement('div');
stone.classList.add('stone', 'white');
cell.appendChild(stone);
// 标记最后一步
if (lastMoveCell) {
lastMoveCell.classList.remove('last-move');
}
cell.classList.add('last-move');
lastMoveCell = cell;
// 播放AI落子音效
playAiMoveSound();
// 检查胜负
if (checkWin(row, col, 'white')) {
endGame('ai');
return;
}
// 检查平局
if (moveCount >= 15 * 15) {
endGame('draw');
return;
}
// 切换玩家
currentPlayer = 'human';
aiThinking = false;
aiThinkingEl.style.display = 'none';
updatePlayerIndicator();
}, thinkTime);
}
// 获取AI移动位置
function getAIMove() {
const emptyCells = [];
for (let i = 0; i < 15; i++) {
for (let j = 0; j < 15; j++) {
if (board[i][j] === '') {
emptyCells.push({row: i, col: j});
}
}
}
if (emptyCells.length === 0) return null;
// 简单难度:随机选择
if (aiDifficulty === 'easy') {
return emptyCells[Math.floor(Math.random() * emptyCells.length)];
}
// 其他难度:使用启发式评估
let bestScore = -Infinity;
let bestMoves = [];
for (const cell of emptyCells) {
const {row, col} = cell;
let score = 0;
// 进攻:评估自己的潜在连线
board[row][col] = 'white';
score += evaluatePosition(row, col, 'white') * 2;
board[row][col] = '';
// 防守:评估对手的潜在连线
board[row][col] = 'black';
score += evaluatePosition(row, col, 'black');
board[row][col] = '';
// 中心偏好
const centerDist = Math.abs(row - 7) + Math.abs(col - 7);
score += (14 - centerDist) * 0.5;
// 根据难度调整进攻/防守权重
if (aiDifficulty === 'medium') {
score += Math.random() * 10; // 增加随机性
} else if (aiDifficulty === 'hard' || aiDifficulty === 'expert') {
// 高级策略:优先形成四连、三连
board[row][col] = 'white';
const whiteLines = getLineCounts(row, col, 'white');
score += whiteLines.four * 1000 + whiteLines.three * 100 + whiteLines.two * 10;
board[row][col] = '';
// 阻止对手形成四连、三连
board[row][col] = 'black';
const blackLines = getLineCounts(row, col, 'black');
score += blackLines.four * 800 + blackLines.three * 80;
board[row][col] = '';
// 专家难度额外考虑更远的位置
if (aiDifficulty === 'expert') {
// 评估两步后的情况
score += evaluateTwoStep(row, col, 'white') * 0.5;
}
}
if (score > bestScore) {
bestScore = score;
bestMoves = [cell];
} else if (score === bestScore) {
bestMoves.push(cell);
}
}
// 从最佳选择中随机选一个(增加不可预测性)
return bestMoves[Math.floor(Math.random() * bestMoves.length)];
}
// 评估位置得分
function evaluatePosition(row, col, player) {
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
let totalScore = 0;
for (const [dx, dy] of directions) {
let line = 1; // 当前位置
let openEnds = 0;
// 正向检查
for (let i = 1; i <= 4; i++) {
const newRow = row + dx * i;
const newCol = col + dy * i;
if (newRow < 0 || newRow >= 15 || newCol < 0 || newCol >= 15) break;
if (board[newRow][newCol] === player) {
line++;
} else if (board[newRow][newCol] === '') {
openEnds++;
break;
} else {
break;
}
}
// 反向检查
for (let i = 1; i <= 4; i++) {
const newRow = row - dx * i;
const newCol = col - dy * i;
if (newRow < 0 || newRow >= 15 || newCol < 0 || newCol >= 15) break;
if (board[newRow][newCol] === player) {
line++;
} else if (board[newRow][newCol] === '') {
openEnds++;
break;
} else {
break;
}
}
// 根据连线长度和开放端评分
if (line >= 5) {
totalScore += 100000; // 胜利
} else if (line === 4) {
totalScore += openEnds === 2 ? 10000 : openEnds === 1 ? 1000 : 0;
} else if (line === 3) {
totalScore += openEnds === 2 ? 1000 : openEnds === 1 ? 100 : 0;
} else if (line === 2) {
totalScore += openEnds === 2 ? 100 : openEnds === 1 ? 10 : 0;
}
}
return totalScore;
}
// 获取连线数量
function getLineCounts(row, col, player) {
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
const counts = {two: 0, three: 0, four: 0};
for (const [dx, dy] of directions) {
let line = 1;
// 正向检查
for (let i = 1; i <= 4; i++) {
const newRow = row + dx * i;
const newCol = col + dy * i;
if (newRow < 0 || newRow >= 15 || newCol < 0 || newCol >= 15) break;
if (board[newRow][newCol] === player) {
line++;
} else {
break;
}
}
// 反向检查
for (let i = 1; i <= 4; i++) {
const newRow = row - dx * i;
const newCol = col - dy * i;
if (newRow < 0 || newRow >= 15 || newCol < 0 || newCol >= 15) break;
if (board[newRow][newCol] === player) {
line++;
} else {
break;
}
}
if (line >= 5) {
// 胜利情况已经在别处处理
} else if (line === 4) {
counts.four++;
} else if (line === 3) {
counts.three++;
} else if (line === 2) {
counts.two++;
}
}
return counts;
}
// 评估两步后的情况(简化版)
function evaluateTwoStep(row, col, player) {
let score = 0;
// 模拟在此位置落子
board[row][col] = player;
// 检查对手的最佳应对
const opponent = player === 'white' ? 'black' : 'white';
let bestOpponentScore = 0;
// 简单评估对手可能的反击
for (let i = 0; i < 15; i++) {
for (let j = 0; j < 15; j++) {
if (board[i][j] === '') {
board[i][j] = opponent;
const opponentScore = evaluatePosition(i, j, opponent);
if (opponentScore > bestOpponentScore) {
bestOpponentScore = opponentScore;
}
board[i][j] = '';
}
}
}
// 还原棋盘
board[row][col] = '';
// 自己的得分减去对手的最佳得分
const myScore = evaluatePosition(row, col, player);
score = myScore - bestOpponentScore * 0.5;
return score;
}
// 检查是否获胜
function 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 <= 4; i++) {
const newRow = row + dx * i;
const newCol = col + dy * i;
if (newRow < 0 || newRow >= 15 || newCol < 0 || newCol >= 15) break;
if (board[newRow][newCol] === player) {
count++;
} else {
break;
}
}
// 反向检查
for (let i = 1; i <= 4; i++) {
const newRow = row - dx * i;
const newCol = col - dy * i;
if (newRow < 0 || newRow >= 15 || newCol < 0 || newCol >= 15) break;
if (board[newRow][newCol] === player) {
count++;
} else {
break;
}
}
if (count >= 5) {
return true;
}
}
return false;
}
// 结束游戏
function endGame(winner) {
gameOver = true;
aiThinking = false;
aiThinkingEl.style.display = 'none';
clearInterval(gameTimer);
// 播放胜利音效
playWinSound();
if (winner === 'human') {
scores.human++;
humanWinsEl.textContent = scores.human;
winnerTitle.textContent = "恭喜你获胜!";
winnerText.textContent = "玩家(黑子)获得胜利!";
} else if (winner === 'ai') {
scores.ai++;
aiWinsEl.textContent = scores.ai;
winnerTitle.textContent = "AI获胜!";
winnerText.textContent = `${aiName.textContent}获得胜利!`;
} else {
winnerTitle.textContent = "平局!";
winnerText.textContent = "棋盘已满,双方平手!";
}
winnerMessage.style.display = 'block';
}
// 悔棋
function undoMove() {
if (moveHistory.length === 0 || gameOver || aiThinking) return;
// 悔一步(如果是玩家的回合,需要悔两步,因为上一步是AI走的)
let stepsToUndo = currentPlayer === 'human' ? 2 : 1;
stepsToUndo = Math.min(stepsToUndo, moveHistory.length);
for (let i = 0; i < stepsToUndo; i++) {
const lastMove = moveHistory.pop();
const {row, col} = lastMove;
// 从棋盘移除棋子
board[row][col] = '';
moveCount--;
moveCountEl.textContent = moveCount;
// 从DOM移除棋子
const cellIndex = row * 15 + col;
const cell = gameBoard.children[cellIndex];
cell.innerHTML = '';
cell.classList.remove('last-move');
}
// 恢复上一步的最后一步标记
if (moveHistory.length > 0) {
const prevMove = moveHistory[moveHistory.length - 1];
const prevCellIndex = prevMove.row * 15 + prevMove.col;
const prevCell = gameBoard.children[prevCellIndex];
prevCell.classList.add('last-move');
lastMoveCell = prevCell;
// 更新当前玩家
currentPlayer = prevMove.player === 'human' ? 'human' : 'ai';
} else {
lastMoveCell = null;
currentPlayer = playerFirst ? 'human' : 'ai';
}
updatePlayerIndicator();
}
// 提示功能
function showHint() {
if (gameOver || currentPlayer !== 'human' || aiThinking) return;
// 清除之前的提示
if (hintCell) {
hintCell.classList.remove('hint-cell');
}
// 获取AI推荐的位置
const hint = getAIMove();
if (!hint) return;
const cellIndex = hint.row * 15 + hint.col;
hintCell = gameBoard.children[cellIndex];
hintCell.classList.add('hint-cell');
}
// 事件监听
newGameBtn.addEventListener('click', initGame);
undoBtn.addEventListener('click', undoMove);
hintBtn.addEventListener('click', showHint);
toggleFirstBtn.addEventListener('click', () => {
playerFirst = !playerFirst;
toggleFirstBtn.innerHTML = playerFirst ?
'<i class="fas fa-chess-board"></i> 玩家先手' :
'<i class="fas fa-robot"></i> AI先手';
initGame();
});
// 难度选择
difficultyBtns.forEach(btn => {
btn.addEventListener('click', () => {
difficultyBtns.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
aiDifficulty = btn.dataset.level;
updateAIName();
// 如果游戏正在进行,重新开始
if (moveCount > 0) {
if (confirm("切换难度将重新开始游戏,确定吗?")) {
initGame();
} else {
// 恢复之前的难度
difficultyBtns.forEach(b => {
if (b.dataset.level === aiDifficulty) {
b.classList.remove('active');
}
});
difficultyBtns.forEach(b => {
if (b.dataset.level === aiDifficulty) {
b.classList.add('active');
}
});
}
}
});
});
closeWinnerBtn.addEventListener('click', () => {
winnerMessage.style.display = 'none';
});
rematchBtn.addEventListener('click', () => {
winnerMessage.style.display = 'none';
initGame();
});
// 音乐控制
toggleMusicBtn.addEventListener('click', () => {
musicEnabled = !musicEnabled;
musicText.textContent = `背景音乐: ${musicEnabled ? '开' : '关'}`;
if (musicEnabled) {
playBackgroundMusic();
} else {
backgroundMusic.pause();
}
});
// 音效控制
toggleSoundBtn.addEventListener('click', () => {
soundEnabled = !soundEnabled;
soundText.textContent = `音效: ${soundEnabled ? '开' : '关'}`;
});
// 音量控制
volumeSlider.addEventListener('input', () => {
const volume = volumeSlider.value / 100;
backgroundMusic.volume = volume;
moveSound.volume = volume;
aiMoveSound.volume = volume;
winSound.volume = volume;
// 如果用户调整音量,视为用户交互
userInteracted = true;
});
// 用户交互处理(解决音频自动播放问题)
document.addEventListener('click', () => {
if (!userInteracted) {
userInteracted = true;
// 用户第一次点击后播放背景音乐
if (musicEnabled && backgroundMusic.paused) {
playBackgroundMusic();
}
}
});
document.addEventListener('keydown', () => {
if (!userInteracted) {
userInteracted = true;
// 用户第一次按键后播放背景音乐
if (musicEnabled && backgroundMusic.paused) {
playBackgroundMusic();
}
}
});
// 初始化游戏
initGame();
// 添加键盘快捷键
document.addEventListener('keydown', (e) => {
if (e.key === 'n' || e.key === 'N') {
initGame();
} else if (e.key === 'z' && e.ctrlKey) {
undoMove();
} else if (e.key === 'h' || e.key === 'H') {
showHint();
} else if (e.key === 'Escape') {
winnerMessage.style.display = 'none';
} else if (e.key === 'f' || e.key === 'F') {
playerFirst = !playerFirst;
toggleFirstBtn.innerHTML = playerFirst ?
'<i class="fas fa-chess-board"></i> 玩家先手' :
'<i class="fas fa-robot"></i> AI先手';
initGame();
} else if (e.key === 'm' || e.key === 'M') {
musicEnabled = !musicEnabled;
musicText.textContent = `背景音乐: ${musicEnabled ? '开' : '关'}`;
if (musicEnabled) {
playBackgroundMusic();
} else {
backgroundMusic.pause();
}
}
});
// 显示音频使用说明
console.log("游戏音频说明:");
console.log("1. 背景音乐: beijing.ogg (循环播放)");
console.log("2. 玩家落子音效: xiaochu.mp3");
console.log("3. AI落子音效: ai.mp3");
console.log("4. 胜利音效: win.mp3");
console.log("请确保这些文件与HTML文件在同一目录下");
</script>
</body>
</html>
更多推荐



所有评论(0)