/*
 
    Texel - A UCI chess engine.
 
    Copyright (C) 2012-2014  Peter Ă–sterlund, peterosterlund2@gmail.com
 
 
 
    This program is free software: you can redistribute it and/or modify
 
    it under the terms of the GNU General Public License as published by
 
    the Free Software Foundation, either version 3 of the License, or
 
    (at your option) any later version.
 
 
 
    This program is distributed in the hope that it will be useful,
 
    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
    GNU General Public License for more details.
 
 
 
    You should have received a copy of the GNU General Public License
 
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
*/
 
 
 
/*
 
 * endGameEval.cpp
 
 *
 
 *  Created on: Dec 26, 2014
 
 *      Author: petero
 
 */
 
 
 
#include "endGameEval.hpp"
 
#include "position.hpp"
 
#include "piece.hpp"
 
#include "parameters.hpp"
 
 
 
const int EndGameEval::distToH1A8[8][8] = { { 0, 1, 2, 3, 4, 5, 6, 7 },
 
                                            { 1, 2, 3, 4, 5, 6, 7, 6 },
 
                                            { 2, 3, 4, 5, 6, 7, 6, 5 },
 
                                            { 3, 4, 5, 6, 7, 6, 5, 4 },
 
                                            { 4, 5, 6, 7, 6, 5, 4, 3 },
 
                                            { 5, 6, 7, 6, 5, 4, 3, 2 },
 
                                            { 6, 7, 6, 5, 4, 3, 2, 1 },
 
                                            { 7, 6, 5, 4, 3, 2, 1, 0 } };
 
 
 
const int EndGameEval::winKingTable[64] = {
 
    0,   4,  10,  10,  10,  10,   4,   0,
 
    4,  15,  19,  20,  20,  19,  15,   4,
 
   10,  19,  25,  25,  25,  25,  19,  10,
 
   10,  20,  25,  25,  25,  25,  20,  10,
 
   10,  20,  25,  25,  25,  25,  20,  10,
 
   10,  19,  25,  25,  25,  25,  19,  10,
 
    4,  15,  19,  20,  20,  19,  15,   4,
 
    0,   4,  10,  10,  10,  10,   4,   0
 
};
 
 
 
 
 
template int EndGameEval::endGameEval<false>(const Position&, U64, int);
 
template int EndGameEval::endGameEval<true>(const Position&, U64, int);
 
 
 
/** Implements special knowledge for some endgame situations. */
 
template <bool doEval>
 
int
 
