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");
}