盤面評価/石の数
ようやく盤面評価を導入します。ミニマックス法のゲーム木における一番末端のノード(葉)の評価値にあたります。盤面評価はオセロにとって非常に重要な要素です。
盤面評価
盤面評価とはある2つの盤面があったときどっちが有利かを判定することです。たとえば、黒が4隅をとっている盤面は白にとっては非常に不利ですよね?この場合、白にとってこの盤面の評価は低くなります。逆に黒にとっては評価は高くなります。
オセロを強くする2つの要素は
- なるべく深く先読みする
- 盤面を正しく評価する
の2つです。上の2つのどちらが大事かというと断然、盤面評価の方です。いくら先まで読んだとしても盤面を正しく評価できなくては何の意味もないからです。AI(白石)が馬鹿で黒が4隅を取った盤面の方が有利だと思ってたら勝てませんよね?
人間だと大体こっちの方がいいかなぁとあいまいな(直観的な)評価もできますが、コンピュータにはまだできません(人間の直感を解明しようとする研究も行われているのでいずれできるでしょうが)。現在のコンピュータには盤面の評価を数値で表してあげないといけません。今回はそれを実装します。
盤面を評価しようとした場合どんな指標があるでしょうか?オセロの定石や強くなるコツはいろいろあります。たとえば、隅や端を取ると有利とか序盤はあまりひっくり返さないほうがよいとかです。ただ複雑な方法を実装するのは大変なのでここでは簡単な指標を使います。それは、ひっくり返した後の白石の数です。AI(白石)の評価ですので白石が盤面に多いほど高評価です。本当はあまりひっくり返すと最後に逆転される可能性が高いのでよくないんですがね・・・
では実装法を見てみます。前回実装したミニマックス法のところを修正します。ゲーム木の末端で盤面を評価するようにします。 valueBoard()メソッドで盤面を評価して値を返しています。
private int minMax(boolean flag, int level) { // ノードの評価値 int value; // 子ノードから伝播してきた評価値 int childValue; // Min-Max法で求めた最大の評価値を持つ場所 int bestX = 0; int bestY = 0; // ゲーム木の末端では盤面評価 // その他のノードはMIN or MAXで伝播する if (level == 0) { return valueBoard(); // 前回は0でした } ・・・ }
次にvalueBoard()です。
/** * 評価関数。盤面を評価して評価値を返す。盤面にある石の数だけで評価する簡単なもの。 * * @return 盤面の評価値。 */ private int valueBoard() { Counter counter = panel.countStone(); // 白石の数が評価値になる // 白石が多い盤面の方が評価が高いとする return counter.whiteCount; }
盤面にある石の数を数えるのは「勝敗を判定する」で出てきたCounterクラスを使います。白石の数を数えてそのまま返しています。盤面に白石が 3つある場合は3を評価値とし、5つある場合は5をそのまま評価値としています。白石が多ければ多いほど評価が高いわけです。
これで終わりです。実行してみるとAI(白石)は、SEARCH_LEVEL先の盤面で白石の数が最も多くなる場所に石を打とうとします(SEARCH_LEVEL=1の場合はそのターンでなるべく多くひっくり返そうとします)。前回のと比べるとけっこうましになったはずです。
次回はパスの実装をしようと思います。今のままだと最後までプレイできないことに気づきました・・・問題点をまとめると
- パスができないので石が打てなくなったらそこで終わりになってしまう
- AIが先読みしている最中にパスしなくてはいけない状況が起きると対応できない