EndGameEval::endGameEval(const Position& pos, U64 passedPawns, int oldScore) {
 
    int score = oldScore;
 
    const int wMtrlPawns = pos.wMtrlPawns();
 
    const int bMtrlPawns = pos.bMtrlPawns();
 
    const int wMtrlNoPawns = pos.wMtrl() - wMtrlPawns;
 
    const int bMtrlNoPawns = pos.bMtrl() - bMtrlPawns;
 
 
 
    // Handle special endgames
 
    using MI = MatId;
 
    switch (pos.materialId()) {
 
    case 0:
 
    case MI::WN: case MI::BN: case MI::WB: case MI::BB:
 
    case MI::WN + MI::BN: case MI::WN + MI::BB:
 
    case MI::WB + MI::BN: case MI::WB + MI::BB:
 
        if (!doEval) return 1;
 
        return 0; // King + minor piece vs king + minor piece is a draw
 
    case MI::WQ + MI::BP: {
 
        if (!doEval) return 1;
 
        int wk = pos.getKingSq(true);
 
        int wq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WQUEEN));
 
        int bk = pos.getKingSq(false);
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        return kqkpEval(wk, wq, bk, bp, pos.isWhiteMove(), score);
 
    }
 
    case MI::BQ + MI::WP: {
 
        if (!doEval) return 1;
 
        int bk = pos.getKingSq(false);
 
        int bq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BQUEEN));
 
        int wk = pos.getKingSq(true);
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        return -kqkpEval(63-bk, 63-bq, 63-wk, 63-wp, !pos.isWhiteMove(), -score);
 
    }
 
    case MI::WQ: {
 
        if (!doEval) return 1;
 
        if (!pos.isWhiteMove() &&
 
            (pos.pieceTypeBB(Piece::BKING) & BitBoard::maskCorners) &&
 
            (pos.pieceTypeBB(Piece::WQUEEN) & BitBoard::sqMask(C2,B3,F2,G3,B6,C7,G6,F7)) &&
 
            (BitBoard::getTaxiDistance(pos.getKingSq(false),
 
                                       BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WQUEEN))) == 3))
 
            return 0;
 
        break;
 
    }
 
    case MI::BQ: {
 
        if (!doEval) return 1;
 
        if (pos.isWhiteMove() &&
 
            (pos.pieceTypeBB(Piece::WKING) & BitBoard::maskCorners) &&
 
            (pos.pieceTypeBB(Piece::BQUEEN) & BitBoard::sqMask(C2,B3,F2,G3,B6,C7,G6,F7)) &&
 
            (BitBoard::getTaxiDistance(pos.getKingSq(true),
 
                                       BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BQUEEN))) == 3))
 
            return 0;
 
        break;
 
    }
 
    case MI::WR + MI::BP: {
 
        if (!doEval) return 1;
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        return krkpEval(pos.getKingSq(true), pos.getKingSq(false),
 
                        bp, pos.isWhiteMove(), score);
 
    }
 
    case MI::BR + MI::WP: {
 
        if (!doEval) return 1;
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        return -krkpEval(63-pos.getKingSq(false), 63-pos.getKingSq(true),
 
                         63-wp, !pos.isWhiteMove(), -score);
 
    }
 
    case MI::WR + MI::BB: {
 
        if (!doEval) return 1;
 
        score /= 8;
 
        const int kSq = pos.getKingSq(false);
 
        const int x = Position::getX(kSq);
 
        const int y = Position::getY(kSq);
 
        if ((pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskDarkSq) != 0)
 
            score += (7 - distToH1A8[7-y][7-x]) * 7;
 
        else
 
            score += (7 - distToH1A8[7-y][x]) * 7;
 
        return score;
 
    }
 
    case MI::BR + MI::WB: {
 
        if (!doEval) return 1;
 
        score /= 8;
 
        const int kSq = pos.getKingSq(true);
 
        const int x = Position::getX(kSq);
 
        const int y = Position::getY(kSq);
 
        if ((pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskDarkSq) != 0)
 
            score -= (7 - distToH1A8[7-y][7-x]) * 7;
 
        else
 
            score -= (7 - distToH1A8[7-y][x]) * 7;
 
        return score;
 
    }
 
    case MI::WR + MI::WP + MI::BR: {
 
        if (!doEval) return 1;
 
        int wk = pos.getKingSq(true);
 
        int bk = pos.getKingSq(false);
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK));
 
        int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK));
 
        return krpkrEval(wk, bk, wp, wr, br, pos.isWhiteMove());
 
    }
 
    case MI::BR + MI::BP + MI::WR: {
 
        if (!doEval) return 1;
 
        int wk = pos.getKingSq(true);
 
        int bk = pos.getKingSq(false);
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK));
 
        int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK));
 
        return -krpkrEval(63-bk, 63-wk, 63-bp, 63-br, 63-wr, !pos.isWhiteMove());
 
    }
 
    case MI::WR + MI::WP + MI::BR + MI::BP: {
 
        if (!doEval) return 1;
 
        int wk = pos.getKingSq(true);
 
        int bk = pos.getKingSq(false);
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK));
 
        int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK));
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        return krpkrpEval(wk, bk, wp, wr, br, bp, pos.isWhiteMove(), score);
 
    }
 
    case MI::WN * 2:
 
    case MI::BN * 2:
 
        if (!doEval) return 1;
 
        return 0; // KNNK is a draw
 
    case MI::WN + MI::WB: {
 
        if (!doEval) return 1;
 
        bool darkBishop = (pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskDarkSq) != 0;
 
        return kbnkEval(pos.getKingSq(true), pos.getKingSq(false), darkBishop);
 
    }
 
    case MI::BN + MI::BB: {
 
        if (!doEval) return 1;
 
        bool darkBishop = (pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskDarkSq) != 0;
 
        return -kbnkEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), darkBishop);
 
    }
 
    case MI::WP: {
 
        if (!doEval) return 1;
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        return kpkEval(pos.getKingSq(true), pos.getKingSq(false),
 
                       wp, pos.isWhiteMove());
 
    }
 
    case MI::BP: {
 
        if (!doEval) return 1;
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        return -kpkEval(63-pos.getKingSq(false), 63-pos.getKingSq(true),
 
                        63-bp, !pos.isWhiteMove());
 
    }
 
    case MI::WP + MI::BP: {
 
        if (!doEval) return 1;
 
        int wk = pos.getKingSq(true);
 
        int bk = pos.getKingSq(false);
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        if (kpkpEval(wk, bk, wp, bp, score))
 
            return score;
 
        break;
 
    }
 
    case MI::WB + MI::WP + MI::BB: {
 
        if (!doEval) return 1;
 
        int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP));
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP));
 
        return kbpkbEval(pos.getKingSq(true), wb, wp, pos.getKingSq(false), bb, score);
 
    }
 
    case MI::BB + MI::BP + MI::WB: {
 
        if (!doEval) return 1;
 
        int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP));
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP));
 
        return -kbpkbEval(63-pos.getKingSq(false), 63-bb, 63-bp, 63-pos.getKingSq(true), 63-wb, -score);
 
    }
 
    case MI::WB + MI::WP + MI::BN: {
 
        if (!doEval) return 1;
 
        int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP));
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        int bn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BKNIGHT));
 
        return kbpknEval(pos.getKingSq(true), wb, wp, pos.getKingSq(false), bn, score);
 
    }
 
    case MI::BB + MI::BP + MI::WN: {
 
        if (!doEval) return 1;
 
        int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP));
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        int wn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WKNIGHT));
 
        return -kbpknEval(63-pos.getKingSq(false), 63-bb, 63-bp, 63-pos.getKingSq(true), 63-wn, -score);
 
    }
 
    case MI::WN + MI::WP + MI::BB: {
 
        if (!doEval) return 1;
 
        int wn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WKNIGHT));
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        int bb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BBISHOP));
 
        return knpkbEval(pos.getKingSq(true), wn, wp, pos.getKingSq(false), bb,
 
                         score, pos.isWhiteMove());
 
    }
 
    case MI::BN + MI::BP + MI::WB: {
 
        if (!doEval) return 1;
 
        int bn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BKNIGHT));
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        int wb = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WBISHOP));
 
        return -knpkbEval(63-pos.getKingSq(false), 63-bn, 63-bp, 63-pos.getKingSq(true), 63-wb,
 
                          -score, !pos.isWhiteMove());
 
    }
 
    case MI::WN + MI::WP: {
 
        if (!doEval) return 1;
 
        int wn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WKNIGHT));
 
        int wp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WPAWN));
 
        return knpkEval(pos.getKingSq(true), wn, wp, pos.getKingSq(false),
 
                        score, pos.isWhiteMove());
 
    }
 
    case MI::BN + MI::BP: {
 
        if (!doEval) return 1;
 
        int bn = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BKNIGHT));
 
        int bp = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BPAWN));
 
        return -knpkEval(63-pos.getKingSq(false), 63-bn, 63-bp, 63-pos.getKingSq(true),
 
                         -score, !pos.isWhiteMove());
 
    }
 
    }
 
 
 
    // QvsRP fortress detection
 
    if (pos.pieceTypeBB(Piece::WQUEEN) && (pos.wMtrl() == qV) &&
 
        pos.pieceTypeBB(Piece::BROOK) && pos.pieceTypeBB(Piece::BPAWN) &&
 
        (pos.bMtrl() - pos.bMtrlPawns() < rV * 2)) {
 
        if (!doEval) return 1;
 
        if (score > 0) {
 
            int wk = pos.getKingSq(true);
 
            int wq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WQUEEN));
 
            int bk = pos.getKingSq(false);
 
            int br = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BROOK));
 
            U64 m = pos.pieceTypeBB(Piece::BPAWN);
 
            int newScore = score;
 
            while (m) {
 
                int bp = BitBoard::extractSquare(m);
 
                int s2 = kqkrpEval(wk, wq, bk, br, bp, pos.isWhiteMove(), score);
 
                newScore = std::min(newScore, s2);
 
            }
 
            if (newScore < score)
 
                return newScore;
 
        }
 
    }
 
    if (pos.pieceTypeBB(Piece::BQUEEN) && (pos.bMtrl() == qV) &&
 
        pos.pieceTypeBB(Piece::WROOK) && pos.pieceTypeBB(Piece::WPAWN) &&
 
        (pos.wMtrl() - pos.wMtrlPawns() < rV * 2)) {
 
        if (!doEval) return 1;
 
        if (score < 0) {
 
            int bk = pos.getKingSq(false);
 
            int bq = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::BQUEEN));
 
            int wk = pos.getKingSq(true);
 
            int wr = BitBoard::numberOfTrailingZeros(pos.pieceTypeBB(Piece::WROOK));
 
            U64 m = pos.pieceTypeBB(Piece::WPAWN);
 
            int newScore = score;
 
            while (m) {
 
                int wp = BitBoard::extractSquare(m);
 
                int s2 = -kqkrpEval(63-bk, 63-bq, 63-wk, 63-wr, 63-wp, !pos.isWhiteMove(), -score);
 
                newScore = std::max(newScore, s2);
 
            }
 
            if (newScore > score)
 
                return newScore;
 
        }
 
    }
 
 
 
    const int nWN = BitBoard::bitCount(pos.pieceTypeBB(Piece::WKNIGHT));
 
    const int nBN = BitBoard::bitCount(pos.pieceTypeBB(Piece::BKNIGHT));
 
    const int nWB1 = BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskLightSq);
 
    const int nWB2 = BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP) & BitBoard::maskDarkSq);
 
    const int nBB1 = BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskLightSq);
 
    const int nBB2 = BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP) & BitBoard::maskDarkSq);
 
    const int qV = ::qV;
 
 
 
    if (pos.materialId() == MI::WB * 2 + MI::BN) {
 
        if (!doEval) return 1;
 
        if (nWB1 == 1)
 
            return 100 + mateEval(pos.getKingSq(true), pos.getKingSq(false));
 
    }
 
    if (pos.materialId() == MI::BB * 2 + MI::WN) {
 
        if (!doEval) return 1;
 
        if (nBB1 == 1)
 
            return -(100 + mateEval(pos.getKingSq(false), pos.getKingSq(true)));
 
    }
 
 
 
    // Bonus for K[BN][BN]KQ
 
    if ((pos.bMtrl() == qV) && pos.pieceTypeBB(Piece::BQUEEN) && ((nWN >= 2) || (nWB1 + nWB2 >= 2))) {
 
        if (!doEval) return 1;
 
        if ((score < 0) && ((nWN >= 2) || ((nWB1 >= 1) && (nWB2 >= 1))))
 
            return -((pos.bMtrl() - pos.wMtrl()) / 8 + mateEval(pos.getKingSq(false), pos.getKingSq(true)));
 
    }
 
    if ((pos.wMtrl() == qV) && pos.pieceTypeBB(Piece::WQUEEN) && ((nBN >= 2) || (nBB1 + nBB2 >= 2))) {
 
        if (!doEval) return 1;
 
        if ((score > 0) && ((nBN >= 2) || ((nBB1 >= 1) && (nBB2 >= 1))))
 
            return (pos.wMtrl() - pos.bMtrl()) / 8 + mateEval(pos.getKingSq(true), pos.getKingSq(false));
 
    }
 
    if ((pos.bMtrl() == qV) && pos.pieceTypeBB(Piece::BQUEEN) && ((nWN >= 1) && (nWB1 + nWB2 >= 1))) {
 
        if (!doEval) return 1;
 
        if (score < 0)
 
            return -((pos.bMtrl() - pos.wMtrl()) / 2 + mateEval(pos.getKingSq(false), pos.getKingSq(true)));
 
    }
 
    if ((pos.wMtrl() == qV) && pos.pieceTypeBB(Piece::WQUEEN) && ((nBN >= 1) && (nBB1 + nBB2 >= 1))) {
 
        if (!doEval) return 1;
 
        if (score > 0)
 
            return (pos.wMtrl() - pos.bMtrl()) / 2 + mateEval(pos.getKingSq(true), pos.getKingSq(false));
 
    }
 
 
 
    // Bonus for KRK
 
    if ((pos.bMtrl() == 0) && pos.pieceTypeBB(Piece::WROOK)) {
 
        if (!doEval) return 1;
 
        return 400 + pos.wMtrl() - pos.bMtrl() + mateEval(pos.getKingSq(true), pos.getKingSq(false));
 
    }
 
    if ((pos.wMtrl() == 0) && pos.pieceTypeBB(Piece::BROOK)) {
 
        if (!doEval) return 1;
 
        return -(400 + pos.bMtrl() - pos.wMtrl() + mateEval(pos.getKingSq(false), pos.getKingSq(true)));
 
    }
 
 
 
    // Bonus for KQK[BN]
 
    const int bV = ::bV;
 
    const int nV = ::nV;
 
    if (pos.pieceTypeBB(Piece::WQUEEN) && (bMtrlPawns == 0) && (pos.bMtrl() <= std::max(bV,nV))) {
 
        if (!doEval) return 1;
 
        return 200 + pos.wMtrl() - pos.bMtrl() + mateEval(pos.getKingSq(true), pos.getKingSq(false));
 
    }
 
    if (pos.pieceTypeBB(Piece::BQUEEN) && (wMtrlPawns == 0) && (pos.wMtrl() <= std::max(bV,nV))) {
 
        if (!doEval) return 1;
 
        return -(200 + pos.bMtrl() - pos.wMtrl() + mateEval(pos.getKingSq(false), pos.getKingSq(true)));
 
    }
 
 
 
    // Bonus for KQK
 
    if ((pos.bMtrl() == 0) && pos.pieceTypeBB(Piece::WQUEEN)) {
 
        if (!doEval) return 1;
 
        return 100 + pos.wMtrl() - pos.bMtrl() + mateEval(pos.getKingSq(true), pos.getKingSq(false));
 
    }
 
    if ((pos.wMtrl() == 0) && pos.pieceTypeBB(Piece::BQUEEN)) {
 
        if (!doEval) return 1;
 
        return -(100 + pos.bMtrl() - pos.wMtrl() + mateEval(pos.getKingSq(false), pos.getKingSq(true)));
 
    }
 
 
 
    if (pos.pieceTypeBB(Piece::WROOK, Piece::WKNIGHT, Piece::WQUEEN) == 0) {
 
        if (!doEval) return 1;
 
        if ((score > 0) && isBishopPawnDraw<true>(pos))
 
            return 0;
 
    }
 
    if (pos.pieceTypeBB(Piece::BROOK, Piece::BKNIGHT, Piece::BQUEEN) == 0) {
 
        if (!doEval) return 1;
 
        if ((score < 0) && isBishopPawnDraw<false>(pos))
 
            return 0;
 
    }
 
 
 
    // Give bonus/penalty if advantage is/isn't large enough to win
 
    if ((wMtrlPawns == 0) && (wMtrlNoPawns <= bMtrlNoPawns + bV)) {
 
        if (!doEval) return 1;
 
        if (score > 0) {
 
            if (wMtrlNoPawns < rV)
 
                return -pos.bMtrl() / 50;
 
            else
 
                return score / 8;        // Too little excess material, probably draw
 
        }
 
    }
 
    if ((bMtrlPawns == 0) && (bMtrlNoPawns <= wMtrlNoPawns + bV)) {
 
        if (!doEval) return 1;
 
        if (score < 0) {
 
            if (bMtrlNoPawns < rV)
 
                return pos.wMtrl() / 50;
 
            else
 
                return score / 8;        // Too little excess material, probably draw
 
        }
 
    }
 
 
 
    if ((bMtrlPawns == 0) && (wMtrlNoPawns - bMtrlNoPawns > bV)) {
 
        if (!doEval) return 1;
 
        return score + 300;       // Enough excess material, should win
 
    }
 
    if ((wMtrlPawns == 0) && (bMtrlNoPawns - wMtrlNoPawns > bV)) {
 
        if (!doEval) return 1;
 
        return score - 300;       // Enough excess material, should win
 
    }
 
 
 
    // Give bonus for advantage larger than KRKP, to avoid evaluation discontinuity
 
    if ((pos.bMtrl() == pV) && pos.pieceTypeBB(Piece::WROOK) && (pos.wMtrl() > rV)) {
 
        if (!doEval) return 1;
 
        return score + krkpBonus;
 
    }
 
    if ((pos.wMtrl() == pV) && pos.pieceTypeBB(Piece::BROOK) && (pos.bMtrl() > rV)) {
 
        if (!doEval) return 1;
 
        return score - krkpBonus;
 
    }
 
 
 
    // Bonus for KRPKN
 
    if (pos.pieceTypeBB(Piece::WROOK) && pos.pieceTypeBB(Piece::WPAWN) &&
 
        !pos.pieceTypeBB(Piece::BBISHOP) && (pos.bMtrl() == nV)  && (bMtrlPawns == 0)) {
 
        if (!doEval) return 1;
 
        return score + krpknBonus;
 
    }
 
    if (pos.pieceTypeBB(Piece::BROOK) && pos.pieceTypeBB(Piece::BPAWN) &&
 
        !pos.pieceTypeBB(Piece::WBISHOP) && (pos.wMtrl() == nV)  && (wMtrlPawns == 0)) {
 
        if (!doEval) return 1;
 
        return score - krpknBonus;
 
    }
 
 
 
    // Bonus for KRPKB
 
    int krpkbAdjustment = 0;
 
    if (pos.pieceTypeBB(Piece::WROOK) && pos.pieceTypeBB(Piece::WPAWN) &&
 
        !pos.pieceTypeBB(Piece::BKNIGHT) && (pos.bMtrl() == bV)  && (bMtrlPawns == 0)) {
 
        if (!doEval) return 1;
 
        score += krpkbBonus;
 
        krpkbAdjustment += krpkbBonus;
 
    }
 
    if (pos.pieceTypeBB(Piece::BROOK) && pos.pieceTypeBB(Piece::BPAWN) &&
 
        !pos.pieceTypeBB(Piece::WKNIGHT) && (pos.wMtrl() == bV)  && (wMtrlPawns == 0)) {
 
        if (!doEval) return 1;
 
        score -= krpkbBonus;
 
        krpkbAdjustment += krpkbBonus;
 
    }
 
 
 
    // Penalty for KRPKB when pawn is on a/h file
 
     if ((wMtrlNoPawns == rV) && (wMtrlPawns <= pV) && pos.pieceTypeBB(Piece::BBISHOP)) {
 
        if (!doEval) return 1;
 
        if (score - krpkbAdjustment > 0) {
 
            U64 pMask = pos.pieceTypeBB(Piece::WPAWN);
 
            U64 bMask = pos.pieceTypeBB(Piece::BBISHOP);
 
            if (((pMask & BitBoard::maskFile[0]) && (bMask & BitBoard::maskDarkSq)) ||
 
                ((pMask & BitBoard::maskFile[7]) && (bMask & BitBoard::maskLightSq))) {
 
                score = (score - krpkbAdjustment) * krpkbPenalty / 128;
 
                return score;
 
            }
 
        }
 
    }
 
    if ((bMtrlNoPawns == rV) && (bMtrlPawns <= pV) && pos.pieceTypeBB(Piece::WBISHOP)) {
 
        if (!doEval) return 1;
 
        if (score + krpkbAdjustment < 0) {
 
            U64 pMask = pos.pieceTypeBB(Piece::BPAWN);
 
            U64 bMask = pos.pieceTypeBB(Piece::WBISHOP);
 
            if (((pMask & BitBoard::maskFile[0]) && (bMask & BitBoard::maskLightSq)) ||
 
                ((pMask & BitBoard::maskFile[7]) && (bMask & BitBoard::maskDarkSq))) {
 
                score = (score + krpkbAdjustment) * krpkbPenalty / 128;
 
                return score;
 
            }
 
        }
 
    }
 
 
 
    auto getPawnAsymmetry = [passedPawns, &pos]() {
 
        int f1 = BitBoard::southFill(pos.pieceTypeBB(Piece::WPAWN)) & 0xff;
 
        int f2 = BitBoard::southFill(pos.pieceTypeBB(Piece::BPAWN)) & 0xff;
 
        int asymmetry = BitBoard::bitCount((f1 & ~f2) | (f2 & ~f1));
 
        U64 passedPawnsW = passedPawns & pos.pieceTypeBB(Piece::WPAWN);
 
        U64 passedPawnsB = passedPawns & pos.pieceTypeBB(Piece::BPAWN);
 
        asymmetry += BitBoard::bitCount(passedPawnsW) + BitBoard::bitCount(passedPawnsB);
 
        return asymmetry;
 
    };
 
 
 
    // Account for draw factor in rook endgames
 
    if ((BitBoard::bitCount(pos.pieceTypeBB(Piece::WROOK)) == 1) &&
 
        (BitBoard::bitCount(pos.pieceTypeBB(Piece::BROOK)) == 1) &&
 
        (pos.pieceTypeBB(Piece::WQUEEN, Piece::WBISHOP, Piece::WKNIGHT,
 
                         Piece::BQUEEN, Piece::BBISHOP, Piece::BKNIGHT) == 0) &&
 
        (BitBoard::bitCount(pos.pieceTypeBB(Piece::WPAWN, Piece::BPAWN)) > 1)) {
 
        if (!doEval) return 1;
 
        int asymmetry = getPawnAsymmetry();
 
        score = score * rookEGDrawFactor[std::min(asymmetry, 6)] / 128;
 
        return score;
 
    }
 
 
 
    // Correction for draw factor in RvsB endgames
 
    if ((BitBoard::bitCount(pos.pieceTypeBB(Piece::WROOK)) == 1) &&
 
        (BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP)) == 1) &&
 
        (pos.pieceTypeBB(Piece::WQUEEN, Piece::WBISHOP, Piece::WKNIGHT,
 
                         Piece::BQUEEN, Piece::BROOK, Piece::BKNIGHT) == 0) &&
 
        (wMtrlPawns - bMtrlPawns == -pV)) {
 
        if (!doEval) return 1;
 
        int asymmetry = getPawnAsymmetry();
 
        score = score * RvsBPDrawFactor[std::min(asymmetry, 6)] / 128;
 
        return score;
 
    }
 
    // Correction for draw factor in RvsB endgames
 
    if ((BitBoard::bitCount(pos.pieceTypeBB(Piece::BROOK)) == 1) &&
 
        (BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP)) == 1) &&
 
        (pos.pieceTypeBB(Piece::BQUEEN, Piece::BBISHOP, Piece::BKNIGHT,
 
                         Piece::WQUEEN, Piece::WROOK, Piece::WKNIGHT) == 0) &&
 
        (wMtrlPawns - bMtrlPawns == pV)) {
 
        if (!doEval) return 1;
 
        int asymmetry = getPawnAsymmetry();
 
        score = score * RvsBPDrawFactor[std::min(asymmetry, 6)] / 128;
 
        return score;
 
    }
 
 
 
    if (!doEval) return 0;
 
    return score;
 
}
 
 
 
