Beispiel #1
0
        /// <summary>
        /// Reverts last move applied to the board
        /// </summary>
        public void RevertLastMove()
        {
            MoveInformation lastMove = Moves.Last();

            Moves.RemoveAt(Moves.Count() - 1);

            ChessPiece lastPieceMoved = FindPieceAt(lastMove.End.File, lastMove.End.Rank);

            lastPieceMoved.Move(lastMove.Start.File, lastMove.Start.Rank);
            if (lastMove.FirstMove)
            {
                lastPieceMoved.Deployed = false; // Reset first move
            }

            // Undo captures
            lastMoveWasCapture = false;
            if (lastMove.IsCapture)
            {
                lastMove.CapturedPiece.Captured = false;
                lastMoveWasCapture = true;
            }

            if (lastMove.IsPromotion)
            {
                lastPieceMoved.Demote();
            }

            // Undo a castling move
            if (lastMove.IsCastle)
            {
                // Move the rook back as well
                ChessPiece rook             = lastMove.CastlingRook;
                PieceFile  castleTargetFile = lastMove.End.File;
                int        rookRank         = (rook.Color == PieceColor.White) ? 1 : 8;
                if (castleTargetFile.ToInt() == 7) // g->h
                {
                    rook.Move(new PieceFile('h'), rookRank);
                }
                else if (castleTargetFile.ToInt() == 3) // c->a
                {
                    rook.Move(new PieceFile('a'), rookRank);
                }
                else
                {
                    // It has to be one of the above if logic is correct
                    throw new IndexOutOfRangeException();
                }
                rook.Deployed = false;
            }

            // Reset castling rights
            ActivePlayerCastlingRights = lastMove.CastlingRights;

            // Flip players
            activePlayer = OppositeColor(activePlayer);

            // Set last FEN
            currentFEN = lastMove.PreviousFEN;
        }
Beispiel #2
0
        /// <summary>
        /// Invoked when the chess engine has responded with a move to apply to the
        /// local board
        /// </summary>
        private void OnEngineBestMoveResponse()
        {
            thinkingIndex = 0;  // reset index counter for simple progress text

            // Get the best move from the engine
            string bestMove = engine.BestMove;

            if ((String.Compare(bestMove, "(none)") == 0) || // Stockfish (and converted ones)
                (String.Compare(bestMove, "a1a1") == 0) ||   // Rybka
                (board.HalfMoveCount >= HalfMovesUntilDraw)) // Propably spinning on self play or just a draw
            {
                if (board.HalfMoveCount >= HalfMovesUntilDraw)
                {
                    Debug.WriteLine("Draw by 50 moves rule...");
                }
                GameOverHandler();
            }
            else if (GetInputState() == InputState.WaitingOnOpponentMove)
            {
                // Extract the board location from the move string
                PieceFile startFile = new PieceFile(bestMove[0]);
                int       startRank = Convert.ToInt16(bestMove[1]) - Convert.ToInt16('0');
                PieceFile destFile  = new PieceFile(bestMove[2]);
                int       destRank  = Convert.ToInt16(bestMove[3]) - Convert.ToInt16('0');

                ChessPiece      foundPiece = board.FindPieceAt(startFile, startRank);
                MoveInformation moveInfo   = new MoveInformation(
                    new BoardSquare(startFile, startRank),
                    new BoardSquare(destFile, destRank),
                    foundPiece.Deployed, board.CurrentFEN);

                moveInfo.Color          = foundPiece.Color;
                moveInfo.CastlingRights = board.ActivePlayerCastlingRights;

                // When coming from the engine, we get the promotion detection for free
                if (bestMove.Length == 5)
                {
                    // Applied on the next move
                    PieceClass promotionJob = ChessBoard.PieceClassFromFen(bestMove[4]);
                    board.PromotePiece(startFile, startRank, destFile, destRank, promotionJob, ref moveInfo);
                }

                // Move the piece on the board, and add it to the official moves list
                board.MovePiece(ref moveInfo);

                // trigger a redraw
                view.Invalidate();

                // Apply the move the engine just gave us with the engine (update it's own move)
                UpdateEnginePosition();
            }
        }
