Beispiel #1
0
        /// <summary>
        /// Returns a list of all pieces belonging to a current player that are still on the board
        /// </summary>
        public List <Piece> GetPlayersPieces(Program.Square currentPlayer)
        {
            var currentPlayersPieces = new List <Piece>();

            // set playersKing to either RedKing or WhiteKing
            var playersKing = currentPlayer == Program.Square.Red ? Program.Square.RedKing : Program.Square.WhiteKing;

            // scan the board to identify squares where current player's pieces are
            for (var row = 0; row < Size; row++)
            {
                for (var col = 0; col < Size; col++)
                {
                    // if current row,column coordinate within the board holds current player's piece
                    if (_board[row, col] == currentPlayer)
                    {
                        currentPlayersPieces.Add(new Piece(currentPlayer, false, row, col));
                    }
                    else if (_board[row, col] == playersKing)
                    {
                        currentPlayersPieces.Add(new Piece(playersKing, true, row, col));
                    }
                }
            }

            return(currentPlayersPieces);
        }
Beispiel #2
0
 public Piece(Program.Square type, bool isKing, int x, int y)
 {
     Type   = type;
     IsKing = isKing;
     X      = x;
     Y      = y;
 }
Beispiel #3
0
        /// <summary>
        /// Changes the location of a piece using the move provided
        /// </summary>
        public void MovePiece(Move move)
        {
            var fromRow = move.FromRow;
            var fromCol = move.FromCol;
            var toRow   = move.ToRow;
            var toCol   = move.ToCol;

            Program.Square pieceType = _board[fromRow, fromCol];

            // if move is more than 2 rows forwards/backwards, then it's illegal
            if (toRow - fromRow > 2 || toRow - fromRow < -2)
            {
                Console.WriteLine("You can't move further than 2 rows");
            }
            else
            {
                // if the piece has reached either first or last row, it becomes a king
                if (toRow == 0 && pieceType == Program.Square.Red || toRow == 7 && pieceType == Program.Square.White)
                {
                    SetKing(pieceType, toRow, toCol);
                    _board[fromRow, fromCol] = Program.Square.EmptyDark;
                }
                else
                {
                    // move piece and replace it with an empty dark square
                    _board[toRow, toCol]     = _board[fromRow, fromCol];
                    _board[fromRow, fromCol] = Program.Square.EmptyDark;
                }
            }
        }
Beispiel #4
0
        /// <summary>
        /// Checks whether provided move is valid
        /// </summary>
        private bool IsMovePermitted(Program.Square currentPiece, Move move)
        {
            // can't move outside the board
            if (move.ToRow < 0 | move.ToRow > 7 | move.ToCol < 0 | move.ToCol > 7)
            {
                return(false);
            }

            // can't move onto a light square
            if (_board[move.ToRow, move.ToCol] == Program.Square.EmptyLight)
            {
                return(false);
            }

            if (currentPiece == Program.Square.Red)
            {
                // can't move backwards
                if (move.ToRow >= move.FromRow)
                {
                    return(false);
                }
                // can only move to an empty dark square
                if (GetPieceAt(move.CoordinateTo) != Program.Square.EmptyDark)
                {
                    return(false);
                }

                return(true);
            }

            if (currentPiece == Program.Square.White)
            {
                // can't move backwards
                if (move.ToRow <= move.FromRow)
                {
                    return(false);
                }
                // can only move to an empty dark square
                if (GetPieceAt(move.CoordinateTo) != Program.Square.EmptyDark)
                {
                    return(false);
                }

                return(true);
            }

            if (currentPiece == Program.Square.WhiteKing || currentPiece == Program.Square.RedKing)
            {
                // can only move to an empty dark square
                if (GetPieceAt(move.CoordinateTo) != Program.Square.EmptyDark)
                {
                    return(false);
                }

                return(true);
            }

            return(false);
        }
Beispiel #5
0
        /// <summary>
        /// Makes a regular red or white piece a king once it's reached the opposite end row of the board
        /// </summary>
        private void SetKing(Program.Square currentPlayer, int rowTo, int colTo)
        {
            if (currentPlayer == Program.Square.Red && rowTo == 0)
            {
                _board[rowTo, colTo] = Program.Square.RedKing;
            }

            if (currentPlayer == Program.Square.White && rowTo == 7)
            {
                _board[rowTo, colTo] = Program.Square.WhiteKing;
            }
        }
Beispiel #6
0
        /// <summary>
        /// Returns current state of the board, ie. how pieces are positioned at this particular stage
        /// </summary>
        public Program.Square[,] GetState()
        {
            var newArr = new Program.Square[8, 8];

            Array.Copy(_board, newArr, _board.Length);

            /*                for (var row = 0; row < 8; row++)
             *              for (var col = 0; col < 8; col++)
             *                  newArr[row, col] = _board[row, col];*/

            return(newArr);
        }
