Пример #1
0
        private static void generate_castle(
            int Side,
            bool Checks,
            Position pos,
            MoveStack[] ms,
            ref int mpos,
            int us)
        {
            if (pos.castle_impeded(us, Side) || (pos.can_castle_CR(Utils.make_castle_right(us, Side)) == 0))
            {
                return;
            }

            // After castling, the rook and king final positions are the same in Chess960
            // as they would be in standard chess.
            var kfrom = pos.king_square(us);
            var rfrom = pos.castle_rook_square(us, Side);
            var kto = Utils.relative_square(us, Side == CastlingSideC.KING_SIDE ? SquareC.SQ_G1 : SquareC.SQ_C1);

            var enemies = pos.pieces_C(us ^ 1);

            Debug.Assert(!pos.in_check());

            int K = pos.chess960 ? kto > kfrom ? -1 : 1 : Side == CastlingSideC.KING_SIDE ? -1 : 1;
            
            for (Square s = kto; s != kfrom; s += (Square)K)
            {
                if ((pos.attackers_to(s) & enemies) != 0)
                {
                    return;
                }
            }

            // Because we generate only legal castling moves we need to verify that
            // when moving the castling rook we do not discover some hidden checker.
            // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
            if (pos.chess960 && ((pos.attackers_to(kto, Utils.xor_bit(pos.occupied_squares, rfrom)) & enemies) != 0))
            {
                return;
            }

            var m = Utils.make(kfrom, rfrom, MoveTypeC.CASTLING);

            if (Checks)
            {
                var ci = CheckInfoBroker.GetObject();
                ci.CreateCheckInfo(pos);
                var givesCheck = pos.move_gives_check(m, ci);
                CheckInfoBroker.Free();
                if (!givesCheck)
                {
                    return;
                }
            }

            ms[mpos++].move = m;
        }
Пример #2
0
        private static void generate_castle(CastlingSide Side, bool OnlyChecks, Position pos, MoveStack[] ms, ref int mpos, Color us)
        {
            if (pos.castle_impeded(us, Side) || (pos.can_castle_CR(Utils.make_castle_right(us, Side))==0) )
                return;

            // After castling, the rook and king final positions are the same in Chess960
            // as they would be in standard chess.
            Square kfrom = pos.king_square(us);
            Square rfrom = pos.castle_rook_square(us, Side);
            Square kto = Utils.relative_square(us, Side == CastlingSideC.KING_SIDE ? SquareC.SQ_G1 : SquareC.SQ_C1);

            Bitboard enemies = pos.pieces_C(us ^ 1);

            Debug.Assert(!pos.in_check());

            for (Square s = Math.Min(kfrom, kto), e = Math.Max(kfrom, kto); s <= e; s++)
                if (s != kfrom // We are not in check
                    && ((pos.attackers_to(s) & enemies) != 0))
                    return;

            // Because we generate only legal castling moves we need to verify that
            // when moving the castling rook we do not discover some hidden checker.
            // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
            if (pos.chess960
                && ((pos.attackers_to(kto, Utils.xor_bit(pos.occupied_squares, rfrom)) & enemies) != 0))
                return;

            Move m = Utils.make_castle(kfrom, rfrom);

            if (OnlyChecks)
            {
                CheckInfo ci = CheckInfoBroker.GetObject();
                ci.CreateCheckInfo(pos);
                bool givesCheck = pos.move_gives_check(m, ci);
                CheckInfoBroker.Free();
                if (!givesCheck) return;
            }

            ms[mpos++].move = m;
        }
Пример #3
0
        // check_is_dangerous() tests if a checking move can be pruned in qsearch().
        // bestValue is updated only when returning false because in that case move
        // will be pruned.
        static bool check_is_dangerous(Position pos, Move move, Value futilityBase, Value beta)
        {
            Bitboard b, occ, oldAtt, newAtt, kingAtt;
            Square from, to, ksq;
            Piece pc;
            Color them;

            from = Utils.from_sq(move);
            to = Utils.to_sq(move);
            them = Utils.flip_C(pos.sideToMove);
            ksq = pos.king_square(them);
            kingAtt = Position.attacks_from_KING(ksq);
            pc = pos.piece_moved(move);

            occ = pos.occupied_squares ^ Utils.SquareBB[from] ^ Utils.SquareBB[ksq];
            oldAtt = Position.attacks_from(pc, from, occ);
            newAtt = Position.attacks_from(pc, to, occ);

            // Rule 1. Checks which give opponent's king at most one escape square are dangerous
            b = kingAtt & ~pos.pieces_C(them) & ~newAtt & ~(1UL << to);

            if ((b & (b - 1)) == 0) // Catches also !b
                return true;

            // Rule 2. Queen contact check is very dangerous
            if (Utils.type_of(pc) == PieceTypeC.QUEEN
                && (Utils.bit_is_set(kingAtt, to) != 0))
                return true;

            // Rule 3. Creating new double threats with checks
            b = pos.pieces_C(them) & newAtt & ~oldAtt & ~(1UL << ksq);

            while (b != 0)
            {
                // Note that here we generate illegal "double move"!
                if (futilityBase + Position.PieceValueEndgame[pos.piece_on(Utils.pop_1st_bit(ref b))] >= beta)
                    return true;
            }
            return false;
        }