int
 
EndGameEval::mateEval(int k1, int k2) {
 
    static const int loseKingTable[64] = {
 
        0,   4,   8,  12,  12,   8,   4,   0,
 
        4,   8,  12,  16,  16,  12,   8,   4,
 
        8,  12,  16,  20,  20,  16,  12,   8,
 
       12,  16,  20,  24,  24,  20,  16,  12,
 
       12,  16,  20,  24,  24,  20,  16,  12,
 
        8,  12,  16,  20,  20,  16,  12,   8,
 
        4,   8,  12,  16,  16,  12,   8,   4,
 
        0,   4,   8,  12,  12,   8,   4,   0
 
    };
 
    return winKingTable[k1] - loseKingTable[k2];
 
}
 
 
 
template <bool whiteBishop>
 
bool
 
EndGameEval::isBishopPawnDraw(const Position& pos) {
 
    const Piece::Type bishop = whiteBishop ? Piece::WBISHOP : Piece::BBISHOP;
 
    const bool darkBishop  = (pos.pieceTypeBB(bishop) & BitBoard::maskDarkSq) != 0;
 
    const bool lightBishop = (pos.pieceTypeBB(bishop) & BitBoard::maskLightSq) != 0;
 
    if (darkBishop && lightBishop)
 
        return false; // No draw against proper bishop pair
 
 
 
    const Piece::Type pawn = whiteBishop ? Piece::WPAWN : Piece::BPAWN;
 
    if (pos.pieceTypeBB(pawn) == 0)
 
        return true; // Only bishops on same color can not win
 
 
 
    // Check for rook pawn + wrong color bishop
 
    if (whiteBishop) {
 
        if (((pos.pieceTypeBB(pawn) & BitBoard::maskBToHFiles) == 0) &&
 
            !lightBishop &&
 
            ((pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(A8,B8,A7,B7)) != 0)) {
 
            return true;
 
        } else
 
        if (((pos.pieceTypeBB(pawn) & BitBoard::maskAToGFiles) == 0) &&
 
            !darkBishop &&
 
            ((pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(G8,H8,G7,H7)) != 0)) {
 
            return true;
 
        }
 
    } else {
 
        if (((pos.pieceTypeBB(pawn) & BitBoard::maskBToHFiles) == 0) &&
 
            !darkBishop &&
 
            ((pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(A1,B1,A2,B2)) != 0)) {
 
            return true;
 
        } else
 
        if (((pos.pieceTypeBB(pawn) & BitBoard::maskAToGFiles) == 0) &&
 
            !lightBishop &&
 
            ((pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(G1,H1,G2,H2)) != 0)) {
 
            return true;
 
        }
 
    }
 
 
 
    // Check for fortress containing WPb6, BPb7, white bishop on dark square
 
    const Piece::Type king = whiteBishop ? Piece::WKING : Piece::BKING;
 
    const Piece::Type oPawn = whiteBishop ? Piece::BPAWN : Piece::WPAWN;
 
    const Piece::Type oKnight = whiteBishop ? Piece::BKNIGHT : Piece::WKNIGHT;
 
    const int b7 = whiteBishop ? (darkBishop ? B7 : G7) : (lightBishop ? B2 : G2);
 
    const int b6 = whiteBishop ? (darkBishop ? B6 : G6) : (lightBishop ? B3 : G3);
 
    const int c7 = whiteBishop ? (darkBishop ? C7 : F7) : (lightBishop ? C2 : F2);
 
    const int a8 = whiteBishop ? (darkBishop ? A8 : H8) : (lightBishop ? A1 : H1);
 
    const int b8 = whiteBishop ? (darkBishop ? B8 : G8) : (lightBishop ? B1 : G1);
 
    const int c8 = whiteBishop ? (darkBishop ? C8 : F8) : (lightBishop ? C1 : F1);
 
    const int d8 = whiteBishop ? (darkBishop ? D8 : E8) : (lightBishop ? D1 : E1);
 
    const int d7 = whiteBishop ? (darkBishop ? D7 : E7) : (lightBishop ? D2 : E2);
 
    const U64 bFile = (whiteBishop == darkBishop) ? 0x0202020202020202ULL : 0x4040404040404040ULL;
 
    const U64 acFile = (whiteBishop == darkBishop) ? 0x0505050505050505ULL : 0xA0A0A0A0A0A0A0A0ULL;
 
    const U64 corner = whiteBishop ? (darkBishop ? BitBoard::sqMask(A8,B8,A7) : BitBoard::sqMask(G8,H8,H7))
 
                                   : (lightBishop ? BitBoard::sqMask(A1,B1,A2) : BitBoard::sqMask(G1,H1,H2));
 
 
 
    if ((pos.getPiece(b7) == oPawn) && (pos.getPiece(b6) == pawn) &&
 
        (pos.getPiece(a8) != oKnight) && ((pos.pieceTypeBB(king) & corner) == 0) &&
 
        (BitBoard::bitCount(pos.pieceTypeBB(oPawn) & acFile) <= 1)) {
 
        if (pos.getPiece(c7) == pawn) {
 
            if (BitBoard::bitCount(pos.pieceTypeBB(pawn) & ~bFile) == 1) {
 
                int oKingSq = pos.getKingSq(!whiteBishop);
 
                if ((oKingSq == c8) || (oKingSq == d7))
 
                    return true;
 
            }
 
        } else {
 
            int oKingSq = pos.getKingSq(!whiteBishop);
 
            if ((pos.pieceTypeBB(pawn) & ~bFile) == 0) {
 
                if ((oKingSq == a8) || (oKingSq == b8) || (oKingSq == c8) ||
 
                    (oKingSq == d8) || (oKingSq == d7))
 
                    return true;
 
            } else if (pos.isWhiteMove() != whiteBishop) { // Test if stale-mate
 
                int oMtrl = whiteBishop ? pos.bMtrl() : pos.wMtrl();
 
                U64 bShift = whiteBishop ? (pos.pieceTypeBB(bishop) << 8) : (pos.pieceTypeBB(bishop) >> 8);
 
                U64 kShift = whiteBishop ? (pos.pieceTypeBB(king) << 8) : (pos.pieceTypeBB(king) >> 8);
 
                const U64 md6_h2 = whiteBishop ?
 
                        (darkBishop  ? BitBoard::sqMask(D6,E5,F4,G3,H2) : BitBoard::sqMask(E6,D5,C4,B3,A2)) :
 
                        (lightBishop ? BitBoard::sqMask(D3,E4,F5,G6,H7) : BitBoard::sqMask(E3,D4,C5,B6,A7));
 
                if ((oMtrl == pV) ||
 
                    ((oMtrl == 2*pV) && ((bShift & pos.pieceTypeBB(oPawn)) ||
 
                                         ((kShift & pos.pieceTypeBB(oPawn)) &&
 
                                          ((pos.pieceTypeBB(oPawn) & md6_h2) == 0))))) {
 
                    const U64 mc7c8 = whiteBishop ?
 
                            (darkBishop  ? BitBoard::sqMask(C7,C8) : BitBoard::sqMask(F7,F8)) :
 
                            (lightBishop ? BitBoard::sqMask(C2,C1) : BitBoard::sqMask(F2,F1));
 
                    const U64 md6e6e7e8 = whiteBishop ?
 
                            (darkBishop  ? BitBoard::sqMask(D6,E6,E7,E8) : BitBoard::sqMask(E6,D6,D7,D8)) :
 
                            (lightBishop ? BitBoard::sqMask(D3,E3,E2,E1) : BitBoard::sqMask(E3,D3,D2,D1));
 
                    const U64 me7e8 = whiteBishop ?
 
                            (darkBishop  ? BitBoard::sqMask(E7,E8) : BitBoard::sqMask(D7,D8)) :
 
                            (lightBishop ? BitBoard::sqMask(E2,E1) : BitBoard::sqMask(D2,D1));
 
                    if (oKingSq == a8) {
 
                        if ((pos.pieceTypeBB(king) & mc7c8) ||
 
                                (pos.pieceTypeBB(bishop) & (md6_h2 | (1ULL << c7))))
 
                            return true;
 
                    } else if (oKingSq == c8) {
 
                        if (pos.getPiece(c7) == bishop) {
 
                            if (pos.pieceTypeBB(king) & md6e6e7e8)
 
                                return true;
 
                        } else {
 
                            if ((pos.pieceTypeBB(bishop) & md6_h2) && (pos.pieceTypeBB(king) & me7e8))
 
                                return true;
 
                        }
 
                    }
 
                }
 
            }
 
        }
 
    }
 
 
 
    // Check for fortress when all pawns are on the B file and there is no bishop
 
    if (whiteBishop) {
 
        if (pos.pieceTypeBB(Piece::WBISHOP) == 0) {
 
            if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileB) == 0) {
 
                if ((pos.getPiece(B7) == Piece::BPAWN) &&
 
                    (pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(A7,A8,B8)))
 
                    return true;
 
            }
 
            if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileG) == 0) {
 
                if ((pos.getPiece(G7) == Piece::BPAWN) &&
 
                    (pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(H7,H8,G8)))
 
                    return true;
 
            }
 
        }
 
    } else {
 
        if (pos.pieceTypeBB(Piece::BBISHOP) == 0) {
 
            if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileB) == 0) {
 
                if ((pos.getPiece(B2) == Piece::WPAWN) &&
 
                    (pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(A2,A1,B1)))
 
                    return true;
 
            }
 
            if ((pos.pieceTypeBB(Piece::WPAWN,Piece::BPAWN) & ~BitBoard::maskFileG) == 0) {
 
                if ((pos.getPiece(G2) == Piece::WPAWN) &&
 
                    (pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(H2,H1,G1)))
 
                    return true;
 
            }
 
        }
 
    }
 
 
 
    return false;
 
}
 
 
 