Beispiel #7
0
        /// <summary>
        /// Evaluates all possible moves that AI player can make during its current turn in order to
        /// find the highest score associated with one of these moves
        /// </summary>
        /// <param name="board"></param>
        /// <param name="state"></param>
        /// <param name="depth"></param>
        /// <param name="alpha"></param>
        /// <param name="beta"></param>
        /// <returns>Returns the highest scored out of all possible moves</returns>
        private double Negamax(Board board, State state, int depth, double alpha, double beta)
        {
            List <Piece>   pieces     = board.GetPlayersPieces(state.CurrentPlayer);
            HashSet <Move> legalJumps = board.GetLegalJumps(pieces);
            HashSet <Move> legalMoves = board.GetLegalMoves(pieces);

            legalMoves.UnionWith(legalJumps);

            // evaluate the state of the board in order to obtain the score if depth has reached zero
            if (depth == 0)
            {
                return(Evaluate(state));
            }

            Program.Square currentPlayer = state.CurrentPlayer;
            Board          boardCopy;
            State          newState;

            foreach (var move in legalMoves)
            {
                boardCopy = board.DeepCopy();

                if (move.IsJump)
                {
                    boardCopy.DoJump(move);
                }
                else
                {
                    boardCopy.MovePiece(move);
                }

                newState = new State(boardCopy.GetState(), Program.ChangeTurn(currentPlayer));
                double newScore = -Negamax(boardCopy, newState, depth - 1, -beta, -alpha);

                // alpha-beta cut-off
                if (newScore >= beta)
                {
                    return(newScore);
                }
                if (newScore > alpha)
                {
                    alpha = newScore;
                }
            }

            return(alpha);
        }
Beispiel #8
0
        /// <summary>
        /// Evaluates the state of the board provided by Negamax method by calculating the difference in number of
        /// both players' pieces, either from Red or from White player's perspective
        /// </summary>
        /// <param name="state"></param>
        /// <returns>Returns the score for a move which is the difference between the number of players' pieces
        /// left on the board once the move has been applied</returns>
        private static int Evaluate(State state)
        {
            int red   = 0;
            int white = 0;

            Program.Square currentPlayer = state.CurrentPlayer;
            Program.Square[,] board = state.BoardState;

            for (var row = 0; row < 8; row++)
            {
                for (var col = 0; col < 8; col++)
                {
                    if (board[row, col] == Program.Square.Red)
                    {
                        red++;
                    }
                    if (board[row, col] == Program.Square.White)
                    {
                        white++;
                    }
                    if (board[row, col] == Program.Square.RedKing)
                    {
                        red += 2;
                    }
                    if (board[row, col] == Program.Square.WhiteKing)
                    {
                        white += 2;
                    }
                }
            }

            // return the difference between the number of red and white pieces if calculating for red player
            if (currentPlayer == Program.Square.Red || currentPlayer == Program.Square.RedKing)
            {
                return(red - white);
            }

            // return the difference between the number of white and red pieces if calculating for white player
            return(white - red);
        }
Beispiel #9
0
        /// <summary>
        /// Retrieves the best move that AI player can perform that is associated with the highest score returned
        /// by Negamax method
        /// </summary>
        /// <param name="board"></param>
        /// <param name="state"></param>
        /// <returns>Returns the best move out of all legal moves for AI player</returns>
        public Move GetBestMove(Board board, State state)
        {
            List <Piece>   pieces     = board.GetPlayersPieces(state.CurrentPlayer);
            HashSet <Move> legalJumps = board.GetLegalJumps(pieces);
            HashSet <Move> legalMoves = board.GetLegalMoves(pieces);

            legalMoves.UnionWith(legalJumps);

            var bestMoves = new List <Move>();

            // analyze all possible moves three turns ahead while searching for best move for this turn
            int    depth = 3;
            double alpha = Double.MinValue;

            Program.Square currentPlayer = state.CurrentPlayer;
            Board          boardCopy;
            State          newState;
            var            rand = new Random();

            foreach (var move in legalMoves)
            {
                // make a deep copy of the board to perform a move without changing the original board
                boardCopy = board.DeepCopy();

                if (move.IsJump)
                {
                    boardCopy.DoJump(move);
                }
                else
                {
                    boardCopy.MovePiece(move);
                }

                // create a new state of the game using copy of the original board after move has been performed
                newState = new State(boardCopy.GetState(), Program.ChangeTurn(currentPlayer));

                // get score for the move that has been applied to copy of the board
                double newScore = -Negamax(boardCopy, newState, depth - 1, Double.MinValue, -alpha);

                //Console.WriteLine("From {0} To {1} = {2}", move.CoordinateFrom, move.CoordinateTo, newScore);

                // if new score is better than alpha, it becomes a new alpha (highest score found so far)
                if (newScore > alpha)
                {
                    alpha = newScore;
                    bestMoves.Clear();
                    bestMoves.Add(move);
                }
                // if score for this move is equal to score of best move found so far, add it to the list of best moves
                else if (newScore == alpha)
                {
                    bestMoves.Add(move);
                }
            }

            // remove all regular moves from the list of best moves if any jumps were found
            if (bestMoves.Exists(other => other.IsJump))
            {
                bestMoves.RemoveAll(notJump => !notJump.IsJump);
            }

            return(bestMoves[rand.Next(bestMoves.Count)]);
        }
