Пример #1
0
        /// Mate with KX vs K. This function is used to evaluate positions with
        /// king and plenty of material vs a lone king. It simply gives the
        /// attacking side a bonus for driving the defending king towards the edge
        /// of the board, and for keeping the distance between the two kings small.
        public Value KXK(Position pos)
        {
            Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0));
            Debug.Assert(0 == pos.checkers()); // Eval is never called when in check

            // Stalemate detection with lone king
            if (pos.side_to_move() == weakSide && 0 == (new MoveList(pos, GenTypeS.LEGAL)).size())
            {
                return(ValueS.VALUE_DRAW);
            }

            Square winnerKSq = pos.king_square(strongSide);
            Square loserKSq  = pos.king_square(weakSide);

            Value result = pos.non_pawn_material(strongSide)
                           + pos.count(strongSide, PieceTypeS.PAWN) * ValueS.PawnValueEg
                           + PushToEdges[loserKSq]
                           + PushClose[BitBoard.square_distance(winnerKSq, loserKSq)];

            if (pos.count(strongSide, PieceTypeS.QUEEN) != 0 ||
                pos.count(strongSide, PieceTypeS.ROOK) != 0 ||
                (pos.count(strongSide, PieceTypeS.BISHOP) != 0 && pos.count(strongSide, PieceTypeS.KNIGHT) != 0) ||
                pos.bishop_pair(strongSide))
            {
                result += ValueS.VALUE_KNOWN_WIN;
            }

            return(strongSide == pos.side_to_move() ? result : -result);
        }
Пример #2
0
        /// TranspositionTable::resize() sets the size of the transposition table,
        /// measured in megabytes. Transposition table consists of a power of 2 number
        /// of clusters and each cluster consists of ClusterSize number of TTEntry.
        public void resize(UInt64 mbSize)
        {
            Debug.Assert(BitBoard.msb((mbSize << 20) / 16) < 32);

            uint size = ClusterSize << BitBoard.msb((mbSize << 20) / 64);

            if (hashMask == size - ClusterSize)
            {
                return;
            }

            hashMask = size - ClusterSize;

            try
            {
                table = new TTEntry[size];
                for (int i = 0; i < table.Length; i++)
                {
                    table[i] = new TTEntry();
                }
            }
            catch (Exception)
            {
                System.Console.Error.WriteLine("Failed to allocate " + mbSize + "MB for transposition table.");
                throw new Exception("Failed to allocate " + mbSize + "MB for transposition table.");
            }
        }
Пример #3
0
            /// Entry::shelter_storm() calculates shelter and storm penalties for the file
            /// the king is on, as well as the two adjacent files.
            public Value shelter_storm(Position pos, Square ksq, Color Us)
            {
                Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

                Value    safety = Pawns.MaxSafetyBonus;
                Bitboard b = pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.in_front_bb(Us, Types.rank_of(ksq)) | BitBoard.rank_bb_square(ksq));
                Bitboard ourPawns = b & pos.pieces_color(Us);
                Bitboard theirPawns = b & pos.pieces_color(Them);
                Rank     rkUs, rkThem;
                File     kf = Math.Max(FileS.FILE_B, Math.Min(FileS.FILE_G, Types.file_of(ksq)));

                for (File f = kf - 1; f <= kf + 1; ++f)
                {
                    b    = ourPawns & BitBoard.file_bb_file(f);
                    rkUs = b != 0 ? Types.relative_rank_square(Us, BitBoard.backmost_sq(Us, b)) : RankS.RANK_1;

                    b      = theirPawns & BitBoard.file_bb_file(f);
                    rkThem = b != 0 ? Types.relative_rank_square(Us, BitBoard.frontmost_sq(Them, b)) : RankS.RANK_1;

                    if ((MiddleEdges & BitBoard.SquareBB[Types.make_square(f, rkThem)]) != 0 &&
                        Types.file_of(ksq) == f &&
                        Types.relative_rank_square(Us, ksq) == rkThem - 1)
                    {
                        safety += 200;
                    }
                    else
                    {
                        safety -= ShelterWeakness[rkUs]
                                  + StormDanger[rkUs == RankS.RANK_1 ? 0 : rkThem == rkUs + 1 ? 2 : 1][rkThem];
                    }
                }

                return(safety);
            }
