Exemple #1
0
        /// move_to_uci() converts a move to a string in coordinate notation
        /// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print
        /// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960
        /// mode. Internally castling moves are always encoded as "king captures rook".
        public static string move_to_uci(Move m, bool chess960)
        {
            Square from = Types.from_sq(m);
            Square to   = Types.to_sq(m);

            if (m == MoveS.MOVE_NONE)
            {
                return("(none)");
            }

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

            if (Types.type_of_move(m) == MoveTypeS.CASTLING && !chess960)
            {
                to = Types.make_square((to > from ? FileS.FILE_G : FileS.FILE_C), Types.rank_of(from));
            }

            string move = Types.square_to_string(from) + Types.square_to_string(to);

            if (Types.type_of_move(m) == MoveTypeS.PROMOTION)
            {
                move += PieceToChar[ColorS.BLACK][Types.promotion_type(m)]; // Lower case
            }
            return(move);
        }
        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;
            }
        }
            /// 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);
            }
        /// 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);
        }
        /// Bitboards::pretty() returns an ASCII representation of a bitboard to be
        /// printed to standard output. This is sometimes useful for debugging.
        public static String pretty(Bitboard b)
        {
            StringBuilder sb = new StringBuilder("+---+---+---+---+---+---+---+---+");

            sb.Append(Types.newline);
            for (Rank r = RankS.RANK_8; r >= RankS.RANK_1; --r)
            {
                for (File f = FileS.FILE_A; f <= FileS.FILE_H; ++f)
                {
                    sb.Append((b & SquareBB[Types.make_square(f, r)]) != 0 ? "| X " : "|   ");
                }
                sb.Append("|");
                sb.Append(Types.newline);
                sb.Append("+---+---+---+---+---+---+---+---+");
                sb.Append(Types.newline);
            }

            return(sb.ToString());
        }
        /// KBPP vs KB. It detects a few basic draws with opposite-colored bishops
        public ScaleFactor KBPPKB(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.BishopValueMg, 2));
            Debug.Assert(verify_material(pos, weakSide, ValueS.BishopValueMg, 0));

            Square wbsq = pos.list(strongSide, PieceTypeS.BISHOP)[0];
            Square bbsq = pos.list(weakSide, PieceTypeS.BISHOP)[0];

            if (!Types.opposite_colors(wbsq, bbsq))
            {
                return(ScaleFactorS.SCALE_FACTOR_NONE);
            }

            Square ksq = pos.king_square(weakSide);
            Square psq1 = pos.list(strongSide, PieceTypeS.PAWN)[0];
            Square psq2 = pos.list(strongSide, PieceTypeS.PAWN)[1];
            Rank   r1 = Types.rank_of(psq1);
            Rank   r2 = Types.rank_of(psq2);
            Square blockSq1, blockSq2;

            if (Types.relative_rank_square(strongSide, psq1) > Types.relative_rank_square(strongSide, psq2))
            {
                blockSq1 = psq1 + Types.pawn_push(strongSide);
                blockSq2 = Types.make_square(Types.file_of(psq2), Types.rank_of(psq1));
            }
            else
            {
                blockSq1 = psq2 + Types.pawn_push(strongSide);
                blockSq2 = Types.make_square(Types.file_of(psq1), Types.rank_of(psq2));
            }

            switch (BitBoard.file_distance(psq1, psq2))
            {
            case 0:
                // Both pawns are on the same file. It's an easy draw if the defender firmly
                // controls some square in the frontmost pawn's path.
                if (Types.file_of(ksq) == Types.file_of(blockSq1) &&
                    Types.relative_rank_square(strongSide, ksq) >= Types.relative_rank_square(strongSide, blockSq1) &&
                    Types.opposite_colors(ksq, wbsq))
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
                else
                {
                    return(ScaleFactorS.SCALE_FACTOR_NONE);
                }

            case 1:
                // Pawns on adjacent files. It's a draw if the defender firmly controls the
                // square in front of the frontmost pawn's path, and the square diagonally
                // behind this square on the file of the other pawn.
                if (ksq == blockSq1 &&
                    Types.opposite_colors(ksq, wbsq) &&
                    (bbsq == blockSq2 ||
                     (pos.attacks_from_square_piecetype(blockSq2, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0 ||
                     Math.Abs(r1 - r2) >= 2))
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }

                else if (ksq == blockSq2 &&
                         Types.opposite_colors(ksq, wbsq) &&
                         (bbsq == blockSq1 ||
                          (pos.attacks_from_square_piecetype(blockSq1, PieceTypeS.BISHOP) & pos.pieces_color_piecetype(weakSide, PieceTypeS.BISHOP)) != 0))
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
                else
                {
                    return(ScaleFactorS.SCALE_FACTOR_NONE);
                }

            default:
                // The pawns are not on the same file or adjacent files. No scaling.
                return(ScaleFactorS.SCALE_FACTOR_NONE);
            }
        }
        /// KRP vs KR. This function knows a handful of the most important classes of
        /// drawn positions, but is far from perfect. It would probably be a good idea
        /// to add more knowledge in the future.
        ///
        /// It would also be nice to rewrite the actual code for this function,
        /// which is mostly copied from Glaurung 1.x, and isn't very pretty.
        public ScaleFactor KRPKR(Position pos)
        {
            Debug.Assert(verify_material(pos, strongSide, ValueS.RookValueMg, 1));
            Debug.Assert(verify_material(pos, weakSide, ValueS.RookValueMg, 0));

            // Assume strongSide is white and the pawn is on files A-D
            Square wksq = normalize(pos, strongSide, pos.king_square(strongSide));
            Square bksq = normalize(pos, strongSide, pos.king_square(weakSide));
            Square wrsq = normalize(pos, strongSide, pos.list(strongSide, PieceTypeS.ROOK)[0]);
            Square wpsq = normalize(pos, strongSide, pos.list(strongSide, PieceTypeS.PAWN)[0]);
            Square brsq = normalize(pos, strongSide, pos.list(weakSide, PieceTypeS.ROOK)[0]);

            File   f          = Types.file_of(wpsq);
            Rank   r          = Types.rank_of(wpsq);
            Square queeningSq = Types.make_square(f, RankS.RANK_8);
            int    tempo      = (pos.side_to_move() == strongSide ? 1 : 0);

            // If the pawn is not too far advanced and the defending king defends the
            // queening square, use the third-rank defence.
            if (r <= RankS.RANK_5 &&
                BitBoard.square_distance(bksq, queeningSq) <= 1 &&
                wksq <= SquareS.SQ_H5 &&
                (Types.rank_of(brsq) == RankS.RANK_6 || (r <= RankS.RANK_3 && Types.rank_of(wrsq) != RankS.RANK_6)))
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            // The defending side saves a draw by checking from behind in case the pawn
            // has advanced to the 6th rank with the king behind.
            if (r == RankS.RANK_6 &&
                BitBoard.square_distance(bksq, queeningSq) <= 1 &&
                Types.rank_of(wksq) + tempo <= RankS.RANK_6 &&
                (Types.rank_of(brsq) == RankS.RANK_1 || (0 == tempo && Math.Abs(Types.file_of(brsq) - f) >= 3)))
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            if (r >= RankS.RANK_6 &&
                bksq == queeningSq &&
                Types.rank_of(brsq) == RankS.RANK_1 &&
                (0 == tempo || BitBoard.square_distance(wksq, wpsq) >= 2))
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
            // and the black rook is behind the pawn.
            if (wpsq == SquareS.SQ_A7 &&
                wrsq == SquareS.SQ_A8 &&
                (bksq == SquareS.SQ_H7 || bksq == SquareS.SQ_G7) &&
                Types.file_of(brsq) == FileS.FILE_A &&
                (Types.rank_of(brsq) <= RankS.RANK_3 || Types.file_of(wksq) >= FileS.FILE_D || Types.rank_of(wksq) <= RankS.RANK_5))
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            // If the defending king blocks the pawn and the attacking king is too far
            // away, it's a draw.
            if (r <= RankS.RANK_5 &&
                bksq == wpsq + SquareS.DELTA_N &&
                BitBoard.square_distance(wksq, wpsq) - tempo >= 2 &&
                BitBoard.square_distance(wksq, brsq) - tempo >= 2)
            {
                return(ScaleFactorS.SCALE_FACTOR_DRAW);
            }

            // Pawn on the 7th rank supported by the rook from behind usually wins if the
            // attacking king is closer to the queening square than the defending king,
            // and the defending king cannot gain tempi by threatening the attacking rook.
            if (r == RankS.RANK_7 &&
                f != FileS.FILE_A &&
                Types.file_of(wrsq) == f &&
                wrsq != queeningSq &&
                (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, queeningSq) - 2 + tempo) &&
                (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, wrsq) + tempo))
            {
                return((ScaleFactor)(ScaleFactorS.SCALE_FACTOR_MAX - 2 * BitBoard.square_distance(wksq, queeningSq)));
            }

            // Similar to the above, but with the pawn further back
            if (f != FileS.FILE_A &&
                Types.file_of(wrsq) == f &&
                wrsq < wpsq &&
                (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, queeningSq) - 2 + tempo) &&
                (BitBoard.square_distance(wksq, wpsq + SquareS.DELTA_N) < BitBoard.square_distance(bksq, wpsq + SquareS.DELTA_N) - 2 + tempo) &&
                (BitBoard.square_distance(bksq, wrsq) + tempo >= 3 ||
                 (BitBoard.square_distance(wksq, queeningSq) < BitBoard.square_distance(bksq, wrsq) + tempo &&
                  (BitBoard.square_distance(wksq, wpsq + SquareS.DELTA_N) < BitBoard.square_distance(bksq, wrsq) + tempo))))
            {
                return((ScaleFactor)(ScaleFactorS.SCALE_FACTOR_MAX
                                     - 8 * BitBoard.square_distance(wpsq, queeningSq)
                                     - 2 * BitBoard.square_distance(wksq, queeningSq)));
            }

            // If the pawn is not far advanced, and the defending king is somewhere in
            // the pawn's path, it's probably a draw.
            if (r <= RankS.RANK_4 && bksq > wpsq)
            {
                if (Types.file_of(bksq) == Types.file_of(wpsq))
                {
                    return(10);
                }
                if (Math.Abs(Types.file_of(bksq) - Types.file_of(wpsq)) == 1 &&
                    BitBoard.square_distance(wksq, bksq) > 2)
                {
                    return(24 - 2 * BitBoard.square_distance(wksq, bksq));
                }
            }
            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }
        /// KB 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.
        public ScaleFactor KBPsK(Position pos)
        {
            Debug.Assert(pos.non_pawn_material(strongSide) == ValueS.BishopValueMg);
            Debug.Assert(pos.count(strongSide, PieceTypeS.PAWN) >= 1);

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

            Bitboard pawns    = pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN);
            File     pawnFile = Types.file_of(pos.list(strongSide, PieceTypeS.PAWN)[0]);

            // All pawns are on a single rook file ?
            if ((pawnFile == FileS.FILE_A || pawnFile == FileS.FILE_H) &&
                0 == (pawns & ~BitBoard.file_bb_file(pawnFile)))
            {
                Square bishopSq   = pos.list(strongSide, PieceTypeS.BISHOP)[0];
                Square queeningSq = Types.relative_square(strongSide, Types.make_square(pawnFile, RankS.RANK_8));
                Square kingSq     = pos.king_square(weakSide);

                if (Types.opposite_colors(queeningSq, bishopSq) &&
                    BitBoard.square_distance(queeningSq, kingSq) <= 1)
                {
                    return(ScaleFactorS.SCALE_FACTOR_DRAW);
                }
            }

            // If all the pawns are on the same B or G file, then it's potentially a draw
            if ((pawnFile == FileS.FILE_B || pawnFile == FileS.FILE_G) &&
                0 == (pos.pieces_piecetype(PieceTypeS.PAWN) & ~BitBoard.file_bb_file(pawnFile)) &&
                pos.non_pawn_material(weakSide) == 0 &&
                pos.count(weakSide, PieceTypeS.PAWN) >= 1)
            {
                // Get weakSide pawn that is closest to the home rank
                Square weakPawnSq = BitBoard.backmost_sq(weakSide, pos.pieces_color_piecetype(weakSide, PieceTypeS.PAWN));

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

                // There's potential for a draw if our pawn is blocked on the 7th rank,
                // the bishop cannot attack it or they only have one pawn left
                if (Types.relative_rank_square(strongSide, weakPawnSq) == RankS.RANK_7 &&
                    (pos.pieces_color_piecetype(strongSide, PieceTypeS.PAWN) & BitBoard.SquareBB[(weakPawnSq + Types.pawn_push(weakSide))]) != 0 &&
                    (Types.opposite_colors(bishopSq, weakPawnSq) || pos.count(strongSide, PieceTypeS.PAWN) == 1))
                {
                    int strongKingDist = BitBoard.square_distance(weakPawnSq, strongKingSq);
                    int weakKingDist   = BitBoard.square_distance(weakPawnSq, weakKingSq);

                    // It's a draw if the weak king is on its back two ranks, within 2
                    // squares of the blocking pawn and the strong king is not
                    // closer. (I think this rule only fails in practically
                    // unreachable positions such as 5k1K/6p1/6P1/8/8/3B4/8/8 w
                    // and positions where qsearch will immediately correct the
                    // problem such as 8/4k1p1/6P1/1K6/3B4/8/8/8 w)
                    if (Types.relative_rank_square(strongSide, weakKingSq) >= RankS.RANK_7 &&
                        weakKingDist <= 2 &&
                        weakKingDist <= strongKingDist)
                    {
                        return(ScaleFactorS.SCALE_FACTOR_DRAW);
                    }
                }
            }

            return(ScaleFactorS.SCALE_FACTOR_NONE);
        }