内容简介:秉承着五子棋的规则我简单梳理并且改造如下哈:正式比赛的规则,可以戳百度百科了解下哈--五子棋。
秉承着 会就分享,不会就折腾 的技术宗旨。自己利用周末的时间将 休闲小游戏-五子棋
重新梳理了一下,整理成一个小的教程,分享出来给大家指点指点。
五子棋规则
五子棋的规则我简单梳理并且改造如下哈:
- 对局双方各执一色棋子;
- 空棋盘开局;
- 黑先、白后或者白先、黑后,交替下子,每次只能下一子;
- 横线、竖线或者斜线上有连续五个同一色的棋子,则游戏结束;
正式比赛的规则,可以戳百度百科了解下哈--五子棋。
代码骨架
这里实现的 五子棋
小游戏是使用 javascript 语言进行编写的,使用到了es6语法,面向对象的思想进行。
// 设置五子棋类 class Gobang { constructor(options={}){ this.options = options; this.init(); } init() { const { options } = this; } } // 实例化对象 let gobang = new Gobang({}); 复制代码
上面的 Gobang 类中,包含了一个constructor和init方法。其中constructor方法是类默认的方法,通过new命令生成对象实例时候,自动调用该方法。一个类必须有一个constructor方法,如果没有显式定义,一个空的constructor方法会默认添加。然后就是init方法了,这里我是整个类的初始化的入口方法。使用类进行的面向对象方法进行编写,比较好管理代码和功能的扩展。
绘制棋盘
棋盘分为两种,一种是视觉(物理)上的棋盘,另外一个是逻辑上的棋盘,你是看不见的。下面的一张图就很形象地展示了20*20棋盘的物理和逻辑方式。
绘制物理棋盘,我们这里使用到了 canvas
的相关知识点,控制画笔绘制棋盘:
// 绘制出物理棋盘 drawChessBoard() { const context = this.chessboard.getContext('2d'); const {padding, count, borderColor} = this.options.gobangStyle; let half_padding = padding/2; this.chessboard.width = this.chessboard.height = padding * count; context.strokeStyle = borderColor; // 画棋盘 for(var i = 0; i < count; i++){ context.moveTo(half_padding+i*padding, half_padding); context.lineTo(half_padding+i*padding, padding*count-half_padding); context.stroke(); // 这里绘制出的是竖轴 context.moveTo(half_padding, half_padding+i*padding); context.lineTo(count*padding-half_padding, half_padding+i*padding); context.stroke(); // 这里绘制出的是横轴 } } 复制代码
这里使用到的 padding,count,borderColor
等都是在实例化的时候传进去的。这样提高了可配置性和管理。上面的代码是绘制物理上的棋盘,那么逻辑上的棋盘虽然不能够绘制出来,但是我们可以表示出来。这里我们使用了二维数组的方法去记录逻辑位置,比如 (0,0)
点对应的数组下标是 [0][0]
;然后 (1,2)
点对应的下标是 [1][2]
...以此类推。然后我们再为这个逻辑点赋值为0,表示当前点没有落子。
// 绘制逻辑矩阵棋盘 initChessboardMatrix(){ const {count} = this.options.gobangStyle; const checkerboard = []; for(let x = 0; x < count; x++){ checkerboard[x] = []; for(let y = 0; y < count; y++){ checkerboard[x][y] = 0; } } } 复制代码
物理棋盘和逻辑棋盘有了之后,就可以考虑到将物理棋盘和逻辑棋盘关联起来了。这个比较简单,就是要计算真实的单元格位置进行除法操作即可。这步的管理在后面的落子步骤有提到。
绘制棋子
五子棋的棋子有且仅有两种--黑色棋子或者白色棋子。这里也是使用canvas的知识点来绘制棋子。
// 绘制黑棋或白棋 drawChessman(x , y, isBlack){ const context = this.chessboard.getContext('2d'); let gradient = context.createRadialGradient(x, y, 10, x-5, y-5, 0); context.beginPath(); context.arc(x, y, 10, 0, 2 * Math.PI); context.closePath(); if(isBlack){ gradient.addColorStop(0,'#0a0a0a'); gradient.addColorStop(1,'#636766'); }else{ gradient.addColorStop(0,'#d1d1d1'); gradient.addColorStop(1,'#f9f9f9'); } context.fillStyle = gradient; context.fill(); } 复制代码
落子实现人人对战
上一节的绘制黑棋和白棋的方法是在单独一个页面出来绘制的。现在我们将绘制棋子和棋盘整合,并实现人人对战的下棋模式。
我们要监听点击在棋盘上的事件,然后关联物理棋盘和逻辑棋盘点,之后在相应的地方刻画棋子即可。
// 监听落子 listenDownChessman() { // 监听点击棋盘对象事件 this.chessboard.onclick = event => { let {padding} = this.options.gobangStyle; let { offsetX: x, offsetY: y, } = event; x = Math.abs(Math.round((x-padding/2)/this.lattice.width)); y = Math.abs(Math.round((y-padding/2)/this.lattice.height)); if(this.checkerboard[x][y] !== undefined && Object.is(this.checkerboard[x][y],0)){ this.checkerboard[x][y] = this.role; // 这里调用刻画棋子的方法 this.drawChessman(x,y,Object.is(this.role , 1)); // 切换棋子的角色 this.role = Object.is(this.role , 1) ? 2 : 1; } } } 复制代码
实现悔棋
在双方下棋的时候,允许双方对已经下的棋子进行调整,也就是 悔棋 。如下截图展示功能:
实现悔棋功能的时候,需要知道下棋的历史记录和当前的落子步数和角色。对于历史的记录,这里对每一步的落子都使用一个对象进行存储,并放到一 个history
的数组里面进行保存。
// 悔棋 regretChess() { if(this.history.length){ const prev = this.history[this.currentStep - 1]; if(prev){ const { x, y, role } = prev; this.minusStep(x,y); this.checkerboard[prev.x][prev.y] = 0; this.currentStep--; this.role = Object.is(role,1) ? 1 : 2; } } } // 销毁棋子 minusStep(x, y) { const context = this.chessboard.getContext('2d'); const {padding, count} = this.options.gobangStyle; context.clearRect(x*padding, y*padding, padding,padding); } 复制代码
上面的代码确实是实现了悔棋功能,但是,在实现悔棋的时候,已经破坏掉了棋盘的UI,因为我们是使用canvas的 clearRect
方法,将撤销的棋子使用新的四边形进行覆盖,那也就覆盖了撤销棋子处的物理棋盘了。为了弥补这个被覆盖的物理棋盘,我们得重新绘制出此处坐标的新物理棋盘线条。这里的修复要考虑到落子在棋盘的不同位置,要分九种不同的情况进行修复:
- 左上角棋盘
- 左边缘棋盘
- 左下角棋盘
- 下边缘棋盘
- 右下角棋盘
- 右边缘棋盘
- 右上角棋盘
- 上边缘棋盘
- 中间(非边界)棋盘
// 修补删除后的棋盘,将九种情况的不同参数传过来即可 fixchessboard (a , b, c , d , e , f , g , h){ const context = this.chessboard.getContext('2d'); const {borderColor, lineWidth} = this.options.gobangStyle; context.strokeStyle = borderColor; context.lineWidth = lineWidth; context.beginPath(); context.moveTo(a , b); context.lineTo(c , d); context.moveTo(e, f); context.lineTo(g , h); context.stroke(); } 复制代码
实现撤销悔棋
有允许悔棋,那么就有允许撤销悔棋这样子才合理。同悔棋功能,撤销悔棋是需要知道下棋的历史记录和当前的步骤和棋子角色的。
// 撤销悔棋 revokedRegretChess(){ const next = this.history[this.currentStep]; if(next) { this.drawChessman(next.x, next.y, next.role === 1); this.checkerboard[next.x][next.y] = next.role; this.currentStep++; this.role = Object.is(this.role, 1) ? 2 : 1; } } 复制代码
胜利提示/游戏结束
五子棋的的结束也就是必须要决出胜利者,或者是棋盘没有位置可以下棋了。这里考虑决出胜利为游戏结束的切入点,上面也说到了如何才算是一方获胜-- 横线、竖线或者斜线上有连续五个同一色的棋子
。那么我们就对这四种情况进行处理,我们在矩阵中记录当前点击的数组点中是否有连续的五个1(黑子)或者连续的五个2(白子)即可。如下截图的x轴上的白子获胜情况,注意gif图右侧打印出来的数组内容:
// 裁判观察棋子,判断获胜一方 checkReferee(x , y , role) { if((x == undefined)||(y == undefined)||(role==undefined)) return; const XContinuous = this.checkerboard.map(x => x[y]); // x轴上连杀 const YContinuous = this.checkerboard[x]; // y轴上连杀 const S1Continuous = []; // 存储左斜线连杀 const S2Continuous = []; // 存储右斜线连杀 this.checkerboard.forEach((_y,i) => { // 左斜线 const S1Item = _y[y - (x - i)]; if(S1Item !== undefined){ S1Continuous.push(S1Item); } // 右斜线 const S2Item = _y[y + (x - i)]; if(S2Item !== undefined) { S2Continuous.push(S2Item); } }); } 复制代码
至此,已经一步步讲解完如何开发一个能够在pc上愉快玩耍的休闲小游戏-五子棋了。不妥之处还请指正哈 @~@
后话
五子棋的体验地址--休闲游戏-五子棋
文章首发地址-- github-五子棋游戏
代码仓库地址-- github-五子棋教程
创作文章不易,既然都看到这里了,留个赞再走呗~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 谈谈前端实现五子棋游戏
- 前端仔教你一步步实现人人对战五子棋小游戏【canvas详细版】
- canvas 五子棋游戏
- 使用 ink + react 制作一个命令行的在线五子棋游戏客户端
- 前端科普系列(三):CommonJS 不是前端却革命了前端
- 前端科普系列(三):CommonJS 不是前端却革命了前端
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Hive编程指南
卡普廖洛 (Edward Capriolo)、万普勒 (Dean Wampler)、卢森格林 (Jason Rutherglen) / 曹坤 / 人民邮电出版社 / 2013-12-1 / 69
市场中第一本Hive图书。 Hive在Hadoop系统中的应用趋势比较可观。一起来看看 《Hive编程指南》 这本书的介绍吧!