Пример #4
0
        public KPKPosition(uint idx)
        {
            wksq   = (Square)((idx >> 0) & 0x3F);
            bksq   = (Square)((idx >> 6) & 0x3F);
            us     = (Color)((idx >> 12) & 0x01);
            psq    = Types.make_square((File)((idx >> 13) & 0x03), (Rank)(RankS.RANK_7 - (idx >> 15)));
            result = Result.UNKNOWN;

            // Check if two pieces are on the same square or if a king can be captured
            if (BitBoard.square_distance(wksq, bksq) <= 1 ||
                wksq == psq ||
                bksq == psq ||
                (us == ColorS.WHITE && (BitBoard.StepAttacksBB[PieceTypeS.PAWN][psq] & BitBoard.SquareBB[bksq]) != 0))
            {
                result = Result.INVALID;
            }

            else if (us == ColorS.WHITE)
            {
                // Immediate win if pawn can be promoted without getting captured
                if (Types.rank_of(psq) == RankS.RANK_7 &&
                    wksq != psq + SquareS.DELTA_N &&
                    (BitBoard.square_distance(bksq, psq + SquareS.DELTA_N) > 1 ||
                     (BitBoard.StepAttacksBB[PieceTypeS.KING][wksq] & BitBoard.SquareBB[psq + SquareS.DELTA_N]) != 0))
                {
                    result = Result.WIN;
                }
            }
            // Immediate draw if it is a stalemate or a king captures undefended pawn
            else if (0 == (BitBoard.StepAttacksBB[PieceTypeS.KING][bksq] & ~(BitBoard.StepAttacksBB[PieceTypeS.KING][wksq] | BitBoard.StepAttacksBB[PieceTypeS.PAWN][psq])) ||
                     (BitBoard.StepAttacksBB[PieceTypeS.KING][bksq] & BitBoard.SquareBB[psq] & ~BitBoard.StepAttacksBB[PieceTypeS.KING][wksq]) != 0)
            {
                result = Result.DRAW;
            }
        }
Пример #5
0
        // init_eval_info() initializes king bitboards for given color adding
        // pawn attacks. To be done at the beginning of the evaluation.
        public static void init_eval_info(Position pos, EvalInfo ei, Color Us)
        {
            Color  Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);
            Square Down = (Us == ColorS.WHITE ? SquareS.DELTA_S : SquareS.DELTA_N);

            ei.pinnedPieces[Us] = pos.pinned_pieces(Us);

            Bitboard b = ei.attackedBy[Them][PieceTypeS.KING] = pos.attacks_from_square_piecetype(pos.king_square(Them), PieceTypeS.KING);

            ei.attackedBy[Us][PieceTypeS.ALL_PIECES] = ei.attackedBy[Us][PieceTypeS.PAWN] = ei.pi.pawn_attacks(Us);

            // Init king safety tables only if we are going to use them
            if (pos.count(Us, PieceTypeS.QUEEN) != 0 && pos.non_pawn_material(Us) > ValueS.QueenValueMg + ValueS.PawnValueMg)
            {
                ei.kingRing[Them] = b | BitBoard.shift_bb(b, Down);
                b &= ei.attackedBy[Us][PieceTypeS.PAWN];
                ei.kingAttackersCount[Us]           = (b != 0) ? Bitcount.popcount_Max15(b) : 0;
                ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
            }
            else
            {
                ei.kingRing[Them]         = 0;
                ei.kingAttackersCount[Us] = 0;
            }
        }
Пример #6
0
        // evaluate_outposts() evaluates bishop and knight outpost squares
        public static Score evaluate_outposts(Position pos, EvalInfo ei, Square s, PieceType Pt, Color Us)
        {
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            Debug.Assert(Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.KNIGHT);

            // Initial bonus based on square
            Value bonus = Outpost[Pt == PieceTypeS.BISHOP ? 1 : 0][Types.relative_square(Us, s)];

            // Increase bonus if supported by pawn, especially if the opponent has
            // no minor piece which can trade with the outpost piece.
            if (bonus != 0 && (ei.attackedBy[Us][PieceTypeS.PAWN] & BitBoard.SquareBB[s]) != 0)
            {
                if (0 == pos.pieces_color_piecetype(Them, PieceTypeS.KNIGHT) &&
                    0 == (BitBoard.squares_of_color(s) & pos.pieces_color_piecetype(Them, PieceTypeS.BISHOP)))
                {
                    bonus += bonus + bonus / 2;
                }
                else
                {
                    bonus += bonus / 2;
                }
            }
            return(Types.make_score(bonus, bonus));
        }
Пример #7
0
        /// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
        /// underpromotions that give check. Returns a pointer to the end of the move list.
        public static int generate_quiet_checks(Position pos, ExtMove[] mlist, int mPos)
        {
            Debug.Assert(0 == pos.checkers());

            Color     us = pos.side_to_move();
            CheckInfo ci = new CheckInfo(pos);
            Bitboard  dc = ci.dcCandidates;

            while (dc != 0)
            {
                Square    from = BitBoard.pop_lsb(ref dc);
                PieceType pt   = Types.type_of_piece(pos.piece_on(from));

                if (pt == PieceTypeS.PAWN)
                {
                    continue; // Will be generated togheter with direct checks
                }
                Bitboard b = pos.attacks_from_piece_square((Piece)pt, from) & ~pos.pieces();

                if (pt == PieceTypeS.KING)
                {
                    b &= ~BitBoard.PseudoAttacks[PieceTypeS.QUEEN][ci.ksq];
                }

                while (b != 0)
                {
                    mlist[mPos++].move = Types.make_move(from, BitBoard.pop_lsb(ref b));
                }
            }

            return(us == ColorS.WHITE ? generate_all(pos, mlist, mPos, ~pos.pieces(), ColorS.WHITE, GenTypeS.QUIET_CHECKS, ci) :
                   generate_all(pos, mlist, mPos, ~pos.pieces(), ColorS.BLACK, GenTypeS.QUIET_CHECKS, ci));
        }
