走井是个古老的亲子游戏,在电子游戏,电脑,手机没有普及时间相信很多孩子都跟大人玩过这个游戏,沙地上找4个石子画个框就能走起。不过为了让电子时代的小鲜肉
们也能知道后面在说什么,下面我们介绍一下走井游戏。
走井规则
走井的有两名玩家,每个玩家有两粒棋子,两名玩家交替走子,当一名玩家移动完自己的子之后,对方无子可动即获得胜利。
well.png
翻译成计算机语言
按照上图0,1,2,3,4这5个位置,列出每个位置的可移动状态,如下表
位置\可移动位置 | 0 | 1 | 2 | 3 | 4 | 十进制 | 十六进制 |
---|---|---|---|---|---|---|---|
0 | NA | 1 | 1 | 1 | 1 | 31 | 0x1F |
1 | 1 | NA | 1 | 0 | 0 | 5 | 0x5 |
2 | 1 | 1 | NA | 1 | 0 | 11 | 0xB |
3 | 1 | 0 | 1 | NA | 1 | 21 | 0x15 |
4 | 1 | 0 | 0 | 1 | NA | 9 | 0x9 |
该表格查看方式类似于公交梯型票价图,如按行查看,位置1可以的移动的目标位置为
0,2
,按位组合后为000101
即十进制数字5,十六进制为0x5
。其它的位置如查表即可得到。
将棋盘上唯的空位按照其所处的位置表示成一个5bit的数字,1的位置(可移动的目标位置或者说是空着的位置)在5bit中来回移动。如开局时空为位于0,该值为00001
--(hex)-->0x1
。
SVG
一种矢量图元描述格式,常用于作图形软件的数据交换。具体参见教程(内网)。
svg导学(互联网)
一个基本的svg结构如下所示:
<html><body><h1>My first SVG</h1><svg width="100%" height="100%"> <!----> <path d="M32 32 l128 0 l0 128 l-128 0 l128,-128" fill="none" stroke="black"></path> <line x1="32" y1="32" x2="160" y2="160" stroke="black" /> <circle cx="96" cy="96" r="20" class="invisible" id='p0' /> <circle cx="32" cy="32" r="20" class="gray" id='p1' /> <circle cx="160" cy="32" r="20" class="black" id='p2' /> <circle cx="160" cy="160" r="20" class="black" id='p3' /> <circle cx="32" cy="160" r="20" class="gray" id='p4' /> <rect id="turnSide" x="200" y="16" width="64" height="32" class="black"></rect> </svg></body></html>
CSS 样式
我们可以通过css模式直接控制svg元素的显示属性。
.black { fill: black } .gray { fill: gray } .invisible { display: none }
我们定义黑色(black)、灰色(gray)、和不可见(invisible)三个显示。
DOM事件
由于svg节点也是DOM节点,DOM事件也是可用的,我们通过向节点增加click
事件,来处理按键点击。
function init() { elements = document.querySelectorAll('circle'); for (var ele of elements) { console.log(ele); ele.addEventListener('click', pawnclick); } }
通过选择器,选择所有的
circle
标签,为它们增加点击事件。
将逻辑连接起来
有了每个位置的可移动位置及当前棋盘上的空位,我们可以通过位运算得出点中子是否可移动,以及其移动的去向。如果子可动则把目标位置置为当前点击位置的颜色(css class),点击位置置为空(.invisible),并进入下一手next()
。下面是pawnclick()
的实现。
function pawnclick() { if (this.className.baseVal != current_player[round % 2]) { console.log('not your turn'); return; } console.log(`click me ${this.id}`); //获取可移动位置 var moveDest = moveCandiate[this.id] & blankPos; if (moveDest != 0) { console.log(`move dest ${moveDest} boardPost ${mask2pos[moveDest]}`); blankPos = pos2mask[this.id];//将空位置为当前子的位置 //如果可以移动,则移动该子,并取消事件监听,进入下一轮 //获取目标位置 var destPos = document.getElementById(mask2pos[moveDest]); destPos.className.baseVal = this.className.baseVal; this.className.baseVal = "invisible"; nextTurn();//换手 } else//没有气不可移动 { console.log(`${this.id} 不可动`); } }
为了方便快速查找
翻译成计算机语言
中的表格,我们定义了moveCandiate,mask2pos,pos2mask
三个查找表。
var moveCandiate = { 'p0': 0x1F, 'p1': 0x5, 'p2': 0xB, 'p3': 0x15, 'p4': 0x9 }; // 移动目标位转棋盘位置 var mask2pos = { 1: 'p0', 2: 'p1', 4: 'p2', 8: 'p3', 16: 'p4' } var pos2mask = { 'p0': 1, 'p1': 2, 'p2': 4, 'p3': 8, 'p4': 16 }
胜负判定
换手后++round
,我们需要判断当前方是否还有子可动,如果无子可动则游戏结束。
function nextTurn() { ++round; var class_selector = current_player[round % 2];//根据手数确定是哪边 document.getElementById('turnSide').className.baseVal = class_selector; var pawns = document.getElementsByClassName(class_selector); var hasMove = 0; for( var pawn of pawns) { hasMove |= (moveCandiate[pawn.id] & blankPos); } if(!hasMove) { console.log(`${class_selector} loose!`); } console.log(pawns); }
在
nextTurn()
中还增加表示当前移动方的指示方格document.getElementById('turnSide').className.baseVal = class_selector
,对应svg
部分的<rect id="turnSide" x="200" y="16" width="64" height="32" class="black"></rect>
。
作者:清洼
链接:https://www.jianshu.com/p/3fd4352c0cbd