Beispiel #1
0
 public PositionDistance(Position position1, Position position2)
 {
     if (position1 == null)
         throw new ArgumentNullException("position1");
     if (position2 == null)
         throw new ArgumentNullException("position2");
     _distanceX = Math.Abs((int)position1.File - (int)position2.File);
     _distanceY = Math.Abs((int)position1.Rank - (int)position2.Rank);
 }
Beispiel #2
0
        public async Task <ChessMatchStatus> Move(Stream stream, ulong channel, IUser player, string rawMove)
        {
            var moveInput = rawMove.Replace(" ", "").ToUpper();

            if (!Regex.IsMatch(moveInput, "[A-H][1-8][A-H][1-8]"))
            {
                throw new ChessException("Error parsing move. Example move: a2a4");
            }

            var match = _chessMatches.SingleOrDefault(x => x.Channel == channel && x.Players.Contains(player));

            if (match == null)
            {
                throw new ChessException("You are not currently in a game");
            }

            var whoseTurn   = match.Game.WhoseTurn;
            var otherPlayer = whoseTurn == Player.White ? Player.Black : Player.White;

            if ((whoseTurn == Player.White && player != match.Challenger) || (whoseTurn == Player.Black && player != match.Challenged))
            {
                throw new ChessException("It's not your turn.");
            }

            var sourceX            = moveInput[0].ToString();
            var sourceY            = moveInput[1].ToString();
            var destX              = moveInput[2].ToString();
            var destY              = moveInput[3].ToString();
            var positionEnumValues = (IEnumerable <ChessDotNet.File>)Enum.GetValues(typeof(ChessDotNet.File));

            var sourcePositionX = positionEnumValues.Single(x => x.ToString("g") == sourceX);
            var destPositionX   = positionEnumValues.Single(x => x.ToString("g") == destX);

            var originalPosition = new ChessDotNet.Position(sourcePositionX, int.Parse(sourceY));
            var destPosition     = new ChessDotNet.Position(destPositionX, int.Parse(destY));

            var move = new Move(originalPosition, destPosition, whoseTurn);

            if (!match.Game.IsValidMove(move))
            {
                throw new ChessException("Invalid move.");
            }

            var chessMove = new ChessMove {
                Move = move, MoveDate = DateTime.Now
            };

            var board = match.Game.GetBoard();

            for (var column = 0; column < board.Length; column++)
            {
                for (var row = 0; row < board[column].Length; row++)
                {
                    chessMove.PreviousBoardState[column][row] = board[column][row];
                }
            }

            match.Game.ApplyMove(move, true);
            match.History.Add(chessMove);
            match.UndoRequest = null;

            var checkMated = match.Game.IsCheckmated(otherPlayer);
            var isOver     = checkMated || match.Game.IsStalemated(otherPlayer);

            var status = new ChessMatchStatus
            {
                IsOver  = isOver,
                Winner  = isOver && checkMated ? player : null,
                IsCheck = match.Game.IsInCheck(otherPlayer)
            };

            await WriteBoard(channel, player, stream);

            if (isOver)
            {
                _chessMatches.Remove(match);
            }

            return(await Task.FromResult(status));
        }
Beispiel #3
0
        protected virtual bool IsInCheck(Player player)
        {
            List<Position> piecePositions = new List<Position>();
            Position kingPos = new Position(File.None, Rank.None);

            for (int i = 0; i < Board.Length; i++)
            {
                for (int j = 0; j < Board[i].Length; j++)
                {
                    ChessPiece curr = Board[i][j];
                    if (curr.Piece != Piece.None && curr.Player == Utilities.GetOpponentOf(player))
                    {
                        piecePositions.Add(new Position((File)j, (Rank)i));
                    }
                    else if (curr.Piece == Piece.King && curr.Player == player)
                    {
                        kingPos = new Position((File)j, (Rank)i);
                    }
                }
            }

            if (kingPos.File == File.None)
                return false;

            ChessGame copy = new ChessGame(Board, Utilities.GetOpponentOf(player), false);
            for (int i = 0; i < piecePositions.Count; i++)
            {
                if (copy.IsValidMove(new Move(piecePositions[i], kingPos, Utilities.GetOpponentOf(player)), false))
                {
                    return true;
                }
            }

            return false;
        }
Beispiel #4
0
 public virtual bool HasAnyValidMoves(Position from)
 {
     Utilities.ThrowIfNull(from, "from");
     ReadOnlyCollection<Move> validMoves = GetValidMoves(from, true);
     return validMoves.Count > 0;
 }
