专栏名称: 前端之神
一位前端小菜鸡,写过300多篇原创文章,全网有5w+个前端朋友,梦想是成为”前端之神“~
目录
相关文章推荐
哎咆科技  ·  苹果憋大招!iOS 18.4 Siri ... ·  14 小时前  
EETOP  ·  一种解决SoC总线功能验证完备性的技术 ·  21 小时前  
哎咆科技  ·  王阳明:修炼自己(深度好文) ·  昨天  
ZOL中关村在线  ·  HKC ... ·  昨天  
EETOP  ·  华为全年收入暴增! ·  3 天前  
51好读  ›  专栏  ›  前端之神

前端给客户实现了一个【五子棋】的小游戏,有点6!

前端之神  · 公众号  ·  · 2024-12-21 11:09

正文

前言

大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心~

又很多朋友最近说想要入门 Canvas ,问我能不能出一篇讲解 Canvas 的文章,但是我感觉直接讲会比较无聊,还不如通过一些案例来让大家去手敲呢

所以我准备使用 Canvas 实现一个 五子棋 的小游戏,希望大家能喜欢~

效果 & 实现思路

最终的效果如下:

五子棋的游戏流程如下:

  • 1、首先绘制棋盘
  • 2、黑白双方轮流落子,且不能将棋子下在已有棋子的位置上
  • 3、检查是否有五子连成一线的情况,若有,则该方获胜
  • 4、附加玩法:与AI对弈(实现单人游戏体验)

绘制棋盘

实际上非常容易,只需使用 ctx.moveTo ctx.lineTo 方法,横向绘制 15 条线条,再纵向绘制 15 条线条,便大功告成

这样就画出了棋盘:

黑白子轮流下棋

  • 一、绘制棋子的操作

监听鼠标的点击事件,从而获取点击位置的坐标,然后利用 ctx.arc 方法在该坐标位置画出棋子。

  • 二、防止重复下棋的策略

首先,我们需要获取鼠标的精确坐标。但这里有个关键点:棋子必须落在网格线的交叉点上。因此,在获取到鼠标坐标后,我们要进行适当的处理—— 四舍五入 ,以确保棋子位于最近的交叉点中心。

接下来,为确保同一位置不会重复下棋,我们可以采用一个二维数组来跟踪棋盘上的状态。最初,数组的所有值都设为 0 ,表示该位置尚未下棋。当此处下了黑棋时,对应的数组值变为1;若下了白棋,则值变为2。但请注意,这里的数组索引 (x,y) 与画布上的实际坐标 (x,y) 是相反的。因此,在后续的代码实现中,我们需要对坐标进行相应的转换。希望大家能深入思考其中的原因~

效果如下:

判断五子连线

怎样去判断呢?存在四种情形:一是 横向上有五个棋子相连 ;二是 纵向上有五个棋子相连 ;三是从 左上角到右下角斜着有五个棋子相连 ;四是从 右上角到左下角斜着有五个棋子相连 。每次落子的时候把这四种情况都判断一遍就可以了

完整代码

play()

function play({
    const canvas = document.getElementById('canvas')

    const ctx = canvas.getContext('2d')

    // 绘制棋盘

    // 水平,总共15条线
    for (let i = 0; i < 15; i++) {
        ctx.beginPath()
        ctx.moveTo(2020 + i * 40)
        ctx.lineTo(58020 + i * 40)
        ctx.stroke()
        ctx.closePath()
    }

    // 垂直,总共15条线
    for (let i = 0; i < 15; i++) {
        ctx.beginPath()
        ctx.moveTo(20 + i * 4020)
        ctx.lineTo(20 + i * 40580)
        ctx.stroke()
        ctx.closePath()
    }

    // 是否下黑棋
    // 黑棋先走
    let isBlack = true


    // 棋盘二维数组
    let cheeks = []

    for (let i = 0; i < 15; i++) {
        cheeks[i] = new Array(15).fill(0)
    }

    canvas.onclick = function (e{
        const clientX = e.clientX
        const clientY = e.clientY
        // 对40进行取整,确保棋子落在交叉处
        const x = Math.round((clientX - 20) / 40) * 40 + 20
        const y = Math.round((clientY - 20) / 40) * 40 + 20
        // cheeks二维数组的索引
        // 这么写有点冗余,这么写你们好理解一点
        const cheeksX = (x - 20) / 40
        const cheeksY = (y - 20) / 40
        // 对应元素不为0说明此地方已有棋,返回
        if (cheeks[cheeksY][cheeksX]) return
        // 黑棋为1,白棋为2
        cheeks[cheeksY][cheeksX] = isBlack ? 1 : 2
        ctx.beginPath()
        // 画圆
        ctx.arc(x, y, 2002 * Math.PI)
        // 判断走黑还是白
        ctx.fillStyle = isBlack ? 'black' : 'white'
        ctx.fill()
        ctx.closePath()

        // canvas画图是异步的,保证画出来再去检测输赢
        setTimeout(() => {
            if (isWin(cheeksX, cheeksY)) {
                const con = confirm(`${isBlack ? '黑棋' : '白棋'}赢了!是否重新开局?`)
                // 重新开局
                ctx.clearRect(00600600)
                con && play()
            }
            // 切换黑白
            isBlack = !isBlack
        }, 0)
    }
    // 判断是否五连子
    function isWin(x, y{
        const flag = isBlack ? 1 : 2
        // 上和下
        if (up_down(x, y, flag)) {
            return true
        }

        // 左和右
        if (left_right(x, y, flag)) {
            return true
        }
        // 左上和右下
        if (lu_rd(x, y, flag)) {
            return true
        }

        // 右上和左下
        if (ru_ld(x, y, flag)) {
            return true
        }

        return false
    }

    function up_down(x, y, flag{
        let num = 1
        // 向上找
        for (let i = 1; i < 5; i++) {
            let tempY = y - i
            console.log(x, tempY)
            if (tempY < 0 || cheeks[tempY][x] !== flag) break
            if (cheeks[tempY][x] === flag) num += 1
        }
        // 向下找
        for (let i = 1; i < 5; i++) {
            let tempY = y + i
            console.log(x, tempY)
            if (tempY > 14 || cheeks[tempY][x] !== flag) break
            if (cheeks[tempY][x] === flag) num += 1
        }
        return num >= 5
    }

    function left_right(x, y, flag{
        let num = 1
        // 向左找
        for (let i = 1; i < 5; i++) {
            let tempX = x - i
            if (tempX < 0 || cheeks[y][tempX] !== flag) break






请到「今天看啥」查看全文