先の先を読む思考ルーチン

開発目標

このページの目標は、思考ルーチンをまあまあな賢こさにすることです。ネストを利用しようと思います。

先の先を読むために

先の先を読むための準備として、パスの判定を実装します。そして、think()の中でthink()を呼び出すネストと呼ばれる技術を使うことで、先の先を読みます。読みの深さはすぐに変更できるようにbestDepthという変数に保存しておくことにします。

ReversiAI.java

//リバーシ思考ルーチン
//2011/09/25  by boco.hp3200.com
//--------------------------------------------------------------------------
package com.hp3200.boco.reversi;

public class ReversiAI {
    //private final int WALL = -1;
    private final int BLANK = 0;
    private final int PLAYER = 1;
    private final int COM = 2;
    private final int[] MOVE = {-11, -10, -9, -1, 1, 9, 10, 11};
    private final int[] POINT_MAP = {    
        0,  0,  0, 0, 0, 0, 0,  0,  0,0,
        0,150,-20,20,10,10,20,-20,150,0,
        0,-20,-30,-5,-5,-5,-5,-30,-20,0,
        0, 20, -5,10, 5, 5,10, -5, 20,0,
        0, 10, -5, 5, 3, 3, 5, -5, 10,0,
        0, 10, -5, 5, 3, 3, 5, -5, 10,0,
        0, 20, -5,10, 5, 5,10, -5, 20,0,
        0,-20,-30,-5,-5,-5,-5,-30,-20,0,
        0,150,-20,20,10,10,20,-20,150,0,
        0,  0,  0, 0, 0, 0, 0,  0,  0,0
    };
    
    private int[] board = new int[100];
    private int bestPlace;
    private int bestDepth = 3;

    //コンストラクタ
    public ReversiAI() {
    }

    //処 理:盤を初期化する
    //引 数:startBoard……初めの盤
    //戻り値:なし
    public void setBoard(int[] startBoard) {
        for(int i=0;i<100;i++) {
            board[i] = startBoard[i];
        }
    }

    //処 理:最善手を返す
    //引 数:なし
    //戻り値:最善手
    public int getPlace() {
        return bestPlace;
    }
    
    //処 理:最善手を考える
    //引 数:myCoin……PLAYER or COM、depth……現在の深さ(1以上)
    //戻り値:最高点数
    public int think(int myCoin, int depth) {
        int yourCoin = PLAYER;
        int i, j;
        int point;
        int bestPoint = -100000;
        int[] baseBoard = new int[100];
        
        //相手の石を設定&盤を保存
        if(myCoin==PLAYER) yourCoin = COM;
        for(i=0;i<100;i++) {
            baseBoard[i] = board[i];
        }
        
        //最高点数を決定
        for(i=11;i<=88;i++) {
            if(board[i]==BLANK) {
                if(reverse(myCoin,i)==true) {
                    if(depth==bestDepth) {
                        point = evaluationFunction(myCoin);
                    } else {
                        if(isPass(yourCoin)==false) {
                            point = (-1)*think(yourCoin,depth+1);
                        } else {
                            if(isPass(myCoin)==false) {
                                point = think(myCoin,depth+1);
                            } else {
                                point = evaluationFunction(myCoin);
                            }
                        }
                    }
                    if(bestPoint<point) {
                        bestPoint = point;
                        if(depth==1) bestPlace = i;
                    }
                    for(j=0;j<100;j++) {
                        board[j] = baseBoard[j];
                    }
                }
            }
        }

        //最高点数を返す
        return bestPoint;
    }

    //処 理:そこに置けるなら、置いて裏返す
    //引 数:myCoin……PLAYER or COM、p……置く場所
    //戻り値:置けなかったらfalse
    boolean reverse(int myCoin, int p) {
        …略…
    }

    //処 理:パスか調べる
    //引 数:myCoin……PLAYER or COM
    //戻り値:パスならtrue
    public boolean isPass(int myCoin) {
        int yourCoin = PLAYER;
        int i, j, p;
        boolean pass = true;
        
        //相手の石を設定
        if(myCoin==PLAYER) yourCoin = COM;
        
        //置けるか判断> 
        for(p=11;p<=88;p++) {
            if(board[p]==BLANK) {
                for(i=0;i<8;i++) {
                    if(board[p+MOVE[i]]==yourCoin) {
                        for(j=2;j<8;j++) {
                            if(board[p+MOVE[i]*j]==myCoin) {
                                pass = false;
                                break;
                            } else if(board[p+MOVE[i]*j]==yourCoin) {
                            } else {
                                break;
                            }
                        }
                    }
                    if(pass==false) break;
                }
            }
            if(pass==false) break;
        }
        
        //パスならtrueを返す
        return pass;
    }
    
    //処 理:評価関数
    //引 数:myCoin……PLAYER or COM
    //戻り値:点数
    public int evaluationFunction(int myCoin) {
        …略…
    }

}

そして、think()の引数が変わったので、ReversiViewクラスの方も少し変更します。このシステムでは、何もしない状態が深さ0で、次が深さ1だと考えているので、ai.think(COM,1)と1を指定します。

ReversiView.java

package com.hp3200.boco.reversi;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.View;

class ReversiView extends View {
    …略…
    
    //描写処理
    @Override
    public void onDraw(Canvas c) {
        …略…
        
        switch(page) {
        …略…
        case COM:
            ai.setBoard(board);
            ai.think(COM,1);
            place = ai.getPlace();
            //ページ移動
            page = REVERS;
            invalidate();
            break;
        …略…
        }
    }
    
    …略…
}

開発結果

これで思考ルーチンがまあまあな賢さになったと思います。確認してみます。

うん、まあまあな賢さになってくれました。ここからは自由ですが、評価関数に小さなアイデアをいくつか導入することによって、もっと賢くしてみようと思います。