如何使用原生 JS,快速写出一个五子棋小游戏

开发 前端
下棋的逻辑很简单,就是点击棋盘的时候,给点击的td对应的那个对象添加一个num属性,黑棋就是1,白棋就是2,然后渲染出来就可以了。下棋顺序可以通过一个全局变量flag来控制,同时声明两个全局数组,用来存放所有的黑棋和白棋。后面判断胜负时,要对这两个数组先进行遍历。

1. 棋盘和棋子的绘制。

let arr = [      [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],  [{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},],]//封装渲染函数const render = () => {  document.querySelector('table').innerHTML = ''  arr.forEach((item, index) => {    let tr = document.createElement('tr')    item.forEach((item2, index2) => {      let td = document.createElement('td')//遍历数组,绘制棋盘      //给td标签设置自定义属性,用来作为坐标使用      td.dataset.y = index      td.dataset.x = index2      tr.appendChild(td)      //给数组里面的对象做条件判断,这样就能渲染出颜色      if (item2.num === 1) {        td.classList.add('bgc1')      }      else if (item2.num === 2) {        td.classList.add('bgc2')      }    })    document.querySelector('table').appendChild(tr)  })}render()

先创建一个15 * 15的二维数组,通过对数组的两层遍历,创建出一个 15 * 15 的表格,这样棋盘就有了。用数组来绘制棋盘的好处是便于查找和筛选。每一个td都对应着一个空对象,下棋的时候通过给这个对象添加一个 num 的属性,num 为 1 时,就渲染成黑色,2 就渲染成白色,再稍微调整一下 css 样式,这样棋盘和棋子就绘制好了。每一个 td 都有自己的自定义属性 x 和 y,类似于坐标,这样就可以很方便的把 td 标签和数组里对应的值联系起来。下面是 css 代码

<style>  * {    margin: 0;    padding: 0;    box-sizing: border-box;    list-style: none;  }  table {    position: relative;    width: 730px;    height: 730px;    margin: 0 auto;    border: 5px solid black;    background: url(./src=http___pic45.nipic.com_20140804_2372131_155038114014_2.jpg&refer=http___pic45.nipic.webp) no-repeat;    background-size: 100%;    background-position: center;    padding: 24px 12px;  }  td {    width: 35px;    height: 35px;    border-radius: 50%;    margin-right: 13px;    margin-bottom: 11px;    cursor: pointer;  }  .bgc1 {    background-color: black;  }  .bgc2 {    background-color: white;  }  button {    position: absolute;    width: 200px;    height: 100px;    bottom: 100px;    right: 200px;    text-align: center;    line-height: 100px;    font-size: 25px;  }</style>
<table></table><button>悔棋</button>

图片

2. 轮流下棋的点击事件

下棋的逻辑很简单,就是点击棋盘的时候,给点击的td对应的那个对象添加一个num属性,黑棋就是1,白棋就是2,然后渲染出来就可以了。下棋顺序可以通过一个全局变量flag来控制,同时声明两个全局数组,用来存放所有的黑棋和白棋。后面判断胜负时,要对这两个数组先进行遍历。

//判断下棋顺序的全局变量let flag = true//所有黑棋数组let blackArr = []//所有白棋数组let whiteArr = []//轮流下棋逻辑document.querySelector('table').addEventListener('click', function (e) {  if (e.target.dataset.x) {    let td = e.target    //判断黑白棋子的顺序    if (flag) {      //判断点击的地方是否已经有棋子了,避免棋子覆盖      if (!arr[td.dataset.y][td.dataset.x].num) {        flag = !flag        arr[td.dataset.y][td.dataset.x].num = 1        //每走一步,就将其添加至对应的数组当中        blackArr.push([td.dataset.y, td.dataset.x])                  }    } else {      if (!arr[td.dataset.y][td.dataset.x].num) {        flag = !flag        arr[td.dataset.y][td.dataset.x].num = 2        whiteArr.push([td.dataset.y, td.dataset.x])      }    }    //调用判断胜负的函数    XWin(td)    YWin(td)    X_YWin(td)    Y_XWin(td)  }  render()})

图片

3.获胜条件判断

接下来就是写获胜条件了。我分成了 4 种情况,横轴,数轴,正斜轴和反斜轴。这 4 种情况逻辑和方法大致都是相同的,就是里面的数据有些细微差别。

3.1 横轴获胜

