五目並べ AI:必殺判定と探索の分業

素の alpha-beta では五目で必殺手を逃します。勝ち・受け・活四の三段で確定する手を先に処理し、残りだけを反復深化と置換表に渡す構成にしました。

五目並べ の AI は当初、深さ 4 の素の alpha-beta だけでした。結果は散々で、相手の活三を放置し、自分の勝ち手を見逃すこともありました。深さが足りないと、評価関数が「三手後に負ける」を普通の局面として読んでしまうためです。

解決策は深さを増やすことではなく、盤面だけで確定する手を探索から外に出すことでした。

三層、順番は固定

順 判定 動作
1 自分が五連を作れるか 即打つ、探索しない
2 相手が次に五連を作れるか 受ける
3 自分/相手が活四を作れるか 打つ/潰す
4 いずれも無し alpha-beta に渡す

前の三つが必殺判定層です。共通する性質は「盤面の形だけで決まり、後の展開に依存しない」こと。これを探索に渡すと、確定事実を再検証するためにノードを浪費し、しかも浅い深さでは検証しきれず間違えます。

探索は不確実性のためにある。盤面が既に語っていることを再発見させる場所ではない。

活四を独立に判定する理由

活四(.XXXX.)は両端が空いた四連で、相手は一子では両端を止められません。つまり出た時点で勝ちです。

第三層で単に「四連があるか」を見ると、片端を塞がれた棒四も数えてしまいます。相手に一手で消されるので、打てば手番の無駄遣いです。よって形の文字列で判定します。011110 が活四、011112 と 211110 は棒四です。

四方向それぞれ 9 マスの窓を走査し、0/1/2 の文字列にして照合します。

const PATTERN = {
  FIVE: 1e7,
  OPEN_FOUR: 3e5,
  FOUR: 2.6e4,
  OPEN_THREE: 2e4,
  THREE: 1100,
  OPEN_TWO: 600,
  TWO: 90,
  ONE: 6,
};

この数値は任意ではありません。五連が他全部の合計を上回ること、そして活四が活三二つより大きいこと。後者を破ると、必勝の活四を二つの脅威と交換してしまいます。

探索層の標準部品三点

  • 反復深化:深さ 1 から順に探索し、完了した最後の層を保持。時間切れでも途中の層ではなく、直前の完了層の結論が使えます。
  • 置換表:Zobrist ハッシュを鍵に、深さ・評価・境界種別(正確/上界/下界)を保存。
  • 候補の絞り込み:既存の石の近傍のみ。225 点のうち検討に値するのは通常 20 点未満です。

見落としやすい点が一つ。時間予算は探索の内部で確認します。外側の setTimeout では駄目です。1024 ノードごとに時計を見て中断フラグを立て、層ごとに巻き戻します。そうしないと途中で切れた層の評価が、不完全な木から計算されてしまいます。

難易度とは予算のこと

五段階は一つのエンジンと五組のパラメータです。

難易度 深さ 時間 揺らぎ 候補幅
1 初心者 1 90ms 0.9 8
2 入門 2 200ms 0.5 10
3 中級 4 450ms 0.18 12
4 上級 6 900ms 0.04 14
5 達人 8 1800ms 0 16

「揺らぎ」は上位候補からのランダム選択です。深さを下げるだけでは弱くなりません。必殺判定層が残るので、初心者でも五連の機会は逃しません。

AI に考えを語らせる

調整中に必ず困るのが「なぜこの手を選んだのか」です。探索の記録自体は簡単で、層ごとの深さ・最善手・評価と、根の候補数件を残すだけです。

本当に手間がかかったのは評価値でした。alpha-beta が返すのは殆ど境界値、つまり「X より良くはない」です。それを表示すると候補が全部同じ数字になり、壊れているように見えます。そこでツリーを開いた時だけ、根の候補を全幅探索し直して正確な値を取ります。遅くなりますが、実際に見たい人だけが払うコストです。

利用者に見せる数字は本物でなければならない。境界値で組んだ「過程」は、過程が無いより悪い。

まとめ

現在の強さは「活三を尊重する」程度です。本物のエンジンはもっと多くを持ちます(VCF/VCT 探索、評価の学習)。しかしブラウザゲームでは、深さを 4 から 8 に上げるより必殺判定層の方がはるかに効きました。勝敗は大抵、短い手順で決まるからです。

← 記事一覧に戻る

コメント

…