コード例 #1
0
    /// <summary>
    /// Returns 1 if player 1 is a winner, -1 if player 2 is a winner, and 0 if it is not a win state
    /// </summary>
    /// <param name="gbs"></param>
    /// <returns></returns>
    public static double CheckWinner(this IGameBoardState gbs)
    {
        int p1Count = 0;
        int p2Count = 0;

        foreach (IPieceState piece in gbs.GetAlivePieces())
        {
            if (piece.GetPlayer() == Player.PlayerNumber.Player1 && piece.GetPieceType() == Piece.PieceType.king)
            {
                p1Count++;
            }
            else if (piece.GetPlayer() == Player.PlayerNumber.Player2 && piece.GetPieceType() == Piece.PieceType.king)
            {
                p2Count++;
            }
        }

        if (p1Count == 0 && p2Count == 0)
        {
            throw new System.Exception("Game should end as soon as one player reaches 0 kings.");
        }
        else if (p1Count == 0)
        {
            return(-1.0);
        }
        else if (p2Count == 0)
        {
            return(1.0);
        }
        else
        {
            return(0.0);
        }
    }
コード例 #2
0
    public static double CalculateUtility(this IGameBoardState gbs)
    {
        int p1score = gbs.CalculateRawScore(Player.PlayerNumber.Player1);
        int p2score = gbs.CalculateRawScore(Player.PlayerNumber.Player2);

        return((double)(p1score - p2score) / (double)MaxRawScore(gbs.GetNumLevels()));
    }
コード例 #3
0
    public GameBoardState Clone()
    {
        GameBoardState newGbs = new GameBoardState(this.levels, this.rows, this.cols);

        IGameBoardState igbs = this;

        for (int lvl = 0; lvl < (igbs.GetNumLevels()); lvl++)
        {
            for (int row = 0; row < (igbs.GetNumRows()); row++)
            {
                for (int col = 0; col < (igbs.GetNumCols()); col++)
                {
                    ISpaceState iss = igbs.GetSpaceState(lvl, row, col);
                    newGbs.spaces[lvl][row][col].occupied = iss.IsOccupied();
                    IPieceState ips = iss.Occupier();

                    if (ips != null)
                    {
                        PieceState ps = ips.CreatePieceState();
                        newGbs.spaces[lvl][row][col].occupier = ps;
                        newGbs.AlivePieces.Add(ps);
                        ps.space = newGbs.spaces[lvl][row][col];
                    }
                }
            }
        }

        foreach (IPieceState piece in DeadPieces)
        {
            newGbs.DeadPieces.Add(piece.CreatePieceState());
        }

        return(newGbs);
    }
コード例 #4
0
    private static List <Move> GetPawnMoves(IGameBoardState gameBoard, IPieceState piece)
    {
        List <Move> moves         = new List <Move>();
        int         moveDirection = GetPawnMoveDirection(piece);
        int         pawnStartRow  = GetPawnStartRow(gameBoard, piece);
        int         possibleDistance;


        //check one space ahead, two spaces ahead if on starting row
        if (piece.GetSpaceState().GetRow() == pawnStartRow)
        {
            possibleDistance = 2;
        }
        else
        {
            possibleDistance = 1;
        }

        int[] vOffsets = { -1, 0, 1 };
        foreach (int vOffset in vOffsets)
        {
            for (int i = 1; i <= possibleDistance; i++)
            {
                ISpaceState cSpace = gameBoard.GetSpaceState(piece.GetSpaceState().GetLevel() + vOffset, piece.GetSpaceState().GetRow() + moveDirection * i, piece.GetSpaceState().GetCol());

                if (cSpace != null && cSpace.IsOccupied())
                {
                    break;
                }
                else if (cSpace != null)
                {
                    moves.Add(new Move(piece, cSpace, Move.MoveType.nocap));
                }
            }
        }

        //check diagonals for possible capture
        int[] cOffsets = { -1, 1 };

        foreach (int vOffset in vOffsets)
        {
            foreach (int cOffset in cOffsets)
            {
                ISpaceState cSpace = gameBoard.GetSpaceState(piece.GetSpaceState().GetLevel(), piece.GetSpaceState().GetRow() + moveDirection, piece.GetSpaceState().GetCol() + cOffset);

                if (cSpace != null && cSpace.IsOccupied() && cSpace.Occupier().GetPlayer() != piece.GetPlayer())
                {
                    moves.Add(new Move(piece, cSpace, Move.MoveType.cap));
                }
            }
        }

        //TODO: check for en passant

        return(moves);
    }
