public void PawnSwap() { ChessBoard b = new ChessBoard(); b.Set("e1", new ChessPiece() { Player = PlayerTypeEnum.White, Type = PieceTypeEnum.King }); b.Set("f7", new ChessPiece() { Player = PlayerTypeEnum.White, Type = PieceTypeEnum.Pawn }); b.Set("e8", new ChessPiece() { Player = PlayerTypeEnum.Black, Type = PieceTypeEnum.King }); ChessGame g = new ChessGame(b, PlayerTypeEnum.White, null); var moveResult = g.PlayerMove(PlayerTypeEnum.White, "f7f8"); ChessPiece queen = b.Get("f8"); Assert.IsTrue(moveResult.Success); Assert.IsTrue(queen.Type == PieceTypeEnum.Queen); }
private bool CanMoveIntoAttackersPath(PlayerTypeEnum player, string lastMoveNotation, ChessBoard board, List<string> attackers, Tuple<int, int> opponentKingTuple) { if (attackers.Count > 1) return false; bool canBlockPathOfAttackers = false; string attackerNotation = attackers.First(); Tuple<int, int> attackerTuple = NotationHelper.Translate(attackerNotation); ChessPiece attackerPiece = board.Get(attackerTuple); if (attackerPiece.Type == PieceTypeEnum.Knight) return false; // get star pattern of each attacker List<Tuple<int, int>[]> relativePaths = new QueenRelativePathProvider().GetRelativePaths( new ChessPiece() { Player = player }); var absolutePaths = BoardLogic.ConvertToAbsolutePaths(attackerTuple, relativePaths); // get path that leads to king var pathOfAttacker = BoardLogic.GetPathContaining(opponentKingTuple, absolutePaths); var trimmedPathOfAttacker = BoardLogic.TrimToBeforeDestination(opponentKingTuple, pathOfAttacker); // can any opponents move into path and block? var opponentPlayerSquares = board.GetPiecesForPlayer(GetOppositePlayer(player)); foreach (var opponentSquare in opponentPlayerSquares) { bool canMoveIntoAttackersPath = trimmedPathOfAttacker.Any(location => { string dest = NotationHelper.Translate(location); return CanMove(board, opponentSquare + dest, GetOppositePlayer(player), lastMoveNotation).Success; }); if (canMoveIntoAttackersPath) { canBlockPathOfAttackers = true; break; } } return canBlockPathOfAttackers; }
private ValidateMoveResult CanMove(ChessBoard board, string source, string dest, PlayerTypeEnum player, string lastMoveNotation, bool playerInCheck) { var result = new ValidateMoveResult(); var sourceTuple = NotationHelper.Translate(source); if (!BoardLogic.IsOnBoard(sourceTuple)) return result.SetMessage(source + " is not on board"); var destTuple = NotationHelper.Translate(dest); if (!BoardLogic.IsOnBoard(destTuple)) return result.SetMessage(dest + " is not on board"); if (source.Equals(dest, StringComparison.OrdinalIgnoreCase)) return result.SetMessage("source and dest are the same"); var sourcePiece = board.Get(sourceTuple); if (sourcePiece == null) return result.SetMessage("source piece is null"); if (sourcePiece.Player != player) return result.SetMessage("piece does not belong to player."); var destPiece = board.Get(destTuple); if (destPiece != null && sourcePiece.Player == destPiece.Player) return result.SetMessage("cannot move on top of own piece"); var relativeMoves = m_relativeMoveProviderFactory .GetProviderByPieceType(sourcePiece.Type) .GetRelativePaths(sourcePiece); var absoluteMoves = BoardLogic.ConvertToAbsolutePaths(sourceTuple, relativeMoves); bool destInPath = BoardLogic.IsInAtleastOnePath(destTuple, absoluteMoves); if (!destInPath) return result.SetMessage("destination not in available path"); string takenPiece = null; string additionalMove = null; switch (sourcePiece.Type) { case PieceTypeEnum.Pawn: { bool isRankOnlyMove = BoardLogic.IsRankOnlyMove(sourceTuple, destTuple); if (isRankOnlyMove) { var pathContaingDestination = BoardLogic.GetPathContaining(destTuple, absoluteMoves); if (!BoardLogic.IsPathClear(board, pathContaingDestination)) return result.SetMessage("path blocked"); var absDelta = BoardLogic.GetAbsoluteDelta(sourceTuple, destTuple); if (absDelta.Item1 == 2 && sourcePiece.HasMoved) return result.SetMessage("pawn has already moved, cannot jump 2 squares"); } else { // en-passant // > is piece in source *rank, destination *file an enemy pawn? // > if yes, was its last move two squares?e var pieceBehindTuple = new Tuple<int, int>( sourceTuple.Item1, destTuple.Item2); var pieceBehind = board.Get(pieceBehindTuple); bool targetWasLastMove = false; bool twoSquareLastMove = false; Tuple<string, string> lastMove = null; if (lastMoveNotation != null) { lastMove = NotationHelper.Breakdown(lastMoveNotation); var lastMoveSourceTuple = NotationHelper.Translate(lastMove.Item1); var lastMoveDestTuple = NotationHelper.Translate(lastMove.Item2); var lastMoveAbsDelta = BoardLogic.GetAbsoluteDelta(lastMoveSourceTuple, lastMoveDestTuple); twoSquareLastMove = lastMoveAbsDelta.Item1 == 2; targetWasLastMove = pieceBehindTuple.Item1 == lastMoveDestTuple.Item1 && pieceBehindTuple.Item2 == lastMoveDestTuple.Item2; } bool enPassant = pieceBehind != null && pieceBehind.Player != player && pieceBehind.Type == PieceTypeEnum.Pawn && twoSquareLastMove && targetWasLastMove ; if (enPassant) { takenPiece = lastMove.Item2; break; } if (destPiece == null) return result.SetMessage("enemy piece not present at destination"); } break; } case PieceTypeEnum.King: { // castle if (BoardLogic.IsFileOnlyMove(sourceTuple, destTuple) && BoardLogic.GetAbsoluteDelta(sourceTuple, destTuple).Item2 == 2) { if (sourcePiece.HasMoved) return result.SetMessage("king has already moved."); ChessPiece rook = null; Tuple<int, int> rookTuple = null; Tuple<int, int> toRookDelta = null; var rookNotations = board.GetPiecesOfType(player, PieceTypeEnum.Rook); if (rookNotations != null && rookNotations.Count > 0) { var moveDelta = BoardLogic.GetDelta(sourceTuple, destTuple); foreach (var rn in rookNotations) { rookTuple = NotationHelper.Translate(rn); toRookDelta = BoardLogic.GetDelta(sourceTuple, rookTuple); if (BoardLogic.IsFileOnlyMove(sourceTuple, rookTuple) && BoardLogic.AreDeltasInSameDirection(moveDelta.Item2, toRookDelta.Item2)) { rook = board.Get(rn); break; } } } if (rook == null) return result.SetMessage("no rook present on rank " + sourceTuple.Item1); if (rook.HasMoved) return result.SetMessage("rook has moved."); Tuple<int, int>[] kingToRookPath = BoardLogic.GetRankPath(sourceTuple, rookTuple); Tuple<int, int>[] trimmedPath = BoardLogic.TrimToBeforeDestination(rookTuple, kingToRookPath); bool isPathClear = BoardLogic.IsPathClear(board, trimmedPath); if (!isPathClear) return result.SetMessage("path blocked"); if (playerInCheck) return result.SetMessage("cannot castle out of check"); bool moveToLeft = toRookDelta.Item2 < 0; additionalMove = NotationHelper.Translate(rookTuple) + NotationHelper.Translate(new Tuple<int, int>(rookTuple.Item1, destTuple.Item2 + (moveToLeft ? 1 : -1))); } break; } case PieceTypeEnum.Knight: { break; } case PieceTypeEnum.Bishop: case PieceTypeEnum.Rook: case PieceTypeEnum.Queen: { // cases handled in general var pathContaingDestination = BoardLogic.GetPathContaining(destTuple, absoluteMoves); var trimmedPath = BoardLogic.TrimToBeforeDestination(destTuple, pathContaingDestination); if (!BoardLogic.IsPathClear(board, trimmedPath)) return result.SetMessage("path blocked"); break; } } if (destPiece != null) takenPiece = dest; result.Success = true; result.TakenPiece = takenPiece; result.Moves = new[] { source + dest, additionalMove }.Where(x => x != null).ToArray(); return result; }
public ValidateMoveResult CanMove(ChessBoard board, string move, PlayerTypeEnum player, string lastMoveNotation) { Tuple<string, string> breakdown = NotationHelper.Breakdown(move); string source = breakdown.Item1; string destination = breakdown.Item2; bool playerInCheck = IsInCheck(player, board, lastMoveNotation).Item1; ValidateMoveResult canMoveResult = CanMove(board, source, destination, player, lastMoveNotation, playerInCheck); if (canMoveResult.Success) { // pawn to queen swap ChessPiece sourceBeforeSimulate = board.Get(source); bool pawnSwap = sourceBeforeSimulate != null && sourceBeforeSimulate.Type == PieceTypeEnum.Pawn && (sourceBeforeSimulate.Direction == -1 ? (int.Parse(destination[1].ToString()) == 8) : (int.Parse(destination[1].ToString()) == 1)); string swap = pawnSwap ? destination : null; canMoveResult.Swap = swap; ChessBoard boardAfterMove = board.SimulateMoves(move, canMoveResult.TakenPiece, swap); // did player move into check? var movedIntoCheckResult = IsInCheck(player, boardAfterMove, lastMoveNotation); if (movedIntoCheckResult.Item1) { canMoveResult.Success = false; return canMoveResult.SetMessage("cannot move into check."); } // did player put opponent in check? var isOpponentInCheckResult = IsInCheck(GetOppositePlayer(player), boardAfterMove, lastMoveNotation); bool isOpponentInCheck = isOpponentInCheckResult.Item1; canMoveResult.MoveType = isOpponentInCheck ? MoveTypeEnum.Check : MoveTypeEnum.Normal; // if player checked opponent, can opponent get out of check? if not, check mate if (isOpponentInCheck) { string opponentKingNotation = boardAfterMove.GetKing(GetOppositePlayer(player)); Tuple<int, int> opponentKingTuple = NotationHelper.Translate(opponentKingNotation); // can checked king move out of check? bool canMoveOutOfCheck = CanMoveOutOfCheck(player, lastMoveNotation, boardAfterMove, opponentKingNotation, opponentKingTuple); // can opponent kill piece that put king in check? is still in check afterwards? bool canTakedownAttacker = false; if (!canMoveOutOfCheck) canTakedownAttacker = CanTakedownAttackers(player, lastMoveNotation, boardAfterMove, isOpponentInCheckResult.Item2); // can block path of piece that put king in check? (brute force method) bool canBlockPathOfAttackers = false; if (!canMoveOutOfCheck && !canTakedownAttacker) canBlockPathOfAttackers = CanMoveIntoAttackersPath(player, lastMoveNotation, boardAfterMove, isOpponentInCheckResult.Item2, opponentKingTuple); if (!canMoveOutOfCheck && !canTakedownAttacker && !canBlockPathOfAttackers) canMoveResult.MoveType = MoveTypeEnum.Checkmate; } } return canMoveResult; }
private bool CanMoveOutOfCheck(PlayerTypeEnum player, string lastMoveNotation, ChessBoard board, string opponentKingNotation, Tuple<int, int> opponentKingTuple) { bool canMoveOutOfCheck = false; ChessPiece opponentKingPiece = board.Get(opponentKingTuple); var relativeMoves = m_relativeMoveProviderFactory .GetProviderByPieceType(opponentKingPiece.Type) .GetRelativePaths(opponentKingPiece); var absolutePaths = BoardLogic.ConvertToAbsolutePaths(opponentKingTuple, relativeMoves); var flattenedAbsoluteMoves = absolutePaths.SelectMany(x => x); foreach (var square in flattenedAbsoluteMoves) { // simulate move of king to available move string destinationNotation = NotationHelper.Translate(square); var getKingOutOfCheckMoveResult = CanMove(board, opponentKingNotation + destinationNotation, GetOppositePlayer(player), lastMoveNotation); if (getKingOutOfCheckMoveResult.Success) { canMoveOutOfCheck = true; break; } } return canMoveOutOfCheck; }
public static bool IsPathClear(ChessBoard board, Tuple<int, int>[] path) { foreach (var move in path) { if (board.Get(move) != null) return false; } return true; }