Beispiel #1
0
        public ChessMove(BoardSquare start, BoardSquare end, char pieceMoved, bool isCapture, Piece promotedPiece)
        {
            this.startSquare = start;
            this.endSquare = end;
            this.pieceMoved = pieceMoved;
            this.checkIndicator = '\0';
            this.isCapture = isCapture;

            if (promotedPiece != null)
                this.promotedPiece = promotedPiece.Notational;

            // Determine if the move resulted in castling
            if (this.pieceMoved == (char)Piece.PieceNotation.King && start.X - end.X == 2)
                this.castleNotation = CASTLE_QUEEN;
            else if (this.pieceMoved == (char)Piece.PieceNotation.King && start.X - end.X == -2)
                this.castleNotation = CASTLE_KING;
            else
                this.castleNotation = String.Empty;
        }
Beispiel #2
0
        public bool IsBetweenPoints(BoardSquare endPointA, BoardSquare endPointB)
        {
            BoardSquare maxVals = BoardSquare.Empty, minVals = BoardSquare.Empty;

            maxVals.X = Math.Max(endPointA.X, endPointB.X);
            maxVals.Y = Math.Max(endPointA.Y, endPointB.Y);
            minVals.X = Math.Min(endPointA.X, endPointB.X);
            minVals.Y = Math.Min(endPointA.Y, endPointB.Y);

            /*if (endPointA.X > endPointB.X)
            {
                maxVals.X = endPointA.X;
                minVals.X = endPointB.X;
            }
            else
            {
                minVals.X = endPointA.X;
                maxVals.X = endPointB.X;
            }

            if (endPointA.Y > endPointB.Y)
            {
                maxVals.Y = endPointA.Y;
                minVals.Y = endPointB.Y;
            }
            else
            {
                minVals.Y = endPointA.Y;
                maxVals.Y = endPointB.Y;
            }*/

            if (this.X < minVals.X || this.X > maxVals.X || this.Y < minVals.Y || this.Y > maxVals.Y)
                return false;

            // Avoid divide by zero, the line goes straight up
            if (endPointB.X == endPointA.X)
            {
                return endPointB.X == endPointB.X;
            }
            else
            {
                double slope = (endPointB.Y - endPointA.Y) / (endPointB.X - endPointA.X);

                // The line is not a clear diagonal or straight across
                if (slope != 0 && Math.Abs(slope) != 1)
                    return false;
                else
                {
                    double yInt = endPointB.Y - slope * endPointB.X;
                    return (this.Y == slope * this.X + yInt);
                }
            }
        }