Beispiel #5
0
 protected virtual ReadOnlyCollection<Move> GetValidMoves(Position from, bool returnIfAny)
 {
     Utilities.ThrowIfNull(from, "from");
     ChessPiece cp = GetPieceAt(from);
     if (cp.Player != WhoseTurn) return new ReadOnlyCollection<Move>(new List<Move>());
     Piece piece = cp.Piece;
     switch (piece)
     {
         case Piece.King:
             return GetValidMovesKing(from, returnIfAny);
         case Piece.Pawn:
             return GetValidMovesPawn(from, returnIfAny);
         case Piece.Knight:
             return GetValidMovesKnight(from, returnIfAny);
         case Piece.Rook:
             return GetValidMovesRook(from, returnIfAny);
         case Piece.Bishop:
             return GetValidMovesBishop(from, returnIfAny);
         case Piece.Queen:
             return GetValidMovesQueen(from, returnIfAny);
         default:
             return new ReadOnlyCollection<Move>(new List<Move>());
     }
 }
Beispiel #6
0
 protected virtual ReadOnlyCollection<Move> GetValidMovesQueen(Position from, bool returnIfAny)
 {
     Utilities.ThrowIfNull(from, "from");
     ReadOnlyCollection<Move> horizontalVerticalMoves = GetValidMovesRook(from, returnIfAny);
     if (returnIfAny && horizontalVerticalMoves.Count > 0)
         return horizontalVerticalMoves;
     ReadOnlyCollection<Move> diagonalMoves = GetValidMovesBishop(from, returnIfAny);
     return new ReadOnlyCollection<Move>(horizontalVerticalMoves.Concat(diagonalMoves).ToList());
 }
Beispiel #7
0
 protected virtual ReadOnlyCollection<Move> GetValidMovesBishop(Position from, bool returnIfAny)
 {
     Utilities.ThrowIfNull(from, "from");
     List<Move> validMoves = new List<Move>();
     ChessPiece cp = GetPieceAt(from);
     int l0 = Board.Length;
     int l1 = Board[0].Length;
     for (int i = -7; i < 8; i++)
     {
         if (i == 0)
             continue;
         if ((int)from.Rank + i > -1 && (int)from.Rank + i < l0
             && (int)from.File + i > -1 && (int)from.File + i < l1)
         {
             Move move = new Move(from, new Position(from.File + i, from.Rank + i), cp.Player);
             if (IsValidMove(move))
             {
                 validMoves.Add(move);
                 if (returnIfAny)
                     return new ReadOnlyCollection<Move>(validMoves);
             }
         }
         if ((int)from.Rank - i > -1 && (int)from.Rank - i < l0
             && (int)from.File + i > -1 && (int)from.File + i < l1)
         {
             Move move = new Move(from, new Position(from.File + i, from.Rank - i), cp.Player);
             if (IsValidMove(move))
             {
                 validMoves.Add(move);
                 if (returnIfAny)
                     return new ReadOnlyCollection<Move>(validMoves);
             }
         }
     }
     return new ReadOnlyCollection<Move>(validMoves);
 }
Beispiel #8
0
 protected virtual ReadOnlyCollection<Move> GetValidMovesKnight(Position from, bool returnIfAny)
 {
     Utilities.ThrowIfNull(from, "from");
     List<Move> validMoves = new List<Move>();
     ChessPiece cp = GetPieceAt(from);
     int l0 = Board.Length;
     int l1 = Board[0].Length;
     int[][] directions = new int[][] { new int[] { 2, 1 }, new int[] { -2, -1 }, new int[] { 1, 2 }, new int[] { -1, -2 },
                 new int[] { 1, -2 }, new int[] { -1, 2 }, new int[] { 2, -1 }, new int[] { -2, 1 } };
     foreach (int[] dir in directions)
     {
         if ((int)from.File + dir[0] < 0 || (int)from.File + dir[0] >= l1
             || (int)from.Rank + dir[1] < 0 || (int)from.Rank + dir[1] >= l0)
             continue;
         Move move = new Move(from, new Position(from.File + dir[0], from.Rank + dir[1]), cp.Player);
         if (IsValidMove(move))
         {
             validMoves.Add(move);
             if (returnIfAny)
                 return new ReadOnlyCollection<Move>(validMoves);
         }
     }
     return new ReadOnlyCollection<Move>(validMoves);
 }
Beispiel #9
0
 public ReadOnlyCollection<Move> GetValidMoves(Position from)
 {
     Utilities.ThrowIfNull(from, "from");
     return GetValidMoves(from, false);
 }
Beispiel #10
0
 public ChessPiece GetPieceAt(Position position)
 {
     Utilities.ThrowIfNull(position, "position");
     return GetPieceAt(position.File, position.Rank);
 }
Beispiel #11
0
 public abstract ReadOnlyCollection<Move> GetValidMoves(Position from, bool returnIfAny, ChessGame game, Func<Move, bool> gameMoveValidator);
