
<!DOCTYPE html>
<html lang="zh">
<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>
:root {
--primary-gradient: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
--panel-bg: rgba(25, 25, 35, 0.85);
--accent-color: #ffd700;
--board-bg: #dcb35c;
--board-border: #5d4037;
--board-line: #8b4513;
--black-stone: radial-gradient(circle at 30% 30%, #555, #000);
--white-stone: radial-gradient(circle at 30% 30%, #fff, #ddd);
--shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.4);
--shadow-md: 0 5px 15px rgba(0, 0, 0, 0.3);
--transition-default: all 0.3s ease;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
background: var(--primary-gradient);
background-attachment: fixed;
color: #fff;
padding: 20px;
line-height: 1.6;
}
.header {
text-align: center;
margin: 20px 0 30px;
width: 100%;
max-width: 800px;
}
.header h1 {
font-size: clamp(2.4rem, 5vw, 3.2rem);
margin-bottom: 8px;
text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
letter-spacing: 1.5px;
background: linear-gradient(to right, var(--accent-color), #ffffff);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.header p {
font-size: 1.1rem;
opacity: 0.9;
max-width: 600px;
margin: 0 auto;
}
.game-container {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 30px;
width: 100%;
max-width: 1200px;
margin-bottom: 30px;
}
.board-section {
display: flex;
flex-direction: column;
align-items: center;
background: var(--panel-bg);
border-radius: 16px;
padding: 25px;
box-shadow: var(--shadow-lg);
backdrop-filter: blur(10px);
transition: var(--transition-default);
}
.info-section {
flex: 1;
min-width: 300px;
display: flex;
flex-direction: column;
gap: 20px;
}
.info-card {
background: var(--panel-bg);
border-radius: 16px;
padding: 25px;
box-shadow: var(--shadow-md);
backdrop-filter: blur(10px);
transition: var(--transition-default);
}
.info-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.info-card h2 {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 18px;
color: var(--accent-color);
font-size: 1.8rem;
}
.board {
display: grid;
grid-template-columns: repeat(15, minmax(24px, 36px));
grid-template-rows: repeat(15, minmax(24px, 36px));
background-color: var(--board-bg);
border: 3px solid var(--board-border);
box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.3), 0 0 20px rgba(0, 0, 0, 0.5);
position: relative;
background-image:
linear-gradient(to right, var(--board-line) 1px, transparent 1px),
linear-gradient(to bottom, var(--board-line) 1px, transparent 1px);
background-size: 100% 100%;
}
/* 棋盘上的五个圆点 */
.board::before {
content: '';
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #000;
left: calc(3 * 100% / 14);
top: calc(3 * 100% / 14);
}
.board::after {
content: '';
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #000;
left: calc(11 * 100% / 14);
top: calc(3 * 100% / 14);
}
.board .center-dot-1 {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #000;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
.board .center-dot-2 {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #000;
left: calc(3 * 100% / 14);
top: calc(11 * 100% / 14);
}
.board .center-dot-3 {
position: absolute;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: #000;
left: calc(11 * 100% / 14);
top: calc(11 * 100% / 14);
}
.cell {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
z-index: 2;
transition: var(--transition-default);
pointer-events: auto;
}
.cell:hover::after {
content: '';
position: absolute;
width: 80%;
height: 80%;
border-radius: 50%;
background-color: rgba(255, 255, 255, 0.2);
z-index: 1;
}
.stone {
width: 85%;
height: 85%;
border-radius: 50%;
position: relative;
z-index: 3;
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.5);
transition: transform 0.2s ease;
}
.stone.animate {
animation: stoneDrop 0.4s ease;
}
@keyframes stoneDrop {
0% { transform: scale(0.1); opacity: 0; }
70% { transform: scale(1.1); opacity: 1; }
100% { transform: scale(1); }
}
.stone.black {
background: var(--black-stone);
border: 1px solid #333;
}
.stone.white {
background: var(--white-stone);
border: 1px solid #ccc;
}
.stone.winning {
animation: winPulse 1.5s infinite;
}
.last-move::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
box-shadow: 0 0 0 3px #ff3333;
z-index: 1;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 3px rgba(255, 51, 51, 0.7); }
50% { box-shadow: 0 0 0 6px rgba(255, 51, 51, 0.3); }
100% { box-shadow: 0 0 0 3px rgba(255, 51, 51, 0.7); }
}
.hint {
animation: hintPulse 0.5s infinite alternate;
}
@keyframes hintPulse {
from { box-shadow: 0 0 5px #00ff00; }
to { box-shadow: 0 0 15px #00ff00, 0 0 20px #00ff00; }
}
.status-area {
margin: 20px 0;
text-align: center;
width: 100%;
}
.status {
font-size: 1.8rem;
font-weight: bold;
padding: 12px 25px;
border-radius: 50px;
display: inline-block;
background: rgba(0, 0, 0, 0.4);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5);
margin-bottom: 15px;
transition: var(--transition-default);
}
.status.black-turn {
color: var(--accent-color);
background: rgba(0, 0, 0, 0.6);
}
.status.white-turn {
color: var(--accent-color);
background: rgba(255, 255, 255, 0.2);
}
.status.win {
color: #4cff00;
animation: winPulse 2s infinite;
}
@keyframes winPulse {
0% { box-shadow: 0 0 10px rgba(76, 255, 0, 0.5); }
50% { box-shadow: 0 0 25px rgba(76, 255, 0, 0.8); }
100% { box-shadow: 0 0 10px rgba(76, 255, 0, 0.5); }
}
.controls {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 15px;
width: 100%;
margin-top: 10px;
}
button {
padding: 14px 26px;
font-size: 1.05rem;
font-weight: 600;
background: linear-gradient(to right, #3498db, #1a5f9e);
color: white;
border: none;
border-radius: 50px;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
transition: var(--transition-default);
box-shadow: var(--shadow-md);
min-width: 130px;
justify-content: center;
}
button:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
background: linear-gradient(to right, #3cb0fd, #1a75ca);
}
button:active {
transform: translateY(1px);
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.3);
}
button#restart {
background: linear-gradient(to right, #e74c3c, #c0392b);
}
button#restart:hover {
background: linear-gradient(to right, #ff6b6b, #e74c3c);
}
button#undo {
background: linear-gradient(to right, #9b59b6, #8e44ad);
}
button#undo:hover {
background: linear-gradient(to right, #a66bbe, #9b59b6);
}
button#save {
background: linear-gradient(to right, #2ecc71, #27ae60);
}
button#save:hover {
background: linear-gradient(to right, #2ecc71, #27ae60);
}
.stats {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
margin-top: 10px;
}
.stat-item {
background: rgba(255, 255, 255, 0.1);
padding: 15px;
border-radius: 10px;
text-align: center;
transition: var(--transition-default);
}
.stat-item:hover {
transform: translateY(-3px);
background: rgba(255, 255, 255, 0.15);
}
.stat-value {
font-size: 2.2rem;
font-weight: bold;
color: var(--accent-color);
margin: 8px 0;
}
.stat-label {
font-size: 0.95rem;
opacity: 0.8;
}
.footer {
text-align: center;
padding: 20px;
font-size: 1rem;
opacity: 0.7;
width: 100%;
max-width: 800px;
margin-top: auto;
}
.settings-panel {
position: fixed;
top: 20px;
right: 20px;
background: var(--panel-bg);
border-radius: 10px;
padding: 15px;
box-shadow: var(--shadow-md);
z-index: 100;
transition: var(--transition-default);
}
.settings-toggle {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 5px;
min-width: auto;
}
.settings-content {
display: none;
margin-top: 10px;
animation: fadeIn 0.3s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(-10px); }
to { opacity: 1; transform: translateY(0); }
}
.settings-content.show {
display: block;
}
.settings-item {
margin-bottom: 10px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px 0;
}
.settings-item label {
margin-right: 10px;
cursor: pointer;
}
/* 复选框美化 */
.toggle-switch {
position: relative;
display: inline-block;
width: 46px;
height: 24px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.2);
transition: .4s;
border-radius: 24px;
}
.toggle-slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .toggle-slider {
background-color: var(--accent-color);
}
input:checked + .toggle-slider:before {
transform: translateX(22px);
}
/* 难度选择器 */
.difficulty-selector {
display: flex;
justify-content: space-between;
margin: 15px 0;
}
.difficulty-btn {
padding: 8px 15px;
font-size: 0.9rem;
background: rgba(255, 255, 255, 0.1);
border: 2px solid transparent;
}
.difficulty-btn.active {
background: rgba(255, 215, 0, 0.3);
border-color: var(--accent-color);
}
/* 游戏模式选择 */
.game-mode {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.mode-btn {
flex: 1;
padding: 10px;
background: rgba(255, 255, 255, 0.1);
}
.mode-btn.active {
background: rgba(52, 152, 219, 0.5);
}
/* 保存的游戏列表 */
.saved-games {
max-height: 200px;
overflow-y: auto;
margin-top: 10px;
}
.saved-game-item {
background: rgba(255, 255, 255, 0.1);
padding: 10px;
border-radius: 8px;
margin-bottom: 8px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
transition: var(--transition-default);
}
.saved-game-item:hover {
background: rgba(255, 255, 255, 0.2);
}
.saved-game-info {
flex: 1;
}
.saved-game-name {
font-weight: bold;
margin-bottom: 3px;
}
.saved-game-date {
font-size: 0.8rem;
opacity: 0.7;
}
.saved-game-controls button {
padding: 5px;
margin-left: 5px;
min-width: auto;
background: none;
box-shadow: none;
}
/* 保存游戏弹窗 */
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 2000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.modal.show {
opacity: 1;
pointer-events: all;
}
.modal-content {
background: var(--panel-bg);
padding: 25px;
border-radius: 16px;
width: 90%;
max-width: 400px;
box-shadow: var(--shadow-lg);
transform: translateY(-20px);
transition: transform 0.3s ease;
}
.modal.show .modal-content {
transform: translateY(0);
}
.modal h3 {
margin-bottom: 15px;
color: var(--accent-color);
font-size: 1.5rem;
}
.modal input {
width: 100%;
padding: 12px 15px;
border: 2px solid rgba(255, 255, 255, 0.2);
background: rgba(255, 255, 255, 0.1);
color: white;
border-radius: 8px;
margin-bottom: 20px;
font-size: 1rem;
}
.modal-buttons {
display: flex;
gap: 10px;
}
.modal-buttons button {
flex: 1;
}
/* 移动设备上的走棋历史 */
.move-history {
max-height: 150px;
overflow-y: auto;
margin-top: 10px;
padding-right: 5px;
}
.move-item {
display: flex;
justify-content: space-between;
padding: 5px 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.move-number {
color: var(--accent-color);
font-weight: bold;
min-width: 30px;
}
.move-position {
flex: 1;
text-align: center;
}
/* 胜利动画样式 */
.win-animation {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.85);
display: flex;
justify-content: center;
align-items: center;
z-index: 3000;
opacity: 0;
pointer-events: none;
transition: opacity 0.5s ease;
}
.win-animation.show {
opacity: 1;
pointer-events: all;
}
.win-content {
text-align: center;
padding: 40px;
background: var(--panel-bg);
border-radius: 20px;
box-shadow: 0 0 30px rgba(255, 215, 0, 0.3);
animation: popIn 0.6s ease;
}
@keyframes popIn {
0% { transform: scale(0.5); opacity: 0; }
70% { transform: scale(1.1); }
100% { transform: scale(1); opacity: 1; }
}
.win-content h2 {
color: var(--accent-color);
font-size: 2.5rem;
margin-bottom: 20px;
}
.win-content p {
font-size: 1.5rem;
margin-bottom: 30px;
}
/* 响应式设计优化 */
@media (max-width: 768px) {
.header {
margin: 10px 0 20px;
}
.game-container {
gap: 20px;
}
.board-section {
padding: 15px;
width: 100%;
}
.info-card {
padding: 20px;
}
.controls {
gap: 10px;
}
button {
padding: 12px 18px;
font-size: 0.9rem;
min-width: auto;
flex: 1;
}
}
@media (max-width: 480px) {
.controls {
flex-direction: column;
}
button {
width: 100%;
}
.stats {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="header">
<h1><i class="fas fa-chess-board"></i> 五子棋大师</h1>
<p>策略与智慧的终极对决!在古老棋盘上展现您的实力,成为五子连珠的冠军</p>
</div>
<div class="settings-panel">
<button class="settings-toggle" id="settings-toggle" aria-label="打开设置"><i class="fas fa-cog"></i></button>
<div class="settings-content" id="settings-content">
<div class="settings-item">
<label for="sound-toggle">声音</label>
<label class="toggle-switch">
<input type="checkbox" id="sound-toggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-item">
<label for="animations-toggle">动画</label>
<label class="toggle-switch">
<input type="checkbox" id="animations-toggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-item">
<label for="hints-toggle">提示</label>
<label class="toggle-switch">
<input type="checkbox" id="hints-toggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-item">
<label for="history-toggle">显示走棋历史</label>
<label class="toggle-switch">
<input type="checkbox" id="history-toggle" checked>
<span class="toggle-slider"></span>
</label>
</div>
</div>
</div>
<div class="game-container">
<div class="board-section">
<div class="board" id="board" role="grid" aria-label="五子棋棋盘">
<div class="center-dot-1"></div>
<div class="center-dot-2"></div>
<div class="center-dot-3"></div>
</div>
<div class="status-area">
<div class="status black-turn" id="status" role="status">黑方回合</div>
<div class="controls">
<button id="restart" aria-label="重新开始游戏"><i class="fas fa-redo"></i> 重新开始</button>
<button id="undo" aria-label="悔棋"><i class="fas fa-undo"></i> 悔棋</button>
<button id="hint" aria-label="显示提示"><i class="fas fa-lightbulb"></i> 提示</button>
<button id="save" aria-label="保存游戏"><i class="fas fa-save"></i> 保存</button>
</div>
</div>
</div>
<div class="info-section">
<div class="info-card">
<h2><i class="fas fa-trophy"></i> 游戏统计</h2>
<div class="stats">
<div class="stat-item">
<div class="stat-value" id="black-wins">0</div>
<div class="stat-label">黑方胜利</div>
</div>
<div class="stat-item">
<div class="stat-value" id="white-wins">0</div>
<div class="stat-label">白方胜利</div>
</div>
<div class="stat-item">
<div class="stat-value" id="black-score">0</div>
<div class="stat-label">黑方得分</div>
</div>
<div class="stat-item">
<div class="stat-value" id="white-score">0</div>
<div class="stat-label">白方得分</div>
</div>
</div>
</div>
<div class="info-card">
<h2><i class="fas fa-history"></i> 走棋历史</h2>
<div class="move-history" id="move-history">
<div class="move-item">
<div class="move-number">-</div>
<div class="move-position">游戏尚未开始</div>
</div>
</div>
</div>
<div class="info-card">
<h2><i class="fas fa-cog"></i> 游戏设置</h2>
<div class="game-mode">
<button class="mode-btn active" data-mode="player-vs-ai">人机对战</button>
<button class="mode-btn" data-mode="player-vs-player">人人对战</button>
</div>
<h3 style="margin: 15px 0 5px; color: var(--accent-color);">难度级别</h3>
<div class="difficulty-selector">
<button class="difficulty-btn active" data-depth="1">简单</button>
<button class="difficulty-btn" data-depth="2">中等</button>
<button class="difficulty-btn" data-depth="3">困难</button>
</div>
</div>
<div class="info-card">
<h2><i class="fas fa-save"></i> 保存的游戏</h2>
<div class="saved-games" id="saved-games">
<div class="no-saved-games">暂无保存的游戏</div>
</div>
</div>
</div>
</div>
<!-- 保存游戏弹窗 -->
<div class="modal" id="save-modal">
<div class="modal-content">
<h3><i class="fas fa-save"></i> 保存当前游戏</h3>
<input type="text" id="game-name" placeholder="输入游戏名称..." value="未命名游戏">
<div class="modal-buttons">
<button id="cancel-save"><i class="fas fa-times"></i> 取消</button>
<button id="confirm-save"><i class="fas fa-check"></i> 保存</button>
</div>
</div>
</div>
<div class="win-animation" id="win-animation">
<div class="win-content">
<h2><i class="fas fa-crown"></i> 胜利!</h2>
<p id="winner-text">黑方获胜</p>
<button id="new-game-btn"><i class="fas fa-play-circle"></i> 开始新游戏</button>
</div>
</div>
<div class="footer">
<p>© 2025 五子棋大师 | 今日:2025年8月24日 农历七月初二 | 最后更新:14:30</p>
</div>
<script>
class GomokuGame {
constructor() {
// 初始化默认设置,防止undefined
this.settings = {
sound: true,
animations: true,
showHints: true,
showHistory: true,
gameMode: 'player-vs-ai',
difficulty: 1
};
// 常量定义
this.BOARD_SIZE = 15;
this.PLAYERS = {
BLACK: 'black',
WHITE: 'white',
AI: 'white' // AI执白棋
};
this.GAME_MODES = {
PLAYER_VS_AI: 'player-vs-ai',
PLAYER_VS_PLAYER: 'player-vs-player'
};
this.DIFFICULTY = {
EASY: 1,
MEDIUM: 2,
HARD: 3
};
this.PATTERNS = {
FIVE: 100000,
OPEN_FOUR: 5000,
HALF_FOUR: 1000,
OPEN_THREE: 500,
HALF_THREE: 100,
OPEN_TWO: 50,
HALF_TWO: 10,
OPEN_ONE: 5
};
// 初始化统计数据
try {
this.blackWins = localStorage.getItem('gomoku_blackWins') ? parseInt(localStorage.getItem('gomoku_blackWins')) : 0;
this.whiteWins = localStorage.getItem('gomoku_whiteWins') ? parseInt(localStorage.getItem('gomoku_whiteWins')) : 0;
} catch (e) {
console.error('加载统计数据失败:', e);
this.blackWins = 0;
this.whiteWins = 0;
}
// 游戏设置
this.currentMode = this.GAME_MODES.PLAYER_VS_AI;
this.aiDepth = this.DIFFICULTY.EASY;
this.showHistory = true;
// 初始化元素引用
this.initElements();
// 绑定事件
this.bindEvents();
// 初始化游戏状态
this.resetGame();
// 先初始化设置,再初始化声音
this.initSettings();
this.initSounds();
// 加载保存的游戏
this.loadSavedGames();
// 更新统计显示
this.updateStatsDisplay();
// 暴露实例到window便于调试
window.gomokuGame = this;
}
initElements() {
this.boardEl = document.getElementById('board');
this.statusDisplay = document.getElementById('status');
this.blackWinsEl = document.getElementById('black-wins');
this.whiteWinsEl = document.getElementById('white-wins');
this.blackScoreEl = document.getElementById('black-score');
this.whiteScoreEl = document.getElementById('white-score');
this.winAnimation = document.getElementById('win-animation');
this.winnerText = document.getElementById('winner-text');
this.settingsToggle = document.getElementById('settings-toggle');
this.settingsContent = document.getElementById('settings-content');
this.soundToggle = document.getElementById('sound-toggle');
this.animationsToggle = document.getElementById('animations-toggle');
this.hintsToggle = document.getElementById('hints-toggle');
this.historyToggle = document.getElementById('history-toggle');
this.modeButtons = document.querySelectorAll('.mode-btn');
this.difficultyButtons = document.querySelectorAll('.difficulty-btn');
this.moveHistoryEl = document.getElementById('move-history');
this.savedGamesEl = document.getElementById('saved-games');
this.noSavedGamesEl = document.querySelector('.no-saved-games');
this.saveButton = document.getElementById('save');
this.saveModal = document.getElementById('save-modal');
this.gameNameInput = document.getElementById('game-name');
this.confirmSaveButton = document.getElementById('confirm-save');
this.cancelSaveButton = document.getElementById('cancel-save');
this.lastMoveCell = null;
this.winningCells = [];
this.aiThinking = false;
}
bindEvents() {
document.getElementById('restart').addEventListener('click', () => this.resetGame());
document.getElementById('undo').addEventListener('click', () => this.undoMove());
document.getElementById('hint').addEventListener('click', () => this.showHint());
document.getElementById('new-game-btn').addEventListener('click', () => {
this.winAnimation.classList.remove('show');
//this.resetGame();
});
this.settingsToggle.addEventListener('click', () => this.toggleSettings());
// 保存游戏相关事件
this.saveButton.addEventListener('click', () => this.openSaveModal());
this.confirmSaveButton.addEventListener('click', () => this.saveCurrentGame());
this.cancelSaveButton.addEventListener('click', () => this.closeSaveModal());
// 游戏模式切换
this.modeButtons.forEach(btn => {
btn.addEventListener('click', () => {
this.modeButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
this.currentMode = btn.dataset.mode;
this.saveSettings();
this.resetGame();
});
});
// 难度切换
this.difficultyButtons.forEach(btn => {
btn.addEventListener('click', () => {
this.difficultyButtons.forEach(b => b.classList.remove('active'));
btn.classList.add('active');
this.aiDepth = parseInt(btn.dataset.depth);
this.saveSettings();
});
});
// 走棋历史显示切换
this.historyToggle.addEventListener('change', (e) => {
this.showHistory = e.target.checked;
this.saveSettings();
if (this.showHistory) {
this.moveHistoryEl.style.display = 'block';
this.updateMoveHistory();
} else {
this.moveHistoryEl.style.display = 'none';
}
});
// 键盘支持
document.addEventListener('keydown', (e) => {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
if (e.key === 'r' && !e.ctrlKey) this.resetGame();
if (e.key === 'u' && !e.ctrlKey) this.undoMove();
if (e.key === 'h' && !e.ctrlKey) this.showHint();
if (e.key === 's' && !e.ctrlKey) this.openSaveModal();
});
}
initSounds() {
// 使用Web Audio API创建音效
this.audioContext = null;
this.sounds = {
place: { frequency: 330, duration: 0.15, volume: 0.3 },
win: { frequency: 660, duration: 0.8, volume: 0.5 },
click: { frequency: 440, duration: 0.1, volume: 0.2 },
error: { frequency: 220, duration: 0.2, volume: 0.3 }
};
}
createSound(frequency, duration, volume) {
if (!this.audioContext) {
try {
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
} catch (e) {
console.log('Web Audio API 不受支持');
return;
}
}
const oscillator = this.audioContext.createOscillator();
const gainNode = this.audioContext.createGain();
oscillator.type = 'sine';
oscillator.frequency.setValueAtTime(frequency, this.audioContext.currentTime);
gainNode.gain.setValueAtTime(volume, this.audioContext.currentTime);
oscillator.connect(gainNode);
gainNode.connect(this.audioContext.destination);
oscillator.start();
oscillator.stop(this.audioContext.currentTime + duration);
gainNode.gain.exponentialRampToValueAtTime(0.001, this.audioContext.currentTime + duration);
}
playSound(type) {
// 添加settings存在性检查
if (!this.settings || !this.settings.sound) return;
const sound = this.sounds[type];
if (sound) {
this.createSound(sound.frequency, sound.duration, sound.volume);
}
}
initSettings() {
try {
// 从本地存储加载设置
const savedSettings = localStorage.getItem('gomoku_settings');
if (savedSettings) {
// 合并保存的设置到现有默认设置
this.settings = { ...this.settings, ...JSON.parse(savedSettings) };
}
} catch (e) {
console.error('加载设置失败,使用默认设置:', e);
// 保留已初始化的默认设置
}
// 更新UI状态
this.soundToggle.checked = this.settings.sound;
this.animationsToggle.checked = this.settings.animations;
this.hintsToggle.checked = this.settings.showHints;
this.historyToggle.checked = this.settings.showHistory;
this.showHistory = this.settings.showHistory;
// 恢复游戏模式和难度
this.currentMode = this.settings.gameMode || this.GAME_MODES.PLAYER_VS_AI;
this.aiDepth = this.settings.difficulty || this.DIFFICULTY.EASY;
// 更新按钮状态
this.modeButtons.forEach(btn => {
btn.classList.toggle('active', btn.dataset.mode === this.currentMode);
});
this.difficultyButtons.forEach(btn => {
btn.classList.toggle('active', parseInt(btn.dataset.depth) === this.aiDepth);
});
// 绑定设置变更事件
this.soundToggle.addEventListener('change', (e) => {
this.settings.sound = e.target.checked;
this.saveSettings();
});
this.animationsToggle.addEventListener('change', (e) => {
this.settings.animations = e.target.checked;
this.saveSettings();
});
this.hintsToggle.addEventListener('change', (e) => {
this.settings.showHints = e.target.checked;
this.saveSettings();
});
}
saveSettings() {
this.settings.gameMode = this.currentMode;
this.settings.difficulty = this.aiDepth;
this.settings.showHistory = this.showHistory;
try {
localStorage.setItem('gomoku_settings', JSON.stringify(this.settings));
} catch (e) {
console.error('保存设置失败:', e);
}
}
toggleSettings() {
this.settingsContent.classList.toggle('show');
this.playSound('click');
}
resetGame() {
this.gameBoard = Array(this.BOARD_SIZE).fill().map(() => Array(this.BOARD_SIZE).fill(null));
this.currentPlayer = this.PLAYERS.BLACK;
this.gameActive = true;
this.lastMove = null;
this.moveHistory = [];
this.cellElements = {};
this.winningCells = [];
this.aiThinking = false;
if (this.lastMoveCell) {
this.lastMoveCell.classList.remove('last-move');
this.lastMoveCell = null;
}
this.renderBoard();
this.updateStatus();
this.updateMoveHistory();
}
renderBoard() {
const fragment = document.createDocumentFragment();
this.boardEl.innerHTML = '';
// 添加棋盘上的五个圆点
const dot1 = document.createElement('div');
dot1.className = 'center-dot-1';
fragment.appendChild(dot1);
const dot2 = document.createElement('div');
dot2.className = 'center-dot-2';
fragment.appendChild(dot2);
const dot3 = document.createElement('div');
dot3.className = 'center-dot-3';
fragment.appendChild(dot3);
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.setAttribute('role', 'gridcell');
cell.setAttribute('aria-label', `行 ${i+1} 列 ${j+1}`);
const handleClick = () => this.handleCellClick(i, j);
cell.addEventListener('click', handleClick);
cell.clickHandler = handleClick;
fragment.appendChild(cell);
this.cellElements[`${i}-${j}`] = cell;
}
}
this.boardEl.appendChild(fragment);
}
handleCellClick(row, col) {
if (!this.gameActive || this.aiThinking) {
this.playSound('error');
return;
}
if (this.currentPlayer === this.PLAYERS.AI && this.currentMode === this.GAME_MODES.PLAYER_VS_AI) {
this.playSound('error');
return;
}
if (this.gameBoard[row][col] !== null) {
this.playSound('error');
return;
}
this.playSound('place');
this.placeStone(row, col, this.currentPlayer);
if (this.currentMode === this.GAME_MODES.PLAYER_VS_AI && this.gameActive) {
this.aiMove();
}
}
placeStone(row, col, player) {
this.gameBoard[row][col] = player;
this.moveHistory.push({row, col, player});
this.lastMove = {row, col};
this.updateBoard();
this.updateMoveHistory();
if (this.checkWin(row, col)) {
this.showWinningStones();
this.showWinAnimation(player);
if (player === this.PLAYERS.BLACK) {
this.blackWins++;
} else {
this.whiteWins++;
}
this.saveStats();
this.updateStatsDisplay();
this.updateScore();
this.gameActive = false;
this.playSound('win');
return;
}
if (this.checkDraw()) {
this.statusDisplay.textContent = '平局!';
this.statusDisplay.className = 'status';
this.gameActive = false;
return;
}
this.switchPlayer();
}
updateBoard() {
this.winningCells.forEach(({row, col}) => {
const stone = this.cellElements[`${row}-${col}`].querySelector('.stone');
if (stone) stone.classList.remove('winning');
});
this.winningCells = [];
if (this.lastMove) {
const {row, col} = this.lastMove;
const cell = this.cellElements[`${row}-${col}`];
cell.innerHTML = '';
if (this.gameBoard[row][col]) {
const stone = document.createElement('div');
stone.className = `stone ${this.gameBoard[row][col]}`;
if (this.settings.animations) {
stone.classList.add('animate');
}
cell.appendChild(stone);
if (this.settings.animations) {
setTimeout(() => stone.classList.remove('animate'), 400);
}
}
if (this.lastMoveCell) this.lastMoveCell.classList.remove('last-move');
cell.classList.add('last-move');
this.lastMoveCell = cell;
}
this.updateScore();
}
showWinningStones() {
if (!this.lastMove) return;
const {row, col} = this.lastMove;
const player = this.gameBoard[row][col];
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
for (const [dx, dy] of directions) {
const winningSequence = [{row, col}];
// 正向检查
for (let i = 1; i <= 4; i++) {
const r = row + i * dx;
const c = col + i * dy;
if (r < 0 || r >= this.BOARD_SIZE || c < 0 || c >= this.BOARD_SIZE ||
this.gameBoard[r][c] !== player) break;
winningSequence.push({row: r, col: c});
}
// 反向检查
for (let i = 1; i <= 4; i++) {
const r = row - i * dx;
const c = col - i * dy;
if (r < 0 || r >= this.BOARD_SIZE || c < 0 || c >= this.BOARD_SIZE ||
this.gameBoard[r][c] !== player) break;
winningSequence.push({row: r, col: c});
}
if (winningSequence.length >= 5) {
this.winningCells = winningSequence;
winningSequence.forEach(({row, col}) => {
const stone = this.cellElements[`${row}-${col}`].querySelector('.stone');
if (stone) stone.classList.add('winning');
});
break;
}
}
}
checkWin(row, col) {
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
const player = this.gameBoard[row][col];
return directions.some(([dx, dy]) => {
const count = 1 +
this.countDirection(row, col, dx, dy, player) +
this.countDirection(row, col, -dx, -dy, player);
return count >= 5;
});
}
countDirection(row, col, dx, dy, player) {
let count = 0;
for (let i = 1; i <= 4; i++) {
const r = row + i * dx;
const c = col + i * dy;
if (r < 0 || r >= this.BOARD_SIZE || c < 0 || c >= this.BOARD_SIZE ||
this.gameBoard[r][c] !== player) break;
count++;
}
return count;
}
checkDraw() {
return this.moveHistory.length >= this.BOARD_SIZE * this.BOARD_SIZE;
}
showWinAnimation(player) {
const playerName = player === this.PLAYERS.BLACK ? '黑方' :
(player === this.PLAYERS.WHITE && this.currentMode === this.GAME_MODES.PLAYER_VS_AI ? 'AI' : '白方');
this.winnerText.textContent = `${playerName}获胜!`;
this.winAnimation.classList.add('show');
}
switchPlayer() {
this.currentPlayer = this.currentPlayer === this.PLAYERS.BLACK ? this.PLAYERS.WHITE : this.PLAYERS.BLACK;
this.updateStatus();
}
updateStatus() {
let playerName = this.currentPlayer === this.PLAYERS.BLACK ? '黑方' :
(this.currentPlayer === this.PLAYERS.WHITE && this.currentMode === this.GAME_MODES.PLAYER_VS_AI ? 'AI' : '白方');
this.statusDisplay.textContent = `${playerName}回合`;
this.statusDisplay.className = `status ${this.currentPlayer}-turn`;
this.statusDisplay.setAttribute('aria-live', 'polite');
}
undoMove() {
if (this.moveHistory.length <= 0 || !this.gameActive || this.aiThinking) return;
this.winningCells.forEach(({row, col}) => {
const stone = this.cellElements[`${row}-${col}`].querySelector('.stone');
if (stone) stone.classList.remove('winning');
});
this.winningCells = [];
const stepsToUndo = this.currentMode === this.GAME_MODES.PLAYER_VS_AI ? 2 : 1;
let undone = 0;
while (this.moveHistory.length > 0 && undone < stepsToUndo) {
const lastMove = this.moveHistory.pop();
this.gameBoard[lastMove.row][lastMove.col] = null;
undone++;
}
if (this.moveHistory.length > 0) {
this.lastMove = this.moveHistory[this.moveHistory.length - 1];
this.currentPlayer = this.lastMove.player;
} else {
this.lastMove = null;
this.currentPlayer = this.PLAYERS.BLACK;
}
this.updateBoard();
this.updateStatus();
this.updateMoveHistory();
this.playSound('click');
}
showHint() {
if (!this.settings.showHints || !this.gameActive || this.aiThinking) return;
const bestMove = this.findBestMove();
if (bestMove) {
this.playSound('click');
const cell = this.cellElements[`${bestMove.row}-${bestMove.col}`];
cell.classList.add('hint');
setTimeout(() => cell.classList.remove('hint'), 2000);
}
}
aiMove() {
if (!this.gameActive || this.currentPlayer !== this.PLAYERS.WHITE) return;
this.aiThinking = true;
this.statusDisplay.textContent = 'AI思考中...';
setTimeout(() => {
if (!this.gameActive) {
this.aiThinking = false;
return;
}
const bestMove = this.minimax(this.aiDepth, -Infinity, Infinity, true);
if (bestMove && this.gameActive) {
this.playSound('place');
this.placeStone(bestMove.row, bestMove.col, this.PLAYERS.AI);
}
this.aiThinking = false;
}, this.aiDepth * 300);
}
minimax(depth, alpha, beta, isMaximizingPlayer) {
if (depth === 0) {
return { score: this.evaluateBoard() };
}
const possibleMoves = this.getPossibleMoves();
if (possibleMoves.length === 0) {
return { score: 0 };
}
let bestMove = null;
if (isMaximizingPlayer) {
let maxScore = -Infinity;
for (const move of possibleMoves) {
this.gameBoard[move.row][move.col] = this.PLAYERS.AI;
if (this.checkWin(move.row, move.col)) {
const score = this.PATTERNS.FIVE * (depth + 1);
this.gameBoard[move.row][move.col] = null;
return { ...move, score };
}
const result = this.minimax(depth - 1, alpha, beta, false);
const currentScore = result.score;
this.gameBoard[move.row][move.col] = null;
if (currentScore > maxScore) {
maxScore = currentScore;
bestMove = move;
}
alpha = Math.max(alpha, currentScore);
if (beta <= alpha) break;
}
return { ...bestMove, score: maxScore };
} else {
let minScore = Infinity;
for (const move of possibleMoves) {
this.gameBoard[move.row][move.col] = this.PLAYERS.BLACK;
if (this.checkWin(move.row, move.col)) {
const score = -this.PATTERNS.FIVE * (depth + 1);
this.gameBoard[move.row][move.col] = null;
return { ...move, score };
}
const result = this.minimax(depth - 1, alpha, beta, true);
const currentScore = result.score;
this.gameBoard[move.row][move.col] = null;
if (currentScore < minScore) {
minScore = currentScore;
bestMove = move;
}
beta = Math.min(beta, currentScore);
if (beta <= alpha) break;
}
return { ...bestMove, score: minScore };
}
}
getPossibleMoves() {
const moves = [];
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.gameBoard[i][j] === null && this.hasNearbyStones(i, j)) {
const score = this.evaluatePosition(i, j, this.PLAYERS.AI);
moves.push({ row: i, col: j, score });
}
}
}
if (moves.length === 0) {
const center = Math.floor(this.BOARD_SIZE / 2);
for (let i = center - 2; i <= center + 2; i++) {
for (let j = center - 2; j <= center + 2; j++) {
if (i >= 0 && i < this.BOARD_SIZE && j >= 0 && j < this.BOARD_SIZE && this.gameBoard[i][j] === null) {
moves.push({ row: i, col: j, score: 1 });
}
}
}
}
return moves.sort((a, b) => b.score - a.score);
}
hasNearbyStones(row, col, distance = 2) {
for (let i = Math.max(0, row - distance); i <= Math.min(this.BOARD_SIZE - 1, row + distance); i++) {
for (let j = Math.max(0, col - distance); j <= Math.min(this.BOARD_SIZE - 1, col + distance); j++) {
if (this.gameBoard[i][j] !== null) {
return true;
}
}
}
return false;
}
evaluateBoard() {
let score = 0;
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.gameBoard[i][j] === this.PLAYERS.AI) {
score += this.evaluatePosition(i, j, this.PLAYERS.AI);
} else if (this.gameBoard[i][j] === this.PLAYERS.BLACK) {
score -= this.evaluatePosition(i, j, this.PLAYERS.BLACK) * 0.9;
}
}
}
return score;
}
findBestMove() {
const possibleMoves = this.getPossibleMoves();
if (possibleMoves.length === 0) return null;
let bestScore = -Infinity;
let bestMove = null;
for (const move of possibleMoves) {
this.gameBoard[move.row][move.col] = this.currentPlayer;
const isWinningMove = this.checkWin(move.row, move.col);
this.gameBoard[move.row][move.col] = null;
if (isWinningMove) {
return move;
}
}
for (const move of possibleMoves) {
const score = this.evaluatePosition(move.row, move.col, this.currentPlayer);
if (score > bestScore) {
bestScore = score;
bestMove = move;
}
}
return bestMove;
}
evaluatePosition(row, col, player) {
const opponent = player === this.PLAYERS.BLACK ? this.PLAYERS.WHITE : this.PLAYERS.BLACK;
let score = 0;
score += this.evaluatePattern(row, col, player);
score += this.evaluatePattern(row, col, opponent) * 0.8;
const center = (this.BOARD_SIZE - 1) / 2;
const distanceToCenter = Math.sqrt(Math.pow(row - center, 2) + Math.pow(col - center, 2));
score += (this.BOARD_SIZE - distanceToCenter) * 2;
return score;
}
evaluatePattern(row, col, player) {
let totalScore = 0;
const directions = [[0, 1], [1, 0], [1, 1], [1, -1]];
directions.forEach(([dx, dy]) => {
const {type} = this.checkPattern(row, col, dx, dy, player);
totalScore += this.PATTERNS[type] || 0;
});
return totalScore;
}
checkPattern(row, col, dx, dy, player) {
let count = 1;
let openEnds = 0;
for (const [multiplier, isReverse] of [[1, false], [-1, true]]) {
let blocked = false;
for (let i = 1; i <= 4; i++) {
const r = row + i * dx * multiplier;
const c = col + i * dy * multiplier;
if (r < 0 || r >= this.BOARD_SIZE || c < 0 || c >= this.BOARD_SIZE) {
blocked = true;
break;
}
if (this.gameBoard[r][c] === player) {
count++;
} else if (this.gameBoard[r][c] === null) {
if (!isReverse) openEnds++;
break;
} else {
blocked = true;
break;
}
}
if (!blocked && isReverse) openEnds++;
}
let type = '';
if (count >= 5) type = 'FIVE';
else if (count === 4) {
type = openEnds >= 1 ? (openEnds === 2 ? 'OPEN_FOUR' : 'HALF_FOUR') : '';
} else if (count === 3) {
type = openEnds >= 1 ? (openEnds === 2 ? 'OPEN_THREE' : 'HALF_THREE') : '';
} else if (count === 2) {
type = openEnds >= 1 ? (openEnds === 2 ? 'OPEN_TWO' : 'HALF_TWO') : '';
} else if (count === 1) {
type = openEnds === 2 ? 'OPEN_ONE' : '';
}
return {type, count, openEnds};
}
updateScore() {
let blackCount = 0;
let whiteCount = 0;
for (let i = 0; i < this.BOARD_SIZE; i++) {
for (let j = 0; j < this.BOARD_SIZE; j++) {
if (this.gameBoard[i][j] === this.PLAYERS.BLACK) blackCount++;
else if (this.gameBoard[i][j] === this.PLAYERS.WHITE || this.gameBoard[i][j] === this.PLAYERS.AI) whiteCount++;
}
}
this.blackScore = blackCount;
this.whiteScore = whiteCount;
this.blackScoreEl.textContent = this.blackScore;
this.whiteScoreEl.textContent = this.whiteScore;
}
updateStatsDisplay() {
this.blackWinsEl.textContent = this.blackWins;
this.whiteWinsEl.textContent = this.whiteWins;
}
saveStats() {
try {
localStorage.setItem('gomoku_blackWins', this.blackWins.toString());
localStorage.setItem('gomoku_whiteWins', this.whiteWins.toString());
} catch (e) {
console.error('保存统计数据失败:', e);
}
}
updateMoveHistory() {
if (!this.showHistory) return;
this.moveHistoryEl.innerHTML = '';
if (this.moveHistory.length === 0) {
const emptyItem = document.createElement('div');
emptyItem.className = 'move-item';
emptyItem.innerHTML = `
<div class="move-number">-</div>
<div class="move-position">游戏尚未开始</div>
`;
this.moveHistoryEl.appendChild(emptyItem);
return;
}
this.moveHistory.forEach((move, index) => {
const moveNumber = Math.floor(index / 2) + 1;
const isBlackMove = move.player === this.PLAYERS.BLACK;
let moveItem;
if (index % 2 === 0) {
moveItem = document.createElement('div');
moveItem.className = 'move-item';
moveItem.innerHTML = `
<div class="move-number">${moveNumber}</div>
<div class="move-position ${isBlackMove ? 'black' : 'white'}">
${String.fromCharCode(65 + move.col)}${move.row + 1}
</div>
<div class="move-position white"></div>
`;
this.moveHistoryEl.appendChild(moveItem);
} else {
const lastItem = this.moveHistoryEl.lastChild;
if (lastItem) {
const whitePos = lastItem.querySelector('.move-position.white');
whitePos.textContent = `${String.fromCharCode(65 + move.col)}${move.row + 1}`;
}
}
});
this.moveHistoryEl.scrollTop = this.moveHistoryEl.scrollHeight;
}
openSaveModal() {
if (!this.gameActive) {
this.playSound('error');
return;
}
this.saveModal.classList.add('show');
this.gameNameInput.focus();
this.playSound('click');
}
closeSaveModal() {
this.saveModal.classList.remove('show');
this.playSound('click');
}
saveCurrentGame() {
const gameName = this.gameNameInput.value.trim() || '未命名游戏';
const timestamp = new Date().toISOString();
const formattedDate = new Date().toLocaleString();
const gameData = {
id: Date.now().toString(),
name: gameName,
date: formattedDate,
timestamp: timestamp,
board: JSON.parse(JSON.stringify(this.gameBoard)),
currentPlayer: this.currentPlayer,
moveHistory: JSON.parse(JSON.stringify(this.moveHistory)),
gameMode: this.currentMode,
difficulty: this.aiDepth
};
try {
let savedGames = JSON.parse(localStorage.getItem('gomoku_saved_games') || '[]');
savedGames.push(gameData);
localStorage.setItem('gomoku_saved_games', JSON.stringify(savedGames));
} catch (e) {
console.error('保存游戏失败:', e);
alert('保存游戏失败,请重试');
return;
}
this.closeSaveModal();
this.loadSavedGames();
this.playSound('click');
}
loadSavedGames() {
try {
const savedGames = JSON.parse(localStorage.getItem('gomoku_saved_games') || '[]');
this.savedGamesEl.innerHTML = '';
if (savedGames.length === 0) {
const noGamesEl = document.createElement('div');
noGamesEl.className = 'no-saved-games';
noGamesEl.textContent = '暂无保存的游戏';
this.savedGamesEl.appendChild(noGamesEl);
return;
}
savedGames.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
savedGames.forEach(game => {
const gameItem = document.createElement('div');
gameItem.className = 'saved-game-item';
gameItem.innerHTML = `
<div class="saved-game-info">
<div class="saved-game-name">${game.name}</div>
<div class="saved-game-date">${game.date}</div>
</div>
<div class="saved-game-controls">
<button class="load-game" data-id="${game.id}" title="加载游戏"><i class="fas fa-play"></i></button>
<button class="delete-game" data-id="${game.id}" title="删除游戏"><i class="fas fa-trash"></i></button>
</div>
`;
this.savedGamesEl.appendChild(gameItem);
});
document.querySelectorAll('.load-game').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
this.loadGame(e.target.closest('.load-game').dataset.id);
});
});
document.querySelectorAll('.delete-game').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
this.deleteGame(e.target.closest('.delete-game').dataset.id);
});
});
} catch (e) {
console.error('加载保存的游戏失败:', e);
const errorEl = document.createElement('div');
errorEl.className = 'error-message';
errorEl.textContent = '加载保存的游戏失败';
this.savedGamesEl.appendChild(errorEl);
}
}
// 加载指定游戏 - 修复了此处的代码不完整问题
loadGame(gameId) {
try {
const savedGames = JSON.parse(localStorage.getItem('gomoku_saved_games') || '[]');
const gameData = savedGames.find(game => game.id === gameId);
if (!gameData) return;
// 恢复游戏状态
this.gameBoard = JSON.parse(JSON.stringify(gameData.board));
this.currentPlayer = gameData.currentPlayer;
this.moveHistory = JSON.parse(JSON.stringify(gameData.moveHistory));
this.currentMode = gameData.gameMode;
this.aiDepth = gameData.difficulty;
this.gameActive = true;
this.aiThinking = false;
// 更新UI
this.renderBoard();
this.updateBoard();
this.updateStatus();
this.updateMoveHistory();
// 更新模式和难度按钮
this.modeButtons.forEach(btn => {
if (btn.dataset.mode === this.currentMode) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
this.difficultyButtons.forEach(btn => {
if (parseInt(btn.dataset.depth) === this.aiDepth) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
this.playSound('click');
} catch (e) {
console.error('加载游戏失败:', e);
alert('加载游戏失败,请重试');
}
}
// 删除指定游戏
deleteGame(gameId) {
if (!confirm('确定要删除这个保存的游戏吗?')) return;
try {
let savedGames = JSON.parse(localStorage.getItem('gomoku_saved_games') || '[]');
savedGames = savedGames.filter(game => game.id !== gameId);
localStorage.setItem('gomoku_saved_games', JSON.stringify(savedGames));
this.loadSavedGames();
this.playSound('click');
} catch (e) {
console.error('删除游戏失败:', e);
alert('删除游戏失败,请重试');
}
}
}
document.addEventListener('DOMContentLoaded', () => {
// 确保DOM完全加载后初始化游戏
try {
new GomokuGame();
} catch (e) {
console.error('初始化游戏失败:', e);
alert('游戏初始化失败,请刷新页面重试');
}
});
</script>
</body>
</html>
995

被折叠的 条评论
为什么被折叠?



