ぬるーむ

Unity初心者が誰もが知っているゲームの模倣をしています。個人的な備忘録ですが、入門書を読み終えたばかりの初心者の方は「こんなへなちょこでもいいのか!」「俺の方がうまく作れる」と作成意欲がわいたりするかもしれません。

Unityでリバーシを作ってみた 3


スポンサードリンク

LogicBoardクラス

ビットボードを用いて盤の状態の更新、合法手の取得を行う。
主に下記サイトを参考にした。
オセロをビットボードで実装する - Qiita

public class LogicBoard
{
    private readonly Stack<ulong[]> log = new Stack<ulong[]>();
    private ulong[] bitBoards = new ulong[2];

    public ulong[] CurrentBitBoards
    {
        get
        {
            return (ulong[])bitBoards.Clone();
        }
        set
        {
            bitBoards = value;
        }
    }

    public LogicBoard()
    {
        bitBoards[(int)Stone.ColorType.Black] = 0x0000000000000000;
        bitBoards[(int)(int)Stone.ColorType.White] = 0x0000000000000000;
    }

    //ビットボードをList<Vector2Int>に変換
    public static List<Vector2Int> BitToPoints(ulong bitBoard)
    {
        var mask = 0x8000000000000000;

        var points = new List<Vector2Int>();

        for (int i = 0; i < 64; i++)
        {
            if ((bitBoard & (mask >> i)) != 0)
            {
                var x = i % 8;
                var y = (int)(i / 8);
                points.Add(new Vector2Int(x, y));
            }
        }

        return points;
    }

    //1の数を数える
    public static int CountBit(ulong bitBoard)
    {
        bitBoard = (bitBoard & 0x5555555555555555) + (bitBoard >> 1 & 0x5555555555555555);
        bitBoard = (bitBoard & 0x3333333333333333) + (bitBoard >> 2 & 0x3333333333333333);
        bitBoard = (bitBoard & 0x0f0f0f0f0f0f0f0f) + (bitBoard >> 4 & 0x0f0f0f0f0f0f0f0f);
        bitBoard = (bitBoard & 0x00ff00ff00ff00ff) + (bitBoard >> 8 & 0x00ff00ff00ff00ff);
        bitBoard = (bitBoard & 0x0000ffff0000ffff) + (bitBoard >> 16 & 0x0000ffff0000ffff);
        bitBoard = (bitBoard & 0xffffffffffffffff) + (bitBoard >> 32 & 0xffffffffffffffff);

        return (int)bitBoard;
    }

    public void Move(int x, int y, Stone.ColorType color)
    {
        var mask = 0x8000000000000000;
        var move = mask >> x + y * 8;

        bitBoards[(int)color] |= move;
    }

    //合法手を取得
    public ulong GetMovableBitBoard(Stone.ColorType color)
    {
        var current = bitBoards[(int)color];
        var opponent = bitBoards[(int)color ^ 1];
        //左右のセルを除いた相手の盤の状態
        var horizontalWatcher = opponent & 0x7e7e7e7e7e7e7e7e;
        //上下のセルを除いた相手の盤の状態
        var verticalWatcher = opponent & 0x00ffffffffffff00;
        //上下左右のセルを除いた相手の盤の状態
        var allSideWatcher = opponent & 0x007e7e7e7e7e7e00;
        var empty = ~(current | opponent);

        //8方向それぞれの合法手を調べ重ね合わせる
        var movable = GetMovableBitBoardInLine(current, empty, horizontalWatcher,
            c => c << 1);

        movable |= GetMovableBitBoardInLine(current, empty, horizontalWatcher,
            c => c >> 1);

        movable |= GetMovableBitBoardInLine(current, empty, verticalWatcher,
            c => c << 8);

        movable |= GetMovableBitBoardInLine(current, empty, verticalWatcher,
            c => c >> 8);

        movable |= GetMovableBitBoardInLine(current, empty, allSideWatcher,
            c => c << 7);

        movable |= GetMovableBitBoardInLine(current, empty, allSideWatcher,
            c => c >> 7);

        movable |= GetMovableBitBoardInLine(current, empty, allSideWatcher,
            c => c << 9);

        movable |= GetMovableBitBoardInLine(current, empty, allSideWatcher,
            c => c >> 9);

        return movable;
    }

    public ulong Reverse(ulong move, Stone.ColorType color)
    {
        int c = (int)color;
        var opponent = bitBoards[c ^ 1];
        ulong allReversed = 0;
        ulong lineReversed;

        //一方向ずつ反転できる石を探し重ね合わせる
        for (var i = 0; i < 8; i++)
        {
            var next = ShiftBitboard(move, i);
            lineReversed = 0;

            while (next != 0 && (next & opponent) != 0)
            {
                lineReversed |= next;
                next = ShiftBitboard(next, i);
            }

            if ((next & bitBoards[c]) != 0)
            {
                allReversed |= lineReversed;
            }
        }

        //石を置き、反転させる。
        bitBoards[c] |= move | allReversed;
        bitBoards[c ^ 1] ^= allReversed;

        return allReversed;
    }

    public ulong Reverse(int x, int y, Stone.ColorType color)
    {
        var mask = 0x8000000000000000;
        var move = mask >> (x + y * 8);

        return Reverse(move, color);
    }

    public void Log()
    {
        log.Push(CurrentBitBoards);
    }

    public void Undo()
    {
        if (log.Count == 0) throw new Exception("Undo Error");
        CurrentBitBoards = log.Pop();
    }

    //任意の方向にある合法手を取得
    private ulong GetMovableBitBoardInLine(ulong current, ulong empty,
       ulong watcher, Func<ulong, ulong> shift)
    {
        ulong temp = watcher & shift(current);

        for (var i = 0; i < 5; i++)
        {
            temp |= watcher & shift(temp);
        }

        return empty & shift(temp);
    }

    //任意の方向にシフト
    private ulong ShiftBitboard(ulong current, int dir)
    {
        ulong next = 0;

        switch (dir)
        {
            case 0:
                next = 0xffffffffffffff00 & (current << 8);
                break;
            case 1:
                next = 0x00ffffffffffffff & (current >> 8);
                break;
            case 2:
                next = 0xfefefefefefefefe & (current << 1);
                break;
            case 3:
                next = 0x7f7f7f7f7f7f7f7f & (current >> 1);
                break;
            case 4:
                next = 0x7f7f7f7f7f7f7f00 & (current << 7);
                break;
            case 5:
                next = 0x00fefefefefefefe & (current >> 7);
                break;
            case 6:
                next = 0xfefefefefefefe00 & (current << 9);
                break;
            case 7:
                next = 0x007f7f7f7f7f7f7f & (current >> 9);
                break;
        }

        return next;
    }
}