走井是个古老的亲子游戏,在电子游戏,电脑,手机没有普及时间相信很多孩子都跟大人玩过这个游戏,沙地上找4个石子画个框就能走起。不过为了让电子时代的小鲜肉
们也能知道后面在说什么,下面我们介绍一下走井游戏。
走井规则
走井的有两名玩家,每个玩家有两粒棋子,两名玩家交替走子,当一名玩家移动完自己的子之后,对方无子可动即获得胜利✌。
翻译成计算机语言
按照上图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>
。