int
 
EndGameEval::kqkpEval(int wKing, int wQueen, int bKing, int bPawn, bool whiteMove, int score) {
 
    bool canWin = false;
 
    if (((1ULL << bKing) & 0xFFFF) == 0) {
 
        canWin = true; // King doesn't support pawn
 
    } else if (std::abs(Position::getX(bPawn) - Position::getX(bKing)) > 2) {
 
        canWin = true; // King doesn't support pawn
 
    } else {
 
        switch (bPawn) {
 
        case A2:
 
            canWin = ((1ULL << wKing) & 0x0F1F1F1F1FULL) != 0;
 
            if (canWin && (bKing == A1) && (Position::getX(wQueen) == 1) && !whiteMove)
 
                canWin = false; // Stale-mate
 
            break;
 
        case C2:
 
            canWin = ((1ULL << wKing) & 0x071F1F1FULL) != 0;
 
            break;
 
        case F2:
 
            canWin = ((1ULL << wKing) & 0xE0F8F8F8ULL) != 0;
 
            break;
 
        case H2:
 
            canWin = ((1ULL << wKing) & 0xF0F8F8F8F8ULL) != 0;
 
            if (canWin && (bKing == H1) && (Position::getX(wQueen) == 6) && !whiteMove)
 
                canWin = false; // Stale-mate
 
            break;
 
        default:
 
            canWin = true;
 
            break;
 
        }
 
    }
 
 
 
    const int dist = BitBoard::getKingDistance(wKing, bPawn);
 
    score = score - 20 * (dist - 4);
 
    if (!canWin)
 
        score /= 50;
 
    return score;
 
}
 
 
 