Beispiel #3
0
        /*
         * Determines if the last move caused the game to end
         *
         * First, check if the king is in check, and store it in a boolean variable.
         *
         * If the king is in check, check if the attack is preventable. This is done
         * by seeing it the king can either move away safely or can neutralize all
         * attacks (both the primary attack and discoveryCheck if it occured). If the
         * king is in check and the conditions checking if the attempted capture
         * is preventable, checkmate has occured, otherwise the game is not over and the
         * King is simply in check.
         *
         * If the king is not in check, determine if any of the various draw conditions are
         * met, including by repetition, redundancy (x moves without a capture or pawn movement),
         * stalemate (where a player can not make a legal move) and by material (where neither side
         * has enough material to checkmate).
         *
         * If none of those conditions are met, the game can still be played, and is
         * considered to have normal status.
         */
        private void IsGameOver(ChessMove.MoveStatusCodes moveType, BoardSquare directAttacker)
        {
            // Check if king is in check (discovered, normal)
            // Be sure to account for rook when castled, removed pawn in en passant

            // If one or the other, check for blocks, capture, move out of way
            // If both, check move out of way
            // If neither, check other pieces can move (stalemate)
            // Check draw by repetition, inaction (no capture/movement), and material

            // Set gameStatus internal variable as appropriate

            BoardSquare currKingPos = this.kingPos[(int)(whiteTurn ? Sides.white : Sides.black)];
            BitBoard futureMoves = this.board[directAttacker].getAllMoves(this.board, (short)directAttacker.X, (short)directAttacker.Y);
            bool isDirectCheck = futureMoves.Contains(currKingPos);
            bool isDiscoverCheck;

            this.attackingPieces[(int)AttackTypes.discovery] = ExtrapolateAttack((short)this.startCoord.X, (short)this.startCoord.Y, (short)currKingPos.X, (short)currKingPos.Y);
            this.attackingPieces[(int)AttackTypes.regular] = isDirectCheck ? endCoord : BoardSquare.Empty;

            isDiscoverCheck = this.attackingPieces[(int)AttackTypes.discovery] != BoardSquare.Empty;

            if (!isDiscoverCheck)
            {
                BoardSquare lastKingPos = this.kingPos[(int)(whiteTurn ? Sides.black : Sides.white)];

                if (moveType == ChessMove.MoveStatusCodes.queenCastle)
                {
                    isDiscoverCheck = this[lastKingPos.Y, lastKingPos.X + 1].getAllMoves(this.board, (short)(this.endCoord.X + 1), (short)this.endCoord.Y).Contains(currKingPos);
                    if (isDiscoverCheck)
                        this.attackingPieces[(int)AttackTypes.discovery] = new BoardSquare(lastKingPos.X + 1, lastKingPos.Y);
                }
                else if (moveType == ChessMove.MoveStatusCodes.queenCastle)
                {
                    isDiscoverCheck = this[lastKingPos.Y, lastKingPos.X - 1].getAllMoves(this.board, (short)(this.endCoord.X - 1), (short)this.endCoord.Y).Contains(currKingPos);
                    if (isDiscoverCheck)
                        this.attackingPieces[(int)AttackTypes.discovery] = new BoardSquare(lastKingPos.X - 1, lastKingPos.Y);
                }
                else if (moveType == ChessMove.MoveStatusCodes.enPassant)
                {
                    // Check captured square
                    this.attackingPieces[(int)AttackTypes.discovery] = ExtrapolateAttack((short)endCoord.X, (short)startCoord.Y, (short)currKingPos.X, (short)currKingPos.Y);
                    isDiscoverCheck = this.attackingPieces[(int)AttackTypes.discovery] != BoardSquare.Empty;
                }
            }

            if (isDirectCheck || isDiscoverCheck)
            {
                this.gameStatus = GameStatusCodes.check;

                // If king can't move safely, check alternatives
                if (this.board[currKingPos].getAllMoves(this.board, (short)currKingPos.X, (short)currKingPos.Y) == BitBoard.Empty)
                {
                    // Double attack, can only get out of it by moving
                    if (isDirectCheck && isDiscoverCheck)
                        this.gameStatus = GameStatusCodes.checkMate;
                    else
                    {
                        BoardSquare attackSquare = BoardSquare.Empty;

                        bool canBlockCap = false;

                        if (isDirectCheck) attackSquare = this.attackingPieces[(int)AttackTypes.regular];
                        else attackSquare = this.attackingPieces[(int)AttackTypes.discovery];

                        // Check for blocking, capture
                        foreach (BoardSquare sq in this.board)
                        {
                            Piece p = this.board[sq];

                            if (p.Colour == this.board[currKingPos].Colour)
                            {
                                BitBoard bbMoves = p.getAllMoves(this.board, (short)sq.X, (short)sq.Y);
                                foreach (BoardSquare sqMove in bbMoves)
                                {
                                    // Can block or capture
                                    if (sqMove == attackSquare || (!(board[sq] is King) && sqMove.IsBetweenPoints(currKingPos, attackSquare))
                                        || board[sq] is Pawn && ((Pawn)board[sq]).MayEnPassant(this.board, (short)sq.X, (short)sq.Y, (short)sqMove.X, (short)sqMove.Y))
                                    {
                                        canBlockCap = true;
                                        break;
                                    }
                                }

                                if (canBlockCap)
                                    break;
                            }
                        }

                        if (!canBlockCap)
                            this.gameStatus = GameStatusCodes.checkMate;
                    }
                }
            }
            else
            {
                gameStatus = GameStatusCodes.normal;

                //Check for draw conditions (stalemate, repetition, inactivity (no capture/pawn), material
                if (this.positions.isDrawByMoves())
                    this.gameStatus = GameStatusCodes.forcedDraw;
                else if (this.lastMove.IsCapture && IsDrawByMaterial())
                    this.gameStatus = GameStatusCodes.drawByMaterial;
                else
                {
                    bool isStalemate = true;
                    char nextMoveCol = whiteTurn ? Piece.NOTATION_W : Piece.NOTATION_B;

                    foreach (BoardSquare sq in this.board)
                        if (this.board[sq].Colour == nextMoveCol && this.board[sq].getAllMoves(this.board, (short)sq.X, (short)sq.Y) != BitBoard.Empty)
                        {
                            isStalemate = false;
                            break;
                        }

                    if (isStalemate)
                        this.gameStatus = GameStatusCodes.stalemate;
                }
            }
        }