以横轴为例,如何判断获胜,先判断是黑棋还是白棋,然后遍历对应的数组。已黑棋为例,遍历之后把y值相同的黑棋筛选出来都放入一个数组中,也就是同一行的黑棋。接着比较这一行的这些黑棋的x值,如果有5个连续的x值,则说明横轴上有5个连续的黑棋,就可以判断获胜了。怎么比较这些x值呢,我的做法是先将他们用sort()方法排序,接着从小到大依次比较。建一个新数组,第二个值等于第一个值加1,就把他们扔到这个新数组中,如果出现某个值不连续了,就将这个数组清空,这样通过判断这个数组的长度,就能判断胜负了。

//横轴获胜逻辑function XWin(td) {  //当前X轴的所有棋子集合  let xAllArr = []  //判断横轴胜负逻辑的X轴棋子  let xWinArr = []  //判断下的是黑棋还是白棋  if (!flag) {    blackArr.map(item => {      if (item[0] == td.dataset.y) {        //将当前排的所有棋子加入对应数组        xAllArr.push(item[1])      }    })  } else {    whiteArr.map(item => {      if (item[0] == td.dataset.y) {        xAllArr.push(item[1])      }    })  }  //把横排总数组排序,方便比较  xAllArr.sort((a, b) => a - b)  for (let i = 1; i < xAllArr.length; i++) {    // console.log(xAllArr[i]);    if (xAllArr[i] == (+xAllArr[i - 1] + 1)) {      //如果相邻的两个棋子数量相差1,就将其添加至胜负逻辑数组      xWinArr.push(xAllArr[i])    } else {      //如果数组长度大于4,就跳出循环      if (xWinArr.length >= 4) break      //否则得清空      xWinArr = []    }  }  //获胜条件  if (xWinArr.length >= 4) {    //这里要用定时器将弹框变成异步任务,否则第五颗棋子渲染不出来就提示获胜了    if (!flag) {      setTimeout(function () {        alert('黑棋获胜!')        location.reload()      }, 100)    } else {      setTimeout(function () {        alert('白棋获胜!')        location.reload()      }, 100)    }  }}

图片

3.2 数轴获胜

​竖轴和横轴代码基本上也相同

只是换了个条件,把 if (item[0] == td.dataset.y) ​换成了 if (item[1] == td.dataset.x),意思就是选出这一列所有的棋子。后面的逻辑和代码就和横轴一样了。​

//竖轴获胜逻辑function YWin(td) {  //当前Y轴的所有棋子集合  let yAllArr = []  //判断竖轴胜负逻辑的X轴棋子  let yWinArr = []  if (!flag) {    blackArr.map(item => {      if (item[1] == td.dataset.x) {        yAllArr.push(item[0])      }    })  } else {    whiteArr.map(item => {      if (item[1] == td.dataset.x) {        yAllArr.push(item[0])      }    })  }  //竖排总数组排序  yAllArr.sort((a, b) => a - b)  for (let i = 1; i < yAllArr.length; i++) {    // console.log(xAllArr[i]);    if (yAllArr[i] == (+yAllArr[i - 1] + 1)) {      yWinArr.push(yAllArr[i])    } else {    //如果数组长度大于4,就跳出循环      if (yWinArr.length >= 4) break      yWinArr = []    }  }  if (yWinArr.length >= 4) {    if (!flag) {      setTimeout(function () {        alert('黑棋获胜!')        location.reload()      }, 100)    } else {      setTimeout(function () {        alert('白棋获胜!')        location.reload()      }, 100)    }  }}

图片

3.3 正斜轴获胜

斜轴困难一点的地方就是,怎么筛选出这一条斜线上的所有棋子。

只要能把这条斜线上的棋子给找出来,后面的逻辑判断就都一样了。所有的斜线都是 45 度角,也就是说斜线上的任意两个棋子,他们的x值之差于y值之差是相等的。这样的话,判断起来就简单了。​​if ((item[0] - td.dataset.y) == (item[1] - td.dataset.x))​​ 这样就可以了。斜线上的棋子找出来后,后面的步骤就都一样了,复制粘贴即可。

//正斜轴获胜逻辑function X_YWin(td) {  //当前X轴的所有棋子集合  let x_yAllArr = []  //判断横轴胜负逻辑的X轴棋子  let x_yWinArr = []  if (!flag) {    blackArr.map(item => {      //判断斜轴棋子,斜轴棋子的x和y之差都是相同的      if ((item[0] - td.dataset.y) == (item[1] - td.dataset.x)) {        x_yAllArr.push(item[1])      }    })  } else {    whiteArr.map(item => {      if ((item[0] - td.dataset.y) == (item[1] - td.dataset.x)) {        x_yAllArr.push(item[1])      }    })  }  x_yAllArr.sort((a, b) => a - b)  for (let i = 1; i < x_yAllArr.length; i++) {    if (x_yAllArr[i] == (+x_yAllArr[i - 1] + 1)) {      //如果相邻的两个棋子数量相差1,就将其添加至胜负逻辑数组      x_yWinArr.push(x_yAllArr[i])    } else {      //如果数组长度大于4,就跳出循环      if (x_yWinArr.length >= 4) break      //否则得清空      x_yWinArr = []    }  }  //获胜条件  if (x_yWinArr.length >= 4) {    if (!flag) {      setTimeout(function () {        alert('黑棋获胜!')        location.reload()      }, 100)    } else {      setTimeout(function () {        alert('白棋获胜!')        location.reload()      }, 100)    }  }}

