欢迎加入开源鸿蒙PC社区:
atomgit仓库地址: https://atomgit.com/m0_66062719/jingziqi




一、项目概述
井字棋是一款经典的双人对战游戏,本应用实现了完整的游戏逻辑,支持双人模式和人机对战模式。游戏使用Minimax算法实现AI对手,提供不可战胜的游戏体验。
1.1 功能定位
| 功能模块 | 核心能力 | 业务价值 |
|---|---|---|
| 游戏引擎 | 棋盘管理、胜负判断 | 核心游戏逻辑 |
| AI对手 | Minimax算法决策 | 单人游戏体验 |
| 计分系统 | 实时分数统计 | 竞技性激励 |
| 数据持久化 | 游戏数据保存 | 进度延续 |
1.2 技术目标
- 游戏完整性:完整实现井字棋规则
- AI智能:实现最优策略的AI对手
- 用户体验:流畅动画和直观交互
- 跨平台:一次开发,多端运行
二、技术架构设计
2.1 整体架构
┌─────────────────────────────────────────────────────────────┐
│ 表示层 (UI) │
│ 游戏棋盘、玩家信息、计分板、获胜弹窗 │
├─────────────────────────────────────────────────────────────┤
│ 业务逻辑层 │
│ TicTacToe类:游戏引擎、AI决策、胜负判断 │
├─────────────────────────────────────────────────────────────┤
│ 数据层 │
│ LocalStorage:分数统计、游戏记录 │
└─────────────────────────────────────────────────────────────┘
2.2 核心类设计
javascript
class TicTacToe {
constructor() {
this.board = Array(9).fill(null); // 3×3棋盘
this.currentPlayer = 'X'; // 当前玩家
this.gameActive = true; // 游戏状态
this.isVsAI = false; // 是否人机对战
this.winningCombinations = [/* 获胜组合 */];
}
}
三、核心代码实现
3.1 游戏引擎
javascript
makeMove(index) {
if (!this.gameActive || this.board[index] !== null) return;
this.board[index] = this.currentPlayer;
this.updateBoard();
if (this.checkWin()) {
this.handleWin();
} else if (this.checkDraw()) {
this.handleDraw();
} else {
this.switchPlayer();
if (this.isVsAI && this.currentPlayer === 'O' && this.gameActive) {
setTimeout(() => this.aiMove(), 500);
}
}
}
游戏流程:
- 验证移动合法性
- 更新棋盘状态
- 检查胜负
- 切换玩家或触发AI
3.2 胜负判断
javascript
winningCombinations = [
[0, 1, 2], [3, 4, 5], [6, 7, 8], // 横向
[0, 3, 6], [1, 4, 7], [2, 5, 8], // 纵向
[0, 4, 8], [2, 4, 6] // 对角线
];
checkWin() {
for (const combo of this.winningCombinations) {
const [a, b, c] = combo;
if (this.board[a] && this.board[a] === this.board[b] && this.board[a] === this.board[c]) {
this.winningCells = combo;
return true;
}
}
return false;
}
判断逻辑:
- 遍历8种获胜组合
- 检查三个位置是否为同一玩家
- 记录获胜格子用于高亮显示
3.3 Minimax算法
javascript
minimax(board, depth, isMaximizing) {
const scores = {
X: -10 + depth,
O: 10 - depth,
draw: 0
};
const result = this.checkResult(board);
if (result !== null) {
return scores[result];
}
if (isMaximizing) {
let bestScore = -Infinity;
for (let i = 0; i < 9; i++) {
if (board[i] === null) {
board[i] = 'O';
const score = this.minimax(board, depth + 1, false);
board[i] = null;
bestScore = Math.max(score, bestScore);
}
}
return bestScore;
} else {
let bestScore = Infinity;
for (let i = 0; i < 9; i++) {
if (board[i] === null) {
board[i] = 'X';
const score = this.minimax(board, depth + 1, true);
board[i] = null;
bestScore = Math.min(score, bestScore);
}
}
return bestScore;
}
}
算法原理:
- 递归遍历所有可能的移动
- Maximizing玩家(AI)选择最高分
- Minimizing玩家(人类)选择最低分
- 深度加权:更快获胜/更晚失败得分更高
3.4 AI决策
javascript
getBestMove() {
let bestScore = -Infinity;
let bestMove = -1;
for (let i = 0; i < 9; i++) {
if (this.board[i] === null) {
this.board[i] = 'O';
const score = this.minimax(this.board, 0, false);
this.board[i] = null;
if (score > bestScore) {
bestScore = score;
bestMove = i;
}
}
}
return bestMove;
}
决策流程:
- 遍历所有空位
- 模拟AI落子
- 使用Minimax评估得分
- 选择最高分的位置
四、界面设计
4.1 深色游戏主题
css
body {
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
}
.header h1 {
background: linear-gradient(135deg, #e94560 0%, #ff6b6b 50%, #ffa502 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
色彩方案:
- 深蓝紫色背景营造游戏氛围
- 渐变色标题吸引注意力
- 红色X和橙色O区分玩家
4.2 棋子动画
css
.cell.X {
color: #e94560;
animation: xPop 0.3s ease-out;
}
.cell.O {
color: #ffa502;
animation: oPop 0.3s ease-out;
}
@keyframes xPop {
0% { transform: scale(0); opacity: 0; }
50% { transform: scale(1.2); }
100% { transform: scale(1); opacity: 1; }
}
@keyframes oPop {
0% { transform: scale(0) rotate(0deg); }
50% { transform: scale(1.2) rotate(180deg); }
100% { transform: scale(1) rotate(360deg); }
}
动画效果:
- X棋子:缩放弹出
- O棋子:旋转弹出
- 增强交互反馈
4.3 获胜效果
css
.cell.win {
background: rgba(233, 69, 96, 0.3);
animation: winPulse 0.5s ease-in-out infinite;
}
@keyframes winPulse {
0%, 100% { box-shadow: 0 0 20px rgba(233, 69, 96, 0.4); }
50% { box-shadow: 0 0 40px rgba(233, 69, 96, 0.8); }
}
五、数据持久化
5.1 LocalStorage使用
javascript
loadStats() {
const saved = localStorage.getItem('ticTacToeStats');
if (saved) {
const data = JSON.parse(saved);
this.scores = data.scores || this.scores;
this.stats = data.stats || this.stats;
}
}
saveStats() {
const data = {
scores: this.scores,
stats: this.stats
};
localStorage.setItem('ticTacToeStats', JSON.stringify(data));
}
存储数据结构:
javascript
{
scores: { X: 5, O: 3 },
stats: {
totalGames: 10,
drawGames: 2,
currentStreak: 3,
lastWinner: 'X'
}
}
六、功能模块详解
6.1 模式切换
javascript
toggleMode() {
this.isVsAI = !this.isVsAI;
const btn = document.getElementById('toggleModeBtn');
btn.textContent = this.isVsAI ? '🤖 人机对战' : '👤 双人模式';
if (this.isVsAI) {
document.getElementById('playerO').innerHTML = `
<span class="player-icon">🤖</span>
<span class="player-name">AI对手</span>
<span class="player-score">${this.scores.O}</span>
`;
} else {
document.getElementById('playerO').innerHTML = `
<span class="player-icon">⭕</span>
<span class="player-name">玩家O</span>
<span class="player-score">${this.scores.O}</span>
`;
}
this.newRound();
}
6.2 游戏统计
javascript
handleWin() {
this.gameActive = false;
this.scores[this.currentPlayer]++;
this.stats.totalGames++;
if (this.stats.lastWinner === this.currentPlayer) {
this.stats.currentStreak++;
} else {
this.stats.currentStreak = 1;
}
this.stats.lastWinner = this.currentPlayer;
this.highlightWinningCells();
this.saveStats();
}
6.3 获胜弹窗
javascript
showWinOverlay(player) {
document.getElementById('winIcon').textContent = '🎉';
document.getElementById('winText').textContent = `玩家${player} 获胜!`;
document.getElementById('winOverlay').classList.add('show');
}
七、Minimax算法详解
7.1 算法原理
Minimax是一种零和博弈算法,用于在完全信息游戏中找到最优策略。
当前状态
/ | \
/ | \
A B C ← Maximizing层 (AI选择)
/|\ |
D E F G ← Minimizing层 (人类选择)
... ...
7.2 评估函数
javascript
const scores = {
X: -10 + depth, // 人类获胜,深度越小惩罚越大
O: 10 - depth, // AI获胜,深度越小奖励越大
draw: 0 // 平局
};
深度加权原因:
- 更快获胜更好(AI优先选择快速获胜的路径)
- 更晚失败更好(AI优先选择延迟失败的路径)
7.3 状态空间
井字棋的状态空间相对较小:
- 最多9步
- 约5478种有效状态
- 完全搜索可行
八、扩展功能建议
8.1 难度调整
javascript
// 限制搜索深度实现难度调整
minimax(board, depth, isMaximizing, maxDepth = 9) {
if (depth >= maxDepth) {
return this.evaluateBoard(board);
}
// ...
}
8.2 历史记录
javascript
// 记录所有游戏步骤
class GameHistory {
constructor() {
this.history = [];
}
addMove(player, position) {
this.history.push({ player, position, timestamp: Date.now() });
}
replay() {
// 重新播放游戏
}
}
8.3 在线对战
javascript
// WebSocket实现在线对战
const socket = new WebSocket('wss://game-server.com');
socket.onmessage = (event) => {
const move = JSON.parse(event.data);
app.makeMove(move.position);
};
九、总结
9.1 技术成果
成功实现了一个完整的井字棋游戏,包含:
- 游戏引擎:完整的3×3棋盘管理
- Minimax AI:不可战胜的AI对手
- 双人模式:本地双人对战
- 计分系统:实时分数和统计
- 数据持久化:LocalStorage保存
- 动画效果:流畅的视觉反馈
9.2 技术价值
- 算法学习:实践Minimax博弈算法
- 游戏设计:完整的游戏状态管理
- 用户体验:流畅的交互和动画
9.3 未来展望
后续可扩展功能:
- 难度级别调整
- 游戏历史记录
- 在线多人对战
- 移动端优化