Beispiel #3
0
        /// <summary>
        /// Process the input when waiting for the current player to select a move
        /// </summary>
        /// <param name="x">x coordinate in form</param>
        /// <param name="y">y coordinate in form</param>
        private void OnWaitingForMoveSelection(int x, int y)
        {
            BoardSquare square = ((IChessBoardView)view).GetSquare(x, y);

            foreach (BoardSquare move in legalMoves)
            {
                if (move == square)
                {
                    // Done - this is the move
                    MoveInformation moveInfo = new MoveInformation(
                        new BoardSquare(selectedPiece.File, selectedPiece.Rank), move, selectedPiece.Deployed, board.CurrentFEN);

                    moveInfo.Color          = selectedPiece.Color;
                    moveInfo.CastlingRights = board.ActivePlayerCastlingRights;

                    Debug.WriteLine("Valid Move Detected: [{0},{1}]=>[{2},{3}]",
                                    selectedPiece.File, selectedPiece.Rank, move.File, move.Rank);

                    // Need to detect promotion and launch dialog for it...
                    bool isPawnMovingToBackRank = (selectedPiece.Color == PieceColor.White) ? (moveInfo.End.Rank == 8) : (moveInfo.End.Rank == 1);
                    if ((selectedPiece.Job == PieceClass.Pawn) && isPawnMovingToBackRank)
                    {
                        PieceClass promotionJob = view.ChoosePromotionJob();
                        board.PromotePiece(moveInfo.Start.File, moveInfo.Start.Rank, moveInfo.End.File, moveInfo.End.Rank, promotionJob, ref moveInfo);
                    }

                    // Always returns true now
                    board.MovePiece(ref moveInfo);
                    view.Invalidate();

                    Debug.WriteLine(String.Format("Fullmoves: {0}", board.FullMoveCount));
                    Debug.WriteLine(String.Format("Halfmoves: {0}", board.HalfMoveCount));
                    Debug.WriteLine(String.Format("WhCastle: {0}", board.WhiteCastlingRights.ToString()));
                    Debug.WriteLine(String.Format("BlCastle: {0}", board.BlackCastlingRights.ToString()));

                    // Update the position with the engine
                    UpdateEnginePosition();
                    break;
                }
            }

            // Either way this gets cleared
            legalMoves.Clear();
            ((IChessBoardView)view).ClearHiglightedSquares();
            selectedPiece = null;
        }
Beispiel #4
0
        /// <summary>
        /// Moves the rook for a castle move of a King.  At this point the castling
        /// has (or should have) been detected.  This is called to bring the rook
        /// along for the ride when the king is moved. See IsCastling for detection
        /// </summary>
        /// <param name="targetFile">ChessFile the King is moving to</param>
        /// <param name="moveInfo">Extended move information</param>
        private void PerformCastle(PieceFile targetFile, ref MoveInformation moveInfo)
        {
            // Find the corresponding Rook and move it too
            int       rookRank    = (activePlayer == PieceColor.White) ? 1 : 8;
            PieceFile gTargetFile = new PieceFile('g');
            PieceFile cTargetFile = new PieceFile('c');
            PieceFile rookStartFile;
            PieceFile rookTargetFile;

            // These are the only 2 legal files to move a king when castling
            // and they are the same for both players (only the rank differs above)
            if (targetFile == gTargetFile)
            {
                rookStartFile  = new PieceFile('h');
                rookTargetFile = new PieceFile('f');
            }
            else if (targetFile == cTargetFile)
            {
                rookStartFile  = new PieceFile('a');
                rookTargetFile = new PieceFile('d');
            }
            else
            {
                // If the above chess logic was sound, this should not happen.
                throw new ArgumentOutOfRangeException();
            }

            // Get the rook (which should exist if logic is sound)
            ChessPiece castleRook = FindPieceAt(rookStartFile, rookRank);

            // Move it
            castleRook.Move(rookTargetFile, rookRank);

            // Save the castling info
            moveInfo.CastlingRook = castleRook;

            // Remove all castling rights for the active player
            if (activePlayer == PieceColor.White)
            {
                whiteCastlingRights = BoardSide.None;
            }
            else
            {
                blackCastlingRights = BoardSide.None;
            }
        }