Пример #8
0
        public static int generate_promotions(ExtMove[] mlist, int mPos, Bitboard pawnsOn7, Bitboard target, CheckInfo ci, GenType Type, Square Delta)
        {
            Bitboard b = BitBoard.shift_bb(pawnsOn7, Delta) & target;

            while (b != 0)
            {
                Square to = BitBoard.pop_lsb(ref b);

                if (Type == GenTypeS.CAPTURES || Type == GenTypeS.EVASIONS || Type == GenTypeS.NON_EVASIONS)
                {
                    mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.QUEEN);
                }

                if (Type == GenTypeS.QUIETS || Type == GenTypeS.EVASIONS || Type == GenTypeS.NON_EVASIONS)
                {
                    mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.ROOK);
                    mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.BISHOP);
                    mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.KNIGHT);
                }

                // Knight promotion is the only promotion that can give a direct check
                // that's not already included in the queen promotion.
                if (Type == GenTypeS.QUIET_CHECKS && (BitBoard.StepAttacksBB[PieceS.W_KNIGHT][to] & BitBoard.SquareBB[ci.ksq]) != 0)
                {
                    mlist[mPos++].move = Types.make(to - Delta, to, MoveTypeS.PROMOTION, PieceTypeS.KNIGHT);
                }
            }

            return(mPos);
        }
Пример #9
0
        // polyglot_key() returns the PolyGlot hash key of the given position
        public static Key polyglot_key(Position pos)
        {
            Key      key = 0;
            Bitboard b   = pos.pieces();

            while (b != 0)
            {
                Square s  = BitBoard.pop_lsb(ref b);
                Piece  pc = pos.piece_on(s);
                // PolyGlot pieces are: BP = 0, WP = 1, BN = 2, ... BK = 10, WK = 11
                int pieceOfs = 2 * (Types.type_of_piece(pc) - 1) + ((Types.color_of(pc) == ColorS.WHITE) ? 1 : 0);
                key ^= PG[psq + (64 * pieceOfs + s)];
            }

            b = (ulong)pos.can_castle_castleright(CastlingRightS.ANY_CASTLING);

            while (b != 0)
            {
                key ^= PG[castle + BitBoard.pop_lsb(ref b)];
            }

            if (pos.ep_square() != SquareS.SQ_NONE)
            {
                key ^= PG[enpassant + Types.file_of(pos.ep_square())];
            }

            if (pos.side_to_move() == ColorS.WHITE)
            {
                key ^= PG[turn + 0];
            }

            return(key);
        }
Пример #10
0
        /// KBP vs KB. There are two rules: if the defending king is somewhere along the
        /// path of the pawn, and the square of the king is not of the same color as the
        /// stronger side's bishop, it's a draw. If the two bishops have opposite color,
        /// it's almost always a draw.
        public ScaleFactor KBPKB(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 1));
            Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0));

            Square pawnSq           = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square strongerBishopSq = pos.list(strongSide, PieceTypeS.BISHOP)[0];
            Square weakerBishopSq   = pos.list(weakSide, PieceTypeS.BISHOP)[0];
            Square weakerKingSq     = pos.king_square(weakSide);

            // Case 1: Defending king blocks the pawn, and cannot be driven away
            if (Types.file_of(weakerKingSq) == Types.file_of(pawnSq) &&
                Types.relative_rank_square(strongSide, pawnSq) < Types.relative_rank_square(strongSide, weakerKingSq) &&
                (Types.opposite_colors(weakerKingSq, strongerBishopSq) ||
                 Types.relative_rank_square(strongSide, weakerKingSq) <= RankS.RANK_6))
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            // Case 2: Opposite colored bishops
            if (Types.opposite_colors(strongerBishopSq, weakerBishopSq))
            {
                // We assume that the position is drawn in the following three situations:
                //
                //   a. The pawn is on rank 5 or further back.
                //   b. The defending king is somewhere in the pawn's path.
                //   c. The defending bishop attacks some square along the pawn's path,
                //      and is at least three squares away from the pawn.
                //
                // These rules are probably not perfect, but in practice they work
                // reasonably well.

                if (Types.relative_rank_square(strongSide, pawnSq) <= RankS.RANK_5)
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
                else
                {
                    Bitboard path = BitBoard.forward_bb(strongSide, pawnSq);

                    if ((path & pos.pieces_color_piecetype(weakSide, PieceTypeS.KING)) != 0)
                    {
                        return(ScaleFactorS.SCALE_FACTOR_DRAW);
                    }

                    if (((pos.attacks_from_square_piecetype(weakerBishopSq, PieceTypeS.BISHOP) & path) != 0) &&
                        BitBoard.square_distance(weakerBishopSq, pawnSq) >= 3)
                    {
                        return(ScaleFactorS.SCALE_FACTOR_DRAW);
                    }
                }
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
Пример #11
0
        // evaluate_unstoppable_pawns() scores the most advanced among the passed and
        // candidate pawns. In case opponent has no pieces but pawns, this is somewhat
        // related to the possibility that pawns are unstoppable.
        public static Score evaluate_unstoppable_pawns(Position pos, Color us, EvalInfo ei)
        {
            Bitboard b = ei.pi.passed_pawns(us) | ei.pi.candidate_pawns(us);

            if (0 == b || pos.non_pawn_material(Types.notColor(us)) != 0)
            {
                return(ScoreS.SCORE_ZERO);
            }

            return(Unstoppable * (Types.relative_rank_square(us, BitBoard.frontmost_sq(us, b))));
        }
Пример #12
0
        /// KR vs KN. The attacking side has slightly better winning chances than
        /// in KR vs KB, particularly if the king and the knight are far apart.
        public Value KRKN(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 0));
            Debug.Assert(verify_material(pos, weakSide, ValueS.KnightValueMg, 0));

            Square bksq   = pos.king_square(weakSide);
            Square bnsq   = pos.list(weakSide, PieceTypeS.KNIGHT)[0];
            Value  result = (PushToEdges[bksq] + PushAway[BitBoard.square_distance(bksq, bnsq)]);

            return(strongSide == pos.side_to_move() ? result : -result);
        }
