private void FindBishopMoves( List <Move> moves, ThreatMatrix threats, Player player, BoardLocation playerPieceLocation, ThreatDirection?playerPiecePinDirection) { //There are NO possible moves if the bishop is under any line threats. if (playerPiecePinDirection.HasThreats(ThreatDirection.Line)) { return; } //we will use the threats matrix for this bishop to calculate our moves... var bishopThreatLocations = _threatProvider.FindThreatsForBoardLocation(_board, playerPieceLocation); var hasGradeThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Grade); var hasSlopeThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Slope); foreach (var location in bishopThreatLocations.Keys) { //only add moves that do a capture or move along the same threat vector if ((_board.LocationIsEmptyOrOccupiedBy(location, player.OpposingPlayer())) && ((hasGradeThreats == false) || (location.IsOnSameGradeAs(playerPieceLocation))) && ((hasSlopeThreats == false) || (location.IsOnSameSlopeAs(playerPieceLocation)))) { moves.Add(new Move(playerPieceLocation, location, _board[location])); } } }
private void FindQueenMoves( List <Move> moves, ThreatMatrix threats, Player player, BoardLocation playerPieceLocation, ThreatDirection?playerPiecePinDirection) { //we will use the threats matrix for this queen to calculate our moves... var queenThreatLocations = _threatProvider.FindThreatsForBoardLocation(_board, playerPieceLocation); var hasVerticalThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Vertical); var hasHorizontalThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Horizontal); var hasSlopeThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Slope); var hasGradeThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Grade); foreach (var location in queenThreatLocations.Keys) { //only add moves that do a capture or move along the same threat vector if ((_board.LocationIsEmptyOrOccupiedBy(location, player.OpposingPlayer())) && ((hasGradeThreats == false) || (location.IsOnSameGradeAs(playerPieceLocation))) && ((hasSlopeThreats == false) || (location.IsOnSameSlopeAs(playerPieceLocation))) && ((hasVerticalThreats == false) || (location.IsOnSameColumnAs(playerPieceLocation))) && ((hasHorizontalThreats == false) || (location.IsOnSameRowAs(playerPieceLocation)))) { moves.Add(new Move(playerPieceLocation, location, _board[location])); } } }
private void FindKnightMoves( List <Move> moves, ThreatMatrix threats, Player player, BoardLocation playerPieceLocation, ThreatDirection?playerPiecePinDirection) { //There are NO possible moves if the knight is under non-direct threat because //he is not capable of removing any linear or diagonal threats ever. This is due to his L-shaped //move pattern (thank god for small favors) if (playerPiecePinDirection.HasThreats(ThreatDirection.NonDirect)) { return; } //we will use the threats matrix for this knight to calculate our moves... var knightThreatLocations = _threatProvider.FindThreatsForBoardLocation(_board, playerPieceLocation); foreach (var location in knightThreatLocations.Keys) { if (_board.LocationIsEmptyOrOccupiedBy(location, player.OpposingPlayer())) { moves.Add(new Move(playerPieceLocation, location, _board[location])); } } }
private void AddPawnThreats( Board board, Player player, BoardLocation playerKingLocation, BoardLocation opposingPieceBoardLocation, ThreatMatrix threatMatrix) { //PGN format for the board // A B C D E F G H //8:| 7|15|23|31|39|47|55|63| //7:| 6|14|22|30|38|46|54|62| //6:| 5|13|21|29|37|45|53|61| //5:| 4|12|20|28|36|44|52|60| //4:| 3|11|19|27|35|43|51|59| //3:| 2|10|18|26|34|42|50|58| //2:| 1| 9|17|25|33|41|49|57| //1:| 0| 8|16|24|32|40|48|56| BoardLocation?[] moves = new BoardLocation?[2]; if (player == Player.White) { //then we are worried about the black guys pieces moves[0] = opposingPieceBoardLocation.SouthEast(); moves[1] = opposingPieceBoardLocation.SouthWest(); } else { moves[0] = opposingPieceBoardLocation.NorthEast(); moves[1] = opposingPieceBoardLocation.NorthWest(); } moves .Where(a => a.HasValue) .ForEach(a => AddThreat(threatMatrix, playerKingLocation, opposingPieceBoardLocation, a.Value, ThreatDirection.Direct)); }
public void MakeMove(IMove move) { if (GameIsOver) { throw new ChessException(ChessErrorCode.InvalidMoveGameOver, "The move is not valid because the game is over."); } if (!MoveIsLegal(move)) { throw new ChessException(ChessErrorCode.IllegalMove, "The move is not valid because it is not legal."); } //Make the move MakeMoveForMoveType(move); //add the history _moveHistory.Add(move); //clear the moves and threats (for caching of next moves) _moves = null; _threatMatrix = null; //determine the game state now that this move has been made DetermineGameState(); }
private void FindRookMoves( List <Move> moves, ThreatMatrix threats, Player player, BoardLocation playerPieceLocation, ThreatDirection?playerPiecePinDirection) { //There are NO possible moves if the rook is under any diagonal threats. if (playerPiecePinDirection.HasThreats(ThreatDirection.Diagonal)) { return; } //we will use the threats matrix for this rook to calculate our moves... var rookThreatLocations = _threatProvider.FindThreatsForBoardLocation(_board, playerPieceLocation); var hasVerticalThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Vertical); var hasHorizontalThreats = playerPiecePinDirection.HasThreats(ThreatDirection.Horizontal); foreach (var location in rookThreatLocations.Keys) { //only add moves that do a capture or move along the same threat vector if ((_board.LocationIsEmptyOrOccupiedBy(location, player.OpposingPlayer())) && ((hasVerticalThreats == false) || (location.IsOnSameColumnAs(playerPieceLocation))) && ((hasHorizontalThreats == false) || (location.IsOnSameRowAs(playerPieceLocation)))) { moves.Add(new Move(playerPieceLocation, location, _board[location])); } } }
private void FindKingCastleMoves( List <Move> moves, ThreatMatrix threats, Player player, BoardLocation playerPieceLocation) { //When are you not allowed to castle? // There are a number of cases when castling is not permitted: // -Your king has been moved earlier in the game. // -The rook that castles has been moved earlier in the game. // -There are pieces standing between your king and rook. // -The king is in check. // -The king moves through a square that is attacked by a piece of the opponent. // -The king would be in check after castling. if ((KingHasMoved(player)) || //king has moved (threats.ContainsKey(playerPieceLocation))) //king in check { return; } if (CastleQueensideMoveAvailable(player, threats)) { moves.Add(new Move(playerPieceLocation, player == Player.White ? BoardLocation.C1 : BoardLocation.C8, SpecialMoveType.CastleQueenside)); } if (CastleKingsideMoveAvailable(player, threats)) { moves.Add(new Move(playerPieceLocation, player == Player.White ? BoardLocation.G1 : BoardLocation.G8, SpecialMoveType.CastleKingside)); } }
private void AddKingThreats( Board board, Player player, BoardLocation playerKingLocation, BoardLocation opposingPieceBoardLocation, ThreatMatrix threatMatrix) { opposingPieceBoardLocation .Neighbors() .ForEach(a => AddThreat(threatMatrix, playerKingLocation, opposingPieceBoardLocation, a, ThreatDirection.Direct)); }
public IReadOnlyList <IMove> FindMoves() { if (_moves != null) { return(_moves); } var moves = new List <Move>(); //get threats against current player var threats = _threatProvider.FindThreatsForPlayer(_board, CurrentPlayer); _threatMatrix = threats; if (GameIsOver) { _moves = moves; return(moves); } //find da man and //find if any chumps be threatenin da man var playerKingLocation = _board.GetKingsLocation(CurrentPlayer); var playerKingThreat = threats.ContainsKey(playerKingLocation) ? threats[playerKingLocation] : null; //if da king has multiple threats then fleeing is his only option if (playerKingThreat != null && playerKingThreat.HasMultipleKingThreats) { //only chance for is for the king to move to a location that is not threatened. FindMoves(moves, threats, playerKingLocation); } else {//we have a few more options //get all pieces for current player. var playerPieceLocations = _board.FindPlayerPieceLocations(CurrentPlayer); //find moves for each given piece playerPieceLocations .ForEach(a => FindMoves(moves, threats, a)); //if the king is in check, now we need to trim the moves appropriately if (playerKingThreat != null) { moves = TrimMovesForKingInCheck(moves, threats, playerKingLocation, playerKingThreat); } } //cache the moves _moves = moves; //return list of available moves return(moves); }
/// <summary> /// Finds all threats for the given player and board. Finds all the opposing players /// pieces and calculates the threats against the passed in player. /// </summary> /// <param name="board"></param> /// <param name="player"></param> /// <returns></returns> public ThreatMatrix FindThreatsForPlayer(Board board, Player player) { //find all opposing pieces var opposingPlayer = player.OpposingPlayer(); var playerKingLocation = board.GetKingsLocation(player); var opposingPieceLocations = board.FindPlayerPieceLocations(opposingPlayer); ThreatMatrix threatMatrix = new ThreatMatrix(); opposingPieceLocations .ForEach(a => AddThreats(board, player, playerKingLocation, a, threatMatrix)); return(threatMatrix); }
/// <summary> /// Finds all threats generated by the given piece at the specified board location. /// returns an empty dictionary if the location does not contain a chess piece. /// </summary> /// <param name="board"></param> /// <param name="boardLocation"></param> /// <returns></returns> public ThreatMatrix FindThreatsForBoardLocation(Board board, BoardLocation boardLocation) { ThreatMatrix threatMatrix = new ThreatMatrix(); var chessPiece = board[boardLocation]; var player = chessPiece.Player().OpposingPlayer(); var opposingPlayer = chessPiece.Player(); if (chessPiece != ChessPiece.None) { AddThreats(board, player, board.GetKingsLocation(player), boardLocation, threatMatrix); } return(threatMatrix); }
private void AddBishopThreats( Board board, Player player, BoardLocation playerKingLocation, BoardLocation opposingPieceBoardLocation, ThreatMatrix threatMatrix) { //slope AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.NorthWest, threatMatrix); AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.SouthEast, threatMatrix); //grade AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.SouthWest, threatMatrix); AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.NorthEast, threatMatrix); }
private void AddRookThreats( Board board, Player player, BoardLocation playerKingLocation, BoardLocation opposingPieceBoardLocation, ThreatMatrix threatMatrix) { //horizontal AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.East, threatMatrix); AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.West, threatMatrix); //vertical AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.North, threatMatrix); AddDirectionalThreats(board, player, playerKingLocation, opposingPieceBoardLocation, Direction.South, threatMatrix); }
private void AddThreats( Board board, Player player, BoardLocation playerKingLocation, BoardLocation opposingPieceBoardLocation, ThreatMatrix threatMatrix) { var chessPiece = board[opposingPieceBoardLocation]; switch (chessPiece) { case ChessPiece.BlackPawn: case ChessPiece.WhitePawn: AddPawnThreats(board, player, playerKingLocation, opposingPieceBoardLocation, threatMatrix); break; case ChessPiece.BlackRook: case ChessPiece.WhiteRook: AddRookThreats(board, player, playerKingLocation, opposingPieceBoardLocation, threatMatrix); break; case ChessPiece.BlackKnight: case ChessPiece.WhiteKnight: AddKnightThreats(board, player, playerKingLocation, opposingPieceBoardLocation, threatMatrix); break; case ChessPiece.BlackBishop: case ChessPiece.WhiteBishop: AddBishopThreats(board, player, playerKingLocation, opposingPieceBoardLocation, threatMatrix); break; case ChessPiece.BlackQueen: case ChessPiece.WhiteQueen: //queen is rook and bishop so we can cheat it AddRookThreats(board, player, playerKingLocation, opposingPieceBoardLocation, threatMatrix); AddBishopThreats(board, player, playerKingLocation, opposingPieceBoardLocation, threatMatrix); break; case ChessPiece.BlackKing: case ChessPiece.WhiteKing: AddKingThreats(board, player, playerKingLocation, opposingPieceBoardLocation, threatMatrix); break; default: throw new Exception($"Unexpected chessPiece value {opposingPieceBoardLocation.ToString()}"); } }
private List <Move> TrimMovesForKingInCheck( List <Move> moves, ThreatMatrix threats, BoardLocation playerKingLocation, Threat playerKingThreat) { var pieceThreateningKing = Board[playerKingThreat.FirstPieceThreateningKingBoardLocation.Value]; //if we have a direct threat to the king then the only moves allowed are ones that capture //the piece which is threatening the king, or of course the ones that represent the king itself moving if (playerKingThreat.ThreatDirection == ThreatDirection.Direct) { return(moves .Where(a => playerKingLocation == a.Source || a.Destination == playerKingThreat.FirstPieceThreateningKingBoardLocation.Value) .ToList()); } else { //ok, so if this is a non-direct threat then any move that is either //captures the piece in question or blocks the threat path is valid //AND of course, any move that represents the king moving List <BoardLocation> allowedBoardLocations = new List <BoardLocation>(); BoardLocation? currentLocation = playerKingThreat.FirstPieceThreateningKingBoardLocation.Value; var direction = playerKingThreat.ThreatDirection.ToDirection(); //get all allowable moves while ((currentLocation != null) && (currentLocation.Value != playerKingLocation)) { allowedBoardLocations.Add(currentLocation.Value); currentLocation = currentLocation.Neighbor(direction); } //get kings moves and remove them from the original moves list and put them into the finalmoves var finalMoves = moves.Where(a => a.Source == playerKingLocation).ToList(); moves.RemoveAll(a => a.Source == playerKingLocation); //Now that the moves list only contains the moves that are non king moves, we can simply //join to the allowableboardlocations and add this to the final moves finalMoves.AddRange( moves .Join(allowedBoardLocations, a => a.Destination, a => a, (a, b) => a)); return(finalMoves); } }
private void FindMoves( List <Move> moves, ThreatMatrix threats, BoardLocation playerPieceLocation) { var chessPiece = _board[playerPieceLocation]; var player = chessPiece.Player(); var playerPiecePinDirection = threats.ContainsKey(playerPieceLocation) ? threats[playerPieceLocation].Pin : default(ThreatDirection?); switch (chessPiece) { case ChessPiece.BlackPawn: case ChessPiece.WhitePawn: FindPawnMoves(moves, threats, player, playerPieceLocation, playerPiecePinDirection); break; case ChessPiece.BlackKnight: case ChessPiece.WhiteKnight: FindKnightMoves(moves, threats, player, playerPieceLocation, playerPiecePinDirection); break; case ChessPiece.BlackBishop: case ChessPiece.WhiteBishop: FindBishopMoves(moves, threats, player, playerPieceLocation, playerPiecePinDirection); break; case ChessPiece.BlackRook: case ChessPiece.WhiteRook: FindRookMoves(moves, threats, player, playerPieceLocation, playerPiecePinDirection); break; case ChessPiece.BlackQueen: case ChessPiece.WhiteQueen: FindQueenMoves(moves, threats, player, playerPieceLocation, playerPiecePinDirection); break; case ChessPiece.BlackKing: case ChessPiece.WhiteKing: //da king cant be pinned yo! FindKingMoves(moves, threats, player, playerPieceLocation); break; default: break; } }
private bool CastleKingsideMoveAvailable(Player player, ThreatMatrix threats) { if (player == Player.White) { return ((!_moveHistory.Any(a => a.Source == BoardLocation.H1)) && //rook has not moved (!threats.ContainsKey(BoardLocation.F1) && !threats.ContainsKey(BoardLocation.G1)) && //no threats in path (_board[BoardLocation.G1].IsEmpty() && _board[BoardLocation.G1].IsEmpty())); //path is empty } else { return ((!_moveHistory.Any(a => a.Source == BoardLocation.H8)) && //rook has not moved (!threats.ContainsKey(BoardLocation.F8) && !threats.ContainsKey(BoardLocation.G8)) && //no threats in path (_board[BoardLocation.F8].IsEmpty() && _board[BoardLocation.G8].IsEmpty())); //path is empty } }
private void AddThreat( ThreatMatrix threatMatrix, BoardLocation playerKingLocation, BoardLocation threateningPieceLocation, BoardLocation boardLocationBeingThreatening, ThreatDirection direction, ThreatDirection?pin = null) { if (!threatMatrix.ContainsKey(boardLocationBeingThreatening)) { threatMatrix.Add(boardLocationBeingThreatening, direction, pin); } else { //update the threat direction threatMatrix[boardLocationBeingThreatening].AddDirection(direction); //can only be pinned in one direction (since there is only one king) if (pin != null) { threatMatrix[boardLocationBeingThreatening].AddPin(pin.Value); } } //if this is a king threat if (playerKingLocation == boardLocationBeingThreatening) { //we set the piece that is threatening the king here. Please note that it is possible more than one piece //is threatening the king but it is IMPOSSIBLE for a triple check to occur. var threat = threatMatrix[boardLocationBeingThreatening]; if (threat.FirstPieceThreateningKingBoardLocation.HasValue) { threat.SecondPieceThreateningKingBoardLocation = threateningPieceLocation; } else { threat.FirstPieceThreateningKingBoardLocation = threateningPieceLocation; } } }
private void FindKingMoves( List <Move> moves, ThreatMatrix threats, Player player, BoardLocation playerPieceLocation) { //we will use the threats matrix for this king to calculate our moves... var kingThreatLocations = _threatProvider.FindThreatsForBoardLocation(_board, playerPieceLocation); foreach (var location in kingThreatLocations.Keys) { //only add moves that do not have a threat on them if ((_board.LocationIsEmptyOrOccupiedBy(location, player.OpposingPlayer())) && (threats.ContainsKey(location) == false)) { moves.Add(new Move(playerPieceLocation, location, _board[location])); } } //find our castle moves FindKingCastleMoves(moves, threats, player, playerPieceLocation); }
public void UndoLastMove() { if (_moveHistory?.Any() == false) { throw new ChessException(ChessErrorCode.UndoMoveIllegal, $"Unable to undo last move because no moves have been made"); } //undo this move now var lastMove = _moveHistory[_moveHistory.Count - 1]; UndoLastMoveForMoveType(lastMove); _moveHistory.RemoveAt(_moveHistory.Count - 1); //clear the moves and threats (for caching of next moves) _moves = null; _threatMatrix = null; //we put the game back into play. We KNOW we can do this because we are //UNDOING a previous move which means the game MUST have been in play in order to allow the move //to be made in the first place. GameState = GameState.InPlay; }
private void AddDirectionalThreats( Board board, Player player, BoardLocation playerKingLocation, BoardLocation opposingPieceBoardLocation, Direction direction, ThreatMatrix threatMatrix) { //get the next square in the given direction BoardLocation?currentLocation = opposingPieceBoardLocation.Neighbor(direction); //for directional threats the king might be pinned... var kingCouldBePinned = playerKingLocation.IsInGivenDirectionFrom(opposingPieceBoardLocation, direction); var threatDirection = direction.ToThreatDirection(); while (currentLocation != null) { //get the piece var piece = board[currentLocation.Value]; //as long as we are empty we can keep moving! if (piece == ChessPiece.None) { //empty square, no pin possible AddThreat(threatMatrix, playerKingLocation, opposingPieceBoardLocation, currentLocation.Value, threatDirection); } else { ThreatDirection?pinDirection = null; //our piece which MIGHT have a pin if our king is on the same directional vector..check for it if ((piece.BelongsTo(player)) && (kingCouldBePinned) && PieceIsPinned(board, currentLocation.Value, playerKingLocation, direction)) { pinDirection = direction.ToThreatDirection(); } AddThreat( threatMatrix, playerKingLocation, opposingPieceBoardLocation, currentLocation.Value, threatDirection, pinDirection); //NOTE: This is a hack, but its a domain hack. From a threat perspective, we can pretend //that the enemy king is 'invisible'. This will allow long range threats to penetrate the king //and proceed to any columns, rows, or diagonals he is on. This in turn will help us //when determining the kings moves by making 'backing up' an invalid strategy for him. //without this check, a king himself would demarcate the end of a long range threat (since a piece is like a stop) //but THAT would create the problem of making it seem like the square behind him was 'safe'. For Example: // | | | | | | | | | | // | |WR|BK| | To=> | |WR| |BK| would be allowed // | | | | | | | | | | //Where it is kind of a hack is by treating the king as invisible it means the white rook is threatening //'beyond' a piece. Theoretically, this would then also allow it to move into those spaces, in essence just //'hoping over' the king. This would be a HUGE problem if not for the golden rule of chess that it is impossible //to make a move that actually captures a king. The game literally ends as soon as a board state emerges where //a king is able to captured on the next turn. Not sure why this is, I am sure it harkens back to some //haughty rule about how 'ignoble' it would be to ever allow the capture of a royal. if (piece.IsKing(player) == false) { //short circuit search since we are done break; } } currentLocation = currentLocation.Neighbor(direction); } }
private void AddKnightThreats( Board board, Player player, BoardLocation playerKingLocation, BoardLocation opposingPieceBoardLocation, ThreatMatrix threatMatrix) { //PGN format for the board // A B C D E F G H //8:| 7|15|23|31|39|47|55|63| //7:| 6| |22| |38|46|54|62| //6:| |13|21|29| |45|53|61| //5:| 4|12|20|28|36|44|52|60| //4:| |11|19|27| |43|51|59| //3:| 2| |18| |34|42|50|58| //2:| 1| 9|17|25|33|41|49|57| //1:| 0| 8|16|24|32|40|48|56| //A knights move from postion 20. //-17 //-15 //-10 //-6 //+17 //+15 //+10 //+6 List <BoardLocation> possibleMoves = new List <BoardLocation>(); var knightLocation = opposingPieceBoardLocation.ToByte(); //same column var leftDown = knightLocation - 17; var leftUp = knightLocation - 15; //same row var upLeft = knightLocation - 6; var upRight = knightLocation + 10; //same column var rightUp = knightLocation + 17; var rightDown = knightLocation + 15; //same row var downLeft = knightLocation - 10; var downRight = knightLocation + 6; if ((leftDown >= 0) && ((knightLocation / 8) - (leftDown / 8) == 2)) { possibleMoves.Add((BoardLocation)leftDown); } if ((leftUp >= 0) && ((knightLocation / 8) - (leftUp / 8) == 2)) { possibleMoves.Add((BoardLocation)leftUp); } if ((upLeft >= 0) && ((knightLocation / 8) - (upLeft / 8) == 1)) { possibleMoves.Add((BoardLocation)upLeft); } if ((downLeft >= 0) && ((knightLocation / 8) - (downLeft / 8) == 1)) { possibleMoves.Add((BoardLocation)downLeft); } if ((upRight < Constants.BoardSize) && ((knightLocation / 8) - (upRight / 8) == -1)) { possibleMoves.Add((BoardLocation)upRight); } if ((downRight < Constants.BoardSize) && ((knightLocation / 8) - (downRight / 8) == -1)) { possibleMoves.Add((BoardLocation)downRight); } if ((rightUp < Constants.BoardSize) && ((knightLocation / 8) - (rightUp / 8) == -2)) { possibleMoves.Add((BoardLocation)rightUp); } if ((rightDown < Constants.BoardSize) && ((knightLocation / 8) - (rightDown / 8) == -2)) { possibleMoves.Add((BoardLocation)rightDown); } possibleMoves .ForEach(a => AddThreat(threatMatrix, playerKingLocation, opposingPieceBoardLocation, a, ThreatDirection.Direct)); }
private void FindPawnMoves( List <Move> moves, ThreatMatrix threats, Player player, BoardLocation playerPieceLocation, ThreatDirection?playerPieceThreatDirection) { var opposingPlayer = player.OpposingPlayer(); var marchDirection = player == Player.White ? Direction.North : Direction.South; var gradeAttackDirection = player == Player.White ? Direction.NorthEast : Direction.SouthWest; var slopeAttackDirection = player == Player.White ? Direction.NorthWest : Direction.SouthEast; //check to see if we can move up/down //note that any diagonal or horizontal threats will block these moves if ((playerPieceThreatDirection.HasThreats(ThreatDirection.Horizontal | ThreatDirection.Diagonal) == false) && (_board.NeighboringLocationIsEmpty(playerPieceLocation, marchDirection))) { var firstMarchLocation = playerPieceLocation.Neighbor(marchDirection).Value; var row = firstMarchLocation.Row(); if (row == 0 || row == 7)//we can assume this is a pawn promotion { moves.Add(new Move(playerPieceLocation, firstMarchLocation, SpecialMoveType.PawnPromotionKnight)); moves.Add(new Move(playerPieceLocation, firstMarchLocation, SpecialMoveType.PawnPromotionBishop)); moves.Add(new Move(playerPieceLocation, firstMarchLocation, SpecialMoveType.PawnPromotionRook)); moves.Add(new Move(playerPieceLocation, firstMarchLocation, SpecialMoveType.PawnPromotionQueen)); } else { moves.Add(new Move(playerPieceLocation, firstMarchLocation)); } //we can also move up two spaces if the next northern square is empty if ((playerPieceLocation.IsPawnStartingLocation(player)) && (_board.NeighboringLocationIsEmpty(firstMarchLocation, marchDirection))) { moves.Add(new Move(playerPieceLocation, firstMarchLocation.Neighbor(marchDirection).Value)); } } //check to see if we can move along the grade //note that any line or slope threats will block this move if (playerPieceThreatDirection.HasThreats(ThreatDirection.Line | ThreatDirection.Slope) == false) { //if we have an opposing piece in the next square, make hte move if (_board.NeighboringLocationIsOccupiedBy(playerPieceLocation, gradeAttackDirection, opposingPlayer)) { var neighbor = playerPieceLocation.Neighbor(gradeAttackDirection).Value; var row = neighbor.Row(); //capture pawn promotion if (row == 0 || row == 7) { moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionKnight)); moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionBishop)); moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionRook)); moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionQueen)); } else//normal capture { moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor])); } } else if (LastMoveAllowsEnPassantFor(playerPieceLocation, gradeAttackDirection)) {//we can do an en passant. var neighbor = playerPieceLocation.Neighbor(gradeAttackDirection).Value; moves.Add(new Move(playerPieceLocation, neighbor, CurrentPlayer == Player.White ? ChessPiece.BlackPawn : ChessPiece.WhitePawn, SpecialMoveType.AuPassant)); } } //check to see if we can move along the slope //note that any line or grade threats will block this move if (playerPieceThreatDirection.HasThreats(ThreatDirection.Line | ThreatDirection.Grade) == false) { //if we have an opposing piece in the next square, make hte move if (_board.NeighboringLocationIsOccupiedBy(playerPieceLocation, slopeAttackDirection, opposingPlayer)) { var neighbor = playerPieceLocation.Neighbor(slopeAttackDirection).Value; var row = neighbor.Row(); //capture pawn promotion if (row == 0 || row == 7) { moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionKnight)); moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionBishop)); moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionRook)); moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor], SpecialMoveType.PawnPromotionQueen)); } else//normal capture { moves.Add(new Move(playerPieceLocation, neighbor, _board[neighbor])); } } else if (LastMoveAllowsEnPassantFor(playerPieceLocation, slopeAttackDirection)) {//we can do an en passant. var neighbor = playerPieceLocation.Neighbor(slopeAttackDirection).Value; moves.Add(new Move(playerPieceLocation, neighbor, CurrentPlayer == Player.White ? ChessPiece.BlackPawn : ChessPiece.WhitePawn, SpecialMoveType.AuPassant)); } } }