/* GNU Chess 5.0 - eval.c - evaluation code Copyright (c) 1999-2002 Free Software Foundation, Inc. GNU Chess is based on the two research programs Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft. GNU Chess 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 2, or (at your option) any later version. GNU Chess 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 GNU Chess; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Contact Info: bug-gnu-chess@gnu.org cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net */ /**************************************************************************** * * * *****************************************************************************/ #include #include #include #include #include "common.h" #include "eval.h" int LoneKing (int, int); int ScoreKBNK (int, int); int KPK (int); int BishopTrapped (short); int DoubleQR7 (short); BitBoard passed[2]; BitBoard weaked[2]; static int PawnSq[2][64] = { { 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5,-10,-10, 5, 5, 5, -2, -2, -2, 6, 6, -2, -2, -2, 0, 0, 0, 25, 25, 0, 0, 0, 2, 2, 12, 16, 16, 12, 2, 2, 4, 8, 12, 16, 16, 12, 4, 4, 4, 8, 12, 16, 16, 12, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, 0, 0, 4, 8, 12, 16, 16, 12, 4, 4, 4, 8, 12, 16, 16, 12, 4, 4, 2, 2, 12, 16, 16, 12, 2, 2, 0, 0, 0, 25, 25, 0, 0, 0, -2, -2, -2, 6, 6, -2, -2, -2, 5, 5, 5,-10,-10, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0} }; static const int Passed[2][8] = { { 0, 48, 48, 120, 144, 192, 240, 0}, {0, 240, 192, 144, 120, 48, 48, 0} }; /* Penalties for one or more isolated pawns on a given file */ static const int isolani_normal[8] = { -8, -10, -12, -14, -14, -12, -10, -8 }; /* Penalties if the file is half-open (i.e. no enemy pawns on it) */ static const int isolani_weaker[8] = { -22, -24, -26, -28, -28, -26, -24, -22 }; static const BitBoard d2e2[2] = { ULL(0x0018000000000000), ULL(0x0000000000001800) }; static const BitBoard brank7[2] = { ULL(0x000000000000FF00), ULL(0x00FF000000000000) }; static const BitBoard brank8[2] = { ULL(0x00000000000000FF), ULL(0xFF00000000000000) }; static const BitBoard brank67[2] = { ULL(0x0000000000FFFF00), ULL(0x00FFFF0000000000) }; static const BitBoard brank58[2] = { ULL(0x00000000FFFFFFFF), ULL(0xFFFFFFFF00000000) }; int ScoreP (short side) /*************************************************************************** * * Pawn evaluation is based on the following factors (which is being * constantly updated!). * * 1. Pawn square tables. * 2. Passed pawns. * 3. Backward pawns. * 4. Pawn base under attack. * 5. Doubled pawns * 6. Isolated pawns * 7. Connected passed pawns on 6/7th rank. * 8. Unmoved & blocked d, e pawn * 9. Passed pawn which cannot be caught. * 10. Pawn storms. * ***************************************************************************/ { int xside; int s, sq, i, i1; int n1, n2, backward; int nfile[8]; int EnemyKing; BitBoard c, t, p, blocker, *e; PawnSlot *ptable; if (board.b[side][pawn] == NULLBITBOARD) return (0); xside = 1^side; EnemyKing = board.king[xside]; p = board.b[xside][pawn]; c = t = board.b[side][pawn]; ptable = PawnTab[side] + (PawnHashKey & PHashMask); TotalPawnHashCnt++; if (ptable->phase == phase && ptable->pkey == KEY(PawnHashKey)) { GoodPawnHashCnt++; s = ptable->score; passed[side] = ptable->passed; weaked[side] = ptable->weaked; goto phase2; } s = 0; passed[side] = NULLBITBOARD; weaked[side] = NULLBITBOARD; memset (nfile, 0, sizeof (nfile)); while (t) { sq = leadz (t); CLEARBIT (t, sq); s += PawnSq[side][sq]; /* Passed pawns */ if ((p & PassedPawnMask[side][sq]) == NULLBITBOARD) { if ((side == white && (FromToRay[sq][sq|56] & c) == 0) || (side == black && (FromToRay[sq][sq&7] & c) == 0)) { passed[side] |= BitPosArray[sq]; s += (Passed[side][RANK(sq)] * phase) / 12; } } /* Backward pawns */ backward = false; /* i = sq + (side == white ? 8 : -8); */ if ( side == white ) { i = sq + 8; } else { i= sq - 8; } if (!(PassedPawnMask[xside][i] & ~FileBit[ROW(sq)] & c) && cboard[i] != pawn) { n1 = nbits (c & MoveArray[ptype[xside]][i]); n2 = nbits (p & MoveArray[ptype[side]][i]); if (n1 < n2) backward = true; } if (!backward && (BitPosArray[sq] & brank7[xside])) { i1 = 1; i = i + (side == white ? 8 : -8); if (!(PassedPawnMask[xside][i] & ~FileBit[ROW(i1)] & c)) { n1 = nbits (c & MoveArray[ptype[xside]][i]); n2 = nbits (p & MoveArray[ptype[side]][i]); if (n1 < n2) backward = true; } } if (backward) { weaked[side] |= BitPosArray[sq]; s += BACKWARDPAWN; } /* Pawn base under attack */ if ((MoveArray[ptype[side]][sq] & p) && (MoveArray[ptype[side]][sq] & c)) s += PAWNBASEATAK; /* Increment file count for isolani & doubled pawn evaluation */ nfile[ROW(sq)]++; } for (i = 0; i <= 7; i++) { /* Doubled pawns */ if (nfile[i] > 1) s += DOUBLEDPAWN; /* Isolated pawns */ if (nfile[i] && (!(c & IsolaniMask[i]))) { /* Isolated on a half-open file */ if (!(FileBit[i] & board.b[xside][pawn])) s += isolani_weaker[i] * nfile[i]; else /* Normal isolated pawn */ s += isolani_normal[i] * nfile[i]; weaked[side] |= (c & FileBit[i]); } } if (computerplays == side) { /* Penalize having eight pawns */ if (nbits(board.b[computerplays][pawn]) == 8) s += EIGHT_PAWNS; /* Detect stonewall formation in enemy */ if (nbits(stonewall[xside] & board.b[xside][pawn]) == 3) s += STONEWALL; /* Locked pawns */ n = 0; if (side == white) n = nbits((c >> 8) & board.b[xside][pawn] & boxes[1]); else n = nbits((c << 8) & board.b[xside][pawn] & boxes[1]); if (n > 1) s += n * LOCKEDPAWNS; } /* Save the score into the pawn hash table */ ptable->pkey = KEY(PawnHashKey); ptable->passed = passed[side]; ptable->weaked = weaked[side]; ptable->score = s; ptable->phase = phase; /*************************************************************************** * * This section of the pawn code cannot be saved into the pawn hash as * they depend on the position of other pieces. So they have to be * calculated again. * ***************************************************************************/ phase2: /* Pawn on f6/c6 with Queen against castled king is very strong */ c = board.b[side][pawn]; sq = board.king[xside]; if (side == white && board.b[side][queen] && (BitPosArray[C6] | BitPosArray[F6]) & c) { if (c & BitPosArray[F6] && sq > H6 && distance[sq][G7]==1) s += PAWNNEARKING; if (c & BitPosArray[C6] && sq > H6 && distance[sq][B7]==1) s += PAWNNEARKING; } else if (side == black && board.b[side][queen] && (BitPosArray[C3] | BitPosArray[F3]) & c) { if (c & BitPosArray[F3] && sq < A3 && distance[sq][G2]==1) s += PAWNNEARKING; if (c & BitPosArray[C3] && sq < A3 && distance[sq][B2]==1) s += PAWNNEARKING; } /* Connected passed pawns on 6th or 7th rank */ t = passed[side] & brank67[side]; if (t && (board.pmaterial[xside] == ValueR || (board.pmaterial[xside] == ValueN && pieces[xside] == board.b[xside][knight]))) { n1 = ROW(board.king[xside]); n2 = RANK(board.king[xside]); for (i = 0; i <= 6; i++) { if (t & FileBit[i] && t & FileBit[i+1] && (n1 < i-1 || n1 > i+1 || (side == white && n2 < 4) || (side == black && n2 > 3))) s += CONNECTEDPP; } } /* Pawn on d2,e2/d7,e7 is blocked */ blocker = board.friends[side] | board.friends[xside]; if (side == white && (((c & d2e2[white]) >> 8) & blocker)) s += BLOCKDEPAWN; if (side == black && (((c & d2e2[black]) << 8) & blocker)) s += BLOCKDEPAWN; /* Enemy has no pieces & King is outside of passed pawn square */ if (passed[side] && board.pmaterial[xside]==0) { e = board.b[xside]; i1 = board.king[xside]; p = passed[side]; while (p) { sq = leadz (p); CLEARBIT (p, sq); if (board.side == side) { if (!(SquarePawnMask[side][sq] & board.b[xside][king])) s += ValueQ * Passed[side][RANK(sq)] / PFACTOR; } else if (!(MoveArray[king][i1] & SquarePawnMask[side][sq])) s += ValueQ * Passed[side][RANK(sq)] / PFACTOR; } } /* If both sides are castled on different sides, bonus for pawn storms */ c = board.b[side][pawn]; if (abs (ROW (board.king[side]) - ROW (board.king[xside])) >= 4 && PHASE < 6) { n1 = ROW (board.king[xside]); p = (IsolaniMask[n1] | FileBit[n1]) & c; while (p) { sq = leadz (p); CLEARBIT (p, sq); s += 10 * (5 - distance[sq][board.king[xside]]); } } return (s); } static const int Outpost[2][64] = { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 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 inline int CTL(short sq, short piece __attribute__ ((unused)), short side) /*************************************************************************** * * Return a score corresponding to the number of squares in the bitboard * target multiplied by a specified bonus for controlling each square. * * Can be used for control of the center and attacks around the king. * ***************************************************************************/ { int s, n, EnemyKing, FriendlyKing; BitBoard controlled; s = 0; EnemyKing = board.king[1^side]; FriendlyKing = board.king[side]; controlled = AttackXFrom (sq, side); /* Center control */ n = nbits (controlled & boxes[0]); s += 4*n; /* Attacks against enemy king */ n = nbits (controlled & DistMap[EnemyKing][2]); s += n; /* Defenses for friendly king */ n = nbits (controlled & DistMap[FriendlyKing][2]); s += n; /* Mobility */ n = nbits(controlled); s += 4*n; return (s); } int ScoreN (short side) /*************************************************************************** * * 1. central knight - distance from enemy king. * 2. mobility/control/attack * 3. outpost knight protected by pawn. * 4. knight attacking weak pawns. * ***************************************************************************/ { int xside; int s, s1, sq; int EnemyKing; BitBoard c, t; if (board.b[side][knight] == NULLBITBOARD) return (0); xside = side^1; s = s1 = 0; c = board.b[side][knight]; t = board.b[xside][pawn]; EnemyKing = board.king[xside]; if ( c & pinned ) { s += PINNEDKNIGHT * nbits(c & pinned); } while (c) { sq = leadz (c); CLEARBIT (c, sq); /* Control */ s1 = CTL(sq,knight,side); if ( (BitPosArray[sq] & rings[3]) != NULLBITBOARD) s1 += KNIGHTONRIM; if (Outpost[side][sq] && !(t & IsolaniMask[ROW(sq)] & PassedPawnMask[side][sq]) ) { s1 += OUTPOSTKNIGHT; /* Knight defended by own pawn */ if (MoveArray[ptype[xside]][sq] & board.b[side][pawn]) s1 += OUTPOSTKNIGHT; } /* Attack on weak opponent pawns */ if (MoveArray[knight][sq] & weaked[xside]) s1 += ATAKWEAKPAWN; s += s1; } return (s); } int ScoreB (short side) /**************************************************************************** * * 1. double bishops. * 2. mobility/control/attack * 3. outpost bishop * 4. fianchetto bishop * 5. Bishop pair * ****************************************************************************/ { int xside; int s, s1, n, sq, EnemyKing; BitBoard c, t; if (board.b[side][bishop] == NULLBITBOARD) return (0); s = s1 = 0; c = board.b[side][bishop]; xside = side ^ 1; EnemyKing = board.king[xside]; n = 0; t = board.b[xside][pawn]; if ( c & pinned ) { s += PINNEDBISHOP * nbits(c & pinned); } while (c) { sq = leadz (c); CLEARBIT (c, sq); n++; /* Control */ s1 = CTL(sq,bishop,side); /* Outpost bishop */ if (Outpost[side][sq] && !(t & IsolaniMask[ROW(sq)] & PassedPawnMask[side][sq])) { s1 += OUTPOSTBISHOP; /* Bishop defended by own pawn */ if (MoveArray[ptype[xside]][sq] & board.b[side][pawn]) s1 += OUTPOSTBISHOP; } /* Fianchetto bishop */ if (side == white) { if (board.king[side] >= F1 && board.king[side] <= H1 && sq == G2) s1 += FIANCHETTO; if (board.king[side] >= A1 && board.king[side] <= C1 && sq == B2) s1 += FIANCHETTO; } else if (side == black) { if (board.king[side] >= F8 && board.king[side] <= H8 && sq == G7) s1 += FIANCHETTO; if (board.king[side] >= A8 && board.king[side] <= C8 && sq == B7) s1 += FIANCHETTO; } /* Attack on weak opponent pawns */ if (BishopAttack(sq) & weaked[xside]) s1 += ATAKWEAKPAWN; s += s1; } /* Doubled bishops */ if (n > 1) s += DOUBLEDBISHOPS; return (s); } int BishopTrapped (short side) /**************************************************************************** * * Check for bishops trapped at A2/H2/A7/H7 * ****************************************************************************/ { int s = 0; /* Don't waste time */ if (board.b[side][bishop] == NULLBITBOARD) return (0); if (side == white) { if ((board.b[white][bishop] & BitPosArray[A7]) && (board.b[black][pawn] & BitPosArray[B6]) && SwapOff(MOVE(A7,B6)) < 0) s += BISHOPTRAPPED; if ((board.b[white][bishop] & BitPosArray[H7]) && (board.b[black][pawn] & BitPosArray[G6]) && SwapOff(MOVE(H7,G6)) < 0) s += BISHOPTRAPPED; } else { if ((board.b[black][bishop] & BitPosArray[A2]) && (board.b[white][pawn] & BitPosArray[B3]) && SwapOff(MOVE(A2,B3)) < 0) s += BISHOPTRAPPED; if ((board.b[black][bishop] & BitPosArray[H2]) && (board.b[white][pawn] & BitPosArray[G3]) && SwapOff(MOVE(H2,G3)) < 0) s += BISHOPTRAPPED; } return (s); } int ScoreR (short side) /**************************************************************************** * * 1. rook on 7th rank and Enemy king on 8th rank or pawns on 7th rank. * 2. rook on open/half-open file. * 3. rook in front/behind passed pawns (pawn >= 5th rank) * ****************************************************************************/ { int s, s1, sq, xside, fyle, EnemyKing; BitBoard c; if (board.b[side][rook] == NULLBITBOARD) return (0); s = s1 = 0; c = board.b[side][rook]; xside = side ^ 1; EnemyKing = board.king[xside]; if ( c & pinned ) { s += PINNEDROOK * nbits(c & pinned); } while (c) { sq = leadz (c); CLEARBIT (c, sq); /* Control */ s1 = CTL(sq,rook,side); fyle = ROW(sq); if (PHASE < 7) { if (!(board.b[side][pawn] & FileBit[fyle])) { if (fyle == 5 && ROW(board.king[xside])>=E_FILE) s1 += ROOKLIBERATED; s1 += ROOKHALFFILE; if (!(board.b[xside][pawn] & FileBit[fyle])) s1 += ROOKOPENFILE; } } if (phase > 6 && (FileBit[fyle] & passed[white] & brank58[white])) { if (nbits (Ray[sq][7] & passed[white]) == 1) s1 += ROOKBEHINDPP; else if (Ray[sq][4] & passed[white]) s1 += ROOKINFRONTPP; } if (FileBit[fyle] & passed[black] & brank58[black]) { if (nbits (Ray[sq][4] & passed[black]) == 1) s1 += ROOKBEHINDPP; else if (Ray[sq][7] & passed[black]) s1 += ROOKINFRONTPP; } /* Attack on weak opponent pawns */ if (RookAttack(sq) & weaked[xside]) s1 += ATAKWEAKPAWN; /* Rook on 7th rank */ if (RANK (sq) == rank7[side] && (RANK (EnemyKing) == rank8[side] || board.b[xside][pawn] & RankBit[RANK(sq)])) s1 += ROOK7RANK; s += s1; } return (s); } int DoubleQR7 (short side) /*************************************************************************** * * This code just check to see if there is a QQ or QR or RR combo on the * 7th rank. This is very strong and given quite a big bonus. This * routine is called by the lazy section. * ***************************************************************************/ { int xside; xside = 1^side; if (nbits ((board.b[side][queen]|board.b[side][rook]) & brank7[side]) > 1 && ((board.b[xside][king] & brank8[side]) || (board.b[xside][pawn] & brank7[side]))) return (ROOKS7RANK); else return (0); } int ScoreQ (short side) /*************************************************************************** * * 1. queen centralization. * 2. king tropism. * 3. Bonus if opponent king is exposed. * ***************************************************************************/ { int xside; int s, s1, sq, EnemyKing; BitBoard c; s = s1 = 0; /* Try to keep our queen on the board for attacking purposes. */ if (board.b[side][queen] == NULLBITBOARD) { if (side == computer) { s += QUEEN_NOT_PRESENT; } return(s); } xside = 1 ^ side; c = board.b[side][queen]; EnemyKing = board.king[xside]; if ( c & pinned ) { s += PINNEDQUEEN * nbits(c & pinned); } while (c) { sq = leadz (c); CLEARBIT (c, sq); /* Control */ s1 = CTL(sq,queen,side); if (distance[sq][EnemyKing] <= 2) s1 += QUEENNEARKING; /* Attack on weak opponent pawns */ if (QueenAttack(sq) & weaked[xside]) s1 += ATAKWEAKPAWN; s += s1; } return (s); } static const int KingSq[64] = { 24, 24, 24, 16, 16, 0, 32, 32, 24, 20, 16, 12, 12, 16, 20, 24, 16, 12, 8, 4, 4, 8, 12, 16, 12, 8, 4, 0, 0, 4, 8, 12, 12, 8, 4, 0, 0, 4, 8, 12, 16, 12, 8, 4, 4, 8, 12, 16, 24, 20, 16, 12, 12, 16, 20, 24, 24, 24, 24, 16, 16, 0, 32, 32 }; static const int EndingKing[64] = { 0, 6, 12, 18, 18, 12, 6, 0, 6, 12, 18, 24, 24, 18, 12, 6, 12, 18, 24, 32, 32, 24, 18, 12, 18, 24, 32, 48, 48, 32, 24, 18, 18, 24, 32, 48, 48, 32, 24, 18, 12, 18, 24, 32, 32, 24, 18, 12, 6, 12, 18, 24, 24, 18, 12, 6, 0, 6, 12, 18, 18, 12, 6, 0 }; static const int pawncover[9] = { -60, -30, 0, 5, 30, 30, 30, 30, 30 }; static const int factor[9] = { 7, 8, 8, 7, 6, 5, 4, 2, 0, }; int ScoreK (short side) /*************************************************************************** * * 1. king in the corner. ?? SRW 2002-08-02 Unclear if implemented * 2. pawns around king. * 3. king on open file. * 4. Uncastled king. * 5. Open rook file via Ng5 or Bxh7 sac. * 6. No major or minor piece in the king's quadrant. * ***************************************************************************/ { int xside; int s, sq, sq1, n, n1, n2, file, fsq, rank; BitBoard b, x; s = 0; xside = 1^side; sq = board.king[side]; file = ROW (sq); rank = RANK (sq); KingSafety[side] = 0; if (!ENDING) { s += ((6 - phase) * KingSq[sq] + phase * EndingKing[sq]) / 6; /* After castling kingside, reward having all 3 pawns in front but not if there is a threatening pawn. This permits the freeing move F3/F6. */ if (side == white) n = nbits (MoveArray[king][sq] & board.b[side][pawn] & RankBit[rank+1]); else n = nbits (MoveArray[king][sq] & board.b[side][pawn] & RankBit[rank-1]); s += pawncover[n]; /* Penalize compromised wing pawn formations prior to castle. */ if (!board.castled[side]) { n = -1; if (side == white) { /* King on original square and unmoved */ if (sq == 4 && Mvboard[sq] == 0) { /* Kingside - rook on original square and unmoved. */ if ( (board.b[side][rook] & BitPosArray[H1])!=NULLBITBOARD && Mvboard[H1] == 0) n = nbits (MoveArray[king][G1] & board.b[side][pawn] & RankBit[rank+1]); /* Queenside */ if ( (board.b[side][rook] & BitPosArray[A1])!=NULLBITBOARD && Mvboard[A1] == 0) n = nbits (MoveArray[king][C1] & board.b[side][pawn] & RankBit[rank+1]); } } else { if (sq == 60 && Mvboard[sq] == 0) { /* Kingside */ if ( (board.b[side][rook] & BitPosArray[H8])!=NULLBITBOARD && Mvboard[H8] == 0) n = nbits (MoveArray[king][G8] & board.b[side][pawn] & RankBit[rank-1]); /* Queenside */ if ( (board.b[side][rook] & BitPosArray[A8])!=NULLBITBOARD && Mvboard[A8] == 0) n = nbits (MoveArray[king][C8] & board.b[side][pawn] & RankBit[rank-1]); } } /* Penalize breaking the wing pawn formations prior to castle */ if (n != -1) s += pawncover[n]; } if (side == computer && file >= F_FILE && !(FileBit[G_FILE] & board.b[side][pawn])) { if (side == white && cboard[F2] == pawn) s += GOPEN; else if (side == black && cboard[F7] == pawn) s += GOPEN; } /* No friendly pawns on this king file */ if (!(FileBit[file] & board.b[side][pawn])) s += KINGOPENFILE; /* No enemy pawns on this king file */ if (!(FileBit[file] & board.b[xside][pawn])) s += KINGOPENFILE1; switch (file) { case A_FILE : case E_FILE : case F_FILE : case G_FILE : if (!(FileBit[file+1] & board.b[side][pawn])) s += KINGOPENFILE; if (!(FileBit[file+1] & board.b[xside][pawn])) s += KINGOPENFILE1; break; case H_FILE : case D_FILE : case C_FILE : case B_FILE : if (!(FileBit[file-1] & board.b[side][pawn])) s += KINGOPENFILE; if (!(FileBit[file-1] & board.b[xside][pawn])) s += KINGOPENFILE1; break; default : break; } if (board.castled[side]) { if (side == white) { if (file > E_FILE) { if (!(BitPosArray[F2] & board.b[side][pawn]) || !(BitPosArray[G2] & board.b[side][pawn]) || !(BitPosArray[H2] & board.b[side][pawn]) ) s += RUPTURE; } else if (file < E_FILE) { if (!(BitPosArray[A2] & board.b[side][pawn]) || !(BitPosArray[B2] & board.b[side][pawn]) || !(BitPosArray[C2] & board.b[side][pawn]) ) s += RUPTURE; } } else { if (file > E_FILE) { if (!(BitPosArray[F7] & board.b[side][pawn]) || !(BitPosArray[G7] & board.b[side][pawn]) || !(BitPosArray[H7] & board.b[side][pawn]) ) s += RUPTURE; } else if (file < E_FILE) { if (!(BitPosArray[A7] & board.b[side][pawn]) || !(BitPosArray[B7] & board.b[side][pawn]) || !(BitPosArray[C7] & board.b[side][pawn]) ) s += RUPTURE; } } } if (side == computer) { /* Stock piece sacrifice on h file */ if (file >= E_FILE && board.b[xside][queen] && board.b[xside][rook] && !((board.b[side][pawn]|board.b[xside][pawn]) & FileBit[7])) s += HOPEN; /* King trapping rook */ if (side == white) { if (file > E_FILE) { if (board.b[side][rook]&mask_kr_trapped_w[H_FILE-file]) { s += ROOKTRAPPED; } } else if (file < D_FILE) { if (board.b[side][rook]&mask_qr_trapped_w[file]) { s += ROOKTRAPPED; } } } else { if (file > E_FILE) { if (board.b[side][rook]&mask_kr_trapped_b[H_FILE-file]) { s += ROOKTRAPPED; } } else if (file < D_FILE) { if (board.b[side][rook]&mask_qr_trapped_b[file]) { s += ROOKTRAPPED; } } } } /* Don't give fianchetto target for advanced pawns */ if (file > E_FILE && ROW(board.king[xside]) < D_FILE) { if (side == white) fsq = G3; else fsq = G6; if ((BitPosArray[fsq] & board.b[side][pawn]) != NULLBITBOARD) if (((BitPosArray[F4]|BitPosArray[H4]| BitPosArray[F5]|BitPosArray[H5]) & board.b[xside][pawn]) != NULLBITBOARD) s += FIANCHETTO_TARGET; } if (file < E_FILE && ROW(board.king[xside]) > E_FILE) { if (side == white) fsq = B3; else fsq = B6; if ((BitPosArray[fsq] & board.b[side][pawn]) != NULLBITBOARD) if (((BitPosArray[A4]|BitPosArray[C4]| BitPosArray[A5]|BitPosArray[C5]) & board.b[xside][pawn]) != NULLBITBOARD) s += FIANCHETTO_TARGET; } /* No major/minor piece in king's quadrant */ /* First identify the quadrant */ x = boardhalf[side] & boardside[file<=D_FILE]; /* Now identify the number of non-pawn enemy in quadrant */ n1 = nbits(x & (board.friends[xside])); if (n1 > 0) { /* Now identify the number of non-pawn friends in quadrant */ n2 = nbits(x & (board.friends[side] & ~board.b[side][pawn] & ~board.b[side][king])); if (n1 > n2) s += (n1 - n2) * KING_DEFENDER_DEFICIT; } KingSafety[side] = s; s = (s * factor[phase]) / 8; } else { s += EndingKing[sq]; s += CTL(sq,king,side); b = (board.b[white][pawn] | board.b[black][pawn]); while (b) { sq1 = leadz (b); CLEARBIT (b, sq1); if (BitPosArray[sq1] & board.b[white][pawn]) s -= distance[sq][sq1+8] * 10 - 5; else if (BitPosArray[sq1] & board.b[white][pawn]) s -= distance[sq][sq1-8] * 10 - 5; else s -= distance[sq][sq1] - 5; } /* Attack on weak opponent pawns */ if (MoveArray[king][sq] & weaked[xside]) s += ATAKWEAKPAWN * 2; } if (phase >= 4) { /* Weak back rank */ if (side == white) { if (sq < A2) if (!(MoveArray[king][sq] & (~board.b[side][pawn] & RankBit[1]))) s += KING_BACK_RANK_WEAK; } else { if (sq > H7) if (!(MoveArray[king][sq] & (~board.b[side][pawn] & RankBit[6]))) s += KING_BACK_RANK_WEAK; } } return (s); } int LoneKing (int side, int loser) /************************************************************************** * * One side has a lonely king and the other has no pawns, but enough * mating material. We give an additional bonus of 150 points for the * winning side to attract the search to such positions. * **************************************************************************/ { int s, winer, sq1, sq2; winer = 1^loser; if (board.material[winer] == ValueB+ValueN && nbits(board.b[winer][bishop]) == 1 && nbits(board.b[winer][knight]) == 1) return (ScoreKBNK (side, loser)); sq1 = board.king[winer]; sq2 = board.king[loser]; s = 150 - 6 * taxicab[sq1][sq2] - EndingKing[sq2]; if (side == loser) s = -s; s += MATERIAL; return (s); } int KPK (int side) /************************************************************************** * * A KPK endgame evaluator. Side is the one on the move. * This is not a perfect evaluator, it merely identifies SOME positions * as wins or draw. Some WON positions could be seen as draws; the search * will be able to use the knowledge here to identify more positions. * **************************************************************************/ { int winer, loser, sq, sqw, sql; int s; winer = (board.b[white][pawn] ? white : black); loser = 1 ^ winer; sq = leadz (board.b[winer][pawn]); sqw = board.king[winer]; sql = board.king[loser]; s = ValueP + (ValueQ * Passed[winer][RANK(sq)] / PFACTOR) + 4 * (winer == white ? RANK(sqw) : 7-RANK(sqw)); /************************************************************************** * * Pawn is outside the square of the king * **************************************************************************/ if (~SquarePawnMask[winer][sq] & board.b[loser][king]) { if (!(MoveArray[king][sql] & SquarePawnMask[winer][sq])) return (winer == side ? s : -s); if (winer == side) return (s); } /************************************************************************** * * Friendly king is on same or adjacent file to the pawn, and the pawn is * on a file other than a rook file and ... * **************************************************************************/ if (ROW(sq) != 0 && ROW(sq) != 7 && ((IsolaniMask[ROW(sq)] | FileBit[ROW(sq)]) & board.b[winer][king])) { /************************************************************************** * * a. friendly king is 2 ranks more advanced than the pawn * b. friendly king is 1 rank more advanced than the pawn * i. The friendly king is on the sixth rank. * ii. The enemy king does not have direct opposition by being 2 ranks * in front of the friendly king and on the same file. * c. friendly king is same rank as pawn * i. The enemy king is not 2-4 ranks more advanced that the pawn. * ii. The pawn is on the sixth rank and the enemy king does not have * direct opposition. * d. pawn is on the 7th rank, friendly king is on sixth rank and * i. The enemy king is not on the queening square. * ii. The enemy is on the queening square but both kings are in the same * file. * **************************************************************************/ if (winer == white) { if (RANK(sqw) == RANK(sq) + 2) return (winer == side ? s : -s); if (RANK(sqw) == RANK(sq) + 1) { if (RANK(sqw) == 5) return (winer == side ? s : -s); if (sqw < A6) { if (sqw+16 == sql && winer == side) return (0); else return (winer == side ? s : -s); } } if (RANK(sqw) == RANK(sq)) { if ((RANK(sql) - RANK(sq) < 2 || RANK(sql) - RANK(sq) > 4) && winer == side) return (s); if ((RANK(sql) - RANK(sq) < 1 || RANK(sql) - RANK(sq) > 5) && loser == side) return (-s); if (RANK(sq) == 5 && sqw+16 != sql) return (winer == side ? s : 0); } if (RANK(sq) == 6 && RANK(sqw) == 5) { if (sql != sq+8) return (winer == side ? s : 0); if (sql == sq+8 && sql == sqw+16) return (winer == side ? s : 0); } } else { if (RANK(sqw) == RANK(sq) - 2) return (winer == side ? s : -s); if (RANK(sqw) == RANK(sq) - 1) { if (RANK(sqw) == 2) return (winer == side ? s : -s); if (sqw > H3) { if (sqw-16 == sql && winer == side) return (0); else return (winer == side ? s : -s); } } if (RANK(sqw) == RANK(sq)) { if ((RANK(sq) - RANK(sql) < 2 || RANK(sq) - RANK(sql) > 4) && winer == side) return (s); if ((RANK(sq) - RANK(sql) < 1 || RANK(sq) - RANK(sql) > 5) && loser == side) return (-s); if (RANK(sq) == 5 && sqw+16 != sql) return (winer == side ? s : 0); } if (RANK(sq) == 1 && RANK(sqw) == 2) { if (sql != sq-8) return (winer == side ? s : 0); if (sql == sq-8 && sql == sqw-16) return (winer == side ? s : 0); } } } return (0); } int KBNK[64] = { 0, 10, 20, 30, 40, 50, 60, 70, 10, 20, 30, 40, 50, 60, 70, 60, 20, 30, 40, 50, 60, 70, 60, 50, 30, 40, 50, 60, 70, 60, 50, 40, 40, 50, 60, 70, 60, 50, 40, 30, 50, 60, 70, 60, 50, 40, 30, 20, 60, 70, 60, 50, 40, 30, 20, 10, 70, 60, 50, 40, 30, 20, 10, 0 }; int ScoreKBNK (int side, int loser) /**************************************************************************** * * My very own KBNK routine! * ****************************************************************************/ { int s, winer, sq1, sq2, sqB; winer = 1^loser; sqB = board.king[loser]; if (board.b[winer][bishop] & WHITESQUARES) sqB = RANK(sqB)*8 + 7 - ROW(sqB); sq1 = board.king[winer]; sq2 = board.king[loser]; s = 300 - 6 * taxicab[sq1][sq2]; s -= KBNK[sqB]; s -= EndingKing[sq2]; s -= taxicab[leadz(board.b[winer][knight])][sq2]; s -= taxicab[leadz(board.b[winer][bishop])][sq2]; /* King in the central 4x4 region is good! */ if (board.b[winer][king] & ULL(0x00003C3C3C3C0000)) s += 20; if (side == loser) s = -s; s += MATERIAL; return (s); } static const BitBoard nn[2] = { ULL(0x4200000000000000), ULL(0x0000000000000042) }; static const BitBoard bb[2] = { ULL(0x2400000000000000), ULL(0x0000000000000024) }; int ScoreDev (short side) /*************************************************************************** * * Calculate the development score for side (for opening only). * Penalize the following. * . Uncastled and cannot castled * . Undeveloped knights and bishops * . Early queen move. * ***************************************************************************/ { int s; int sq; BitBoard c; /* Calculate whether we are developed */ c = (board.b[side][knight] & nn[side]) | (board.b[side][bishop] & bb[side]); s = nbits(c) * -8; /* If we are castled or beyond the 20th move, no more ScoreDev */ if (board.castled[side] || GameCnt >= 38) return (s); s += NOTCASTLED; /* If the king is moved, nail it, otherwise check rooks */ if (Mvboard[board.king[side]] > 0) s += KINGMOVED; /* Discourage rook moves */ c = board.b[side][rook]; while (c) { sq = leadz(c); CLEARBIT(c, sq); if (Mvboard[sq] > 0) s += ROOKMOVED; } /* Penalize a queen that moves at all */ if (board.b[side][queen]) { sq = leadz (board.b[side][queen]); if (Mvboard[sq] > 0) s += EARLYQUEENMOVE; /* s += Mvboard[sq] * EARLYQUEENMOVE; */ } /* Discourage repeat minor piece moves */ c = board.b[side][knight] | board.b[side][bishop]; while (c) { sq = leadz(c); CLEARBIT(c, sq); if (Mvboard[sq] > 1) s += EARLYMINORREPEAT; /* s += Mvboard[sq] * EARLYMINORREPEAT; */ } /* Discourage any wing pawn moves */ /* c = board.b[side][pawn] & (FileBit[0]|FileBit[1]|FileBit[6]|FileBit[7]); */ c = board.b[side][pawn] & ULL(0xc3c3c3c3c3c3c3c3); while (c) { sq = leadz(c); CLEARBIT(c, sq); if (Mvboard[sq] > 0) s += EARLYWINGPAWNMOVE; } /* Discourage any repeat center pawn moves */ /* c = board.b[side][pawn] & (FileBit[2]|FileBit[3]|FileBit[4]|FileBit[5]); */ c = board.b[side][pawn] & ULL(0x3c3c3c3c3c3c3c3c); while (c) { sq = leadz(c); CLEARBIT(c, sq); if (Mvboard[sq] > 1) s += EARLYCENTERPREPEAT; } return (s); } /* Array of pointer to functions */ static int (*ScorePiece[7]) (short) = { NULL, ScoreP, ScoreN, ScoreB, ScoreR, ScoreQ, ScoreK }; int Evaluate (int alpha, int beta) /**************************************************************************** * * First check to see if this position can be specially dealt with. * E.g. if our bounds indicate that we are looking for a mate score, * then just return the material score. Nothing else is important. * If its a KPK endgame, call our KPK routine. * If one side has a lone king & the winning side has no pawns then call * the LoneKing() mating driver routine. Note that there is enough * mating material as we have already check for insufficient mating material * in the call to EvaluateDraw() in search()/quiesce(). * ****************************************************************************/ { int side, xside; int piece, s, s1, score; int npiece[2]; BitBoard *b; side = board.side; xside = 1 ^ side; /* If we are looking for a MATE, just return the material */ if (alpha > MATE-255 || beta < -MATE+255) return (MATERIAL); /* A KPK endgame. */ if (board.material[white]+board.material[black] == ValueP) return (KPK (side)); /* One side has a lone king and other side has no pawns */ if (board.material[xside] == 0 && board.b[side][pawn] == NULLBITBOARD) return LoneKing (side, xside); if (board.material[side] == 0 && board.b[xside][pawn] == NULLBITBOARD) return LoneKing (side, side); /**************************************************************************** * * Lets try a lazy evaluation. In this stage, we should evaluate all those * features that gives big bonus/penalties. E.g. squares around king is * attacked by enemy pieces, 2 rooks on 7th rank, runaway passed pawns etc. * This will be the direction, so things will continue to change in this * section. * ****************************************************************************/ EvalCall++; phase = PHASE; b = board.b[white]; pieces[white] = b[knight] | b[bishop] | b[rook] | b[queen]; npiece[white] = nbits (pieces[white]); b = board.b[black]; pieces[black] = b[knight] | b[bishop] | b[rook] | b[queen]; npiece[black] = nbits (pieces[black]); s1 = MATERIAL; if ((s1 + maxposnscore[side] < alpha || s1 - maxposnscore[xside] > beta) && phase <= 6) { score = s1; goto next; } s = 0; s += ScoreDev (side) - ScoreDev (xside); s += ScoreP (side) - ScoreP (xside); s += ScoreK (side) - ScoreK (xside); s += BishopTrapped (side) - BishopTrapped (xside); s += DoubleQR7 (side) - DoubleQR7 (xside); s1 = s + MATERIAL; /************************************************************************** * * See if we can have a lazy evaluation cut. Otherwise its a slow eval. * **************************************************************************/ if (s1 + lazyscore[side] < alpha || s1 - lazyscore[side] > beta) { score = s1; } else { EvalCnt++; GenAtaks(); s1 = HUNGPENALTY * ( EvalHung(side) - EvalHung(xside) ); FindPins(&pinned); for (piece = knight; piece < king; piece++) { s1 += (*ScorePiece[piece]) (side) - (*ScorePiece[piece]) (xside); } lazyscore[side] = MAX (s1, lazyscore[side]); maxposnscore[side] = MAX (maxposnscore[side], s + s1); score = s + s1 + MATERIAL; } /*************************************************************************** * * Trade down bonus code. When ahead, trade pieces & not pawns; * ***************************************************************************/ next: if (MATERIAL >= 200) { score += (RootPieces - nbits(pieces[white] | pieces[black])) * TRADEPIECE; score -= (RootPawns - nbits(board.b[white][pawn] | board.b[black][pawn])) * TRADEPAWNS; } else if (MATERIAL <= -200) { score -= (RootPieces - nbits(pieces[white] | pieces[black])) * TRADEPIECE; score += (RootPawns - nbits(board.b[white][pawn] | board.b[black][pawn])) * TRADEPAWNS; } /*************************************************************************** * * Opposite color bishops is drawish. * ***************************************************************************/ if (ENDING && pieces[white] == board.b[white][bishop] && pieces[black] == board.b[black][bishop] && ((pieces[white] & WHITESQUARES && pieces[black] & BLACKSQUARES) || (pieces[white] & BLACKSQUARES && pieces[black] & WHITESQUARES))) { score /= 2; } /*************************************************************************** * * When one side has no mating material, then his score can never be > 0. * ***************************************************************************/ if (score > 0 && !board.b[side][pawn] && (board.material[side] < ValueR || pieces[side] == board.b[side][knight])) score = 0; if (score < 0 && !board.b[xside][pawn] && (board.material[xside] < ValueR || pieces[xside] == board.b[xside][knight])) score = 0; return (score); } short EvaluateDraw (void) /*************************************************************************** * * This routine is called by search() and quiesce() before anything else * is done. Its purpose it to check if the current position is a draw. * 0. 50-move draw. * 1. If there are any pawns, it is not. * 2. If both sides has anything less than a rook, draw. * 3. If both sides has <= 2 knights only, draw. * 4. If its a KBBK and bishops of same color, draw. * ***************************************************************************/ { BitBoard *w, *b; int wm, bm, wn, bn; /* * Exception - if we are close to a pawn move, promotion * or capture it is possible a forced mate will follow. * So we assume not drawn for 2 moves. */ if ( (GameCnt-Game50) < 5 ) return (false); /* 50 move rule */ if ( (GameCnt-Game50) > 100 ) return (true); w = board.b[white]; b = board.b[black]; if (w[pawn] != 0 || b[pawn] != 0) return (false); wm = board.material[white]; bm = board.material[black]; wn = nbits (w[knight]); bn = nbits (b[knight]); if ((wm