Пример #13
0
        /// KQ vs KR.  This is almost identical to KX vs K:  We give the attacking
        /// king a bonus for having the kings close together, and for forcing the
        /// defending king towards the edge. If we also take care to avoid null move for
        /// the defending side in the search, this is usually sufficient to win KQ vs KR.
        public Value KQKR(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.QueenValueMg, 0));
            Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 0));

            Square winnerKSq = pos.king_square(strongSide);
            Square loserKSq  = pos.king_square(weakSide);

            Value result = ValueS.QueenValueEg
                           - ValueS.RookValueEg
                           + PushToEdges[loserKSq]
                           + PushClose[BitBoard.square_distance(winnerKSq, loserKSq)];

            return(strongSide == pos.side_to_move() ? result : -result);
        }
Пример #14
0
        /// KNP vs KB. If knight can block bishop from taking pawn, it's a win.
        /// Otherwise the position is drawn.
        public ScaleFactor KNPKB(Position pos)
        {
            Square pawnSq       = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square bishopSq     = pos.list(weakSide, PieceTypeS.BISHOP)[0];
            Square weakerKingSq = pos.king_square(weakSide);

            // King needs to get close to promoting pawn to prevent knight from blocking.
            // Rules for this are very tricky, so just approximate.
            if ((BitBoard.forward_bb(strongSide, pawnSq) & pos.attacks_from_square_piecetype(bishopSq, PieceTypeS.BISHOP)) != 0)
            {
                return(BitBoard.square_distance(weakerKingSq, pawnSq));
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
Пример #15
0
        public static int generate_castling(Position pos, ExtMove[] mlist, int mPos, Color us, CheckInfo ci, CastlingRight Cr, bool Checks, bool Chess960)
        {
            bool KingSide = (Cr == CastlingRightS.WHITE_OO || Cr == CastlingRightS.BLACK_OO);

            if (pos.castling_impeded(Cr) || 0 == pos.can_castle_castleright(Cr))
            {
                return(mPos);
            }

            // 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.castling_rook_square(Cr);
            Square   kto     = Types.relative_square(us, KingSide ? SquareS.SQ_G1 : SquareS.SQ_C1);
            Bitboard enemies = pos.pieces_color(Types.notColor(us));

            Debug.Assert(0 == pos.checkers());

            Square K = Chess960 ? kto > kfrom ? SquareS.DELTA_W : SquareS.DELTA_E
                              : KingSide ? SquareS.DELTA_W : SquareS.DELTA_E;

            for (Square s = kto; s != kfrom; s += K)
            {
                if ((pos.attackers_to(s) & enemies) != 0)
                {
                    return(mPos);
                }
            }

            // 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 (Chess960 && (BitBoard.attacks_bb_SBBPT(kto, pos.pieces() ^ BitBoard.SquareBB[rfrom], PieceTypeS.ROOK) & pos.pieces_color_piecetype(Types.notColor(us), PieceTypeS.ROOK, PieceTypeS.QUEEN)) != 0)
            {
                return(mPos);
            }

            Move m = Types.make(kfrom, rfrom, MoveTypeS.CASTLING);

            if (Checks && !pos.gives_check(m, ci))
            {
                return(mPos);
            }

            mlist[mPos++].move = m;

            return(mPos);
        }
Пример #16
0
        public ScaleFactor KRPKB(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 1));
            Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0));

            // Test for a rook pawn
            if ((pos.pieces_piecetype(PieceTypeS.PAWN) & (BitBoard.FileABB | BitBoard.FileHBB)) != 0)
            {
                Square ksq  = pos.king_square(weakSide);
                Square bsq  = pos.list(weakSide, PieceTypeS.BISHOP)[0];
                Square psq  = pos.list(strongSide, PieceTypeS.PAWN)[0];
                Rank   rk   = Types.relative_rank_square(strongSide, psq);
                Square push = Types.pawn_push(strongSide);

                // If the pawn is on the 5th rank and the pawn (currently) is on
                // the same color square as the bishop then there is a chance of
                // a fortress. Depending on the king position give a moderate
                // reduction or a stronger one if the defending king is near the
                // corner but not trapped there.
                if (rk == RankS.RANK_5 && !Types.opposite_colors(bsq, psq))
                {
                    int d = BitBoard.square_distance(psq + 3 * push, ksq);

                    if (d <= 2 && !(d == 0 && ksq == pos.king_square(strongSide) + 2 * push))
                    {
                        return(24);
                    }
                    else
                    {
                        return(48);
                    }
                }

                // When the pawn has moved to the 6th rank we can be fairly sure
                // it's drawn if the bishop attacks the square in front of the
                // pawn from a reasonable distance and the defending king is near
                // the corner
                if (rk == RankS.RANK_6 &&
                    BitBoard.square_distance(psq + 2 * push, ksq) <= 1 &&
                    (BitBoard.PseudoAttacks[PieceTypeS.BISHOP][bsq] & BitBoard.SquareBB[(psq + push)]) != 0 &&
                    BitBoard.file_distance(bsq, psq) >= 2)
                {
                    return(8);
                }
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
Пример #17
0
        /// KNP vs K. There is a single rule: if the pawn is a rook pawn on the 7th rank
        /// and the defending king prevents the pawn from advancing, the position is drawn.
        public ScaleFactor KNPK(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.KnightValueMg, 1));
            Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0));

            // Assume strongSide is white and the pawn is on files A-D
            Square pawnSq     = normalize(pos, strongSide, pos.list(strongSide, PieceTypeS.PAWN)[0]);
            Square weakKingSq = normalize(pos, strongSide, pos.king_square(weakSide));

            if (pawnSq == SquareS.SQ_A7 && BitBoard.square_distance(SquareS.SQ_A8, weakKingSq) <= 1)
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
Пример #18
0
        /// KR vs KP. This is a somewhat tricky endgame to evaluate precisely without
        /// a bitbase. The function below returns drawish scores when the pawn is
        /// far advanced with support of the king, while the attacking king is far
        /// away.
        public Value KRKP(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 0));
            Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 1));

            Square wksq = Types.relative_square(strongSide, pos.king_square(strongSide));
            Square bksq = Types.relative_square(strongSide, pos.king_square(weakSide));
            Square rsq  = Types.relative_square(strongSide, pos.list(strongSide, PieceTypeS.ROOK)[0]);
            Square psq  = Types.relative_square(strongSide, pos.list(weakSide, PieceTypeS.PAWN)[0]);

            Square queeningSq = Types.make_square(Types.file_of(psq), RankS.RANK_1);
            Value  result;

            // If the stronger side's king is in front of the pawn, it's a win
            if (wksq < psq && Types.file_of(wksq) == Types.file_of(psq))
            {
                result = ValueS.RookValueEg - (BitBoard.square_distance(wksq, psq));
            }

            // If the weaker side's king is too far from the pawn and the rook,
            // it's a win
            else if (BitBoard.square_distance(bksq, psq) >= 3 + ((pos.side_to_move() == weakSide)?1:0) &&
                     BitBoard.square_distance(bksq, rsq) >= 3)
            {
                result = ValueS.RookValueEg - (BitBoard.square_distance(wksq, psq));
            }

            // If the pawn is far advanced and supported by the defending king,
            // the position is drawish
            else if (Types.rank_of(bksq) <= RankS.RANK_3 &&
                     BitBoard.square_distance(bksq, psq) == 1 &&
                     Types.rank_of(wksq) >= RankS.RANK_4 &&
                     BitBoard.square_distance(wksq, psq) > 2 + ((pos.side_to_move() == strongSide)?1:0))
            {
                result = 80 - 8 * BitBoard.square_distance(wksq, psq);
            }
            else
            {
                result = (Value)(200) - 8 * (BitBoard.square_distance(wksq, psq + SquareS.DELTA_S)
                                             - BitBoard.square_distance(bksq, psq + SquareS.DELTA_S)
                                             - BitBoard.square_distance(psq, queeningSq));
            }

            return(strongSide == pos.side_to_move() ? result : -result);
        }
