Пример #1
0
        internal static Value Endgame_KBBKN(Color strongerSide, Position pos)
        {
            Color weakerSide = strongerSide ^ 1;

            Debug.Assert(pos.piece_count(strongerSide, PieceTypeC.BISHOP) == 2);
            Debug.Assert(pos.non_pawn_material(strongerSide) == 2 * Constants.BishopValueMidgame);
            Debug.Assert(pos.piece_count(weakerSide, PieceTypeC.KNIGHT) == 1);
            Debug.Assert(pos.non_pawn_material(weakerSide) == Constants.KnightValueMidgame);
            Debug.Assert(pos.pieces_PT(PieceTypeC.PAWN) == 0);

            Value result = Constants.BishopValueEndgame;
            Square wksq = pos.king_square(strongerSide);
            Square bksq = pos.king_square(weakerSide);
            Square nsq = pos.pieceList[weakerSide][PieceTypeC.KNIGHT][0];

            // Bonus for attacking king close to defending king
            result += (DistanceBonus[Utils.square_distance(wksq, bksq)]);

            // Bonus for driving the defending king and knight apart
            result += (Utils.square_distance(bksq, nsq) * 32);

            // Bonus for restricting the knight's mobility
            result += ((8 - Bitcount.popcount_1s_Max15(Position.attacks_from_KNIGHT(nsq))) * 8);

            return strongerSide == pos.sideToMove ? result : -result;
        }