コード例 #5
0
    private static List <Move> GetBishopMoves(IGameBoardState gameBoard, IPieceState piece)
    {
        List <Move> moves = new List <Move>();

        //bishop can move diagonally foward-left, foward-right, back-left, back-right, forward-left-up, forward-left-down, forward-right-up, forward-right-down, back-left-up, back-left-down, back-right-up, back-right-down
        int[,] triplets = { { 0, 1, 1 }, { 0, 1, -1 }, { 0, -1, 1 }, { 0, -1, -1 }, { 1, 1, 1 }, { -1, 1, 1 }, { 1, 1, -1 }, { -1, 1, -1 }, { 1, -1, 1 }, { -1, -1, 1 }, { 1, -1, -1 }, { -1, -1, -1 } };

        for (int i = 0; i < triplets.GetLength(0); i++)
        {
            moves.AddRange(GetMovesInDirection(gameBoard, piece, triplets[i, 0], triplets[i, 1], triplets[i, 2]).ToArray());
        }

        return(moves);
    }
コード例 #6
0
    private static List <Move> GetRookMoves(IGameBoardState gameBoard, IPieceState piece)
    {
        List <Move> moves = new List <Move>();

        //rook can move straight forward, back, left, right, up, or down
        int[,] triplets = { { 0, 1, 0 }, { 0, -1, 0 }, { 0, 0, 1 }, { 0, 0, -1 }, { 1, 0, 0 }, { -1, 0, 0 } };

        for (int i = 0; i < triplets.GetLength(0); i++)
        {
            moves.AddRange(GetMovesInDirection(gameBoard, piece, triplets[i, 0], triplets[i, 1], triplets[i, 2]).ToArray());
        }

        return(moves);
    }
コード例 #7
0
    private static int GetPawnStartRow(IGameBoardState gameBoard, IPieceState piece)
    {
        int pawnStartRow;

        if (piece.GetPlayer() == Player.PlayerNumber.Player1)
        {
            pawnStartRow = 1;
        }
        else
        {
            pawnStartRow = gameBoard.GetNumRows() - 2;
        }

        return(pawnStartRow);
    }
コード例 #8
0
    public static void AiMinMaxSearchAsyncStopRequest(this IGameBoardState gbs)
    {
        if (jobStatus != AIMinMaxJobStatus.Started && jobStatus != AIMinMaxJobStatus.Finished)
        {
            throw new System.Exception("Impossible to end an AI job that has not started.");
        }

        lock (jobLock)
        {
            if (jobStatus == AIMinMaxJobStatus.Started)
            {
                jobStatus = AIMinMaxJobStatus.StopRequested;
            }
        }
    }
コード例 #9
0
    public static void AIMinMaxSearchAsyncBegin(this IGameBoardState gbs, int searchLevels, Player.PlayerNumber currentPlayer)
    {
        if (jobStatus != AIMinMaxJobStatus.None)
        {
            throw new System.Exception("Only 1 async job should run at a time. This must be enforced in the game logic.");
        }

        jobStatus      = AIMinMaxJobStatus.Started;
        LevelsSearched = 0;
        StatesSearched = 0;
        MovesSearched  = 0;
        Debug.Log("AI Thread Started");

        //clone gameboardstate to ensure it is not a MonoBehavior - we need to pass it to a new thread
        IGameBoardState gbs_clone = gbs.Clone();

        jobThread = new Thread(() =>
        {
            ///must run at least one level to complete
            jobResult = gbs_clone.AIMinMaxSearch(1, currentPlayer);
            LevelsSearched++;
            //code is in an iterative deepening pattern, but I is initialized to the deepest level for testing
            //usually i should start at 2 and iterate forward
            for (int i = searchLevels; i <= searchLevels; i++)
            {
                AIMinMaxResult res = gbs_clone.AIMinMaxSearch(i, currentPlayer);

                if (jobStatus == AIMinMaxJobStatus.StopRequested)
                {
                    break;
                }

                LevelsSearched++;
                jobResult = res;
            }

            lock (jobLock)
            {
                jobStatus = AIMinMaxJobStatus.Finished;
                Debug.Log("AI Thread Finished");
            }
        });
        jobThread.IsBackground = true;
        jobThread.Start();
    }
