/*
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/>.
*/
/*
* evaluate.cpp
*
* Created on: Feb 25, 2012
* Author: petero
*/
#include "evaluate.hpp"
#include "endGameEval.hpp"
#include <vector>
int Evaluate::pieceValueOrder[Piece::nPieceTypes] = {
0,
5, 4, 3, 2, 2, 1,
5, 4, 3, 2, 2, 1
};
static const int empty[64] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0};
int Evaluate::castleMaskFactor[256];
static StaticInitializer<Evaluate> evInit;
/** Get bitboard mask for a square translated (dx,dy). Return 0 if square outside board. */
static inline U64 getMask(int sq, int dx, int dy) {
int x = Position::getX(sq) + dx;
int y = Position::getY(sq) + dy;
if (x >= 0 && x < 8 && y >= 0 && y < 8)
return 1ULL << Position::getSquare(x, y);
else
return 0;
}
void
Evaluate::staticInitialize() {
psTab1[Piece::EMPTY] = empty;
psTab1[Piece::WKING] = kt1w.getTable();
psTab1[Piece::WQUEEN] = qt1w.getTable();
psTab1[Piece::WROOK] = rt1w.getTable();
psTab1[Piece::WBISHOP] = bt1w.getTable();
psTab1[Piece::WKNIGHT] = nt1w.getTable();
psTab1[Piece::WPAWN] = pt1w.getTable();
psTab1[Piece::BKING] = kt1b.getTable();
psTab1[Piece::BQUEEN] = qt1b.getTable();
psTab1[Piece::BROOK] = rt1b.getTable();
psTab1[Piece::BBISHOP] = bt1b.getTable();
psTab1[Piece::BKNIGHT] = nt1b.getTable();
psTab1[Piece::BPAWN] = pt1b.getTable();
psTab2[Piece::EMPTY] = empty;
psTab2[Piece::WKING] = kt2w.getTable();
psTab2[Piece::WQUEEN] = qt2w.getTable();
psTab2[Piece::WROOK] = rt1w.getTable();
psTab2[Piece::WBISHOP] = bt2w.getTable();
psTab2[Piece::WKNIGHT] = nt2w.getTable();
psTab2[Piece::WPAWN] = pt2w.getTable();
psTab2[Piece::BKING] = kt2b.getTable();
psTab2[Piece::BQUEEN] = qt2b.getTable();
psTab2[Piece::BROOK] = rt1b.getTable();
psTab2[Piece::BBISHOP] = bt2b.getTable();
psTab2[Piece::BKNIGHT] = nt2b.getTable();
psTab2[Piece::BPAWN] = pt2b.getTable();
// Initialize knight/bishop king safety patterns
for (int sq = 0; sq < 64; sq++) {
const int x = Position::getX(sq);
const int y = Position::getY(sq);
int dx = (x < 4) ? -1 : 1;
int dy = (y < 4) ? 1 : -1;
U64 n = getMask(sq, -dx, 0) | getMask(sq, dx, 0) | getMask(sq, 0, dy) | getMask(sq, 0, 2*dy) | getMask(sq, dx, 2*dy);
U64 b = getMask(sq, -dx, 0) | getMask(sq, 0, dy) | getMask(sq, dx, 2*dy);
knightKingProtectPattern[sq] = n;
bishopKingProtectPattern[sq] = b;
}
}
void
Evaluate::updateEvalParams() {
// Castle bonus
for (int i = 0; i < 256; i++) {
int h1Dist = 100;
bool h1Castle = (i & (1<<7)) != 0;
if (h1Castle)
h1Dist = BitBoard::bitCount(i & BitBoard::sqMask(F1,G1));
int a1Dist = 100;
bool a1Castle = (i & 1) != 0;
if (a1Castle)
a1Dist = BitBoard::bitCount(i & BitBoard::sqMask(B1,C1,D1));
int dist = std::min(a1Dist, h1Dist);
castleMaskFactor[i] = dist < 4 ? castleFactor[dist] : 0;
}
// Knight mobility scores
for (int sq = 0; sq < 64; sq++) {
int x = Position::getX(sq);
int y = Position::getY(sq);
if (x >= 4) x = 7 - x;
if (y >= 4) y = 7 - y;
if (x < y) std::swap(x, y);
int maxMob = 0;
switch (y*8+x) {
case A1: maxMob = 2; break;
case B1: maxMob = 3; break;
case C1: maxMob = 4; break;
case D1: maxMob = 4; break;
case B2: maxMob = 4; break;
case C2: maxMob = 6; break;
case D2: maxMob = 6; break;
case C3: maxMob = 8; break;
case D3: maxMob = 8; break;
case D4: maxMob = 8; break;
default:
assert(false);
}
for (int m = 0; m <= 8; m++) {
int offs = 0;
switch (maxMob) {
case 2: offs = 0; break;
case 3: offs = 3; break;
case 4: offs = 7; break;
case 6: offs = 12; break;
case 8: offs = 19; break;
}
knightMobScoreA[sq][m] = knightMobScore[offs + std::min(m, maxMob)];
}
}
}
const int* Evaluate::psTab1[Piece::nPieceTypes];
const int* Evaluate::psTab2[Piece::nPieceTypes];
int Evaluate::knightMobScoreA[64][9];
U64 Evaluate::knightKingProtectPattern[64];
U64 Evaluate::bishopKingProtectPattern[64];
Evaluate::Evaluate(EvalHashTables& et)
: pawnHash(et.pawnHash),
materialHash(et.materialHash),
kingSafetyHash(et.kingSafetyHash),
wKingZone(0), bKingZone(0),
wKingAttacks(0), bKingAttacks(0),
wAttacksBB(0), bAttacksBB(0),
wPawnAttacks(0), bPawnAttacks(0) {
}
int
Evaluate::evalPos(const Position& pos) {
return evalPos<false>(pos);
}
int
Evaluate::evalPosPrint(const Position& pos) {
return evalPos<true>(pos);
}
template <bool print>
inline int
Evaluate::evalPos(const Position& pos) {
int score = materialScore(pos, print);
wKingAttacks = bKingAttacks = 0;
wKingZone = BitBoard::kingAttacks[pos.getKingSq(true)]; wKingZone |= wKingZone << 8;
bKingZone = BitBoard::kingAttacks[pos.getKingSq(false)]; bKingZone |= bKingZone >> 8;
wAttacksBB = bAttacksBB = 0L;
U64 pawns = pos.pieceTypeBB(Piece::WPAWN);
wPawnAttacks = ((pawns & BitBoard::maskBToHFiles) << 7) |
((pawns & BitBoard::maskAToGFiles) << 9);
pawns = pos.pieceTypeBB(Piece::BPAWN);
bPawnAttacks = ((pawns & BitBoard::maskBToHFiles) >> 9) |
((pawns & BitBoard::maskAToGFiles) >> 7);
score += pieceSquareEval(pos);
if (print) std::cout << "eval pst :" << score << std::endl;
score += pawnBonus(pos);
if (print) std::cout << "eval pawn :" << score << std::endl;
score += castleBonus(pos);
if (print) std::cout << "eval castle :" << score << std::endl;
score += rookBonus(pos);
if (print) std::cout << "eval rook :" << score << std::endl;
score += bishopEval(pos, score);
if (print) std::cout << "eval bishop :" << score << std::endl;
score += knightEval(pos);
if (print) std::cout << "eval knight :" << score << std::endl;
score += threatBonus(pos);
if (print) std::cout << "eval threat :" << score << std::endl;
score += protectBonus(pos);
if (print) std::cout << "eval protect:" << score << std::endl;
score += kingSafety(pos);
if (print) std::cout << "eval king :" << score << std::endl;
if (mhd->endGame)
score = EndGameEval::endGameEval<true>(pos, phd->passedPawns, score);
if (print) std::cout << "eval endgame:" << score << std::endl;
if (pos.pieceTypeBB(Piece::WPAWN, Piece::BPAWN)) {
int hmc = clamp(pos.getHalfMoveClock() / 10, 0, 9);
score = score * halfMoveFactor[hmc] / 128;
}
if (print) std::cout << "eval halfmove:" << score << std::endl;
if (score > 0) {
int nStale = BitBoard::bitCount(BitBoard::southFill(phd->stalePawns & pos.pieceTypeBB(Piece::WPAWN)) & 0xff);
score = score * stalePawnFactor[nStale] / 128;
} else if (score < 0) {
int nStale = BitBoard::bitCount(BitBoard::southFill(phd->stalePawns & pos.pieceTypeBB(Piece::BPAWN)) & 0xff);
score = score * stalePawnFactor[nStale] / 128;
}
if (print) std::cout << "eval staleP :" << score << std::endl;
if (!pos.isWhiteMove())
score = -score;
return score;
}
/** Compensate for the fact that many knights are stronger compared to queens
* than what the default material scores would predict. */
static inline int correctionNvsQ(int n, int q) {
if (n <= q+1)
return 0;
int knightBonus = 0;
if (q == 1)
knightBonus = knightVsQueenBonus1;
else if (q == 2)
knightBonus = knightVsQueenBonus2;
else if (q >= 3)
knightBonus = knightVsQueenBonus3;
int corr = knightBonus * (n - q - 1);
return corr;
}
void
Evaluate::computeMaterialScore(const Position& pos, MaterialHashData& mhd, bool print) const {
// Compute material part of score
int score = pos.wMtrl() - pos.bMtrl();
if (print) std::cout << "eval mtrlraw:" << score << std::endl;
const int nWQ = BitBoard::bitCount(pos.pieceTypeBB(Piece::WQUEEN));
const int nBQ = BitBoard::bitCount(pos.pieceTypeBB(Piece::BQUEEN));
const int nWN = BitBoard::bitCount(pos.pieceTypeBB(Piece::WKNIGHT));
const int nBN = BitBoard::bitCount(pos.pieceTypeBB(Piece::BKNIGHT));
int wCorr = correctionNvsQ(nWN, nBQ);
int bCorr = correctionNvsQ(nBN, nWQ);
score += wCorr - bCorr;
if (print) std::cout << "eval qncorr :" << score << std::endl;
score += tradeBonus(pos, wCorr, bCorr);
if (print) std::cout << "eval trade :" << score << std::endl;
const int nWR = BitBoard::bitCount(pos.pieceTypeBB(Piece::WROOK));
const int nBR = BitBoard::bitCount(pos.pieceTypeBB(Piece::BROOK));
{ // Redundancy of major pieces
int wMajor = nWQ + nWR;
int bMajor = nBQ + nBR;
int w = std::min(wMajor, 3);
int b = std::min(bMajor, 3);
score += majorPieceRedundancy[w*4+b];
}
if (print) std::cout << "eval majred :" << score << std::endl;
const int wMtrl = pos.wMtrl();
const int bMtrl = pos.bMtrl();
const int wMtrlPawns = pos.wMtrlPawns();
const int bMtrlPawns = pos.bMtrlPawns();
const int wMtrlNoPawns = wMtrl - wMtrlPawns;
const int bMtrlNoPawns = bMtrl - bMtrlPawns;
// Handle imbalances
const int nWB = BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP));
const int nBB = BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP));
const int nWP = BitBoard::bitCount(pos.pieceTypeBB(Piece::WPAWN));
const int nBP = BitBoard::bitCount(pos.pieceTypeBB(Piece::BPAWN));
{
const int dQ = nWQ - nBQ;
const int dR = nWR - nBR;
const int dB = nWB - nBB;
const int dN = nWN - nBN;
int nMinor = nWB + nWN + nBB + nBN;
if ((dQ == 1) && (dR == -2)) {
score += QvsRRBonus[std::min(4, nMinor)];
} else if ((dQ == -1) && (dR == 2)) {
score -= QvsRRBonus[std::min(4, nMinor)];
}
const int dP = nWP - nBP;
if ((dR == 1) && (dB + dN == -1)) {
score += RvsMBonus[clamp(dP, -3, 3)+3];
if (wMtrlNoPawns == rV && dB == -1 && dP == -1)
score += RvsBPBonus;
} else if ((dR == -1) && (dB + dN == 1)) {
score -= RvsMBonus[clamp(-dP, -3, 3)+3];
if (bMtrlNoPawns == rV && dB == 1 && dP == 1)
score -= RvsBPBonus;
}
if ((dR == 1) && (dB + dN == -2)) {
score += RvsMMBonus[clamp(dP, -3, 3)+3];
} else if ((dR == -1) && (dB + dN == 2)) {
score -= RvsMMBonus[clamp(-dP, -3, 3)+3];
}
if ((dQ == 1) && (dR == -1) && (dB + dN == -1)) {
score += (nWR == 0) ? (int)QvsRMBonus1 : (int)QvsRMBonus2; // Pierre-Marie Baty -- added type cast
} else if ((dQ == -1) && (dR == 1) && (dB + dN == 1)) {
score -= (nBR == 0) ? (int)QvsRMBonus1 : (int)QvsRMBonus2; // Pierre-Marie Baty -- added type cast
}
}
if (print) std::cout << "eval imbala :" << score << std::endl;
mhd.id = pos.materialId();
mhd.score = score;
mhd.endGame = EndGameEval::endGameEval<false>(pos, 0, 0);
// Compute interpolation factors
{ // Pawn
const int loMtrl = pawnLoMtrl;
const int hiMtrl = pawnHiMtrl;
mhd.wPawnIPF = interpolate(bMtrlNoPawns, loMtrl, 0, hiMtrl, IPOLMAX);
mhd.bPawnIPF = interpolate(wMtrlNoPawns, loMtrl, 0, hiMtrl, IPOLMAX);
if (wCorr > 100)
mhd.wPawnIPF = mhd.wPawnIPF * 100 / wCorr;
if (bCorr > 100)
mhd.bPawnIPF = mhd.bPawnIPF * 100 / bCorr;
}
{ // Knight/bishop
const int loMtrl = minorLoMtrl;
const int hiMtrl = minorHiMtrl;
mhd.wKnightIPF = interpolate(bMtrl, loMtrl, 0, hiMtrl, IPOLMAX);
mhd.bKnightIPF = interpolate(wMtrl, loMtrl, 0, hiMtrl, IPOLMAX);
}
{ // Castle
const int loMtrl = castleLoMtrl;
const int hiMtrl = castleHiMtrl;
const int m = wMtrlNoPawns + bMtrlNoPawns;
mhd.castleIPF = interpolate(m, loMtrl, 0, hiMtrl, IPOLMAX);
}
{
const int loMtrl = queenLoMtrl;
const int hiMtrl = queenHiMtrl;
const int m = wMtrlNoPawns + bMtrlNoPawns;
mhd.queenIPF = interpolate(m, loMtrl, 0, hiMtrl, IPOLMAX);
}
{ // Passed pawn
const int loMtrl = passedPawnLoMtrl;
const int hiMtrl = passedPawnHiMtrl;
mhd.wPassedPawnIPF = interpolate(bMtrlNoPawns-nBN*(nV/2), loMtrl, 0, hiMtrl, IPOLMAX);
mhd.bPassedPawnIPF = interpolate(wMtrlNoPawns-nWN*(nV/2), loMtrl, 0, hiMtrl, IPOLMAX);
}
{ // King safety
const int loMtrl = kingSafetyLoMtrl;
const int hiMtrl = kingSafetyHiMtrl;
const int m = (wMtrlNoPawns + bMtrlNoPawns) / 2;
mhd.kingSafetyIPF = interpolate(m, loMtrl, 0, hiMtrl, IPOLMAX);
if (wCorr + bCorr > 200)
mhd.kingSafetyIPF = mhd.kingSafetyIPF * 200 / (wCorr + bCorr);
}
{ // Different color bishops
const int loMtrl = oppoBishopLoMtrl;
const int hiMtrl = oppoBishopHiMtrl;
const int m = wMtrlNoPawns + bMtrlNoPawns;
mhd.diffColorBishopIPF = interpolate(m, loMtrl, 0, hiMtrl, IPOLMAX);
}
{ // Knight outpost
const int loMtrl = knightOutpostLoMtrl;
const int hiMtrl = knightOutpostHiMtrl;
mhd.wKnightOutPostIPF = interpolate(bMtrlPawns, loMtrl, 0, hiMtrl, IPOLMAX);
mhd.bKnightOutPostIPF = interpolate(wMtrlPawns, loMtrl, 0, hiMtrl, IPOLMAX);
}
}
int
Evaluate::tradeBonus(const Position& pos, int wCorr, int bCorr) const {
const int wM = pos.wMtrl() + wCorr;
const int bM = pos.bMtrl() + bCorr;
const int wPawn = pos.wMtrlPawns();
const int bPawn = pos.bMtrlPawns();
const int deltaScore = wM - bM;
int pBonus = 0;
pBonus += interpolate((deltaScore > 0) ? wPawn : bPawn, 0, -pawnTradePenalty * deltaScore / 100, pawnTradeThreshold, 0);
pBonus += interpolate((deltaScore > 0) ? bM : wM, 0, pieceTradeBonus * deltaScore / 100, pieceTradeThreshold * 100, 0);
return pBonus;
}
int
Evaluate::pieceSquareEval(const Position& pos) {
int score = 0;
// Kings/pawns
if (pos.wMtrlPawns() + pos.bMtrlPawns() > 0) {
{
const int k1 = pos.psScore1(Piece::WKING) + pos.psScore1(Piece::WPAWN);
const int k2 = pos.psScore2(Piece::WKING) + pos.psScore2(Piece::WPAWN);
score += interpolate(k2, k1, mhd->wPawnIPF);
}
{
const int k1 = pos.psScore1(Piece::BKING) + pos.psScore1(Piece::BPAWN);
const int k2 = pos.psScore2(Piece::BKING) + pos.psScore2(Piece::BPAWN);
score -= interpolate(k2, k1, mhd->bPawnIPF);
}
} else { // Use symmetric tables if no pawns left
if (pos.wMtrl() > pos.bMtrl())
score += EndGameEval::mateEval(pos.getKingSq(true), pos.getKingSq(false));
else if (pos.wMtrl() < pos.bMtrl())
score -= EndGameEval::mateEval(pos.getKingSq(false), pos.getKingSq(true));
else
score += EndGameEval::winKingTable[pos.getKingSq(true)] -
EndGameEval::winKingTable[pos.getKingSq(false)];
}
// Knights/bishops
{
int n1 = pos.psScore1(Piece::WKNIGHT) + pos.psScore1(Piece::WBISHOP);
int n2 = pos.psScore2(Piece::WKNIGHT) + pos.psScore2(Piece::WBISHOP);
score += interpolate(n2, n1, mhd->wKnightIPF);
n1 = pos.psScore1(Piece::BKNIGHT) + pos.psScore1(Piece::BBISHOP);
n2 = pos.psScore2(Piece::BKNIGHT) + pos.psScore2(Piece::BBISHOP);
score -= interpolate(n2, n1, mhd->bKnightIPF);
}
// Queens
{
const U64 occupied = pos.occupiedBB();
int q1 = pos.psScore1(Piece::WQUEEN);
int q2 = pos.psScore2(Piece::WQUEEN);
score += interpolate(q2, q1, mhd->queenIPF);
U64 m = pos.pieceTypeBB(Piece::WQUEEN);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
U64 atk = BitBoard::rookAttacks(sq, occupied) | BitBoard::bishopAttacks(sq, occupied);
wAttacksBB |= atk;
score += queenMobScore[BitBoard::bitCount(atk & ~(pos.whiteBB() | bPawnAttacks))];
bKingAttacks += BitBoard::bitCount(atk & bKingZone) * 2;
}
q1 = pos.psScore1(Piece::BQUEEN);
q2 = pos.psScore2(Piece::BQUEEN);
score -= interpolate(q2, q1, mhd->queenIPF);
m = pos.pieceTypeBB(Piece::BQUEEN);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
U64 atk = BitBoard::rookAttacks(sq, occupied) | BitBoard::bishopAttacks(sq, occupied);
bAttacksBB |= atk;
score -= queenMobScore[BitBoard::bitCount(atk & ~(pos.blackBB() | wPawnAttacks))];
wKingAttacks += BitBoard::bitCount(atk & wKingZone) * 2;
}
}
// Rooks
{
int r1 = pos.psScore1(Piece::WROOK);
if (r1 != 0) {
const int nP = BitBoard::bitCount(pos.pieceTypeBB(Piece::BPAWN));
const int s = r1 * std::min(nP, 6) / 6;
score += s;
}
r1 = pos.psScore1(Piece::BROOK);
if (r1 != 0) {
const int nP = BitBoard::bitCount(pos.pieceTypeBB(Piece::WPAWN));
const int s = r1 * std::min(nP, 6) / 6;
score -= s;
}
}
return score;
}
int
Evaluate::castleBonus(const Position& pos) {
if (pos.getCastleMask() == 0) return 0;
const int k1 = kt1b[G8] - kt1b[E8];
const int k2 = kt2b[G8] - kt2b[E8];
const int ks = interpolate(k2, k1, mhd->castleIPF);
const int castleValue = ks + rt1b[F8] - rt1b[H8];
if (castleValue <= 0)
return 0;
U64 occupied = pos.occupiedBB();
int tmp = (int) (occupied & BitBoard::sqMask(B1,C1,D1,F1,G1));
if (pos.a1Castle()) tmp |= 1;
if (pos.h1Castle()) tmp |= (1 << 7);
const int wBonus = (castleValue * castleMaskFactor[tmp]) >> 7;
tmp = (int) ((occupied >> 56) & BitBoard::sqMask(B1,C1,D1,F1,G1));
if (pos.a8Castle()) tmp |= 1;
if (pos.h8Castle()) tmp |= (1 << 7);
const int bBonus = (castleValue * castleMaskFactor[tmp]) >> 7;
return wBonus - bBonus;
}
int
Evaluate::pawnBonus(const Position& pos) {
U64 key = pos.pawnZobristHash();
PawnHashData& phd = getPawnHashEntry(pawnHash, key);
if (phd.key != key)
computePawnHashData(pos, phd);
this->phd = &phd;
int score = phd.score;
// Bonus for own king supporting passed pawns
int passedScore = phd.passedBonusW;
const U64 passedPawnsW = phd.passedPawns & pos.pieceTypeBB(Piece::WPAWN);
U64 m = passedPawnsW;
if (m != 0) {
U64 kMask = pos.pieceTypeBB(Piece::WKING);
int ky = Position::getY(pos.getKingSq(true));
if ((m << 8) & kMask)
passedScore += kingPPSupportK[0] * kingPPSupportP[ky-1] / 32;
else if ((m << 16) & kMask)
passedScore += kingPPSupportK[1] * kingPPSupportP[ky-2] / 32;
m = ((m & BitBoard::maskAToGFiles) << 1) | ((m & BitBoard::maskBToHFiles) >> 1);
if (m & kMask)
passedScore += kingPPSupportK[2] * kingPPSupportP[ky-0] / 32;
if ((m << 8) & kMask)
passedScore += kingPPSupportK[3] * kingPPSupportP[ky-1] / 32;
if ((m << 16) & kMask)
passedScore += kingPPSupportK[4] * kingPPSupportP[ky-2] / 32;
// Penalty for opponent pieces blocking passed pawns
U64 ppBlockSquares = passedPawnsW << 8;
if (ppBlockSquares & pos.blackBB()) {
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BKNIGHT)) * ppBlockerBonus[0];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BBISHOP)) * ppBlockerBonus[1];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BROOK)) * ppBlockerBonus[2];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BQUEEN)) * ppBlockerBonus[3];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BKING)) * ppBlockerBonus[4];
}
ppBlockSquares = BitBoard::northFill(passedPawnsW << 16);
if (ppBlockSquares & pos.blackBB()) {
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BKNIGHT)) * ppBlockerBonus[5];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BBISHOP)) * ppBlockerBonus[6];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BROOK)) * ppBlockerBonus[7];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BQUEEN)) * ppBlockerBonus[8];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::BKING)) * ppBlockerBonus[9];
}
// Bonus for rook behind passed pawn
m = BitBoard::southFill(passedPawnsW);
passedScore += RBehindPP1 * BitBoard::bitCount(m & pos.pieceTypeBB(Piece::WROOK));
passedScore -= RBehindPP2 * BitBoard::bitCount(m & pos.pieceTypeBB(Piece::BROOK));
}
score += interpolate(passedScore * passedPawnEGFactor / 32, passedScore, mhd->wPassedPawnIPF);
passedScore = phd.passedBonusB;
const U64 passedPawnsB = phd.passedPawns & pos.pieceTypeBB(Piece::BPAWN);
m = passedPawnsB;
if (m != 0) {
U64 kMask = pos.pieceTypeBB(Piece::BKING);
int ky = Position::getY(pos.getKingSq(false));
if ((m >> 8) & kMask)
passedScore += kingPPSupportK[0] * kingPPSupportP[6-ky] / 32;
else if ((m >> 16) & kMask)
passedScore += kingPPSupportK[1] * kingPPSupportP[5-ky] / 32;
m = ((m & BitBoard::maskAToGFiles) << 1) | ((m & BitBoard::maskBToHFiles) >> 1);
if (m & kMask)
passedScore += kingPPSupportK[2] * kingPPSupportP[7-ky] / 32;
if ((m >> 8) & kMask)
passedScore += kingPPSupportK[3] * kingPPSupportP[6-ky] / 32;
if ((m >> 16) & kMask)
passedScore += kingPPSupportK[4] * kingPPSupportP[5-ky] / 32;
// Penalty for opponent pieces blocking passed pawns
U64 ppBlockSquares = passedPawnsB >> 8;
if (ppBlockSquares & pos.whiteBB()) {
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WKNIGHT)) * ppBlockerBonus[0];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WBISHOP)) * ppBlockerBonus[1];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WROOK)) * ppBlockerBonus[2];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WQUEEN)) * ppBlockerBonus[3];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WKING)) * ppBlockerBonus[4];
}
ppBlockSquares = BitBoard::southFill(passedPawnsB >> 16);
if (ppBlockSquares && pos.whiteBB()) {
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WKNIGHT)) * ppBlockerBonus[5];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WBISHOP)) * ppBlockerBonus[6];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WROOK)) * ppBlockerBonus[7];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WQUEEN)) * ppBlockerBonus[8];
passedScore -= BitBoard::bitCount(ppBlockSquares & pos.pieceTypeBB(Piece::WKING)) * ppBlockerBonus[9];
}
// Bonus for rook behind passed pawn
m = BitBoard::northFill(passedPawnsB);
passedScore += RBehindPP1 * BitBoard::bitCount(m & pos.pieceTypeBB(Piece::BROOK));
passedScore -= RBehindPP2 * BitBoard::bitCount(m & pos.pieceTypeBB(Piece::WROOK));
}
score -= interpolate(passedScore * passedPawnEGFactor / 32, passedScore, mhd->bPassedPawnIPF);
// Passed pawns are more dangerous if enemy king is far away
const int hiMtrl = passedPawnHiMtrl;
m = passedPawnsW;
int bestWPawnDist = 8;
int bestWPromSq = -1;
if (m != 0) {
int mtrlNoPawns = pos.bMtrl() - pos.bMtrlPawns();
if (mtrlNoPawns < hiMtrl) {
int kingPos = pos.getKingSq(false);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
int x = Position::getX(sq);
int y = Position::getY(sq);
int pawnDist = std::min(5, 7 - y);
int kingDist = BitBoard::getKingDistance(kingPos, Position::getSquare(x, 7));
int kScore = kingDist * 4;
if (kingDist > pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist);
score += interpolate(kScore, 0, mhd->wPassedPawnIPF);
if (!pos.isWhiteMove())
kingDist--;
if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) {
if (BitBoard::northFill(1ULL<<sq) & (1LL << pos.getKingSq(true)))
pawnDist++; // Own king blocking pawn
if (pawnDist < bestWPawnDist) {
bestWPawnDist = pawnDist;
bestWPromSq = Position::getSquare(x, 7);
}
}
}
}
}
int bestBPawnDist = 8;
int bestBPromSq = -1;
m = passedPawnsB;
if (m != 0) {
int mtrlNoPawns = pos.wMtrl() - pos.wMtrlPawns();
if (mtrlNoPawns < hiMtrl) {
int kingPos = pos.getKingSq(true);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
int x = Position::getX(sq);
int y = Position::getY(sq);
int pawnDist = std::min(5, y);
int kingDist = BitBoard::getKingDistance(kingPos, Position::getSquare(x, 0));
int kScore = kingDist * 4;
if (kingDist > pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist);
score -= interpolate(kScore, 0, mhd->bPassedPawnIPF);
if (pos.isWhiteMove())
kingDist--;
if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) {
if (BitBoard::southFill(1ULL<<sq) & (1LL << pos.getKingSq(false)))
pawnDist++; // Own king blocking pawn
if (pawnDist < bestBPawnDist) {
bestBPawnDist = pawnDist;
bestBPromSq = Position::getSquare(x, 0);
}
}
}
}
}
// Evaluate pawn races in pawn end games
const int prBonus = pawnRaceBonus;
if (bestWPromSq >= 0) {
if (bestBPromSq >= 0) {
int wPly = bestWPawnDist * 2; if (pos.isWhiteMove()) wPly--;
int bPly = bestBPawnDist * 2; if (!pos.isWhiteMove()) bPly--;
if (wPly < bPly - 1) {
score += prBonus;
} else if (wPly == bPly - 1) {
if (BitBoard::getDirection(bestWPromSq, pos.getKingSq(false)))
score += prBonus;
} else if (wPly == bPly + 1) {
if (BitBoard::getDirection(bestBPromSq, pos.getKingSq(true)))
score -= prBonus;
} else {
score -= prBonus;
}
} else
score += prBonus;
} else if (bestBPromSq >= 0)
score -= prBonus;
return score;
}
template <bool white>
static inline int
evalConnectedPP(int x, int y, U64 ppMask) {
if ((x >= 7) || !(BitBoard::maskFile[x+1] & ppMask))
return 0;
int y2 = 0;
if (white) {
for (int i = 6; i >= 1; i--) {
int sq = Position::getSquare(x+1, i);
if (ppMask & (1ULL << sq)) {
y2 = i;
break;
}
}
} else {
for (int i = 1; i <= 6; i++) {
int sq = Position::getSquare(x+1, i);
if (ppMask & (1ULL << sq)) {
y2 = i;
break;
}
}
}
if (y2 == 0)
return 0;
if (!white) {
y = 7 - y;
y2 = 7 - y2;
}
return connectedPPBonus[(y-1)*6 + (y2-1)];
}
/** Compute subset of squares given by mask that white is in control over, ie
* squares that have at least as many white pawn guards as black has pawn
* attacks on the square. */
static inline U64
wPawnCtrlSquares(U64 mask, U64 wPawns, U64 bPawns) {
U64 wLAtks = (wPawns & BitBoard::maskBToHFiles) << 7;
U64 wRAtks = (wPawns & BitBoard::maskAToGFiles) << 9;
U64 bLAtks = (bPawns & BitBoard::maskBToHFiles) >> 9;
U64 bRAtks = (bPawns & BitBoard::maskAToGFiles) >> 7;
return ((mask & ~bLAtks & ~bRAtks) |
(mask & (bLAtks ^ bRAtks) & (wLAtks | wRAtks)) |
(mask & wLAtks & wRAtks));
}
static inline U64
bPawnCtrlSquares(U64 mask, U64 wPawns, U64 bPawns) {
U64 wLAtks = (wPawns & BitBoard::maskBToHFiles) << 7;
U64 wRAtks = (wPawns & BitBoard::maskAToGFiles) << 9;
U64 bLAtks = (bPawns & BitBoard::maskBToHFiles) >> 9;
U64 bRAtks = (bPawns & BitBoard::maskAToGFiles) >> 7;
return ((mask & ~wLAtks & ~wRAtks) |
(mask & (wLAtks ^ wRAtks) & (bLAtks | bRAtks)) |
(mask & bLAtks & bRAtks));
}
U64
Evaluate::computeStalePawns(const Position& pos) {
const U64 wPawns = pos.pieceTypeBB(Piece::WPAWN);
const U64 bPawns = pos.pieceTypeBB(Piece::BPAWN);
// Compute stale white pawns
U64 wStale;
{
U64 wPawnCtrl = wPawnCtrlSquares(wPawns, wPawns, bPawns);
for (int i = 0; i < 4; i++)
wPawnCtrl |= wPawnCtrlSquares((wPawnCtrl << 8) & ~bPawns, wPawnCtrl, bPawns);
wPawnCtrl &= ~BitBoard::maskRow8;
U64 wPawnCtrlLAtk = (wPawnCtrl & BitBoard::maskBToHFiles) << 7;
U64 wPawnCtrlRAtk = (wPawnCtrl & BitBoard::maskAToGFiles) << 9;
U64 bLAtks = (bPawns & BitBoard::maskBToHFiles) >> 9;
U64 bRAtks = (bPawns & BitBoard::maskAToGFiles) >> 7;
U64 wActive = ((bLAtks ^ bRAtks) |
(bLAtks & bRAtks & (wPawnCtrlLAtk | wPawnCtrlRAtk)));
for (int i = 0; i < 4; i++)
wActive |= (wActive & ~(wPawns | bPawns)) >> 8;
wStale = wPawns & ~wActive;
}
// Compute stale black pawns
U64 bStale;
{
U64 bPawnCtrl = bPawnCtrlSquares(bPawns, wPawns, bPawns);
for (int i = 0; i < 4; i++)
bPawnCtrl |= bPawnCtrlSquares((bPawnCtrl >> 8) & ~wPawns, wPawns, bPawnCtrl);
bPawnCtrl &= ~BitBoard::maskRow1;
U64 bPawnCtrlLAtk = (bPawnCtrl & BitBoard::maskBToHFiles) >> 9;
U64 bPawnCtrlRAtk = (bPawnCtrl & BitBoard::maskAToGFiles) >> 7;
U64 wLAtks = (wPawns & BitBoard::maskBToHFiles) << 7;
U64 wRAtks = (wPawns & BitBoard::maskAToGFiles) << 9;
U64 bActive = ((wLAtks ^ wRAtks) |
(wLAtks & wRAtks & (bPawnCtrlLAtk | bPawnCtrlRAtk)));
for (int i = 0; i < 4; i++)
bActive |= (bActive & ~(wPawns | bPawns)) << 8;
bStale = bPawns & ~bActive;
}
return wStale | bStale;
}
void
Evaluate::computePawnHashData(const Position& pos, PawnHashData& ph) {
int score = 0;
// Evaluate double pawns and pawn islands
const U64 wPawns = pos.pieceTypeBB(Piece::WPAWN);
const U64 wPawnFiles = BitBoard::southFill(wPawns) & 0xff;
const int wIslands = BitBoard::bitCount(((~wPawnFiles) >> 1) & wPawnFiles);
const U64 bPawns = pos.pieceTypeBB(Piece::BPAWN);
const U64 bPawnFiles = BitBoard::southFill(bPawns) & 0xff;
const int bIslands = BitBoard::bitCount(((~bPawnFiles) >> 1) & bPawnFiles);
score -= (wIslands - bIslands) * pawnIslandPenalty;
// Evaluate doubled pawns
const U64 wDoubled = BitBoard::northFill(wPawns << 8) & wPawns;
U64 m = wDoubled;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score -= pawnDoubledPenalty[Position::getX(sq)];
}
const U64 bDoubled = BitBoard::southFill(bPawns >> 8) & bPawns;
m = bDoubled;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score += pawnDoubledPenalty[Position::getX(sq)];
}
// Evaluate isolated pawns
const U64 wIsolated = wPawns & ~BitBoard::northFill(BitBoard::southFill(
((wPawns & BitBoard::maskAToGFiles) << 1) |
((wPawns & BitBoard::maskBToHFiles) >> 1)));
m = wIsolated;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score -= pawnIsolatedPenalty[Position::getX(sq)];
}
const U64 bIsolated = bPawns & ~BitBoard::northFill(BitBoard::southFill(
((bPawns & BitBoard::maskAToGFiles) << 1) |
((bPawns & BitBoard::maskBToHFiles) >> 1)));
m = bIsolated;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score += pawnIsolatedPenalty[Position::getX(sq)];
}
// Evaluate backward pawns, defined as a pawn that guards a friendly pawn,
// can't be guarded by friendly pawns, can advance, but can't advance without
// being captured by an enemy pawn.
const U64 bPawnNoAtks = ~BitBoard::southFill(bPawnAttacks);
const U64 wPawnNoAtks = ~BitBoard::northFill(wPawnAttacks);
ph.outPostsW = bPawnNoAtks & wPawnAttacks;
ph.outPostsB = wPawnNoAtks & bPawnAttacks;
U64 wBackward = wPawns & ~((wPawns | bPawns) >> 8) & (bPawnAttacks >> 8) & wPawnNoAtks;
wBackward &= (((wPawns & BitBoard::maskBToHFiles) >> 9) |
((wPawns & BitBoard::maskAToGFiles) >> 7));
wBackward &= ~BitBoard::northFill(bPawnFiles);
U64 bBackward = bPawns & ~((wPawns | bPawns) << 8) & (wPawnAttacks << 8) & bPawnNoAtks;
bBackward &= (((bPawns & BitBoard::maskBToHFiles) << 7) |
((bPawns & BitBoard::maskAToGFiles) << 9));
bBackward &= ~BitBoard::northFill(wPawnFiles);
score -= (BitBoard::bitCount(wBackward) - BitBoard::bitCount(bBackward)) * pawnBackwardPenalty;
// Evaluate "semi-backward pawns", defined as pawns on 2:nd or 3:rd rank that can advance,
// but the advanced pawn is attacked by an enemy pawn.
U64 wSemiBackward = wPawns & ~((wPawns | bPawns) >> 8) & (bPawnAttacks >> 8);
score -= BitBoard::bitCount(wSemiBackward & BitBoard::maskRow2) * pawnSemiBackwardPenalty1;
score -= BitBoard::bitCount(wSemiBackward & BitBoard::maskRow3) * pawnSemiBackwardPenalty2;
U64 bSemiBackward = bPawns & ~((wPawns | bPawns) << 8) & (wPawnAttacks << 8);
score += BitBoard::bitCount(bSemiBackward & BitBoard::maskRow7) * pawnSemiBackwardPenalty1;
score += BitBoard::bitCount(bSemiBackward & BitBoard::maskRow6) * pawnSemiBackwardPenalty2;
// Evaluate passed pawn bonus, white
U64 passedPawnsW = wPawns & ~BitBoard::southFill(bPawns | bPawnAttacks | (wPawns >> 8));
int passedBonusW = 0;
if (passedPawnsW != 0) {
U64 m = passedPawnsW;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
int x = Position::getX(sq);
int y = Position::getY(sq);
passedBonusW += passedPawnBonusX[x] + passedPawnBonusY[y];
passedBonusW += evalConnectedPP<true>(x, y, passedPawnsW);
}
}
// Evaluate passed pawn bonus, black
U64 passedPawnsB = bPawns & ~BitBoard::northFill(wPawns | wPawnAttacks | (bPawns << 8));
int passedBonusB = 0;
if (passedPawnsB != 0) {
U64 m = passedPawnsB;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
int x = Position::getX(sq);
int y = Position::getY(sq);
passedBonusB += passedPawnBonusX[x] + passedPawnBonusY[7-y];
passedBonusB += evalConnectedPP<false>(x, y, passedPawnsB);
}
}
// Evaluate candidate passed pawn bonus
const U64 wLeftAtks = (wPawns & BitBoard::maskBToHFiles) << 7;
const U64 wRightAtks = (wPawns & BitBoard::maskAToGFiles) << 9;
const U64 bLeftAtks = (bPawns & BitBoard::maskBToHFiles) >> 9;
const U64 bRightAtks = (bPawns & BitBoard::maskAToGFiles) >> 7;
const U64 bBlockSquares = ((bLeftAtks | bRightAtks) & ~(wLeftAtks | wRightAtks)) |
((bLeftAtks & bRightAtks) & ~(wLeftAtks & wRightAtks));
const U64 wCandidates = wPawns & ~BitBoard::southFill(bPawns | (wPawns >> 8) | bBlockSquares) & ~passedPawnsW;
const U64 wBlockSquares = ((wLeftAtks | wRightAtks) & ~(bLeftAtks | bRightAtks)) |
((wLeftAtks & wRightAtks) & ~(bLeftAtks & bRightAtks));
const U64 bCandidates = bPawns & ~BitBoard::northFill(wPawns | (bPawns << 8) | wBlockSquares) & ~passedPawnsB;
{
U64 m = wCandidates;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
int y = Position::getY(sq);
passedBonusW += candidatePassedBonus[y];
}
}
{
U64 m = bCandidates;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
int y = Position::getY(sq);
passedBonusB += candidatePassedBonus[7-y];
}
}
{ // Bonus for pawns protected by pawns
U64 m = wPawnAttacks & wPawns;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score += protectedPawnBonus[63-sq];
}
m = bPawnAttacks & bPawns;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score -= protectedPawnBonus[sq];
}
}
{ // Bonus for pawns attacked by pawns
U64 m = wPawnAttacks & bPawns;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score += attackedPawnBonus[63-sq];
}
m = bPawnAttacks & wPawns;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score -= attackedPawnBonus[sq];
}
}
ph.key = pos.pawnZobristHash();
ph.score = score;
ph.passedBonusW = (S16)passedBonusW;
ph.passedBonusB = (S16)passedBonusB;
ph.passedPawns = passedPawnsW | passedPawnsB;
ph.stalePawns = computeStalePawns(pos) & ~passedPawnsW & ~passedPawnsB;
}
int
Evaluate::rookBonus(const Position& pos) {
int score = 0;
const U64 wPawns = pos.pieceTypeBB(Piece::WPAWN);
const U64 bPawns = pos.pieceTypeBB(Piece::BPAWN);
const U64 occupied = pos.occupiedBB();
U64 m = pos.pieceTypeBB(Piece::WROOK);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
const int x = Position::getX(sq);
if ((wPawns & BitBoard::maskFile[x]) == 0) { // At least half-open file
score += (bPawns & BitBoard::maskFile[x]) == 0 ? (int)rookOpenBonus : (int)rookHalfOpenBonus; // Pierre-Marie Baty -- added type cast
} // Pierre-Marie Baty -- braces fix
U64 atk = BitBoard::rookAttacks(sq, occupied);
wAttacksBB |= atk;
score += rookMobScore[BitBoard::bitCount(atk & ~(pos.whiteBB() | bPawnAttacks))];
if ((atk & bKingZone) != 0)
bKingAttacks += BitBoard::bitCount(atk & bKingZone);
}
U64 r7 = pos.pieceTypeBB(Piece::WROOK) & BitBoard::maskRow7;
if (((r7 & (r7 - 1)) != 0) &&
((pos.pieceTypeBB(Piece::BKING) & BitBoard::maskRow8) != 0))
score += rookDouble7thRowBonus; // Two rooks on 7:th row
m = pos.pieceTypeBB(Piece::BROOK);
while (m != 0) {
int sq = BitBoard::extractSquare(m);
const int x = Position::getX(sq);
if ((bPawns & BitBoard::maskFile[x]) == 0) {
score -= (wPawns & BitBoard::maskFile[x]) == 0 ? (int)rookOpenBonus : (int)rookHalfOpenBonus; // Pierre-Marie Baty -- added type cast
} // Pierre-Marie Baty -- braces fix
U64 atk = BitBoard::rookAttacks(sq, occupied);
bAttacksBB |= atk;
score -= rookMobScore[BitBoard::bitCount(atk & ~(pos.blackBB() | wPawnAttacks))];
if ((atk & wKingZone) != 0)
wKingAttacks += BitBoard::bitCount(atk & wKingZone);
}
r7 = pos.pieceTypeBB(Piece::BROOK) & BitBoard::maskRow2;
if (((r7 & (r7 - 1)) != 0) &&
((pos.pieceTypeBB(Piece::WKING) & BitBoard::maskRow1) != 0))
score -= rookDouble7thRowBonus; // Two rooks on 2:nd row
return score;
}
int
Evaluate::bishopEval(const Position& pos, int oldScore) {
int score = 0;
const U64 occupied = pos.occupiedBB();
const U64 wBishops = pos.pieceTypeBB(Piece::WBISHOP);
const U64 bBishops = pos.pieceTypeBB(Piece::BBISHOP);
if ((wBishops | bBishops) == 0)
return 0;
U64 m = wBishops;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
U64 atk = BitBoard::bishopAttacks(sq, occupied);
wAttacksBB |= atk;
score += bishMobScore[BitBoard::bitCount(atk & ~(pos.whiteBB() | bPawnAttacks))];
if ((atk & bKingZone) != 0)
bKingAttacks += BitBoard::bitCount(atk & bKingZone);
}
m = bBishops;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
U64 atk = BitBoard::bishopAttacks(sq, occupied);
bAttacksBB |= atk;
score -= bishMobScore[BitBoard::bitCount(atk & ~(pos.blackBB() | wPawnAttacks))];
if ((atk & wKingZone) != 0)
wKingAttacks += BitBoard::bitCount(atk & wKingZone);
}
bool whiteDark = (wBishops & BitBoard::maskDarkSq ) != 0;
bool whiteLight = (wBishops & BitBoard::maskLightSq) != 0;
bool blackDark = (bBishops & BitBoard::maskDarkSq ) != 0;
bool blackLight = (bBishops & BitBoard::maskLightSq) != 0;
int numWhite = (whiteDark ? 1 : 0) + (whiteLight ? 1 : 0);
int numBlack = (blackDark ? 1 : 0) + (blackLight ? 1 : 0);
// Bishop pair bonus
if (numWhite == 2) {
int numMinors = BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP, Piece::BKNIGHT));
const int numPawns = BitBoard::bitCount(pos.pieceTypeBB(Piece::WPAWN));
score += bishopPairValue[std::min(numMinors,3)] - numPawns * bishopPairPawnPenalty;
}
if (numBlack == 2) {
int numMinors = BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP, Piece::WKNIGHT));
const int numPawns = BitBoard::bitCount(pos.pieceTypeBB(Piece::BPAWN));
score -= bishopPairValue[std::min(numMinors,3)] - numPawns * bishopPairPawnPenalty;
}
if ((numWhite == 1) && (numBlack == 1) && (whiteDark != blackDark) &&
(pos.wMtrl() - pos.wMtrlPawns() == pos.bMtrl() - pos.bMtrlPawns())) {
const int penalty = (oldScore + score) * oppoBishopPenalty / 128;
score -= interpolate(penalty, 0, mhd->diffColorBishopIPF);
} else {
if (numWhite == 1) {
U64 bishColorMask = whiteDark ? BitBoard::maskDarkSq : BitBoard::maskLightSq;
U64 m = pos.pieceTypeBB(Piece::WPAWN) & bishColorMask;
m |= (m << 8) & pos.pieceTypeBB(Piece::BPAWN);
score -= 2 * BitBoard::bitCount(m);
}
if (numBlack == 1) {
U64 bishColorMask = blackDark ? BitBoard::maskDarkSq : BitBoard::maskLightSq;
U64 m = pos.pieceTypeBB(Piece::BPAWN) & bishColorMask;
m |= (m >> 8) & pos.pieceTypeBB(Piece::WPAWN);
score += 2 * BitBoard::bitCount(m);
}
}
// Penalty for bishop trapped behind pawn at a2/h2/a7/h7
if (((wBishops | bBishops) & BitBoard::sqMask(A2,H2,A7,H7)) != 0) {
const int bTrapped = trappedBishopPenalty;
if ((pos.getPiece(A7) == Piece::WBISHOP) &&
(pos.getPiece(B6) == Piece::BPAWN) &&
(pos.getPiece(C7) == Piece::BPAWN))
score -= bTrapped;
if ((pos.getPiece(H7) == Piece::WBISHOP) &&
(pos.getPiece(G6) == Piece::BPAWN) &&
(pos.getPiece(F7) == Piece::BPAWN))
score -= bTrapped;
if ((pos.getPiece(A2) == Piece::BBISHOP) &&
(pos.getPiece(B3) == Piece::WPAWN) &&
(pos.getPiece(C2) == Piece::WPAWN))
score += bTrapped;
if ((pos.getPiece(H2) == Piece::BBISHOP) &&
(pos.getPiece(G3) == Piece::WPAWN) &&
(pos.getPiece(F2) == Piece::WPAWN))
score += bTrapped;
}
return score;
}
int
Evaluate::knightEval(const Position& pos) {
int score = 0;
U64 wKnights = pos.pieceTypeBB(Piece::WKNIGHT);
U64 bKnights = pos.pieceTypeBB(Piece::BKNIGHT);
if ((wKnights | bKnights) == 0)
return 0;
// Knight mobility
U64 m = wKnights;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
U64 atk = BitBoard::knightAttacks[sq];
wAttacksBB |= atk;
score += knightMobScoreA[sq][BitBoard::bitCount(atk & ~pos.whiteBB() & ~bPawnAttacks)];
}
m = bKnights;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
U64 atk = BitBoard::knightAttacks[sq];
bAttacksBB |= atk;
score -= knightMobScoreA[sq][BitBoard::bitCount(atk & ~pos.blackBB() & ~wPawnAttacks)];
}
m = wKnights & phd->outPostsW;
if (m != 0) {
int outPost = 0;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
outPost += knightOutpostBonus[63-sq];
}
score += interpolate(0, outPost, mhd->wKnightOutPostIPF);
}
m = bKnights & phd->outPostsB;
if (m != 0) {
int outPost = 0;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
outPost += knightOutpostBonus[sq];
}
score -= interpolate(0, outPost, mhd->bKnightOutPostIPF);
}
return score;
}
int
Evaluate::threatBonus(const Position& pos) {
int score = 0;
// Sum values for all black pieces under attack
wAttacksBB &= pos.pieceTypeBB(Piece::BKNIGHT, Piece::BBISHOP, Piece::BROOK, Piece::BQUEEN);
wAttacksBB |= wPawnAttacks;
U64 m = wAttacksBB & pos.blackBB() & ~pos.pieceTypeBB(Piece::BKING);
int tmp = 0;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
tmp += ::pieceValue[pos.getPiece(sq)];
}
score += tmp + tmp * tmp / threatBonus2;
// Sum values for all white pieces under attack
bAttacksBB &= pos.pieceTypeBB(Piece::WKNIGHT, Piece::WBISHOP, Piece::WROOK, Piece::WQUEEN);
bAttacksBB |= bPawnAttacks;
m = bAttacksBB & pos.whiteBB() & ~pos.pieceTypeBB(Piece::WKING);
tmp = 0;
while (m != 0) {
int sq = BitBoard::extractSquare(m);
tmp += ::pieceValue[pos.getPiece(sq)];
}
score -= tmp + tmp * tmp / threatBonus2;
return score / threatBonus1;
}
int
Evaluate::protectBonus(const Position& pos) {
int score = 0;
score += BitBoard::bitCount(pos.pieceTypeBB(Piece::WKNIGHT) & wPawnAttacks) * ::protectBonus[0];
score += BitBoard::bitCount(pos.pieceTypeBB(Piece::WBISHOP) & wPawnAttacks) * ::protectBonus[1];
score += BitBoard::bitCount(pos.pieceTypeBB(Piece::WROOK ) & wPawnAttacks) * ::protectBonus[2];
score += BitBoard::bitCount(pos.pieceTypeBB(Piece::WQUEEN ) & wPawnAttacks) * ::protectBonus[3];
score -= BitBoard::bitCount(pos.pieceTypeBB(Piece::BKNIGHT) & bPawnAttacks) * ::protectBonus[0];
score -= BitBoard::bitCount(pos.pieceTypeBB(Piece::BBISHOP) & bPawnAttacks) * ::protectBonus[1];
score -= BitBoard::bitCount(pos.pieceTypeBB(Piece::BROOK ) & bPawnAttacks) * ::protectBonus[2];
score -= BitBoard::bitCount(pos.pieceTypeBB(Piece::BQUEEN ) & bPawnAttacks) * ::protectBonus[3];
return score;
}
/** Compute king safety for both kings. */
int
Evaluate::kingSafety(const Position& pos) {
const int minM = (rV + bV) * 2;
const int m = pos.wMtrl() - pos.wMtrlPawns() + pos.bMtrl() - pos.bMtrlPawns();
if (m <= minM)
return 0;
const int wKing = pos.getKingSq(true);
const int bKing = pos.getKingSq(false);
int score = kingSafetyKPPart(pos);
if (Position::getY(wKing) == 0) {
if (((pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(F1,G1)) != 0) &&
((pos.pieceTypeBB(Piece::WROOK) & BitBoard::sqMask(G1,H1)) != 0) &&
((pos.pieceTypeBB(Piece::WPAWN) & BitBoard::maskFile[6]) != 0)) {
score -= ((pos.pieceTypeBB(Piece::WPAWN) & BitBoard::maskFile[7]) != 0) ?
(int)trappedRookPenalty1 : (int)trappedRookPenalty2; // Pierre-Marie Baty -- added type cast
} else
if (((pos.pieceTypeBB(Piece::WKING) & BitBoard::sqMask(B1,C1)) != 0) &&
((pos.pieceTypeBB(Piece::WROOK) & BitBoard::sqMask(A1,B1)) != 0) &&
((pos.pieceTypeBB(Piece::WPAWN) & BitBoard::maskFile[1]) != 0)) {
score -= ((pos.pieceTypeBB(Piece::WPAWN) & BitBoard::maskFile[0]) != 0) ?
(int)trappedRookPenalty1 : (int)trappedRookPenalty2; // Pierre-Marie Baty -- added type cast
}
}
if (Position::getY(bKing) == 7) {
if (((pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(F8,G8)) != 0) &&
((pos.pieceTypeBB(Piece::BROOK) & BitBoard::sqMask(G8,H8)) != 0) &&
((pos.pieceTypeBB(Piece::BPAWN) & BitBoard::maskFile[6]) != 0)) {
score += ((pos.pieceTypeBB(Piece::BPAWN) & BitBoard::maskFile[7]) != 0) ?
(int)trappedRookPenalty1 : (int)trappedRookPenalty2; // Pierre-Marie Baty -- added type cast
} else
if (((pos.pieceTypeBB(Piece::BKING) & BitBoard::sqMask(B8,C8)) != 0) &&
((pos.pieceTypeBB(Piece::BROOK) & BitBoard::sqMask(A8,B8)) != 0) &&
((pos.pieceTypeBB(Piece::BPAWN) & BitBoard::maskFile[1]) != 0)) {
score += ((pos.pieceTypeBB(Piece::BPAWN) & BitBoard::maskFile[0]) != 0) ?
(int)trappedRookPenalty1 : (int)trappedRookPenalty2; // Pierre-Marie Baty -- added type cast
}
}
// Bonus for minor pieces protecting king
score += BitBoard::bitCount(Evaluate::knightKingProtectPattern[wKing] & pos.pieceTypeBB(Piece::WKNIGHT)) * knightKingProtectBonus;
score += BitBoard::bitCount(Evaluate::bishopKingProtectPattern[wKing] & pos.pieceTypeBB(Piece::WBISHOP)) * bishopKingProtectBonus;
score -= BitBoard::bitCount(Evaluate::knightKingProtectPattern[bKing] & pos.pieceTypeBB(Piece::BKNIGHT)) * knightKingProtectBonus;
score -= BitBoard::bitCount(Evaluate::bishopKingProtectPattern[bKing] & pos.pieceTypeBB(Piece::BBISHOP)) * bishopKingProtectBonus;
score += kingAttackWeight[std::min(bKingAttacks, 13)] - kingAttackWeight[std::min(wKingAttacks, 13)];
const int kSafety = interpolate(0, score, mhd->kingSafetyIPF);
return kSafety;
}
template <bool white, bool right>
static inline int
evalKingPawnShelter(const Position& pos) {
const int mPawn = white ? Piece::WPAWN : Piece::BPAWN;
const int oPawn = white ? Piece::BPAWN : Piece::WPAWN;
const int yBeg = white ? 1 : 6;
const int yInc = white ? 1 : -1;
const int yEnd = white ? 4 : 3;
const int xBeg = right ? 5 : 2;
const int xInc = right ? 1 : -1;
const int xEnd = right ? 8 : -1;
int idx = 0;
int score = 0;
for (int y = yBeg; y != yEnd; y += yInc) {
for (int x = xBeg; x != xEnd; x += xInc) {
int p = pos.getPiece(Position::getSquare(x, y));
if (p == mPawn)
score += pawnShelterTable[idx];
else if (p == oPawn)
score -= pawnStormTable[idx];
idx++;
}
}
return score;
}
int
Evaluate::kingSafetyKPPart(const Position& pos) {
const U64 key = pos.pawnZobristHash() ^ pos.kingZobristHash();
KingSafetyHashData& ksh = getKingSafetyHashEntry(kingSafetyHash, key);
if (ksh.key != key) {
int score = 0;
const U64 wPawns = pos.pieceTypeBB(Piece::WPAWN);
const U64 bPawns = pos.pieceTypeBB(Piece::BPAWN);
{ // White pawn shelter bonus
int safety = 0;
int halfOpenFiles = 0;
if (Position::getY(pos.wKingSq()) < 2) {
U64 shelter = 1ULL << Position::getX(pos.wKingSq());
shelter |= ((shelter & BitBoard::maskBToHFiles) >> 1) |
((shelter & BitBoard::maskAToGFiles) << 1);
shelter <<= 8;
safety += kingSafetyWeight1 * BitBoard::bitCount(wPawns & shelter);
safety -= kingSafetyWeight2 * BitBoard::bitCount(bPawns & (shelter | (shelter << 8)));
shelter <<= 8;
safety += kingSafetyWeight3 * BitBoard::bitCount(wPawns & shelter);
shelter <<= 8;
safety -= kingSafetyWeight4 * BitBoard::bitCount(bPawns & shelter);
U64 wOpen = BitBoard::southFill(shelter) & (~BitBoard::southFill(wPawns)) & 0xff;
if (wOpen != 0) {
halfOpenFiles += kingSafetyHalfOpenBCDEFG1 * BitBoard::bitCount(wOpen & 0xe7);
halfOpenFiles += kingSafetyHalfOpenAH1 * BitBoard::bitCount(wOpen & 0x18);
}
U64 bOpen = BitBoard::southFill(shelter) & (~BitBoard::southFill(bPawns)) & 0xff;
if (bOpen != 0) {
halfOpenFiles += kingSafetyHalfOpenBCDEFG2 * BitBoard::bitCount(bOpen & 0xe7);
halfOpenFiles += kingSafetyHalfOpenAH2 * BitBoard::bitCount(bOpen & 0x18);
}
const int th = kingSafetyThreshold;
safety = std::min(safety, th);
const int xKing = Position::getX(pos.wKingSq());
if (xKing >= 5)
score += evalKingPawnShelter<true, true>(pos);
else if (xKing <= 2)
score += evalKingPawnShelter<true, false>(pos);
}
const int kSafety = safety - halfOpenFiles;
score += kSafety;
}
{ // Black pawn shelter bonus
int safety = 0;
int halfOpenFiles = 0;
if (Position::getY(pos.bKingSq()) >= 6) {
U64 shelter = 1ULL << (56 + Position::getX(pos.bKingSq()));
shelter |= ((shelter & BitBoard::maskBToHFiles) >> 1) |
((shelter & BitBoard::maskAToGFiles) << 1);
shelter >>= 8;
safety += kingSafetyWeight1 * BitBoard::bitCount(bPawns & shelter);
safety -= kingSafetyWeight2 * BitBoard::bitCount(wPawns & (shelter | (shelter >> 8)));
shelter >>= 8;
safety += kingSafetyWeight3 * BitBoard::bitCount(bPawns & shelter);
shelter >>= 8;
safety -= kingSafetyWeight4 * BitBoard::bitCount(wPawns & shelter);
U64 bOpen = BitBoard::southFill(shelter) & (~BitBoard::southFill(bPawns)) & 0xff;
if (bOpen != 0) {
halfOpenFiles += kingSafetyHalfOpenBCDEFG1 * BitBoard::bitCount(bOpen & 0xe7);
halfOpenFiles += kingSafetyHalfOpenAH1 * BitBoard::bitCount(bOpen & 0x18);
}
U64 wOpen = BitBoard::southFill(shelter) & (~BitBoard::southFill(wPawns)) & 0xff;
if (wOpen != 0) {
halfOpenFiles += kingSafetyHalfOpenBCDEFG2 * BitBoard::bitCount(wOpen & 0xe7);
halfOpenFiles += kingSafetyHalfOpenAH2 * BitBoard::bitCount(wOpen & 0x18);
}
const int th = kingSafetyThreshold;
safety = std::min(safety, th);
const int xKing = Position::getX(pos.bKingSq());
if (xKing >= 5)
score -= evalKingPawnShelter<false, true>(pos);
else if (xKing <= 2)
score -= evalKingPawnShelter<false, false>(pos);
}
const int kSafety = safety - halfOpenFiles;
score -= kSafety;
}
// Pawn storm bonus
static const int kingZone[8] = {0,0,0, 1,1, 2,2,2};
static const U64 pStormMask[3] = { 0x0707070707070707ULL, 0, 0xE0E0E0E0E0E0E0E0ULL };
const int wKingZone = kingZone[Position::getX(pos.wKingSq())];
const int bKingZone = kingZone[Position::getX(pos.bKingSq())];
const int kingDiff = std::abs(wKingZone - bKingZone);
if (kingDiff > 1) {
U64 m = wPawns & pStormMask[bKingZone];
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score += pawnStormBonus * (Position::getY(sq)-5);
}
m = bPawns & pStormMask[wKingZone];
while (m != 0) {
int sq = BitBoard::extractSquare(m);
score += pawnStormBonus * (Position::getY(sq)-2);
}
}
ksh.key = key;
ksh.score = score;
}
return ksh.score;
}
std::shared_ptr<Evaluate::EvalHashTables>
Evaluate::getEvalHashTables() {
return std::make_shared<EvalHashTables>();
}
int
Evaluate::swindleScore(int evalScore) {
int sgn = evalScore >= 0 ? 1 : -1;
int score = std::abs(evalScore) + 4;
int lg = floorLog2(score);
score = (lg - 3) * 4 + (score >> (lg - 2));
return sgn * score;
}