Пример #4
0
        // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite
        // conservative and returns a winning score only when we are very sure that the pawn is winning.
        private static int evaluate_unstoppable_pawns(Position pos, EvalInfo ei)
        {
            ulong b, b2, blockers, supporters, queeningPath, candidates;
            int s, blockSq, queeningSquare;
            int c, winnerSide, loserSide;
            bool pathDefended, opposed;
            int pliesToGo = 0, movesToGo, oppMovesToGo = 0, sacptg, blockersCount, minKingDist, kingptg, d;
            int pliesToQueenWHITE = 256, pliesToQueenBLACK = 256, pliesToQueenWinner = 256;

            // Step 1. Hunt for unstoppable passed pawns. If we find at least one,
            // record how many plies are required for promotion.
            for (c = ColorC.WHITE; c <= ColorC.BLACK; c++)
            {
                // Skip if other side has non-pawn pieces
                if (pos.non_pawn_material(Utils.flip_C(c)) != 0)
                {
                    continue;
                }

                b = ei.pi.passed_pawns(c);

                while (b != 0)
                {
                    s = Utils.pop_lsb(ref b);
                    queeningSquare = Utils.relative_square(c, Utils.make_square(Utils.file_of(s), RankC.RANK_8));
                    queeningPath = Utils.forward_bb(c, s);

                    // Compute plies to queening and check direct advancement
                    movesToGo = Utils.rank_distance(s, queeningSquare)
                                - (Utils.relative_rank_CS(c, s) == RankC.RANK_2 ? 1 : 0);
                    oppMovesToGo = Utils.square_distance(pos.king_square(Utils.flip_C(c)), queeningSquare)
                                   - ((c != pos.sideToMove) ? 1 : 0);
                    pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);

                    if (movesToGo >= oppMovesToGo && !pathDefended)
                    {
                        continue;
                    }

                    // Opponent king cannot block because path is defended and position
                    // is not in check. So only friendly pieces can be blockers.
                    Debug.Assert(!pos.in_check());
                    Debug.Assert((queeningPath & pos.occupied_squares) == (queeningPath & pos.pieces_C(c)));

                    // Add moves needed to free the path from friendly pieces and retest condition
                    movesToGo += Bitcount.popcount_1s_Max15(queeningPath & pos.pieces_C(c));

                    if (movesToGo >= oppMovesToGo && !pathDefended)
                    {
                        continue;
                    }

                    pliesToGo = 2 * movesToGo - ((c == pos.sideToMove) ? 1 : 0);

                    if (c == ColorC.WHITE)
                    {
                        pliesToQueenWHITE = Math.Min(pliesToQueenWHITE, pliesToGo);
                    }
                    else
                    {
                        pliesToQueenBLACK = Math.Min(pliesToQueenBLACK, pliesToGo);
                    }
                }
            }

            // Step 2. If either side cannot promote at least three plies before the other side then situation
            // becomes too complex and we give up. Otherwise we determine the possibly "winning side"
            if (Math.Abs(pliesToQueenWHITE - pliesToQueenBLACK) < 3)
            {
                return ScoreC.SCORE_ZERO;
            }

            winnerSide = (pliesToQueenWHITE < pliesToQueenBLACK ? ColorC.WHITE : ColorC.BLACK);
            pliesToQueenWinner = (winnerSide == ColorC.WHITE) ? pliesToQueenWHITE : pliesToQueenBLACK;
            loserSide = Utils.flip_C(winnerSide);

            // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
            b = candidates = pos.pieces_PTC(PieceTypeC.PAWN, loserSide);

            while (b != 0)
            {
                s = Utils.pop_lsb(ref b);

                // Compute plies from queening
                queeningSquare = Utils.relative_square(loserSide, Utils.make_square(Utils.file_of(s), RankC.RANK_8));
                movesToGo = Utils.rank_distance(s, queeningSquare)
                            - ((Utils.relative_rank_CS(loserSide, s) == RankC.RANK_2) ? 1 : 0);
                pliesToGo = 2 * movesToGo - ((loserSide == pos.sideToMove) ? 1 : 0);

                // Check if (without even considering any obstacles) we're too far away or doubled
                if ((pliesToQueenWinner + 3 <= pliesToGo)
                    || ((Utils.forward_bb(loserSide, s) & pos.pieces_PTC(PieceTypeC.PAWN, loserSide)) != 0))
                {
                    Utils.xor_bit(ref candidates, s);
                }
            }

            // If any candidate is already a passed pawn it _may_ promote in time. We give up.
            if ((candidates & ei.pi.passed_pawns(loserSide)) != 0)
            {
                return ScoreC.SCORE_ZERO;
            }

            // Step 4. Check new passed pawn creation through king capturing and pawn sacrifices
            b = candidates;

            while (b != 0)
            {
                s = Utils.pop_lsb(ref b);
                sacptg = blockersCount = 0;
                minKingDist = kingptg = 256;

                // Compute plies from queening
                queeningSquare = Utils.relative_square(loserSide, Utils.make_square(Utils.file_of(s), RankC.RANK_8));
                movesToGo = Utils.rank_distance(s, queeningSquare)
                            - ((Utils.relative_rank_CS(loserSide, s) == RankC.RANK_2) ? 1 : 0);
                pliesToGo = 2 * movesToGo - ((loserSide == pos.sideToMove) ? 1 : 0);

                // Generate list of blocking pawns and supporters
                supporters = Utils.adjacent_files_bb(Utils.file_of(s)) & candidates;
                opposed = (Utils.forward_bb(loserSide, s) & pos.pieces_PTC(PieceTypeC.PAWN, winnerSide)) != 0;
                blockers = Utils.passed_pawn_mask(loserSide, s) & pos.pieces_PTC(PieceTypeC.PAWN, winnerSide);

                Debug.Assert(blockers != 0);

                // How many plies does it take to remove all the blocking pawns?
                while (blockers != 0)
                {
                    blockSq = Utils.pop_lsb(ref blockers);
                    movesToGo = 256;

                    // Check pawns that can give support to overcome obstacle, for instance
                    // black pawns: a4, b4 white: b2 then pawn in b4 is giving support.
                    if (!opposed)
                    {
                        b2 = supporters & Utils.in_front_bb_CS(winnerSide, blockSq + Utils.pawn_push(winnerSide));

                        while (b2 != 0) // This while-loop could be replaced with LSB/MSB (depending on color)
                        {
                            d = Utils.square_distance(blockSq, Utils.pop_lsb(ref b2)) - 2;
                            movesToGo = Math.Min(movesToGo, d);
                        }
                    }

                    // Check pawns that can be sacrificed against the blocking pawn
                    b2 = Utils.attack_span_mask(winnerSide, blockSq) & candidates & ~(1UL << s);

                    while (b2 != 0) // This while-loop could be replaced with LSB/MSB (depending on color)
                    {
                        d = Utils.square_distance(blockSq, Utils.pop_lsb(ref b2)) - 2;
                        movesToGo = Math.Min(movesToGo, d);
                    }

                    // If obstacle can be destroyed with an immediate pawn exchange / sacrifice,
                    // it's not a real obstacle and we have nothing to add to pliesToGo.
                    if (movesToGo <= 0)
                    {
                        continue;
                    }

                    // Plies needed to sacrifice against all the blocking pawns
                    sacptg += movesToGo * 2;
                    blockersCount++;

                    // Plies needed for the king to capture all the blocking pawns
                    d = Utils.square_distance(pos.king_square(loserSide), blockSq);
                    minKingDist = Math.Min(minKingDist, d);
                    kingptg = (minKingDist + blockersCount) * 2;
                }

                // Check if pawn sacrifice plan _may_ save the day
                if (pliesToQueenWinner + 3 > pliesToGo + sacptg)
                {
                    return ScoreC.SCORE_ZERO;
                }

                // Check if king capture plan _may_ save the day (contains some false positives)
                if (pliesToQueenWinner + 3 > pliesToGo + kingptg)
                {
                    return ScoreC.SCORE_ZERO;
                }
            }

            // Winning pawn is unstoppable and will promote as first, return big score
            var score = Utils.make_score(0, 0x500 - 0x20 * pliesToQueenWinner);
            return winnerSide == ColorC.WHITE ? score : -score;
        }
