Example #1
0
    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);
    }
Example #2
0
    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}");
    }
Example #3
0
    /// <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);
    }