Пример #19
0
        public Engine(string[] args)
        {
            inOut.WriteLine(Misc.engine_info(), MutexAction.ATOMIC);

            Uci.init(Options);
            BitBoard.init();
            Position.init();
            Bitbases.init_kpk();
            Search.init();
            Pawns.init();
            Eval.init();
            Threads.init();
            TT.resize((ulong)Options["Hash"].getInt());

            Uci.loop(args);

            Threads.exit();
        }
Пример #20
0
        public Result classify(KPKPosition[] db, Color Us)
        {
            // White to Move: If one move leads to a position classified as WIN, the result
            // of the current position is WIN. If all moves lead to positions classified
            // as DRAW, the current position is classified as DRAW, otherwise the current
            // position is classified as UNKNOWN.
            //
            // Black to Move: If one move leads to a position classified as DRAW, the result
            // of the current position is DRAW. If all moves lead to positions classified
            // as WIN, the position is classified as WIN, otherwise the current position is
            // classified as UNKNOWN.
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            Result   r = Result.INVALID;
            Bitboard b = BitBoard.StepAttacksBB[PieceTypeS.KING][Us == ColorS.WHITE ? wksq : bksq];

            while (b != 0)
            {
                r |= (Us == ColorS.WHITE) ? db[Bitbases.index(Them, bksq, BitBoard.pop_lsb(ref b), psq)].result
                                         : db[Bitbases.index(Them, BitBoard.pop_lsb(ref b), wksq, psq)].result;
            }

            if (Us == ColorS.WHITE && Types.rank_of(psq) < RankS.RANK_7)
            {
                Square s = (psq + SquareS.DELTA_N);
                r |= db[Bitbases.index(ColorS.BLACK, bksq, wksq, s)].result; // Single push

                if (Types.rank_of(psq) == RankS.RANK_2 && s != wksq && s != bksq)
                {
                    r |= db[Bitbases.index(ColorS.BLACK, bksq, wksq, s + SquareS.DELTA_N)].result; // Double push
                }
            }

            if (Us == ColorS.WHITE)
            {
                return(result = (r & Result.WIN) != 0 ? Result.WIN : (r & Result.UNKNOWN) != 0 ? Result.UNKNOWN : Result.DRAW);
            }
            else
            {
                return(result = (r & Result.DRAW) != 0 ? Result.DRAW : (r & Result.UNKNOWN) != 0 ? Result.UNKNOWN : Result.WIN);
            }
        }