コード例 #10
0
    private static List <Move> GetKnightMoves(IGameBoardState gameBoard, IPieceState piece)
    {
        List <Move> moves = new List <Move>();

        //knight moves 2 forward 1 left, 2 forward 1 right, 2 right 1 foward, 2 right 1 back, 2 left 1 forward, 2 left 1 back, 2 back 1 left, 2 back 1 right
        //these moves can happen on the same level, 2 levels up, or 2 levels down only.
        int[,] doublets = { { 2, 1 }, { 2, -1 }, { 1, 2 }, { 1, -2 }, { -1, 2 }, { -1, -2 }, { -2, 1 }, { -2, -1 } };
        int[] levelDirections = { -2, 0, 2 };

        foreach (int levelDirection in levelDirections)
        {
            for (int i = 0; i < doublets.GetLength(0); i++)
            {
                moves.AddRange(GetMovesInDirection(gameBoard, piece, levelDirection, doublets[i, 0], doublets[i, 1], 1).ToArray());
            }
        }

        return(moves);
    }
コード例 #11
0
    private static List <Move> GetMovesInDirection(IGameBoardState gameBoard, IPieceState piece, int levelDirection, int rowDirection, int colDirection, int max = 99)
    {
        List <Move> moves = new List <Move>();

        int levelChange = levelDirection;
        int rowChange   = rowDirection;
        int colChange   = colDirection;

        for (int i = 0; i < max; i++)
        {
            ISpaceState cSpace = gameBoard.GetSpaceState(piece.GetSpaceState().GetLevel() + levelChange, piece.GetSpaceState().GetRow() + rowChange, piece.GetSpaceState().GetCol() + colChange);

            if (cSpace == null)
            {
                break;
            }

            if (cSpace.IsOccupied() && cSpace.Occupier().GetPlayer() != piece.GetPlayer())
            {
                //last move in the sequence is a cap move
                moves.Add(new Move(piece, cSpace, Move.MoveType.cap));
                break;
            }

            if (cSpace.IsOccupied() && cSpace.Occupier().GetPlayer() == piece.GetPlayer())
            {
                break;
            }

            if (!cSpace.IsOccupied())
            {
                moves.Add(new Move(piece, cSpace, Move.MoveType.nocap));
                //amplify move for next iteration
                levelChange += levelDirection;
                rowChange   += rowDirection;
                colChange   += colDirection;
            }
        }

        return(moves);
    }
コード例 #12
0
    private static List <Move> GetQueenMoves(IGameBoardState gameBoard, IPieceState piece)
    {
        List <Move> moves = new List <Move>();

        //queen can move one space in any direction, so generate cartesian product of all possible directions
        int[] rowDirections = { -1, 0, 1 };
        int[] colDirections = { -1, 0, 1 };
        int[] lvlDirections = { -1, 0, 1 };

        foreach (int rowDirection in rowDirections)
        {
            foreach (int colDirection in colDirections)
            {
                foreach (int lvlDirection in lvlDirections)
                {
                    moves.AddRange(GetMovesInDirection(gameBoard, piece, lvlDirection, rowDirection, colDirection).ToArray());
                }
            }
        }

        return(moves);
    }
