public void EnPassantTest() { Index attackerLoc = new Index(4, 'C'); Index victimLoc = new Index(5, 'D'); Index target = new Index(4, 'D'); var piecePositions = new BidirectionalDictionary <(Team, Piece), Index>(); piecePositions.Add(victimLoc, (Team.White, Piece.Pawn4)); piecePositions.Add(attackerLoc, (Team.Black, Piece.Pawn1)); var state = new BoardState(piecePositions, Team.Black, Team.None, Team.None, 0); // var allmoves = MoveGenerator.GetAllPossiblePawnMoves(attackerLoc, Team.Black, state).ToArray(); var allmoves = MoveGenerator.GetAllPossibleMoves(attackerLoc, Piece.Pawn1, Team.Black, state, Enumerable.Empty <Promotion>()).ToArray(); Assert.AreEqual((target, MoveType.EnPassant), allmoves[0]); Assert.AreEqual((attackerLoc.GetNeighborAt(HexNeighborDirection.Down).Value, MoveType.Move), allmoves[1]); Assert.AreEqual(2, allmoves.Length); // Cannot en passant when target is occupied piecePositions.Add(target, (Team.White, Piece.Queen)); // allmoves = MoveGenerator.GetAllPossiblePawnMoves(attackerLoc, Team.Black, state).ToArray(); allmoves = MoveGenerator.GetAllPossibleMoves(attackerLoc, Piece.Pawn1, Team.Black, state, Enumerable.Empty <Promotion>()).ToArray(); Assert.AreEqual((target, MoveType.Attack), allmoves[0]); Assert.AreEqual((attackerLoc.GetNeighborAt(HexNeighborDirection.Down).Value, MoveType.Move), allmoves[1]); Assert.AreEqual(2, allmoves.Length); }
public static string Get(BoardState boardState, Move move, List <Promotion> promotions, NotationType notationType = NotationType.LongForm) { if (move.lastTeam == Team.None) { return(""); } string fromIndex = move.from.GetKey(); string toIndex = move.to.GetKey(); string piece = GetStringForPiece(move.lastPiece, move.lastTeam, promotions, move); string type = move.capturedPiece.HasValue ? "x" : move.defendedPiece.HasValue ? "d" : "m"; // When using freeplace mode, you can swap non-rook pieces, we want to clearly label that as a swap and not a defend unless a rook is involved if (type == "d" && !HexachessagonEngine.GetRealPiece((move.lastTeam, move.lastPiece), promotions).IsRook()) { type = "-"; } string otherPiece = type switch { "x" => GetStringForPiece(move.capturedPiece.Value, move.lastTeam.Enemy(), promotions, move), string t when(t == "d" || t == "-") => GetStringForPiece(move.defendedPiece.Value, move.lastTeam, promotions, move), _ => "" }; string freePlaced = ""; bool shortFormMod = notationType == NotationType.ShortForm; if (move.from == Index.invalid && move.to == Index.invalid) { // No piece moved - skipped move with free place mode fromIndex = ""; toIndex = ""; piece = ""; type = ""; otherPiece = ""; freePlaced = "skipped"; shortFormMod = false; } else if (move.to == Index.invalid) { // Put in jail with free place mode otherPiece = ""; type = ""; toIndex = ""; freePlaced = " jailed"; shortFormMod = false; } else if (move.from == Index.invalid) { // Freed from jail with free place mode fromIndex = ""; otherPiece = ""; type = ""; freePlaced = " freed to "; shortFormMod = false; } string promo = ""; Team lt = move.lastTeam; Piece lp = move.lastPiece; IEnumerable <Promotion> applicablePromotions = promotions.Where(promo => promo.team == lt && promo.from == lp); if (applicablePromotions.Any()) { Promotion applicablePromo = applicablePromotions.First(); promo = applicablePromo.turnNumber == move.turn + (move.lastTeam == Team.Black).BoolToInt() && move.lastTeam == applicablePromo.team ? $"={applicablePromo.to.GetPieceShortString()}" : ""; } string check = boardState.checkmate != Team.None ? "#" : boardState.check != Team.None ? "+" : ""; // modify for shortform if (notationType == NotationType.ShortForm && shortFormMod) { if (piece == "p") { piece = ""; if (type != "-") { otherPiece = ""; type = type == "x" ? type : ""; fromIndex = type == "x" ? $"{fromIndex.First()}" : ""; } } else { List <Piece> alternatePieces = HexachessagonEngine.GetRealPiece((lt, lp), promotions).GetAlternates().ToList(); foreach (Promotion potentialAlternate in promotions) { if (potentialAlternate.turnNumber < move.turn) { List <Piece?> toAdd = new List <Piece?>(); foreach (Piece alt in alternatePieces) { if (potentialAlternate.to == alt) { toAdd.Add(potentialAlternate.from); } } toAdd.ForEach(p => alternatePieces.Add(p.Value)); if (potentialAlternate.from == lp) { alternatePieces.Add(potentialAlternate.to); } if (potentialAlternate.to == lp) { alternatePieces.Add(potentialAlternate.from); } } } type = type == "m" ? "" : type; if (alternatePieces.Count > 0) { bool fileAmbiguity = false; bool rankAmbiguity = false; foreach (Piece alternate in alternatePieces) { if (boardState.TryGetIndex((lt, alternate), out Index index)) { Piece realPiece = HexachessagonEngine.GetRealPiece((lt, alternate), promotions); var possibleMovesConcerningHex = MoveGenerator.GetAllPossibleMoves(index, realPiece, lt, boardState, promotions, true) .Where(kvp => kvp.target != null && kvp.target == move.to); if (MoveValidator.ValidateMoves(possibleMovesConcerningHex, (lt, alternate), boardState, promotions).Any()) { if (index.col == move.from.col) { rankAmbiguity = true; } if (index.row == move.from.row) { fileAmbiguity = true; } } } } if (fileAmbiguity && rankAmbiguity) { fromIndex = move.from.GetKey(); } else if (fileAmbiguity) { fromIndex = $"{move.from.GetLetter()}"; } else if (rankAmbiguity) { fromIndex = $"{move.from.GetNumber()}"; } else { fromIndex = ""; } } else { fromIndex = ""; } } } return($"{piece}{fromIndex}{type}{otherPiece}{freePlaced}{toIndex}{promo}{check}"); }
/// <summary> /// Is a piece from <paramref name="checkForTeam"/> attacking the enemy king? /// </summary> /// <param name="checkForTeam"></param> /// <returns>true if the enemy king is threatened</returns> public static bool IsChecking(Team checkForTeam, BoardState state, List <Promotion> promotions) { Team enemy = checkForTeam.Enemy(); if (!state.allPiecePositions.TryGetValue((enemy, Piece.King), out Index enemyKingLoc)) { return(false); } foreach (var rayDirection in EnumArray <HexNeighborDirection> .Values) { Index?hex = enemyKingLoc; (Team team, Piece piece)occupier; bool isBishopDirection = rayDirection switch { HexNeighborDirection.Up => false, HexNeighborDirection.Down => false, _ => true }; bool isRookDirection = !isBishopDirection; bool isPawnDirection = rayDirection switch { HexNeighborDirection.DownLeft => checkForTeam == Team.White, HexNeighborDirection.DownRight => checkForTeam == Team.White, HexNeighborDirection.UpLeft => checkForTeam != Team.White, HexNeighborDirection.UpRight => checkForTeam != Team.White, _ => false }; for (int distance = 1; distance < 20; distance++) { hex = hex.Value.GetNeighborAt(rayDirection); if (!hex.HasValue) { break; } if (state.allPiecePositions.TryGetValue(hex.Value, out occupier)) { if (occupier.team == checkForTeam) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (distance == 1) { if (isPawnDirection && realPiece.IsPawn()) { return(true); } if (realPiece == Piece.King) { return(true); } } if (isBishopDirection && (realPiece.IsBishop() || realPiece == Piece.Queen)) { return(true); } if (isRookDirection && (realPiece.IsRook() || realPiece == Piece.Queen)) { return(true); } } break; } } } foreach ((Index target, MoveType moveType)move in MoveGenerator.GetAllPossibleMoves(enemyKingLoc, Piece.BlackSquire, enemy, state, promotions)) { if (move.moveType == MoveType.Attack && state.TryGetPiece(move.target, out var occupier)) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsSquire()) { return(true); } } } foreach ((Index target, MoveType moveType)move in MoveGenerator.GetAllPossibleMoves(enemyKingLoc, Piece.KingsKnight, enemy, state, promotions)) { if (move.moveType == MoveType.Attack && state.TryGetPiece(move.target, out var occupier)) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsKnight()) { return(true); } } } for (int i = 1; i < 20; ++i) // Queen/Rook slide left { Index hex = new Index(enemyKingLoc.row, enemyKingLoc.col - i); if (!hex.IsInBounds) { break; } if (state.TryGetPiece(hex, out (Team team, Piece piece)occupier)) { if (occupier.team == checkForTeam) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsRook() || realPiece == Piece.Queen) { return(true); } } break; } } for (int i = 1; i < 20; i++) // Queen/Rook slide right { Index hex = new Index(enemyKingLoc.row, enemyKingLoc.col + i); if (!hex.IsInBounds) { break; } if (state.TryGetPiece(hex, out (Team team, Piece piece)occupier)) { if (occupier.team == checkForTeam) { Piece realPiece = HexachessagonEngine.GetRealPiece(occupier, promotions); if (realPiece.IsRook() || realPiece == Piece.Queen) { return(true); } } break; } } return(false); }