欢迎加入开源鸿蒙PC社区:
https://harmonypc.csdn.net/

atomgit仓库地址: https://atomgit.com/m0_66062719/jingziqi
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、项目概述

井字棋是一款经典的双人对战游戏,本应用实现了完整的游戏逻辑,支持双人模式和人机对战模式。游戏使用Minimax算法实现AI对手,提供不可战胜的游戏体验。

1.1 功能定位

功能模块 核心能力 业务价值
游戏引擎 棋盘管理、胜负判断 核心游戏逻辑
AI对手 Minimax算法决策 单人游戏体验
计分系统 实时分数统计 竞技性激励
数据持久化 游戏数据保存 进度延续

1.2 技术目标

  • 游戏完整性:完整实现井字棋规则
    • 核心规则:严格遵循3×3棋盘、X和O轮流落子、先连成一线(横、竖、斜)者获胜、棋盘填满无胜者则为平局等经典规则。
    • 状态管理:实时跟踪棋盘状态、当前玩家、游戏是否活跃、获胜组合等,确保逻辑严密无漏洞。
    • 边界处理:对非法落子(如重复点击、游戏结束后点击)进行拦截和友好提示,保证游戏流程的健壮性。
  • AI智能:实现最优策略的AI对手
    • 算法核心:采用经典的Minimax算法,通过递归模拟所有可能的游戏路径,为AI选择最优落子点。
    • 不可战胜性:在井字棋的完全信息状态下,该算法能保证AI至少不败(最优结果为平局),为玩家提供极具挑战性的对手。
    • 性能优化:虽然井字棋状态空间有限,但算法实现中通过深度加权(depth)来优先选择更快获胜或更晚失败的路径,体现了算法优化的思想。
  • 用户体验:流畅动画和直观交互
    • 视觉反馈:为棋子落子(X的缩放弹出、O的旋转弹出)、获胜连线(脉动高光)设计了细腻的CSS动画,增强操作的正反馈。
    • 界面设计:采用深色渐变主题,营造沉浸式游戏氛围;使用清晰的图标和色彩(红色X、橙色O)区分玩家,信息一目了然。
    • 交互逻辑:提供一键模式切换、实时计分板、获胜弹窗等,操作流程简洁直观,降低学习成本。
  • 跨平台:一次开发,多端运行
    • 技术栈选择:基于纯HTML、CSS和JavaScript(Vanilla JS)开发,不依赖任何第三方框架或库,确保了极佳的兼容性。
    • 响应式设计:界面布局采用Flexbox等现代CSS技术,能自适应不同尺寸的屏幕,在PC、平板和手机端均可获得良好体验。
    • 部署简便:项目为静态文件,可轻松部署至任何Web服务器或GitHub Pages等托管平台,实现真正的“编写一次,处处运行”。

二、技术架构设计

2.1 整体架构

┌─────────────────────────────────────────────────────────────┐
│                    表示层 (UI)                            │
│  游戏棋盘、玩家信息、计分板、获胜弹窗                      │
├─────────────────────────────────────────────────────────────┤
│                    业务逻辑层                              │
│  TicTacToe类:游戏引擎、AI决策、胜负判断                 │
├─────────────────────────────────────────────────────────────┤
│                    数据层                                  │
│   LocalStorage:分数统计、游戏记录                       │
└─────────────────────────────────────────────────────────────┘

2.2 核心类设计

class TicTacToe {
    constructor() {
        this.board = Array(9).fill(null);  // 3×3棋盘
        this.currentPlayer = 'X';           // 当前玩家
        this.gameActive = true;            // 游戏状态
        this.isVsAI = false;               // 是否人机对战
        this.winningCombinations = [/* 获胜组合 */];
    }
}

三、核心代码实现

3.1 游戏引擎

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);
        }
    }
}

游戏流程

  1. 验证移动合法性
  2. 更新棋盘状态
  3. 检查胜负
  4. 切换玩家或触发AI

3.2 胜负判断

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算法

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决策

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;
}

决策流程

  1. 遍历所有空位
  2. 模拟AI落子
  3. 使用Minimax评估得分
  4. 选择最高分的位置

四、界面设计

4.1 深色游戏主题

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 棋子动画

.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 获胜效果

.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使用

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));
}

存储数据结构

{
    scores: { X: 5, O: 3 },
    stats: {
        totalGames: 10,
        drawGames: 2,
        currentStreak: 3,
        lastWinner: 'X'
    }
}

六、功能模块详解

6.1 模式切换

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 游戏统计

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 获胜弹窗

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 评估函数

const scores = {
    X: -10 + depth,   // 人类获胜,深度越小惩罚越大
    O: 10 - depth,    // AI获胜,深度越小奖励越大
    draw: 0           // 平局
};

深度加权原因

  • 更快获胜更好(AI优先选择快速获胜的路径)
  • 更晚失败更好(AI优先选择延迟失败的路径)

7.3 状态空间

井字棋的状态空间相对较小:

  • 最多9步
  • 约5478种有效状态
  • 完全搜索可行

八、扩展功能建议

8.1 难度调整

// 限制搜索深度实现难度调整
minimax(board, depth, isMaximizing, maxDepth = 9) {
    if (depth >= maxDepth) {
        return this.evaluateBoard(board);
    }
    // ...
}

8.2 历史记录

// 记录所有游戏步骤
class GameHistory {
    constructor() {
        this.history = [];
    }
    
    addMove(player, position) {
        this.history.push({ player, position, timestamp: Date.now() });
    }
    
    replay() {
        // 重新播放游戏
    }
}

8.3 在线对战

// WebSocket实现在线对战
const socket = new WebSocket('wss://game-server.com');

socket.onmessage = (event) => {
    const move = JSON.parse(event.data);
    app.makeMove(move.position);
};

九、总结

9.1 技术成果

成功实现了一个完整的井字棋游戏,包含:

  1. 游戏引擎:完整的3×3棋盘管理
  2. Minimax AI:不可战胜的AI对手
  3. 双人模式:本地双人对战
  4. 计分系统:实时分数和统计
  5. 数据持久化:LocalStorage保存
  6. 动画效果:流畅的视觉反馈

9.2 技术价值

  • 算法学习:实践Minimax博弈算法
  • 游戏设计:完整的游戏状态管理
  • 用户体验:流畅的交互和动画

9.3 未来展望

后续可扩展功能:

  • 难度级别调整
  • 游戏历史记录
  • 在线多人对战
  • 移动端优化

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