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);
        }
        /// 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);
        }
        /// 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);
        }
        /// 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);
            }
        }