The renju rule

The renju rule, the international standard for Gomoku, assumes that Black, who moves first, has the advantage, and imposes forbidden moves on Black only. Black may not make a double-three, a double-four or an overline (six or more in a row), while White has no restrictions at all. An overline is even a win for White.

When we first built the engine with the renju rule, White became almost invincible. Black's attacking shapes are blocked as forbidden moves, while White can win freely with the same shapes.

The same forbidden moves for Black and White

So we changed the rules so that there is no difference between Black and White.

  • Double-three: forbidden for both Black and White. Broken threes (●_●●, ●●_●) also count as threes.
  • Overline: forbidden for both, and six or more in a row does not count as a win. Only exactly five wins.
  • Double-four: allowed for both.
  • A move that makes five is checked before the forbidden-move check. A winning move can always be played.
Forbidden shapes. Double-three (solid three + solid three), double-three (broken three + solid three) and overline are forbidden for both; double-four is allowed
Forbidden shapes. Double-three (solid three + solid three), double-three (broken three + solid three) and overline are forbidden for both; double-four is allowed

White still won after the change

The time of the rule change is not on record, so we split the games at the fifth day of automatic play, when the engine file last changed. In the 445 games before it, White won 419 (94%); in the 42 games after it, 38 (90%).

In the training games, White was always the VLM and Black was always the local AI. So these records alone cannot tell whether White won because of the rules or because the local AI playing Black was weak. We need to play again with the same AI alternating between Black and White.

The actual code for this step

This is the forbidden-move check in the web engine. It first checks whether the move wins, then blocks only overlines and double-threes.

omook-web/lib/board.tstypescript · 16 lines
export function hasKyungsuBan(
  board: Stone[],
  row: number,
  col: number,
  stone: Stone,
  size: number,
): RejectReason | null {
  if (!inBounds(row, col, size)) return "out_of_bounds";
  if (cell(board, row, col, size) !== EMPTY) return "occupied";
  if (wouldWinIfPlaced(board, row, col, stone, size)) return null;

  const g = withStone(board, row, col, stone, size);
  if (hasOverline(g, row, col, stone, size)) return "changmok";
  if (countOpenThreeAxes(g, row, col, stone, size) >= 2) return "sansan";
  return null;
}

The next post is "3. An AI that cannot see wins through a broken four".