Beispiel #10
0
 public State(Program.Square[,] state, Program.Square currentPlayer)
 {
     BoardState    = state;
     CurrentPlayer = currentPlayer;
 }
Beispiel #11
0
        /// <summary>
        /// Checks whether provided jump is valid
        /// </summary>
        private bool CanJump(Piece piece, Move move)
        {
            Program.Square currentPlayer = piece.Type;
            Program.Square opponent;
            Program.Square opponentsKing;

            var toRow        = move.ToRow;
            var toCol        = move.ToCol;
            var colDirection = move.FromCol - move.ToCol;
            var rowDirection = move.FromRow - move.ToRow;

            // can't jump outside the board
            if (move.ToRow < 0 | move.ToRow > 7 | move.ToCol < 0 | move.ToCol > 7)
            {
                return(false);
            }

            // set opponent and opponentsKing variables to opposite colour to the currentPlayer's pieces
            if (currentPlayer == Program.Square.Red || currentPlayer == Program.Square.RedKing)
            {
                opponent      = Program.Square.White;
                opponentsKing = Program.Square.WhiteKing;
            }
            else
            {
                opponent      = Program.Square.Red;
                opponentsKing = Program.Square.RedKing;
            }

            if (currentPlayer == Program.Square.Red && !piece.HasJustJumped)
            {
                // checking jumps to the right
                if (colDirection == -2)
                {
                    // checking if a jump to the right can be made to the row below
                    if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                        (GetPieceAt(new Point(toRow + 1, toCol - 1)) == opponent ||
                         GetPieceAt(new Point(toRow + 1, toCol - 1)) == opponentsKing))
                    {
                        return(true);
                    }
                    return(false);
                }
                // checking jumps to the left
                if (colDirection == 2)
                {
                    // checking if a jump to the left can be made to the row below
                    if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                        (GetPieceAt(new Point(toRow + 1, toCol + 1)) == opponent ||
                         GetPieceAt(new Point(toRow + 1, toCol + 1)) == opponentsKing))
                    {
                        return(true);
                    }
                    return(false);
                }
            }

            if (currentPlayer == Program.Square.White && !piece.HasJustJumped)
            {
                // checking jumps to the right
                if (colDirection == -2)
                {
                    // checking if a jump to the right can be made to the row above
                    if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                        (GetPieceAt(new Point(toRow - 1, toCol - 1)) == opponent ||
                         GetPieceAt(new Point(toRow - 1, toCol - 1)) == opponentsKing))
                    {
                        return(true);
                    }
                    return(false);
                }
                // checking jumps to the left
                if (colDirection == 2)
                {
                    // checking if a jump to the left can be made to the row above
                    if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                        (GetPieceAt(new Point(toRow - 1, toCol + 1)) == opponent ||
                         GetPieceAt(new Point(toRow - 1, toCol + 1)) == opponentsKing))
                    {
                        return(true);
                    }
                    return(false);
                }
            }

            // king and regular piece that has just jumped can jump in all directions
            if (currentPlayer == Program.Square.RedKing || currentPlayer == Program.Square.WhiteKing || piece.HasJustJumped)
            {
                // checking jumps to the right
                if (colDirection == -2)
                {
                    // checking jumps to the row above
                    if (rowDirection == -2)
                    {
                        if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                            (GetPieceAt(new Point(toRow - 1, toCol - 1)) == opponent ||
                             GetPieceAt(new Point(toRow - 1, toCol - 1)) == opponentsKing))
                        {
                            return(true);
                        }
                    }

                    // checking jumps to the row below
                    if (rowDirection == 2)
                    {
                        if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                            (GetPieceAt(new Point(toRow + 1, toCol - 1)) == opponent ||
                             GetPieceAt(new Point(toRow + 1, toCol - 1)) == opponentsKing))
                        {
                            return(true);
                        }
                    }

                    return(false);
                }

                // checking jumps to the left
                if (colDirection == 2)
                {
                    // checking jumps to the row above
                    if (rowDirection == -2)
                    {
                        if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                            (GetPieceAt(new Point(toRow - 1, toCol + 1)) == opponent ||
                             GetPieceAt(new Point(toRow - 1, toCol + 1)) == opponentsKing))
                        {
                            return(true);
                        }
                    }

                    // checking jumps to the row below
                    if (rowDirection == 2)
                    {
                        if (GetPieceAt(new Point(toRow, toCol)) == Program.Square.EmptyDark &&
                            (GetPieceAt(new Point(toRow + 1, toCol + 1)) == opponent ||
                             GetPieceAt(new Point(toRow + 1, toCol + 1)) == opponentsKing))
                        {
                            return(true);
                        }
                    }

                    return(false);
                }
            }

            return(false);
        }