Beispiel #12
0
        public async Task <ChessMatchStatus> Move(Stream stream, ulong channel, ulong player, string rawMove)
        {
            return(await Task.Run(async() =>
            {
                var moveInput = rawMove.Replace(" ", "").ToUpper();

                if (!Regex.IsMatch(moveInput, "^[A-H][1-8][A-H][1-8][Q|N|B|R]?$"))
                {
                    throw new ChessException("Error parsing move. Example move: a2a4");
                }

                var match = _chessMatches.SingleOrDefault(x => x.Channel == channel && x.Players.Contains(player));

                if (match == null)
                {
                    throw new ChessException("You are not currently in a game");
                }

                var whoseTurn = match.Game.WhoseTurn;
                var otherPlayer = whoseTurn == Player.White ? Player.Black : Player.White;

                if ((whoseTurn == Player.White && player != match.Challenger) || (whoseTurn == Player.Black && player != match.Challenged))
                {
                    throw new ChessException("It's not your turn.");
                }

                var sourceX = moveInput[0].ToString();
                var sourceY = moveInput[1].ToString();
                var destX = moveInput[2].ToString();
                var destY = moveInput[3].ToString();

                char?promotionChar = moveInput.Length > 4 ? moveInput[4].ToString().ToLower()[0] : 'q';

                var positionEnumValues = (IEnumerable <ChessDotNet.File>)Enum.GetValues(typeof(ChessDotNet.File));

                var sourcePositionX = positionEnumValues.Single(x => x.ToString("g") == sourceX);
                var destPositionX = positionEnumValues.Single(x => x.ToString("g") == destX);

                var originalPosition = new ChessDotNet.Position(sourcePositionX, int.Parse(sourceY));
                var destPosition = new ChessDotNet.Position(destPositionX, int.Parse(destY));

                var piece = match.Game.GetPieceAt(originalPosition);

                if (piece != null && !(piece is Pawn))
                {
                    promotionChar = null;
                }

                var move = new Move(originalPosition, destPosition, whoseTurn, promotionChar);

                if (!match.Game.IsValidMove(move))
                {
                    throw new ChessException("Invalid move.");
                }

                var chessMove = new ChessMove
                {
                    NewRank = move.NewPosition.Rank,
                    NewFile = move.NewPosition.File,
                    OriginalFile = move.OriginalPosition.File,
                    OriginalRank = move.OriginalPosition.Rank,
                    MoveDate = DateTime.Now,
                    PreviousWhoseTurn = match.Game.WhoseTurn
                };

                var board = match.Game.GetBoard();
                for (var column = 0; column < board.Length; column++)
                {
                    for (var row = 0; row < board[column].Length; row++)
                    {
                        chessMove.PreviousBoardState[column][row] = board[column][row];
                    }
                }

                match.Game.ApplyMove(move, true);
                match.History.Add(chessMove);
                match.UndoRequest = null;

                var checkMated = match.Game.IsCheckmated(otherPlayer);
                var isOver = checkMated || match.Game.IsStalemated(otherPlayer);
                var winner = isOver && checkMated ? player : (ulong?)null;

                var status = new ChessMatchStatus
                {
                    IsOver = isOver,
                    Winner = winner,
                    IsCheck = match.Game.IsInCheck(otherPlayer)
                };

                await WriteBoard(channel, player, stream);

                if (isOver)
                {
                    #pragma warning disable 4014
                    Task.Run(async() =>
                    {
                        using (Db db = _services.GetService <Db>())
                        {
                            await db.EndMatch(match, (long?)winner);
                            db.SaveChanges();
                        }
                    });
                    #pragma warning restore 4014
                    _chessMatches.Remove(match);
                }
                else
                {
                    #pragma warning disable 4014
                    Task.Run(async() =>
                    {
                        using (Db db = _services.GetService <Db>())
                        {
                            await db.SaveOrUpdateMatch(match);
                            db.SaveChanges();
                        }
                    });
                    #pragma warning restore 4014
                }


                return await Task.FromResult(status);
            }));
        }
