/* CuckooChess - A java chess program. Copyright (C) 2011 Peter Ă–sterlund, peterosterlund2@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package chess; import java.io.IOException; import java.io.InputStream; /** * Position evaluation routines. * * @author petero */ public class Evaluate { static final int pV = 92 + Parameters.instance().getIntPar("pV"); static final int nV = 385 + Parameters.instance().getIntPar("nV"); static final int bV = 385 + Parameters.instance().getIntPar("bV"); static final int rV = 593 + Parameters.instance().getIntPar("rV"); static final int qV = 1244 + Parameters.instance().getIntPar("qV"); static final int kV = 9900; // Used by SEE algorithm, but not included in board material sums static final int[] pieceValue; static { // Initialize material table pieceValue = new int[Piece.nPieceTypes]; pieceValue[Piece.WKING ] = kV; pieceValue[Piece.WQUEEN ] = qV; pieceValue[Piece.WROOK ] = rV; pieceValue[Piece.WBISHOP] = bV; pieceValue[Piece.WKNIGHT] = nV; pieceValue[Piece.WPAWN ] = pV; pieceValue[Piece.BKING ] = kV; pieceValue[Piece.BQUEEN ] = qV; pieceValue[Piece.BROOK ] = rV; pieceValue[Piece.BBISHOP] = bV; pieceValue[Piece.BKNIGHT] = nV; pieceValue[Piece.BPAWN ] = pV; pieceValue[Piece.EMPTY ] = 0; } /** Piece/square table for king during middle game. */ static final int[] kt1b = { -22,-35,-40,-40,-40,-40,-35,-22, -22,-35,-40,-40,-40,-40,-35,-22, -25,-35,-40,-45,-45,-40,-35,-25, -15,-30,-35,-40,-40,-35,-30,-15, -10,-15,-20,-25,-25,-20,-15,-10, 4, -2, -5,-15,-15, -5, -2, 4, 16, 14, 7, -3, -3, 7, 14, 16, 24, 24, 9, 0, 0, 9, 24, 24 }; /** Piece/square table for king during end game. */ static final int[] kt2b = { 0, 8, 16, 24, 24, 16, 8, 0, 8, 16, 24, 32, 32, 24, 16, 8, 16, 24, 32, 40, 40, 32, 24, 16, 24, 32, 40, 48, 48, 40, 32, 24, 24, 32, 40, 48, 48, 40, 32, 24, 16, 24, 32, 40, 40, 32, 24, 16, 8, 16, 24, 32, 32, 24, 16, 8, 0, 8, 16, 24, 24, 16, 8, 0 }; /** Piece/square table for pawns during middle game. */ static final int[] pt1b = { 0, 0, 0, 0, 0, 0, 0, 0, 8, 16, 24, 32, 32, 24, 16, 8, 3, 12, 20, 28, 28, 20, 12, 3, -5, 4, 10, 20, 20, 10, 4, -5, -6, 4, 5, 16, 16, 5, 4, -6, -6, 4, 2, 5, 5, 2, 4, -6, -6, 4, 4,-15,-15, 4, 4, -6, 0, 0, 0, 0, 0, 0, 0, 0 }; /** Piece/square table for pawns during end game. */ static final int[] pt2b = { 0, 0, 0, 0, 0, 0, 0, 0, 25, 40, 45, 45, 45, 45, 40, 25, 17, 32, 35, 35, 35, 35, 32, 17, 5, 24, 24, 24, 24, 24, 24, 5, -9, 11, 11, 11, 11, 11, 11, -9, -17, 3, 3, 3, 3, 3, 3,-17, -20, 0, 0, 0, 0, 0, 0,-20, 0, 0, 0, 0, 0, 0, 0, 0 }; /** Piece/square table for knights during middle game. */ static final int[] nt1b = { -53,-42,-32,-21,-21,-32,-42,-53, -42,-32,-10, 0, 0,-10,-32,-42, -21, 5, 10, 16, 16, 10, 5,-21, -18, 0, 10, 21, 21, 10, 0,-18, -18, 0, 3, 21, 21, 3, 0,-18, -21,-10, 0, 0, 0, 0,-10,-21, -42,-32,-10, 0, 0,-10,-32,-42, -53,-42,-32,-21,-21,-32,-42,-53 }; /** Piece/square table for knights during end game. */ static final int[] nt2b = { -56,-44,-34,-22,-22,-34,-44,-56, -44,-34,-10, 0, 0,-10,-34,-44, -22, 5, 10, 17, 17, 10, 5,-22, -19, 0, 10, 22, 22, 10, 0,-19, -19, 0, 3, 22, 22, 3, 0,-19, -22,-10, 0, 0, 0, 0,-10,-22, -44,-34,-10, 0, 0,-10,-34,-44, -56,-44,-34,-22,-22,-34,-44,-56 }; /** Piece/square table for bishops during middle game. */ static final int[] bt1b = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 2, 2, 2, 2, 4, 0, 0, 2, 4, 4, 4, 4, 2, 0, 0, 2, 4, 4, 4, 4, 2, 0, 0, 2, 4, 4, 4, 4, 2, 0, 0, 3, 4, 4, 4, 4, 3, 0, 0, 4, 2, 2, 2, 2, 4, 0, -5, -5, -7, -5, -5, -7, -5, -5 }; /** Piece/square table for bishops during middle game. */ static final int[] bt2b = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 2, 4, 4, 4, 4, 2, 0, 0, 2, 4, 4, 4, 4, 2, 0, 0, 2, 4, 4, 4, 4, 2, 0, 0, 2, 4, 4, 4, 4, 2, 0, 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /** Piece/square table for queens during middle game. */ static final int[] qt1b = { -10, -5, 0, 0, 0, 0, -5,-10, -5, 0, 5, 5, 5, 5, 0, -5, 0, 5, 5, 6, 6, 5, 5, 0, 0, 5, 6, 6, 6, 6, 5, 0, 0, 5, 6, 6, 6, 6, 5, 0, 0, 5, 5, 6, 6, 5, 5, 0, -5, 0, 5, 5, 5, 5, 0, -5, -10, -5, 0, 0, 0, 0, -5,-10 }; /** Piece/square table for rooks during middle game. */ static final int[] rt1b = { 8, 11, 13, 13, 13, 13, 11, 8, 22, 27, 27, 27, 27, 27, 27, 22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0, 0, -2, -2, 0, 0, 2, 2, 0, 0, -2, -3, 2, 5, 5, 5, 5, 2, -3, 0, 3, 5, 5, 5, 5, 3, 0 }; static final int[] kt1w, qt1w, rt1w, bt1w, nt1w, pt1w, kt2w, bt2w, nt2w, pt2w; static { kt1w = new int[64]; qt1w = new int[64]; rt1w = new int[64]; bt1w = new int[64]; nt1w = new int[64]; pt1w = new int[64]; kt2w = new int[64]; bt2w = new int[64]; nt2w = new int[64]; pt2w = new int[64]; for (int i = 0; i < 64; i++) { kt1w[i] = kt1b[63-i]; qt1w[i] = qt1b[63-i]; rt1w[i] = rt1b[63-i]; bt1w[i] = bt1b[63-i]; nt1w[i] = nt1b[63-i]; pt1w[i] = pt1b[63-i]; kt2w[i] = kt2b[63-i]; bt2w[i] = bt2b[63-i]; nt2w[i] = nt2b[63-i]; pt2w[i] = pt2b[63-i]; } } private static final int[] empty = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}; static final int[][] psTab1 = { empty, kt1w, qt1w, rt1w, bt1w, nt1w, pt1w, kt1b, qt1b, rt1b, bt1b, nt1b, pt1b }; static final int[][] psTab2 = { empty, kt2w, qt1w, rt1w, bt2w, nt2w, pt2w, kt2b, qt1b, rt1b, bt2b, nt2b, pt2b }; static final int[][] distToH1A8 = { { 0, 1, 2, 3, 4, 5, 6, 7 }, { 1, 2, 3, 4, 5, 6, 7, 6 }, { 2, 3, 4, 5, 6, 7, 6, 5 }, { 3, 4, 5, 6, 7, 6, 5, 4 }, { 4, 5, 6, 7, 6, 5, 4, 3 }, { 5, 6, 7, 6, 5, 4, 3, 2 }, { 6, 7, 6, 5, 4, 3, 2, 1 }, { 7, 6, 5, 4, 3, 2, 1, 0 } }; static final int[] rookMobScore = {-10,-7,-4,-1,2,5,7,9,11,12,13,14,14,14,14}; static final int[] bishMobScore = {-15,-10,-6,-2,2,6,10,13,16,18,20,22,23,24}; static final int[] queenMobScore = {-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9,9,10,10,10,10,10,10,10,10,10,10,10,10}; private static final class PawnHashData { long key; int score; // Positive score means good for white short passedBonusW; short passedBonusB; long passedPawnsW; // The most advanced passed pawns for each file long passedPawnsB; } static final PawnHashData[] pawnHash; static { final int numEntries = 1<<16; pawnHash = new PawnHashData[numEntries]; for (int i = 0; i < numEntries; i++) { PawnHashData phd = new PawnHashData(); phd.key = -1; // Non-zero to avoid collision for positions with no pawns phd.score = 0; pawnHash[i] = phd; } } static byte[] kpkTable = null; static byte[] krkpTable = null; // King safety variables private long wKingZone, bKingZone; // Squares close to king that are worth attacking private int wKingAttacks, bKingAttacks; // Number of attacks close to white/black king private long wAttacksBB, bAttacksBB; private long wPawnAttacks, bPawnAttacks; // Squares attacked by white/black pawns /** Constructor. */ public Evaluate() { if (kpkTable == null) kpkTable = readTable("/kpk.bitbase", 2*32*64*48/8); if (krkpTable == null) krkpTable = readTable("/krkp.winmasks", 2*32*48*8); } private byte[] readTable(String resource, int length) { byte[] table = new byte[2*32*64*48/8]; InputStream inStream = getClass().getResourceAsStream(resource); try { int off = 0; while (off < table.length) { int len = inStream.read(table, off, table.length - off); if (len < 0) throw new RuntimeException(); off += len; } inStream.close(); return table; } catch (IOException e) { throw new RuntimeException(); } } /** * Static evaluation of a position. * @param pos The position to evaluate. * @return The evaluation score, measured in centipawns. * Positive values are good for the side to make the next move. */ final public int evalPos(Position pos) { int score = pos.wMtrl - pos.bMtrl; wKingAttacks = bKingAttacks = 0; wKingZone = BitBoard.kingAttacks[pos.getKingSq(true)]; wKingZone |= wKingZone << 8; bKingZone = BitBoard.kingAttacks[pos.getKingSq(false)]; bKingZone |= bKingZone >>> 8; wAttacksBB = bAttacksBB = 0L; long pawns = pos.pieceTypeBB[Piece.WPAWN]; wPawnAttacks = ((pawns & BitBoard.maskBToHFiles) << 7) | ((pawns & BitBoard.maskAToGFiles) << 9); pawns = pos.pieceTypeBB[Piece.BPAWN]; bPawnAttacks = ((pawns & BitBoard.maskBToHFiles) >>> 9) | ((pawns & BitBoard.maskAToGFiles) >>> 7); score += pieceSquareEval(pos); score += pawnBonus(pos); score += tradeBonus(pos); score += castleBonus(pos); score += rookBonus(pos); score += bishopEval(pos, score); score += threatBonus(pos); score += kingSafety(pos); score = endGameEval(pos, score); if (!pos.whiteMove) score = -score; return score; // FIXME! Test penalty if side to move has >1 hanging piece // FIXME! Test "tempo value" } /** Compute white_material - black_material. */ static final int material(Position pos) { return pos.wMtrl - pos.bMtrl; } /** Compute score based on piece square tables. Positive values are good for white. */ private final int pieceSquareEval(Position pos) { int score = 0; final int wMtrl = pos.wMtrl; final int bMtrl = pos.bMtrl; final int wMtrlPawns = pos.wMtrlPawns; final int bMtrlPawns = pos.bMtrlPawns; // Kings { final int t1 = qV + 2 * rV + 2 * bV; final int t2 = rV; { final int k1 = pos.psScore1[Piece.WKING]; final int k2 = pos.psScore2[Piece.WKING]; final int t = bMtrl - bMtrlPawns; score += interpolate(t, t2, k2, t1, k1); } { final int k1 = pos.psScore1[Piece.BKING]; final int k2 = pos.psScore2[Piece.BKING]; final int t = wMtrl - wMtrlPawns; score -= interpolate(t, t2, k2, t1, k1); } } // Pawns { final int t1 = qV + 2 * rV + 2 * bV; final int t2 = rV; int wp1 = pos.psScore1[Piece.WPAWN]; int wp2 = pos.psScore2[Piece.WPAWN]; if ((wp1 != 0) || (wp2 != 0)) { final int tw = bMtrl - bMtrlPawns; score += interpolate(tw, t2, wp2, t1, wp1); } int bp1 = pos.psScore1[Piece.BPAWN]; int bp2 = pos.psScore2[Piece.BPAWN]; if ((bp1 != 0) || (bp2 != 0)) { final int tb = wMtrl - wMtrlPawns; score -= interpolate(tb, t2, bp2, t1, bp1); } } // Knights { final int t1 = qV + 2 * rV + 1 * bV + 1 * nV + 6 * pV; final int t2 = nV + 8 * pV; int n1 = pos.psScore1[Piece.WKNIGHT]; int n2 = pos.psScore2[Piece.WKNIGHT]; if ((n1 != 0) || (n2 != 0)) { score += interpolate(bMtrl, t2, n2, t1, n1); } n1 = pos.psScore1[Piece.BKNIGHT]; n2 = pos.psScore2[Piece.BKNIGHT]; if ((n1 != 0) || (n2 != 0)) { score -= interpolate(wMtrl, t2, n2, t1, n1); } } // Bishops { score += pos.psScore1[Piece.WBISHOP]; score -= pos.psScore1[Piece.BBISHOP]; } // Queens { final long occupied = pos.whiteBB | pos.blackBB; score += pos.psScore1[Piece.WQUEEN]; long m = pos.pieceTypeBB[Piece.WQUEEN]; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); long atk = BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied); wAttacksBB |= atk; score += queenMobScore[Long.bitCount(atk & ~(pos.whiteBB | bPawnAttacks))]; bKingAttacks += Long.bitCount(atk & bKingZone) * 2; m &= m-1; } score -= pos.psScore1[Piece.BQUEEN]; m = pos.pieceTypeBB[Piece.BQUEEN]; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); long atk = BitBoard.rookAttacks(sq, occupied) | BitBoard.bishopAttacks(sq, occupied); bAttacksBB |= atk; score -= queenMobScore[Long.bitCount(atk & ~(pos.blackBB | wPawnAttacks))]; wKingAttacks += Long.bitCount(atk & wKingZone) * 2; m &= m-1; } } // Rooks { int r1 = pos.psScore1[Piece.WROOK]; if (r1 != 0) { final int nP = bMtrlPawns / pV; final int s = r1 * Math.min(nP, 6) / 6; score += s; } r1 = pos.psScore1[Piece.BROOK]; if (r1 != 0) { final int nP = wMtrlPawns / pV; final int s = r1 * Math.min(nP, 6) / 6; score -= s; } } return score; } /** Implement the "when ahead trade pieces, when behind trade pawns" rule. */ private final int tradeBonus(Position pos) { final int wM = pos.wMtrl; final int bM = pos.bMtrl; final int wPawn = pos.wMtrlPawns; final int bPawn = pos.bMtrlPawns; final int deltaScore = wM - bM; int pBonus = 0; pBonus += interpolate((deltaScore > 0) ? wPawn : bPawn, 0, -30 * deltaScore / 100, 6 * pV, 0); pBonus += interpolate((deltaScore > 0) ? bM : wM, 0, 30 * deltaScore / 100, qV + 2 * rV + 2 * bV + 2 * nV, 0); return pBonus; } private static final int[] castleFactor; static { castleFactor = new int[256]; for (int i = 0; i < 256; i++) { int h1Dist = 100; boolean h1Castle = (i & (1<<7)) != 0; if (h1Castle) h1Dist = 2 + Long.bitCount(i & 0x0000000000000060L); // f1,g1 int a1Dist = 100; boolean a1Castle = (i & 1) != 0; if (a1Castle) a1Dist = 2 + Long.bitCount(i & 0x000000000000000EL); // b1,c1,d1 castleFactor[i] = 1024 / Math.min(a1Dist, h1Dist); } } /** Score castling ability. */ private final int castleBonus(Position pos) { if (pos.getCastleMask() == 0) return 0; final int k1 = kt1b[7*8+6] - kt1b[7*8+4]; final int k2 = kt2b[7*8+6] - kt2b[7*8+4]; final int t1 = qV + 2 * rV + 2 * bV; final int t2 = rV; final int t = pos.bMtrl - pos.bMtrlPawns; final int ks = interpolate(t, t2, k2, t1, k1); final int castleValue = ks + rt1b[7*8+5] - rt1b[7*8+7]; if (castleValue <= 0) return 0; long occupied = pos.whiteBB | pos.blackBB; int tmp = (int) (occupied & 0x6E); if (pos.a1Castle()) tmp |= 1; if (pos.h1Castle()) tmp |= (1 << 7); final int wBonus = (castleValue * castleFactor[tmp]) >> 10; tmp = (int) ((occupied >>> 56) & 0x6E); if (pos.a8Castle()) tmp |= 1; if (pos.h8Castle()) tmp |= (1 << 7); final int bBonus = (castleValue * castleFactor[tmp]) >> 10; return wBonus - bBonus; } private final int pawnBonus(Position pos) { long key = pos.pawnZobristHash(); PawnHashData phd = pawnHash[(int)key & (pawnHash.length - 1)]; if (phd.key != key) computePawnHashData(pos, phd); int score = phd.score; final int hiMtrl = qV + rV; score += interpolate(pos.bMtrl - pos.bMtrlPawns, 0, 2 * phd.passedBonusW, hiMtrl, phd.passedBonusW); score -= interpolate(pos.wMtrl - pos.wMtrlPawns, 0, 2 * phd.passedBonusB, hiMtrl, phd.passedBonusB); // Passed pawns are more dangerous if enemy king is far away int bestWPawnDist = 8; int bestWPromSq = -1; long m = phd.passedPawnsW; if (m != 0) { int mtrlNoPawns = pos.bMtrl - pos.bMtrlPawns; if (mtrlNoPawns < hiMtrl) { int kingPos = pos.getKingSq(false); while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); int x = Position.getX(sq); int y = Position.getY(sq); int pawnDist = Math.min(5, 7 - y); int kingDist = BitBoard.getDistance(kingPos, Position.getSquare(x, 7)); int kScore = kingDist * 4; if (kingDist > pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist); score += interpolate(mtrlNoPawns, 0, kScore, hiMtrl, 0); if (!pos.whiteMove) kingDist--; if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) { if ((BitBoard.northFill(1L<<sq) & (1L << pos.getKingSq(true))) != 0) pawnDist++; // Own king blocking pawn if (pawnDist < bestWPawnDist) { bestWPawnDist = pawnDist; bestWPromSq = Position.getSquare(x, 7); } } m &= m-1; } } } int bestBPawnDist = 8; int bestBPromSq = -1; m = phd.passedPawnsB; if (m != 0) { int mtrlNoPawns = pos.wMtrl - pos.wMtrlPawns; if (mtrlNoPawns < hiMtrl) { int kingPos = pos.getKingSq(true); while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); int x = Position.getX(sq); int y = Position.getY(sq); int pawnDist = Math.min(5, y); int kingDist = BitBoard.getDistance(kingPos, Position.getSquare(x, 0)); int kScore = kingDist * 4; if (kingDist > pawnDist) kScore += (kingDist - pawnDist) * (kingDist - pawnDist); score -= interpolate(mtrlNoPawns, 0, kScore, hiMtrl, 0); if (pos.whiteMove) kingDist--; if ((pawnDist < kingDist) && (mtrlNoPawns == 0)) { if ((BitBoard.southFill(1L<<sq) & (1L << pos.getKingSq(false))) != 0) pawnDist++; // Own king blocking pawn if (pawnDist < bestBPawnDist) { bestBPawnDist = pawnDist; bestBPromSq = Position.getSquare(x, 0); } } m &= m-1; } } } // Evaluate pawn races in pawn end games if (bestWPromSq >= 0) { if (bestBPromSq >= 0) { int wPly = bestWPawnDist * 2; if (pos.whiteMove) wPly--; int bPly = bestBPawnDist * 2; if (!pos.whiteMove) bPly--; if (wPly < bPly - 1) { score += 500; } else if (wPly == bPly - 1) { if (BitBoard.getDirection(bestWPromSq, pos.getKingSq(false)) != 0) score += 500; } else if (wPly == bPly + 1) { if (BitBoard.getDirection(bestBPromSq, pos.getKingSq(true)) != 0) score -= 500; } else { score -= 500; } } else score += 500; } else if (bestBPromSq >= 0) score -= 500; return score; } /** Compute pawn hash data for pos. */ private final void computePawnHashData(Position pos, PawnHashData ph) { int score = 0; // Evaluate double pawns and pawn islands long wPawns = pos.pieceTypeBB[Piece.WPAWN]; long wPawnFiles = BitBoard.southFill(wPawns) & 0xff; int wDouble = Long.bitCount(wPawns) - Long.bitCount(wPawnFiles); int wIslands = Long.bitCount(((~wPawnFiles) >>> 1) & wPawnFiles); int wIsolated = Long.bitCount(~(wPawnFiles<<1) & wPawnFiles & ~(wPawnFiles>>>1)); long bPawns = pos.pieceTypeBB[Piece.BPAWN]; long bPawnFiles = BitBoard.southFill(bPawns) & 0xff; int bDouble = Long.bitCount(bPawns) - Long.bitCount(bPawnFiles); int bIslands = Long.bitCount(((~bPawnFiles) >>> 1) & bPawnFiles); int bIsolated = Long.bitCount(~(bPawnFiles<<1) & bPawnFiles & ~(bPawnFiles>>>1)); score -= (wDouble - bDouble) * 25; score -= (wIslands - bIslands) * 15; score -= (wIsolated - bIsolated) * 15; // Evaluate backward pawns, defined as a pawn that guards a friendly pawn, // can't be guarded by friendly pawns, can advance, but can't advance without // being captured by an enemy pawn. long wPawnAttacks = (((wPawns & BitBoard.maskBToHFiles) << 7) | ((wPawns & BitBoard.maskAToGFiles) << 9)); long bPawnAttacks = (((bPawns & BitBoard.maskBToHFiles) >>> 9) | ((bPawns & BitBoard.maskAToGFiles) >>> 7)); long wBackward = wPawns & ~((wPawns | bPawns) >>> 8) & (bPawnAttacks >>> 8) & ~BitBoard.northFill(wPawnAttacks); wBackward &= (((wPawns & BitBoard.maskBToHFiles) >>> 9) | ((wPawns & BitBoard.maskAToGFiles) >>> 7)); wBackward &= ~BitBoard.northFill(bPawnFiles); long bBackward = bPawns & ~((wPawns | bPawns) << 8) & (wPawnAttacks << 8) & ~BitBoard.southFill(bPawnAttacks); bBackward &= (((bPawns & BitBoard.maskBToHFiles) << 7) | ((bPawns & BitBoard.maskAToGFiles) << 9)); bBackward &= ~BitBoard.northFill(wPawnFiles); score -= (Long.bitCount(wBackward) - Long.bitCount(bBackward)) * 15; // Evaluate passed pawn bonus, white long passedPawnsW = wPawns & ~BitBoard.southFill(bPawns | bPawnAttacks | (wPawns >>> 8)); final int[] ppBonus = {-1,24,26,30,36,55,100,-1}; int passedBonusW = 0; if (passedPawnsW != 0) { long guardedPassedW = passedPawnsW & (((wPawns & BitBoard.maskBToHFiles) << 7) | ((wPawns & BitBoard.maskAToGFiles) << 9)); passedBonusW += 15 * Long.bitCount(guardedPassedW); long m = passedPawnsW; while (m != 0) { int sq = Long .numberOfTrailingZeros(m); int y = Position.getY(sq); passedBonusW += ppBonus[y]; m &= m-1; } } // Evaluate passed pawn bonus, black long passedPawnsB = bPawns & ~BitBoard.northFill(wPawns | wPawnAttacks | (bPawns << 8)); int passedBonusB = 0; if (passedPawnsB != 0) { long guardedPassedB = passedPawnsB & (((bPawns & BitBoard.maskBToHFiles) >>> 9) | ((bPawns & BitBoard.maskAToGFiles) >>> 7)); passedBonusB += 15 * Long.bitCount(guardedPassedB); long m = passedPawnsB; while (m != 0) { int sq = Long .numberOfTrailingZeros(m); int y = Position.getY(sq); passedBonusB += ppBonus[7-y]; m &= m-1; } } // Connected passed pawn bonus. Seems logical but scored -8 elo in tests // if (passedPawnsW != 0) { // long mask = passedPawnsW; // mask = (((mask >> 7) | (mask << 1) | (mask << 9)) & BitBoard.maskBToHFiles) | // (((mask >> 9) | (mask >> 1) | (mask << 7)) & BitBoard.maskAToGFiles); // passedBonusW += 13 * Long.bitCount(passedPawnsW & mask); // } // if (passedPawnsB != 0) { // long mask = passedPawnsB; // mask = (((mask >> 7) | (mask << 1) | (mask << 9)) & BitBoard.maskBToHFiles) | // (((mask >> 9) | (mask >> 1) | (mask << 7)) & BitBoard.maskAToGFiles); // passedBonusB += 13 * Long.bitCount(passedPawnsB & mask); // } ph.key = pos.pawnZobristHash(); ph.score = score; ph.passedBonusW = (short)passedBonusW; ph.passedBonusB = (short)passedBonusB; ph.passedPawnsW = passedPawnsW; ph.passedPawnsB = passedPawnsB; } /** Compute rook bonus. Rook on open/half-open file. */ private final int rookBonus(Position pos) { int score = 0; final long wPawns = pos.pieceTypeBB[Piece.WPAWN]; final long bPawns = pos.pieceTypeBB[Piece.BPAWN]; final long occupied = pos.whiteBB | pos.blackBB; long m = pos.pieceTypeBB[Piece.WROOK]; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); final int x = Position.getX(sq); if ((wPawns & BitBoard.maskFile[x]) == 0) { // At least half-open file score += (bPawns & BitBoard.maskFile[x]) == 0 ? 25 : 12; } long atk = BitBoard.rookAttacks(sq, occupied); wAttacksBB |= atk; score += rookMobScore[Long.bitCount(atk & ~(pos.whiteBB | bPawnAttacks))]; if ((atk & bKingZone) != 0) bKingAttacks += Long.bitCount(atk & bKingZone); m &= m-1; } long r7 = (pos.pieceTypeBB[Piece.WROOK] >>> 48) & 0x00ffL; if (((r7 & (r7 - 1)) != 0) && ((pos.pieceTypeBB[Piece.BKING] & 0xff00000000000000L) != 0)) score += 30; // Two rooks on 7:th row m = pos.pieceTypeBB[Piece.BROOK]; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); final int x = Position.getX(sq); if ((bPawns & BitBoard.maskFile[x]) == 0) { score -= (wPawns & BitBoard.maskFile[x]) == 0 ? 25 : 12; } long atk = BitBoard.rookAttacks(sq, occupied); bAttacksBB |= atk; score -= rookMobScore[Long.bitCount(atk & ~(pos.blackBB | wPawnAttacks))]; if ((atk & wKingZone) != 0) wKingAttacks += Long.bitCount(atk & wKingZone); m &= m-1; } r7 = pos.pieceTypeBB[Piece.BROOK] & 0xff00L; if (((r7 & (r7 - 1)) != 0) && ((pos.pieceTypeBB[Piece.WKING] & 0xffL) != 0)) score -= 30; // Two rooks on 2:nd row return score; } /** Compute bishop evaluation. */ private final int bishopEval(Position pos, int oldScore) { int score = 0; final long occupied = pos.whiteBB | pos.blackBB; long wBishops = pos.pieceTypeBB[Piece.WBISHOP]; long bBishops = pos.pieceTypeBB[Piece.BBISHOP]; if ((wBishops | bBishops) == 0) return 0; long m = wBishops; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); long atk = BitBoard.bishopAttacks(sq, occupied); wAttacksBB |= atk; score += bishMobScore[Long.bitCount(atk & ~(pos.whiteBB | bPawnAttacks))]; if ((atk & bKingZone) != 0) bKingAttacks += Long.bitCount(atk & bKingZone); m &= m-1; } m = bBishops; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); long atk = BitBoard.bishopAttacks(sq, occupied); bAttacksBB |= atk; score -= bishMobScore[Long.bitCount(atk & ~(pos.blackBB | wPawnAttacks))]; if ((atk & wKingZone) != 0) wKingAttacks += Long.bitCount(atk & wKingZone); m &= m-1; } boolean whiteDark = (pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq ) != 0; boolean whiteLight = (pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskLightSq) != 0; boolean blackDark = (pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq ) != 0; boolean blackLight = (pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskLightSq) != 0; int numWhite = (whiteDark ? 1 : 0) + (whiteLight ? 1 : 0); int numBlack = (blackDark ? 1 : 0) + (blackLight ? 1 : 0); // Bishop pair bonus if (numWhite == 2) { final int numPawns = pos.wMtrlPawns / pV; score += 28 + (8 - numPawns) * 3; } if (numBlack == 2) { final int numPawns = pos.bMtrlPawns / pV; score -= 28 + (8 - numPawns) * 3; } // FIXME!!! Bad bishop if ((numWhite == 1) && (numBlack == 1) && (whiteDark != blackDark) && (pos.wMtrl - pos.wMtrlPawns == pos.bMtrl - pos.bMtrlPawns)) { final int penalty = (oldScore + score) / 2; final int loMtrl = 2 * bV; final int hiMtrl = 2 * (qV + rV + bV); int mtrl = pos.wMtrl + pos.bMtrl - pos.wMtrlPawns - pos.bMtrlPawns; score -= interpolate(mtrl, loMtrl, penalty, hiMtrl, 0); } // Penalty for bishop trapped behind pawn at a2/h2/a7/h7 if (((wBishops | bBishops) & 0x0081000000008100L) != 0) { if ((pos.squares[48] == Piece.WBISHOP) && // a7 (pos.squares[41] == Piece.BPAWN) && (pos.squares[50] == Piece.BPAWN)) score -= pV * 3 / 2; if ((pos.squares[55] == Piece.WBISHOP) && // h7 (pos.squares[46] == Piece.BPAWN) && (pos.squares[53] == Piece.BPAWN)) score -= (pos.pieceTypeBB[Piece.WQUEEN] != 0) ? pV : pV * 3 / 2; if ((pos.squares[8] == Piece.BBISHOP) && // a2 (pos.squares[17] == Piece.WPAWN) && (pos.squares[10] == Piece.WPAWN)) score += pV * 3 / 2; if ((pos.squares[15] == Piece.BBISHOP) && // h2 (pos.squares[22] == Piece.WPAWN) && (pos.squares[13] == Piece.WPAWN)) score += (pos.pieceTypeBB[Piece.BQUEEN] != 0) ? pV : pV * 3 / 2; } return score; } private int threatBonus(Position pos) { // FIXME!! Try higher weight for attacks on more valuable pieces. int score = 0; // Sum values for all black pieces under attack long m = pos.pieceTypeBB[Piece.WKNIGHT]; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); wAttacksBB |= BitBoard.knightAttacks[sq]; m &= m-1; } wAttacksBB &= (pos.pieceTypeBB[Piece.BKNIGHT] | pos.pieceTypeBB[Piece.BBISHOP] | pos.pieceTypeBB[Piece.BROOK] | pos.pieceTypeBB[Piece.BQUEEN]); wAttacksBB |= wPawnAttacks; m = wAttacksBB & pos.blackBB & ~pos.pieceTypeBB[Piece.BKING]; int tmp = 0; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); tmp += pieceValue[pos.squares[sq]]; m &= m-1; } score += tmp + tmp * tmp / qV; // Sum values for all white pieces under attack m = pos.pieceTypeBB[Piece.BKNIGHT]; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); bAttacksBB |= BitBoard.knightAttacks[sq]; m &= m-1; } bAttacksBB &= (pos.pieceTypeBB[Piece.WKNIGHT] | pos.pieceTypeBB[Piece.WBISHOP] | pos.pieceTypeBB[Piece.WROOK] | pos.pieceTypeBB[Piece.WQUEEN]); bAttacksBB |= bPawnAttacks; m = bAttacksBB & pos.whiteBB & ~pos.pieceTypeBB[Piece.WKING]; tmp = 0; while (m != 0) { int sq = BitBoard.numberOfTrailingZeros(m); tmp += pieceValue[pos.squares[sq]]; m &= m-1; } score -= tmp + tmp * tmp / qV; return score / 64; } /** Compute king safety for both kings. */ private final int kingSafety(Position pos) { final int minM = rV + bV; final int m = (pos.wMtrl - pos.wMtrlPawns + pos.bMtrl - pos.bMtrlPawns) / 2; if (m <= minM) return 0; final int maxM = qV + 2 * rV + 2 * bV + 2 * nV; int score = kingSafetyKPPart(pos); if (Position.getY(pos.wKingSq) == 0) { if (((pos.pieceTypeBB[Piece.WKING] & 0x60L) != 0) && // King on f1 or g1 ((pos.pieceTypeBB[Piece.WROOK] & 0xC0L) != 0) && // Rook on g1 or h1 ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[6]) != 0) && ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[7]) != 0)) { score -= 6 * 15; } else if (((pos.pieceTypeBB[Piece.WKING] & 0x6L) != 0) && // King on b1 or c1 ((pos.pieceTypeBB[Piece.WROOK] & 0x3L) != 0) && // Rook on a1 or b1 ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[0]) != 0) && ((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskFile[1]) != 0)) { score -= 6 * 15; } } if (Position.getY(pos.bKingSq) == 7) { if (((pos.pieceTypeBB[Piece.BKING] & 0x6000000000000000L) != 0) && // King on f8 or g8 ((pos.pieceTypeBB[Piece.BROOK] & 0xC000000000000000L) != 0) && // Rook on g8 or h8 ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[6]) != 0) && ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[7]) != 0)) { score += 6 * 15; } else if (((pos.pieceTypeBB[Piece.BKING] & 0x600000000000000L) != 0) && // King on b8 or c8 ((pos.pieceTypeBB[Piece.BROOK] & 0x300000000000000L) != 0) && // Rook on a8 or b8 ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[0]) != 0) && ((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskFile[1]) != 0)) { score += 6 * 15; } } score += (bKingAttacks - wKingAttacks) * 4; final int kSafety = interpolate(m, minM, 0, maxM, score); return kSafety; } private static final class KingSafetyHashData { long key; int score; } private static final KingSafetyHashData[] kingSafetyHash; static { final int numEntries = 1 << 15; kingSafetyHash = new KingSafetyHashData[numEntries]; for (int i = 0; i < numEntries; i++) { KingSafetyHashData ksh = new KingSafetyHashData(); ksh.key = -1; ksh.score = 0; kingSafetyHash[i] = ksh; } } private final int kingSafetyKPPart(Position pos) { // FIXME!!! Try non-linear king safety final long key = pos.pawnZobristHash() ^ pos.kingZobristHash(); KingSafetyHashData ksh = kingSafetyHash[(int)key & (kingSafetyHash.length - 1)]; if (ksh.key != key) { int score = 0; long wPawns = pos.pieceTypeBB[Piece.WPAWN]; long bPawns = pos.pieceTypeBB[Piece.BPAWN]; { int safety = 0; int halfOpenFiles = 0; if (Position.getY(pos.wKingSq) < 2) { long shelter = 1L << Position.getX(pos.wKingSq); shelter |= ((shelter & BitBoard.maskBToHFiles) >>> 1) | ((shelter & BitBoard.maskAToGFiles) << 1); shelter <<= 8; safety += 3 * Long.bitCount(wPawns & shelter); safety -= 2 * Long.bitCount(bPawns & (shelter | (shelter << 8))); shelter <<= 8; safety += 2 * Long.bitCount(wPawns & shelter); shelter <<= 8; safety -= Long.bitCount(bPawns & shelter); long wOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(wPawns)) & 0xff; if (wOpen != 0) { halfOpenFiles += 25 * Long.bitCount(wOpen & 0xe7); halfOpenFiles += 10 * Long.bitCount(wOpen & 0x18); } long bOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(bPawns)) & 0xff; if (bOpen != 0) { halfOpenFiles += 25 * Long.bitCount(bOpen & 0xe7); halfOpenFiles += 10 * Long.bitCount(bOpen & 0x18); } safety = Math.min(safety, 8); } final int kSafety = (safety - 9) * 15 - halfOpenFiles; score += kSafety; } { int safety = 0; int halfOpenFiles = 0; if (Position.getY(pos.bKingSq) >= 6) { long shelter = 1L << (56 + Position.getX(pos.bKingSq)); shelter |= ((shelter & BitBoard.maskBToHFiles) >>> 1) | ((shelter & BitBoard.maskAToGFiles) << 1); shelter >>>= 8; safety += 3 * Long.bitCount(bPawns & shelter); safety -= 2 * Long.bitCount(wPawns & (shelter | (shelter >>> 8))); shelter >>>= 8; safety += 2 * Long.bitCount(bPawns & shelter); shelter >>>= 8; safety -= Long.bitCount(wPawns & shelter); long wOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(wPawns)) & 0xff; if (wOpen != 0) { halfOpenFiles += 25 * Long.bitCount(wOpen & 0xe7); halfOpenFiles += 10 * Long.bitCount(wOpen & 0x18); } long bOpen = BitBoard.southFill(shelter) & (~BitBoard.southFill(bPawns)) & 0xff; if (bOpen != 0) { halfOpenFiles += 25 * Long.bitCount(bOpen & 0xe7); halfOpenFiles += 10 * Long.bitCount(bOpen & 0x18); } safety = Math.min(safety, 8); } final int kSafety = (safety - 9) * 15 - halfOpenFiles; score -= kSafety; } ksh.key = key; ksh.score = score; } return ksh.score; } /** Implements special knowledge for some endgame situations. */ private final int endGameEval(Position pos, int oldScore) { int score = oldScore; if (pos.wMtrl + pos.bMtrl > 6 * rV) return score; final int wMtrlPawns = pos.wMtrlPawns; final int bMtrlPawns = pos.bMtrlPawns; final int wMtrlNoPawns = pos.wMtrl - wMtrlPawns; final int bMtrlNoPawns = pos.bMtrl - bMtrlPawns; boolean handled = false; if ((wMtrlPawns + bMtrlPawns == 0) && (wMtrlNoPawns < rV) && (bMtrlNoPawns < rV)) { // King + minor piece vs king + minor piece is a draw return 0; } if (!handled && (pos.wMtrl == qV) && (pos.bMtrl == pV) && (pos.pieceTypeBB[Piece.WQUEEN] != 0)) { int wk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WKING]); int wq = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WQUEEN]); int bk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BKING]); int bp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BPAWN]); score = evalKQKP(wk, wq, bk, bp, pos.whiteMove); handled = true; } if (!handled && (pos.wMtrl == rV) && (pos.pieceTypeBB[Piece.WROOK] != 0)) { if (pos.bMtrl == pV) { int bp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BPAWN]); score = krkpEval(pos.getKingSq(true), pos.getKingSq(false), bp, pos.whiteMove); handled = true; } else if ((pos.bMtrl == bV) && (pos.pieceTypeBB[Piece.BBISHOP] != 0)) { score /= 8; final int kSq = pos.getKingSq(false); final int x = Position.getX(kSq); final int y = Position.getY(kSq); if ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq) != 0) { score += (7 - distToH1A8[7-y][7-x]) * 7; } else { score += (7 - distToH1A8[7-y][x]) * 7; } handled = true; } } if (!handled && (pos.bMtrl == qV) && (pos.wMtrl == pV) && (pos.pieceTypeBB[Piece.BQUEEN] != 0)) { int bk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BKING]); int bq = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BQUEEN]); int wk = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WKING]); int wp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WPAWN]); score = -evalKQKP(63-bk, 63-bq, 63-wk, 63-wp, !pos.whiteMove); handled = true; } if (!handled && (pos.bMtrl == rV) && (pos.pieceTypeBB[Piece.BROOK] != 0)) { if (pos.wMtrl == pV) { int wp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WPAWN]); score = -krkpEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), 63-wp, !pos.whiteMove); handled = true; } else if ((pos.wMtrl == bV) && (pos.pieceTypeBB[Piece.WBISHOP] != 0)) { score /= 8; final int kSq = pos.getKingSq(true); final int x = Position.getX(kSq); final int y = Position.getY(kSq); if ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq) != 0) { score -= (7 - distToH1A8[7-y][7-x]) * 7; } else { score -= (7 - distToH1A8[7-y][x]) * 7; } handled = true; } } if (!handled && (score > 0)) { if ((wMtrlPawns == 0) && (wMtrlNoPawns <= bMtrlNoPawns + bV)) { if (wMtrlNoPawns < rV) { return -pos.bMtrl / 50; } else { score /= 8; // Too little excess material, probably draw handled = true; } } else if ((pos.pieceTypeBB[Piece.WROOK] | pos.pieceTypeBB[Piece.WKNIGHT] | pos.pieceTypeBB[Piece.WQUEEN]) == 0) { // Check for rook pawn + wrong color bishop if (((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskBToHFiles) == 0) && ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskLightSq) == 0) && ((pos.pieceTypeBB[Piece.BKING] & 0x0303000000000000L) != 0)) { return 0; } else if (((pos.pieceTypeBB[Piece.WPAWN] & BitBoard.maskAToGFiles) == 0) && ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq) == 0) && ((pos.pieceTypeBB[Piece.BKING] & 0xC0C0000000000000L) != 0)) { return 0; } } } if (!handled) { if (bMtrlPawns == 0) { if (wMtrlNoPawns - bMtrlNoPawns > bV) { int wKnights = Long.bitCount(pos.pieceTypeBB[Piece.WKNIGHT]); int wBishops = Long.bitCount(pos.pieceTypeBB[Piece.WBISHOP]); if ((wKnights == 2) && (pos.wMtrl == 2 * nV) && (bMtrlNoPawns == 0)) { score /= 50; // KNNK is a draw } else if ((wKnights == 1) && (wBishops == 1) && (wMtrlNoPawns == nV + bV) && (bMtrlNoPawns == 0)) { score /= 10; score += nV + bV + 300; final int kSq = pos.getKingSq(false); final int x = Position.getX(kSq); final int y = Position.getY(kSq); if ((pos.pieceTypeBB[Piece.WBISHOP] & BitBoard.maskDarkSq) != 0) { score += (7 - distToH1A8[7-y][7-x]) * 10; } else { score += (7 - distToH1A8[7-y][x]) * 10; } } else { score += 300; // Enough excess material, should win } handled = true; } else if ((wMtrlNoPawns + bMtrlNoPawns == 0) && (wMtrlPawns == pV)) { // KPK int wp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.WPAWN]); score = kpkEval(pos.getKingSq(true), pos.getKingSq(false), wp, pos.whiteMove); handled = true; } } } if (!handled && (score < 0)) { if ((bMtrlPawns == 0) && (bMtrlNoPawns <= wMtrlNoPawns + bV)) { if (bMtrlNoPawns < rV) { return pos.wMtrl / 50; } else { score /= 8; // Too little excess material, probably draw handled = true; } } else if ((pos.pieceTypeBB[Piece.BROOK] | pos.pieceTypeBB[Piece.BKNIGHT] | pos.pieceTypeBB[Piece.BQUEEN]) == 0) { // Check for rook pawn + wrong color bishop if (((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskBToHFiles) == 0) && ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq) == 0) && ((pos.pieceTypeBB[Piece.WKING] & 0x0303L) != 0)) { return 0; } else if (((pos.pieceTypeBB[Piece.BPAWN] & BitBoard.maskAToGFiles) == 0) && ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskLightSq) == 0) && ((pos.pieceTypeBB[Piece.WKING] & 0xC0C0L) != 0)) { return 0; } } } if (!handled) { if (wMtrlPawns == 0) { if (bMtrlNoPawns - wMtrlNoPawns > bV) { int bKnights = Long.bitCount(pos.pieceTypeBB[Piece.BKNIGHT]); int bBishops = Long.bitCount(pos.pieceTypeBB[Piece.BBISHOP]); if ((bKnights == 2) && (pos.bMtrl == 2 * nV) && (wMtrlNoPawns == 0)) { score /= 50; // KNNK is a draw } else if ((bKnights == 1) && (bBishops == 1) && (bMtrlNoPawns == nV + bV) && (wMtrlNoPawns == 0)) { score /= 10; score -= nV + bV + 300; final int kSq = pos.getKingSq(true); final int x = Position.getX(kSq); final int y = Position.getY(kSq); if ((pos.pieceTypeBB[Piece.BBISHOP] & BitBoard.maskDarkSq) != 0) { score -= (7 - distToH1A8[7-y][7-x]) * 10; } else { score -= (7 - distToH1A8[7-y][x]) * 10; } } else { score -= 300; // Enough excess material, should win } handled = true; } else if ((wMtrlNoPawns + bMtrlNoPawns == 0) && (bMtrlPawns == pV)) { // KPK int bp = BitBoard.numberOfTrailingZeros(pos.pieceTypeBB[Piece.BPAWN]); score = -kpkEval(63-pos.getKingSq(false), 63-pos.getKingSq(true), 63-bp, !pos.whiteMove); handled = true; } } } return score; // FIXME! Add evaluation of KRPKR : eg 8/8/8/5pk1/1r6/R7/8/4K3 w - - 0 74 // FIXME! KRBKR is very hard to draw } private static final int evalKQKP(int wKing, int wQueen, int bKing, int bPawn, boolean whiteMove) { boolean canWin = false; if (((1L << bKing) & 0xFFFF) == 0) { canWin = true; // King doesn't support pawn } else if (Math.abs(Position.getX(bPawn) - Position.getX(bKing)) > 2) { canWin = true; // King doesn't support pawn } else { switch (bPawn) { case 8: // a2 canWin = ((1L << wKing) & 0x0F1F1F1F1FL) != 0; if (canWin && (bKing == 0) && (Position.getX(wQueen) == 1) && !whiteMove) canWin = false; // Stale-mate break; case 10: // c2 canWin = ((1L << wKing) & 0x071F1F1FL) != 0; break; case 13: // f2 canWin = ((1L << wKing) & 0xE0F8F8F8L) != 0; break; case 15: // h2 canWin = ((1L << wKing) & 0xF0F8F8F8F8L) != 0; if (canWin && (bKing == 7) && (Position.getX(wQueen) == 6) && !whiteMove) canWin = false; // Stale-mate break; default: canWin = true; break; } } final int dist = BitBoard.getDistance(wKing, bPawn); int score = qV - pV - 20 * dist; if (!canWin) score /= 50; return score; } private static final int kpkEval(int wKing, int bKing, int wPawn, boolean whiteMove) { if (Position.getX(wKing) >= 4) { // Mirror X wKing ^= 7; bKing ^= 7; wPawn ^= 7; } int index = whiteMove ? 0 : 1; index = index * 32 + Position.getY(wKing)*4+Position.getX(wKing); index = index * 64 + bKing; index = index * 48 + wPawn - 8; int bytePos = index / 8; int bitPos = index % 8; boolean draw = (((int)kpkTable[bytePos]) & (1 << bitPos)) == 0; if (draw) return 0; return qV - pV / 4 * (7-Position.getY(wPawn)); } private static final int krkpEval(int wKing, int bKing, int bPawn, boolean whiteMove) { if (Position.getX(bKing) >= 4) { // Mirror X wKing ^= 7; bKing ^= 7; bPawn ^= 7; } int index = whiteMove ? 0 : 1; index = index * 32 + Position.getY(bKing)*4+Position.getX(bKing); index = index * 48 + bPawn - 8; index = index * 8 + Position.getY(wKing); byte mask = krkpTable[index]; boolean canWin = (mask & (1 << Position.getX(wKing))) != 0; int score = rV - pV + Position.getY(bPawn) * pV / 4; if (canWin) score += 150; else score /= 50; return score; } /** * Interpolate between (x1,y1) and (x2,y2). * If x < x1, return y1, if x > x2 return y2. Otherwise, use linear interpolation. */ static final int interpolate(int x, int x1, int y1, int x2, int y2) { if (x > x2) { return y2; } else if (x < x1) { return y1; } else { return (x - x1) * (y2 - y1) / (x2 - x1) + y1; } } }