コンピュータとオセロ対戦26　～JavaでBitBoard～

https://qiita.com/tt_and_tk/items/068f2afde6db637e189f

[前回](https://qiita.com/tt_and_tk/items/9279ee9b1cab8efe529e)

# 今回の目標
前回、データ集めも機械学習もPythonで行うと書きましたが、Javaにも機械学習ライブラリがあると知りやっぱりJavaで行うことにしました。
今までc++でデータ集め、Pythonで学習と分けて行っていた理由は、

* c++は最高クラスの実行速度があるものの機械学習ライブラリがない -> データ集めには向くが学習はできない
* Pythonは実行速度こそ遅いものの機械学習ライブラリとそれに関する情報が豊富 -> データ集めには向かないが学習は得意

という両極端な性能があるからでした。
しかしJavaなら、最高クラスとまではいかないものの高速な実行が可能で、かつPythonほど情報が豊富ではないものの機械学習ライブラリが存在するという中間的な性能を持っています。そのため今回目指しているような、「データをいったんcsvなどで保存することなく、一つのプログラムの中でデータ集めから学習まで行う」という場合には最適な言語と判断しました。
さらに、個人的にJavaには興味があり、いつかはやってみたいと思っていました。
こういった二つの理由で、[前回](https://qiita.com/tt_and_tk/items/9279ee9b1cab8efe529e)作ったプログラムは無駄になってしまいますが、まずはデータ集めのためJavaでBitBoardオセロを作ることにしました。

# ここから本編
Javaは慣れない言語なので、c++やPythonで行ったような、コンストラクタで思考方法を指定する方法が分かりませんでした。
そのため、思考方法によらず共通する関数だけを集めた親クラスと、思考方法及びplay関数のみ乗せた子クラスでオセロを表現することにしました。
今回使用するクラスは以下の四つです。

* BitBoard　put関数やcheck関数など、思考方法によらず共通する関数を集めた親クラス
* oseroRandRand　ランダムに置く関数とplay関数のみ乗せた、ランダム対ランダムの試合が行えるクラス
* oseroHumanHuman　人に聞く関数をplay関数のみ乗せた、人対人の試合が行えるクラス
* run　実際にインスタンス化し実行するクラス

以下が実際のプログラムの中身ですが、正直c++で書いたものをほぼ書き写しただけなので説明することはあまりありません。

## BitBoard
intが32ビットだったにもかかわらず、cと違い暗黙のキャストが行われなかったのでintからlongを生成する関数を追加しました。
また、日本語が使えなかったので黒石を「@」、白石を「O」で表現しています。
他はc++と全く同じです。

```BitBoard.java
package BitBoard;

public class BitBoard {
    public long bw[] = new long[2];
    public boolean turn = false;

    public BitBoard(){
        setup();
    }

    public boolean check_all(){
        int i, j;

        for (i = 0; i < 8; i++){
            for (j = 0; j < 8; j++){
                if (check(i, j, bw, turn)) return true;
            }
        }

        return false;
    }

    public boolean check(int line, int col, long[] now, boolean turn){
        long place = 1;
        place = place << (line << 3) + col;

        if ((now[0] & place) != 0) return false;
        if ((now[1] & place) != 0) return false;

        int line_x, col_y;
        int x, y;
        int my, opp;

        if (turn){
            my = 1; opp = 0;
        }else{
            my = 0; opp = 1;
        }

        for (x = -1; x <= 1; x++){
            for (y = -1; y <= 1; y++){
                if (x == 0 && y == 0){
                    continue;
                }
                line_x = line + x;
                col_y = col + y;
                place = (long)1 << (line_x << 3) + col_y;
                while ((now[opp] & place) != 0
                       && 0 <= x + line_x && x + line_x < 8
                       && 0 <= y + col_y && y + col_y < 8){
                    line_x += x;
                    col_y += y;
                    place = (long)1 << (line_x << 3) + col_y;
                    if ((now[my] & place) != 0){
                        return true;
                    }
                }
            }
        }

        return false;
    }

    public void put(int line, int col, long[] now, boolean turn){
        int x, y;
        int line_x, col_y;
        int my, opp;
        long inver, place;

        if (turn){
            my = 1; opp = 0;
        }else{
            my = 0; opp = 1;
        }

        now[my] += (long)1 << (line << 3) + col;

        for (x = -1; x <= 1; x++){
            for (y = -1; y <= 1; y++){
                if (x == 0 && y == 0){
                    continue;
                }
                inver = 0;
                line_x = line + x;
                col_y = col + y;
                place = (long)1 << (line_x << 3) + col_y;
                while ((now[opp] & place) != 0
                       && 0 <= x + line_x && x + line_x < 8
                       && 0 <= y + col_y && y + col_y < 8){
                    inver += place;
                    line_x += x;
                    col_y += y;
                    place = (long)1 << (line_x << 3) + col_y;
                }
                if ((now[my] & place) != 0){
                    now[my] += inver;
                    now[opp] -= inver;
                }
            }
        }
    }

    public void count_last(){
        int black = popcount(bw[0]);
        int white = popcount(bw[1]);

        System.out.printf("black: %d, white: %d\n", black, white);

        if (black > white){
            System.out.println("black win!");
        }else if (white > black){
            System.out.println("white win!");
        }else{
            System.out.println("draw!");
        }
    }

    public int popcount(long now){
        now = now - ((now >> 1) & doubling(0x55555555));
        now = (now & doubling(0x33333333)) + ((now >> 2) & doubling(0x33333333));
        now = (now + (now >> 4)) & doubling(0x0f0f0f0f);
        now = now + (now >> 8);
        now = now + (now >> 16);
        now = now + (now >> 32);
    
        return (int)now & 0x7f;
    }

    public void printb(){
        int num = 0;
        long place = 1;

        System.out.print("\n  ");
        for (int i = 0; i < 8; i++) System.out.printf(" %d ", i + 1);

        System.out.println("\n -------------------------");
        while (place != 0){
            if (num % 8 == 0) System.out.printf("%d", (num >> 3) + 1);
            if ((bw[0] & place) != 0) System.out.print("|@ ");
            else if ((bw[1] & place) != 0) System.out.print("|O ");
            else System.out.print("|  ");
            if (num % 8 == 7) System.out.println("|\n -------------------------");
            num++;
            place = place << 1;
        }
    }

    public void setup(){
        turn = false;
        bw[0] = shift32(0x8) + 0x10000000;
        bw[1] = shift32(0x10) + 0x08000000;
    }

    public long doubling(int num){
        return ((long)num << 32) + num;
    }

    public long shift32(int num){
        return (long)num << 32;
    }
}
```

## oseroRandRand
特筆することはなし。

```oseroRandRand.java
package BitBoard;

import java.util.Random;

public class oseroRandRand extends BitBoard{
    Random rand = new Random();

    public void play(){
        boolean can = true;
        boolean old_can = true;

        printb();

        while ((can = check_all()) || old_can){
            if (can){
                random();
                printb();
            }
            turn = !turn;
            old_can = can;
        }

        count_last();
    }
   
    public void random(){
        int line, col;

        do{
            line = rand.nextInt(8);
            col = rand.nextInt(8);
        }while (!check(line, col, bw, turn));

        put(line, col, bw, turn);
    }
}
```

## oseroHumanHuman
こちらも特筆することはなし。

```oseroHumanHuman.java
package BitBoard;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class oseroHumanHuman extends BitBoard{
    InputStreamReader isr = new InputStreamReader(System.in);
    BufferedReader br = new BufferedReader(isr);

    public void play(){
        boolean can = true;
        boolean old_can = true;

        printb();

        while ((can = check_all()) || old_can){
            if (can){
                if (turn){
                    System.out.println("whtie turn.");
                }else{
                    System.out.println("black turn.");
                }
                human();
                printb();
            }else{
                System.out.print("not place. once ");
            }
            turn = !turn;
            old_can = can;
        }

        count_last();
    }

    public void human(){
        int line = -1, col = -1;
        String line_s, col_s;

        while (true){
            try{
                System.out.print("line: ");
                line_s = br.readLine();
                System.out.print("col: ");
                col_s = br.readLine();

                line = Integer.parseInt(line_s);
                col = Integer.parseInt(col_s);
                line--; col--;
            }catch (Exception e){
                System.out.println("error. once choose.");
                continue;
            }

            if (check(line, col, bw, turn)){
                break;
            }else{
                System.out.println("can't put that place. once choose.");
            }
        }

        put(line, col, bw, turn);
    }
}
```

## run
これだけ別フォルダにあります。

```run.java
import BitBoard.*;

public class run{
    static oseroRandRand run = new oseroRandRand();
    // static oseroHumanHuman run = new oseroHumanHuman();
    
    public static void main(String[] str){
        run.setup();
        run.play();
    }
}
```

# フルバージョン

https://github.com/TTaisei/osero/tree/main/mecha_learn
# 次回は
次回こそ機械学習やります。

[次回](https://qiita.com/tt_and_tk/items/98a06feb9b3c66fa2e89)

https://qiita.com/tt_and_tk/items/068f2afde6db637e189f