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

开源atomgit仓库地址:
https://atomgit.com/feng8403000/wuziqi

演示效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

项目背景

五子棋是一种古老而经典的策略棋类游戏,深受人们喜爱。在现代数字化时代,将传统游戏搬到电脑平台上,不仅可以保留游戏的乐趣,还可以通过AI技术提供不同难度的挑战,让玩家随时随地享受游戏的乐趣。

本文将详细介绍如何使用Electron和HTML5 Canvas开发一款PC版五子棋游戏,包括游戏界面设计、核心逻辑实现、AI算法开发以及不同难度等级的实现。

技术栈选择

前端技术

  • HTML5 Canvas:用于绘制棋盘和棋子,提供流畅的游戏视觉体验
  • JavaScript:实现游戏逻辑和AI算法
  • CSS:设计美观的游戏界面

后端技术

  • Electron:构建跨平台桌面应用,提供原生桌面体验

技术优势

  • 跨平台:Electron可以在Windows、macOS和Linux上运行
  • 性能优异:Canvas绘制提供高效的图形渲染
  • 开发效率高:使用熟悉的Web技术栈,开发周期短
  • 用户体验好:桌面应用提供更沉浸式的游戏体验

游戏功能介绍

核心功能

  1. 15x15标准棋盘:符合五子棋标准规则的棋盘大小
  2. 三种难度等级
    • 简单:电脑随机下棋
    • 中等:电脑会攻击和防守
    • 困难:使用Minimax算法,具有较高的智能
  3. 玩家与电脑对战:玩家执黑棋,电脑执白棋
  4. 胜负判断:自动检测五子连珠,判断游戏结果
  5. 游戏状态显示:实时显示当前游戏状态和轮到谁下棋
  6. 游戏控制:开始游戏、重新开始功能

界面设计

  • 简洁美观:采用现代化的UI设计,界面清爽整洁
  • 响应式布局:适配不同窗口大小
  • 视觉反馈:下棋时有明显的视觉反馈
  • 游戏结束模态框:游戏结束时显示结果

核心代码分析

1. 棋盘绘制

// 绘制棋盘
function drawBoard() {
    // 清空画布
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // 绘制棋盘网格
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 1;
    
    for (let i = 0; i < BOARD_SIZE; i++) {
        // 绘制垂直线
        ctx.beginPath();
        ctx.moveTo(i * CELL_SIZE + CELL_SIZE / 2, CELL_SIZE / 2);
        ctx.lineTo(i * CELL_SIZE + CELL_SIZE / 2, (BOARD_SIZE - 1) * CELL_SIZE + CELL_SIZE / 2);
        ctx.stroke();
        
        // 绘制水平线
        ctx.beginPath();
        ctx.moveTo(CELL_SIZE / 2, i * CELL_SIZE + CELL_SIZE / 2);
        ctx.lineTo((BOARD_SIZE - 1) * CELL_SIZE + CELL_SIZE / 2, i * CELL_SIZE + CELL_SIZE / 2);
        ctx.stroke();
    }
    
    // 绘制棋子
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] === PLAYER) {
                drawStone(i, j, '#000'); // 黑棋
            } else if (board[i][j] === AI) {
                drawStone(i, j, '#fff'); // 白棋
            }
        }
    }
}

代码解析

  • 使用Canvas API绘制15x15的棋盘网格
  • 绘制垂直线和水平线,形成棋盘格子
  • 根据棋盘数组状态绘制黑白棋子
  • 每次棋盘状态变化时调用此函数重绘

2. 胜负判断

// 检查是否获胜
function checkWin(x, y, player) {
    // 检查水平方向
    let count = 0;
    for (let i = Math.max(0, x - 4); i <= Math.min(BOARD_SIZE - 1, x + 4); i++) {
        if (board[i][y] === player) {
            count++;
            if (count === 5) return true;
        } else {
            count = 0;
        }
    }
    
    // 检查垂直方向
    count = 0;
    for (let j = Math.max(0, y - 4); j <= Math.min(BOARD_SIZE - 1, y + 4); j++) {
        if (board[x][j] === player) {
            count++;
            if (count === 5) return true;
        } else {
            count = 0;
        }
    }
    
    // 检查对角线方向(左上到右下)
    count = 0;
    let i = x - 4;
    let j = y - 4;
    while (i <= x + 4 && j <= y + 4) {
        if (i >= 0 && i < BOARD_SIZE && j >= 0 && j < BOARD_SIZE) {
            if (board[i][j] === player) {
                count++;
                if (count === 5) return true;
            } else {
                count = 0;
            }
        }
        i++;
        j++;
    }
    
    // 检查对角线方向(右上到左下)
    count = 0;
    i = x + 4;
    j = y - 4;
    while (i >= x - 4 && j <= y + 4) {
        if (i >= 0 && i < BOARD_SIZE && j >= 0 && j < BOARD_SIZE) {
            if (board[i][j] === player) {
                count++;
                if (count === 5) return true;
            } else {
                count = 0;
            }
        }
        i--;
        j++;
    }
    
    return false;
}

