导读:本期聚焦于小伙伴创作的《HTML5 Canvas实现五子棋游戏完整教程:从棋盘绘制到胜负判断的JavaScript核心逻辑》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《HTML5 Canvas实现五子棋游戏完整教程:从棋盘绘制到胜负判断的JavaScript核心逻辑》有用,将其分享出去将是对创作者最好的鼓励。

HTML实现五子棋游戏及胜负判断逻辑详解

五子棋是一款经典的双人对弈棋类游戏,使用HTML、CSS和JavaScript即可实现完整的网页版五子棋。本文将逐步讲解页面结构搭建、棋盘绘制、落子逻辑以及核心的胜负判断算法实现。

一、页面基础结构搭建

首先我们需要搭建五子棋游戏的HTML基础结构,包含棋盘容器、状态提示区域和控制按钮。注意正文中提到的HTML标签需要转义,例如画布使用<canvas>标签实现:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>网页版五子棋</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h3>五子棋对战</h3>
        <div class="status" id="status">当前轮到:黑子落子</div>
        <div class="board-wrapper">
            <canvas id="chessBoard" width="600" height="600"></canvas>
        </div>
        <div class="controls">
            <button id="restartBtn">重新开始</button>
            <button id="undoBtn">悔棋</button>
        </div>
    </div>
    <script src="chess.js"></script>
</body>
</html>

二、棋盘与落子逻辑实现

我们使用<canvas>标签绘制15x15的标准五子棋棋盘,通过监听鼠标点击事件获取落子位置,同时记录每一步的落子信息用于后续胜负判断和悔棋功能。

以下是核心JavaScript逻辑的代码实现:

// 初始化棋盘和状态
const canvas = document.getElementById('chessBoard');
const ctx = canvas.getContext('2d');
const statusEl = document.getElementById('status');
const restartBtn = document.getElementById('restartBtn');
const undoBtn = document.getElementById('undoBtn');

// 棋盘配置
const BOARD_SIZE = 15; // 15x15棋盘
const CELL_SIZE = 40; // 每个格子大小
const PIECE_RADIUS = 18; // 棋子半径
let board = []; // 棋盘状态数组,0为空,1为黑子,2为白子
let currentPlayer = 1; // 当前玩家,1黑2白
let history = []; // 落子历史记录,用于悔棋

// 初始化棋盘状态
function initBoard() {
    board = [];
    for (let i = 0; i < BOARD_SIZE; i++) {
        board[i] = [];
        for (let j = 0; j < BOARD_SIZE; j++) {
            board[i][j] = 0;
        }
    }
    history = [];
    currentPlayer = 1;
    statusEl.textContent = '当前轮到:黑子落子';
    drawBoard();
}

// 绘制棋盘
function drawBoard() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    // 绘制横线
    for (let i = 0; i < BOARD_SIZE; i++) {
        ctx.beginPath();
        ctx.moveTo(CELL_SIZE, CELL_SIZE + i * CELL_SIZE);
        ctx.lineTo(CELL_SIZE + (BOARD_SIZE - 1) * CELL_SIZE, CELL_SIZE + i * CELL_SIZE);
        ctx.stroke();
    }
    // 绘制竖线
    for (let i = 0; i < BOARD_SIZE; i++) {
        ctx.beginPath();
        ctx.moveTo(CELL_SIZE + i * CELL_SIZE, CELL_SIZE);
        ctx.lineTo(CELL_SIZE + i * CELL_SIZE, CELL_SIZE + (BOARD_SIZE - 1) * CELL_SIZE);
        ctx.stroke();
    }
    // 绘制已有棋子
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (board[i][j] !== 0) {
                drawPiece(i, j, board[i][j]);
            }
        }
    }
}

// 绘制单个棋子
function drawPiece(x, y, player) {
    ctx.beginPath();
    ctx.arc(
        CELL_SIZE + x * CELL_SIZE,
        CELL_SIZE + y * CELL_SIZE,
        PIECE_RADIUS,
        0,
        Math.PI * 2
    );
    ctx.closePath();
    // 设置棋子颜色
    if (player === 1) {
        ctx.fillStyle = '#000000';
    } else {
        ctx.fillStyle = '#ffffff';
        ctx.strokeStyle = '#000000';
        ctx.stroke();
    }
    ctx.fill();
}