int
 
EndGameEval::kqkrpEval(int wKing, int wQueen, int bKing, int bRook, int bPawn, bool whiteMove, int score) {
 
    if (!(BitBoard::bPawnAttacks[bPawn] & (1ULL << bRook)))
 
        return score; // Rook not protected by pawn, no fortress
 
    if ((1ULL << bPawn) & (BitBoard::maskFileE | BitBoard::maskFileF |
 
                           BitBoard::maskFileG | BitBoard::maskFileH)) { // Mirror X
 
        wKing ^= 7;
 
        wQueen ^= 7;
 
        bKing ^= 7;
 
        bRook ^= 7;
 
        bPawn ^= 7;
 
    }
 
    bool drawish = false;
 
    switch (bPawn) {
 
    case A6:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(A8,B8,A7,B7)) &&
 
                  (Position::getX(wKing) >= 2) &&
 
                  (Position::getY(wKing) <= 3);
 
        break;
 
    case A2:
 
        drawish = (((1ULL << bKing) & BitBoard::sqMask(A4,B4,A3,B3)) != 0); // Pierre-Marie Baty -- boolean conversion fix
 
        break;
 
    case B7:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(A8,B8,C8,A7,C7)) &&
 
                  (Position::getY(wKing) <= 4);
 
        break;
 
    case B6:
 
        if (bRook == C5) {
 
            drawish = ((1ULL << bKing) & BitBoard::sqMask(A7,B7)) ||
 
                      (((1ULL << bKing) & BitBoard::sqMask(A8,B8)) &&
 
                       ((Position::getX(wKing) >= 3) || (Position::getY(wKing) <= 3)) &&
 
                       (((1ULL << wQueen) & BitBoard::maskRow7) ||
 
                        (!whiteMove && (wQueen != A6)) ||
 
                        (whiteMove && !((1ULL << wQueen) & BitBoard::sqMask(A6,A5,A4,A3,A2,A1,B5,B4,B3,B2,B1,C4,D3,E2,F1)))));
 
        }
 
        break;
 
    case B5:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(A6,B6,C6,A5)) &&
 
                  (Position::getY(wKing) <= 2);
 
        break;
 
    case B4:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(A6,B6,C6,A5,B5,C5)) &&
 
                  ((Position::getY(wKing) <= 2) || (Position::getX(wKing) >= 4));
 
        break;
 
    case B3:
 
        drawish = (((1ULL << bKing) & BitBoard::sqMask(A4,B4,C4,A3)) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1Row8)) ||
 
                  (((1ULL << bKing) & BitBoard::sqMask(A5,B5)) &&
 
                   ((1ULL << wQueen) & BitBoard::maskRow4) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1));
 
        break;
 
    case B2:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(A4,B4,C4,A3,B3,C3,A2,C2)) &&
 
                  ((1ULL << wKing) & BitBoard::sqMask(A4,B4,C4,A3,B3,C3,A2,C2)) == 0;
 
        break;
 
    case C7:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(B8,C8,D8,B7,D7)) &&
 
                  (Position::getY(wKing) <= 4);
 
        break;
 
    case C3:
 
        drawish = (((1ULL << bKing) & BitBoard::sqMask(B4,C4,D4)) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1Row8)) ||
 
                  (((1ULL << bKing) & BitBoard::sqMask(B5,C5)) &&
 
                   (((1ULL << wQueen) & BitBoard::maskRow4) || !whiteMove) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1)) ||
 
                  ((((bKing == B3) && (wQueen != B5)) || ((bKing == D3) && (wQueen != D5))) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1));
 
 
 
        break;
 
    case C2:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(B3,C3,D3,B2,D2)) &&
 
                  ((Position::getX(wKing) == 0) || (Position::getX(wKing) >= 4));
 
        break;
 
    case D7:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(C8,D8,E8,C7,E7)) &&
 
                  (Position::getY(wKing) <= 4);
 
        break;
 
    case D3:
 
        drawish = (((1ULL << bKing) & BitBoard::sqMask(C4,D4,E4)) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1Row8)) ||
 
                  (((1ULL << bKing) & BitBoard::sqMask(C5,D5,E5)) &&
 
                   (((1ULL << wQueen) & BitBoard::maskRow4) || !whiteMove) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1)) ||
 
                  ((((bKing == C3) && (wQueen != C5)) || ((bKing == E3) && (wQueen != E5))) &&
 
                   ((1ULL << wKing) & BitBoard::maskRow1));
 
        break;
 
    case D2:
 
        drawish = ((1ULL << bKing) & BitBoard::sqMask(C4,D4,E4,C3,D3,E3,C2,E2)) &&
 
                  ((1ULL << wKing) & BitBoard::sqMask(C4,D4,E4,C3,D3,E3,C2,E2)) == 0;
 
        break;
 
    default:
 
        drawish = false;
 
        break;
 
    }
 
    return drawish ? score / 16 : score;
 
}
 
 
 
