#include "chess.h"
#include "data.h"
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* Evaluate() is used to evaluate the chess board. Broadly, it addresses *
* four (4) distinct areas: (1) material score which is simply a summing of *
* piece types multiplied by piece values; (2) pawn scoring which considers *
* placement of pawns and also evaluates passed pawns, particularly in end- *
* game situations; (3) piece scoring which evaluates the placement of each *
* piece as well as things like piece mobility; (4) king safety which *
* considers the pawn shelter around the king along with material present to *
* facilitate an attack. *
* *
*******************************************************************************
*/
int Evaluate(TREE * RESTRICT tree, int ply, int wtm, int alpha, int beta) {
PAWN_HASH_ENTRY *ptable;
PXOR *pxtable;
int score, side, majors, minors, can_win = 3;
int phase, lscore, cutoff;
/*
**********************************************************************
* *
* Initialize. *
* *
**********************************************************************
*/
cutoff = (TotalPieces(white, occupied) && TotalPieces(black, occupied))
? KNIGHT_VALUE : ROOK_VALUE;
lscore = (wtm) ? Material : -Material;
if (lscore + cutoff < alpha || lscore - cutoff > beta)
return lscore;
tree->dangerous[white] = (Queens(white) && TotalPieces(white, occupied) > 9)
|| (TotalPieces(white, rook) > 1 && TotalPieces(white, occupied) > 15);
tree->dangerous[black] = (Queens(black) && TotalPieces(black, occupied) > 9)
|| (TotalPieces(black, rook) > 1 && TotalPieces(black, occupied) > 15);
tree->evaluations++;
tree->score_mg = 0;
tree->score_eg = 0;
EvaluateMaterial(tree, wtm);
#if defined(SKILL)
if (skill < 100) {
int i, j;
for (i = 0; i < burnc[skill / 10]; i++)
for (j = 1; j < 10; j++)
burner[j - 1] = burner[j - 1] * burner[j];
}
#endif
/*
**********************************************************************
* *
* Check for draws due to insufficient material and adjust the score *
* as necessary. This code also handles a special endgame case *
* where one side has only a lone king, and the king has no legal *
* moves. This has been shown to break a few evaluation terms such *
* as bishop + wrong color rook pawn. If this case is detected, a *
* drawscore is returned. *
* *
**********************************************************************
*/
if (TotalPieces(white, occupied) < 13 && TotalPieces(black, occupied) < 13)
do {
/*
************************************************************
* *
* If neither side has any pieces, and both sides have *
* non-rookpawns, then either side can win. *
* *
************************************************************
*/
if (TotalPieces(white, occupied) == 0 &&
TotalPieces(black, occupied) == 0 && (Pawns(white) & not_rook_pawns
&& Pawns(black) & not_rook_pawns))
break;
/*
************************************************************
* *
* If one side is an exchange up, but has no pawns, then *
* that side can not possibly win. *
* *
************************************************************
*/
majors = TotalMajors(white) - TotalMajors(black);
if (Abs(majors) == 1) {
minors = TotalMinors(white) - TotalMinors(black);
if (majors == -minors) {
if (TotalPieces(black, pawn) == 0)
can_win &= 1;
if (TotalPieces(white, pawn) == 0)
can_win &= 2;
}
if (can_win == 0)
break;
}
/*
************************************************************
* *
* check several special cases, such as bishop + the wrong *
* rook pawn and adjust can_win accordingly. *
* *
************************************************************
*/
if (!EvaluateWinningChances(tree, white, wtm))
can_win &= 2;
if (!EvaluateWinningChances(tree, black, wtm))
can_win &= 1;
} while (0);
/*
**********************************************************************
* *
* Determine if this position should be evaluated to force mate *
* (neither side has pawns) or if it should be evaluated normally. *
* *
* Note the special case of no pawns, one side is ahead in total *
* material, but the game is a hopeless draw. KRN vs KR is one *
* example. If EvaluateWinningChances() determines that the side *
* with extra material can not win, the score is pulled closer to a *
* draw although it can not collapse completely to the drawscore as *
* it is possible to lose KRB vs KR if the KR side lets the king get *
* trapped on the edge of the board. *
* *
**********************************************************************
*/
tree->all_pawns = Pawns(black) | Pawns(white);
if (!tree->all_pawns) {
if (TotalPieces(white, occupied) > 3 || TotalPieces(black, occupied) > 3) {
if (Material > 0)
EvaluateMate(tree, white);
else if (Material < 0)
EvaluateMate(tree, black);
if (tree->score_eg > DrawScore(1) && !(can_win & 1))
tree->score_eg = tree->score_eg / 4;
if (tree->score_eg < DrawScore(1) && !(can_win & 2))
tree->score_eg = tree->score_eg / 4;
#if defined(SKILL)
if (skill < 100)
tree->score_eg =
(int) (skill * tree->score_eg / 100 + ((100 -
skill) * PAWN_VALUE * (uint64_t) Random32() /
0x100000000ull) / 100); // Pierre-Marie Baty -- added type cast
#endif
return (wtm) ? tree->score_eg : -tree->score_eg;
}
}
/*
**********************************************************************
* *
* Now evaluate pawns. If the pawn hash signature has not changed *
* from the last entry to Evaluate() then we already have everything *
* we need in the pawn hash entry. In this case, we do not need to *
* call EvaluatePawns() at all. EvaluatePawns() does all of the *
* analysis for information specifically regarding only pawns. In *
* many cases, it merely records the presence/absence of positional *
* pawn features because those features also depends on pieces. *
* Note that anything put into EvaluatePawns() can only consider the *
* placement of pawns. Kings or other pieces can not influence the *
* score because those pieces are not hashed into the pawn hash *
* signature. Violating this principle leads to lots of very *
* difficult and challenging debugging problems. *
* *
**********************************************************************
*/
else {
if (PawnHashKey == tree->pawn_score.key) {
tree->score_mg += tree->pawn_score.score_mg;
tree->score_eg += tree->pawn_score.score_eg;
}
/*
************************************************************
* *
* First check to see if this position has been handled *
* before. If so, we can skip the work saved in the pawn *
* hash table. *
* *
************************************************************
*/
else {
ptable = pawn_hash_table + (PawnHashKey & pawn_hash_mask);
pxtable = (PXOR *) & (tree->pawn_score);
tree->pawn_score = *ptable;
tree->pawn_score.key ^=
pxtable->entry[1] ^ pxtable->entry[2] ^ pxtable->entry[3];
if (tree->pawn_score.key != PawnHashKey) {
tree->pawn_score.key = PawnHashKey;
tree->pawn_score.score_mg = 0;
tree->pawn_score.score_eg = 0;
for (side = black; side <= white; side++)
EvaluatePawns(tree, side);
ptable->key =
pxtable->entry[0] ^ pxtable->entry[1] ^ pxtable->
entry[2] ^ pxtable->entry[3];
memcpy((char *) ptable
+ 8, (char *) &(tree
->pawn_score
) + 8, 20);
}
tree->score_mg += tree->pawn_score.score_mg;
tree->score_eg += tree->pawn_score.score_eg;
}
/*
**********************************************************************
* *
* If there are any passed pawns, first call EvaluatePassedPawns() *
* to evaluate them. Then, if one side has a passed pawn and the *
* other side has no pieces, call EvaluatePassedPawnRaces() to see *
* if the passed pawn can be stopped from promoting. *
* *
**********************************************************************
*/
if (tree->pawn_score.passed[black] || tree->pawn_score.passed[white]) {
for (side = black; side <= white; side++)
if (tree->pawn_score.passed[side])
EvaluatePassedPawns(tree, side, wtm);
if ((TotalPieces(white, occupied) == 0 &&
tree->pawn_score.passed[black])
|| (TotalPieces(black, occupied) == 0 &&
tree->pawn_score.passed[white]))
EvaluatePassedPawnRaces(tree, wtm);
}
}
/*
**********************************************************************
* *
* Call EvaluateDevelopment() to evaluate development. Note that we *
* only do this when either side has not castled at the root. *
* *
**********************************************************************
*/
for (side = black; side <= white; side++)
EvaluateDevelopment(tree, ply, side);
/*
**********************************************************************
* *
* Then evaluate pieces. *
* *
**********************************************************************
*/
phase =
Min(62, TotalPieces(white, occupied) + TotalPieces(black, occupied));
score = ((tree->score_mg * phase) + (tree->score_eg * (62 - phase))) / 62;
lscore = (wtm) ? score : -score;
cutoff = (tree->dangerous[white]
|| tree->dangerous[black]) ? 114 + phase : 102;
if (lscore + cutoff > alpha && lscore - cutoff < beta) {
tree->tropism[white] = 0;
tree->tropism[black] = 0;
for (side = black; side <= white; side++)
EvaluateKnights(tree, side);
for (side = black; side <= white; side++)
EvaluateBishops(tree, side);
for (side = black; side <= white; side++)
EvaluateRooks(tree, side);
for (side = black; side <= white; side++)
EvaluateQueens(tree, side);
for (side = black; side <= white; side++)
EvaluateKings(tree, ply, side);
}
/*
**********************************************************************
* *
* Adjust the score if the game is drawish but one side appears to *
* be significantly better according to the computed score. *
* *
**********************************************************************
*/
score = ((tree->score_mg * phase) + (tree->score_eg * (62 - phase))) / 62;
score = EvaluateDraws(tree, ply, can_win, score);
#if defined(SKILL)
if (skill < 100)
score =
(int) (skill * score / 100 + ((100 -
skill) * PAWN_VALUE * (uint64_t) Random32() / 0x100000000ull) /
100); // Pierre-Marie Baty -- added type cast
#endif
return (wtm) ? score : -score;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateBishops() is used to evaluate bishops. *
* *
*******************************************************************************
*/
void EvaluateBishops(TREE * RESTRICT tree, int side) {
uint64_t temp, moves;
int square, t, mobility;
int score_eg = 0, score_mg = 0, enemy = Flip(side);
/*
************************************************************
* *
* First, locate each bishop and add in its piece/square *
* score. *
* *
************************************************************
*/
for (temp = Bishops(side); temp; temp &= temp - 1) {
square = LSB(temp);
score_mg += bval[mg][side][square];
score_eg += bval[eg][side][square];
/*
************************************************************
* *
* Evaluate for "outposts" which is a bishop that can't be *
* driven off by an enemy pawn, and which is supported by *
* a friendly pawn. *
* *
* If the enemy has NO minor to take this bishop, then *
* increase the bonus. *
* *
* Note that the bishop_outpost array is overloaded to *
* serve a dual prupose. A negative value is used to flag *
* squares A7 and H7 to test for a trapped bishop. This *
* is done for speed. *
* *
************************************************************
*/
t = bishop_outpost[side][square];
if (t) {
if (t > 0) {
if (!(mask_no_pattacks[enemy][square] & Pawns(enemy))) {
if (pawn_attacks[enemy][square] & Pawns(side)) {
t += t / 2;
if (!Knights(enemy) && !(Color(square) & Bishops(enemy)))
t += bishop_outpost[side][square];
}
score_eg += t;
score_mg += t;
}
}
/*
************************************************************
* *
* Check to see if the bishop is trapped at a7 or h7 with *
* a pawn at b6 or g6 that has trapped the bishop. *
* *
************************************************************
*/
else {
if (square == sqflip[side][A7]) {
if (SetMask(sqflip[side][B6]) & Pawns(enemy)) {
score_eg -= bishop_trapped;
score_mg -= bishop_trapped;
}
} else if (square == sqflip[side][H7]) {
if (SetMask(sqflip[side][G6]) & Pawns(enemy)) {
score_eg -= bishop_trapped;
score_mg -= bishop_trapped;
}
}
}
}
/*
************************************************************
* *
* Mobility counts the number of squares the piece *
* attacks, and weighs each square according to *
* centralization. *
* *
************************************************************
*/
mobility = BishopMobility(square, OccupiedSquares);
if (mobility < 0 && (pawn_attacks[enemy][square] & Pawns(side))
&& (File(square) == FILEA || File(square) == FILEH))
mobility -= 8;
score_mg += mobility;
score_eg += mobility;
/*
************************************************************
* *
* Check for pawns on both wings, which makes a bishop *
* even more valuable against an enemy knight *
* *
************************************************************
*/
if (tree->all_pawns & mask_fgh && tree->all_pawns & mask_abc) {
score_mg += bishop_with_wing_pawns[mg];
score_eg += bishop_with_wing_pawns[eg];
}
/*
************************************************************
* *
* Adjust the tropism count for this piece. *
* *
************************************************************
*/
if (tree->dangerous[side]) {
moves = king_attacks[KingSQ(enemy)];
t = ((bishop_attacks[square] & moves)
&& ((BishopAttacks(square,
OccupiedSquares & ~(Queens(side)))) & moves)) ? 1 :
Distance(square, KingSQ(enemy));
tree->tropism[side] += king_tropism_b[t];
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateDevelopment() is used to encourage the program to develop its *
* pieces before moving its queen. Standard developmental principles are *
* applied. They include: (1) don't move the queen until minor pieces are *
* developed; (2) advance the center pawns as soon as possible; (3) don't *
* move the king unless its a castling move. *
* *
*******************************************************************************
*/
void EvaluateDevelopment(TREE * RESTRICT tree, int ply, int side) {
int score_mg = 0;
int enemy = Flip(side);
/*
************************************************************
* *
* First, some "thematic" things, which includes don't *
* block the c-pawn in queen-pawn openings. *
* *
************************************************************
*/
if (!(SetMask(sqflip[side][E4]) & Pawns(side))
&& SetMask(sqflip[side][D4]) & Pawns(side)
&& SetMask(sqflip[side][C2]) & Pawns(side)
&& SetMask(sqflip[side][C3]) & (Knights(side) | Bishops(side)))
score_mg -= development_thematic;
/*
************************************************************
* *
* If the king hasn't moved at the beginning of the *
* search, but it has moved somewhere in the current *
* search path, make *sure* it's a castle move or else *
* penalize the loss of castling privilege. *
* *
************************************************************
*/
if (Castle(1, side) > 0) {
int oq = (Queens(enemy)) ? 3 : 1;
if (Castle(ply, side) != Castle(1, side)) {
if (Castle(ply, side) == 0)
score_mg -= oq * development_losing_castle;
else if (Castle(ply, side) > 0)
score_mg -= (oq * development_losing_castle) / 2;
} else
score_mg -= oq * development_not_castled;
}
/*
************************************************************
* *
* Check for an undeveloped knight/rook combo *
* *
************************************************************
*/
if (PcOnSq(sqflip[side][B1]) == pieces[side][knight]
&& PcOnSq(sqflip[side][A1]) == pieces[side][rook])
score_mg -= undeveloped_piece;
if (PcOnSq(sqflip[side][G1]) == pieces[side][knight]
&& PcOnSq(sqflip[side][H1]) == pieces[side][rook])
score_mg -= undeveloped_piece;
tree->score_mg += sign[side] * score_mg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateDraws() is used to adjust the score based on whether the side *
* that appears to be better according the computed score can actually win *
* the game or not. If the answer is "no" then the score is reduced *
* significantly to reflect the lack of winning chances. *
* *
*******************************************************************************
*/
int EvaluateDraws(TREE * RESTRICT tree, int ply, int can_win, int score) {
/*
************************************************************
* *
* If the ending has only bishops of opposite colors, the *
* score is pulled closer to a draw. If the score says *
* one side is winning, but that side doesn't have enough *
* material to win, the score is also pulled closer to a *
* draw. *
* *
* If this is a pure BOC ending, it is very drawish unless *
* one side has at least 4 pawns. More pawns makes it *
* harder for a bishop and king to stop them all from *
* advancing. *
* *
************************************************************
*/
if (TotalPieces(white, occupied) <= 8 && TotalPieces(black, occupied) <= 8) {
if (TotalPieces(white, bishop) == 1 && TotalPieces(black, bishop) == 1)
if (square_color[LSB(Bishops(black))] !=
square_color[LSB(Bishops(white))]) {
if (TotalPieces(white, occupied) == 3 &&
TotalPieces(black, occupied) == 3 &&
((TotalPieces(white, pawn) < 4 && TotalPieces(black, pawn) < 4)
|| Abs(TotalPieces(white, pawn) - TotalPieces(black,
pawn)) < 2))
score = score / 2 + DrawScore(1);
else if (TotalPieces(white, occupied) == TotalPieces(black, occupied))
score = 3 * score / 4 + DrawScore(1);
}
}
if (can_win != 3) {
if (can_win != 1 && score > DrawScore(1))
score = score / 4 + DrawScore(1);
else if (can_win != 2 && score < DrawScore(1))
score = score / 4 + DrawScore(1);
}
/*
************************************************************
* *
* If we are running into the 50-move rule, then start *
* dragging the score toward draw. This is the idea of a *
* "weariness factor" as mentioned by Dave Slate many *
* times. This avoids slamming into a draw at move 50 and *
* having to move something quickly, rather than slowly *
* discovering that the score is dropping and that pushing *
* a pawn or capturing something will cause it to go back *
* to its correct value a bit more smoothly. *
* *
************************************************************
*/
if (Reversible(ply) > 80) {
int iscale = 101 - Reversible(ply);
score = DrawScore(1) + score * iscale / 20;
}
return score;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateHasOpposition() is used to determine if one king stands in *
* "opposition" to the other. If the kings are opposed on the same file or *
* else are opposed on the same diagonal, then the side not-to-move has the *
* opposition and the side-to-move must give way. *
* *
*******************************************************************************
*/
int EvaluateHasOpposition(int on_move, int king, int enemy_king) {
int file_distance, rank_distance;
file_distance = FileDistance(king, enemy_king);
rank_distance = RankDistance(king, enemy_king);
if (rank_distance < 2)
return 1;
if (on_move) {
if (rank_distance & 1)
rank_distance--;
if (file_distance & 1)
file_distance--;
}
if (!(file_distance & 1) && !(rank_distance & 1))
return 1;
return 0;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateKings() is used to evaluate kings. *
* *
*******************************************************************************
*/
void EvaluateKings(TREE * RESTRICT tree, int ply, int side) {
int score_eg = 0, score_mg = 0, defects;
int ksq = KingSQ(side), enemy = Flip(side);
/*
************************************************************
* *
* First, check for where the king should be if this is an *
* endgame. Ie with pawns on one wing, the king needs to *
* be on that wing. With pawns on both wings, the king *
* belongs in the center. *
* *
************************************************************
*/
if (tree->all_pawns) {
if (tree->all_pawns & mask_efgh && tree->all_pawns & mask_abcd)
score_eg += kval_n[side][ksq];
else if (tree->all_pawns & mask_efgh)
score_eg += kval_k[side][ksq];
else
score_eg += kval_q[side][ksq];
}
/*
************************************************************
* *
* Do castle scoring, if the king has castled, the pawns *
* in front are important. If not castled yet, the pawns *
* on the kingside should be preserved for this. *
* *
************************************************************
*/
if (tree->dangerous[enemy]) {
defects = 0;
if (Castle(ply, side) <= 0) {
if (File(ksq) >= FILEE) {
if (File(ksq) > FILEE)
defects = tree->pawn_score.defects_k[side];
else
defects = tree->pawn_score.defects_e[side];
} else {
if (File(ksq) < FILED)
defects = tree->pawn_score.defects_q[side];
else
defects = tree->pawn_score.defects_d[side];
}
} else {
if (Castle(ply, side) == 3)
defects =
Min(Min(tree->pawn_score.defects_k[side],
tree->pawn_score.defects_e[side]),
tree->pawn_score.defects_q[side]);
else if (Castle(ply, side) == 1)
defects =
Min(tree->pawn_score.defects_k[side],
tree->pawn_score.defects_e[side]);
else
defects =
Min(tree->pawn_score.defects_q[side],
tree->pawn_score.defects_e[side]);
if (defects < 3)
defects = 3;
}
/*
************************************************************
* *
* Fold in the king tropism and king pawn shelter scores *
* together. *
* *
************************************************************
*/
if (tree->tropism[enemy] < 0)
tree->tropism[enemy] = 0;
else if (tree->tropism[enemy] > 15)
tree->tropism[enemy] = 15;
if (defects > 15)
defects = 15;
score_mg -= king_safety[defects][tree->tropism[enemy]];
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateKingsFile computes defects for a file, based on whether the file *
* is open or half-open. If there are friendly pawns still on the file, *
* they are penalized for advancing in front of the king. *
* *
*******************************************************************************
*/
int EvaluateKingsFile(TREE * RESTRICT tree, int whichfile, int side) {
int defects = 0, file;
int enemy = Flip(side);
for (file = whichfile - 1; file <= whichfile + 1; file++)
if (!(file_mask[file] & tree->all_pawns))
defects += open_file[file];
else {
if (!(file_mask[file] & Pawns(enemy)))
defects += half_open_file[file] / 2;
else
defects +=
pawn_defects[side][Rank(Advanced(enemy,
file_mask[file] & Pawns(enemy)))];
if (!(file_mask[file] & Pawns(side)))
defects += half_open_file[file];
else if (!(Pawns(side) & SetMask(sqflip[side][A2] + file))) {
defects++;
if (!(Pawns(side) & SetMask(sqflip[side][A3] + file)))
defects++;
}
}
return defects;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateKnights() is used to evaluate knights. *
* *
*******************************************************************************
*/
void EvaluateKnights(TREE * RESTRICT tree, int side) {
uint64_t temp;
int square, t, score_eg = 0, score_mg = 0;
int enemy = Flip(side);
/*
************************************************************
* *
* First fold in centralization score from the piece/ *
* square table "nval". *
* *
************************************************************
*/
for (temp = Knights(side); temp; temp &= temp - 1) {
square = LSB(temp);
score_mg += nval[mg][side][square];
score_eg += nval[eg][side][square];
/*
************************************************************
* *
* Evaluate for "outposts" which is a knight that can't *
* be driven off by an enemy pawn, and which is supported *
* by a friendly pawn. *
* *
* If the enemy has NO minor to take this knight, then *
* increase the bonus. *
* *
************************************************************
*/
t = knight_outpost[side][square];
if (t && !(mask_no_pattacks[enemy][square] & Pawns(enemy))) {
if (pawn_attacks[enemy][square] & Pawns(side)) {
t += t / 2;
if (!Knights(enemy) && !(Color(square) & Bishops(enemy)))
t += knight_outpost[side][square];
}
score_eg += t;
score_mg += t;
}
/*
************************************************************
* *
* Mobility counts the number of squares the piece *
* attacks, including squares with friendly pieces, and *
* weighs each square according to centralization. *
* *
************************************************************
*/
score_mg += knight_mobility_table[square];
score_eg += knight_mobility_table[square];
/*
************************************************************
* *
* Adjust the tropism count for this piece. *
* *
************************************************************
*/
if (tree->dangerous[side]) {
t = Distance(square, KingSQ(enemy));
tree->tropism[side] += king_tropism_n[t];
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateMate() is used to evaluate positions where neither side has pawns *
* and one side has enough material to force checkmate. It simply trys to *
* force the losing king to the edge of the board, and then to the corner *
* where mates are easier to find. *
* *
*******************************************************************************
*/
void EvaluateMate(TREE * RESTRICT tree, int side) {
int mate_score = 0;
int enemy = Flip(side);
/*
************************************************************
* *
* If one side has a bishop+knight and the other side has *
* no pieces or pawns, then use the special bishop_knight *
* scoring board for the losing king to force it to the *
* right corner for mate. *
* *
************************************************************
*/
if (!TotalPieces(enemy, occupied) && TotalMinors(side) == 2 &&
TotalPieces(side, bishop) == 1) {
if (dark_squares & Bishops(side))
mate_score = b_n_mate_dark_squares[KingSQ(enemy)];
else
mate_score = b_n_mate_light_squares[KingSQ(enemy)];
}
/*
************************************************************
* *
* If one side is winning, force the enemy king to the *
* edge of the board. *
* *
************************************************************
*/
else {
mate_score = mate[KingSQ(enemy)];
mate_score -=
(Distance(KingSQ(side), KingSQ(enemy)) - 3) * king_king_tropism;
}
tree->score_mg += sign[side] * mate_score;
tree->score_eg += sign[side] * mate_score;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateMaterial() is used to evaluate material on the board. It really *
* accomplishes detecting cases where one side has made a 'bad trade' as the *
* comments below show. *
* *
*******************************************************************************
*/
void EvaluateMaterial(TREE * RESTRICT tree, int wtm) {
int score_mg, score_eg, majors, minors, imbal;
static int bon[17] =
{ 0, 40, 40, 35, 30, 24, 16, 12, 10, 8, 7, 6, 5, 4, 3, 2, 1 };
/*
**********************************************************************
* *
* We start with the raw Material balance for the current position, *
* then adjust this with a small bonus for the side on move. *
* *
**********************************************************************
*/
score_mg = Material + ((wtm) ? wtm_bonus[mg] : -wtm_bonus[mg]);
score_eg = Material + ((wtm) ? wtm_bonus[eg] : -wtm_bonus[eg]);
/*
**********************************************************************
* *
* If Majors or Minors are not balanced, then apply the appropriate *
* bonus/penatly from our imbalance table. *
* *
**********************************************************************
*/
majors = 4 + TotalMajors(white) - TotalMajors(black);
minors = 4 + TotalMinors(white) - TotalMinors(black);
majors = Max(Min(majors, 8), 0);
minors = Max(Min(minors, 8), 0);
imbal = imbalance[majors][minors];
score_mg += imbal;
score_eg += imbal;
/*
************************************************************
* *
* Add a bonus per side if side has a pair of bishops, *
* which can become very strong in open positions. *
* *
************************************************************
*/
if (TotalPieces(white, bishop) > 1) {
score_mg += bishop_pair[mg];
score_eg += bishop_pair[eg];
}
if (TotalPieces(black, bishop) > 1) {
score_mg -= bishop_pair[mg];
score_eg -= bishop_pair[eg];
}
/*
************************************************************
* *
* Check for pawns on both wings, which makes a bishop *
* even more valuable against an enemy knight (knight vs. *
* bishop in endgame) *
* *
************************************************************
*/
if (!imbal && !TotalMajors(white) && TotalMinors(white) == 1) {
imbal = bon[(TotalPieces(white, pawn) + TotalPieces(black, pawn))];
if (Bishops(white)) {
score_mg += imbal;
score_eg += imbal;
}
if (Bishops(black)) {
score_mg -= imbal;
score_eg -= imbal;
}
}
tree->score_mg += score_mg;
tree->score_eg += score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluatePassedPawns() is used to evaluate passed pawns and the danger *
* they produce. This code considers pieces as well, so it has been *
* separated from the normal EvaluatePawns() code that hashes information *
* based only on pawn positions. *
* *
*******************************************************************************
*/
void EvaluatePassedPawns(TREE * RESTRICT tree, int side, int wtm) {
uint64_t behind;
int file, square, score_mg = 0, score_eg = 0;
int pawns, rank;
int enemy = Flip(side);
/*
************************************************************
* *
* Initialize. *
* *
************************************************************
*/
for (pawns = tree->pawn_score.passed[side]; pawns; pawns &= pawns - 1) {
file = LSB8Bit(pawns);
square = Advanced(side, Pawns(side) & file_mask[file]);
rank = rankflip[side][Rank(square)];
/*
************************************************************
* *
* We have located the most advanced pawn on this file, *
* which is the only one that will get any sort of bonus. *
* Add in the MG/EG scores first. *
************************************************************
*/
score_mg += passed_pawn_value[mg][rank];
score_eg += passed_pawn_value[eg][rank];
/*
************************************************************
* *
* Add in a bonus if the passed pawn is connected with *
* another pawn for support. *
* *
************************************************************
*/
if (mask_pawn_connected[square] & Pawns(side)) {
score_mg += passed_pawn_connected[mg][rank];
score_eg += passed_pawn_connected[eg][rank];
}
/*
************************************************************
* *
* See if this pawn is either supported by a friendly rook *
* from behind, or is attacked by an enemy rook from *
* behind. *
* *
************************************************************
*/
if (Rooks(white) | Rooks(black)) {
behind =
((side) ? minus8dir[square] : plus8dir[square]) &
RookAttacks(square, OccupiedSquares);
if (behind) {
if (behind & Rooks(side)) {
score_mg += rook_behind_passed_pawn[mg][rank];
score_eg += rook_behind_passed_pawn[eg][rank];
} else if (behind & Rooks(enemy)) {
score_mg -= rook_behind_passed_pawn[mg][rank];
score_eg -= rook_behind_passed_pawn[eg][rank];
}
}
}
/*
************************************************************
* *
* If the pawn is blockaded by an enemy piece, it cannot *
* move and is therefore not nearly as valuable as if it *
* were free to advance. If it is blocked by a friendly *
* piece, it is penalized 1/2 the normal blockade amount *
* to encourage the blocking piece to move so the pawn can *
* advance. *
* *
************************************************************
*/
if (OccupiedSquares & ((side) ? plus8dir[square] : minus8dir[square])) {
score_eg -= passed_pawn_obstructed[rank];
if (SetMask(square + direction[side]) & Occupied(enemy)) {
score_mg -= passed_pawn_blockaded_by_enemy[mg][rank];
score_eg -= passed_pawn_blockaded_by_enemy[eg][rank];
} else if (SetMask(square + direction[side]) & Occupied(side)) {
score_mg -= passed_pawn_blockaded_by_friendly[mg][rank];
score_eg -= passed_pawn_blockaded_by_friendly[eg][rank];
}
/*
************************************************************
* *
* If the pawn has nothing blocking its progress to the *
* promotion square, then we give a bonus. If the pawn is *
* outside the square of the enemy king, this bonus is *
* increased, because this makes it more difficult for the *
* opponent to trade pieces which might let this pawn run. *
* *
************************************************************
*/
} else {
if (!(pawn_race[side][wtm][square] & Kings(enemy)))
score_eg += passed_pawn_far_away[rank];
else
score_eg += passed_pawn_not_far_away[rank];
}
/*
************************************************************
* *
* Add in a bonus based on how close the friendly king is, *
* and a penalty based on how close the enemy king is. *
* The bonus/penalty is based on how advanced the pawn is *
* to attract the kings toward the most advanced (and most *
* dangerous) passed pawn. *
* *
************************************************************
*/
square += direction[side];
score_eg -=
(Distance(square, KingSQ(side)) - Distance(square,
KingSQ(enemy))) * pp_dist_bonus[rank];
}
/*
************************************************************
* *
* Check to see if side has an outside passed pawn. *
* *
************************************************************
*/
if (tree->pawn_score.passed[side]
&& is_outside[tree->pawn_score.passed[side]]
[tree->pawn_score.all[enemy]]) {
score_mg += outside_passed[mg];
score_eg += outside_passed[eg];
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluatePassedPawnRaces() is used to evaluate passed pawns when one *
* side has passed pawns and the other side (or neither) has pieces. In *
* such a case, the critical question is can the defending king stop the pawn*
* from queening or is it too far away? If only one side has pawns that can *
* "run" then the situation is simple. When both sides have pawns that can *
* "run" it becomes more complex as it then becomes necessary to see if *
* one side can use a forced king move to stop the other side, while the *
* other side doesn't have the same ability to stop ours. *
* *
* In the case of king and pawn endings with exactly one pawn, the simple *
* evaluation rules are used: if the king is two squares in front of the *
* pawn then it is a win, if the king is one one square in front with the *
* opposition, then it is a win, if the king is on the 6th rank with the *
* pawn close by, it is a win. Rook pawns are handled separately and are *
* more difficult to queen because the king can get trapped in front of the *
* pawn blocking promotion. *
* *
*******************************************************************************
*/
void EvaluatePassedPawnRaces(TREE * RESTRICT tree, int wtm) {
int file, square;
int queen_distance;
int pawnsq;
uint64_t pawns;
int passed;
int side, enemy;
int queener[2] = { 8, 8 };
int forced_km[2] = { 0, 0 };
/*
************************************************************
* *
* Check to see if side has one pawn and neither side has *
* any pieces. If so, use the simple pawn evaluation *
* logic. *
* *
************************************************************
*/
for (side = black; side <= white; side++) {
enemy = Flip(side);
if (Pawns(side) && !Pawns(enemy) && TotalPieces(white, occupied) == 0 &&
TotalPieces(black, occupied) == 0) {
for (pawns = Pawns(side); pawns; pawns &= pawns - 1) {
pawnsq = LSB(pawns);
/*
************************************************************
* *
* King must be in front of the pawn or we go no further. *
* *
************************************************************
*/
if (sign[side] * Rank(KingSQ(side)) <= sign[side] * Rank(pawnsq))
continue;
/*
************************************************************
* *
* First a special case. If this is a rook pawn, then the *
* king must be on the adjacent file, and be closer to the *
* queening square than the opposing king. *
* *
************************************************************
*/
if (File(pawnsq) == FILEA) {
if (File(KingSQ(side)) == FILEB &&
Distance(KingSQ(side),
sqflip[side][A8]) < Distance(KingSQ(enemy),
sqflip[side][A8])) {
tree->score_eg += sign[side] * pawn_can_promote;
return;
}
continue;
} else if (File(pawnsq) == FILEH) {
if (File(KingSQ(side)) == FILEG &&
Distance(KingSQ(side),
sqflip[side][H8]) < Distance(KingSQ(enemy),
sqflip[side][H8])) {
tree->score_eg += sign[side] * pawn_can_promote;
return;
}
continue;
}
/*
************************************************************
* *
* If king is two squares in front of the pawn then it's a *
* win immediately. If the king is on the 6th rank and *
* closer to the pawn than the opposing king, it's also a *
* win. *
* *
************************************************************
*/
if (Distance(KingSQ(side), pawnsq) < Distance(KingSQ(enemy), pawnsq)) {
if (sign[side] * Rank(KingSQ(side)) >
sign[side] * (Rank(pawnsq) - 1 + 2 * side)) {
tree->score_eg += sign[side] * pawn_can_promote;
return;
}
if (Rank(KingSQ(side)) == rankflip[side][RANK6]) {
tree->score_eg += sign[side] * pawn_can_promote;
return;
}
/*
************************************************************
* *
* Last chance: if the king is one square in front of the *
* pawn and has the opposition, then it's still a win. *
* *
************************************************************
*/
if (Rank(KingSQ(side)) == Rank(pawnsq) - 1 + 2 * side &&
EvaluateHasOpposition(wtm == side, KingSQ(side),
KingSQ(enemy))) {
tree->score_eg += sign[side] * pawn_can_promote;
return;
}
}
}
}
/*
************************************************************
* *
* Check to see if enemy is out of pieces and stm has *
* passed pawns. If so, see if any of these passed pawns *
* can outrun the defending king and promote. *
* *
************************************************************
*/
if (TotalPieces(enemy, occupied) == 0 && tree->pawn_score.passed[side]) {
passed = tree->pawn_score.passed[side];
for (; passed; passed &= passed - 1) {
file = LSB8Bit(passed);
square = Advanced(side, Pawns(side) & file_mask[file]);
forced_km[enemy] =
(pawn_race[side][wtm][square] & Kings(enemy)) !=
(pawn_race[side][Flip(wtm)][square] & Kings(enemy));
if (!(pawn_race[side][wtm][square] & Kings(enemy))) {
queen_distance = Abs(rankflip[side][RANK8] - Rank(square));
if (Kings(side) & ((side) ? plus8dir[square] : minus8dir[square])) {
if (file == FILEA || file == FILEH)
queen_distance = 99;
queen_distance++;
}
if (Rank(square) == rankflip[side][RANK2])
queen_distance--;
if (queen_distance < queener[side])
queener[side] = queen_distance;
}
}
}
}
if (queener[white] == 8 && queener[black] == 8)
return;
/*
************************************************************
* *
* Now that we know which pawns can outrun the kings for *
* each side, we need to do the following: *
* *
* (1) If both sides are forced to move their king to *
* prevent the opponent from promoting, we let the *
* search resolve this as the depth increases. *
* *
* (2) If white can run while black can not, then white *
* wins, or vice-versa. *
* *
* (3) If white queens and black's king can't stop it *
* no matter who moves first, while black has a pawn *
* that white can stop if a king move is made *
* immediately, then white wins, and vice-versa. *
* *
* (4) Other situations are left to the search to *
* resolve. *
* *
************************************************************
*/
if (forced_km[white] & forced_km[black])
return;
if (queener[white] < 8 && queener[black] == 8) {
tree->score_eg += pawn_can_promote + (5 - queener[white]) * 10;
return;
} else if (queener[black] < 8 && queener[white] == 8) {
tree->score_eg += -(pawn_can_promote + (5 - queener[black]) * 10);
return;
}
if (queener[white] < queener[black] && forced_km[white]) {
tree->score_eg += pawn_can_promote + (5 - queener[white]) * 10;
return;
} else if (queener[black] < queener[white] && forced_km[white]) {
tree->score_eg += -(pawn_can_promote + (5 - queener[black]) * 10);
return;
}
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluatePawns() is used to evaluate pawns. It evaluates pawns for only *
* one side, and fills in the pawn hash entry information. It requires two *
* calls to evaluate all pawns on the board. Comments below indicate the *
* particular pawn structure features that are evaluated. *
* *
* This procedure also fills in information (without scoring) that other *
* evaluation procedures use, such as which pawns are passed or candidates, *
* which pawns are weak, which files are open, and so forth. *
* *
*******************************************************************************
*/
void EvaluatePawns(TREE * RESTRICT tree, int side) {
uint64_t pawns;
uint64_t temp;
uint64_t p_moves[2];
int square, file, rank, score_eg = 0, score_mg = 0;
int defenders, attackers, sq;
int enemy = Flip(side);
/*
************************************************************
* *
* Initialize. *
* *
************************************************************
*/
tree->pawn_score.all[side] = 0;
tree->pawn_score.passed[side] = 0;
/*
************************************************************
* *
* First, determine which squares pawns can reach. *
* *
************************************************************
*/
p_moves[side] = 0;
for (pawns = Pawns(side); pawns; pawns &= pawns - 1) {
square = LSB(pawns);
tree->pawn_score.all[side] |= 1 << File(square);
for (sq = square; sq != File(square) + ((side) ? RANK7 << 3 : RANK2 << 3);
sq += direction[side]) {
p_moves[side] |= SetMask(sq);
if (SetMask(sq + direction[side]) & tree->all_pawns)
break;
defenders =
PopCnt(pawn_attacks[enemy][sq + direction[side]] & Pawns(side));
attackers =
PopCnt(pawn_attacks[side][sq + direction[side]] & Pawns(enemy));
if (attackers - defenders > 0)
break;
}
}
/*
************************************************************
* *
* Loop through all pawns for this side. *
* *
************************************************************
*/
for (pawns = Pawns(side); pawns; pawns &= pawns - 1) {
square = LSB(pawns);
file = File(square);
rank = rankflip[side][Rank(square)];
/*
************************************************************
* *
* Evaluate pawn advances. Center pawns are encouraged to *
* advance, while wing pawns are pretty much neutral. *
* This is a simple piece/square value. *
* *
************************************************************
*/
score_mg += pval[mg][side][square];
score_eg += pval[eg][side][square];
/*
************************************************************
* *
* Evaluate isolated pawns, which are penalized based on *
* the file, with central isolani being worse than when on *
* the wings. *
* *
************************************************************
*/
if (!(mask_pawn_isolated[square] & Pawns(side))) {
score_mg -= pawn_isolated[mg];
score_eg -= pawn_isolated[eg];
if (!(Pawns(enemy) & file_mask[file])) {
score_mg -= pawn_isolated[mg] / 2;
score_eg -= pawn_isolated[eg] / 2;
}
}
/*
************************************************************
* *
* Evaluate weak pawns. Weak pawns are evaluated by the *
* following rules: (1) if a pawn is defended by a pawn, *
* it isn't weak; (2) if a pawn is undefended by a pawn *
* and advances one (or two if it hasn't moved yet) ranks *
* and is defended fewer times than it is attacked, it is *
* weak. Note that the penalty is greater if the pawn is *
* on an open file. Note that an isolated pawn is just *
* another case of a weak pawn, since it can never be *
* defended by a pawn. *
* *
* First, test the pawn where it sits to determine if it *
* is defended more times than attacked. If so, it is not *
* weak and we are done. If it is weak where it sits, can *
* it advance one square and become not weak. If so we *
* are again finished with this pawn. Otherwise we fall *
* into the next test. *
* *
************************************************************
*/
else {
do {
attackers = 0;
defenders = 0;
temp =
p_moves[side] & ((side) ? plus8dir[square] : minus8dir[square]);
for (; temp; temp &= temp - 1) {
sq = LSB(temp);
defenders = PopCnt(pawn_attacks[enemy][sq] & Pawns(side));
attackers = PopCnt(pawn_attacks[side][sq] & Pawns(enemy));
if (defenders && defenders >= attackers)
break;
}
if (defenders && defenders >= attackers)
break;
/*
************************************************************
* *
* If the pawn can be defended by a pawn, and that pawn *
* can safely advance to actually defend this pawn, then *
* this pawn is not weak. *
* *
************************************************************
*/
if (!(pawn_attacks[enemy][square] & p_moves[side])) {
score_mg -= pawn_weak[mg];
score_eg -= pawn_weak[eg];
if (!(Pawns(enemy) & file_mask[file]))
score_mg -= pawn_weak[mg] / 2;
}
} while (0);
/*
************************************************************
* *
* Evaluate doubled pawns. If there are other pawns on *
* this file, penalize this pawn. *
* *
************************************************************
*/
if (PopCnt(file_mask[file] & Pawns(side)) > 1) {
score_mg -= doubled_pawn_value[mg];
score_eg -= doubled_pawn_value[eg];
}
/*
************************************************************
* *
* Test the pawn to see if it is connected with a neighbor *
* which makes it more mobile. *
* *
************************************************************
*/
if (mask_pawn_connected[square] & Pawns(side)) {
score_mg += pawn_connected[mg];
score_eg += pawn_connected[eg];
}
}
/*
************************************************************
* *
* Discover and flag passed pawns for use later. *
* *
************************************************************
*/
if (!(mask_passed[side][square] & Pawns(enemy)))
tree->pawn_score.passed[side] |= 1 << file;
/*
************************************************************
* *
* Determine if this pawn is a candidate passer, since we *
* now know it isn't passed. A candidate is a pawn on a *
* file with no enemy pawns in front of it, and if it *
* advances until it contacts an enemy pawn, and it is *
* defended as many times as it is attacked when it *
* reaches that pawn, then all that is left is to see if *
* it is passed when the attacker(s) get removed. *
* *
************************************************************
*/
else if (!(file_mask[File(square)] & Pawns(enemy))
&& mask_pawn_isolated[square] & Pawns(side)
&& !(pawn_attacks[side][square] & Pawns(enemy))) {
attackers = 1;
defenders = 0;
for (sq = square;
sq != File(square) + ((side) ? RANK7 << 3 : RANK2 << 3);
sq += direction[side]) {
if (SetMask(sq + direction[side]) & tree->all_pawns)
break;
defenders = PopCnt(pawn_attacks[enemy][sq] & p_moves[side]);
attackers = PopCnt(pawn_attacks[side][sq] & Pawns(enemy));
if (attackers)
break;
}
if (attackers <= defenders) {
if (!(mask_passed[side][sq + direction[side]] & Pawns(enemy))) {
score_mg += passed_pawn_candidate[mg][rank];
score_eg += passed_pawn_candidate[eg][rank];
}
}
}
/*
************************************************************
* *
* Evaluate "hidden" passed pawns. Simple case is a pawn *
* chain (white) at b5, a6, with a black pawn at a7. *
* It appears the b-pawn is backward, with a ram at a6/a7 *
* but this is misleading, because the pawn at a6 is *
* really passed when white plays b6. *
* *
************************************************************
*/
if (Rank(square) == ((side) ? RANK6 : RANK3)
&& SetMask(square + direction[side]) & Pawns(enemy)
&& ((File(square) < FILEH &&
SetMask(square + 9 - 16 * side) & Pawns(side)
&& !(mask_hidden_right[side][File(square)] & Pawns(enemy)))
|| (File(square) > FILEA &&
SetMask(square + 7 - 16 * side) & Pawns(side)
&& !(mask_hidden_left[side][File(square)] & Pawns(enemy))))) {
score_mg += passed_pawn_hidden[mg];
score_eg += passed_pawn_hidden[eg];
}
}
/*
************************************************************
* *
* Evaluate king safety. *
* *
* This uses the function EvaluateKingsFile() which looks *
* at four possible positions for the king, either castled *
* kingside, queenside or else standing on the d or e file *
* stuck in the middle. This essentially is about the *
* pawns in front of the king and what kind of "shelter" *
* they provide for the king during the middlegame. *
* *
************************************************************
*/
tree->pawn_score.defects_q[side] = EvaluateKingsFile(tree, FILEB, side);
tree->pawn_score.defects_d[side] = EvaluateKingsFile(tree, FILED, side);
tree->pawn_score.defects_e[side] = EvaluateKingsFile(tree, FILEE, side);
tree->pawn_score.defects_k[side] = EvaluateKingsFile(tree, FILEG, side);
tree->pawn_score.score_mg += sign[side] * score_mg;
tree->pawn_score.score_eg += sign[side] * score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateQueens() is used to evaluate queens. *
* *
*******************************************************************************
*/
void EvaluateQueens(TREE * RESTRICT tree, int side) {
uint64_t temp;
int square, t, score_mg = 0, score_eg = 0;
int enemy = Flip(side);
/*
************************************************************
* *
* First locate each queen and obtain it's centralization *
* score from the static piece/square table for queens. *
* *
************************************************************
*/
for (temp = Queens(side); temp; temp &= temp - 1) {
square = LSB(temp);
/*
************************************************************
* *
* Then, add in the piece/square table value for the *
* queen. *
* *
************************************************************
*/
score_mg += qval[mg][side][square];
score_eg += qval[eg][side][square];
/*
************************************************************
* *
* Adjust the tropism count for this piece. *
* *
************************************************************
*/
if (tree->dangerous[side]) {
t = KingSQ(enemy);
tree->tropism[side] += king_tropism_q[Distance(square, t)];
t = 8 - (RankDistance(square, t) + FileDistance(square, t));
score_mg += t;
score_eg += t;
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateRooks() is used to evaluate rooks. *
* *
*******************************************************************************
*/
void EvaluateRooks(TREE * RESTRICT tree, int side) {
uint64_t temp, moves;
int square, rank, fRank, file, i, t, mobility;
int score_mg = 0, score_eg = 0;
int enemy = Flip(side);
/*
************************************************************
* *
* Initialize. *
* *
************************************************************
*/
for (temp = Rooks(side); temp; temp &= temp - 1) {
square = LSB(temp);
file = File(square);
rank = Rank(square);
fRank = rankflip[side][rank];
/*
************************************************************
* *
* Determine if the rook is on an open file or on a half- *
* open file, either of which increases its ability to *
* attack important squares. *
* *
************************************************************
*/
if (!(file_mask[file] & Pawns(side))) {
if (!(file_mask[file] & Pawns(enemy))) {
score_mg += rook_open_file[mg];
score_eg += rook_open_file[eg];
} else {
score_mg += rook_half_open_file[mg];
score_eg += rook_half_open_file[eg];
}
}
/*
************************************************************
* *
* Check to see if the king has been forced to move and *
* has trapped a rook at a1/b1/g1/h1, if so, then penalize *
* the trapped rook to help extricate it. *
* *
************************************************************
*/
if (fRank == RANK1) {
if (rank == Rank(KingSQ(side))) {
i = File(KingSQ(side));
if (i > FILEE) {
if (file > i) {
score_mg -= rook_trapped;
score_eg -= rook_trapped;
}
} else if (i < FILED && file < i) {
score_mg -= rook_trapped;
score_eg -= rook_trapped;
}
}
}
/*
************************************************************
* *
* Determine if the rook is on the 7th rank. If so the *
* rook exerts a "cramping" effect that is valuable. *
* *
************************************************************
*/
else if (fRank == RANK7) {
score_mg += rook_on_7th[mg];
score_eg += rook_on_7th[eg];
}
/*
************************************************************
* *
* Mobility counts the number of squares the piece *
* attacks, and weighs each square according to a complex *
* formula that includes files as well as total number of *
* squares attacked. *
* *
* For efficiency, we use a pre-computed mobility score *
* that is accessed just like a magic attack generation. *
* *
************************************************************
*/
mobility = RookMobility(square, OccupiedSquares);
score_mg += mobility;
score_eg += mobility;
/*
************************************************************
* *
* Adjust the tropism count for this piece. *
* *
************************************************************
*/
if (tree->dangerous[side]) {
moves = king_attacks[KingSQ(enemy)];
t = (rook_attacks[square] & moves &&
RookAttacks(square,
OccupiedSquares & ~(Queens(side) | (Rooks(side)))) & moves) ? 1
: Distance(square, KingSQ(enemy));
tree->tropism[side] += king_tropism_r[t];
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 02/23/14 */
/*
*******************************************************************************
* *
* EvaluateWinningChances() is used to determine if one side has reached a *
* position which can not be won, period, even though side may be ahead in *
* material in some way. *
* *
* Return values: *
* 0 -> side on move can not win. *
* 1 -> side on move can win. *
* *
*******************************************************************************
*/
int EvaluateWinningChances(TREE * RESTRICT tree, int side, int wtm) {
int square, ekd, promote;
int enemy = Flip(side);
/*
************************************************************
* *
* If side has a piece and no pawn, it can not possibly *
* win. If side is a piece ahead, the only way it can win *
* is if the enemy is already trapped on the edge of the *
* board (special case to handle KRB vs KR which can be *
* won if the king gets trapped). *
* *
************************************************************
*/
if (TotalPieces(side, pawn) == 0) {
if (TotalPieces(side, occupied) <= 3)
return 0;
if (TotalPieces(side, occupied) - TotalPieces(enemy, occupied) <= 3 &&
mask_not_edge & Kings(enemy))
return 0;
}
/*
************************************************************
* *
* If "side" has a pawn, then either the pawn had better *
* not be a rook pawn, or else white had better have the *
* right color bishop or any other piece, otherwise it is *
* not winnable if the enemy king can get to the queening *
* square first. *
* *
************************************************************
*/
else if (!(Pawns(side) & not_rook_pawns))
do {
if (TotalPieces(side, occupied) > 3 || (TotalPieces(side, occupied) == 3
&& Knights(side)))
continue;
if (file_mask[FILEA] & Pawns(side) && file_mask[FILEH] & Pawns(side))
continue;
if (Bishops(side)) {
if (Bishops(side) & dark_squares) {
if (file_mask[dark_corner[side]] & Pawns(side))
continue;
} else if (file_mask[light_corner[side]] & Pawns(side))
continue;
}
if (Pawns(side) & file_mask[FILEA])
promote = A8;
else
promote = H8;
ekd = Distance(KingSQ(enemy), sqflip[side][promote]) - (wtm != side);
if (ekd <= 1)
return 0;
} while (0);
/*
************************************************************
* *
* Check to see if this is a KRP vs KR or KQP vs KQ type *
* ending. If so, and the losing king is in front of the *
* passer, then this is a drawish ending. *
* *
************************************************************
*/
if (TotalPieces(side, pawn) == 1 && TotalPieces(enemy, pawn) == 0 &&
((TotalPieces(side, occupied) == 5 && TotalPieces(enemy, occupied) == 5)
|| (TotalPieces(side, occupied) == 9 &&
TotalPieces(enemy, occupied) == 9))) {
square = LSB(Pawns(side));
if (FileDistance(KingSQ(enemy), square) <= 1 &&
InFront(side, Rank(KingSQ(enemy)), Rank(square)))
return 0;
}
/*
************************************************************
* *
* If this side has pawns, and we have made it through the *
* previous tests, then this side has winning chances. *
* *
************************************************************
*/
if (TotalPieces(side, pawn))
return 1;
/*
************************************************************
* *
* If this side has two bishops, and the enemy has only a *
* single kinght, the two bishops win. *
* *
************************************************************
*/
if (TotalPieces(side, occupied) == 6)
if (TotalPieces(enemy, occupied) == 3 && (Knights(side)
|| !Knights(enemy)))
return 0;
/*
************************************************************
* *
* If one side is two knights ahead and the opponent has *
* no remaining material, it is a draw. *
* *
************************************************************
*/
if (TotalPieces(side, occupied) == 6 && !Bishops(side)
&& TotalPieces(enemy, occupied) + TotalPieces(enemy, pawn) == 0)
return 0;
return 1;
}