五子棋小案例

1 篇文章 0 订阅
1 篇文章 0 订阅

 整体步骤:

  1. 搭建结构
  2. 美化样式
  3. js逻辑
    1. 定义相关变量
    2. 定义 绘制棋盘函数
    3. 定义 绘制棋子函数
    4. 定义 初始化棋子数组函数
      1. 全部初始化为 -1 (表示没有棋子,只占位)
    5. 定义 点击canvs事件 
      1. 判断是正常点击还是悔棋操作
        1. 通过Array.isArray(),判断是否e对象参数,还是back数组参数
        2. 正常操作:拿到x,y通过在计算棋盘的中的位置(x,y),其中x:几行,y:几列
        3. 悔棋操作:使用传入的坐标作为下棋位置
      2. 判断该位置是否为空
        1. 绘制棋子
        2. 记录棋子 0 或1 到 棋盘数组中
      3. 判断是否胜利
      4. 切换玩家 0或1
    6. 定义 判断胜利事件(是否连成五子)
    7. 监听重新开始按钮,并定义事件 -> 重置所有相关的变量
    8. 监听悔棋按钮,并定义事件 -> 游戏未胜利,或棋子数量足够,才可进行
      1. for循环调用 点击 canvas事件,并传入 back 数组,该数组专门存储从开始到现在所下的棋子信息[行数,列数,当前玩家]
      2. 通过判断 点击 canvas事件 的事件参数,判断是正常点击事件还是悔棋操作
      3. 最后,    使用传入的坐标作为下棋位置

html结构:

目录

html结构:

css样式:

 1.定义变量

2.开局调用 初始化棋盘与棋子

3.定义  初始化棋盘布局函数

4.定义 初始化棋子

5.定义 绘制棋子函数

 6.定义 判断胜利函数

 7.监听 canvas 

 8.定义 点击 canvas事件

9.重新开始

10.悔棋


 <div class="container">

    <h2 class="text-center" style="text-align: center;color: #0feab0;">五子棋</h2>
    <div class="btn-box">
      <button class="btn">重新开始</button>
      <button class="btn2">悔棋</button>
    </div>
    <canvas width="360" height="360" id="canvas"></canvas>
  </div>

  </div>

css样式:

  .container,
    body {
      margin: 0;
      width: 100vw;
      height: 100vh;
      background-image: url('https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F386c15f2-d308-42f4-b741-113b9ffb6585%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1685778512&t=e162bf3543dc0adab286d27e73078539');
      overflow: hidden;
    }

    .container {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }

    #canvas {
      border: 1px solid black;
    }

    h2 {

      margin-bottom: 6vw;
      font-size: 28px;
    }

    .btn-box {
      display: flex;
      justify-content: space-between;
    }

    button {
      width: 25vw;
      height: 15vw;
      border: none;
      background-color: #f14e25;
      margin: 5vw 5vw;
      color: rgba(255, 255, 255, .5);
      font-size: 18px;
      border-radius: 5vw;
      font-family: Verdana, Geneva, Tahoma, sans-serif;
    }

    .btn {
      background-color: #dff089;
      color: rgba(216, 78, 78, 0.5);
    }

 js代码:

 1.定义变量



      // 1.渲染上下文
      var canvas=document.querySelector('#canvas')
      var ctx=canvas.getContext('2d');
      // 棋盘格子的宽度
      var cellWidth=30;
      // 棋盘格子的行数和列数
      var rows=11;
      var cols=11;
      // 棋盘的边缘空白区域
      var margin=15;
      // 棋盘的宽度 12个棋子
      var boardWidth=360;
      // 棋子的颜色
      var chessColor=['white','black'];
      // 当下的玩家
      var currentPlayer=0;
      // 棋盘上的棋子
      var chessboard=[];
      // 是否游戏结束
      var gameOver=false;
      // 悔棋
      var back=[]

     

     

2.开局调用 初始化棋盘与棋子

 initChessboard() //  初始化棋子
 drawChessboard() //  初始化棋盘

3.定义  初始化棋盘布局函数