Пример #2
0
        /// MaterialTable::material_info() takes a position object as input,
        /// computes or looks up a MaterialInfo object, and returns a pointer to it.
        /// If the material configuration is not already present in the table, it
        /// is stored there, so we don't have to recompute everything when the
        /// same material configuration occurs again.
        internal void probe(Position pos, out MaterialEntry e)
        {
            var key = pos.material_key();
            e = this.entries[((uint)key) & Constants.MaterialTableMask];

            // If mi->key matches the position's material hash key, it means that we
            // have analysed this material configuration before, and we can simply
            // return the information we found the last time instead of recomputing it.
            if (e.key == key)
            {
                return;
            }

            // Initialize MaterialInfo entry
            var npm = pos.non_pawn_material(ColorC.WHITE) + pos.non_pawn_material(ColorC.BLACK);
            e.value = 0;
            e.scalingFunctionWHITE = null;
            e.scalingFunctionBLACK = null;
            e.spaceWeight = 0;
            e.key = key;
            e.factorWHITE = e.factorBLACK = ScaleFactorC.SCALE_FACTOR_NORMAL;
            e.gamePhase = npm >= MidgameLimit
                              ? PhaseC.PHASE_MIDGAME
                              : npm <= EndgameLimit
                                    ? PhaseC.PHASE_ENDGAME
                                    : (((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));

            // Let's look if we have a specialized evaluation function for this
            // particular material configuration. First we look for a fixed
            // configuration one, then a generic one if previous search failed.
            if ((e.evaluationFunction = Endgame.probeValue(key, out e.evaluationFunctionColor)) != null)
            {
                return;
            }

            if (is_KXK(ColorC.WHITE, pos))
            {
                e.evaluationFunction = Endgame.Endgame_KXK;
                e.evaluationFunctionColor = ColorC.WHITE;
                return;
            }

            if (is_KXK(ColorC.BLACK, pos))
            {
                e.evaluationFunction = Endgame.Endgame_KXK;
                e.evaluationFunctionColor = ColorC.BLACK;
                return;
            }

            if ((pos.pieces_PT(PieceTypeC.PAWN) == 0) && (pos.pieces_PT(PieceTypeC.ROOK) == 0)
                && (pos.pieces_PT(PieceTypeC.QUEEN) == 0))
            {
                // Minor piece endgame with at least one minor piece per side and
                // no pawns. Note that the case KmmK is already handled by KXK.
                Debug.Assert(
                    (pos.pieces_PTC(PieceTypeC.KNIGHT, ColorC.WHITE) | pos.pieces_PTC(PieceTypeC.BISHOP, ColorC.WHITE))
                    != 0);
                Debug.Assert(
                    (pos.pieces_PTC(PieceTypeC.KNIGHT, ColorC.BLACK) | pos.pieces_PTC(PieceTypeC.BISHOP, ColorC.BLACK))
                    != 0);

                if (pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) + pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT)
                    <= 2
                    && pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP)
                    + pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT) <= 2)
                {
                    e.evaluationFunction = Endgame.Endgame_KmmKm;
                    e.evaluationFunctionColor = pos.sideToMove;
                    return;
                }
            }

            // OK, we didn't find any special evaluation function for the current
            // material configuration. Is there a suitable scaling function?
            //
            // We face problems when there are several conflicting applicable
            // scaling functions and we need to decide which one to use.
            EndgameScaleFactor sf;
            int c;
            if ((sf = Endgame.probeScaleFactor(key, out c)) != null)
            {
                if (c == ColorC.WHITE)
                {
                    e.scalingFunctionWHITE = sf;
                }
                else
                {
                    e.scalingFunctionBLACK = sf;
                }
                return;
            }

            // Generic scaling functions that refer to more then one material
            // distribution. Should be probed after the specialized ones.
            // Note that these ones don't return after setting the function.
            if (is_KBPsKs(ColorC.WHITE, pos))
            {
                e.scalingFunctionWHITE = Endgame.Endgame_KBPsK;
            }

            if (is_KBPsKs(ColorC.BLACK, pos))
            {
                e.scalingFunctionBLACK = Endgame.Endgame_KBPsK;
            }

            if (is_KQKRPs(ColorC.WHITE, pos))
            {
                e.scalingFunctionWHITE = Endgame.Endgame_KQKRPs;
            }

            else if (is_KQKRPs(ColorC.BLACK, pos))
            {
                e.scalingFunctionBLACK = Endgame.Endgame_KQKRPs;
            }

            var npm_w = pos.non_pawn_material(ColorC.WHITE);
            var npm_b = pos.non_pawn_material(ColorC.BLACK);

            if (npm_w + npm_b == ValueC.VALUE_ZERO)
            {
                if (pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 0)
                {
                    Debug.Assert(pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) >= 2);
                    e.scalingFunctionWHITE = Endgame.Endgame_KPsK;
                }
                else if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 0)
                {
                    Debug.Assert(pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) >= 2);
                    e.scalingFunctionBLACK = Endgame.Endgame_KPsK;
                }
                else if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 1
                         && pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 1)
                {
                    // This is a special case because we set scaling functions
                    // for both colors instead of only one.
                    e.scalingFunctionWHITE = Endgame.Endgame_KPKP;
                    e.scalingFunctionBLACK = Endgame.Endgame_KPKP;
                }
            }

            // No pawns makes it difficult to win, even with a material advantage
            if (pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN) == 0 && npm_w - npm_b <= Constants.BishopValueMidgame)
            {
                e.factorWHITE =
                    (byte)
                    (npm_w == npm_b || npm_w < Constants.RookValueMidgame
                         ? 0
                         : NoPawnsSF[Math.Min(pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP), 2)]);
            }

            if (pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN) == 0 && npm_b - npm_w <= Constants.BishopValueMidgame)
            {
                e.factorBLACK =
                    (byte)
                    (npm_w == npm_b || npm_b < Constants.RookValueMidgame
                         ? 0
                         : NoPawnsSF[Math.Min(pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP), 2)]);
            }

            // Compute the space weight
            if (npm_w + npm_b
                >= 2 * Constants.QueenValueMidgame + 4 * Constants.RookValueMidgame + 2 * Constants.KnightValueMidgame)
            {
                var minorPieceCount = pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT)
                                      + pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP)
                                      + pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT)
                                      + pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP);

                e.spaceWeight = minorPieceCount * minorPieceCount;
            }

            // Evaluate the material imbalance. We use PIECE_TYPE_NONE as a place holder
            // for the bishop pair "extended piece", this allow us to be more flexible
            // in defining bishop pair bonuses.
            this.pieceCount[0][0] = pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP) > 1 ? 1 : 0;
            this.pieceCount[0][1] = pos.piece_count(ColorC.WHITE, PieceTypeC.PAWN);
            this.pieceCount[0][2] = pos.piece_count(ColorC.WHITE, PieceTypeC.KNIGHT);
            this.pieceCount[0][3] = pos.piece_count(ColorC.WHITE, PieceTypeC.BISHOP);
            this.pieceCount[0][4] = pos.piece_count(ColorC.WHITE, PieceTypeC.ROOK);
            this.pieceCount[0][5] = pos.piece_count(ColorC.WHITE, PieceTypeC.QUEEN);

            this.pieceCount[1][0] = pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP) > 1 ? 1 : 0;
            this.pieceCount[1][1] = pos.piece_count(ColorC.BLACK, PieceTypeC.PAWN);
            this.pieceCount[1][2] = pos.piece_count(ColorC.BLACK, PieceTypeC.KNIGHT);
            this.pieceCount[1][3] = pos.piece_count(ColorC.BLACK, PieceTypeC.BISHOP);
            this.pieceCount[1][4] = pos.piece_count(ColorC.BLACK, PieceTypeC.ROOK);
            this.pieceCount[1][5] = pos.piece_count(ColorC.BLACK, PieceTypeC.QUEEN);

            e.value = (short)((this.imbalance(ColorC.WHITE) - this.imbalance(ColorC.BLACK)) / 16);
        }
