Protector Code
Brought to you by:
raimundheid
--- a +++ b/evaluation.c @@ -0,0 +1,3291 @@ +/* + Protector -- a UCI chess engine + + Copyright (C) 2008 Raimund Heid (Raimund_Heid@yahoo.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/>. + +*/ + +#include <assert.h> +#include <stdlib.h> +#include "position.h" +#include "fen.h" +#include "io.h" + +#define dumpPos dumpPosition(position); + +#define KNIGHT_VS_PASSED_WING_PAWNS +#define BISHOP_BLOCKERS +#define OUTPOST_SQUARE_BONUS +#define PINNED_KNIGHT_BONUS +#define MALUS_PASSED_PAWN_LACK_OF_SUPPORT +#define MALUS_ROOK_BLOCKING_PASSER +#define BONUS_SPACE_ATTACKS +#define MALUS_QUEEN_DEFENDER_DISTANCE +#define MALUS_BLOCKABLE_PASSERS +#define BONUS_HIDDEN_PASSER +#define BONUS_WINNING_PASSER + +#include "evaluation.h" + +/* +#define CALCULATE_TARGETS +#define MALUS_OFFSIDE_KING +#define BONUS_CONNECTED_DISTANT_PASSERS +#define BONUS_EXCHANGE_UP +*/ + +/* -------------------------------------------------------------------------- */ + +#define MAL_STD 100 +#define MAL_WGT 75 +const int MALUS_FACTOR = 105; + +const int PAWN_VALUE_OPENING = 75; +const int PAWN_VALUE_ENDGAME = 90; +const int PAWN_MALUS_DOUBLED_OPENING = (10 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_DOUBLED_ENDGAME = (20 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_ISOLATED_OPENING = (10 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_ISOLATED_ON_OPEN_FILE = (20 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_ISOLATED_ENDGAME = (20 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_ISOLATED_FIXED_OPENING = (6 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_ISOLATED_FIXED_ENDGAME = (6 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_BACKWARD_OPENING = (8 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_BACKWARD_ON_OPEN_FILE = (16 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_BACKWARD_ENDGAME = (10 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_BACKWARD_FIXED_OPENING = (4 * MAL_WGT) / MAL_STD; +const int PAWN_MALUS_BACKWARD_FIXED_ENDGAME = (4 * MAL_WGT) / MAL_STD; + +const int PAWN_CANDIDATE_OPENING_MIN = 5; +const int PAWN_CANDIDATE_OPENING_MAX = 55; +const int PAWN_CANDIDATE_ENDGAME_MIN = 10; +const int PAWN_CANDIDATE_ENDGAME_MAX = 110; + +const int PASSED_PAWN_BONUS_OPENING_MIN = 10; +const int PASSED_PAWN_BONUS_OPENING_MAX = 70; +const int PASSED_PAWN_BONUS_ENDGAME_MIN = 20; +const int PASSED_PAWN_BONUS_ENDGAME_MAX = 140; +const int PASSED_PAWN_BONUS_UNSTOPPABLE = 800; +const int PASSED_PAWN_BONUS_NOT_BLOCKED = 60; +const int PASSED_PAWN_MALUS_BLOCKED = 30; +const int PASSED_PAWN_MALUS_LACK_OF_SUPPORT = 30; +const int PASSED_PAWN_BONUS_CONNECTED_DISTANT = 40; +const int PASSED_PAWN_BONUS_CONNECTED_DISTANT_DUO = 10; +const int PASSED_PAWN_ATTACKERDIST_WEIGHT = 5; +const int PASSED_PAWN_DEFENDERDIST_WEIGHT = 20; + +const int KNIGHT_MOBILITY_THRESHOLD = 4; +const int KNIGHT_MOBILITY_BONUS_OPENING = 4; +const int KNIGHT_MOBILITY_BONUS_ENDGAME = 4; +const int KNIGHT_BONUS_ATTACK = 20; +const int KNIGHT_MALUS_VS_BOTH_WINGS = 10; +const int KNIGHT_MALUS_VS_OUTSIDE_PASSED = 15; + +const int BISHOP_MOBILITY_THRESHOLD = 6; +const int BISHOP_MOBILITY_BONUS_OPENING = 5; +const int BISHOP_MOBILITY_BONUS_ENDGAME = 5; +const int BISHOP_MALUS_BLOCKER[3] = { 0, 8, 20 }; +const int BISHOP_BONUS_ATTACK = 20; +const int BISHOP_MALUS_BLOCKED = 50; +const int BISHOP_MALUS_TRAPPED = 100; +const int BISHOP_BONUS_PINNING_KNIGHT_OPENING = 15; +const int BISHOP_BONUS_PINNING_KNIGHT_ENDGAME = 10; + +const int ROOK_MOBILITY_THRESHOLD = 7; +const int ROOK_MOBILITY_BONUS_OPENING = 2; +const int ROOK_MOBILITY_BONUS_ENDGAME = 4; +const int ROOK_BONUS_ON_SEMIOPEN_FILE = 10; +const int ROOK_BONUS_ON_OPEN_FILE = 20; +const int ROOK_MALUS_BLOCKED = 48; +const int ROOK_MALUS_SQUEEZED = 12; +const int ROOK_BONUS_KING_FILE = 20; +const int ROOK_BONUS_LATERAL_KING_FILE = 10; +const int ROOK_BONUS_ON_SEVENTH_RANK_OPENING = 20; +const int ROOK_BONUS_ON_SEVENTH_RANK_ENDGAME = 40; +const int ROOK_BONUS_ATTACK = 40; +const int ROOK_MALUS_BLOCKING_PASSER = 90; + +const int QUEEN_MOBILITY_THRESHOLD = 13; +const int QUEEN_MOBILITY_BONUS_OPENING = 1; +const int QUEEN_MOBILITY_BONUS_ENDGAME = 2; +const int QUEEN_BONUS_ON_SEVENTH_RANK_OPENING = 10; +const int QUEEN_BONUS_ON_SEVENTH_RANK_ENDGAME = 20; +const int QUEEN_BONUS_ATTACK = 80; + +#ifdef BONUS_SPACE_ATTACKS +const int KING_MALUS_PAWN_ATTACK[8] = { 0, 0, 30, 20, 10, 0, 0, 0 }; +#else +const int KING_MALUS_PAWN_ATTACK[8] = { 0, 0, 60, 30, 10, 0, 0, 0 }; +#endif + +const int KING_ATTACK_WEIGHT[17] = { 0, 0, 128, 192, 224, 240, 248, + 252, 254, 255, 256, 256, 256, 256, 256, 256, 256 +}; +const int KING_MALUS_TRAPPED_OPENING = 20; +const int KING_MALUS_TRAPPED_ENDGAME = 20; + +static const int SPACE_BONUS_WEIGHT[16] = { + 0, 2, 5, 8, 11, 14, 17, 20, 20, 20, 20, 20, 20, 20, 20, 20 +}; + +/* -------------------------------------------------------------------------- */ + +#define PAWN_PHASE 0 +#define KNIGHT_PHASE 1 +#define BISHOP_PHASE 1 +#define ROOK_PHASE 2 +#define QUEEN_PHASE 4 +const int PHASE_MAX = 16 * PAWN_PHASE + 4 * KNIGHT_PHASE + 4 * BISHOP_PHASE + + 4 * ROOK_PHASE + 2 * QUEEN_PHASE; + +const int KnightOutpostBonus[_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, 2, 5, 10, 10, 5, 2, 0, + 0, 2, 5, 10, 10, 5, 2, 0, + 0, 0, 4, 5, 5, 4, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, +}; + +int centerDistance[_64_], centerTaxiDistance[_64_]; +int KNIGHT_OUTPOST_BONUS[2][_64_]; +int attackPoints[16]; +Bitboard leeSquares[_64_], luvSquares[_64_]; +Bitboard abghFiles; +Bitboard weakOutpostSquareCandidates[2]; +Bitboard butterflySquares[_64_]; +Bitboard lateralSquares[_64_]; +Bitboard companionFiles[_64_]; +Bitboard passedPawnRectangle[2][_64_]; +Bitboard passedPawnCorridor[2][_64_]; +Bitboard passedPawnCatcher[2][_64_]; +int passedPawnDefenderMalus[2][2][_64_][_64_]; +Bitboard candidateDefenders[2][_64_]; +Bitboard candidateSupporters[2][_64_]; +Bitboard rookTraps[2]; +Bitboard rookBlockers[_64_]; +Bitboard centralFiles; +Bitboard kingRealm[2][_64_][_64_]; +Bitboard attackingRealm[2]; +KingAttacks kingAttacks[_64_]; +UINT64 numEvals = 0, sumDiff = 0, sumDiffSquare = 0; + +void dumpEvalStats() +{ + logReport("numEvals: %llu sumDiff: %llu sumDiffSquare: %llu", + numEvals, sumDiff, sumDiffSquare); +} + +INLINE static int quad(int y_min, int y_max, int rank) +{ + const int bonusPerRank[8] = { 0, 0, 0, 26, 77, 154, 256, 0 }; + + return y_min + ((y_max - y_min) * bonusPerRank[rank] + 128) / 256; +} + +INLINE static bool oppositeColoredBishops(const Position * position) +{ + if (getPieceCount(position, WHITE_BISHOP) == 1 && + getPieceCount(position, BLACK_BISHOP) == 1) + { + const Bitboard bishops = + position->piecesOfType[WHITE_BISHOP] | + position->piecesOfType[BLACK_BISHOP]; + + return (bool) ((lightSquares & bishops) != EMPTY_BITBOARD && + (darkSquares & bishops) != EMPTY_BITBOARD); + } + else + { + return FALSE; + } +} + +INLINE static bool squareIsPawnSafe(const Position * position, + const Color color, const Square square) +{ + const Bitboard attackers = position->piecesOfType[PAWN | opponent(color)]; + + return (bool) ((attackers & candidateDefenders[color][square]) == + EMPTY_BITBOARD); +} + +INLINE static bool hasAttackingBishop(const Position * position, + const Color attackingColor, + const Square square) +{ + const Bitboard attackers = + ((lightSquares & minValue[square]) != EMPTY_BITBOARD ? + lightSquares : darkSquares); + + return (bool) + ((attackers & position->piecesOfType[BISHOP | attackingColor]) != + EMPTY_BITBOARD); +} + +INLINE static bool pieceIsPinnedByBishop(const Position * position, + const Square pinnedPiece, + const Square pinningPiece, + const Bitboard targets) +{ + return (bool) + ((getDiaSquaresBehind(position, pinnedPiece, pinningPiece) & targets) != + EMPTY_BITBOARD); +} + +INLINE static bool pieceIsPinnedByRook(const Position * position, + const Square pinnedPiece, + const Square pinningPiece, + const Bitboard targets) +{ + return (bool) + ((getOrthoSquaresBehind(position, pinnedPiece, pinningPiece) & targets) + != EMPTY_BITBOARD); +} + +INLINE static void getPawnInfo(const Position * position, + EvaluationBase * base) +{ + Bitboard white = position->piecesOfType[WHITE_PAWN]; + Bitboard black = position->piecesOfType[BLACK_PAWN]; + Bitboard whiteLateralSquares, blackLateralSquares; + Bitboard whiteSwamp, blackSwamp; + register Bitboard tmp1, tmp2; + + /* Calculate upward and downward realms */ + tmp1 = (white << 8) | (white << 16) | (white << 24); + tmp1 |= (tmp1 << 24); + tmp2 = (white >> 8) | (white >> 16) | (white >> 24); + tmp2 |= (tmp2 >> 24); + + base->doubledPawns[WHITE] = white & tmp2; + base->upwardRealm[WHITE] = (tmp1 = tmp1 | white); + base->pawnAttackableSquares[WHITE] = + ((tmp1 & nonA) << 7) | ((tmp1 & nonH) << 9); + base->downwardRealm[WHITE] = tmp2; + + /* Calculate upward and downward realms */ + tmp1 = (black >> 8) | (black >> 16) | (black >> 24); + tmp1 |= (tmp1 >> 24); + tmp2 = (black << 8) | (black << 16) | (black << 24); + tmp2 |= (tmp2 << 24); + + base->doubledPawns[BLACK] = black & tmp2; + base->upwardRealm[BLACK] = (tmp1 = tmp1 | black); + base->pawnAttackableSquares[BLACK] = + ((tmp1 & nonA) >> 9) | ((tmp1 & nonH) >> 7); + base->downwardRealm[BLACK] = tmp2; + + /* Calculate the squares protected by a pawn */ + whiteLateralSquares = ((white & nonA) >> 1) | ((white & nonH) << 1); + base->pawnProtectedSquares[WHITE] = whiteLateralSquares << 8; + blackLateralSquares = ((black & nonA) >> 1) | ((black & nonH) << 1); + base->pawnProtectedSquares[BLACK] = blackLateralSquares >> 8; + + /* Identify the passed pawns */ + whiteSwamp = base->downwardRealm[BLACK] | base->upwardRealm[WHITE] | + base->pawnAttackableSquares[WHITE]; + blackSwamp = base->downwardRealm[WHITE] | base->upwardRealm[BLACK] | + base->pawnAttackableSquares[BLACK]; + + base->passedPawns[WHITE] = white & ~blackSwamp; + base->passedPawns[BLACK] = black & ~whiteSwamp; + + /* Calculate the weak pawns */ + tmp2 = ~(white | black | base->pawnProtectedSquares[BLACK]); + tmp1 = (whiteLateralSquares & tmp2) >> 8; + tmp1 |= (tmp1 & squaresOfRank[RANK_3] & tmp2) >> 8; + base->weakPawns[WHITE] = + (white & ~(base->pawnAttackableSquares[WHITE] | + whiteLateralSquares | tmp1)); + + tmp2 = ~(white | black | base->pawnProtectedSquares[WHITE]); + tmp1 = (blackLateralSquares & tmp2) << 8; + tmp1 |= (tmp1 & squaresOfRank[RANK_6] & tmp2) << 8; + base->weakPawns[BLACK] = + (black & ~(base->pawnAttackableSquares[BLACK] | + blackLateralSquares | tmp1)); + + base->weakOutpostSquares[WHITE] = base->upwardRealm[WHITE] & + ~(base->pawnAttackableSquares[WHITE] | + base->downwardRealm[BLACK] | white | black) & + weakOutpostSquareCandidates[WHITE]; + + base->weakOutpostSquares[BLACK] = base->upwardRealm[BLACK] & + ~(base->pawnAttackableSquares[BLACK] | + base->downwardRealm[WHITE] | white | black) & + weakOutpostSquareCandidates[BLACK]; + + /* Calculate the candidates */ + base->candidatePawns[WHITE] = white & ~base->passedPawns[WHITE] & + (base->pawnAttackableSquares[WHITE] | whiteLateralSquares) & + ~(base->upwardRealm[BLACK] | base->downwardRealm[WHITE]); + + base->candidatePawns[BLACK] = black & ~base->passedPawns[BLACK] & + (base->pawnAttackableSquares[BLACK] | blackLateralSquares) & + ~(base->upwardRealm[WHITE] | base->downwardRealm[BLACK]); + +#ifdef BONUS_HIDDEN_PASSER + /* Calculate the hidden candidates */ + base->hiddenCandidatePawns[WHITE] = white & (black >> 8) & + ~base->pawnAttackableSquares[BLACK] & ~(blackLateralSquares) & + (squaresOfRank[RANK_5] | squaresOfRank[RANK_6]) & + base->pawnProtectedSquares[WHITE]; + + base->hiddenCandidatePawns[BLACK] = black & (white << 8) & + ~base->pawnAttackableSquares[WHITE] & ~(whiteLateralSquares) & + (squaresOfRank[RANK_4] | squaresOfRank[RANK_3]) & + base->pawnProtectedSquares[BLACK]; +#endif + + tmp1 = black & base->pawnProtectedSquares[BLACK]; + tmp2 = ((tmp1 & nonA) >> 9) & ((tmp1 & nonH) >> 7); + tmp2 &= ~base->pawnAttackableSquares[WHITE]; + tmp1 = tmp2 | (tmp2 >> 8); + base->fixedPawns[WHITE] = tmp1 | (tmp1 >> 16) | (tmp1 >> 32); + + tmp1 = white & base->pawnProtectedSquares[WHITE]; + tmp2 = ((tmp1 & nonA) << 7) & ((tmp1 & nonH) << 9); + tmp2 &= ~base->pawnAttackableSquares[BLACK]; + tmp1 = tmp2 | (tmp2 << 8); + base->fixedPawns[BLACK] = tmp1 | (tmp1 << 16) | (tmp1 << 32); + +#ifdef BONUS_HIDDEN_PASSER + base->hasPassersOrCandidates[WHITE] = (bool) + (base->passedPawns[WHITE] != EMPTY_BITBOARD || + base->candidatePawns[WHITE] != EMPTY_BITBOARD || + base->hiddenCandidatePawns[WHITE] != EMPTY_BITBOARD); + + base->hasPassersOrCandidates[BLACK] = (bool) + (base->passedPawns[BLACK] != EMPTY_BITBOARD || + base->candidatePawns[BLACK] != EMPTY_BITBOARD || + base->hiddenCandidatePawns[BLACK] != EMPTY_BITBOARD); +#endif +} + +bool pawnIsPassed(const Position * position, const Square pawnSquare) +{ + const Color pawnColor = pieceColor(position->piece[pawnSquare]); + const Color defenderColor = opponent(pawnColor); + const Bitboard blockers = position->piecesOfType[PAWN | defenderColor] & + (candidateDefenders[pawnColor][pawnSquare] | + passedPawnCorridor[pawnColor][pawnSquare]); + + return (bool) (blockers == EMPTY_BITBOARD); +} + +static int getPassedPawnDefenderMalus(const Color pawnColor, + const Color activeColor, + const Square passedPawnSquare, + const Square defenderSquare) +{ + const int pawnDirection = (pawnColor == WHITE ? 8 : -8); + const Square stopSquare = (Square) (passedPawnSquare + pawnDirection); + const Square rectangleSquare = + (pawnColor == activeColor ? + passedPawnSquare : (Square) (passedPawnSquare - pawnDirection)); + const bool kingInRectangle = + testSquare(passedPawnRectangle[pawnColor][rectangleSquare], + defenderSquare); + const int defenderDistWeight = + (kingInRectangle ? (2 * PASSED_PAWN_DEFENDERDIST_WEIGHT) / 3 : + PASSED_PAWN_DEFENDERDIST_WEIGHT); + + return distance(stopSquare, defenderSquare) * defenderDistWeight; +} + +INLINE bool passerWalks(const Position * position, const Square passerSquare, + const Color passerColor) +{ + const Square attackerKingSquare = position->king[passerColor]; + const Square defenderKingSquare = position->king[opponent(passerColor)]; + const int attackerDistance = distance(attackerKingSquare, passerSquare); + const Rank kingRank = colorRank(passerColor, attackerKingSquare); + const File passerFile = file(passerSquare); + + if (passerFile >= FILE_B && passerFile <= FILE_G) + { + if ((kingRank == RANK_6 || kingRank == RANK_7) && + kingRank > colorRank(passerColor, passerSquare) && + abs(file(attackerKingSquare) - passerFile) <= 1 && + attackerDistance <= 2) + { + if (position->activeColor == passerColor || + attackerDistance == 1 || + distance(defenderKingSquare, passerSquare) > 1) + { + return TRUE; + } + } + + /* + if (kingRank == colorRank(passerColor, passerSquare) + 2 && + abs(file(attackerKingSquare) - passerFile) <= 1 && + attackerDistance <= 2) + { + if (position->activeColor == passerColor || + attackerDistance == 1 || + distance(defenderKingSquare, passerSquare) > 1) + { + return TRUE; + } + } + */ + } + else if ((kingRank == RANK_7 || kingRank == RANK_8) && + abs(file(attackerKingSquare) - passerFile) == 1 && + attackerDistance <= 2) + { + if (position->activeColor == passerColor || + attackerDistance == 1 || + distance(defenderKingSquare, passerSquare) > 1) + { + return TRUE; + } + } + + return FALSE; +} + +INLINE static void evaluatePassedPawns(Position * position, + EvaluationBase * base) +{ + Square square; + Bitboard pieces = base->passedPawns[WHITE] | base->passedPawns[BLACK]; + + ITERATE_BITBOARD(&pieces, square) + { + const Piece currentPiece = position->piece[square]; + const Color pawnColor = pieceColor(currentPiece); + const Color oppColor = opponent(pawnColor); + const Rank pawnRank = colorRank(pawnColor, square); + const int pawnDirection = (pawnColor == WHITE ? 8 : -8); + const Square stopSquare = (Square) (square + pawnDirection); + const int numDefenders = position->numberOfPieces[oppColor] - + position->numberOfPawns[oppColor]; + int delta = PASSED_PAWN_BONUS_ENDGAME_MAX - + PASSED_PAWN_BONUS_ENDGAME_MIN; + bool blocked = TRUE; + const bool hasSupportingBishop = + hasAttackingBishop(position, pawnColor, stopSquare); + const bool oppHasBlockingBishop = + hasAttackingBishop(position, oppColor, stopSquare); + + base->openingPoints[pawnColor] += + quad(PASSED_PAWN_BONUS_OPENING_MIN, PASSED_PAWN_BONUS_OPENING_MAX, + pawnRank); + + if (numDefenders == 1) + { + const int kingDistance = distance(square, position->king[pawnColor]); + const Square rectangleSquare = + (pawnColor == position->activeColor ? + square : (Square) (square - pawnDirection)); + const bool kingInRectangle = + testSquare(passedPawnRectangle[pawnColor][rectangleSquare], + position->king[oppColor]); + + if ((kingInRectangle == FALSE && + (passedPawnCorridor[pawnColor][square] & + position->piecesOfColor[pawnColor]) == EMPTY_BITBOARD)) + { + delta += PASSED_PAWN_BONUS_UNSTOPPABLE; + } + else if (kingDistance == 1) + { + const File pawnFile = file(square); + const File kingFile = file(position->king[pawnColor]); + const Square promotionSquare = + (pawnColor == WHITE ? getSquare(pawnFile, RANK_8) : + getSquare(pawnFile, RANK_1)); + const bool clearPath = (bool) + (kingFile != pawnFile || + (kingFile != FILE_A && kingFile != FILE_H)); + + if (clearPath && + distance(promotionSquare, position->king[pawnColor]) <= 1) + { + delta += PASSED_PAWN_BONUS_UNSTOPPABLE; + } + } + +#ifdef BONUS_WINNING_PASSER + if (delta < PASSED_PAWN_BONUS_UNSTOPPABLE && + base->hasPassersOrCandidates[oppColor] == FALSE && + passerWalks(position, square, pawnColor)) + { + delta += PASSED_PAWN_BONUS_UNSTOPPABLE; + } +#endif + } + + if (position->piece[stopSquare] == NO_PIECE && + delta < PASSED_PAWN_BONUS_UNSTOPPABLE) + { + const Piece newPiece = (pawnRank == RANK_7 ? WHITE_QUEEN : NO_PIECE); + const Move move = getPackedMove(square, stopSquare, + newPiece); + int exValue = seeMove(position, move); + +#ifndef NDEBUG + { + Move flippedMove = getPackedMove(getFlippedSquare(square), + getFlippedSquare + (stopSquare), newPiece); + Position flippedPosition = *position; + + flipPosition(&flippedPosition); + initializePosition(&flippedPosition); + + if (seeMove(&flippedPosition, flippedMove) != exValue) + { + exValue = -100; + } + } +#endif + + if (exValue >= 0) + { + delta += PASSED_PAWN_BONUS_NOT_BLOCKED; + blocked = FALSE; + } + } + + delta -= + distance(stopSquare, position->king[pawnColor]) * + PASSED_PAWN_ATTACKERDIST_WEIGHT; + delta += + passedPawnDefenderMalus[pawnColor] + [position->activeColor][square][position->king[oppColor]]; + + base->endgamePoints[pawnColor] += PASSED_PAWN_BONUS_ENDGAME_MIN; + +#ifdef MALUS_PASSED_PAWN_LACK_OF_SUPPORT + if (numberOfNonPawnPieces(position, pawnColor) <= + numberOfNonPawnPieces(position, oppColor) && + ((hasOrthoPieces(position, oppColor) && + hasOrthoPieces(position, pawnColor) == FALSE) || + (numberOfNonPawnPieces(position, pawnColor) < + numberOfNonPawnPieces(position, oppColor)))) + { + delta -= 10; + } +#endif + +#ifdef BONUS_CONNECTED_DISTANT_PASSERS + if (testSquare(passedPawnRectangle[pawnColor][square], + position->king[oppColor]) == FALSE) + { + const File pawnFile = file(square); + const int fileDiff = pawnFile - file(position->king[oppColor]); + const Bitboard dangerousCompanionFiles = + (fileDiff > 0 ? squaresOfFileRange[pawnFile][FILE_H] : + squaresOfFileRange[FILE_A][pawnFile]); + const Bitboard dangerousCompanions = + base->passedPawns[pawnColor] & + butterflySquares[square] & dangerousCompanionFiles; + + if (dangerousCompanions != EMPTY_BITBOARD) + { + delta += PASSED_PAWN_BONUS_CONNECTED_DISTANT; + } + } +#endif + +#ifdef MALUS_BLOCKABLE_PASSERS + if (numberOfNonPawnPieces(position, pawnColor) <= + numberOfNonPawnPieces(position, oppColor) && oppHasBlockingBishop) + { + if (hasSupportingBishop) + { + delta -= 5; + } + else + { + delta -= 20; + } + } + + if (blocked && + numberOfNonPawnPieces(position, pawnColor) <= + numberOfNonPawnPieces(position, oppColor) && + getPieceCount(position, (Piece) (BISHOP | pawnColor)) > 0 && + hasSupportingBishop == FALSE) + { + delta -= 10; + } + + if ((base->passedPawns[pawnColor] & butterflySquares[square]) != + EMPTY_BITBOARD) + { + const int attackerDist = distance(position->king[pawnColor], square); + const int defenderDist = distance(position->king[oppColor], square); + + delta += max(0, 5 * (defenderDist - attackerDist)); + + if (oppHasBlockingBishop) + { + if (hasSupportingBishop) + { + delta += 10; + } + else + if (getPieceCount(position, (Piece) (KNIGHT | pawnColor)) > 0) + { + delta += 5; + } + } + else + { + delta += 20; + } + } +#endif + + if (delta > 0) + { + base->endgamePoints[pawnColor] += quad(0, delta, pawnRank); + } + } +} + +INLINE static void evaluatePawns(const Position * position, + EvaluationBase * base) +{ + int numWeaknesses[2]; + Square square; + Bitboard pieces = (base->weakPawns[WHITE] | base->weakPawns[BLACK]); + const int whiteDoubles = getNumberOfSetSquares(base->doubledPawns[WHITE]); + const int blackDoubles = getNumberOfSetSquares(base->doubledPawns[BLACK]); + Color color; + + numWeaknesses[WHITE] = whiteDoubles; + numWeaknesses[BLACK] = blackDoubles; + base->openingPoints[WHITE] -= whiteDoubles * PAWN_MALUS_DOUBLED_OPENING; + base->endgamePoints[WHITE] -= whiteDoubles * PAWN_MALUS_DOUBLED_ENDGAME; + base->openingPoints[BLACK] -= blackDoubles * PAWN_MALUS_DOUBLED_OPENING; + base->endgamePoints[BLACK] -= blackDoubles * PAWN_MALUS_DOUBLED_ENDGAME; + + ITERATE_BITBOARD(&pieces, square) + { + const Color pawnColor = pieceColor(position->piece[square]); + const Color oppColor = opponent(pawnColor); + const bool isolated = (bool) + ((companionFiles[square] & + position->piecesOfType[position->piece[square]]) == + EMPTY_BITBOARD); + const bool > + (testSquare(base->upwardRealm[oppColor], square) == FALSE && + testSquare(base->doubledPawns[pawnColor], square) == FALSE); + + numWeaknesses[pawnColor]++; + + if (isolated) + { + if (onOpenFile) + { + base->openingPoints[pawnColor] -= + PAWN_MALUS_ISOLATED_ON_OPEN_FILE; + } + else + { + base->openingPoints[pawnColor] -= PAWN_MALUS_ISOLATED_OPENING; + } + + base->endgamePoints[pawnColor] -= PAWN_MALUS_ISOLATED_ENDGAME; + + if (testSquare(base->fixedPawns[pawnColor], square)) + { + base->openingPoints[pawnColor] -= + PAWN_MALUS_ISOLATED_FIXED_OPENING; + base->endgamePoints[pawnColor] -= + PAWN_MALUS_ISOLATED_FIXED_ENDGAME; + } + } + else /* backward */ + { + if (onOpenFile) + { + base->openingPoints[pawnColor] -= + PAWN_MALUS_BACKWARD_ON_OPEN_FILE; + } + else + { + base->openingPoints[pawnColor] -= PAWN_MALUS_BACKWARD_OPENING; + } + + base->endgamePoints[pawnColor] -= PAWN_MALUS_BACKWARD_ENDGAME; + + if (testSquare(base->fixedPawns[pawnColor], square)) + { + base->openingPoints[pawnColor] -= + PAWN_MALUS_BACKWARD_FIXED_OPENING; + base->endgamePoints[pawnColor] -= + PAWN_MALUS_BACKWARD_FIXED_ENDGAME; + } + } + } + + for (color = WHITE; color <= BLACK; color++) + { + int weight = 100, i; + + for (i = 0; i < numWeaknesses[color]; i++) + { + weight = (weight * MALUS_FACTOR) / 100; + } + + base->openingPoints[color] = + (weight * base->openingPoints[color]) / 100; + base->endgamePoints[color] = + (weight * base->endgamePoints[color]) / 100; + } + + pieces = (base->candidatePawns[WHITE] | base->candidatePawns[BLACK]); + + ITERATE_BITBOARD(&pieces, square) + { + const Color pawnColor = pieceColor(position->piece[square]); + const Bitboard supporters = candidateSupporters[pawnColor][square] & + position->piecesOfType[PAWN | pawnColor]; + const Bitboard defenders = candidateDefenders[pawnColor][square] & + position->piecesOfType[PAWN | opponent(pawnColor)]; + + if (getNumberOfSetSquares(supporters) >= + getNumberOfSetSquares(defenders)) + { + const Bitboard ownDefenders = + generalMoves[PAWN | opponent(pawnColor)][square] & + position->piecesOfType[PAWN | pawnColor]; + const Bitboard attackers = + generalMoves[PAWN | pawnColor][square] & + position->piecesOfType[PAWN | opponent(pawnColor)]; + + if (getNumberOfSetSquares(ownDefenders) >= + getNumberOfSetSquares(attackers)) + { + const Rank pawnRank = colorRank(pawnColor, square); + + base->openingPoints[pawnColor] += + quad(PAWN_CANDIDATE_OPENING_MIN, + PAWN_CANDIDATE_OPENING_MAX, pawnRank); + + base->endgamePoints[pawnColor] += + quad(PAWN_CANDIDATE_ENDGAME_MIN, + PAWN_CANDIDATE_ENDGAME_MAX, pawnRank); + } + } + } +} + +INLINE bool isBlockingSquare(const Position * position, const Square square, + const Color blockingColor) +{ + const Square blockedSquare = (Square) + (blockingColor == WHITE ? square + 8 : square - 8); + const Piece blockedPawn = (Piece) (PAWN | opponent(blockingColor)); + + return (bool) (position->piece[blockedSquare] == blockedPawn && + squareIsPawnSafe(position, blockingColor, square)); +} + +INLINE int pawnDefenderDistance(const Square defenderSquare, + const Color defenderColor, + const Square pawnSquare) +{ + const int defenderRank = rank(defenderSquare); + const int pawnRank = rank(pawnSquare); + + if (defenderColor == WHITE) + { + return (defenderRank <= pawnRank ? + abs(file(defenderSquare) - file(pawnSquare)) : + distance(defenderSquare, pawnSquare)); + } + else + { + return (defenderRank >= pawnRank ? + abs(file(defenderSquare) - file(pawnSquare)) : + distance(defenderSquare, pawnSquare)); + } +} + +INLINE static void evaluateKnight(const Position * position, + EvaluationBase * base, const Square square) +{ + const Piece piece = position->piece[square]; + const Color color = pieceColor(piece); + const Color oppColor = opponent(color); + const Bitboard moves = getKnightMoves(square); + const int mobilityCount = + getNumberOfSetSquares(moves & base->countedSquares[color]) - + KNIGHT_MOBILITY_THRESHOLD; + +#ifdef OUTPOST_SQUARE_BONUS + const Bitboard outpostAttacks = moves & base->weakOutpostSquares[oppColor]; +#endif + +#ifdef CALCULATE_TARGETS + base->pieceAttacks[color] |= moves; +#endif + + base->openingPoints[color] += mobilityCount * + KNIGHT_MOBILITY_BONUS_OPENING; + base->endgamePoints[color] += mobilityCount * + KNIGHT_MOBILITY_BONUS_ENDGAME; + + if (base->evaluateKingSafety[oppColor] && + (moves & base->kingAttackSquares[oppColor]) != EMPTY_BITBOARD) + { + base->numAttackers[color]++; + base->attackPoints[color] += KNIGHT_BONUS_ATTACK; + } + + if (KNIGHT_OUTPOST_BONUS[color][square] > 0 && + testSquare(base->pawnProtectedSquares[color], square)) + { + const Bitboard protectors = + generalMoves[PAWN | oppColor][square] & + position->piecesOfType[PAWN | color]; + + base->openingPoints[color] += getNumberOfSetSquares(protectors) * + KNIGHT_OUTPOST_BONUS[color][square]; + } + +#ifdef KNIGHT_VS_PASSED_WING_PAWNS + if (base->passedPawns[oppColor] != EMPTY_BITBOARD) + { + const int malus[8] = { 12, 8, 5, 3, 3, 5, 8, 12 }; + Bitboard passers = base->passedPawns[oppColor]; + Square pawnSquare; + + ITERATE_BITBOARD(&passers, pawnSquare) + { + base->endgamePoints[color] -= malus[file(pawnSquare)] + + pawnDefenderDistance(position->king[color], color, pawnSquare); + } + } +#endif + +#ifdef OUTPOST_SQUARE_BONUS + if (outpostAttacks != EMPTY_BITBOARD) + { + const int count = getNumberOfSetSquares(outpostAttacks); + + base->openingPoints[color] += count * KNIGHT_MOBILITY_BONUS_OPENING; + } + else if (testSquare(base->weakOutpostSquares[oppColor], square)) + { + base->openingPoints[color] += KNIGHT_MOBILITY_BONUS_OPENING; + } +#endif +} + +INLINE static void evaluateBishop(const Position * position, + EvaluationBase * base, const Square square) +{ + const Piece piece = position->piece[square]; + const Color color = pieceColor(piece); + const Color oppColor = opponent(color); + const Bitboard moves = getMagicBishopMoves(square, position->allPieces); + const int mobilityCount = + getNumberOfSetSquares(moves & base->countedSquares[color]) - + BISHOP_MOBILITY_THRESHOLD; +#ifdef PINNED_KNIGHT_BONUS + Bitboard pinnedKnights = moves & position->piecesOfType[oppColor | KNIGHT]; +#endif + +#ifdef OUTPOST_SQUARE_BONUS + const Bitboard outpostAttacks = moves & base->weakOutpostSquares[oppColor]; +#endif + +#ifdef BISHOP_BLOCKERS + const Bitboard blockers = squaresAbove[color][square] & moves & + (position->piecesOfType[PAWN | color] | + (position->piecesOfType[PAWN | opponent(color)] & + base->pawnProtectedSquares[opponent(color)])); +#endif + +#ifdef CALCULATE_TARGETS + base->pieceAttacks[color] |= moves; +#endif + + base->openingPoints[color] += mobilityCount * + BISHOP_MOBILITY_BONUS_OPENING; + base->endgamePoints[color] += mobilityCount * + BISHOP_MOBILITY_BONUS_ENDGAME; + + if (base->evaluateKingSafety[oppColor] && + testSquare(base->kingAttacks[oppColor]->diaAttackers, square)) + { + const Bitboard xrayPieces = position->piecesOfType[QUEEN | color]; + const Bitboard xrayMoves = + getMagicBishopMoves(square, position->allPieces & ~xrayPieces); + + if ((xrayMoves & base->kingAttackSquares[oppColor]) != EMPTY_BITBOARD) + { + base->numAttackers[color]++; + base->attackPoints[color] += BISHOP_BONUS_ATTACK; + } + } + +#ifdef BISHOP_BLOCKERS + if (blockers != EMPTY_BITBOARD) + { + const int numBlockers = getNumberOfSetSquares(blockers); + + assert(numBlockers >= 1 && numBlockers <= 2); + base->openingPoints[color] -= BISHOP_MALUS_BLOCKER[numBlockers]; + base->endgamePoints[color] -= BISHOP_MALUS_BLOCKER[numBlockers]; + } +#endif + +#ifdef OUTPOST_SQUARE_BONUS + if (outpostAttacks != EMPTY_BITBOARD) + { + const int count = getNumberOfSetSquares(outpostAttacks); + + base->openingPoints[color] += count * BISHOP_MOBILITY_BONUS_OPENING; + } + else if (testSquare(base->weakOutpostSquares[oppColor], square)) + { + base->openingPoints[color] += BISHOP_MOBILITY_BONUS_OPENING; + } +#endif + +#ifdef PINNED_KNIGHT_BONUS + if (pinnedKnights != EMPTY_BITBOARD) + { + Square knightSquare; + const Bitboard targets = position->piecesOfType[oppColor | QUEEN] | + position->piecesOfType[oppColor | ROOK] | + minValue[position->king[oppColor]]; + + ITERATE_BITBOARD(&pinnedKnights, knightSquare) + { + if (squareIsPawnSafe(position, color, knightSquare) && + pieceIsPinnedByBishop(position, knightSquare, square, targets)) + { + base->openingPoints[color] += BISHOP_BONUS_PINNING_KNIGHT_OPENING; + base->endgamePoints[color] += BISHOP_BONUS_PINNING_KNIGHT_ENDGAME; + } + } + } +#endif +} + +INLINE static void evaluateWhiteTrappedBishops(const Position * position, + EvaluationBase * base) +{ + if ((position->piece[A7] == WHITE_BISHOP && + position->piece[B6] == BLACK_PAWN) || + (position->piece[B8] == WHITE_BISHOP && + position->piece[C7] == BLACK_PAWN) || + (position->piece[H7] == WHITE_BISHOP && + position->piece[G6] == BLACK_PAWN) || + (position->piece[G8] == WHITE_BISHOP && + position->piece[F7] == BLACK_PAWN)) + { + base->openingPoints[WHITE] -= BISHOP_MALUS_TRAPPED; + base->endgamePoints[WHITE] -= BISHOP_MALUS_TRAPPED; + } + + if ((position->piece[A6] == WHITE_BISHOP && + position->piece[B5] == BLACK_PAWN) || + (position->piece[H6] == WHITE_BISHOP && + position->piece[G5] == BLACK_PAWN)) + { + base->openingPoints[WHITE] -= BISHOP_MALUS_TRAPPED / 2; + base->endgamePoints[WHITE] -= BISHOP_MALUS_TRAPPED / 2; + } + + if ((position->piece[C1] == WHITE_BISHOP && + position->piece[D2] == WHITE_PAWN && + position->piece[D3] != NO_PIECE) || + (position->piece[F1] == WHITE_BISHOP && + position->piece[E2] == WHITE_PAWN && position->piece[E3] != NO_PIECE)) + { + base->openingPoints[WHITE] -= BISHOP_MALUS_BLOCKED; + base->endgamePoints[WHITE] -= BISHOP_MALUS_BLOCKED; + } +} + +INLINE static void evaluateBlackTrappedBishops(const Position * position, + EvaluationBase * base) +{ + if ((position->piece[A2] == BLACK_BISHOP && + position->piece[B3] == WHITE_PAWN) || + (position->piece[B1] == BLACK_BISHOP && + position->piece[C2] == WHITE_PAWN) || + (position->piece[H2] == BLACK_BISHOP && + position->piece[G3] == WHITE_PAWN) || + (position->piece[G1] == BLACK_BISHOP && + position->piece[F2] == WHITE_PAWN)) + { + base->openingPoints[BLACK] -= BISHOP_MALUS_TRAPPED; + base->endgamePoints[BLACK] -= BISHOP_MALUS_TRAPPED; + } + + if ((position->piece[A3] == BLACK_BISHOP && + position->piece[B4] == WHITE_PAWN) || + (position->piece[H3] == BLACK_BISHOP && + position->piece[G4] == WHITE_PAWN)) + { + base->openingPoints[BLACK] -= BISHOP_MALUS_TRAPPED / 2; + base->endgamePoints[BLACK] -= BISHOP_MALUS_TRAPPED / 2; + } + + if ((position->piece[C8] == BLACK_BISHOP && + position->piece[D7] == BLACK_PAWN && + position->piece[D6] != NO_PIECE) || + (position->piece[F8] == BLACK_BISHOP && + position->piece[E7] == BLACK_PAWN && position->piece[E6] != NO_PIECE)) + { + base->openingPoints[BLACK] -= BISHOP_MALUS_BLOCKED; + base->endgamePoints[BLACK] -= BISHOP_MALUS_BLOCKED; + } +} + +INLINE static void evaluateRook(const Position * position, + EvaluationBase * base, const Square square) +{ + const Piece piece = position->piece[square]; + const Color color = pieceColor(piece); + const Color oppColor = opponent(color); + const Bitboard seventhRank = + (color == WHITE ? squaresOfRank[RANK_7] : squaresOfRank[RANK_2]); + const Bitboard moves = getMagicRookMoves(square, position->allPieces); + const int mobilityCount = + getNumberOfSetSquares(moves & base->countedSquares[color]) - + ROOK_MOBILITY_THRESHOLD; + const Bitboard fileSquares = squaresOfFile[file(square)]; + const Bitboard ownPawns = position->piecesOfType[PAWN | color]; + +#ifdef OUTPOST_SQUARE_BONUS + const Bitboard outpostAttacks = moves & base->weakOutpostSquares[oppColor]; +#endif + +#ifdef CALCULATE_TARGETS + base->pieceAttacks[color] |= moves; +#endif + + base->openingPoints[color] += mobilityCount * ROOK_MOBILITY_BONUS_OPENING; + base->endgamePoints[color] += mobilityCount * ROOK_MOBILITY_BONUS_ENDGAME; + + /* Add a bonus if this rook is located on an open file. */ + if ((ownPawns & fileSquares) == EMPTY_BITBOARD) + { + const int fileDiff = abs(file(square) - file(position->king[oppColor])); + + if ((position->piecesOfType[PAWN | oppColor] & fileSquares) == + EMPTY_BITBOARD) + { + base->openingPoints[color] += ROOK_BONUS_ON_OPEN_FILE; + base->endgamePoints[color] += ROOK_BONUS_ON_OPEN_FILE; + } + else + { + base->openingPoints[color] += ROOK_BONUS_ON_SEMIOPEN_FILE; + base->endgamePoints[color] += ROOK_BONUS_ON_SEMIOPEN_FILE; + } + + if (fileDiff == 0 && base->evaluateKingSafety[oppColor]) + { + base->openingPoints[color] += ROOK_BONUS_KING_FILE; + } + + if (fileDiff == 1 && base->evaluateKingSafety[oppColor]) + { + base->openingPoints[color] += ROOK_BONUS_LATERAL_KING_FILE; + } + } + + if (testSquare(rookTraps[color], square) && + (moves & centralFiles) == EMPTY_BITBOARD) + { + const Bitboard legalRankMoves = moves & base->countedSquares[color] & + squaresOfRank[rank(square)]; + + if (getNumberOfSetSquares(legalRankMoves) <= 1) + { + const Bitboard blockers = minValue[position->king[color]] | + position->piecesOfType[PAWN | color]; + const int malus = + ((blockers & rookBlockers[square]) != EMPTY_BITBOARD ? + ROOK_MALUS_BLOCKED : ROOK_MALUS_SQUEEZED); + + base->openingPoints[color] -= malus; + base->endgamePoints[color] -= malus; + } + } + + if (testSquare(seventhRank, square) && + ((seventhRank & position->piecesOfType[PAWN | oppColor]) || + (rank(position->king[oppColor]) == rank(square) + colorSign[color]))) + { + base->openingPoints[color] += ROOK_BONUS_ON_SEVENTH_RANK_OPENING; + base->endgamePoints[color] += ROOK_BONUS_ON_SEVENTH_RANK_ENDGAME; + } + + if (base->evaluateKingSafety[oppColor] && + testSquare(base->kingAttacks[oppColor]->orthoAttackers, square)) + { + const Bitboard xrayPieces = position->piecesOfType[QUEEN | color] | + position->piecesOfType[piece]; + const Bitboard xrayMoves = + getMagicRookMoves(square, position->allPieces & ~xrayPieces); + + if ((xrayMoves & base->kingAttackSquares[oppColor]) != EMPTY_BITBOARD) + { + base->numAttackers[color]++; + base->attackPoints[color] += ROOK_BONUS_ATTACK; + } + } + +#ifdef OUTPOST_SQUARE_BONUS + if ((outpostAttacks & fileSquares) != EMPTY_BITBOARD) + { + const int count = getNumberOfSetSquares(outpostAttacks); + + base->openingPoints[color] += count * ROOK_MOBILITY_BONUS_OPENING; + } + else if (testSquare(base->weakOutpostSquares[oppColor], square)) + { + base->openingPoints[color] += ROOK_MOBILITY_BONUS_OPENING; + } +#endif + +#ifdef MALUS_ROOK_BLOCKING_PASSER + if (numberOfNonPawnPieces(position, color) == 2 && + colorRank(color, square) == RANK_8 && + testSquare(base->passedPawns[color], + (Square) downward(color, square)) && + (fileSquares & squaresBelow[color][square] & + position->piecesOfType[ROOK | oppColor]) != EMPTY_BITBOARD && + (companionFiles[square] & + (position->piecesOfType[WHITE_PAWN] | + position->piecesOfType[BLACK_PAWN])) == EMPTY_BITBOARD) + { + base->endgamePoints[color] -= ROOK_MALUS_BLOCKING_PASSER; + } +#endif + +#ifdef BONUS_SPACE_ATTACKS + if (testSquare(attackingRealm[color], square)) + { + base->spaceAttackPoints[color]++; + } +#endif +} + +INLINE static void evaluateQueen(const Position * position, + EvaluationBase * base, const Square square) +{ + const Piece piece = position->piece[square]; + const Color color = pieceColor(piece); + const Color oppColor = opponent(color); + const Bitboard seventhRank = + (color == WHITE ? squaresOfRank[RANK_7] : squaresOfRank[RANK_2]); + const int mobilityCount = 10 - + taxiDistance(square, position->king[oppColor]); + +#ifdef OUTPOST_SQUARE_BONUS + const Bitboard moves = getMagicQueenMoves(square, position->allPieces); + const Bitboard outpostAttacks = moves & base->weakOutpostSquares[oppColor]; +#endif + +#ifdef CALCULATE_TARGETS + base->pieceAttacks[color] |= moves; +#endif + + base->openingPoints[color] += mobilityCount; + base->endgamePoints[color] += mobilityCount; + + if (testSquare(seventhRank, square) && + ((seventhRank & position->piecesOfType[PAWN | oppColor]) || + (rank(position->king[oppColor]) == rank(square) + colorSign[color]))) + { + base->openingPoints[color] += QUEEN_BONUS_ON_SEVENTH_RANK_OPENING; + base->endgamePoints[color] += QUEEN_BONUS_ON_SEVENTH_RANK_ENDGAME; + } + + if (base->evaluateKingSafety[oppColor] && + (moves & base->kingAttackSquares[oppColor]) != EMPTY_BITBOARD) + { + base->numAttackers[color]++; + base->attackPoints[color] += QUEEN_BONUS_ATTACK; + } + +#ifdef OUTPOST_SQUARE_BONUS + if (outpostAttacks != EMPTY_BITBOARD) + { + const int count = getNumberOfSetSquares(outpostAttacks); + + base->openingPoints[color] += count; + } +#endif + +#ifdef BONUS_SPACE_ATTACKS + if (testSquare(attackingRealm[color], square)) + { + base->spaceAttackPoints[color] += 3; + } +#endif +} + +INLINE static int getSafetyMalusOfKingFile(const Position * position, + const int file, + const Square kingSquare, + const Color color) +{ + const Square startSquare = getSquare(file, rank(kingSquare)); + const Square promotionSquare = (color == WHITE ? + getSquare(file, RANK_8) : + getSquare(file, RANK_1)); + Bitboard candidates = (squaresBetween[startSquare][promotionSquare] | + (minValue[startSquare])) & + position->piecesOfType[PAWN | color]; + int promotionDistance = 0, pawnSquare; + + ITERATE_BITBOARD(&candidates, pawnSquare) + { + if (distance(pawnSquare, promotionSquare) > promotionDistance) + { + promotionDistance = distance(pawnSquare, promotionSquare); + } + } + + assert(promotionDistance >= 0 && promotionDistance <= 6); + + return 36 - promotionDistance * promotionDistance; +} + +INLINE static int getPawnAttackMalusOfKingFile(const Position * position, + const int file, + const Color color) +{ + const Color oppColor = opponent(color); + const Square promotionSquare = (color == WHITE ? + getSquare(file, RANK_1) : + getSquare(file, RANK_8)); + Bitboard candidates = squaresOfFile[file] & + position->piecesOfType[PAWN | oppColor]; + int promotionDistance = 7, pawnSquare; + + ITERATE_BITBOARD(&candidates, pawnSquare) + { + if (distance(pawnSquare, promotionSquare) < promotionDistance) + { + promotionDistance = distance(pawnSquare, promotionSquare); + } + } + + assert(promotionDistance >= 1 && promotionDistance <= 7); + + return KING_MALUS_PAWN_ATTACK[promotionDistance]; +} + +INLINE static int getExpositionValue(const Position * position, + const Square kingSquare, + const Color color) +{ + const Bitboard obstacles = position->piecesOfType[PAWN | color]; + const Bitboard diaExposition = + getMagicBishopMoves(kingSquare, obstacles) & ~obstacles; + const int diaExpositionValue = + 6 - min(6, getNumberOfSetSquares(diaExposition)); + + return 36 - diaExpositionValue * diaExpositionValue; +} + +INLINE static int getSafetyMalusOfKingSquare(const Position * position, + const Square kingSquare, + const Color color) +{ + const int kingFile = file(kingSquare); + int malus = getExpositionValue(position, kingSquare, color) + + getSafetyMalusOfKingFile(position, kingFile, kingSquare, + color); + + if (kingFile > FILE_A) + { + malus += getSafetyMalusOfKingFile(position, kingFile - 1, kingSquare, + color); + } + + if (kingFile < FILE_H) + { + malus += getSafetyMalusOfKingFile(position, kingFile + 1, kingSquare, + color); + } + + if (malus == 0) + { + malus = 11; /* malus for weak back rank */ + } + + malus += getPawnAttackMalusOfKingFile(position, kingFile, color); + + if (kingFile > FILE_A) + { + malus += getPawnAttackMalusOfKingFile(position, kingFile - 1, color); + } + + if (kingFile < FILE_H) + { + malus += getPawnAttackMalusOfKingFile(position, kingFile + 1, color); + } + + return malus; +} + +static int getPawnShelterMalus(const Position * position, const Color color, + KingSafetyHashInfo * kingSafetyHashtable) +{ + const Bitboard hashValue = getKingPawnSafetyHashValue(position, color); + KingSafetyHashInfo *kingSafetyHashInfo = + &kingSafetyHashtable[hashValue & KINGSAFETY_HASHTABLE_MASK]; + + if (kingSafetyHashInfo->hashValue == hashValue && + kingSafetyHashInfo->hashValue != 0) + { + return kingSafetyHashInfo->safetyMalus; + } + else + { + const int rankByColor[2] = { RANK_1, RANK_8 }; + int cr00, cr000; + const Square kingSquare = position->king[color]; + int pawnShelterMalus = 0, castlingShelterMalus = 0; + + if (color == WHITE) + { + cr00 = WHITE_00, cr000 = WHITE_000; + } + else + { + cr00 = BLACK_00, cr000 = BLACK_000; + } + + pawnShelterMalus = castlingShelterMalus = + getSafetyMalusOfKingSquare(position, kingSquare, color); + + if (position->castlingRights & cr00) + { + const Square kingSquare = getSquare(FILE_G, rankByColor[color]); + const int malus00 = + getSafetyMalusOfKingSquare(position, kingSquare, color); + + castlingShelterMalus = min(malus00, castlingShelterMalus); + } + + if (position->castlingRights & cr000) + { + const Square kingSquare = getSquare(FILE_B, rankByColor[color]); + const int malus000 = + getSafetyMalusOfKingSquare(position, kingSquare, color); + + castlingShelterMalus = min(malus000, castlingShelterMalus); + } + + pawnShelterMalus = (pawnShelterMalus + castlingShelterMalus) / 2; + kingSafetyHashInfo->hashValue = hashValue; + kingSafetyHashInfo->safetyMalus = pawnShelterMalus; + + return pawnShelterMalus; + } +} + +static int getKingSafetyMalus(const Position * position, + EvaluationBase * base, const Color color) +{ + const Color oppColor = opponent(color); + const Square kingSquare = position->king[color]; + const Bitboard protectingPawn = (color == WHITE ? + minValue[kingSquare] << 8 : + minValue[kingSquare] >> 8); + const Bitboard pawnAttackers = + position->piecesOfType[PAWN | oppColor] & kingAttacks[kingSquare]. + pawnAttackers[oppColor] & ~protectingPawn; + int pawnShelterMalus = 0, attackMalus = 0; + +#ifdef MALUS_QUEEN_DEFENDER_DISTANCE + int queenMalus; + const int queenDistance = + getMinimalTaxiDistance(position, kingSquare, (Piece) (QUEEN | color)); +#endif + + base->numAttackers[oppColor] += getNumberOfSetSquares(pawnAttackers); + attackMalus = (base->attackPoints[oppColor] * + KING_ATTACK_WEIGHT[base->numAttackers[oppColor]]) / 256; + +#ifdef MALUS_QUEEN_DEFENDER_DISTANCE + queenMalus = + (KING_ATTACK_WEIGHT[min(16, 1 + base->numAttackers[oppColor])] * + queenDistance * 8) / 256; +#endif + + pawnShelterMalus = + getPawnShelterMalus(position, color, base->kingsafetyHashtable); + +#ifdef MALUS_QUEEN_DEFENDER_DISTANCE + return attackMalus + pawnShelterMalus + queenMalus; +#else + return attackMalus + pawnShelterMalus; +#endif +} + +static int getWinningChances(const Position * position, Color color) +{ + const int MIN_CHANCES = 1; + const Color oppColor = opponent(color); + const int numPawns = position->numberOfPawns[color]; + const int numPieces = position->numberOfPieces[color] - numPawns - 1; + const int numOppPawns = position->numberOfPawns[oppColor]; + const int numOppPieces = + position->numberOfPieces[oppColor] - numOppPawns - 1; + const bool rookPresent = pieceIsPresent(position, (Piece) (ROOK | color)); + const bool queenPresent = + pieceIsPresent(position, (Piece) (QUEEN | color)); + + if (rookPresent || queenPresent) + { + const bool oppRookPresent = + pieceIsPresent(position, (Piece) (ROOK | oppColor)); + const bool oppQueenPresent = + pieceIsPresent(position, (Piece) (QUEEN | oppColor)); + + if (queenPresent) + { + if (numPieces == 1 && numPawns == 2 && oppQueenPresent) + { + const Bitboard pawns = position->piecesOfType[PAWN | color]; + + if ((((pawns & squaresOfFile[FILE_A]) && + (pawns & squaresOfFile[FILE_B])) || + ((pawns & squaresOfFile[FILE_H]) && + (pawns & squaresOfFile[FILE_G])))) + { + return 12; + } + } + + if (rookPresent || numPawns >= 2 || numPieces >= 3 || + getPieceCount(position, (Piece) (QUEEN | color)) >= 2) + { + return 16; + } + + assert(rookPresent == FALSE); + assert(numPawns <= 1); + assert(numPieces <= 2); + assert(getPieceCount(position, (Piece) (QUEEN | color)) == 1); + + if (numPawns == 0) /* KQKx, KQBKx, KQNKx */ + { + if (oppQueenPresent || + getPieceCount(position, (Piece) (ROOK | oppColor)) >= 2) + { + return 2; /* KQBKQ, KQNKQ, KQBKRR, KQNKRR */ + } + else if (numPieces == 1 && numOppPieces >= 2) + { + /* single queen vs. two pieces */ + + return (oppRookPresent ? 2 : 8); + } + } + else + { + assert(numPawns == 1); + + if (numPieces == 1 && + (oppQueenPresent || (numOppPieces >= 2 && oppRookPresent))) + { + /* KQPKQx, KQPKRBx, KQPKRNx */ + + Bitboard pawns = + position->piecesOfType[(Piece) (PAWN | color)]; + const Square pawnSquare = getNextSquare(&pawns); + + assert(pawnSquare != NO_SQUARE); + + if (testSquare(passedPawnCorridor[color][pawnSquare], + position->king[oppColor])) + { + return 4; + } + else + { + return 16; + } + } + + if (numPieces == 2 && oppQueenPresent && numOppPieces >= 2) + { + assert(pieceIsPresent(position, (Piece) (KNIGHT | color)) || + pieceIsPresent(position, (Piece) (BISHOP | color))); + + /* KQ[BN]PKQ[RBN] */ + + return 4; /* kamikaze sac possible */ + } + } + } + else /* rook present: */ + { + assert(rookPresent); + assert(queenPresent == FALSE); + + if (numPieces == 1 && numPawns == 2) + { + const Bitboard pawns = + position->piecesOfType[(Piece) (PAWN | color)]; + + if ((((pawns & squaresOfFile[FILE_A]) && + (pawns & squaresOfFile[FILE_C])) || + ((pawns & squaresOfFile[FILE_H]) && + (pawns & squaresOfFile[FILE_F])))) + { + return 12; + } + } + + if (numPawns >= 2 || numPieces >= 3) + { + if (getPieceCount(position, (Piece) (ROOK | color)) == 1 && + numPawns == 0 && oppRookPresent && + numPieces <= numOppPieces + 1) + { + return 4; /* R+[BN]+[BN] vs. R+[BN] */ + } + + if (numPawns == 0 && + getPieceCount(position, (Piece) (ROOK | oppColor)) >= 2) + { + /* R+R+[BN] vs. R+R+x */ + + return 2; + } + + return 16; + } + + assert(numPawns <= 1); + assert(numPieces <= 2); + + if (getPieceCount(position, (Piece) (ROOK | color)) == 2) + { + if (numPawns >= 1) + { + return 16; + } + + assert(numPawns == 0); + + if (oppQueenPresent || (oppRookPresent && numOppPieces >= 2)) + { + /* 2 rooks vs. queen */ + + return 2; + } + else + { + return 16; + } + } + + assert(getPieceCount(position, (Piece) (ROOK | color)) == 1); + + if (numPawns == 0) + { + if (oppQueenPresent || oppRookPresent) + { + /* rook (+ minor piece) vs. queen */ + + return 2; + } + else + { + /* rook (+ minor piece) vs. minor piece(s) */ + + return (numPieces > numOppPieces ? 16 : 2); + } + } + else + { + assert(numPawns == 1); + + if (numPieces == 1) + { + if (numOppPieces >= 2) + { + /* rook + pawn vs. 2+ pieces */ + + if (oppRookPresent || + pieceIsPresent(position, (Piece) (BISHOP | oppColor))) + { + return 2; /* kamikaze sac possible */ + } + else + { + return 16; /* two knights might have serious problems */ + } + } + + if (oppRookPresent || numOppPawns >= 2) + { + return 12; + } + } + else + { + assert(numPieces == 2); + + if (oppRookPresent && numOppPieces >= 2) + { + /* KR[BN]PKR[BN]x */ + + return 8; /* kamikaze sac possible */ + } + } + } + } + } + else /* no major pieces present: */ + { + const bool kamikazeSac = (bool) (numOppPieces >= numPawns); + + if (numPieces == 0) /* king (+pawns) */ + { + if (numPawns == 0) + { + return 0; + } + else + { + Bitboard pawns = position->piecesOfType[(Piece) (PAWN | color)]; + + if ((pawns & nonA & nonH) != EMPTY_BITBOARD) + { + return 16; + } + else + { + const Square king = position->king[color]; + const Square oppKing = position->king[oppColor]; + Square square; + + ITERATE_BITBOARD(&pawns, square) + { + const int promotionRank = + (color == WHITE ? RANK_8 : RANK_1); + const Square promotionSquare = + getSquare(file(square), promotionRank); + const int oppKingDist = distance(oppKing, promotionSquare); + + assert(file(square) == FILE_A || file(square) == FILE_H); + + if (oppKingDist > distance(square, promotionSquare) || + oppKingDist > distance(king, promotionSquare)) + { + return 16; + } + } + + return MIN_CHANCES; /* king holds pawn(s) */ + } + } + } + + if (numPawns == 0 && numPieces - numOppPieces <= 1 && + getPieceCount(position, (Piece) (BISHOP | color)) < 2) + { + /* no pawns, only one minor piece up */ + + if (numOppPawns > 0 && numOppPieces > 0 && + pieceIsPresent(position, (Piece) (BISHOP | color)) == FALSE) + { + return 12; /* opp might have chances */ + } + + return (numPieces > 1 ? MIN_CHANCES + 1 : MIN_CHANCES); + } + + if (numPawns == 1 && numPieces <= numOppPieces) + { + return 2; /* one pawn, opp may sac piece for pawn */ + } + + if (pieceIsPresent(position, (Piece) (KNIGHT | color)) == FALSE) + { /* only bishops: */ + const Bitboard exclude = + position->piecesOfType[(Piece) (PAWN | color)] | + minValue[position->king[color]]; + const Bitboard bishops = position->piecesOfColor[color] & ~exclude; + Bitboard pawns = position->piecesOfType[(Piece) (PAWN | color)]; + int numBishops = numPieces; + + assert(getPieceCount(position, (Piece) (BISHOP | color)) == + numPieces); + + if (numPieces > 1) + { + if (((bishops & lightSquares) == EMPTY_BITBOARD) || + ((bishops & darkSquares) == EMPTY_BITBOARD)) + { + numBishops = 1; + } + } + + if (numBishops == 1) + { + const int max = (oppositeColoredBishops(position) ? 8 : 16); + + if (numPawns == 0) + { + return MIN_CHANCES; + } + else if (kamikazeSac) + { + if (pieceIsPresent(position, (Piece) (BISHOP | oppColor)) || + pieceIsPresent(position, (Piece) (ROOK | oppColor))) + { + return 4; + } + else + { + return 12; + } + } + else if ((pawns & nonA & nonH) != EMPTY_BITBOARD) + { + return max; + } + else /* Check for wrong colored bishops: */ + { + const Square oppKing = position->king[oppColor]; + Square square; + + ITERATE_BITBOARD(&pawns, square) + { + const int promotionRank = + (color == WHITE ? RANK_8 : RANK_1); + const Square promotionSquare = + getSquare(file(square), promotionRank); + Bitboard requiredBishop, criticalOwnPawns; + + if (distance(oppKing, promotionSquare) > 1) + { + return max; + } + + requiredBishop = (squareColor(promotionSquare) == LIGHT ? + lightSquares : darkSquares); + + if ((requiredBishop & bishops) != EMPTY_BITBOARD) + { + return max; + } + + criticalOwnPawns = position->piecesOfType[PAWN | oppColor] & + squaresAbove[color][square] & + (file(square) == FILE_A ? + squaresOfFile[FILE_B] : squaresOfFile[FILE_G]); + + if (criticalOwnPawns != EMPTY_BITBOARD) + { + return max; + } + } + + /* no winning bishop+pawn combination found */ + + return MIN_CHANCES; + } + } + } + else if (pieceIsPresent(position, (Piece) (BISHOP | color)) == FALSE) + { /* only knights: */ + assert(getPieceCount(position, (Piece) (KNIGHT | color)) == + numPieces); + + if (numPieces == 2) + { + /* king + two knights (+ pawn) */ + + if (numPawns == 0) + { + if (numOppPawns > 0 && + pieceIsPresent(position, (Piece) (ROOK | oppColor))) + { + return 12; /* winning chances for opponent */ + } + else if (numOppPieces == 0 && numOppPawns != 0) + { + return 2; /* small chances */ + } + else + { + return MIN_CHANCES; /* no chances */ + } + } + + if (kamikazeSac) + { + return 12; + } + } + + if (numPieces == 1) + { + Bitboard pawns = position->piecesOfType[(Piece) (PAWN | color)]; + + if (numPawns == 0) + { + return MIN_CHANCES; + } + else if (kamikazeSac) + { + if (pieceIsPresent(position, (Piece) (BISHOP | oppColor)) || + pieceIsPresent(position, (Piece) (ROOK | oppColor))) + { + return 4; + } + else + { + return 12; + } + } + else if ((pawns & nonA & nonH) != EMPTY_BITBOARD) + { + return 16; + } + else /* Check for advanced border pawns: */ + { + const Square pawnSquare = getNextSquare(&pawns); + const int promotionRank = (color == WHITE ? RANK_8 : RANK_1); + const Square promotionSquare = + getSquare(file(pawnSquare), promotionRank); + + assert(pawnSquare != NO_SQUARE); + assert(file(pawnSquare) == FILE_A || + file(pawnSquare) == FILE_H); + + if (distance(pawnSquare, promotionSquare) == 1 && + distance(position->king[oppColor], promotionSquare) <= 1) + { + assert((color == WHITE && rank(pawnSquare) == RANK_7) || + (color == BLACK && rank(pawnSquare) == RANK_2)); + + return MIN_CHANCES; + } + } + } + } + } + + return 16; +} + +INLINE static bool kingSafetyEvalRequired(const Position * position, + const Color color) +{ + const Color oppColor = opponent(color); + + return (bool) (numberOfNonPawnPieces(position, oppColor) >= 3 && + position->piecesOfType[QUEEN | oppColor] != EMPTY_BITBOARD); +} + +INLINE static int getPawnAttackDistance(const Position * position, + const EvaluationBase * base, + const Color color) +{ + const Color oppColor = opponent(color); + const Bitboard forbiddenSquares = + position->piecesOfType[PAWN | color] | + base->pawnProtectedSquares[oppColor]; + + return getFloodValue(position->king[color], + position->piecesOfType[PAWN | oppColor], + ~forbiddenSquares); +} + +INLINE static bool kingIsOffside(const Position * position, + const EvaluationBase * base, + const Color color, int *distanceDiff) +{ + if (base->passedPawns[color] != EMPTY_BITBOARD || + position->piecesOfType[PAWN | color] == EMPTY_BITBOARD) + { + return FALSE; + } + else + { + const Color oppColor = opponent(color); + const Bitboard realm = kingRealm[oppColor] + [position->king[oppColor]][position->king[color]]; + const Bitboard ownPawns = position->piecesOfType[PAWN | color]; + + if ((realm & ownPawns) == ownPawns) + { + const int attackerDistance = + getPawnAttackDistance(position, base, oppColor); + const int defenderDistance = + getPawnAttackDistance(position, base, color); + *distanceDiff = (position->activeColor == color ? + (defenderDistance - 1) - attackerDistance : + defenderDistance - (attackerDistance - 1)); + + return (bool) (*distanceDiff > 1); + } + } + + return FALSE; +} + +INLINE static bool kingIsTrapped(const Position * position, + const Color kingColor) +{ + const Square kingSquare = position->king[kingColor]; + const Rank kingRank = colorRank(kingColor, kingSquare); + const File kingFile = file(kingSquare); + + if (kingRank == RANK_1 && (kingFile == FILE_A || kingFile == FILE_H)) + { + const int upward = (kingColor == WHITE ? 8 : -8); + const Color oppColor = opponent(kingColor); + const Piece blockingPiece = + position->piece[kingSquare + upward + upward]; + + if (position->piece[kingSquare + upward] == (PAWN | kingColor) && + (blockingPiece == (PAWN | oppColor) || + blockingPiece == (BISHOP | oppColor))) + { + return TRUE; + } + } + + return FALSE; +} + +INLINE static void initializeEvaluationBase(EvaluationBase * base, + KingSafetyHashInfo * + kingsafetyHashtable) +{ + base->openingPoints[WHITE] = base->openingPoints[BLACK] = 0; + base->endgamePoints[WHITE] = base->endgamePoints[BLACK] = 0; + base->kingsafetyHashtable = kingsafetyHashtable; +} + +INLINE static void evaluatePieces(const Position * position, + EvaluationBase * base) +{ + const Bitboard exclude = position->piecesOfType[WHITE_PAWN] | + position->piecesOfType[BLACK_PAWN] | + minValue[position->king[WHITE]] | minValue[position->king[BLACK]]; + Bitboard pieces = position->allPieces & (~exclude); + Square square; + +#ifdef BONUS_EXCHANGE_UP + Color color; +#endif + + ITERATE_BITBOARD(&pieces, square) + { + const PieceType pieceType = pieceType(position->piece[square]); + + switch (pieceType) + { + case QUEEN: + evaluateQueen(position, base, square); + break; + + case ROOK: + evaluateRook(position, base, square); + break; + + case BISHOP: + evaluateBishop(position, base, square); + break; + + case KNIGHT: + evaluateKnight(position, base, square); + break; + } + } + + if (position->piecesOfType[WHITE_BISHOP] != EMPTY_BITBOARD) + { + evaluateWhiteTrappedBishops(position, base); + } + + if (position->piecesOfType[BLACK_BISHOP] != EMPTY_BITBOARD) + { + evaluateBlackTrappedBishops(position, base); + } + +#ifdef BONUS_EXCHANGE_UP + for (color = WHITE; color <= BLACK; color++) + { + const Color oppColor = opponent(color); + + if (hasOrthoPieces(position, oppColor) == FALSE && + position->piecesOfType[ROOK | color] != EMPTY_BITBOARD && + numberOfNonPawnPieces(position, color) >= + numberOfNonPawnPieces(position, oppColor) && + (base->weakPawns[oppColor] != EMPTY_BITBOARD || + base->passedPawns[color] != EMPTY_BITBOARD)) + { + base->endgamePoints[color] += 8; + + if (base->passedPawns[color] != EMPTY_BITBOARD) + { + base->endgamePoints[color] += 24; + } + else if (getWidth(position->piecesOfType[PAWN | color]) > 3) + { + base->endgamePoints[color] += 8; + } + } + } +#endif +} + +INLINE static int getPositionalValue(Position * position, + EvaluationBase * base) +{ + int value, openingValue, endgameValue; + const int pi = phaseIndex(position); + +#ifdef MALUS_OFFSIDE_KING + Color color; +#endif +#ifdef BONUS_VALUABLE_TARGETS + Color color; +#endif + + assert(pi >= 0 && pi <= 256); + + base->openingPoints[WHITE] = base->openingPoints[BLACK] = + base->endgamePoints[WHITE] = base->endgamePoints[BLACK] = 0; + base->countedSquares[WHITE] = ~position->piecesOfColor[WHITE]; + base->countedSquares[BLACK] = ~position->piecesOfColor[BLACK]; + base->numAttackers[WHITE] = base->numAttackers[BLACK] = 0; + base->attackPoints[WHITE] = base->attackPoints[BLACK] = 0; + base->spaceAttackPoints[WHITE] = base->spaceAttackPoints[BLACK] = 0; +#ifdef CALCULATE_TARGETS + base->pieceAttacks[WHITE] = base->pieceAttacks[BLACK] = EMPTY_BITBOARD; +#endif + + if ((base->evaluateKingSafety[WHITE] = + kingSafetyEvalRequired(position, WHITE)) != FALSE) + { + base->kingAttackSquares[WHITE] = getKingMoves(position->king[WHITE]); + base->kingAttacks[WHITE] = &kingAttacks[position->king[WHITE]]; + } + + if ((base->evaluateKingSafety[BLACK] = + kingSafetyEvalRequired(position, BLACK)) != FALSE) + { + base->kingAttackSquares[BLACK] = getKingMoves(position->king[BLACK]); + base->kingAttacks[BLACK] = &kingAttacks[position->king[BLACK]]; + } + + evaluatePieces(position, base); + + if (base->passedPawns[WHITE] != EMPTY_BITBOARD || + base->passedPawns[BLACK] != EMPTY_BITBOARD) + { + evaluatePassedPawns(position, base); + } + + if (base->evaluateKingSafety[WHITE]) + { + base->openingPoints[WHITE] -= getKingSafetyMalus(position, base, WHITE); + } + + if (base->evaluateKingSafety[BLACK]) + { + base->openingPoints[BLACK] -= getKingSafetyMalus(position, base, BLACK); + } + +#ifdef MALUS_OFFSIDE_KING + for (color = WHITE; color <= BLACK; color++) + { + const int numNonPawnPieces = numberOfNonPawnPieces(position, color); + int distanceDiff = 0; + + if (numNonPawnPieces <= 2 && + kingIsOffside(position, base, color, &distanceDiff)) + { + int malus = 0; + + if (numNonPawnPieces == 1) + { + malus = 30 * distanceDiff; + } + else + { + malus = 15 * distanceDiff; + } + + base->endgamePoints[color] -= malus; + } + } +#endif + +#ifdef BONUS_SPACE_ATTACKS + { + const Bitboard whiteAttackers = position->piecesOfColor[WHITE] & + attackingRealm[WHITE]; + const Bitboard blackAttackers = position->piecesOfColor[BLACK] & + attackingRealm[BLACK]; + const int numWhiteAttackers = getNumberOfSetSquares(whiteAttackers); + const int numBlackAttackers = getNumberOfSetSquares(blackAttackers); + + base->openingPoints[WHITE] += SPACE_BONUS_WEIGHT[numWhiteAttackers] * + (base->spaceAttackPoints[WHITE] + numWhiteAttackers); + base->openingPoints[BLACK] += SPACE_BONUS_WEIGHT[numBlackAttackers] * + (base->spaceAttackPoints[BLACK] + numBlackAttackers); + } +#endif + + openingValue = base->openingPoints[WHITE] - base->openingPoints[BLACK]; + endgameValue = base->endgamePoints[WHITE] - base->endgamePoints[BLACK]; + + value = (openingValue * (256 - pi) + endgameValue * pi) / 256; + + return value; +} + +int getValue(Position * position, const int alpha, const int beta, + PawnHashInfo * pawnHashtable, + KingSafetyHashInfo * kingsafetyHashtable) +{ + const int MAX_DELTA = 200; + EvaluationBase base; + int value, lazyValue, chances = 16; + Color winningColor; + PawnHashInfo *pawnHashInfo = + &pawnHashtable[position->pawnHashValue & PAWN_HASHTABLE_MASK]; + + initializeEvaluationBase(&base, kingsafetyHashtable); + + if (pawnHashInfo->hashValue == position->pawnHashValue && + pawnHashInfo->hashValue != 0) + { +#ifndef NDEBUG + getPawnInfo(position, &base); + evaluatePawns(position, &base); + + assert(base.openingPoints[WHITE] == pawnHashInfo->openingPoints[WHITE]); + assert(base.openingPoints[BLACK] == pawnHashInfo->openingPoints[BLACK]); + assert(base.endgamePoints[WHITE] == pawnHashInfo->endgamePoints[WHITE]); + assert(base.endgamePoints[BLACK] == pawnHashInfo->endgamePoints[BLACK]); + assert(base.pawnProtectedSquares[WHITE] == + pawnHashInfo->pawnProtectedSquares[WHITE]); + assert(base.pawnProtectedSquares[BLACK] == + pawnHashInfo->pawnProtectedSquares[BLACK]); + assert(base.passedPawns[WHITE] == pawnHashInfo->passedPawns[WHITE]); + assert(base.passedPawns[BLACK] == pawnHashInfo->passedPawns[BLACK]); +#endif + + base.openingPoints[WHITE] = pawnHashInfo->openingPoints[WHITE]; + base.openingPoints[BLACK] = pawnHashInfo->openingPoints[BLACK]; + base.endgamePoints[WHITE] = pawnHashInfo->endgamePoints[WHITE]; + base.endgamePoints[BLACK] = pawnHashInfo->endgamePoints[BLACK]; + } + else + { + getPawnInfo(position, &base); + evaluatePawns(position, &base); + + pawnHashInfo->hashValue = position->pawnHashValue; + pawnHashInfo->openingPoints[WHITE] = base.openingPoints[WHITE]; + pawnHashInfo->openingPoints[BLACK] = base.openingPoints[BLACK]; + pawnHashInfo->endgamePoints[WHITE] = base.endgamePoints[WHITE]; + pawnHashInfo->endgamePoints[BLACK] = base.endgamePoints[BLACK]; + pawnHashInfo->pawnProtectedSquares[WHITE] = + base.pawnProtectedSquares[WHITE]; + pawnHashInfo->pawnProtectedSquares[BLACK] = + base.pawnProtectedSquares[BLACK]; + pawnHashInfo->passedPawns[WHITE] = base.passedPawns[WHITE]; + pawnHashInfo->passedPawns[BLACK] = base.passedPawns[BLACK]; + pawnHashInfo->weakOutpostSquares[WHITE] = + base.weakOutpostSquares[WHITE]; + pawnHashInfo->weakOutpostSquares[BLACK] = + base.weakOutpostSquares[BLACK]; +#ifdef BONUS_HIDDEN_PASSER + pawnHashInfo->hasPassersOrCandidates[WHITE] = + base.hasPassersOrCandidates[WHITE]; + pawnHashInfo->hasPassersOrCandidates[BLACK] = + base.hasPassersOrCandidates[BLACK]; +#endif + } + + value = positionalBalance(position, &base); + + winningColor = (value > 0 ? + position->activeColor : opponent(position->activeColor)); + + if (numberOfNonPawnPieces(position, winningColor) <= 4) + { + chances = getWinningChances(position, winningColor); + } + + lazyValue = (value * chances) / 16; + +#ifdef TRACE_EVAL + logDebug("\nStarting evaluation.\n"); + logDebug("phaseIndex = %d\n", phaseIndex(position)); + logDebug("opvWhite = %d egvWhite = %d\n", + position->openingValue[WHITE], position->endgameValue[WHITE]); + logDebug("opvBlack = %d egvBlack = %d\n", + position->openingValue[BLACK], position->endgameValue[BLACK]); + logDebug("basicValue = %d\n\n", basicValue); + logPosition(position); + logDebug("\n"); +#endif + + if (numberOfNonPawnPieces(position, WHITE) <= 3 || + numberOfNonPawnPieces(position, BLACK) <= 3 || + (lazyValue >= alpha - MAX_DELTA && lazyValue <= beta + MAX_DELTA)) + { + int positionalValue; + Color effectiveWinningColor; + + if (pawnHashInfo->hashValue == position->pawnHashValue && + pawnHashInfo->hashValue != 0) + { + base.pawnProtectedSquares[WHITE] = + pawnHashInfo->pawnProtectedSquares[WHITE]; + base.pawnProtectedSquares[BLACK] = + pawnHashInfo->pawnProtectedSquares[BLACK]; + base.passedPawns[WHITE] = pawnHashInfo->passedPawns[WHITE]; + base.passedPawns[BLACK] = pawnHashInfo->passedPawns[BLACK]; + base.weakOutpostSquares[WHITE] = + pawnHashInfo->weakOutpostSquares[WHITE]; + base.weakOutpostSquares[BLACK] = + pawnHashInfo->weakOutpostSquares[BLACK]; +#ifdef BONUS_HIDDEN_PASSER + base.hasPassersOrCandidates[WHITE] = + pawnHashInfo->hasPassersOrCandidates[WHITE]; + base.hasPassersOrCandidates[BLACK] = + pawnHashInfo->hasPassersOrCandidates[BLACK]; +#endif + } + + positionalValue = getPositionalValue(position, &base); + + value += (position->activeColor == WHITE ? + positionalValue : -positionalValue); + + effectiveWinningColor = (value > 0 ? + position->activeColor : + opponent(position->activeColor)); + + if (effectiveWinningColor != winningColor) + { + chances = + (numberOfNonPawnPieces(position, effectiveWinningColor) <= 4 ? + getWinningChances(position, effectiveWinningColor) : 16); + } + } + + return (value * chances) / 16; +} + +static void transposeMatrix(const int human[], int machine[]) +{ + int file, rank, i = 0; + + for (rank = RANK_8; rank >= RANK_1; rank--) + { + for (file = FILE_A; file <= FILE_H; file++) + { + const Square machineSquare = getSquare(file, rank); + + machine[machineSquare] = human[i++]; + } + } +} + +#define PSV(piece,square,stage) pieceSquareValue[(piece)][(square)][(stage)] + +static void initializePieceSquareValues() +{ + const int PawnFileOpening = 5; + const int KnightCentreOpening = 5; + const int KnightCentreEndgame = 5; + const int KnightRankOpening = 5; + const int KnightBackRankOpening = 0; + const int KnightTrapped = 100; + const int BishopCentreOpening = 2; + const int BishopCentreEndgame = 3; + const int BishopBackRankOpening = 10; + const int BishopDiagonalOpening = 4; + const int RookFileOpening = 3; + const int QueenCentreOpening = 1; + const int QueenCentreEndgame = 4; + const int QueenBackRankOpening = 5; + const int KingCentreEndgame = 12; + const int KingFileOpening = 10; + const int KingRankOpening = 10; + const int PawnFile[8] = { -3, -1, +0, +1, +1, +0, -1, -3, }; + const int KnightLine[8] = { -4, -2, +0, +1, +1, +0, -2, -4, }; + const int KnightRank[8] = { -2, -1, +0, +1, +2, +3, +2, +1, }; + const int BishopLine[8] = { -3, -1, +0, +1, +1, +0, -1, -3, }; + const int RookFile[8] = { -2, -1, +0, +1, +1, +0, -1, -2, }; + const int QueenFileOpening[8] = { -2, -1, +0, +0, +0, +0, -1, -2, }; + const int QueenRankOpening[8] = { -1, +0, +0, +0, +0, +0, +0, -1, }; + const int QueenFileEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, }; + const int QueenRankEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, }; + const int KingFileEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, }; + const int KingRankEndgame[8] = { -3, -1, +0, +1, +1, +0, -1, -3, }; + const int KingFile[8] = { +3, +4, +2, +0, +0, +2, +4, +3, }; + const int KingRank[8] = { +1, +0, -2, -3, -4, -5, -6, -7, }; + int i; + Square sq; + + for (i = 0; i < 16; i++) + { + int j; + + for (j = 0; j < _64_; j++) + { + pieceSquareValue[i][j][0] = pieceSquareValue[i][j][1] = 0; + } + } + + /* pawns */ + + ITERATE(sq) + { + PSV(WHITE_PAWN, sq, OPENING) += PawnFile[file(sq)] * PawnFileOpening; + PSV(WHITE_PAWN, sq, OPENING) += PAWN_VALUE_OPENING; + PSV(WHITE_PAWN, sq, ENDGAME) += PAWN_VALUE_ENDGAME; + } + + PSV(WHITE_PAWN, D3, OPENING) += 10; + PSV(WHITE_PAWN, E3, OPENING) += 10; + PSV(WHITE_PAWN, D4, OPENING) += 20; + PSV(WHITE_PAWN, E4, OPENING) += 20; + PSV(WHITE_PAWN, D5, OPENING) += 10; + PSV(WHITE_PAWN, E5, OPENING) += 10; + + /* knights */ + + ITERATE(sq) + { + PSV(WHITE_KNIGHT, sq, OPENING) += VALUE_KNIGHT_OPENING; + PSV(WHITE_KNIGHT, sq, ENDGAME) += VALUE_KNIGHT_ENDGAME; + PSV(WHITE_KNIGHT, sq, OPENING) += + KnightLine[file(sq)] * KnightCentreOpening; + PSV(WHITE_KNIGHT, sq, OPENING) += + KnightLine[rank(sq)] * KnightCentreOpening; + PSV(WHITE_KNIGHT, sq, ENDGAME) += + KnightLine[file(sq)] * KnightCentreEndgame; + PSV(WHITE_KNIGHT, sq, ENDGAME) += + KnightLine[rank(sq)] * KnightCentreEndgame; + PSV(WHITE_KNIGHT, sq, OPENING) += + KnightRank[rank(sq)] * KnightRankOpening; + } + + for (sq = A1; sq <= H1; sq++) + { + PSV(WHITE_KNIGHT, sq, OPENING) -= KnightBackRankOpening; + } + + PSV(WHITE_KNIGHT, A8, OPENING) -= KnightTrapped; + PSV(WHITE_KNIGHT, H8, OPENING) -= KnightTrapped; + + /* bishops */ + + ITERATE(sq) + { + PSV(WHITE_BISHOP, sq, OPENING) += VALUE_BISHOP_OPENING; + PSV(WHITE_BISHOP, sq, ENDGAME) += VALUE_BISHOP_ENDGAME; + PSV(WHITE_BISHOP, sq, OPENING) += + BishopLine[file(sq)] * BishopCentreOpening; + PSV(WHITE_BISHOP, sq, OPENING) += + BishopLine[rank(sq)] * BishopCentreOpening; + PSV(WHITE_BISHOP, sq, ENDGAME) += + BishopLine[file(sq)] * BishopCentreEndgame; + PSV(WHITE_BISHOP, sq, ENDGAME) += + BishopLine[rank(sq)] * BishopCentreEndgame; + } + + for (sq = A1; sq <= H1; sq++) + { + PSV(WHITE_BISHOP, sq, OPENING) -= BishopBackRankOpening; + } + + for (i = 0; i < 8; i++) + { + sq = getSquare(i, i); + PSV(WHITE_BISHOP, sq, OPENING) += BishopDiagonalOpening; + PSV(WHITE_BISHOP, getFlippedSquare(sq), OPENING) += + BishopDiagonalOpening; + } + + /* rooks */ + + ITERATE(sq) + { + PSV(WHITE_ROOK, sq, OPENING) += VALUE_ROOK_OPENING; + PSV(WHITE_ROOK, sq, ENDGAME) += VALUE_ROOK_ENDGAME; + PSV(WHITE_ROOK, sq, OPENING) += + RookFile[file(sq)] * RookFileOpening - 25; + } + + /* queens */ + + ITERATE(sq) + { + PSV(WHITE_QUEEN, sq, OPENING) += VALUE_QUEEN_OPENING; + PSV(WHITE_QUEEN, sq, ENDGAME) += VALUE_QUEEN_ENDGAME; + PSV(WHITE_QUEEN, sq, OPENING) += + QueenFileOpening[file(sq)] * QueenCentreOpening; + PSV(WHITE_QUEEN, sq, OPENING) += + QueenRankOpening[rank(sq)] * QueenCentreOpening; + PSV(WHITE_QUEEN, sq, ENDGAME) += + QueenFileEndgame[file(sq)] * QueenCentreEndgame; + PSV(WHITE_QUEEN, sq, ENDGAME) += + QueenRankEndgame[rank(sq)] * QueenCentreEndgame; + } + + for (sq = A1; sq <= H1; sq++) + { + PSV(WHITE_QUEEN, sq, OPENING) -= QueenBackRankOpening; + } + + /* king */ + + ITERATE(sq) + { + PSV(WHITE_KING, sq, ENDGAME) += + KingFileEndgame[file(sq)] * KingCentreEndgame; + PSV(WHITE_KING, sq, ENDGAME) += + KingRankEndgame[rank(sq)] * KingCentreEndgame; + PSV(WHITE_KING, sq, OPENING) += KingFile[file(sq)] * KingFileOpening; + PSV(WHITE_KING, sq, OPENING) += KingRank[rank(sq)] * KingRankOpening; + } + + /* Set the value of the black pieces. */ + + ITERATE(sq) + { + int stage; + + for (stage = OPENING; stage <= ENDGAME; stage++) + { + pieceSquareValue[BLACK_KING][sq][stage] = + pieceSquareValue[WHITE_KING][getFlippedSquare(sq)][stage]; + pieceSquareValue[BLACK_QUEEN][sq][stage] = + pieceSquareValue[WHITE_QUEEN][getFlippedSquare(sq)][stage]; + pieceSquareValue[BLACK_ROOK][sq][stage] = + pieceSquareValue[WHITE_ROOK][getFlippedSquare(sq)][stage]; + pieceSquareValue[BLACK_BISHOP][sq][stage] = + pieceSquareValue[WHITE_BISHOP][getFlippedSquare(sq)][stage]; + pieceSquareValue[BLACK_KNIGHT][sq][stage] = + pieceSquareValue[WHITE_KNIGHT][getFlippedSquare(sq)][stage]; + pieceSquareValue[BLACK_PAWN][sq][stage] = + pieceSquareValue[WHITE_PAWN][getFlippedSquare(sq)][stage]; + } + } +} + +void initializeKingAttacks() +{ + Square square; + + ITERATE(square) + { + const Bitboard corona = getKingMoves(square); + KingAttacks *attackInfo = &kingAttacks[square]; + Square attackerSquare; + + attackInfo->diaAttackers = attackInfo->orthoAttackers = + attackInfo->knightAttackers = attackInfo->pawnAttackers[WHITE] = + attackInfo->pawnAttackers[WHITE] = EMPTY_BITBOARD; + + ITERATE(attackerSquare) + { + attackInfo->attackedByDia[attackerSquare] = + attackInfo->attackedByOrtho[attackerSquare] = NO_SQUARE; + } + + ITERATE(attackerSquare) + { + Bitboard dia, ortho; + const Bitboard knight = + corona & generalMoves[WHITE_KNIGHT][attackerSquare]; + const Bitboard whitePawn = + corona & generalMoves[WHITE_PAWN][attackerSquare]; + const Bitboard blackPawn = + corona & generalMoves[BLACK_PAWN][attackerSquare]; + + dia = corona & generalMoves[WHITE_BISHOP][attackerSquare]; + ortho = corona & generalMoves[WHITE_ROOK][attackerSquare]; + + if (dia != EMPTY_BITBOARD) + { + Square attackedSquare; + int dist = 8; + + setSquare(attackInfo->diaAttackers, attackerSquare); + + ITERATE_BITBOARD(&dia, attackedSquare) + { + const int currentDistance = + distance(attackerSquare, attackedSquare); + + if (currentDistance < dist) + { + attackInfo->attackedByDia[attackerSquare] = attackedSquare; + dist = currentDistance; + } + } + } + + if (ortho != EMPTY_BITBOARD) + { + Square attackedSquare; + int dist = 8; + + setSquare(attackInfo->orthoAttackers, attackerSquare); + + ITERATE_BITBOARD(&ortho, attackedSquare) + { + const int currentDistance = + distance(attackerSquare, attackedSquare); + + if (currentDistance < dist) + { + attackInfo->attackedByOrtho[attackerSquare] = + attackedSquare; + dist = currentDistance; + } + } + } + + if (knight != EMPTY_BITBOARD) + { + setSquare(attackInfo->knightAttackers, attackerSquare); + } + + if (whitePawn != EMPTY_BITBOARD) + { + setSquare(attackInfo->pawnAttackers[WHITE], attackerSquare); + } + + if (blackPawn != EMPTY_BITBOARD) + { + setSquare(attackInfo->pawnAttackers[BLACK], attackerSquare); + } + } + } +} + +int initializeModuleEvaluation() +{ + int tmp1[_64_]; + Square square, kingsquare, catchersquare; + + centralFiles = squaresOfFile[FILE_D] | squaresOfFile[FILE_E]; + attackingRealm[WHITE] = squaresOfRank[RANK_5] | squaresOfRank[RANK_6] | + squaresOfRank[RANK_7] | squaresOfRank[RANK_8]; + attackingRealm[BLACK] = squaresOfRank[RANK_4] | squaresOfRank[RANK_3] | + squaresOfRank[RANK_2] | squaresOfRank[RANK_1]; + + rookTraps[WHITE] = rookTraps[BLACK] = EMPTY_BITBOARD; + setSquare(rookTraps[WHITE], A1); + setSquare(rookTraps[WHITE], B1); + setSquare(rookTraps[WHITE], C1); + setSquare(rookTraps[WHITE], A2); + setSquare(rookTraps[WHITE], B2); + setSquare(rookTraps[WHITE], F1); + setSquare(rookTraps[WHITE], G1); + setSquare(rookTraps[WHITE], H1); + setSquare(rookTraps[WHITE], G2); + setSquare(rookTraps[WHITE], H2); + + transposeMatrix(&KnightOutpostBonus[0], &tmp1[0]); + + ITERATE(square) + { + Color color; + Square kingSquare; + + leeSquares[square] = luvSquares[square] = EMPTY_BITBOARD; + + for (color = WHITE; color <= BLACK; color++) + { + passedPawnRectangle[color][square] = + passedPawnCorridor[color][square] = + passedPawnCatcher[color][square] = + candidateDefenders[color][square] = + candidateSupporters[color][square] = EMPTY_BITBOARD; + } + + if (testSquare(rookTraps[WHITE], square)) + { + setSquare(rookTraps[BLACK], getFlippedSquare(square)); + } + + KNIGHT_OUTPOST_BONUS[WHITE][square] = tmp1[square]; + + ITERATE(kingSquare) + { + kingRealm[WHITE][square][kingSquare] = + kingRealm[BLACK][square][kingSquare] = EMPTY_BITBOARD; + } + } + + ITERATE(square) + { + const int squarefile = file(square); + const int squarerank = rank(square); + int d1 = min(distance(square, D4), distance(square, E4)); + int d2 = min(distance(square, D5), distance(square, E5)); + int td1 = min(taxiDistance(square, D4), taxiDistance(square, E4)); + int td2 = min(taxiDistance(square, D5), taxiDistance(square, E5)); + + KNIGHT_OUTPOST_BONUS[BLACK][square] = + KNIGHT_OUTPOST_BONUS[WHITE][getFlippedSquare(square)]; + + centerDistance[square] = min(d1, d2); + centerTaxiDistance[square] = min(td1, td2); + butterflySquares[square] = + generalMoves[KING][square] & ~squaresOfFile[squarefile]; + lateralSquares[square] = + generalMoves[KING][square] & squaresOfRank[squarerank]; + companionFiles[square] = + ((squaresOfFile[squarefile] & nonA) >> 1) | + ((squaresOfFile[squarefile] & nonH) << 1); + rookBlockers[square] = EMPTY_BITBOARD; + + ITERATE(kingsquare) + { + const int kingsquarefile = file(kingsquare); + const int kingsquarerank = rank(kingsquare); + Square targetSquare; + + if (kingsquarerank >= squarerank && + distance(square, kingsquare) <= 7 - squarerank) + { + setSquare(passedPawnRectangle[WHITE][square], kingsquare); + } + + if (kingsquarerank <= squarerank && + distance(square, kingsquare) <= squarerank) + { + setSquare(passedPawnRectangle[BLACK][square], kingsquare); + } + + if (file(square) <= FILE_D) + { + if (kingsquarefile < squarefile) + { + setSquare(leeSquares[square], kingsquare); + } + + if (kingsquarefile > squarefile) + { + setSquare(luvSquares[square], kingsquare); + } + } + else + { + if (kingsquarefile > squarefile) + { + setSquare(leeSquares[square], kingsquare); + } + + if (kingsquarefile < squarefile) + { + setSquare(luvSquares[square], kingsquare); + } + } + + if (kingsquarefile == squarefile) + { + if (kingsquarerank > squarerank) + { + setSquare(passedPawnCorridor[WHITE][square], kingsquare); + } + + if (kingsquarerank < squarerank) + { + setSquare(passedPawnCorridor[BLACK][square], kingsquare); + } + } + + if (squarerank == kingsquarerank) + { + if (squarefile <= FILE_C && kingsquarefile <= FILE_C && + kingsquarefile > squarefile) + { + setSquare(rookBlockers[square], kingsquare); + } + + if (squarefile >= FILE_F && kingsquarefile >= FILE_F && + kingsquarefile < squarefile) + { + setSquare(rookBlockers[square], kingsquare); + } + } + + ITERATE(targetSquare) + { + if (distance(square, targetSquare) < + distance(kingsquare, targetSquare)) + { + const Rank targetrank = rank(targetSquare); + + if (targetrank <= squarerank + 1) + { + setSquare(kingRealm[WHITE][square][kingsquare], + targetSquare); + } + + if (targetrank >= squarerank - 1) + { + setSquare(kingRealm[BLACK][square][kingsquare], + targetSquare); + } + } + } + } + + ITERATE(catchersquare) + { + const int catcherrank = rank(catchersquare); + const int promotionCountWhite = RANK_8 - squarerank; + const int promotionCountBlack = squarerank - RANK_1; + const int promotionCountWhiteCatcher = + (catcherrank == RANK_2 ? + RANK_8 - catcherrank - 1 : RANK_8 - catcherrank); + const int promotionCountBlackCatcher = + (catcherrank == RANK_7 ? + catcherrank - RANK_1 - 1 : catcherrank - RANK_1); + + if (promotionCountBlackCatcher < promotionCountWhite + 1) + { + setSquare(passedPawnCatcher[WHITE][square], catchersquare); + } + + if (promotionCountWhiteCatcher < promotionCountBlack + 1) + { + setSquare(passedPawnCatcher[BLACK][square], catchersquare); + } + + if (abs(file(catchersquare) - squarefile) == 1) + { + if (rank(catchersquare) > squarerank) + { + setSquare(candidateDefenders[WHITE][square], catchersquare); + } + + if (rank(catchersquare) <= squarerank) + { + setSquare(candidateSupporters[WHITE][square], catchersquare); + } + + if (rank(catchersquare) < squarerank) + { + setSquare(candidateDefenders[BLACK][square], catchersquare); + } + + if (rank(catchersquare) >= squarerank) + { + setSquare(candidateSupporters[BLACK][square], catchersquare); + } + } + } + } + + ITERATE(square) + { + const int squarerank = rank(square); + + ITERATE(kingsquare) + { + if (squarerank >= RANK_2 && squarerank <= RANK_7) + { + passedPawnDefenderMalus[WHITE][WHITE][square][kingsquare] = + getPassedPawnDefenderMalus(WHITE, WHITE, square, kingsquare); + passedPawnDefenderMalus[WHITE][BLACK][square][kingsquare] = + getPassedPawnDefenderMalus(WHITE, BLACK, square, kingsquare); + passedPawnDefenderMalus[BLACK][WHITE][square][kingsquare] = + getPassedPawnDefenderMalus(BLACK, WHITE, square, kingsquare); + passedPawnDefenderMalus[BLACK][BLACK][square][kingsquare] = + getPassedPawnDefenderMalus(BLACK, BLACK, square, kingsquare); + } + else + { + passedPawnDefenderMalus[WHITE][WHITE][square][kingsquare] = + passedPawnDefenderMalus[WHITE][BLACK][square][kingsquare] = + passedPawnDefenderMalus[BLACK][WHITE][square][kingsquare] = + passedPawnDefenderMalus[BLACK][BLACK][square][kingsquare] = 0; + } + } + } + + abghFiles = squaresOfFile[FILE_A] | squaresOfFile[FILE_B] | + squaresOfFile[FILE_G] | squaresOfFile[FILE_H]; + + initializePieceSquareValues(); + initializeKingAttacks(); + + attackPoints[WHITE_KING] = 0; + attackPoints[WHITE_QUEEN] = QUEEN_BONUS_ATTACK; + attackPoints[WHITE_ROOK] = ROOK_BONUS_ATTACK; + attackPoints[WHITE_BISHOP] = BISHOP_BONUS_ATTACK; + attackPoints[WHITE_KNIGHT] = KNIGHT_BONUS_ATTACK; + attackPoints[WHITE_PAWN] = 0; + attackPoints[BLACK_KING] = 0; + attackPoints[BLACK_QUEEN] = QUEEN_BONUS_ATTACK; + attackPoints[BLACK_ROOK] = ROOK_BONUS_ATTACK; + attackPoints[BLACK_BISHOP] = BISHOP_BONUS_ATTACK; + attackPoints[BLACK_KNIGHT] = KNIGHT_BONUS_ATTACK; + attackPoints[BLACK_PAWN] = 0; + + weakOutpostSquareCandidates[WHITE] = weakOutpostSquareCandidates[BLACK] = + EMPTY_BITBOARD; + + setSquare(weakOutpostSquareCandidates[WHITE], C4); + setSquare(weakOutpostSquareCandidates[WHITE], D4); + setSquare(weakOutpostSquareCandidates[WHITE], E4); + setSquare(weakOutpostSquareCandidates[WHITE], F4); + setSquare(weakOutpostSquareCandidates[WHITE], C3); + setSquare(weakOutpostSquareCandidates[WHITE], D3); + setSquare(weakOutpostSquareCandidates[WHITE], E3); + setSquare(weakOutpostSquareCandidates[WHITE], F3); + setSquare(weakOutpostSquareCandidates[BLACK], C5); + setSquare(weakOutpostSquareCandidates[BLACK], D5); + setSquare(weakOutpostSquareCandidates[BLACK], E5); + setSquare(weakOutpostSquareCandidates[BLACK], F5); + setSquare(weakOutpostSquareCandidates[BLACK], C6); + setSquare(weakOutpostSquareCandidates[BLACK], D6); + setSquare(weakOutpostSquareCandidates[BLACK], E6); + setSquare(weakOutpostSquareCandidates[BLACK], F6); + + return 0; +} + +bool flipTest(Position * position, + PawnHashInfo * pawnHashtable, + KingSafetyHashInfo * kingsafetyHashtable) +{ + int v1, v2; + + initializePosition(position); + v1 = getValue(position, VALUE_MATED, -VALUE_MATED, + pawnHashtable, kingsafetyHashtable); + + flipPosition(position); + initializePosition(position); + v2 = getValue(position, VALUE_MATED, -VALUE_MATED, + pawnHashtable, kingsafetyHashtable); + + flipPosition(position); + initializePosition(position); + + if (v1 != v2) + { + const int debugFlag = debugOutput; + + debugOutput = TRUE; + + logDebug("flip test failed: v1=%d v2=%d\n", v1, v2); + logPosition(position); + logDebug("hash: %llu\n", position->hashValue); + getValue(position, VALUE_MATED, -VALUE_MATED, + pawnHashtable, kingsafetyHashtable); + flipPosition(position); + initializePosition(position); + logPosition(position); + getValue(position, VALUE_MATED, -VALUE_MATED, + pawnHashtable, kingsafetyHashtable); + flipPosition(position); + initializePosition(position); + + debugOutput = debugFlag; + } + + return (bool) (v1 == v2); +} + +static int testPawnInfoGeneration() +{ + Variation variation; + EvaluationBase base; + + initializeVariation(&variation, + "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - - 0 1"); + getPawnInfo(&variation.singlePosition, &base); + + assert(getNumberOfSetSquares(base.pawnProtectedSquares[WHITE]) == 8); + assert(testSquare(base.pawnProtectedSquares[WHITE], B4)); + assert(testSquare(base.pawnProtectedSquares[WHITE], A3)); + assert(testSquare(base.pawnProtectedSquares[WHITE], C3)); + assert(testSquare(base.pawnProtectedSquares[WHITE], D4)); + assert(testSquare(base.pawnProtectedSquares[WHITE], F4)); + assert(testSquare(base.pawnProtectedSquares[WHITE], E5)); + assert(testSquare(base.pawnProtectedSquares[WHITE], G5)); + assert(testSquare(base.pawnProtectedSquares[WHITE], G3)); + + assert(getNumberOfSetSquares(base.pawnProtectedSquares[BLACK]) == 7); + assert(testSquare(base.pawnProtectedSquares[BLACK], B3)); + assert(testSquare(base.pawnProtectedSquares[BLACK], D3)); + assert(testSquare(base.pawnProtectedSquares[BLACK], C2)); + assert(testSquare(base.pawnProtectedSquares[BLACK], E2)); + assert(testSquare(base.pawnProtectedSquares[BLACK], E4)); + assert(testSquare(base.pawnProtectedSquares[BLACK], G4)); + assert(testSquare(base.pawnProtectedSquares[BLACK], G6)); + + assert(getNumberOfSetSquares(base.passedPawns[WHITE]) == 0); + assert(getNumberOfSetSquares(base.passedPawns[BLACK]) == 1); + assert(testSquare(base.passedPawns[BLACK], D3)); + + assert(getNumberOfSetSquares(base.weakPawns[WHITE]) == 3); + assert(testSquare(base.weakPawns[WHITE], B2)); + assert(testSquare(base.weakPawns[WHITE], E3)); + assert(testSquare(base.weakPawns[WHITE], H2)); + + assert(getNumberOfSetSquares(base.weakPawns[BLACK]) == 4); + assert(testSquare(base.weakPawns[BLACK], A4)); + assert(testSquare(base.weakPawns[BLACK], C4)); + assert(testSquare(base.weakPawns[BLACK], F5)); + assert(testSquare(base.weakPawns[BLACK], H7)); + + initializeVariation(&variation, + "4k3/2p5/p2p4/P2P4/1PP3p1/7p/7P/4K3 w - - 0 1"); + getPawnInfo(&variation.singlePosition, &base); + + assert(getNumberOfSetSquares(base.passedPawns[WHITE]) == 0); + assert(getNumberOfSetSquares(base.passedPawns[BLACK]) == 0); + assert(getNumberOfSetSquares(base.weakPawns[WHITE]) == 1); + assert(testSquare(base.weakPawns[WHITE], H2)); + + assert(getNumberOfSetSquares(base.weakPawns[BLACK]) == 3); + assert(testSquare(base.weakPawns[BLACK], A6)); + assert(testSquare(base.weakPawns[BLACK], C7)); + assert(testSquare(base.weakPawns[BLACK], G4)); + + return 0; +} + +static int testWeakPawnDetection() +{ + Position position; + Bitboard expectedResult = EMPTY_BITBOARD; + EvaluationBase base; + + clearPosition(&position); + position.piece[E1] = WHITE_KING; + position.piece[E8] = BLACK_KING; + position.piece[A3] = WHITE_PAWN; + position.piece[B5] = WHITE_PAWN; + position.piece[B6] = WHITE_PAWN; + position.piece[C4] = WHITE_PAWN; + position.piece[E4] = WHITE_PAWN; + position.piece[G4] = WHITE_PAWN; + position.piece[H2] = WHITE_PAWN; + position.piece[A4] = BLACK_PAWN; + position.piece[B7] = BLACK_PAWN; + setSquare(expectedResult, A3); + setSquare(expectedResult, E4); + initializePosition(&position); + getPawnInfo(&position, &base); + assert(base.weakPawns[WHITE] == expectedResult); + expectedResult = EMPTY_BITBOARD; + setSquare(expectedResult, B7); + assert(base.weakPawns[BLACK] == expectedResult); + assert(base.candidatePawns[BLACK] == EMPTY_BITBOARD); + + position.piece[C4] = NO_PIECE; + position.piece[C5] = WHITE_PAWN; + initializePosition(&position); + getPawnInfo(&position, &base); + expectedResult = EMPTY_BITBOARD; + setSquare(expectedResult, C5); + assert(base.candidatePawns[WHITE] == expectedResult); + + clearPosition(&position); + position.piece[E1] = WHITE_KING; + position.piece[E8] = BLACK_KING; + position.piece[A3] = WHITE_PAWN; + position.piece[B5] = WHITE_PAWN; + position.piece[B6] = WHITE_PAWN; + position.piece[C4] = WHITE_PAWN; + position.piece[E4] = WHITE_PAWN; + position.piece[G4] = WHITE_PAWN; + position.piece[H2] = WHITE_PAWN; + position.piece[A4] = BLACK_PAWN; + position.piece[B7] = BLACK_PAWN; + position.piece[D6] = BLACK_PAWN; + expectedResult = EMPTY_BITBOARD; + setSquare(expectedResult, A3); + setSquare(expectedResult, E4); + setSquare(expectedResult, C4); + initializePosition(&position); + getPawnInfo(&position, &base); + assert(base.weakPawns[WHITE] == expectedResult); + assert(base.candidatePawns[WHITE] == EMPTY_BITBOARD); + expectedResult = EMPTY_BITBOARD; + setSquare(expectedResult, B7); + setSquare(expectedResult, D6); + assert(base.weakPawns[BLACK] == expectedResult); + assert(base.candidatePawns[BLACK] == EMPTY_BITBOARD); + + position.piece[G5] = BLACK_PAWN; + expectedResult = EMPTY_BITBOARD; + setSquare(expectedResult, A3); + setSquare(expectedResult, E4); + setSquare(expectedResult, C4); + setSquare(expectedResult, H2); + initializePosition(&position); + getPawnInfo(&position, &base); + assert(base.weakPawns[WHITE] == expectedResult); + assert(base.candidatePawns[WHITE] == EMPTY_BITBOARD); + expectedResult = EMPTY_BITBOARD; + setSquare(expectedResult, B7); + setSquare(expectedResult, D6); + setSquare(expectedResult, G5); + assert(base.weakPawns[BLACK] == expectedResult); + assert(base.candidatePawns[BLACK] == EMPTY_BITBOARD); + + return 0; +} + +static int testBaseInitialization() +{ + Variation variation; + + initializeVariation(&variation, FEN_GAMESTART); + + assert(testSquare(passedPawnCorridor[WHITE][B4], B6)); + assert(testSquare(passedPawnCorridor[BLACK][B4], B6) == FALSE); + assert(testSquare(passedPawnCorridor[WHITE][C2], H7) == FALSE); + assert(testSquare(passedPawnCorridor[BLACK][G6], G2)); + + assert(KNIGHT_OUTPOST_BONUS[WHITE][A8] == KnightOutpostBonus[0]); + assert(KNIGHT_OUTPOST_BONUS[WHITE][H1] == KnightOutpostBonus[63]); + assert(KNIGHT_OUTPOST_BONUS[WHITE][D4] == KnightOutpostBonus[35]); + assert(KNIGHT_OUTPOST_BONUS[WHITE][D3] == KnightOutpostBonus[43]); + + return 0; +} + +/* +static int testFlippings() +{ + const char fen1[] = + "2rr2k1/1b3ppp/pb2p3/1p2P3/1P2BPnq/P1N3P1/1B2Q2P/R4R1K b - - 0 1"; + const char fen2[] = "4k3/2p5/p2p4/P2P4/1PP3p1/7p/7P/4K3 w - - 0 1"; + const char fen3[] = "8/7p/5k2/5p2/p1p2P2/Pr1pPK2/1P1R3P/8 b - - 0 1"; + const char fen4[] = + "6r1/Q2Pn2k/p1p1P2p/5p2/2PqR1r1/1P6/P6P/5R1K b - - 5 4"; + const char fen5[] = + "Q4rk1/2bb1ppp/4pn2/pQ5q/3P4/N4N2/5PPP/R1B2RK1 w - a6 0 4"; + Variation variation; + + initializeVariation(&variation, fen1); + assert(flipTest(&variation.singlePosition) != FALSE); + + initializeVariation(&variation, fen2); + assert(flipTest(&variation.singlePosition) != FALSE); + + initializeVariation(&variation, fen3); + assert(flipTest(&variation.singlePosition) != FALSE); + + initializeVariation(&variation, fen4); + assert(flipTest(&variation.singlePosition) != FALSE); + + initializeVariation(&variation, fen5); + assert(flipTest(&variation.singlePosition) != FALSE); + + return 0; +} +*/ + +static int testWinningChancesCalculations() +{ + Position position; + + clearPosition(&position); + + position.piece[E1] = WHITE_KING; + position.piece[E8] = BLACK_KING; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) == 0); + assert(getWinningChances(&position, BLACK) == 0); + + position.piece[A8] = BLACK_KNIGHT; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) == 1); + + position.piece[A8] = BLACK_BISHOP; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) == 1); + + position.piece[A8] = BLACK_ROOK; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) > 0); + + position.piece[A8] = WHITE_QUEEN; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) > 0); + position.piece[A8] = NO_PIECE; + + position.piece[B1] = WHITE_KNIGHT; + position.piece[G1] = WHITE_KNIGHT; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) == 1); + + position.piece[A7] = BLACK_PAWN; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) > 0); + + position.piece[B8] = BLACK_KNIGHT; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) <= 4); + + position.piece[B2] = WHITE_KNIGHT; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) > 0); + + clearPosition(&position); + + position.piece[E1] = WHITE_KING; + position.piece[E8] = BLACK_KING; + position.piece[C1] = WHITE_BISHOP; + position.piece[F1] = WHITE_BISHOP; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) > 0); + + position.piece[F1] = NO_PIECE; + position.piece[G1] = WHITE_BISHOP; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) == 1); + + position.piece[A2] = WHITE_PAWN; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) > 0); + + position.piece[E8] = NO_PIECE; + position.piece[B8] = BLACK_KING; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) == 1); + + position.piece[A2] = NO_PIECE; + position.piece[H2] = WHITE_PAWN; + position.piece[B8] = NO_PIECE; + position.piece[H8] = BLACK_KING; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) > 0); + + clearPosition(&position); + + position.piece[E1] = WHITE_KING; + position.piece[E8] = BLACK_KING; + position.piece[C7] = BLACK_BISHOP; + position.piece[F7] = BLACK_BISHOP; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) > 0); + + position.piece[F7] = NO_PIECE; + position.piece[G7] = BLACK_BISHOP; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) == 1); + + position.piece[H4] = BLACK_PAWN; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) > 0); + + position.piece[E1] = NO_PIECE; + position.piece[H2] = WHITE_KING; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) == 1); + + position.piece[H4] = NO_PIECE; + position.piece[G4] = BLACK_PAWN; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) > 0); + + position.piece[G4] = NO_PIECE; + position.piece[A4] = BLACK_PAWN; + position.piece[H2] = NO_PIECE; + position.piece[A2] = WHITE_KING; + initializePosition(&position); + assert(getWinningChances(&position, BLACK) > 0); + + clearPosition(&position); + + position.piece[F1] = WHITE_KING; + position.piece[G7] = BLACK_KING; + position.piece[H2] = WHITE_PAWN; + position.piece[H3] = WHITE_PAWN; + position.piece[H4] = WHITE_PAWN; + position.piece[D5] = WHITE_BISHOP; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) == 1); + + position.piece[D5] = NO_PIECE; + position.piece[D6] = WHITE_BISHOP; + initializePosition(&position); + assert(getWinningChances(&position, WHITE) == 16); + + return 0; +} + +int testModuleEvaluation() +{ + int result; + + if ((result = testPawnInfoGeneration()) != 0) + { + return result; + } + + if ((result = testWeakPawnDetection()) != 0) + { + return result; + } + + if ((result = testBaseInitialization()) != 0) + { + return result; + } + + /* + if ((result = testFlippings()) != 0) + { + return result; + } + */ + + if ((result = testWinningChancesCalculations()) != 0) + { + return result; + } + + return 0; +}