likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 20

背景

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

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

禁手介绍

禁手的分类

三三禁手(黑棋一子落下同时形成两个活三,此子必须为两个活三共同的构成子);

四四禁手(黑棋一子落下同时形成两个或两个以上的冲四或活四);

长连禁手(黑棋一子落下形成一个或一个以上的长连)。

构成禁手的基本子力要素

活三(本方再走一着可以形成活四的三);

活四(有两个点可以成五的四);

冲四(只有一个点可以成五的四);

长连(在棋盘上的阳线和阴线任意一条线上,形成的5个以上同色棋子不间隔的相连)。

有关禁手的规定

黑方五连与禁手同时形成时,禁手失效,黑方胜。

注意:如果长连了,算黑方败。

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

判断禁手的时机

黑方下棋后,立马判断。此时场上一定有奇数个棋子。

这里跟产品形态有关系。我期望下棋前,「是否禁手」应该由用户判断,而非计算机替用户计算。这样锻炼了用户的思维能力,有助于提升水平。这也是对真实生活的模拟,下实体五子棋时,确实没有计算机提示禁手。

此外,下棋后,若触发了禁手,计算机则给出提示,白方可以选择忽略禁手,或直接指认黑方输掉,黑方可以提出悔棋,白方同意悔棋则可继续。

开发

程序计算是有顺序的,我们需要先判断长连、再判断四四,最后判断三三。

因为长连禁手触发条件最苛刻,我们排除这种情况后,再去判断四四禁手。

而四四禁手条件相对三三更为苛刻,所以我们判断三三前先判断四四。

今天我们先开发长连禁手。

长连禁手

长连禁手的前提是黑方已经大于五连了,用我们之前的judgeWin就可以判断是否有五连。

如果有五连,我们再继续判断是否长连。如果没有五连,那么一定没有长连。大体逻辑如下:

const win = judgeWin(pieces);
if (win) {
  // 如果获胜,则需要判断长连。只要没长连,一定没触发其它禁手,所以直接return
  return judgeLongBan(pieces);
}
// 如果没赢,一定没长连,无需判断了
// TODO: 判断四四、三三

在开发之前,我们先准备一些常用的工具函数,包括getPieceXY计算某个棋子的横纵坐标。之前说过,我的棋盘存储方式,是0-225(即15*15)这些数字组成的数组。这个函数就是把这225个数字具体的横纵坐标计算出来。还包括逆运算getXYPiece

另外isBlack用于根据横纵坐标判断这个位置是不是黑色棋子,如果这个地方没棋子、或者为白色,都返回false。

function getPieceXY(piece: number) {
  return [piece % 15, Math.floor(piece / 15)];
}

function getXYPiece(x: number, y: number) {
  return y * 15 + x;
}

function isBlack(x: number, y: number, pieces: number[]) {
  return pieces.indexOf(getXYPiece(x, y)) % 2 === 0;
}

然后,编写judgeLongBan的逻辑,思路就是以当前黑棋子为中心,向4个方向(横、竖、左斜线、右斜线)判断有连续几个黑子。如果超过5个,则直接返回true(表明触发了长连禁手),否则为false(没有长连)。

function judgeLongBan(pieces: number[]) {
  const piece = pieces[pieces.length - 1];
  const [x, y] = getPieceXY(piece);
  let len = 1;
  for (let i = x - 1; i >= 0; i--) {
    if (isBlack(i, y, pieces)) len += 1;
    else break;
  }
  for (let i = x + 1; i <= 14; i++) {
    if (isBlack(i, y, pieces)) len += 1;
    else break;
  }
  if (len > 5) return true;
  len = 1;
  for (let i = y - 1; i >= 0; i--) {
    if (isBlack(x, i, pieces)) len += 1;
    else break;
  }
  for (let i = y + 1; i <= 14; i++) {
    if (isBlack(x, i, pieces)) len += 1;
    else break;
  }
  if (len > 5) return true;
  len = 1;
  for (let i = y - 1, j = x - 1; i >= 0 && j >= 0; i--, j--) {
    if (isBlack(i, j, pieces)) len += 1;
    else break;
  }
  for (let i = y + 1, j = x + 1; i <= 14 && j <= 14; i++, j++) {
    if (isBlack(i, j, pieces)) len += 1;
    else break;
  }
  if (len > 5) return true;
  len = 1;
  for (let i = y - 1, j = x + 1; i >= 0 && j <= 14; i--, j++) {
    if (isBlack(i, j, pieces)) len += 1;
    else break;
  }
  for (let i = y + 1, j = x - 1; i <= 14 && j >= 0; i++, j--) {
    if (isBlack(i, j, pieces)) len += 1;
    else break;
  }
  return len > 5;
}

写在最后

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