图片

3.4 反斜轴获胜

反斜轴同理,条件改成 if (0 - (item[0] - td.dataset.y) == (item[1] - td.dataset.x)),其余的复制粘贴。

//反斜轴获胜逻辑function Y_XWin(td) {  //当前X轴的所有棋子集合  let y_xAllArr = []  //判断横轴胜负逻辑的X轴棋子  let y_xWinArr = []  if (!flag) {    blackArr.map(item => {      //判断斜轴棋子      if (0 - (item[0] - td.dataset.y) == (item[1] - td.dataset.x)) {        y_xAllArr.push(item[1])      }    })  } else {    whiteArr.map(item => {      if (0 - (item[0] - td.dataset.y) == (item[1] - td.dataset.x)) {        y_xAllArr.push(item[1])      }    })  }  y_xAllArr.sort((a, b) => a - b)  for (let i = 1; i < y_xAllArr.length; i++) {    if (y_xAllArr[i] == (+y_xAllArr[i - 1] + 1)) {      //如果相邻的两个棋子数量相差1,就将其添加至胜负逻辑数组      y_xWinArr.push(y_xAllArr[i])    } else {      //如果数组长度大于4,就跳出循环      if (y_xWinArr.length >= 4) break      //否则得清空      y_xWinArr = []    }  }  //获胜条件  if (y_xWinArr.length >= 4) {    if (!flag) {      setTimeout(function () {        alert('黑棋获胜!')        location.reload()      }, 100)    } else {      setTimeout(function () {        alert('白棋获胜!')        location.reload()      }, 100)    }  }}

图片

把这些函数放到下棋事件里面调用,整个功能就完成了。

4. 悔棋功能

最后写一下悔棋功能,点击悔棋,把对应数组里面的数据删除,然后重新渲染棋盘就完事了。

//悔棋document.querySelector('button').addEventListener('click', function () {  //判断前面一步是黑棋还是白棋  if (!flag) {    //黑棋    //获取对应棋子总数组的最后一个数据的值    const y = blackArr[blackArr.length - 1][0]    const x = blackArr[blackArr.length - 1][1]    //将对应的对象里的num值删除,这样渲染出来对应棋子就消失了    delete arr[y][x].num    //删除总数组里的最后一个数据,否则胜负逻辑会有问题    blackArr.splice(blackArr.length - 1, 1)    //重置下棋顺序    flag = !flag  } else {    //白棋    const y = whiteArr[whiteArr.length - 1][0]    const x = whiteArr[whiteArr.length - 1][1]    delete arr[y][x].num    whiteArr.splice(whiteArr.length - 1, 1)    flag = !flag  }  render()})

总结

整个代码写下来,都是些 js 的基本语法,几个数组的方法来回用,希望能给 js 初学者一些帮助。

图片

责任编辑:武晓燕 来源: 前端YUE
相关推荐

2022-11-07 11:27:00

JS游戏开发

2022-03-17 15:28:18

五子棋HarmonyOSJSAPI

2010-03-18 17:07:32

2015-01-19 13:42:08

Android游戏源码蓝牙对战五子棋

2015-06-16 14:24:41

开发五子棋网游

2023-10-07 11:58:52

PythonPygame

2021-04-13 06:35:13

Elixir语言编程语言软件开发

2015-01-14 14:22:30

Android源码游戏

2022-08-22 17:28:34

ArkUI鸿蒙

2022-11-01 15:17:48

JS鸿蒙小游戏

2011-06-21 15:01:55

2022-10-31 15:22:37

JS鸿蒙小游戏

2020-11-30 06:20:13

javascript

2022-03-24 07:57:58

Python水果忍者游戏

2024-01-15 07:47:09

井字棋游戏编程练习Python

2021-03-30 05:58:01

JavascriptCss3转盘小游戏

2023-02-13 14:47:32

人工智能机器学习ChatGPT

2022-02-11 14:39:11

游戏JS鸿蒙

2023-11-29 08:10:36

javascriptH5游戏

2020-10-29 16:00:03

Node.jsweb前端
点赞
收藏

51CTO技术栈公众号