CPW King
Home * Engines * CPW-Engine * CPW_King
This is a patch to the CPW-Engine, changing its evaluation function. Modifications include:
- getting rid of lazy eval
- using eval hash table as default
- modifying mobility functions, so that they will count number of attacks on squares near enemy king as well as the number of attackers
- placing those functions in the piece-specific routines, before king safety calculation, so that it can make use of them
- adding functions to count attacks by queens
Resulting program plays at the roughly the same strength as regular CPW-Engine, but is much more entertaining to watch. To obtain optimal results, piece/square values fo bishops and knights should be raised a tiny bit.
The most obvious drawback of the presented code is that it does not see attackers that are lined up, such as rooks doubled on a file. If You consider making this example into a real program, this is the first thing to change.
We need to add the following function to the eval_init.cpp, in order to define squares which are considered worthwhile to attack:
void setSquaresNearKing() { for (int i = 0; i < 128; ++i) for (int j = 0; j < 128; ++j) { e.sqNearK[WHITE][i][j] = 0; e.sqNearK[BLACK][i][j] = 0; if (IS_SQ(i) && IS_SQ(j)) { // squares constituting the ring around both kings if (j == i + NORTH || j == i + SOUTH || j == i + EAST || j == i + WEST || j == i + NW || j == i + NE || j == i + SW || j == i + SE) { e.sqNearK[WHITE][i][j] = 1; e.sqNearK[BLACK][i][j] = 1; } /* squares in front of the white king ring */ if (j == i + NORTH + NORTH || j == i + NORTH + NE || j == i + NORTH + NW) e.sqNearK[WHITE][i][j] = 1; // squares in front og the black king ring if (j == i + SOUTH + SOUTH || j == i + SOUTH + SE || j == i + SOUTH + SW) e.sqNearK[WHITE][i][j] = 1; } } }
The complete eval.cpp looks as follows:
#include "stdafx.h" #include "0x88_math.h" #include "eval.h" #include "transposition.h" #define use_eval_hash /* mobility values for various piece kinds */ int bish_mob[16] = { -10, -4, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8 }; int rook_mob[16] = { -4, -2, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 }; int knight_mob[9] = { -6, -4, 0, 2, 4, 5, 6, 7, 8 }; /* adjustements of piece value based on the number of own pawns */ int knight_adj[9] = { -20, -16, -12, -8, -4, 0, 4, 8, 12 }; int rook_adj[9] = { 15, 12, 9, 6, 3, 0, -3, -6, -9 }; #define MINOR_ATT 2 #define ROOK_ATT 4 #define QUEEN_ATT 8 /******************************************************************* * This struct holds data about certain aspects of evaluation, * * which allows program to print them if desired. * *******************************************************************/ struct eval_vector { int MaterialAdjustement[2]; int Blockages[2]; int PositionalThemes[2]; int Mobility[2]; int KingAttackers[2]; // no. of pieces attacking enemy king int KingPressure[2]; // value of king pressure } v; /* local lists of pieces and pawns and indexes to them */ U8 pieceList[32]; U8 pieceIndex; U8 pawnList[32]; U8 pawnIndex; /* global flag used by mobility/attack detection functions to know if a piece examined currently is a king attacker */ int isAttacker = 0; /***************************************************************************** * Fast evaluation (material + pcsq + pawn structure) * *****************************************************************************/ int fast_eval() { /* fold in incrementally updated values */ int result = p.PieceMaterial[WHITE] + p.PawnMaterial[WHITE] + p.Pcsq[WHITE] - p.PieceMaterial[BLACK] - p.PawnMaterial[BLACK] - p.Pcsq[BLACK]; /* evaluate pawn structure, remembering that a pawn list must be set */ result += getPawnScoreNoList(); /* return score relative to the side to move */ if (b.stm == BLACK) return -result; else return result; } /***************************************************************************** * 1. Main evaluation function * *****************************************************************************/ int eval(int alpha, int beta) { int result; /* probe the evaluatinon hashtable */ int probeval = tteval_probe(); if (probeval != INVALID) return probeval; /* set internal data of the evaluation function */ eval_setPieceLists(); eval_clearVector(); /* sum the incrementally counted material and pcsq values */ result = p.PieceMaterial[WHITE] + p.PawnMaterial[WHITE] + p.Pcsq[WHITE] - p.PieceMaterial[BLACK] - p.PawnMaterial[BLACK] - p.Pcsq[BLACK]; /* tempo bonus */ if (b.stm == WHITE) result += e.TEMPO; else result -= e.TEMPO; /* add in pawn structure evaluation */ result += getPawnScore(); /******************************************************************* * Low material correction - guarding against an illusory material * * advantage. Program will not not expect to win having only * * a single minor piece and no pawns. * *******************************************************************/ if ((p.PawnMaterial[WHITE] == 0) && (p.PieceMaterial[WHITE] < 400) && (result > 0)) return 0; if ((p.PawnMaterial[BLACK] == 0) && (p.PieceMaterial[BLACK] < 400) && (result < 0)) return 0; /******************************************************************* * Program will not expect to win having only two knights in case * * neither side has pawns. Please note that this code assumes * * different values for bishop and knight, and eval_init() should * * take care of that. * *******************************************************************/ if (!p.PawnMaterial[WHITE] && !p.PawnMaterial[BLACK]) { if (p.PieceMaterial[WHITE] == 2 * e.PIECE_VALUE[KNIGHT] && result > 0) result = 0; if (p.PieceMaterial[BLACK] == 2 * e.PIECE_VALUE[KNIGHT] && result < 0) result = 0; } /******************************************************************* * Adjusting material value for the various combinations of pieces. * * Currently it scores bishop, knight and rook pairs. The first one * * gets a bonus, the latter two - a penalty. Please also note that * * adjustements of knight and rook value based on the number of own * * pawns on the board are done within the piece-specific routines. * *******************************************************************/ if (p.PieceCount[WHITE][BISHOP] > 1) v.MaterialAdjustement[WHITE] += e.BISHOP_PAIR; if (p.PieceCount[BLACK][BISHOP] > 1) v.MaterialAdjustement[BLACK] += e.BISHOP_PAIR; if (p.PieceCount[WHITE][KNIGHT] > 1) v.MaterialAdjustement[WHITE] -= e.P_KNIGHT_PAIR; if (p.PieceCount[BLACK][KNIGHT] > 1) v.MaterialAdjustement[BLACK] -= e.P_KNIGHT_PAIR; if (p.PieceCount[WHITE][ROOK] > 1) v.MaterialAdjustement[WHITE] -= e.P_ROOK_PAIR; if (p.PieceCount[BLACK][ROOK] > 1) v.MaterialAdjustement[BLACK] -= e.P_ROOK_PAIR; // penalty for the lack of pawns - added 28.07.2008 if (p.PieceCount[WHITE][PAWN] == 0) v.MaterialAdjustement[WHITE] -= 16; if (p.PieceCount[BLACK][PAWN] == 0) v.MaterialAdjustement[BLACK] -= 16; /******************************************************************** * Evaluate piece placement. This giant loop calls piece-specific * * functions which tend to do four things: * * * * (1) they look for the trapped pieces and blockages * * (2) they look for rooks on (half) open files * * (3) they calculate mobility and king safety * * (4) they calculate adjustements of material value based on * * the number of pawns * ********************************************************************/ for (int i = 0; i < pieceIndex; i++) { S8 sq = pieceList[i]; switch (b.color[sq]) { case WHITE: { switch (b.pieces[sq]) { case KNIGHT: wKnightEval(sq); break; case BISHOP: wBishopEval(sq); break; case ROOK: wRookEval(sq); break; case QUEEN: wQueenEval(sq); break; } } break; case BLACK: { switch (b.pieces[sq]) { case KNIGHT: bKnightEval(sq); break; case BISHOP: bBishopEval(sq); break; case ROOK: bRookEval(sq); break; case QUEEN: bQueenEval(sq); break; } } break; } } /******************************************************************** * After the piece evaluation loop we have the king tropism data * * in order, so it is time to do full king evaluation. For details * * see comments in wKingEval() function. * ********************************************************************/ result += wKingEval(p.KingLoc[WHITE]); result -= bKingEval(p.KingLoc[BLACK]); /******************************************************************** * Pattern evaluation - mainly things interrelated with the pawn * * position, not fitting elsewhere. * ********************************************************************/ blockedCentralPawns(); // don't block central pawns on initial squares blockedRooks(); // avoid pseudo-castling which blocks the rook slavMistake(); // don't play c4-c5 against Slav / Stonewall evalFianchetto(); /******************************************************************** * Fold in data gathered in evaluation vector. * ********************************************************************/ result += v.MaterialAdjustement[WHITE]; result -= v.MaterialAdjustement[BLACK]; result += v.Blockages[WHITE]; result -= v.Blockages[BLACK]; result += v.PositionalThemes[WHITE]; result -= v.PositionalThemes[BLACK]; /****************************************************************** * Here mobility score is scaled according to the side to move. * * We give more weight to opponent's mobility to encourage * * playing for restraint. * ******************************************************************/ if (sd.myside == WHITE) { v.Mobility[BLACK] *= 4; v.Mobility[BLACK] /= 3; } else { v.Mobility[WHITE] *= 4; v.Mobility[WHITE] /= 3; } result += v.Mobility[WHITE]; result -= v.Mobility[BLACK]; /******************************************************************* * Finally return the score relative to the side to move. * *******************************************************************/ if (b.stm == BLACK) result = -result; // save value in the eval tt tteval_save(result); return result; } /*********************************************************************** * 2. Preparatory routines * ***********************************************************************/ void eval_setPieceLists() { /*********************************************************************** * Create local lists of pieces and pawns. This is done to avoid * * looping through the entire board three times: for pawns, for pieces * * and again evaluating mobility if lazy eval does not produce a cutoff.* ***********************************************************************/ pieceIndex = 0; pawnIndex = 0; for (U8 row = 0; row < 8; row++) for (U8 col = 0; col < 8; col++) { S8 sq = row * 16 + col; if (b.color[sq] != COLOR_EMPTY) { if (b.pieces[sq] == PAWN) { pawnList[pawnIndex] = sq; ++pawnIndex; } else { pieceList[pieceIndex] = sq; ++pieceIndex; } } } } /*********************************************************************** * This is a reduced version of the previous function, and it creates * * only a list of pawns. * ***********************************************************************/ void eval_setPawnLists() { pawnIndex = 0; for (U8 row = 0; row < 8; row++) for (U8 col = 0; col < 8; col++) { S8 sq = row * 16 + col; if (b.color[sq] != COLOR_EMPTY && b.pieces[sq] == PAWN) { pawnList[pawnIndex] = sq; ++pawnIndex; } } } void eval_clearVector() { v.MaterialAdjustement[WHITE] = 0; v.MaterialAdjustement[BLACK] = 0; v.PositionalThemes[WHITE] = 0; v.PositionalThemes[BLACK] = 0; v.KingAttackers[WHITE] = 0; v.KingAttackers[BLACK] = 0; v.KingPressure[WHITE] = 0; v.KingPressure[BLACK] = 0; v.Blockages[WHITE] = 0; v.Blockages[BLACK] = 0; v.Mobility[WHITE] = 0; v.Mobility[BLACK] = 0; } /************************************************************************ * 3. King safety evaluation * ************************************************************************/ int wKingEval(S8 sq) { int result = 0; if (p.PieceMaterial[WHITE] < e.ENDGAME_MAT) { result += e.endgame_king[sq]; } else { result += e.PIECESQUARE[KING][WHITE][sq]; result += wKingShield(); result -= scaleAttacks(v.KingPressure[BLACK], v.KingAttackers[BLACK]); /* Scale the middlegame king evaluation against remaining enemy material */ result *= p.PieceMaterial[BLACK]; result /= e.START_MATERIAL; } return result; } int bKingEval(S8 sq) { int result = 0; if (p.PieceMaterial[BLACK] < e.ENDGAME_MAT) { result += e.endgame_king[sq]; } else { result += e.PIECESQUARE[KING][BLACK][sq]; result += bKingShield(); result -= scaleAttacks(v.KingPressure[WHITE], v.KingAttackers[WHITE]); /* Scale the middlegame king evaluation against remaining enemy material */ result *= p.PieceMaterial[WHITE]; result /= e.START_MATERIAL; } return result; } int scaleAttacks(int attack_value, int n_of_attackers) { int result; switch (n_of_attackers) { case 0: result = 0; case 1: result = 0; case 2: result = attack_value; case 3: result = (attack_value * 4) / 3; case 4: result = (attack_value * 3) / 2; default: result = attack_value * 2; } return result; } int wKingShield() { int result = 0; /* king on the kingside */ if (COL(p.KingLoc[WHITE]) > COL_E) { if (isPiece(WHITE, PAWN, F2)) result += e.SHIELD_1; else if (isPiece(WHITE, PAWN, F3)) result += e.SHIELD_2; if (isPiece(WHITE, PAWN, G2)) result += e.SHIELD_1; else if (isPiece(WHITE, PAWN, G3)) result += e.SHIELD_2; else if (p.PawnsOnFile[WHITE][COL_G] == 0) result -= e.P_NO_SHIELD; if (isPiece(WHITE, PAWN, H2)) result += e.SHIELD_1; else if (isPiece(WHITE, PAWN, H3)) result += e.SHIELD_2; else if (p.PawnsOnFile[WHITE][COL_H] == 0) result -= e.P_NO_SHIELD; } /* king on the queenside */ else if (COL(p.KingLoc[WHITE]) < COL_D) { if (isPiece(WHITE, PAWN, A2)) result += e.SHIELD_1; else if (isPiece(WHITE, PAWN, A3)) result += e.SHIELD_2; else if (p.PawnsOnFile[WHITE][COL_A] == 0) result -= e.P_NO_SHIELD; if (isPiece(WHITE, PAWN, B2)) result += e.SHIELD_1; else if (isPiece(WHITE, PAWN, B3)) result += e.SHIELD_2; else if (p.PawnsOnFile[WHITE][COL_B] == 0) result -= e.P_NO_SHIELD; if (isPiece(WHITE, PAWN, C2)) result += e.SHIELD_1; else if (isPiece(WHITE, PAWN, C3)) result += e.SHIELD_2; } return result; } int bKingShield() { int result = 0; /* king on the kingside */ if (COL(p.KingLoc[BLACK]) > COL_E) { if (isPiece(BLACK, PAWN, F7)) result += e.SHIELD_1; else if (isPiece(BLACK, PAWN, F6)) result += e.SHIELD_2; if (isPiece(BLACK, PAWN, G7)) result += e.SHIELD_1; else if (isPiece(BLACK, PAWN, G6)) result += e.SHIELD_2; else if (p.PawnsOnFile[BLACK][COL_G] == 0) result -= e.P_NO_SHIELD; if (isPiece(BLACK, PAWN, H7)) result += e.SHIELD_1; else if (isPiece(BLACK, PAWN, H6)) result += e.SHIELD_2; else if (p.PawnsOnFile[BLACK][COL_H] == 0) result -= e.P_NO_SHIELD; } /* king on the queenside */ else if (COL(p.KingLoc[BLACK]) < COL_D) { if (isPiece(BLACK, PAWN, A7)) result += e.SHIELD_1; else if (isPiece(BLACK, PAWN, A6)) result += e.SHIELD_2; else if (p.PawnsOnFile[BLACK][COL_A] == 0) result -= e.P_NO_SHIELD; if (isPiece(BLACK, PAWN, B7)) result += e.SHIELD_1; else if (isPiece(BLACK, PAWN, B6)) result += e.SHIELD_2; else if (p.PawnsOnFile[BLACK][COL_B] == 0) result -= e.P_NO_SHIELD; if (isPiece(BLACK, PAWN, C7)) result += e.SHIELD_1; else if (isPiece(BLACK, PAWN, C6)) result += e.SHIELD_2; } return result; } /********************************************************************************* * 4. Pawn structure evaluaton * *********************************************************************************/ int getPawnScore() { int result; /***************************************************************************** * This function wraps hashing mechanism around evalPawnStructure(). Please * * note that since we use the pawn hashtable, evalPawnStructure() must not * * take into account the piece position. In a more elaborate program, pawn * * hashtable would contain only the characteristics of pawn structure, and * * scoring them in conjunction with the piece position would have been done * * elsewhere. * ******************************************************************************/ int probeval = ttpawn_probe(); if (probeval != INVALID) return probeval; result = evalPawnStructure(); ttpawn_save(result); return result; } /* a clone of getPawnScore, used in fastEval, when the pawn list is not set */ int getPawnScoreNoList() { int result; int probeval = ttpawn_probe(); if (probeval != INVALID) return probeval; eval_setPawnLists(); result = evalPawnStructure(); ttpawn_save(result); return result; } int evalPawnStructure() { int result = 0; /* 1. evaluate pawn center */ result += evalPawnCenter(); /* 2. evaluate doubled/tripled pawns */ for (U8 col = 0; col < 8; col++) { result -= e.P_MULTI_PAWN[p.PawnsOnFile[WHITE][col]]; result += e.P_MULTI_PAWN[p.PawnsOnFile[BLACK][col]]; } /* 3. core procedure: loop through pawn list, evaluating each pawn on the board */ for (U8 i = 0; i < pawnIndex; i++) { S8 sq = pawnList[i]; if (b.color[sq] == WHITE) result += wPawnEval(sq); else result -= bPawnEval(sq); } return result; } int evalPawnCenter() { int result = 0; if (isPiece(WHITE, PAWN, D4)) { if (isPiece(WHITE, PAWN, E4)) result += e.DUO_D4E4; if (isPiece(WHITE, PAWN, C4)) result += e.DUO_D4C4; if (isPiece(WHITE, PAWN, E3)) result += e.DUO_D4E3; } if (isPiece(WHITE, PAWN, E4)) { if (isPiece(WHITE, PAWN, F4)) result += e.DUO_E4F4; if (isPiece(WHITE, PAWN, D3)) result += e.DUO_E4D3; } if (isPiece(BLACK, PAWN, D5)) { if (isPiece(BLACK, PAWN, E5)) result -= e.DUO_D4E4; if (isPiece(BLACK, PAWN, C5)) result -= e.DUO_D4C4; if (isPiece(BLACK, PAWN, E6)) result -= e.DUO_D4E3; } if (isPiece(BLACK, PAWN, E5)) { if (isPiece(BLACK, PAWN, F5)) result -= e.DUO_E4F4; if (isPiece(BLACK, PAWN, D6)) result -= e.DUO_E4D3; } return result; } int wPawnEval(S8 sq) { int result = 0; /* 1. Evaluate passed pawns, scoring them higher if they are protected by friendly pawns */ if (isWPFree(sq)) { if (isWPSupported(sq)) result += e.w_protected_passer[sq]; else result += e.w_passed_pawn[sq]; } /* 2. Evaluate weak pawns */ if (isWPWeak(sq)) { result += e.w_weak_pawn[sq]; /* weak pawns on half-open files tend to be even weaker */ if (p.PawnsOnFile[BLACK][COL(sq)] == 0) result -= 4; } return result; } int bPawnEval(S8 sq) { int result = 0; /* 1. Evaluate passed pawns, scoring them higher if they are protected by friendly pawns */ if (isBPFree(sq)) { if (isBPSupported(sq)) result += e.b_protected_passer[sq]; else result += e.b_passed_pawn[sq]; } /* 2. Evaluate weak pawns */ if (isBPWeak(sq)) { result += e.b_weak_pawn[sq]; /* weak pawns on half-open files tend to be even weaker */ if (p.PawnsOnFile[WHITE][COL(sq)] == 0) result -= 4; } return result; } int isWPFree(S8 sq) { S8 nextSq = sq + NORTH; while (IS_SQ(nextSq)) { /* either blocked by enemy pawn or doubled */ if (b.pieces[nextSq] == PAWN) return 0; if (IS_SQ(nextSq + WEST) && isPiece(BLACK, PAWN, nextSq + WEST)) return 0; if (IS_SQ(nextSq + EAST) && isPiece(BLACK, PAWN, nextSq + EAST)) return 0; nextSq += NORTH; } return 1; } int isBPFree(S8 sq) { S8 nextSq = sq + SOUTH; while (IS_SQ(nextSq)) { /* either blocked by enemy pawn or doubled */ if (b.pieces[nextSq] == PAWN) return 0; if (IS_SQ(nextSq + WEST) && isPiece(WHITE, PAWN, nextSq + WEST)) return 0; if (IS_SQ(nextSq + EAST) && isPiece(WHITE, PAWN, nextSq + EAST)) return 0; nextSq += SOUTH; } return 1; } int isWPWeak(S8 sq) { S8 nextSq = sq; while (IS_SQ(nextSq)) { if (IS_SQ(nextSq + WEST) && isPiece(WHITE, PAWN, nextSq + WEST)) return 0; if (IS_SQ(nextSq + EAST) && isPiece(WHITE, PAWN, nextSq + EAST)) return 0; nextSq += SOUTH; } return 1; } int isBPWeak(S8 sq) { S8 nextSq = sq; while (IS_SQ(nextSq)) { if (IS_SQ(nextSq + WEST) && isPiece(BLACK, PAWN, nextSq + WEST)) return 0; if (IS_SQ(nextSq + EAST) && isPiece(BLACK, PAWN, nextSq + EAST)) return 0; nextSq += NORTH; } return 1; } /**************************************************************** * The next two procedures are used in passed pawn evaluation, * * the assumption being that passed pawns that are defended or * * or whose stop square is protected by a friendly pawn tend * * to be stronger. * *****************************************************************/ int isWPSupported(S8 sq) { if (IS_SQ(sq + WEST) && isPiece(WHITE, PAWN, sq + WEST)) return 1; if (IS_SQ(sq + EAST) && isPiece(WHITE, PAWN, sq + EAST)) return 1; if (IS_SQ(sq + SE) && isPiece(WHITE, PAWN, sq + SE)) return 1; if (IS_SQ(sq + SW) && isPiece(WHITE, PAWN, sq + SW)) return 1; return 0; } int isBPSupported(S8 sq) { if (IS_SQ(sq + WEST) && isPiece(BLACK, PAWN, sq + WEST)) return 1; if (IS_SQ(sq + EAST) && isPiece(BLACK, PAWN, sq + EAST)) return 1; if (IS_SQ(sq + NE) && isPiece(BLACK, PAWN, sq + NE)) return 1; if (IS_SQ(sq + NW) && isPiece(BLACK, PAWN, sq + NW)) return 1; return 0; } /************************************************************************************ * 5. Evaluation of pieces * ************************************************************************************/ void wKnightEval(S8 sq) { /* material value adjustement based on the no. of own pawns */ v.MaterialAdjustement[WHITE] += knight_adj[p.PieceCount[WHITE][PAWN]]; /* mobility and king attacks calculation */ v.Mobility[WHITE] += wKnightMob(sq); /* trapped or blocking knight */ switch (sq) { case A8: if (isPiece(BLACK, PAWN, A7) || isPiece(BLACK, PAWN, C7)) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A8; break; case H8: if (isPiece(BLACK, PAWN, H7) || isPiece(BLACK, PAWN, F7)) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A8; break; case A7: if (isPiece(BLACK, PAWN, A6) && isPiece(BLACK, PAWN, B7)) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A7; break; case H7: if (isPiece(BLACK, PAWN, H6) && isPiece(BLACK, PAWN, G7)) v.Blockages[WHITE] -= e.P_KNIGHT_TRAPPED_A7; break; case C3: if (isPiece(WHITE, PAWN, C2) && isPiece(WHITE, PAWN, D4) && !isPiece(WHITE, PAWN, E4)) v.Blockages[WHITE] -= e.P_C3_KNIGHT; break; } } void bKnightEval(S8 sq) { /* material value adjustement based on the no. of own pawns */ v.MaterialAdjustement[BLACK] += knight_adj[p.PieceCount[BLACK][PAWN]]; /* mobility and king attack calculation */ v.Mobility[BLACK] += bKnightMob(sq); /* trapped or blocking knight */ switch (sq) { case A1: if (isPiece(WHITE, PAWN, A2) || isPiece(WHITE, PAWN, C2)) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A8; break; case H1: if (isPiece(WHITE, PAWN, H2) || isPiece(WHITE, PAWN, F2)) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A8; break; case A2: if (isPiece(WHITE, PAWN, A3) && isPiece(WHITE, PAWN, B2)) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A7; break; case H2: if (isPiece(WHITE, PAWN, H3) && isPiece(WHITE, PAWN, G2)) v.Blockages[BLACK] -= e.P_KNIGHT_TRAPPED_A7; break; case C6: if (isPiece(BLACK, PAWN, C7) && isPiece(BLACK, PAWN, D5) && !isPiece(BLACK, PAWN, E5)) v.Blockages[BLACK] -= e.P_C3_KNIGHT; break; } } void wBishopEval(S8 sq) { /* mobility and king attack calculaion */ v.Mobility[WHITE] += wBishopMob(sq); /* trapped bishop and returning bishop */ switch (sq) { case A7: if (isPiece(BLACK, PAWN, B6)) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break; case H7: if (isPiece(BLACK, PAWN, G6)) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break; case B8: if (isPiece(BLACK, PAWN, C7)) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break; case G8: if (isPiece(BLACK, PAWN, F7)) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A7; break; case A6: if (isPiece(BLACK, PAWN, B5)) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A6; break; case H6: if (isPiece(BLACK, PAWN, G5)) v.Blockages[WHITE] -= e.P_BISHOP_TRAPPED_A6; break; case F1: if (isPiece(WHITE, KING, G1)) v.PositionalThemes[WHITE] += e.RETURNING_BISHOP; break; case C1: if (isPiece(WHITE, KING, B1)) v.PositionalThemes[WHITE] += e.RETURNING_BISHOP; break; } } void bBishopEval(S8 sq) { /* mobility and king attack calculation */ v.Mobility[BLACK] += bBishopMob(sq); /* trapped bishop and returning bishop */ switch (sq) { case A2: if (isPiece(WHITE, PAWN, B3)) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break; case H2: if (isPiece(WHITE, PAWN, G3)) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break; case B1: if (isPiece(WHITE, PAWN, C2)) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break; case G1: if (isPiece(WHITE, PAWN, F2)) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A7; break; case A3: if (isPiece(WHITE, PAWN, B4)) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A6; break; case H3: if (isPiece(WHITE, PAWN, G4)) v.Blockages[BLACK] -= e.P_BISHOP_TRAPPED_A6; break; case F8: if (isPiece(BLACK, KING, G8)) v.PositionalThemes[BLACK] += e.RETURNING_BISHOP; break; case C8: if (isPiece(BLACK, KING, B8)) v.PositionalThemes[BLACK] += e.RETURNING_BISHOP; break; } } void wRookEval(S8 sq) { /* material value adjustement based on the no. of own pawns */ v.MaterialAdjustement[WHITE] += rook_adj[p.PieceCount[WHITE][PAWN]]; /* mobility and king attack calculation */ v.Mobility[WHITE] += wRookMob(sq); /* open and half-open files */ if (p.PawnsOnFile[WHITE][COL(sq)] == 0) { if (p.PawnsOnFile[BLACK][COL(sq)] == 0) v.PositionalThemes[WHITE] += e.ROOK_OPEN; else v.PositionalThemes[WHITE] += e.ROOK_HALF; } } void bRookEval(S8 sq) { /* material value adjustement based on the no. of own pawns */ v.MaterialAdjustement[BLACK] += rook_adj[p.PieceCount[BLACK][PAWN]]; /* mobility and king attack calculation */ v.Mobility[BLACK] += bRookMob(sq); /* open and half-open files */ if (p.PawnsOnFile[BLACK][COL(sq)] == 0) { if (p.PawnsOnFile[WHITE][COL(sq)] == 0) v.PositionalThemes[BLACK] += e.ROOK_OPEN; else v.PositionalThemes[BLACK] += e.ROOK_HALF; } } void wQueenEval(S8 sq) { /* mobility and king attack calculation */ v.Mobility[WHITE] += wQueenMob(sq); /* penalize premature developement */ if (ROW(sq) > ROW_2) { if (isPiece(WHITE, KNIGHT, B1)) v.PositionalThemes[WHITE] -= 2; if (isPiece(WHITE, BISHOP, C1)) v.PositionalThemes[WHITE] -= 2; if (isPiece(WHITE, BISHOP, F1)) v.PositionalThemes[WHITE] -= 2; if (isPiece(WHITE, KNIGHT, G1)) v.PositionalThemes[WHITE] -= 2; } } void bQueenEval(S8 sq) { /* mobility and king attack calculation */ v.Mobility[BLACK] += bQueenMob(sq); /* penalize premature developement */ if (ROW(sq) < ROW_7) { if (isPiece(BLACK, KNIGHT, B8)) v.PositionalThemes[BLACK] -= 2; if (isPiece(BLACK, BISHOP, C8)) v.PositionalThemes[BLACK] -= 2; if (isPiece(BLACK, BISHOP, F8)) v.PositionalThemes[BLACK] -= 2; if (isPiece(BLACK, KNIGHT, G8)) v.PositionalThemes[BLACK] -= 2; } } /********************************************************************************** * 6. Mobility and king attack evaluation * **********************************************************************************/ int wKnightMob(S8 sq) { isAttacker = 0; int localMobility = leaperMobility(WHITE, sq, KNIGHT, MINOR_ATT); if (isAttacker) ++v.KingAttackers[WHITE]; return knight_mob[localMobility]; } int bKnightMob(S8 sq) { isAttacker = 0; int localMobility = leaperMobility(BLACK, sq, KNIGHT, MINOR_ATT); if (isAttacker) ++v.KingAttackers[BLACK]; return knight_mob[localMobility]; } int wBishopMob(S8 sq) { isAttacker = 0; int localMobility = sliderMobility(WHITE, sq, NE, MINOR_ATT) + sliderMobility(WHITE, sq, NW, MINOR_ATT) + sliderMobility(WHITE, sq, SE, MINOR_ATT) + sliderMobility(WHITE, sq, SW, MINOR_ATT); if (isAttacker) ++v.KingAttackers[WHITE]; return bish_mob[localMobility]; } int bBishopMob(S8 sq) { isAttacker = 0; int localMobility = sliderMobility(BLACK, sq, NE, MINOR_ATT) + sliderMobility(BLACK, sq, NW, MINOR_ATT) + sliderMobility(BLACK, sq, SE, MINOR_ATT) + sliderMobility(BLACK, sq, SW, MINOR_ATT); if (isAttacker) ++v.KingAttackers[BLACK]; return bish_mob[localMobility]; } int wRookMob(S8 sq) { isAttacker = 0; int localMobility = sliderMobility(WHITE, sq, NORTH, ROOK_ATT) + sliderMobility(WHITE, sq, SOUTH, ROOK_ATT) + sliderMobility(WHITE, sq, EAST, ROOK_ATT) + sliderMobility(WHITE, sq, WEST, ROOK_ATT); if (isAttacker) ++v.KingAttackers[WHITE]; return rook_mob[localMobility]; } int bRookMob(S8 sq) { isAttacker = 0; int localMobility = sliderMobility(BLACK, sq, NORTH, ROOK_ATT) + sliderMobility(BLACK, sq, SOUTH, ROOK_ATT) + sliderMobility(BLACK, sq, EAST, ROOK_ATT) + sliderMobility(BLACK, sq, WEST, ROOK_ATT); if (isAttacker) ++v.KingAttackers[BLACK]; return rook_mob[localMobility]; } // with queen, currently we look for king attacks, not for mobility int wQueenMob(S8 sq) { isAttacker = 0; int localMobility = sliderMobility(WHITE, sq, NORTH, QUEEN_ATT) + sliderMobility(WHITE, sq, SOUTH, QUEEN_ATT) + sliderMobility(WHITE, sq, EAST, QUEEN_ATT) + sliderMobility(WHITE, sq, WEST, QUEEN_ATT) + sliderMobility(WHITE, sq, NE, QUEEN_ATT) + sliderMobility(WHITE, sq, NW, QUEEN_ATT) + sliderMobility(WHITE, sq, SE, QUEEN_ATT) + sliderMobility(WHITE, sq, SW, QUEEN_ATT); if (isAttacker) ++v.KingAttackers[WHITE]; return 0; } int bQueenMob(S8 sq) { isAttacker = 0; int localMobility = sliderMobility(BLACK, sq, NORTH, QUEEN_ATT) + sliderMobility(BLACK, sq, SOUTH, QUEEN_ATT) + sliderMobility(BLACK, sq, EAST, QUEEN_ATT) + sliderMobility(BLACK, sq, WEST, QUEEN_ATT) + sliderMobility(BLACK, sq, NE, QUEEN_ATT) + sliderMobility(BLACK, sq, NW, QUEEN_ATT) + sliderMobility(BLACK, sq, SE, QUEEN_ATT) + sliderMobility(BLACK, sq, SW, QUEEN_ATT); if (isAttacker) ++v.KingAttackers[BLACK]; return 0; } int sliderMobility(U8 color, S8 sq, int vect, int attBonus) { int nextSq = sq + vect; int result = 0; while (IS_SQ(nextSq)) { if (e.sqNearK[!color][p.KingLoc[!color]][nextSq]) { isAttacker = 1; v.KingPressure[color] += attBonus; } if (b.color[nextSq] != COLOR_EMPTY) { if (b.color[nextSq] != color) return result + 1; return result; } ++result; nextSq = nextSq + vect; } return result; } int leaperMobility(U8 color, S8 sq, char byPiece, int attBonus) { S8 nextSq; int result = 0; for (U8 dir = 0; dir<8; dir++) { nextSq = sq + vector[byPiece][dir]; if (IS_SQ(nextSq) && b.color[nextSq] != color) { /* king attack */ if (e.sqNearK[!color][p.KingLoc[!color]][nextSq]) { isAttacker = 1; v.KingPressure[color] += attBonus; } ++result; } } return result; } /*************************************************************************************** * 7. Pattern detection * ***************************************************************************************/ void blockedCentralPawns() { if (isPiece(WHITE, PAWN, D2) && b.color[D3] != COLOR_EMPTY) v.Blockages[WHITE] -= e.P_BLOCK_CENTRAL_PAWN; if (isPiece(WHITE, PAWN, E2) && b.color[E3] != COLOR_EMPTY) v.Blockages[WHITE] -= e.P_BLOCK_CENTRAL_PAWN; if (isPiece(BLACK, PAWN, D7) && b.color[D6] != COLOR_EMPTY) v.Blockages[BLACK] -= e.P_BLOCK_CENTRAL_PAWN; if (isPiece(BLACK, PAWN, E7) && b.color[E6] != COLOR_EMPTY) v.Blockages[BLACK] -= e.P_BLOCK_CENTRAL_PAWN; } void blockedRooks() { if ((isPiece(WHITE, KING, F1) || isPiece(WHITE, KING, G1)) && (isPiece(WHITE, ROOK, H1) || isPiece(WHITE, ROOK, G1)) ) v.Blockages[WHITE] -= e.P_KING_BLOCKS_ROOK; if ((isPiece(WHITE, KING, C1) || isPiece(WHITE, KING, B1)) && (isPiece(WHITE, ROOK, A1) || isPiece(WHITE, ROOK, B1)) ) v.Blockages[WHITE] -= e.P_KING_BLOCKS_ROOK; if ((isPiece(BLACK, KING, F8) || isPiece(BLACK, KING, G8)) && (isPiece(BLACK, ROOK, H8) || isPiece(BLACK, ROOK, G8)) ) v.Blockages[BLACK] -= e.P_KING_BLOCKS_ROOK; if ((isPiece(BLACK, KING, C8) || isPiece(BLACK, KING, B8)) && (isPiece(BLACK, ROOK, A8) || isPiece(BLACK, ROOK, B8)) ) v.Blockages[BLACK] -= e.P_KING_BLOCKS_ROOK; } void slavMistake() { if (isPiece(WHITE, PAWN, D4) && isPiece(WHITE, PAWN, C5) && isPiece(BLACK, PAWN, D5) && isPiece(BLACK, PAWN, C6)) v.PositionalThemes[WHITE] -= e.P_SLAV_MISTAKE; if (isPiece(BLACK, PAWN, D5) && isPiece(BLACK, PAWN, C4) && isPiece(WHITE, PAWN, D4) && isPiece(WHITE, PAWN, C3)) v.PositionalThemes[BLACK] -= e.P_SLAV_MISTAKE; } void evalFianchetto() { if (isPiece(WHITE, PAWN, G3)) { if (isPiece(WHITE, BISHOP, G2)) v.PositionalThemes[WHITE] += e.FIANCHETTO; else { if (!isPiece(WHITE, BISHOP, F3) && !isPiece(WHITE, BISHOP, H1) && !isPiece(WHITE, BISHOP, H3)) v.PositionalThemes[WHITE] -= e.P_NO_FIANCHETTO; } } if (isPiece(WHITE, PAWN, B3)) { if (isPiece(WHITE, BISHOP, B2)) v.PositionalThemes[WHITE] += e.FIANCHETTO; else { if (!isPiece(WHITE, BISHOP, C3) && !isPiece(WHITE, BISHOP, A1) && !isPiece(WHITE, BISHOP, A3)) v.PositionalThemes[WHITE] -= e.P_NO_FIANCHETTO; } } if (isPiece(BLACK, PAWN, G6)) { if (isPiece(BLACK, BISHOP, G7)) v.PositionalThemes[BLACK] += e.FIANCHETTO; else { if (!isPiece(BLACK, BISHOP, F6) && !isPiece(BLACK, BISHOP, H8) && !isPiece(BLACK, BISHOP, H6)) v.PositionalThemes[BLACK] -= e.P_NO_FIANCHETTO; } } if (isPiece(BLACK, PAWN, B6)) { if (isPiece(BLACK, BISHOP, B7)) v.PositionalThemes[BLACK] += e.P_NO_FIANCHETTO; else { if (!isPiece(BLACK, BISHOP, C6) && !isPiece(BLACK, BISHOP, A8) && !isPiece(BLACK, BISHOP, A6)) v.PositionalThemes[BLACK] -= e.P_NO_FIANCHETTO; } } } /* determine if two squares lie on the same or neighbouring columns */ int isNearCol(S8 sq1, S8 sq2) { U8 c1 = COL(sq1); U8 c2 = COL(sq2); U8 hor_dist = (U8)abs(c1 - c2); if (hor_dist < 2) return 1; else return 0; } int isPiece(U8 color, U8 piece, S8 sq) { return ((b.pieces[sq] == piece) && (b.color[sq] == color)); } /*************************************************************************************** * 8. Printing eval results * ***************************************************************************************/ void printEval() { eval(-30000, 30000); printf("------------------------------------------\n"); printf("Total value (for side to move): %d \n", eval(-INFINITY, INFINITY)); printf("Material balance : %d \n", p.PieceMaterial[WHITE] + p.PawnMaterial[WHITE] - p.PieceMaterial[BLACK] - p.PawnMaterial[BLACK]); printf("Material adjustement: %d \n", v.MaterialAdjustement[WHITE] - v.MaterialAdjustement[BLACK]); printf("Piece/square tables : %d \n", p.Pcsq[WHITE] - p.Pcsq[BLACK]); printf("Pawn structure : %d \n", evalPawnStructure()); printf("Blockages : %d \n", v.Blockages[WHITE] - v.Blockages[BLACK]); printf("Positional themes : %d \n", v.PositionalThemes[WHITE] - v.PositionalThemes[BLACK]); printf("Mobility: white %d, black %d, total %d \n", v.Mobility[WHITE], v.Mobility[BLACK], v.Mobility[WHITE] - v.Mobility[BLACK]); printf("King pressure: white %d, black %d \n", v.KingPressure[WHITE], v.KingPressure[BLACK]); printf("King attackers: white %d, black %d \n", v.KingAttackers[WHITE], v.KingAttackers[BLACK]); printf("Kings: white %d , black %d, total: %d \n", wKingEval(p.KingLoc[WHITE]), bKingEval(p.KingLoc[BLACK]), wKingEval(p.KingLoc[WHITE]) - bKingEval(p.KingLoc[BLACK])); printf("Tempo: "); if (b.stm == WHITE) printf("%d", e.TEMPO); else printf("%d", -e.TEMPO); printf("\n"); printf("------------------------------------------\n"); }