コード例 #13
0
    public void RenderBoard(IGameBoardState gameBoard)
    {
        int levels = gameBoard.GetNumLevels();
        int rows   = gameBoard.GetNumRows();
        int cols   = gameBoard.GetNumCols();

        for (int i = 0; i < levels; i++)
        {
            for (int j = 0; j < rows; j++)
            {
                for (int k = 0; k < cols; k++)
                {
                    Image img = squareLookup[i][j][k];

                    if (img != null)
                    {
                        if (gameBoard.GetSpaceState(i, j, k).IsOccupied())
                        {
                            Piece.PieceType pieceType = gameBoard.GetSpaceState(i, j, k).Occupier().GetPieceType();
                            img.sprite = PieceSprites[pieceType];

                            img.color = gameBoard.GetSpaceState(i, j, k).Occupier().GetPieceTint();
                            img.Desaturate();
                        }
                        else
                        {
                            img.sprite = BlankSprite;
                            img.color  = Color.white;
                        }
                    }
                    else
                    {
                        Debug.Log("Error: image not found at index " + i + " " + j + " " + k);
                    }
                }
            }
        }
    }
コード例 #14
0
    /// <summary>
    /// Asyncronous end blocks the calling thread until it's finished.
    /// </summary>
    /// <returns></returns>
    public static AIMinMaxResult AIMinMaxSearchAsyncEnd(this IGameBoardState gbs)
    {
        Debug.Log("Tried ending AI thread in state " + jobStatus.ToString());

        if (jobStatus != AIMinMaxJobStatus.Started && jobStatus != AIMinMaxJobStatus.Finished)
        {
            throw new System.Exception("Impossible to end an AI job that has not started.");
        }

        lock (jobLock)
        {
            if (jobStatus != AIMinMaxJobStatus.Finished)
            {
                jobStatus = AIMinMaxJobStatus.StopRequested;
            }
        }

        jobThread.Join();

        jobStatus = AIMinMaxJobStatus.None;

        return(jobResult);
    }
コード例 #15
0
    public static List <Move> GetMoves(IGameBoardState gameBoard, IPieceState piece)
    {
        List <Move> moves;

        switch (piece.GetPieceType())
        {
        case Piece.PieceType.pawn:
            moves = GetPawnMoves(gameBoard, piece);
            break;

        case Piece.PieceType.bishop:
            moves = GetBishopMoves(gameBoard, piece);
            break;

        case Piece.PieceType.king:
            moves = GetKingMoves(gameBoard, piece);
            break;

        case Piece.PieceType.knight:
            moves = GetKnightMoves(gameBoard, piece);
            break;

        case Piece.PieceType.queen:
            moves = GetQueenMoves(gameBoard, piece);
            break;

        case Piece.PieceType.rook:
            moves = GetRookMoves(gameBoard, piece);
            break;

        default:
            moves = new List <Move>();
            break;
        }

        return(moves);
    }
コード例 #16
0
    public static List <Move> GetCaptureMoves(IGameBoardState gameBoard, IPieceState piece)
    {
        List <Move> moves;

        switch (piece.GetPieceType())
        {
        case Piece.PieceType.pawn:
            moves = GetPawnMoves(gameBoard, piece);
            break;

        case Piece.PieceType.bishop:
            moves = GetBishopMoves(gameBoard, piece);
            break;

        case Piece.PieceType.king:
            moves = GetKingMoves(gameBoard, piece);
            break;

        case Piece.PieceType.knight:
            moves = GetKnightMoves(gameBoard, piece);
            break;

        case Piece.PieceType.queen:
            moves = GetQueenMoves(gameBoard, piece);
            break;

        case Piece.PieceType.rook:
            moves = GetRookMoves(gameBoard, piece);
            break;

        default:
            moves = new List <Move>();
            break;
        }

        return(moves.Where(s => s.moveType == Move.MoveType.cap).Select(s => s).ToList());
    }
コード例 #17
0
 public static int CalculateRawScore(this IGameBoardState gbs, Player.PlayerNumber player)
 {
     return(gbs.GetAlivePieces().Where(alive => alive.GetPlayer() == player).Select(alive => ValueMap[alive.GetPieceType()]).Sum());
 }