Пример #5
0
        internal static void generate_evasion(Position pos, MoveStack[] ms, ref int mpos)
        {
            /// generate<EVASIONS> generates all pseudo-legal check evasions when the side
            /// to move is in check. Returns a pointer to the end of the move list.
            Debug.Assert(pos.in_check());

            ulong b;
            int from, checksq;
            var checkersCnt = 0;
            var us = pos.sideToMove;
            var ksq = pos.king_square(us);
            ulong sliderAttacks = 0;
            var checkers = pos.st.checkersBB;

            Debug.Assert(checkers != 0);

            // Find squares attacked by slider checkers, we will remove them from the king
            // evasions so to skip known illegal moves avoiding useless legality check later.
            b = checkers;
            do
            {
                checkersCnt++;
                checksq = Utils.pop_lsb(ref b);

                Debug.Assert(Utils.color_of(pos.piece_on(checksq)) == Utils.flip_C(us));

                switch (Utils.type_of(pos.piece_on(checksq)))
                {
                    case PieceTypeC.BISHOP:
                        sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.BISHOP][checksq];
                        break;
                    case PieceTypeC.ROOK:
                        sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.ROOK][checksq];
                        break;
                    case PieceTypeC.QUEEN:
                        // If queen and king are far or not on a diagonal line we can safely
                        // remove all the squares attacked in the other direction becuase are
                        // not reachable by the king anyway.
                        if ((Utils.between_bb(ksq, checksq) != 0)
                            || ((Utils.bit_is_set(Utils.PseudoAttacks[PieceTypeC.BISHOP][checksq], ksq)) == 0))
                        {
                            sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.QUEEN][checksq];
                        }

                        // Otherwise we need to use real rook attacks to check if king is safe
                        // to move in the other direction. For example: king in B2, queen in A1
                        // a knight in B1, and we can safely move to C1.
                        else
                        {
                            sliderAttacks |= Utils.PseudoAttacks[PieceTypeC.BISHOP][checksq]
                                             | pos.attacks_from_ROOK(checksq);
                        }
                        break;
                    default:
                        break;
                }
            }
            while (b != 0);

            // Generate evasions for king, capture and non capture moves
            b = Position.attacks_from_KING(ksq) & ~pos.pieces_C(us) & ~sliderAttacks;
            from = ksq;
            while (b != 0)
            {
                ms[mpos++].move = Utils.make_move(from, Utils.pop_lsb(ref b));
            }

            // Generate evasions for other pieces only if not under a double check
            if (checkersCnt > 1)
            {
                return;
            }

            // Blocking evasions or captures of the checking piece
            var target = Utils.between_bb(checksq, ksq) | checkers;

            generate_all(GenType.EVASIONS, pos, ms, ref mpos, us, target, null);
        }