代码解析

  • 检查四个方向:水平、垂直、两个对角线
  • 每个方向检查以当前落子为中心的9个格子(前后各4个)
  • 连续5个相同颜色的棋子则判定为获胜
  • 边界处理:确保不会检查棋盘外的格子

3. AI算法实现

简单难度:随机下棋
// 简单难度:随机下棋
function getEasyMove() {
    const emptyCells = [];
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] === EMPTY) {
                emptyCells.push({ x: i, y: j });
            }
        }
    }
    
    if (emptyCells.length > 0) {
        const randomIndex = Math.floor(Math.random() * emptyCells.length);
        return emptyCells[randomIndex];
    }
    return null;
}
中等难度:攻击-防守策略
// 中等难度:优先攻击,其次防守
function getMediumMove() {
    // 检查是否有获胜的一步
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] === EMPTY) {
                board[i][j] = AI;
                if (checkWin(i, j, AI)) {
                    board[i][j] = EMPTY;
                    return { x: i, y: j };
                }
                board[i][j] = EMPTY;
            }
        }
    }
    
    // 检查是否需要防守
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] === EMPTY) {
                board[i][j] = PLAYER;
                if (checkWin(i, j, PLAYER)) {
                    board[i][j] = EMPTY;
                    return { x: i, y: j };
                }
                board[i][j] = EMPTY;
            }
        }
    }
    
    // 否则随机下棋
    return getEasyMove();
}
困难难度:Minimax算法
// 困难难度:基于评分系统
function getHardMove() {
    let bestScore = -Infinity;
    let bestMove = null;
    
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] === EMPTY) {
                board[i][j] = AI;
                let score = minimax(board, 3, -Infinity, Infinity, false);
                board[i][j] = EMPTY;
                
                if (score > bestScore) {
                    bestScore = score;
                    bestMove = { x: i, y: j };
                }
            }
        }
    }
    
    return bestMove || getMediumMove();
}

// minimax算法
function minimax(board, depth, alpha, beta, isMaximizing) {
    // 检查是否有获胜者
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] === AI && checkWin(i, j, AI)) {
                return 1000;
            }
            if (board[i][j] === PLAYER && checkWin(i, j, PLAYER)) {
                return -1000;
            }
        }
    }
    
    // 检查是否平局
    if (isBoardFull() || depth === 0) {
        return evaluateBoard(board);
    }
    
    if (isMaximizing) {
        let maxScore = -Infinity;
        for (let i = 0; i < BOARD_SIZE; i++) {
            for (let j = 0; j < BOARD_SIZE; j++) {
                if (board[i][j] === EMPTY) {
                    board[i][j] = AI;
                    let score = minimax(board, depth - 1, alpha, beta, false);
                    board[i][j] = EMPTY;
                    maxScore = Math.max(maxScore, score);
                    alpha = Math.max(alpha, score);
                    if (beta <= alpha) {
                        break;
                    }
                }
            }
        }
        return maxScore;
    } else {
        let minScore = Infinity;
        for (let i = 0; i < BOARD_SIZE; i++) {
            for (let j = 0; j < BOARD_SIZE; j++) {
                if (board[i][j] === EMPTY) {
                    board[i][j] = PLAYER;
                    let score = minimax(board, depth - 1, alpha, beta, true);
                    board[i][j] = EMPTY;
                    minScore = Math.min(minScore, score);
                    beta = Math.min(beta, score);
                    if (beta <= alpha) {
                        break;
                    }
                }
            }
        }
        return minScore;
    }
}

代码解析

  • 简单难度:随机选择空位置下棋,适合初学者
  • 中等难度:优先检查自己是否有获胜机会,其次检查玩家是否有获胜机会进行防守
  • 困难难度:使用Minimax算法,考虑未来几步的走法,通过评分系统评估棋盘状态
  • Alpha-Beta剪枝:优化Minimax算法,减少计算量

4. 棋盘评估函数

// 评估棋盘
function evaluateBoard(board) {
    let score = 0;
    
    // 评估电脑的棋型
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] === AI) {
                score += evaluatePosition(i, j, AI);
            } else if (board[i][j] === PLAYER) {
                score -= evaluatePosition(i, j, PLAYER);
            }
        }
    }
    
    return score;
}