Beispiel #4
0
        /*
         * Standard set of actions that occur when a valid turn takes takes place.
         *
         * The two arguments, currPos and newPos, are character representations
         * of the original and new positions of the moved piece respectively
         *
         * First, the board array is updated.
         * Second, switch internal track of move, and record the last move.
         * Third, update the array of repeatable board positions, clear discoverCheckPiece, and increment the move count for draws
         * Fourth of all, if the piece that moved was the king, update his board position in the array
         * Lastly, check to see if this move causes the game to end
         */
        private void DoTurn(ChessMove.MoveStatusCodes moveType)
        {
            BoardSquare directAttacker = endCoord;

            switch (moveType)
            {
                case ChessMove.MoveStatusCodes.enPassant:
                    board[lastMove.EndSquare.Y, lastMove.EndSquare.X] = null;
                    break;
                case ChessMove.MoveStatusCodes.kingCastle:
                    board.movePiece(Board.NUM_FILES-1, startCoord.Y, startCoord.X + 1, startCoord.Y);
                    directAttacker = new BoardSquare(startCoord.X + 1, startCoord.Y);
                    break;
                case ChessMove.MoveStatusCodes.queenCastle:
                    board.movePiece(0, startCoord.Y, startCoord.X - 1, startCoord.Y);
                    directAttacker = new BoardSquare(startCoord.X - 1, startCoord.Y);
                    break;
            }

            // Update internal board position and move trackers
            lastMove = new ChessMove(startCoord, endCoord, board[startCoord].Notational, board[endCoord] != null);
            allMoves.Add(lastMove);

            board.movePiece(startCoord.X, startCoord.Y, endCoord.X, endCoord.Y);

            // Switch who's move it is and record move
            whiteTurn = !whiteTurn;

            // Update internal move-by-move trackers
            positions.add(lastMove, board);

            // Update position of king if king moved
            if (lastMove.PieceMoved == (char)Piece.PieceNotation.King)
            {
                kingPos[Convert.ToInt32(whiteTurn)] = endCoord;
                ((King)board[endCoord]).HasNotMoved = false;
            }

            if (moveType != ChessMove.MoveStatusCodes.pawnPromote)
                IsGameOver(moveType, directAttacker);
        }
Beispiel #5
0
 public void SetPromotedValue(BoardSquare square, Piece newPiece)
 {
     if (square.Y == (whiteTurn ? 0 : Board.NUM_ROWS - 1))
     {
         Piece endSquare = board[square];
         if (endSquare != null && endSquare is Pawn)
         {
             board[square] = newPiece;
             IsGameOver(ChessMove.MoveStatusCodes.pawnPromote, this.endCoord);
         }
     }
 }