Пример #21
0
        // evaluate_threats() assigns bonuses according to the type of attacking piece
        // and the type of attacked one.
        public static Score evaluate_threats(Position pos, EvalInfo ei, Color Us, bool Trace)
        {
            Color Them = (Us == ColorS.WHITE ? ColorS.BLACK : ColorS.WHITE);

            Bitboard b, weakEnemies;
            Score    score = ScoreS.SCORE_ZERO;

            // Enemies not defended by a pawn and under our attack
            weakEnemies = pos.pieces_color(Them)
                          & ~ei.attackedBy[Them][PieceTypeS.PAWN]
                          & ei.attackedBy[Us][PieceTypeS.ALL_PIECES];

            // Add a bonus according if the attacking pieces are minor or major
            if (weakEnemies != 0)
            {
                b = weakEnemies & (ei.attackedBy[Us][PieceTypeS.PAWN] | ei.attackedBy[Us][PieceTypeS.KNIGHT] | ei.attackedBy[Us][PieceTypeS.BISHOP]);
                if (b != 0)
                {
                    score += Threat[0][Types.type_of_piece(pos.piece_on(BitBoard.lsb(b)))];
                }

                b = weakEnemies & (ei.attackedBy[Us][PieceTypeS.ROOK] | ei.attackedBy[Us][PieceTypeS.QUEEN]);
                if (b != 0)
                {
                    score += Threat[1][Types.type_of_piece(pos.piece_on(BitBoard.lsb(b)))];
                }

                b = weakEnemies & ~ei.attackedBy[Them][PieceTypeS.ALL_PIECES];
                if (b != 0)
                {
                    score += BitBoard.more_than_one(b) ? Hanging[Us != pos.side_to_move()?1:0] * Bitcount.popcount_Max15(b)
                        : Hanging[Us == pos.side_to_move()?1:0];
                }
            }

            if (Trace)
            {
                Tracing.terms[Us][TermsS.THREAT] = score;
            }

            return(score);
        }
Пример #22
0
        /// KQ vs KP. In general, this is a win for the stronger side, but there are a
        /// few important exceptions. A pawn on 7th rank and on the A,C,F or H files
        /// with a king positioned next to it can be a draw, so in that case, we only
        /// use the distance between the kings.
        public Value KQKP(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.QueenValueMg, 0));
            Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 1));

            Square winnerKSq = pos.king_square(strongSide);
            Square loserKSq  = pos.king_square(weakSide);
            Square pawnSq    = pos.list(weakSide, PieceTypeS.PAWN)[0];

            Value result = (PushClose[BitBoard.square_distance(winnerKSq, loserKSq)]);

            if (Types.relative_rank_square(weakSide, pawnSq) != RankS.RANK_7 ||
                BitBoard.square_distance(loserKSq, pawnSq) != 1 ||
                0 == ((BitBoard.FileABB | BitBoard.FileCBB | BitBoard.FileFBB | BitBoard.FileHBB) & BitBoard.SquareBB[pawnSq]))
            {
                result += ValueS.QueenValueEg - ValueS.PawnValueEg;
            }

            return(strongSide == pos.side_to_move() ? result : -result);
        }
Пример #23
0
        public static Bitboard sliding_attack(Square[] deltas, Square sq, Bitboard occupied)
        {
            Bitboard attack = 0;

            for (int i = 0; i < 4; ++i)
            {
                for (Square s = sq + deltas[i];
                     Types.is_ok_square(s) && BitBoard.square_distance(s, s - deltas[i]) == 1;
                     s += deltas[i])
                {
                    attack |= SquareBB[s];

                    if ((occupied & SquareBB[s]) != 0)
                    {
                        break;
                    }
                }
            }

            return(attack);
        }