Beispiel #13
0
        public virtual bool IsInCheck(Player player)
        {
            if (player == Player.None)
            {
                throw new ArgumentException("IsInCheck: Player.None is an invalid argument.");
            }
            Cache<bool> cache = player == Player.White ? inCheckCacheWhite : inCheckCacheBlack;
            if (cache.CachedAt == Moves.Count)
            {
                return cache.Value;
            }

            Position kingPos = new Position(File.None, -1);

            for (int r = 1; r <= Board.Length; r++)
            {
                for (int f = 0; f < Board[8 - r].Length; f++)
                {
                    Piece curr = GetPieceAt((File)f, r);
                    if (curr is King && curr.Owner == player)
                    {
                        kingPos = new Position((File)f, r);
                        break;
                    }
                }
                if (kingPos != new Position(File.None, -1))
                {
                    break;
                }
            }

            if (kingPos.File == File.None)
                return cache.UpdateCache(false, Moves.Count);

            for (int r = 1; r <= Board.Length; r++)
            {
                for (int f = 0; f < Board[8 - r].Length; f++)
                {
                    Piece curr = GetPieceAt((File)f, r);
                    if (curr == null) continue;
                    Player p = curr.Owner;
                    Move move = new Move(new Position((File)f, r), kingPos, p);
                    List<Move> moves = new List<Move>();
                    if (curr is Pawn && ((move.NewPosition.Rank == 8 && move.Player == Player.White) || (move.NewPosition.Rank == 1 && move.Player == Player.Black)))
                    {
                        moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'Q'));
                        moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'R'));
                        moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'B'));
                        moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'N'));
                        moves.Add(new Move(move.OriginalPosition, move.NewPosition, move.Player, 'K'));
                    }
                    else
                    {
                        moves.Add(move);
                    }
                    foreach (Move m in moves)
                    {
                        if (IsValidMove(m, false, false))
                        {
                            return cache.UpdateCache(true, Moves.Count);
                        }
                    }
                }
            }
            return cache.UpdateCache(false, Moves.Count);
        }
Beispiel #14
0
 protected virtual ReadOnlyCollection<Move> GetValidMoves(Position from, bool returnIfAny)
 {
     ChessUtilities.ThrowIfNull(from, "from");
     Piece piece = GetPieceAt(from);
     if (piece == null || piece.Owner != WhoseTurn) return new ReadOnlyCollection<Move>(new List<Move>());
     return piece.GetValidMoves(from, returnIfAny, this, IsValidMove);
 }
Beispiel #15
0
        protected virtual GameCreationData FenStringToGameCreationData(string fen)
        {
            Dictionary<char, Piece> fenMappings = FenMappings;
            string[] parts = fen.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            if (!AllowedFenPartsLength.Contains(parts.Length)) throw new ArgumentException("The FEN string has too much, or too few, parts.");
            Piece[][] board = new Piece[8][];
            string[] rows = parts[0].Split('/');
            if (rows.Length != 8) throw new ArgumentException("The board in the FEN string does not have 8 rows.");
            GameCreationData data = new GameCreationData();
            for (int i = 0; i < 8; i++)
            {
                string row = rows[i];
                Piece[] currentRow = new Piece[8] { null, null, null, null, null, null, null, null };
                int j = 0;
                foreach (char c in row)
                {
                    if (char.IsDigit(c))
                    {
                        j += (int)char.GetNumericValue(c);
                        continue;
                    }
                    if (!fenMappings.ContainsKey(c)) throw new ArgumentException("The FEN string contains an unknown piece.");
                    currentRow[j] = fenMappings[c];
                    j++;
                }
                if (j != 8)
                {
                    throw new ArgumentException("Not enough pieces provided for a row in the FEN string.");
                }
                board[i] = currentRow;
            }
            data.Board = board;

            if (parts[1] == "w")
            {
                data.WhoseTurn = Player.White;
            }
            else if (parts[1] == "b")
            {
                data.WhoseTurn = Player.Black;
            }
            else
            {
                throw new ArgumentException("Expected `w` or `b` for the active player in the FEN string.");
            }

            if (parts[2].Contains('K')) data.CanWhiteCastleKingSide = true;
            else data.CanWhiteCastleKingSide = false;

            if (parts[2].Contains('Q')) data.CanWhiteCastleQueenSide = true;
            else data.CanWhiteCastleQueenSide = false;

            if (parts[2].Contains('k')) data.CanBlackCastleKingSide = true;
            else data.CanBlackCastleKingSide = false;

            if (parts[2].Contains('q')) data.CanBlackCastleQueenSide = true;
            else data.CanBlackCastleQueenSide = false;

            if (parts[3] == "-") data.EnPassant = null;
            else
            {
                Position ep = new Position(parts[3]);
                if ((data.WhoseTurn == Player.White && (ep.Rank != 6 || !(data.Board[3][(int)ep.File] is Pawn))) ||
                    (data.WhoseTurn == Player.Black && (ep.Rank != 3 || !(data.Board[4][(int)ep.File] is Pawn))))
                {
                    throw new ArgumentException("Invalid en passant field in FEN.");
                }
                data.EnPassant = ep;
            }

            int halfmoveClock;
            if (int.TryParse(parts[4], out halfmoveClock))
            {
                data.HalfMoveClock = halfmoveClock;
            }
            else
            {
                throw new ArgumentException("Halfmove clock in FEN is invalid.");
            }

            int fullMoveNumber;
            if (int.TryParse(parts[5], out fullMoveNumber))
            {
                data.FullMoveNumber = fullMoveNumber;
            }
            else
            {
                throw new ArgumentException("Fullmove number in FEN is invalid.");
            }

            return data;
        }