
获胜的棋形
中间空一格的○_○○○,只要填上那个空格就成了五连。用五子棋术语来说,这叫跳四。如果一手棋同时形成这个跳四和另一条线上的连三(○○○),就能获胜。对方必须先挡四,而在这期间把三变成活四,就再也挡不住了。这就是四三。
人很容易找到这个点。可是现在的AI即使能用自己的棋子形成这个棋形,也不会去下。

在实际对局中
我们重新回放了24局网页对局,寻找轮到AI(白)时能一手形成四三的时刻。这样的时刻有7次,而AI下在那个位置的只有1次。能靠跳四获胜的2次全都错过了。
相反,人却用同样的棋形赢了。在人获胜的22局中,有11局是以四三结束的。

下面是其中的一局。黑棋下在虚线位置后,同时形成了斜向的跳四(●●_●●)和横向的跳三(●●_●)。

本地AI测试
我们把网页上的本地AI代码原样移植过来,在15×15棋盘上随机改变方向进行测试。找到自己四三点的比例,在连四(○○○○)加三的情况下是29%,在跳四(○_○○○)加三的情况下是27%。提前挡住对方下一手能形成四三的位置的比例也只有33%。只挡一个三的情况,连三是67%,跳三是100%。

为什么看不到
本地AI会对每个空格分别给四个方向打分再相加。但每个方向都只从该格往前数五格,比如往右或往下,而不看后面。像上图那样,如果构成四和三的棋子位于四三点的左边和上边,从那个位置就一颗棋子也看不到。反倒是从这条线的左端往右看能看到三颗棋子,于是它就选了那里。
而且它只是逐条线地累加分数,并不会单独判断两条线在一点相交形成四三。寻找获胜着法的代码也只确认能立即形成五连的着法。所以只有在方向碰巧对上时,它才会下出四三。
禁手判定由引擎另外进行,引擎也会把包含跳三的三三判为禁手。规则是看对了的,不足的是下棋一方的判断。
这一步的实际代码
这是本地AI给一个空格打分的部分。可以看到它只数前面的五格。
omook-web/lib/ai.ts
function linePotential(
board: Stone[],
r: number,
c: number,
dr: number,
dc: number,
color: Stone,
size: number,
): number {
let streak = 0;
let blockedNear = false;
const rr0 = r - dr;
const cc0 = c - dc;
if (inBounds(rr0, cc0, size)) {
const oc = board[idx(rr0, cc0, size)];
if (oc !== EMPTY && oc !== color) blockedNear = true;
}
let rr = r;
let cc = c;
for (let k = 0; k < 5 && inBounds(rr, cc, size); k++, rr += dr, cc += dc) {
const v = board[idx(rr, cc, size)];
if (v === color) streak++;
else if (v === EMPTY) {
/* open */
} else break;
}
let score = streak * streak * 120;
if (streak >= 4) score += 50000;
if (blockedNear && streak >= 4) score = Math.floor(score / 8);
return score;
}
function scorePlacement(board: Stone[], r: number, c: number, who: Stone, size: number): number {
let s = 0;
for (const [dr, dc] of DIRS) s += linePotential(board, r, c, dr, dc, who, size);
return s;
}下一篇是“4. 让VLM学习启发式、再让启发式重新成为老师的计划”。
COMMENTS · CHAPTER 03
看不到靠跳四取胜的AI 评论 0
还没有评论。阅读时有疑问或想补充的内容,欢迎留言。
发表评论前,请用 Google 开始并登记昵称和邮箱。
用 Google 开始