函数中先绘制了棋盘的背景和大小,然后通过beginPath()开始一条新路径,依次绘制横向和纵向的网格线。在绘制横向和纵向的网格线时,使用了两个嵌套的for循环,分别处理每一行和每一列上的点。其中,调用了moveTo()方法将笔触移动到指定位置,并调用lineTo()方法创建从当前点到指定点的一条线条。最后,调用stroke()方法,以进行描边操作,完成棋盘的绘制。

 // 2.绘制棋盘
      function drawChessboard() {
        // 绘制棋盘背景与大小
        ctx.fillStyle='#d1b18f' // 设置矩形填充色
        ctx.fillRect(0,0,boardWidth,boardWidth)

        // 绘制棋盘格线
        ctx.beginPath()
        // 绘制 横线(两点成线)
        for(let i=0;i<rows;i++) {
          // 点1: moveTo()方法用于将路径移动到画布中的指定点,
          // 点2:lineTo()方法用于创建从当前点到指定点的一条线条
          // 必须先 moveTo() 再 linTo(),不然线会斜
          ctx.moveTo(margin+cellWidth/2,margin+i*cellWidth+cellWidth/2)
          ctx.lineTo(boardWidth-margin-cellWidth/2,margin+i*cellWidth+cellWidth/2)
        }
        // 绘制 竖线
        for(let i=0;i<cols;i++) {
          ctx.moveTo(margin+i*cellWidth+cellWidth/2,margin+cellWidth/2)
          ctx.lineTo(margin+i*cellWidth+cellWidth/2,boardWidth-margin-cellWidth/2)
        }
        ctx.stroke() // 画线
      }

4.定义 初始化棋子

函数中使用了两个嵌套的for循环,分别处理每一行和每一列上的点。对于每一个点,将其设为默认占位的-1,作为棋子未落下时的状态。

在脚本的其他部分中,会使用该数组记录已经落下的棋子,以及当前棋手的编号

  // 4.初始化 棋子
      function initChessboard() {
        for(let i=0;i<rows;i++) {
          chessboard[i]=[]
          for(let j=0;j<cols;j++) {
            chessboard[i][j]=-1 // 默认占位
          }
        }
      }

5.定义 绘制棋子函数

函数接受三个参数:xy表示棋子的行数和列数color表示棋子颜色

在函数中,首先计算出棋子的坐标,然后定义了一个radius变量来表示棋子的半径。接着使用beginPath()方法开始一条新路径,然后使用arc()方法画圆,并设置颜色填充。最后调用fill()方法,以进行填充操作,完成棋子的绘制。

在绘制棋子时,还添加了悔棋功能的逻辑判断如果当前落下的棋子并没有在back数组中,就将该落子信息存储到数组中,以便在悔棋时可以撤销该步棋。

 // 5.绘制棋子
      function drawChess(x,y,color) {
        // 棋子半径
        var radius=cellWidth/2

        // 计算棋子坐标
        var cx=parseInt(margin+y*cellWidth+cellWidth/2) // x坐标 = 边距+列数x棋子宽度÷2
        var cy=parseInt(margin+x*cellWidth+cellWidth/2) // y坐标 = 边距+行数x棋子宽度÷2

        // 悔棋功能逻辑
        //  1.判断当前落下的棋子是否已经在back数组中
        //    1.1 如果没有 -> 将落下的棋子信息存放在back数组中
        //    1.2 如果存在 -> 否则不做任何操作
        let xyc=[x,y,color]
        if(back.findIndex(item => item.length&&item[0]===xyc[0]&&item[1]===xyc[1])===-1) {
          back.push(xyc)
        }

        // 绘制棋子
        ctx.beginPath()
        ctx.arc(cx,cy,radius,2*Math.PI,0)
        ctx.fillStyle=color // 填充颜色
        ctx.fill() // 填充棋子
      }

 6.定义 判断胜利函数

思路: 函数中先定义了八个方向,即垂直、水平和对角线方向。然后遍历每个方向,将棋子在该方向上延伸并计算相同颜色棋子的数量,如果找到连成五子的情况,则返回true,否则继续遍历其他方向。如果所有方向上都没有找到连成五子的情况,则返回false

// 6.判断胜利
      /**
       * 检查给定位置是否连成五子,如果是则返回true,否则返回false。
       * @param {number} row - 给定的位置的行坐标。
       * @param {number} col - 给定的位置的列坐标。
       * @param {number} player - 当前棋手的编号,0表示黑方,1表示白方。
       */
      function checkWin(row,col,player) {
        // 定义八个方向
        var dirs=[
                     [-1,-1],[-1,0],[-1,1],
                     [0,-1],        [0,1],
                     [1,-1], [1,0], [1,1]
                 ];
        // 遍历每一个方向
        for(let i=0;i<dirs.length;i++) {
          var count=1;
          var dx=dirs[i][0];
          var dy=dirs[i][1];

          // 延伸直线连同颜色的棋子个数
          for(let j=1;j<5;j++) {
            var x=row+j*dx;
            var y=col+j*dy;
            // 如果超出了边界或者下一个棋子与当前棋子颜色不同,则停止延伸
            if(x<0||x>=rows||y<0||y>=cols||chessboard[x][y]!==player) {
              break;
            }
            count++;
          }
          // 如果找到了连成五子的情况,返回true
          if(count===5) {
            return true;
          }
        }
        // 如果没有找到连成五子的情况,返回false
        return false;
      }

 7.监听 canvas 

 // 监听 canvas 画布
      canvas.addEventListener('click',function(e) {
        onTouchStart(e)
      })
    

 

 8.定义 点击 canvas事件

