O formato vencedor

○_○○○, com uma casa vazia no meio, vira cinco assim que essa casa é preenchida. Na terminologia do gomoku, isso é um quatro separado. Se uma jogada cria esse quatro separado junto com um três unido (○○○) em outra linha, você vence. O adversário precisa bloquear primeiro o quatro e, enquanto isso, você transforma o três em um quatro aberto, que não tem como ser bloqueado. Isso é um quatro-três.

Pessoas encontram essa posição com facilidade. Mas a IA atual não joga nela mesmo quando consegue formar esse formato com as próprias pedras.

Um quatro-três que a IA deixou passar. Jogar no ponto pontilhado cria um quatro separado na horizontal e um três unido na vertical, mas a IA jogou na ponta esquerda da linha
Um quatro-três que a IA deixou passar. Jogar no ponto pontilhado cria um quatro separado na horizontal e um três unido na vertical, mas a IA jogou na ponta esquerda da linha

Em partidas reais

Reproduzimos de novo 24 partidas da web e procuramos os momentos em que a IA (brancas) poderia ter formado um quatro-três com uma jogada. Houve 7 momentos assim, e a IA jogou nessa posição só 1 vez. Ela deixou passar as 2 chances de vencer com um quatro separado.

Já as pessoas venceram com o mesmo formato. Das 22 partidas vencidas por pessoas, 11 terminaram com um quatro-três.

Como terminaram as 22 partidas vencidas por pessoas. Quatro aberto 9, quatro-três (três unido) 6, quatro-três (três separado) 5, quatro-quatro 2
Como terminaram as 22 partidas vencidas por pessoas. Quatro aberto 9, quatro-três (três unido) 6, quatro-três (três separado) 5, quatro-quatro 2

Abaixo está uma dessas partidas. Quando as pretas jogaram no ponto pontilhado, surgiram ao mesmo tempo um quatro separado na diagonal (●●_●●) e um três separado na horizontal (●●_●).

Um quatro-três em uma partida real. A 23ª jogada das pretas cria ao mesmo tempo um quatro e um três separado
Um quatro-três em uma partida real. A 23ª jogada das pretas cria ao mesmo tempo um quatro e um três separado

Testando a IA local

Portamos o código da IA local da web sem alterações e o testamos em um tabuleiro 15×15, mudando a orientação aleatoriamente. Ela encontrou a própria posição de quatro-três em 29% das vezes com um quatro unido (○○○○) mais um três, e em 27% com um quatro separado (○_○○○) mais um três. A proporção em que ela bloqueou antes a posição onde o adversário poderia formar um quatro-três na jogada seguinte também ficou em apenas 33%. Bloquear um único três deu certo em 67% das vezes para um três unido e em 100% para um três separado.

Testes da IA local. Encontrar o próprio quatro-três: quatro unido 29%, quatro separado 27%; bloquear a posição de quatro-três do adversário 33%; bloquear três unido 67%; bloquear três separado 100%
Testes da IA local. Encontrar o próprio quatro-três: quatro unido 29%, quatro separado 27%; bloquear a posição de quatro-três do adversário 33%; bloquear três unido 67%; bloquear três separado 100%

Por que ela não enxerga

A IA local dá uma pontuação separada a cada casa vazia em quatro direções e soma tudo. Mas, em cada direção, ela conta cinco casas só para a frente a partir daquela casa, como para a direita ou para baixo, e nunca olha para trás. Se as pedras do quatro e do três estão à esquerda e acima da posição de quatro-três, como na imagem acima, nenhuma pedra é visível a partir dessa posição. Em vez disso, olhando para a direita a partir da ponta esquerda da linha, aparecem três pedras, então ela escolhe essa posição.

Além disso, ela só soma a pontuação de cada linha e nunca verifica separadamente se duas linhas se cruzam em um ponto formando um quatro-três. O código que procura jogadas vencedoras só verifica jogadas que formam cinco imediatamente. Por isso, ela só joga um quatro-três quando a direção calha de coincidir.

A verificação de jogadas proibidas é feita separadamente pelo motor, e o motor identifica corretamente como proibido também o duplo três que inclui um três separado. As regras estão sendo vistas corretamente; o que falta é o julgamento de quem joga.

O código real desta etapa

Esta é a parte em que a IA local dá pontuação a uma única casa vazia. Dá para ver que ela conta só as cinco casas à frente.

omook-web/lib/ai.tstypescript · 38 linhas
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;
}

O próximo post é "4. Um plano para o VLM aprender a heurística e para a heurística voltar a ser professora".