Beispiel #5
0
        /// <summary>
        /// When a pawn has made it to the back rank, it can be promoted.  This method
        /// will mark a piece as needing promotion on the next move.  We don't want to
        /// change the job until it has moved to keep inline with the rest of the logic
        /// </summary>
        /// <param name="startFile"></param>
        /// <param name="startRank"></param>
        /// <param name="targetFile"></param>
        /// <param name="targetRank"></param>
        /// <param name="promotionClass"></param>
        /// <param name="moveInfo">Detailed move info</param>
        public void PromotePiece(PieceFile startFile, int startRank, PieceFile targetFile, int targetRank, PieceClass promotionClass, ref MoveInformation moveInfo)
        {
            ChessPiece piece     = FindPieceAt(startFile, startRank);
            int        validRank = (piece.Color == PieceColor.White) ? 8 : 1;

            if (targetRank != validRank)
            {
                throw new ArgumentOutOfRangeException();
            }

            // Find the pawn and mark it
            if (piece.Job != PieceClass.Pawn)
            {
                // Logic check
                throw new InvalidOperationException();
            }
            moveInfo.PromotionJob = promotionClass;
            piece.PromoteOnNextMove(promotionClass);
        }
Beispiel #6
0
        /// <summary>
        /// Move a piece from startFile:startRank -> targetFile:targetRank.  Because
        /// self-play is the only mode enabled right now, these moves are always
        /// going to be considered valid, since they came from the chess engine
        /// (and we will assume it is correct).  In the future, this will likely
        /// remain, and validation of the legallity for the player can be handled
        /// above this call
        /// </summary>
        /// <param name="moveInfo">detailed move information struct</param>
        public void MovePiece(ref MoveInformation moveInfo)
        {
            // Get the player piece at the starting location
            // Piece should never be null if chess logic is sound
            PieceFile startFile  = moveInfo.Start.File;
            int       startRank  = moveInfo.Start.Rank;
            PieceFile targetFile = moveInfo.End.File;
            int       targetRank = moveInfo.End.Rank;

            ChessPiece playerPiece = FindPieceAt(startFile, startRank);

            if (playerPiece.Color != activePlayer)
            {
                // This also should not be able to happen with correct game logic
                throw new InvalidOperationException();
            }

            // Get each side's pieces
            List <ChessPiece> playerPieces   = ActivePlayerPieces;
            List <ChessPiece> opponentPieces = OpponentPlayerPieces;

            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            // PRE-MOVE CHECKS

            // We have to detect castling.  It does not come across the wire as O-O or O-O-O
            // but rather as a regular move like e1g1.  Separate the detection from the move
            // of the rook
            bool isCastling = IsCastling(playerPiece, targetFile);

            // We also need to check for an en-passant capture if the pieces is a pawn
            if (playerPiece.Job == PieceClass.Pawn)
            {
                moveInfo.CapturedPiece = HandleEnPassant(startFile, startRank, targetFile, targetRank);
            }

            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            // RAW MOVE(S)
            playerPiece.Move(targetFile, targetRank);
            if (isCastling)
            {
                PerformCastle(targetFile, ref moveInfo); // Also move the rook if needed
            }

            // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
            // POST-MOVE CHECKS/UPDATES

            // For normal captures, just do a quick iteration of the opponent pieces
            // there are only 16 of these total in normal chess
            foreach (ChessPiece enemyPiece in opponentPieces)
            {
                if ((enemyPiece.Rank == targetRank) &&   // Enemy piece is located in
                    (enemyPiece.File == targetFile) &&   // the square we just moved to
                    !enemyPiece.Captured)                // and it's not already captured
                {
                    enemyPiece.Captured    = true;       // Stop drawing it (capture)
                    moveInfo.CapturedPiece = enemyPiece; // Record the capture
                    break;                               // exit the search loop
                }
            }

            // save the last capture state for external callers
            lastMoveWasCapture = moveInfo.IsCapture;

            Moves.Add(moveInfo);

            // Update our FEN
            currentFEN     = FenParser.ApplyMoveToFEN(currentFEN, moveInfo.ToString());
            enPassantValid = FenParser.ExtractEnPassantTarget(currentFEN, out enPassantTarget);

            FenParser.ExtractCastlingRights(CurrentFEN, ref whiteCastlingRights, ref blackCastlingRights);
            FenParser.ExtractMoveCounts(CurrentFEN, ref halfMoveCount, ref fullMoveCount);

            // Flip players - easy to just do here rather than parse the FEN again
            activePlayer = OppositeColor(activePlayer);
        }