コード例 #18
0
    /// <summary>
    /// Recursive alpha beta pruning minimax search strategy.
    /// </summary>
    /// <param name="gbs">The Game Board State.</param>
    /// <param name="searchLevels">The number of levels to search (depth)</param>
    /// <param name="currentPlayer">The current player's turn.</param>
    /// <param name="BestMove">The best move found during the search</param>
    /// <param name="alpha">Starting alpha value.</param>
    /// <param name="beta">Starting beta value.</param>
    /// <returns></returns>
    public static AIMinMaxResult AIMinMaxSearch(this IGameBoardState gbs, int searchLevels, Player.PlayerNumber currentPlayer, bool QuiescenceSearch = false, double alpha = -1.0, double beta = 1.0)
    {
        Move BestMove = null;

        double checkWinner = gbs.CheckWinner();

        //cutoff for search (recursive base cases)
        if (checkWinner == -1.0)
        {
            return(new AIMinMaxResult(BestMove, -1.0, 1));
        }

        if (checkWinner == 1.0)
        {
            return(new AIMinMaxResult(BestMove, 1.0, 1));
        }

        if (searchLevels == 0)
        {
            return(new AIMinMaxResult(BestMove, gbs.CalculateUtility(), 1));
        }

        AIMinMaxResult result         = null;
        long           statesSearched = 0;
        //iterate by looking at all possible moves for each piece
        List <IPieceState> pieces = gbs.GetAlivePieces().Where(s => s.GetPlayer() == currentPlayer).Select(s => s).ToList();

        foreach (IPieceState piece in pieces.Shuffle())
        {
            List <Move> moves;
            if (QuiescenceSearch)
            {
                moves = MoveGenerator.GetCaptureMoves(gbs, piece);
            }
            else
            {
                moves = MoveGenerator.GetMoves(gbs, piece);
            }

            MovesSearched += moves.Count;

            //perform each move on a cloned board and search clone recursively, swapping players each turn
            foreach (Move move in moves.Shuffle())
            {
                IGameBoardState clone = gbs.Clone();
                clone.Move(move.piece, move.space);

                if (currentPlayer == Player.PlayerNumber.Player1)
                {
                    result          = clone.AIMinMaxSearch(searchLevels - 1, Player.PlayerNumber.Player2, true, alpha, beta);
                    statesSearched += result.TotalStatesSearched;
                    if (statesSearched > StatesSearched)
                    {
                        StatesSearched = statesSearched;
                    }
                    if (result.AlphaBeta > alpha)
                    {
                        alpha    = result.AlphaBeta;
                        BestMove = move;
                    }

                    //beta cut off
                    if (beta <= alpha)
                    {
                        break;
                    }
                }
                else /* (currentPlayer == Player.PlayerNumber.Player2) */
                {
                    result          = clone.AIMinMaxSearch(searchLevels - 1, Player.PlayerNumber.Player1, true, alpha, beta);
                    statesSearched += result.TotalStatesSearched;
                    if (statesSearched > StatesSearched)
                    {
                        StatesSearched = statesSearched;
                    }
                    if (result.AlphaBeta < beta)
                    {
                        beta     = result.AlphaBeta;
                        BestMove = move;
                    }

                    //alpha cut off
                    if (beta <= alpha)
                    {
                        break;
                    }
                }

                if (jobStatus == AIMinMaxJobStatus.StopRequested && LevelsSearched > 0)
                {
                    searchLevels = 1;
                }
            }

            if (jobStatus == AIMinMaxJobStatus.StopRequested && LevelsSearched > 0)
            {
                searchLevels = 1;
            }
        }

        //no moves found, treat as a base case
        if (BestMove == null)
        {
            return(new AIMinMaxResult(BestMove, gbs.CalculateUtility(), 1));
        }

        //Create a result and return it
        return(new AIMinMaxResult(BestMove, result.AlphaBeta, statesSearched));
    }