0
点赞
收藏
分享

微信扫一扫

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手


背景

之前我开发过五子棋,输出了一些文章:

  • 《用177行代码写个体验超好的五子棋》
  • 《用86行代码写一个联机五子棋WebSocket后端》
  • 《五子棋怎么存棋局信息?》
  • 《五子棋怎么判断输赢?你能5分钟交出代码吗?》
  • 《我做的联机五子棋是如何追求极致用户体验的?(上)》
  • 《我做的联机五子棋是如何追求极致用户体验的?(下)》
  • 《如何开发实现: 判断 五子棋 长连禁手》

之前的版本,五子棋不支持禁手。现在,我准备加一下禁手功能,还真没那么简单。

禁手介绍

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手_公众号

四四禁手

这个判断相对简单,我们判断下棋后,这个棋子是否同时组成了1个以上的4连就可以了(包括活四、冲四)。

  • 活四:就是有两个点可以成五的四。(见下方第一个图)
  • 冲四:有且只有一个点可以形成连五的四。(见下方第二个图,中间是连冲,两边是跳冲)

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手_小游戏_02

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手_JavaScript_03

开发

我们需要兼顾跳冲四的场景。

以竖方向为例子,我们需要判断竖直方向,紧挨着当前黑棋的黑棋子有几个。随后继续往上遍历,如果碰到白棋结束遍历,如果碰到空白棋子,则跳过它,看看后面还有多少连续的黑色棋子。之后再往下遍历,如果碰到白棋结束遍历,如果碰到空白棋子,则跳过它,看看后面还有多少连续的黑色棋子。这样得到3个变量:​​middle​​​ ​​before​​​ ​​after​​。

一个特殊情况

如果​​middle​​已经是4了,我们还需要存下相邻是否有白棋,如果两头都是白色,那么这不算活四冲四。例如,下图带标记的黑色棋子不是禁手:

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手_公众号_04

如果​​middle​​​上方相邻是白棋,我们可以把​​before​​​标为-1,类似的,如果下方相邻白棋,我们可以把​​after​​标记为-1。

另一个特殊情况

还有个特殊情况,下面带标记的黑色棋子,也不是禁手:

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手_小游戏_05

因为左侧的空白,黑色不能落子,那里是长连禁手。此时黑棋只形成了1个跳冲四。

如何解决呢?我们判断​​before​​​和​​middle​​相加大于等于5,则表示会触发长连禁手,那么这里就不算一个活四冲四。

外层逻辑

我们需要遍历4个方向,包括竖直、水平、左斜、右斜。都是一样的算法。我们没必要写四遍,可以通过参数​​dx​​​ ​​dy​​表明每次遍历时,x和y的变化量。通过参数传入。传4遍就可以。例如这样:

function judgeFourFourBan(pieces: number[]) {
const fourCount = getFourOnOneLine(pieces, 1, 0)
+ getFourOnOneLine(pieces, 0, 1)
+ getFourOnOneLine(pieces, -1, 1)
+ getFourOnOneLine(pieces, 1, 1);
return fourCount > 1;
}

这完全是四四禁手的定义:是否存在1个以上的活四冲四。

注:同一行可能有2个冲四,即​​getFourOnOneLine​​​返回值为0-2,也就是说,​​fourCount​​​最大值为8,下面的情况,​​fourCount​​达到最大值,所以中间的黑棋就是禁手了。

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手_JavaScript_06

当然,如果棋局变成这样,由于长连禁手存在,​​fourCount​​应该是0,这时候中间的黑棋不是禁手:

[教你做小游戏] 如何开发实现: 判断 五子棋 四四禁手_小游戏_07

是不是很神奇!我作为开发者,已经懵了!!!佩服五子棋禁手的博大精深!

内层逻辑

下面我们编写核心逻辑​​getFourOnOneLine​​:

function getFourOnOneLine(pieces: number[], dx: number, dy: number) {
const piece = pieces[pieces.length - 1];
const [x, y] = getPieceXY(piece);
let before = 0;
let middle = 1;
let after = 0;
let flag = false;
for (let i = x - dx, j = y - dy; ; i -= dx, j -= dy) {
if (!(i >= 0 && i <= 14 && j >= 0 && j <= 14) || isWhite(i, j, pieces)) {
if (!flag) before = -1;
break;
}
if (isBlack(i, j, pieces)) {
if (flag) before += 1;
else middle += 1;
} else {
if (flag) break;
flag = true;
}
}
flag = false;
for (let i = x + dx, j = y + dy; ; i += dx, j += dy) {
if (!(i >= 0 && i <= 14 && j >= 0 && j <= 14) || isWhite(i, j, pieces)) {
if (!flag) after = -1;
break;
}
if (isBlack(i, j, pieces)) {
if (flag) after += 1;
else middle += 1;
} else {
if (flag) break;
flag = true;
}
}
if (middle === 4) return before === 0 || after === 0 ? 1 : 0;
return (before > 0 && before + middle === 4 ? 1 : 0) + (after > 0 && middle + after === 4 ? 1 : 0);
}

下面,解释一下​​if (middle === 4) return before === 0 || after === 0 ? 1 : 0;​​。这句话不是凭空写出来的,是经过严格推理论证的:

middle

before

after

是否活四冲四

4

0

0

活四

4

-1

0

(白棋点)冲四

4

0

-1

冲四(白棋点)

4

正数

0

(长连禁手点)冲四

4

0

正数

冲四(长连禁手点)

4

-1

-1

(白棋点)四(白棋点)

4

-1

正数

(白棋点)四(长连禁手点)

4

正数

-1

(长连禁手点)四(白棋点)

4

正数

正数

(长连禁手点)四(长连禁手点)

可以看到,middle=4时,形成活四冲四的充要条件:before和after至少一个为0。

写在最后

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,联系我,交个朋友),转发本文前需获得作者HullQin​授权。我独立开发了《联机桌游合集》​,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋、象棋等游戏,不收费无广告。还独立开发了《合成大西瓜重制版》​。还开发了《Dice Crush》​参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:《教你做小游戏》、《极致用户体验》。

举报

相关推荐

0 条评论