Пример #24
0
        /// K and two or more pawns vs K. There is just a single rule here: If all pawns
        /// are on the same rook file and are blocked by the defending king, it's a draw.
        public ScaleFactor KPsK(Position pos)
        {
            Debug.Assert(pos.non_pawn_material(strongSide) == ValueS.VALUE_ZERO);
            Debug.Assert(pos.count(strongSide, PieceTypeS.PAWN) >= 2);
            Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0));

            Square   ksq   = pos.king_square(weakSide);
            Bitboard pawns = pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN);
            Square   psq   = pos.list(strongSide, PieceTypeS.PAWN)[0];

            // If all pawns are ahead of the king, on a single rook file and
            // the king is within one file of the pawns, it's a draw.
            if (0 == (pawns & ~BitBoard.in_front_bb(weakSide, Types.rank_of(ksq))) &&
                !((pawns & ~BitBoard.FileABB) != 0 && (pawns & ~BitBoard.FileHBB) != 0) &&
                BitBoard.file_distance(ksq, psq) <= 1)
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
Пример #25
0
        public static int generate_moves(Position pos, ExtMove[] mlist, int mPos, Color us, Bitboard target, CheckInfo ci, PieceType Pt, bool Checks)
        {
            Debug.Assert(Pt != PieceTypeS.KING && Pt != PieceTypeS.PAWN);

            Square[] pieceList = pos.list(us, Pt);
            int      pl        = 0;

            for (Square from = pieceList[pl]; from != SquareS.SQ_NONE; from = pieceList[++pl])
            {
                if (Checks)
                {
                    if ((Pt == PieceTypeS.BISHOP || Pt == PieceTypeS.ROOK || Pt == PieceTypeS.QUEEN) &&
                        0 == (BitBoard.PseudoAttacks[Pt][from] & target & ci.checkSq[Pt]))
                    {
                        continue;
                    }

                    if (ci.dcCandidates != 0 && (ci.dcCandidates & BitBoard.SquareBB[from]) != 0)
                    {
                        continue;
                    }
                }

                Bitboard b = pos.attacks_from_square_piecetype(from, Pt) & target;

                if (Checks)
                {
                    b &= ci.checkSq[Pt];
                }

                while (b != 0)
                {
                    mlist[mPos++].move = Types.make_move(from, BitBoard.pop_lsb(ref b));
                }
            }

            return(mPos);
        }
Пример #26
0
        /// KRPP vs KRP. There is just a single rule: if the stronger side has no passed
        /// pawns and the defending king is actively placed, the position is drawish.
        public ScaleFactor KRPPKRP(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 2));
            Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 1));

            Square wpsq1 = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square wpsq2 = pos.list(strongSide, PieceTypeS.PAWN)[1];
            Square bksq  = pos.king_square(weakSide);

            // Does the stronger side have a passed pawn?
            if (pos.pawn_passed(strongSide, wpsq1) || pos.pawn_passed(strongSide, wpsq2))
            {
                return(ScaleFactorS.SCALE_FACTOR_NONE);
            }

            Rank r = Math.Max(Types.relative_rank_square(strongSide, wpsq1), Types.relative_rank_square(strongSide, wpsq2));

            if (BitBoard.file_distance(bksq, wpsq1) <= 1 &&
                BitBoard.file_distance(bksq, wpsq2) <= 1 &&
                Types.relative_rank_square(strongSide, bksq) > r)
            {
                switch (r)
                {
                case RankS.RANK_2: return(10);

                case RankS.RANK_3: return(10);

                case RankS.RANK_4: return(15);

                case RankS.RANK_5: return(20);

                case RankS.RANK_6: return(40);

                default: Debug.Assert(false); break;
                }
            }
            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
Пример #27
0
        /// 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.
        public static int generate_evasions(Position pos, ExtMove[] mlist, int mPos)
        {
            Debug.Assert(pos.checkers() != 0);

            Color    us            = pos.side_to_move();
            Square   ksq           = pos.king_square(us);
            Bitboard sliderAttacks = 0;
            Bitboard sliders       = pos.checkers() & ~pos.pieces_piecetype(PieceTypeS.KNIGHT, PieceTypeS.PAWN);

            // Find all the squares attacked by slider checkers. We will remove them from
            // the king evasions in order to skip known illegal moves, which avoids any
            // useless legality checks later on.
            while (sliders != 0)
            {
                Square checksq = BitBoard.pop_lsb(ref sliders);
                sliderAttacks |= BitBoard.LineBB[checksq][ksq] ^ BitBoard.SquareBB[checksq];
            }

            // Generate evasions for king, capture and non capture moves
            Bitboard b = pos.attacks_from_square_piecetype(ksq, PieceTypeS.KING) & ~pos.pieces_color(us) & ~sliderAttacks;

            while (b != 0)
            {
                mlist[mPos++].move = Types.make_move(ksq, BitBoard.pop_lsb(ref b));
            }

            if (BitBoard.more_than_one(pos.checkers()))
            {
                return(mPos); // Double check, only a king move can save the day
            }
            // Generate blocking evasions or captures of the checking piece
            Square   checksq2 = BitBoard.lsb(pos.checkers());
            Bitboard target   = BitBoard.between_bb(checksq2, ksq) | BitBoard.SquareBB[checksq2];

            return(us == ColorS.WHITE ? generate_all(pos, mlist, mPos, target, ColorS.WHITE, GenTypeS.EVASIONS) :
                   generate_all(pos, mlist, mPos, target, ColorS.BLACK, GenTypeS.EVASIONS));
        }
