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 kingsif(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 ringif( 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 kingint 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;elsereturn 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))return0;if(( p.PawnMaterial[BLACK]==0)&&( p.PieceMaterial[BLACK]<400)&&( result <0))return0;/*******************************************************************
* 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.2008if( 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 ){case0: result =0;case1: result =0;case2: result = attack_value;case3: result =( attack_value *4)/3;case4: 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;elseif( isPiece(WHITE, PAWN, F3)) result += e.SHIELD_2;if( isPiece(WHITE, PAWN, G2)) result += e.SHIELD_1;elseif( isPiece(WHITE, PAWN, G3)) result += e.SHIELD_2;elseif( p.PawnsOnFile[WHITE][ COL_G ]==0) result -= e.P_NO_SHIELD;if( isPiece(WHITE, PAWN, H2)) result += e.SHIELD_1;elseif( isPiece(WHITE, PAWN, H3)) result += e.SHIELD_2;elseif( p.PawnsOnFile[WHITE][ COL_H ]==0) result -= e.P_NO_SHIELD;}/* king on the queenside */elseif( COL(p.KingLoc[WHITE])< COL_D ){if( isPiece(WHITE, PAWN, A2)) result += e.SHIELD_1;elseif( isPiece(WHITE, PAWN, A3)) result += e.SHIELD_2;elseif( p.PawnsOnFile[WHITE][ COL_A ]==0) result -= e.P_NO_SHIELD;if( isPiece(WHITE, PAWN, B2)) result += e.SHIELD_1;elseif( isPiece(WHITE, PAWN, B3)) result += e.SHIELD_2;elseif( p.PawnsOnFile[WHITE][ COL_B ]==0) result -= e.P_NO_SHIELD;if( isPiece(WHITE, PAWN, C2)) result += e.SHIELD_1;elseif( 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;elseif( isPiece(BLACK, PAWN, F6)) result += e.SHIELD_2;if( isPiece(BLACK, PAWN, G7)) result += e.SHIELD_1;elseif( isPiece(BLACK, PAWN, G6)) result += e.SHIELD_2;elseif( p.PawnsOnFile[BLACK][ COL_G ]==0) result -= e.P_NO_SHIELD;if( isPiece(BLACK, PAWN, H7)) result += e.SHIELD_1;elseif( isPiece(BLACK, PAWN, H6)) result += e.SHIELD_2;elseif( p.PawnsOnFile[BLACK][ COL_H ]==0) result -= e.P_NO_SHIELD;}/* king on the queenside */elseif( COL(p.KingLoc[BLACK])< COL_D ){if( isPiece(BLACK, PAWN, A7)) result += e.SHIELD_1;elseif( isPiece(BLACK, PAWN, A6)) result += e.SHIELD_2;elseif( p.PawnsOnFile[BLACK][ COL_A ]==0) result -= e.P_NO_SHIELD;if( isPiece(BLACK, PAWN, B7)) result += e.SHIELD_1;elseif( isPiece(BLACK, PAWN, B6)) result += e.SHIELD_2;elseif( p.PawnsOnFile[BLACK][ COL_B ]==0) result -= e.P_NO_SHIELD;if( isPiece(BLACK, PAWN, C7)) result += e.SHIELD_1;elseif( 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 )return0;if( IS_SQ(nextSq + WEST)&& isPiece(BLACK, PAWN, nextSq + WEST))return0;if( IS_SQ(nextSq + EAST)&& isPiece(BLACK, PAWN, nextSq + EAST))return0;
nextSq += NORTH;}return1;}int isBPFree(S8 sq){
S8 nextSq = sq + SOUTH;while( IS_SQ(nextSq)){/* either blocked by enemy pawn or doubled */if( b.pieces[nextSq]== PAWN )return0;if( IS_SQ(nextSq + WEST)&& isPiece(WHITE, PAWN, nextSq + WEST))return0;if( IS_SQ(nextSq + EAST)&& isPiece(WHITE, PAWN, nextSq + EAST))return0;
nextSq += SOUTH;}return1;}int isWPWeak(S8 sq){
S8 nextSq = sq;while( IS_SQ(nextSq)){if( IS_SQ(nextSq + WEST)&& isPiece(WHITE, PAWN, nextSq + WEST))return0;if( IS_SQ(nextSq + EAST)&& isPiece(WHITE, PAWN, nextSq + EAST))return0;
nextSq += SOUTH;}return1;}int isBPWeak(S8 sq){
S8 nextSq = sq;while( IS_SQ(nextSq)){if( IS_SQ(nextSq + WEST)&& isPiece(BLACK, PAWN, nextSq + WEST))return0;if( IS_SQ(nextSq + EAST)&& isPiece(BLACK, PAWN, nextSq + EAST))return0;
nextSq += NORTH;}return1;}/****************************************************************
* 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))return1;if( IS_SQ(sq+EAST)&& isPiece(WHITE,PAWN, sq + EAST))return1;if( IS_SQ( sq+SE )&& isPiece(WHITE,PAWN, sq + SE ))return1;if( IS_SQ( sq+SW )&& isPiece(WHITE,PAWN, sq + SW ))return1;return0;}int isBPSupported(S8 sq){if( IS_SQ(sq+WEST)&& isPiece(BLACK,PAWN, sq + WEST))return1;if( IS_SQ(sq+EAST)&& isPiece(BLACK,PAWN, sq + EAST))return1;if( IS_SQ( sq+NE )&& isPiece(BLACK,PAWN, sq + NE ))return1;if( IS_SQ( sq+NW )&& isPiece(BLACK,PAWN, sq + NW ))return1;return0;}/************************************************************************************
* 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 mobilityint 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];return0;}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];return0;}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)return1;elsereturn0;}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);elseprintf("%d", -e.TEMPO);printf("\n");printf("------------------------------------------\n");}
This is a patch to the CPW-Engine, changing its evaluation function. Modifications include:
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:
The complete eval.cpp looks as follows:
Up one Level