パスを実装する
前回までのプログラムはパスを実装していなかったため最後までプレイできませんでした。そこで今回はパスを実装してみます。
石を打てる場所を数える
どこにも石が打てなくなった場合にパスをします。というわけでまず石を打てる場所の数を数えるメソッド countCanPutDownStone()を作ります。
/** * 石が打てる場所の数を数える。 * @return 石が打てる場所の数。 */ public int countCanPutDownStone() { int count = 0; for (int y = 0; y < MainPanel.MASU; y++) { for (int x = 0; x < MainPanel.MASU; x++) { if (canPutDown(x, y)) { count++; } } } return count; }
そんなに難しくはありません。盤面のすべての場所でcanPutDown()を呼び出して石を打てるか調べ、石が打てる場合はcountをインクリメントしています。もし石がどこにも打てなかったら0が返されるわけです。
パスの実装
プレイヤーがパスする場合とAIがパスする場合を分けて考えます。まずプレイヤーがパスをする場合です。AIクラスのcompute()を見てください。
public void compute() { // AIが石を打つ // 手番を変える panel.nextTurn(); // プレイヤーがパスの場合はもう一回 if (panel.countCanPutDownStone() == 0) { System.out.println("Player PASS!"); panel.nextTurn(); compute(); } }
AIが石を打ち、手番を変えたところでcountCanPutDownStone()を呼び出しています。もし0の場合、プレイヤーは石を打てる場所がないのでパスします。次はもう一度AIが打つので手番を変えてもう一度compute()を呼び出します。次はAIがパスする場合です。MainPanelクラスのmouseClicked()を見てください。
public void mouseClicked(MouseEvent e) { // プレイヤーが石を打つ // 手番を変える nextTurn(); // AIがパスの場合はもう一回 if (countCanPutDownStone() == 0) { System.out.println("AI PASS!"); nextTurn(); return; } else { // パスでなかったらAIが石を打つ ai.compute(); } }
プレイヤーが石を打ち、手番を変えたところでcountCanPutDownStone()を呼び出しています。もし0の場合、AIは石を打てる場所がないのでパスします。次はもう一度プレイヤーが打つので手番を変えてreturnを呼び出します。
先読み時のパスの扱い
ミニマックス法のところで書きましたがAIはミニマックス法を使って盤面を先読みします。この盤面の先読みの最中でもパスが起きる場合があります。一般的にパスをどう扱うのかよくわからないのですが、ここではとりあえず先読みを中止して盤面評価値をそのまま返すことにします。
// もしパスの場合はそのまま盤面評価値を返す if (panel.countCanPutDownStone() == 0) { return valueBoard(); }
パスでもそのまま先読みを続けるという方法も考えましたが大改造が必要そうなのでやめました・・・とりあえずこれで最後までプレイできると思います。何回かやってみましたがAIは結構強いです。じっくり考えて打てば勝てますが適当に打ってると負けます・・・自分が弱いだけか・・・実はめったに起こりませんが。パスが2回続いてどちらも打てなくなった場合にゲームが終了しないことがわかりました。今度(できたら)直します。