// 处理落子事件
canvas.addEventListener('click', function(e) {
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    // 计算点击的棋盘坐标
    const boardX = Math.round((x - CELL_SIZE) / CELL_SIZE);
    const boardY = Math.round((y - CELL_SIZE) / CELL_SIZE);
    // 校验点击位置是否有效
    if (boardX < 0 || boardX >= BOARD_SIZE || boardY < 0 || boardY >= BOARD_SIZE) return;
    if (board[boardX][boardY] !== 0) return;
    // 落子
    board[boardX][boardY] = currentPlayer;
    history.push({x: boardX, y: boardY, player: currentPlayer});
    drawPiece(boardX, boardY, currentPlayer);
    // 判断胜负
    if (checkWin(boardX, boardY, currentPlayer)) {
        statusEl.textContent = currentPlayer === 1 ? '黑子获胜!' : '白子获胜!';
        canvas.removeEventListener('click', arguments.callee);
        return;
    }
    // 切换玩家
    currentPlayer = currentPlayer === 1 ? 2 : 1;
    statusEl.textContent = currentPlayer === 1 ? '当前轮到:黑子落子' : '当前轮到:白子落子';
});

// 重新开始
restartBtn.addEventListener('click', initBoard);

// 悔棋
undoBtn.addEventListener('click', function() {
    if (history.length === 0) return;
    const lastStep = history.pop();
    board[lastStep.x][lastStep.y] = 0;
    currentPlayer = lastStep.player;
    statusEl.textContent = currentPlayer === 1 ? '当前轮到:黑子落子' : '当前轮到:白子落子';
    drawBoard();
});

// 初始化游戏
initBoard();

三、五子棋胜负判断算法

五子棋的胜负判断核心是:从当前落子的位置出发,向四个方向(水平、垂直、左上到右下、右上到左下)分别统计连续相同棋子的数量,如果任意方向连续数量达到5个,则当前落子方获胜。

以下是完整的胜负判断函数实现:

/**
 * 检查当前落子是否获胜
 * @param {number} x - 落子横坐标
 * @param {number} y - 落子纵坐标
 * @param {number} player - 落子玩家,1黑2白
 * @returns {boolean} 是否获胜
 */
function checkWin(x, y, player) {
    // 四个方向的偏移量:水平、垂直、左上到右下、右上到左下
    const directions = [
        [[1, 0], [-1, 0]],  // 水平
        [[0, 1], [0, -1]],  // 垂直
        [[1, 1], [-1, -1]], // 左上到右下
        [[1, -1], [-1, 1]]  // 右上到左下
    ];
    // 遍历每个方向
    for (let dir of directions) {
        let count = 1; // 当前落子算1个
        // 遍历正向和反向
        for (let [dx, dy] of dir) {
            let nx = x + dx;
            let ny = y + dy;
            // 统计连续相同棋子的数量
            while (
                nx >= 0 && nx < BOARD_SIZE &&
                ny >= 0 && ny < BOARD_SIZE &&
                board[nx][ny] === player
            ) {
                count++;
                nx += dx;
                ny += dy;
            }
        }
        // 任意方向达到5个则获胜
        if (count >= 5) {
            return true;
        }
    }
    return false;
}

四、核心逻辑说明

胜负判断算法的核心思路是以当前落子点为中心,向八个方向(四个直线方向的往返)扩展统计,这样做的时间复杂度为O(1),不需要遍历整个棋盘,性能高效。

需要注意的边界情况:

  • 落子位置超出棋盘范围时直接返回,不做统计

  • 统计时需要同时判断正向和反向的连续棋子,避免遗漏

  • 当棋盘下满仍未分出胜负时,可补充平局判断逻辑,例如校验history长度是否等于BOARD_SIZE * BOARD_SIZE

如果需要查看完整的示例效果,可以访问https://www.ipipp.com查看在线演示版本,或者参考上述代码自行搭建本地项目运行。

五子棋游戏Canvas棋盘绘制JavaScript游戏开发胜负判断算法HTML5游戏

免责声明:已尽一切努力确保本网站所含信息的准确性。网站部分内容来源于网络或由用户自行发表,内容观点不代表本站立场。本站是个人网站免费分享,内容仅供个人学习、研究或参考使用,如内容中引用了第三方作品,其版权归原作者所有。若内容触犯了您的权益,请联系我们进行处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。前端、网络、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握网站开发与运维所需的核心技术栈。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端逻辑,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。