Пример #28
0
        public static int generate_all(Position pos, ExtMove[] mlist, int mPos, Bitboard target, Color us, GenType Type, CheckInfo ci = null)
        {
            bool Checks = Type == GenTypeS.QUIET_CHECKS;

            mPos = generate_pawn_moves(pos, mlist, mPos, target, ci, us, Type);
            mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.KNIGHT, Checks);
            mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.BISHOP, Checks);
            mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.ROOK, Checks);
            mPos = generate_moves(pos, mlist, mPos, us, target, ci, PieceTypeS.QUEEN, Checks);

            if (Type != GenTypeS.QUIET_CHECKS && Type != GenTypeS.EVASIONS)
            {
                Square   ksq = pos.king_square(us);
                Bitboard b   = pos.attacks_from_square_piecetype(ksq, PieceTypeS.KING) & target;
                while (b != 0)
                {
                    mlist[mPos++].move = Types.make_move(ksq, BitBoard.pop_lsb(ref b));
                }
            }

            if (Type != GenTypeS.CAPTURES && Type != GenTypeS.EVASIONS && pos.can_castle_color(us) != 0)
            {
                if (pos.is_chess960() != 0)
                {
                    mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.KING_SIDE)).right, Checks, true);
                    mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.QUEEN_SIDE)).right, Checks, true);
                }
                else
                {
                    mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.KING_SIDE)).right, Checks, false);
                    mPos = generate_castling(pos, mlist, mPos, us, ci, (new MakeCastlingS(us, CastlingSideS.QUEEN_SIDE)).right, Checks, false);
                }
            }

            return(mPos);
        }
Пример #29
0
        /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
        /// defending king towards a corner square of the right color.
        public Value KBNK(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.KnightValueMg + ValueS.BishopValueMg, 0));
            Debug.Assert(verify_material(pos, weakSide, ValueS.VALUE_ZERO, 0));

            Square winnerKSq = pos.king_square(strongSide);
            Square loserKSq  = pos.king_square(weakSide);
            Square bishopSq  = pos.list(strongSide, PieceTypeS.BISHOP)[0];

            // kbnk_mate_table() tries to drive toward corners A1 or H8. If we have a
            // bishop that cannot reach the above squares, we flip the kings in order
            // to drive the enemy toward corners A8 or H1.
            if (Types.opposite_colors(bishopSq, SquareS.SQ_A1))
            {
                winnerKSq = Types.notSquare(winnerKSq);
                loserKSq  = Types.notSquare(loserKSq);
            }

            Value result = ValueS.VALUE_KNOWN_WIN
                           + PushClose[BitBoard.square_distance(winnerKSq, loserKSq)]
                           + PushToCorners[loserKSq];

            return(strongSide == pos.side_to_move() ? result : -result);
        }
Пример #30
0
        /// move_to_san() takes a position and a legal Move as input and returns its
        /// short algebraic notation representation.
        public static string move_to_san(Position pos, Move m)
        {
            if (m == MoveS.MOVE_NONE)
            {
                return("(none)");
            }

            if (m == MoveS.MOVE_NULL)
            {
                return("(null)");
            }

            Debug.Assert((new MoveList(pos, GenTypeS.LEGAL).contains(m)));

            Bitboard  others, b;
            string    san  = "";
            Color     us   = pos.side_to_move();
            Square    from = Types.from_sq(m);
            Square    to   = Types.to_sq(m);
            Piece     pc   = pos.piece_on(from);
            PieceType pt   = Types.type_of_piece(pc);

            if (Types.type_of_move(m) == MoveTypeS.CASTLING)
            {
                san = to > from ? "O-O" : "O-O-O";
            }
            else
            {
                if (pt != PieceTypeS.PAWN)
                {
                    san = "" + PieceToChar[ColorS.WHITE][pt]; // Upper case

                    // A disambiguation occurs if we have more then one piece of type 'pt'
                    // that can reach 'to' with a legal move.
                    others = b = (pos.attacks_from_piece_square(pc, to) & pos.pieces_color_piecetype(us, pt)) ^ BitBoard.SquareBB[from];

                    while (b != 0)
                    {
                        Square s = BitBoard.pop_lsb(ref b);
                        if (!pos.legal(Types.make_move(s, to), pos.pinned_pieces(us)))
                        {
                            others ^= BitBoard.SquareBB[s];
                        }
                    }

                    if (0 == others)
                    { /* Disambiguation is not needed */
                    }

                    else if (0 == (others & BitBoard.file_bb_square(from)))
                    {
                        san += Types.file_to_char(Types.file_of(from));
                    }

                    else if (0 == (others & BitBoard.rank_bb_square(from)))
                    {
                        san += Types.rank_to_char(Types.rank_of(from));
                    }

                    else
                    {
                        san += Types.square_to_string(from);
                    }
                }
                else if (pos.capture(m))
                {
                    san = "" + Types.file_to_char(Types.file_of(from));
                }

                if (pos.capture(m))
                {
                    san += 'x';
                }

                san += Types.square_to_string(to);

                if (Types.type_of_move(m) == MoveTypeS.PROMOTION)
                {
                    san += "=" + PieceToChar[ColorS.WHITE][Types.promotion_type(m)];
                }
            }

            if (pos.gives_check(m, new CheckInfo(pos)))
            {
                StateInfo st = new StateInfo();
                pos.do_move(m, st);
                san += (new MoveList(pos, GenTypeS.LEGAL)).size() > 0 ? "+" : "#";
                pos.undo_move(m);
            }

            return(san);
        }