Пример #6
0
        // check_is_dangerous() tests if a checking move can be pruned in qsearch().
        // bestValue is updated only when returning false because in that case move
        // will be pruned.
        private static bool check_is_dangerous(Position pos, int move, int futilityBase, int beta)
        {
            //ulong b, occ, oldAtt, newAtt, kingAtt;
            //int from, to, ksq;
            //int pc;
            //int them;

            //from = Utils.from_sq(move);
            //to = Utils.to_sq(move);
            //them = Utils.flip_C(pos.sideToMove);
            //ksq = pos.king_square(them);
            //kingAtt = Position.attacks_from_KING(ksq);
            //pc = pos.piece_moved(move);

            //occ = pos.occupied_squares ^ Utils.SquareBB[from] ^ Utils.SquareBB[ksq];
            //oldAtt = Position.attacks_from(pc, from, occ);
            //newAtt = Position.attacks_from(pc, to, occ);

            //// Rule 1. Checks which give opponent's king at most one escape square are dangerous
            //b = kingAtt & ~pos.pieces_C(them) & ~newAtt & ~(1UL << to);

            //if ((b & (b - 1)) == 0) // Catches also !b
            Piece pc = pos.piece_moved(move);
            Square from = Utils.from_sq(move);
            Square to = Utils.to_sq(move);
            Color them = pos.sideToMove ^ 1;
            Square ksq = pos.king_square(them);
            Bitboard enemies = pos.pieces_C(them);
            Bitboard kingAtt = Position.attacks_from_KING(ksq);
            Bitboard occ = pos.occupied_squares ^ Utils.SquareBB[from] ^ Utils.SquareBB[ksq];
            Bitboard oldAtt = Position.attacks_from(pc, from, occ);
            Bitboard newAtt = Position.attacks_from(pc, to, occ);

            // Checks which give opponent's king at most one escape square are dangerous
            if (!Utils.more_than_one(kingAtt & ~(enemies | newAtt | (ulong)to)))
            {
                return true;
            }

            // Queen contact check is very dangerous
            if (Utils.type_of(pc) == PieceTypeC.QUEEN && (Utils.bit_is_set(kingAtt, to) != 0))
            {
                return true;
            }

            // Creating new double threats with checks is dangerous
            Bitboard b = (enemies ^ (ulong)ksq) & newAtt & ~oldAtt;
            while (b != 0)
            {
                // Note that here we generate illegal "double move"!
                if (futilityBase + Position.PieceValue[PhaseC.EG][pos.piece_on(Utils.pop_lsb(ref b))] >= beta)
                {
                    return true;
                }
            }
            return false;
        }