思路:

  1.  处理点击事件,落子并判断胜负。
         
  2. 如果参数e是数组,则为悔棋操作,使用数组中的坐标作为下棋位置。
           
  3. 如果不是数组,说明是正常点击事件,获取触摸点的坐标并计算其在棋盘上的行列坐标,并落子,在chessboard数组中记录该位置,并检查是否胜利,如果已经胜利则更新gameOver变量为true
           
   
     
    
      // 5.点击画布
      function onTouchStart(e) {
        // 判断是正常点击事件还是悔棋操作
        if(Array.isArray(e)) {
          // 使用传入的坐标作为下棋位置
          var i=e[0]
          var j=e[1]
        } else {
          // 获取点击位置的坐标
          // offsetY:相对于当前元素的y坐标,(以元素自身为依据,与css中的position的属性值relative类似)
          var x=e.offsetX
          var y=e.offsetY
          // 计算在棋盘的中的位置
          // i: 第几行 = (y坐标-边距)/棋子宽度
          // j: 第几列 = (x坐标-边距)/棋子宽度
          var i=Math.floor((y-margin)/cellWidth)
          var j=Math.floor((x-margin)/cellWidth)
        }


        // 判断该位置是否为空
        if(i>=0&&j>=0&&i<=cols&&j<=rows&&chessboard[i][j]===-1&&!gameOver) {
          // 绘制棋子
          drawChess(i,j,chessColor[currentPlayer])
          // 记录棋子,我方棋子为0,对方棋子为1
          chessboard[i][j]=currentPlayer

          // 判断是否胜利
          if(checkWin(i,j,currentPlayer)) {
            // 标记游戏结束
            gameOver=true
            // 弹出胜利信息
            return alert(`${chessColor[currentPlayer]==="white"? '白方':"黑方"} 玩家胜利!`)
          }
          // 切换玩家 1 => 0 或 0 => 1
          currentPlayer=1-currentPlayer

          //  此函数:处理了点击棋盘的事件,并进行相应的处理。
          //  如果是悔棋操作,则使用数组中的坐标作为下棋位置;
          //  如果不是,则获取触摸点的坐标并计算其在棋盘上的行列坐标,并落子,记录该位置在chessboard数组中,并检查是否胜利,如果胜利则更新gameOver变量为true。

        }
      }
      
   

9.重新开始

思路:  清除画布,并重置数据

 //  重新开始逻辑
      document.querySelector('.btn').addEventListener('click',function() {
        ctx.clearRect(0,0,canvas.width,canvas.height);
        initChessboard() // 初始化棋子
        drawChessboard() // 初始化棋盘
        back=[]
        gameOver=false
        currentPlayer=0
      })

10.悔棋

思路:

  1.   判断棋子是否已下满8个,如果没满则不能进行悔棋操作。
  2.   判断当前游戏是否已经结束,如果结束则不能进行悔棋操作。
  3.   清空画布,并重新绘制棋盘和棋子。
  4.   从存储棋子坐标的back数组中删除最后两个元素,模拟悔棋操作。
  5.   重置当前玩家为0。
  6.   根据存储在back数组中的棋子坐标依次调用onTouchStart()方法,以重新绘制棋子。
      //  悔棋-逻辑
      document.querySelector('.btn2').addEventListener('click',function() {
        if(back.length<8) {
          return alert('总棋子到8步后,方可悔棋!')
        } else if(gameOver) {
          return alert('此局已结束,请开始下一局!')
        }
        // 清空棋盘
        ctx.clearRect(0,0,canvas.width,canvas.height);
        initChessboard() // 初始化棋子
        // 重新渲染棋盘
        drawChessboard()
        // 从back数组中过滤最后两个棋子信息
        back=back.slice(0,-2)
        // 重置当前选手为0
        currentPlayer=0
        // for 循环模拟点击 悔棋之后的逻辑
        for(let index=0;index<back.length;index++) {
          //  传入index 方便调用back中的每一项
          onTouchStart(back[index])
        }

      })

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海424

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值