欢迎加入开源鸿蒙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

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