使用原生JS快速写出一个五子棋小游戏
作者:逍丶
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 { //否则得清空 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 { 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 { //否则得清空 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 { //否则得清空 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初学者一些帮助。
到此这篇关于使用原生JS快速写出一个五子棋小游戏的文章就介绍到这了,更多相关原生JS写五子棋小游戏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!