Пример #3
0
        /// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and
        /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
        /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
        /// will be used.
        internal static int Endgame_KBPsK(int strongerSide, Position pos)
        {
            var weakerSide = strongerSide ^ 1;

            Debug.Assert(pos.non_pawn_material(strongerSide) == Constants.BishopValueMidgame);
            Debug.Assert(pos.piece_count(strongerSide, PieceTypeC.BISHOP) == 1);
            Debug.Assert(pos.piece_count(strongerSide, PieceTypeC.PAWN) >= 1);

            // No Debug.Assertions about the material of weakerSide, because we want draws to
            // be detected even when the weaker side has some pawns.

            var pawns = pos.pieces_PTC(PieceTypeC.PAWN, strongerSide);
            var pawnFile = Utils.file_of(pos.pieceList[strongerSide][PieceTypeC.PAWN][0]);

            // All pawns are on a single rook file ?
            if ((pawnFile == FileC.FILE_A || pawnFile == FileC.FILE_H) && (((pawns & ~Utils.file_bb_F(pawnFile))) == 0))
            {
                var bishopSq = pos.pieceList[strongerSide][PieceTypeC.BISHOP][0];
                var queeningSq = Utils.relative_square(strongerSide, Utils.make_square(pawnFile, RankC.RANK_8));
                var kingSq = pos.king_square(weakerSide);

                if (Utils.opposite_colors(queeningSq, bishopSq) && Math.Abs(Utils.file_of(kingSq) - pawnFile) <= 1)
                {
                    // The bishop has the wrong color, and the defending king is on the
                    // file of the pawn(s) or the adjacent file. Find the rank of the
                    // frontmost pawn.
                    int rank;
                    if (strongerSide == ColorC.WHITE)
                    {
                        for (rank = RankC.RANK_7; (((Utils.rank_bb_R(rank) & pawns)) == 0); rank--)
                        {
                        }
                        Debug.Assert(rank >= RankC.RANK_2 && rank <= RankC.RANK_7);
                    }
                    else
                    {
                        for (rank = RankC.RANK_2; (((Utils.rank_bb_R(rank) & pawns)) == 0); rank++)
                        {
                        }
                        rank = (rank ^ 7); // HACK to get the relative rank
                        Debug.Assert(rank >= RankC.RANK_2 && rank <= RankC.RANK_7);
                    }
                    // If the defending king has distance 1 to the promotion square or
                    // is placed somewhere in front of the pawn, it's a draw.
                    if (Utils.square_distance(kingSq, queeningSq) <= 1
                        || Utils.relative_rank_CS(strongerSide, kingSq) >= rank)
                    {
                        return ScaleFactorC.SCALE_FACTOR_DRAW;
                    }
                }
            }

            // All pawns on same B or G file? Then potential draw
            if ((pawnFile == FileC.FILE_B || pawnFile == FileC.FILE_G)
                  && (pos.pieces_PT(PieceTypeC.PAWN) & ~Utils.file_bb_F(pawnFile)) == 0
                  && pos.non_pawn_material(weakerSide) == 0
                  && pos.piece_count(weakerSide, PieceTypeC.PAWN) >= 1)
            {
                // Get weaker pawn closest to opponent's queening square
                Bitboard wkPawns = pos.pieces_PTC(PieceTypeC.PAWN, weakerSide);
                Square weakerPawnSq = strongerSide == ColorC.WHITE ? Utils.msb(wkPawns) : Utils.lsb(wkPawns);

                Square strongerKingSq = pos.king_square(strongerSide);
                Square weakerKingSq = pos.king_square(weakerSide);
                Square bishopSq = pos.pieceList[strongerSide][PieceTypeC.BISHOP][0];

                // Draw if weaker pawn is on rank 7, bishop can't attack the pawn, and
                // weaker king can stop opposing opponent's king from penetrating.
                if (Utils.relative_rank_CS(strongerSide, weakerPawnSq) == RankC.RANK_7
                    && Utils.opposite_colors(bishopSq, weakerPawnSq)
                    && Utils.square_distance(weakerPawnSq, weakerKingSq) <= Utils.square_distance(weakerPawnSq, strongerKingSq))
                    return ScaleFactorC.SCALE_FACTOR_DRAW;
            }

            return ScaleFactorC.SCALE_FACTOR_NONE;
        }