int
 
EndGameEval::kpkEval(int wKing, int bKing, int wPawn, bool whiteMove) {
 
    if (Position::getX(wKing) >= 4) { // Mirror X
 
        wKing ^= 7;
 
        bKing ^= 7;
 
        wPawn ^= 7;
 
    }
 
    int index = whiteMove ? 0 : 1;
 
    index = index * 32 + Position::getY(wKing)*4+Position::getX(wKing);
 
    index = index * 64 + bKing;
 
    index = index * 48 + wPawn - 8;
 
 
 
    int bytePos = index / 8;
 
    int bitPos = index % 8;
 
    bool draw = (((int)kpkTable[bytePos]) & (1 << bitPos)) == 0;
 
    if (draw)
 
        return 0;
 
    return qV - pV / 4 * (7-Position::getY(wPawn));
 
}
 
 
 
bool
 
EndGameEval::kpkpEval(int wKing, int bKing, int wPawn, int bPawn, int& score) {
 
    const U64 wKingMask = 1ULL << wKing;
 
    const U64 bKingMask = 1ULL << bKing;
 
    if (wPawn == B6 && bPawn == B7) {
 
        if ((bKingMask & BitBoard::sqMask(A8,B8,C8,D8,D7)) &&
 
            ((wKingMask & BitBoard::sqMask(A8,B8,A7)) == 0)) {
 
            score = 0;
 
            return true;
 
        }
 
    } else if (wPawn == G6 && bPawn == G7) {
 
        if ((bKingMask & BitBoard::sqMask(E8,F8,G8,H8,E7)) &&
 
            ((wKingMask & BitBoard::sqMask(G8,H8,H7)) == 0)) {
 
            score = 0;
 
            return true;
 
        }
 
    } else if (wPawn == B2 && bPawn == B3) {
 
        if ((wKingMask & BitBoard::sqMask(A1,B1,C1,D1,D2)) &&
 
            ((bKingMask & BitBoard::sqMask(A1,B1,A2)) == 0)) {
 
            score = 0;
 
            return true;
 
        }
 
    } else if (wPawn == G2 && bPawn == G3) {
 
        if ((wKingMask & BitBoard::sqMask(E1,F1,G1,H1,E2)) &&
 
            ((bKingMask & BitBoard::sqMask(G1,H1,H2)) == 0)) {
 
            score = 0;
 
            return true;
 
        }
 
    }
 
    return false;
 
}
 
 
 