// 评估位置
function evaluatePosition(x, y, player) {
    let score = 0;
    const directions = [
        [1, 0],  // 水平
        [0, 1],  // 垂直
        [1, 1],  // 对角线
        [1, -1]  // 反对角线
    ];
    
    for (let [dx, dy] of directions) {
        let count = 1;
        let empty = 0;
        
        // 正向
        let i = x + dx;
        let j = y + dy;
        while (i >= 0 && i < BOARD_SIZE && j >= 0 && j < BOARD_SIZE && board[i][j] === player) {
            count++;
            i += dx;
            j += dy;
        }
        if (i >= 0 && i < BOARD_SIZE && j >= 0 && j < BOARD_SIZE && board[i][j] === EMPTY) {
            empty++;
        }
        
        // 反向
        i = x - dx;
        j = y - dy;
        while (i >= 0 && i < BOARD_SIZE && j >= 0 && j < BOARD_SIZE && board[i][j] === player) {
            count++;
            i -= dx;
            j -= dy;
        }
        if (i >= 0 && i < BOARD_SIZE && j >= 0 && j < BOARD_SIZE && board[i][j] === EMPTY) {
            empty++;
        }
        
        // 根据连续棋子数和空位评估
        if (count === 5) {
            score += 1000;
        } else if (count === 4 && empty >= 1) {
            score += 100;
        } else if (count === 3 && empty >= 2) {
            score += 10;
        } else if (count === 2 && empty >= 2) {
            score += 1;
        }
    }
    
    return score;
}

代码解析

  • 评估每个位置的棋型价值
  • 考虑四个方向的连续棋子数
  • 考虑棋子两端的空位情况
  • 根据不同的棋型给予不同的分数
  • 五连珠:1000分
  • 活四(两端有空位):100分
  • 活三(两端有空位):10分
  • 活二(两端有空位):1分

开发过程中的挑战与解决方案

1. Canvas绘制性能优化

挑战:频繁重绘棋盘可能导致性能问题

解决方案

  • 只在必要时重绘棋盘
  • 使用requestAnimationFrame优化动画效果
  • 避免不必要的计算和绘制操作

2. AI算法效率

挑战:Minimax算法在大棋盘上计算量巨大

解决方案

  • 使用Alpha-Beta剪枝减少搜索空间
  • 限制搜索深度(设置为3层)
  • 优化评估函数,减少计算复杂度

3. 游戏状态管理

挑战:确保游戏状态的一致性和正确性

解决方案

  • 使用清晰的状态变量管理游戏状态
  • 实现完整的游戏流程控制
  • 添加适当的错误处理

4. 用户界面设计

挑战:创建美观且用户友好的界面

解决方案

  • 使用现代化的CSS设计
  • 实现响应式布局
  • 添加适当的视觉反馈

项目结构

electron-openharmony-vue3/
├── ohos_hap/
│   └── web_engine/
│       └── src/
│           └── main/
│               └── resources/
│                   └── resfile/
│                       └── resources/
│                           └── app/
│                               ├── index.html      # 五子棋游戏主页面
│                               ├── main.js         # Electron主进程
│                               └── preload.js      # 预加载脚本
└── docs/
    └── GOMOKU_GAME_BLOG.md  # 项目博客

未来改进方向

  1. 多人对战:添加网络对战功能,支持玩家之间在线对战
  2. 游戏记录:实现游戏回放和历史记录功能
  3. 自定义棋盘:允许用户自定义棋盘大小和规则
  4. 音效系统:添加游戏音效,增强游戏体验
  5. 皮肤系统:允许用户选择不同的棋盘和棋子皮肤
  6. 难度调整:提供更精细的难度调整选项
  7. 移动端适配:优化移动端触摸操作体验
  8. AI训练:使用机器学习技术提升AI水平

总结

通过本项目,我们成功开发了一款功能完整、界面美观的PC版五子棋游戏。游戏采用Electron和HTML5 Canvas技术栈,实现了以下核心功能:

  • 15x15标准棋盘
  • 三种难度等级(简单、中等、困难)
  • 玩家与电脑对战
  • 自动胜负判断
  • 美观的用户界面

项目中使用的技术和算法包括:

  • Canvas绘制技术
  • 游戏状态管理
  • 胜负判断算法
  • 不同难度的AI实现(随机算法、攻击-防守算法、Minimax算法)
  • Alpha-Beta剪枝优化
  • 棋盘评估系统

这款五子棋游戏不仅提供了娱乐功能,也是学习Electron和Canvas开发的良好示例。通过不断改进和扩展,可以进一步提升游戏的功能和用户体验。

如何运行

  1. 克隆项目到本地
  2. 进入项目目录
  3. 运行Electron应用
  4. 选择难度等级
  5. 点击"开始游戏"按钮开始游戏
  6. 点击棋盘上的位置进行下棋
  7. 游戏结束后,点击"确定"关闭结果模态框
  8. 点击"重新开始"按钮可以重新开始游戏

技术栈总结

技术用途版本
Electron桌面应用框架最新版
HTML5 Canvas绘制棋盘和棋子HTML5
JavaScript游戏逻辑和AI算法ES6+
CSS界面设计CSS3

通过本项目的开发,我们展示了如何使用现代Web技术构建一个功能完整的桌面游戏应用,希望对开发者有所启发和帮助。

Logo

赋能鸿蒙PC开发者,共建全场景原生生态,共享一次开发多端部署创新价值。

更多推荐