Beispiel #6
0
        /*
         * Based on the current position and attempted new position of a piece, determines if the move
         * is valid. Owing to View-Layer checking, it is known that the starting coordinate holds a piece
         * pf the colour who's turn it is to move, and the ending coordinate does not hold a piece of the
         * same colour.
         *
         * Checking starts off by taking the two board coordinates, passed in as 2 character arrays, and
         * converting them to integers so they can be accurately used to reference the 2-D board array
         * holding all the pieces.
         *
         * First to be checked is if performing the move would leave the player in check. Since a cardinal
         * rule of chess involves not ending your turn while you yourself are in check, if true is returned by
         * this function, it is an invalid move, all other checking is ignored, and the appropriate flag is
         * returned.
         *
         * Next check to see if the move is a traditional movement of a piece by checking if the square it's moving
         * to is unoccupied, if the movement follows that piece's traditional movement pattern, and that no pieces
         * lie in the way of the movement. If it is, it is a valid movement, and some piece-specific checking occurs.
         * If it was a king , indicate that he has moved (meaning that he can no longer castle). If it was a pawn, reset
         * the draw counters for draw by repetition and movement, and check and see if the pawn has reached the far side
         * of the board. If it did, perform the turn and return the appropriate flag. If not, piece specific checking
         * ends, the turn is performed, and the movement is a typical movement.
         *
         * If the movement is not a typical movement, checking occurs to see if it is a traditional capture. This means
         * that the square the piece is moving to is occupied, the movement follows the piece's traditional capture
         * movement pattern, and that there are no pieces in the way. If it is, the same actions occur here as do if
         * a regular movement has occured, with the exception that the draw counters are always reset, instead of just
         * if a pawn was moved.
         *
         * If none of the above conditions are met, exception checking occurs.
         * First, if the piece moved was a pawn, checking for en Passant is dealt with. If the movement was valid as
         * being en Passant, reset the draw counters, set the square contents of the captured piece to null manually
         * (since the square was never clicked on, doTurn can not handle doing that), perform the turn, and return
         * the appropriate flag.
         * Second, checking for castling occurs, by seeing if the square moved to is null and castling is allowed for
         * that piece. If it is, the current rank and file of the involved rook is calculated, as is the ending
         * X-coordinate of the rook. Then it is tested if the involved rook is actually on the square that it
         * needs to be on, that there is nothing in between the rook and king, and that the square the
         * king is moving over on it's way to it's ending position is not under attack. If all of these conditions
         * are met, it is a valid castling, the turn is performed, and the appropriate value is returned.
         *
         * If none of the conditions are met, it is an invalid move, and a flag is returned indicating this.
         */
        public ChessMove.MoveStatusCodes IsValidTurn(char[] currPos, char[] newPos)
        {
            startCoord = new BoardSquare(currPos[0], currPos[1]);
            endCoord = new BoardSquare(newPos[0], newPos[1]);
            Piece startPiece = board[startCoord];
            Piece endPiece = board[endCoord];
            ChessMove.MoveStatusCodes resultant = ChessMove.MoveStatusCodes.invalidMove;

            // Clicked on own piece, nothing in way of desired move
            if (startPiece != null && startPiece.Colour == (whiteTurn ? Piece.NOTATION_W : Piece.NOTATION_B) && (endPiece == null || endPiece.Colour != startPiece.Colour) /* && IsPieceFreeToMove((short)startCoord.X, (short)startCoord.Y, (short)endCoord.X, (short)endCoord.Y) */)
            {
                BitBoard b = startPiece.getAllMoves(this.board, (short)startCoord.X, (short)startCoord.Y);

                if (b.Contains(endCoord))
                {
                    resultant = ChessMove.MoveStatusCodes.normalMovement;

                    if (startPiece is King)
                    {
                        if (startCoord.X - endCoord.X == -2)
                            resultant = ChessMove.MoveStatusCodes.kingCastle;
                        else if (startCoord.X - endCoord.X == 2)
                            resultant = ChessMove.MoveStatusCodes.queenCastle;
                    }
                    else if (startPiece is Pawn)
                    {
                        if (endCoord.Y == ((Pawn)startPiece).GetLastRow())
                            resultant = ChessMove.MoveStatusCodes.pawnPromote;
                        else if (endCoord.X - startCoord.X != 0 && this[endCoord.Y, endCoord.X] == null)
                            resultant = ChessMove.MoveStatusCodes.enPassant;
                    }

                    DoTurn(resultant);
                }
            }

            return resultant;
        }
Beispiel #7
0
 public ChessMove(BoardSquare start, BoardSquare end, char pieceMoved, bool isCapture)
     : this(start, end, pieceMoved, isCapture, null)
 {
 }