int
 
EndGameEval::krkpEval(int wKing, int bKing, int bPawn, bool whiteMove, int score) {
 
    if (Position::getX(bKing) >= 4) { // Mirror X
 
        wKing ^= 7;
 
        bKing ^= 7;
 
        bPawn ^= 7;
 
    }
 
    int index = whiteMove ? 0 : 1;
 
    index = index * 32 + Position::getY(bKing)*4+Position::getX(bKing);
 
    index = index * 48 + bPawn - 8;
 
    index = index * 8 + Position::getY(wKing);
 
    U8 mask = krkpTable[index];
 
    bool canWin = (mask & (1 << Position::getX(wKing))) != 0;
 
 
 
    score = score + Position::getY(bPawn) * pV / 4;
 
    if (!canWin)
 
        score /= 50;
 
    else
 
        score += krkpBonus;
 
    return score;
 
}
 
 
 
int
 
EndGameEval::krpkrEval(int wKing, int bKing, int wPawn, int wRook, int bRook, bool whiteMove) {
 
    if (Position::getX(wPawn) >= 4) { // Mirror X
 
        wKing ^= 7;
 
        bKing ^= 7;
 
        wPawn ^= 7;
 
        wRook ^= 7;
 
        bRook ^= 7;
 
    }
 
    int index = whiteMove ? 0 : 1;
 
    index = index * 24 + (Position::getY(wPawn)-1)*4+Position::getX(wPawn);
 
    index = index * 64 + wKing;
 
    const U64 kMask = krpkrTable[index];
 
    const bool canWin = (kMask & (1ULL << bKing)) != 0;
 
    U64 kingNeighbors = BitBoard::kingAttacks[bKing];
 
    const U64 occupied = (1ULL<<wKing) | (1ULL<<bKing) | (1ULL<<wPawn) | (1ULL<<bRook);
 
    const U64 rAtk = BitBoard::rookAttacks(wRook, occupied);
 
    kingNeighbors &= ~(BitBoard::kingAttacks[wKing] | BitBoard::wPawnAttacks[wPawn] | rAtk);
 
    bool close;
 
    if (canWin) {
 
        close = (kMask & kingNeighbors) != kingNeighbors;
 
    } else {
 
        close = (kMask & kingNeighbors) != 0;
 
    }
 
    int score = pV + Position::getY(wPawn) * pV / 4;
 
    if (canWin) {
 
        if (!close)
 
            score += pV;
 
    } else {
 
        if (close)
 
            score /= 2;
 
        else
 
            score /= 4;
 
    }
 
    return score;
 
}
 
 
 
int
 
EndGameEval::krpkrpEval(int wKing, int bKing, int wPawn, int wRook, int bRook, int bPawn, bool whiteMove, int score) {
 
    int hiScore = krpkrEval(wKing, bKing, wPawn, wRook, bRook, whiteMove);
 
    if (score > hiScore * 14 / 16)
 
        return hiScore * 14 / 16;
 
    int loScore = -krpkrEval(63-bKing, 63-wKing, 63-bPawn, 63-bRook, 63-wRook, !whiteMove);
 
    if (score < loScore * 14 / 16)
 
        return loScore * 14 / 16;
 
    return score;
 
}
 
 
 
int
 
EndGameEval::kbnkEval(int wKing, int bKing, bool darkBishop) {
 
    int score = 600;
 
    if (darkBishop) { // Mirror X
 
        wKing ^= 7;
 
        bKing ^= 7;
 
    }
 
    static const int bkTable[64] = { 17, 15, 12,  9,  7,  4,  2,  0,
 
                                     15, 20, 17, 15, 12,  9,  4,  2,
 
                                     12, 17, 22, 20, 17, 15,  9,  4,
 
                                      9, 15, 20, 25, 22, 17, 12,  7,
 
                                      7, 12, 17, 22, 25, 20, 15,  9,
 
                                      4,  9, 15, 17, 20, 22, 17, 12,
 
                                      2,  4,  9, 12, 15, 17, 20, 15,
 
                                      0,  2,  4,  7,  9, 12, 15, 17 };
 
 
 
    score += winKingTable[wKing] - bkTable[bKing];
 
    score -= std::min(0, BitBoard::getTaxiDistance(wKing, bKing) - 3);
 
    return score;
 
}
 
 
 
int
 
EndGameEval::kbpkbEval(int wKing, int wBish, int wPawn, int bKing, int bBish, int score) {
 
    U64 wPawnMask = 1ULL << wPawn;
 
    U64 pawnPath = BitBoard::northFill(wPawnMask);
 
    U64 bKingMask = 1ULL << bKing;
 
    U64 wBishMask = 1ULL << wBish;
 
    U64 wBishControl = (wBishMask & BitBoard::maskDarkSq) ? BitBoard::maskDarkSq : BitBoard::maskLightSq;
 
    if ((bKingMask & pawnPath) && ((bKingMask & wBishControl) == 0))
 
        return 0;
 
 
 
    U64 bBishMask = 1ULL << bBish;
 
    if (((wBishMask & BitBoard::maskDarkSq) == 0) != ((bBishMask & BitBoard::maskDarkSq) == 0)) { // Different color bishops
 
        if (((bBishMask | BitBoard::bishopAttacks(bBish, bKingMask)) & pawnPath & ~wPawnMask) != 0)
 
            if (!(wPawn == A6 && bBish == B8) && !(wPawn == H6 && bBish == G8))
 
                return 0;
 
    }
 
 
 
    if (bKingMask & BitBoard::wPawnBlockerMask[wPawn])
 
        return score / 4;
 
    return score;
 
}
 
 
 
int
 
EndGameEval::kbpknEval(int wKing, int wBish, int wPawn, int bKing, int bKnight, int score) {
 
    U64 wPawnMask = 1ULL << wPawn;
 
    U64 pawnPath = BitBoard::northFill(wPawnMask);
 
    U64 bKingMask = 1ULL << bKing;
 
    U64 wBishMask = 1ULL << wBish;
 
    U64 wBishControl = (wBishMask & BitBoard::maskDarkSq) ? BitBoard::maskDarkSq : BitBoard::maskLightSq;
 
 
 
    U64 edges = 0xff818181818181ffULL;
 
    U64 bKnightMask = 1ULL << bKnight;
 
    if ((bKnightMask & edges & ~wBishControl) != 0) // Knight on edge square where it can be trapped
 
        return score;
 
 
 
    if ((bKingMask & pawnPath) && ((bKingMask & wBishControl) == 0))
 
        return 0;
 
 
 
    if (bKingMask & BitBoard::wPawnBlockerMask[wPawn])
 
        return score / 4;
 
    return score;
 
}
 
 
 
int
 
EndGameEval::knpkbEval(int wKing, int wKnight, int wPawn, int bKing, int bBish, int score, bool wtm) {
 
    U64 wPawnMask = 1ULL << wPawn;
 
    U64 bBishMask = 1ULL << bBish;
 
    U64 bBishControl = (bBishMask & BitBoard::maskDarkSq) ? BitBoard::maskDarkSq : BitBoard::maskLightSq;
 
 
 
    U64 p = wPawnMask;
 
    if (bBishControl & wPawnMask) {
 
        U64 bKingMask = 1ULL << bKing;
 
        U64 wKnightMask = 1ULL << wKnight;
 
        if (!wtm && (BitBoard::bishopAttacks(bBish, bKingMask | wKnightMask) & wPawnMask))
 
            return 0;
 
        p <<= 8;
 
    }
 
    U64 pawnDrawishMask = 0x183c7e7e7e7eULL;
 
    if (p & pawnDrawishMask)
 
        return score / 32;
 
 
 
    return score;
 
}
 
 
 
int
 
EndGameEval::knpkEval(int wKing, int wKnight, int wPawn, int bKing, int score, bool wtm) {
 
    if (Position::getX(wPawn) >= 4) { // Mirror X
 
        wKing ^= 7;
 
        wKnight ^= 7;
 
        wPawn ^= 7;
 
        bKing ^= 7;
 
    }
 
    if (wPawn == A7) {
 
        if (bKing == A8 || bKing == B7) // Fortress
 
            return 0;
 
        if (wKing == A8 && (bKing == C7 || bKing == C8)) {
 
            bool knightDark = Position::darkSquare(Position::getX(wKnight), Position::getY(wKnight));
 
            bool kingDark = Position::darkSquare(Position::getX(bKing), Position::getY(bKing));
 
            if (wtm == (knightDark == kingDark)) // King trapped
 
                return 0;
 
        }
 
    }
 
    return score;
 
}