#include "chess.h"
#include "evaluate.h"
#include "data.h"
/* last modified 08/03/16 */
/*
*******************************************************************************
* *
* 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 and enemy pieces and how close *
* they are to assist in a king-side attack. *
* *
*******************************************************************************
*/
int Evaluate(TREE * RESTRICT tree, int ply, int wtm, int alpha, int beta) {
PAWN_HASH_ENTRY *ptable;
PXOR *pxtable;
int score, side, can_win = 3, phase, lscore, cutoff;
/*
*************************************************************
* *
* First thing we do is if -DSKILL was passed in to the *
* compiler, we burn some time to slow the search down, *
* then we fall into the normal evaluation code. *
* *
*************************************************************
*/
#if defined(SKILL)
if (skill < 100) {
int i, j;
for (i = 0; i < burnc[skill / 10] && !abort_search; i++)
for (j = 1; j < 10 && !abort_search; j++)
burner[j - 1] = burner[j - 1] * burner[j];
if (TimeCheck(tree, 1))
abort_search = 1;
}
#endif
/*
*************************************************************
* *
* First lazy cutoff attempt. If the material score is way *
* below alpha or way above beta (way above means so far *
* above it is very unlikely the positional score can bring *
* the total score back into the alpha / beta window) then *
* we take what is known as a "lazy evaluation exit" and *
* avoid the computational cost of a full evaluation in a *
* position where one side is way ahead or behind. *
* *
*************************************************************
*/
cutoff = (TotalPieces(white, occupied) && TotalPieces(black, occupied))
? KNIGHT_VALUE : ROOK_VALUE;
lscore = MaterialSTM(wtm);
if (lscore + cutoff < alpha)
return alpha;
if (lscore - cutoff > beta)
return beta;
/*
*************************************************************
* *
* Check for draws where one side seems to be ahead, but *
* has no actual winning chances. One simple example is a *
* king, bishop and rook pawn, with the wrong colored *
* bishop and the enemy king too close to the promotion *
* square. *
* *
* The variable "can_win" uses 2 bits. If White can *
* actually win in this position, bit 1 is set. If Black *
* can actually win in this position, bit 0 is set. If *
* both sides can win, both bits are set. This is used *
* later to drag the score closer to a draw score if the *
* side with the better score can't actually win. *
* *
* Note that we only set these bits in minimal material *
* positions (both sides have < 13 points of material *
* total). Otherwise we assume normal scoring should *
* apply. *
* *
*************************************************************
*/
tree->evaluations++;
tree->score_mg = 0;
tree->score_eg = 0;
EvaluateMaterial(tree, wtm);
if (TotalPieces(white, occupied) < 13 && TotalPieces(black, occupied) < 13)
for (side = black; side <= white; side++)
if (!EvaluateWinningChances(tree, side, wtm))
can_win ^= (1 << side);
/*
*************************************************************
* *
* 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 & 2))
tree->score_eg = tree->score_eg / 16;
if (tree->score_eg < DrawScore(1) && !(can_win & 1))
tree->score_eg = tree->score_eg / 16;
#if defined(SKILL)
if (skill < 100)
tree->score_eg =
skill * tree->score_eg / 100 + ((100 -
skill) * PAWN_VALUE * (uint64_t) Random32() /
0x100000000ull) / 100;
#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];
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];
memcpy((char *) ptable
+ 8, (char *) &(tree
->pawn_score
) + 8,
sizeof(PAWN_HASH_ENTRY) - 8);
}
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 EvaluateCastling() to evaluate castling potential. *
* Note we only do this when that side has not castled at *
* the root. *
* *
*************************************************************
*/
for (side = black; side <= white; side++)
if (Castle(1, side) > 0)
EvaluateCastling(tree, ply, side);
/*
*************************************************************
* *
* The "dangerous" flag simply indicates whether that side *
* has enough material to whip up a mating attack if the *
* other side is careless (Q + minor or better, or RR + two *
* minors or better). *
* *
*************************************************************
*/
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);
/*
*************************************************************
* *
* Second lazy evaluation test. We have computed the large *
* positional scores (except for king safety). If the *
* score is too far outside the alpha/beta window, we skip *
* the piece scoring which is the most expensive of all the *
* evaluation terms, and simply use what we have at this *
* point. *
* *
*************************************************************
*/
phase =
Min(62, TotalPieces(white, occupied) + TotalPieces(black, occupied));
score = ((tree->score_mg * phase) + (tree->score_eg * (62 - phase))) / 62;
lscore = (wtm) ? score : -score;
int w_mat = (2 * TotalPieces(white, rook)) + TotalPieces(white,
knight) + TotalPieces(white, bishop);
int b_mat = (2 * TotalPieces(black, rook)) + TotalPieces(black,
knight) + TotalPieces(black, bishop);
cutoff
= 72 + (w_mat
+ b_mat
) * 8 + abs(w_mat
- b_mat
) * 16;
if (tree->dangerous[white] || tree->dangerous[black])
cutoff += 35;
/*
*************************************************************
* *
* Then evaluate pieces if the lazy eval test fails. *
* *
* Note: We MUST evaluate kings last, since their scoring *
* depends on the tropism scores computed by the other *
* piece evaluators. Do NOT try to collapse the following *
* loops into one loop. That will break things since it *
* would violate the kings last rule. More importantly *
* there is no benefit as the loops below are unrolled by *
* the compiler anyway. *
* *
*************************************************************
*/
if (lscore + cutoff > alpha && lscore - cutoff < beta) {
tree->tropism[white] = 0;
tree->tropism[black] = 0;
for (side = black; side <= white; side++)
if (Knights(side))
EvaluateKnights(tree, side);
for (side = black; side <= white; side++)
if (Bishops(side))
EvaluateBishops(tree, side);
for (side = black; side <= white; side++)
if (Rooks(side))
EvaluateRooks(tree, side);
for (side = black; side <= white; side++)
if (Queens(side))
EvaluateQueens(tree, side);
for (side = black; side <= white; side++)
EvaluateKing(tree, ply, side);
}
/*
*************************************************************
* *
* Caclulate the final score, which is interpolated between *
* the middlegame score and endgame score based on the *
* material left on the board. *
* *
* Adjust the score if one side can't win, but the score *
* actually favors that side significantly. *
* *
*************************************************************
*/
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 =
skill * score / 100 + ((100 -
skill) * PAWN_VALUE * (uint64_t) Random32() / 0x100000000ull) /
100;
#endif
return (wtm) ? score : -score;
}
/* last modified 10/19/15 */
/*
*******************************************************************************
* *
* EvaluateBishops() is used to evaluate bishops. *
* *
*******************************************************************************
*/
void EvaluateBishops(TREE * RESTRICT tree, int side) {
uint64_t temp, moves;
int square, special, i, mobility;
int score_eg = 0, score_mg = 0, enemy = Flip(side), tpawns;
/*
************************************************************
* *
* First, locate each bishop and add in its piece/square *
* table 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. *
* *
************************************************************
*/
special = bishop_outpost[side][square];
if (special) {
if (!(mask_pattacks[enemy][square] & Pawns(enemy))) {
if (pawn_attacks[enemy][square] & Pawns(side)) {
special += special / 2;
if (!Knights(enemy) && !(Color(square) & Bishops(enemy)))
special += bishop_outpost[side][square];
}
score_eg += special;
score_mg += special;
}
}
/*
************************************************************
* *
* Next we count the number of friendly pawns on the same *
* color squares as the bishop. This is a bad thing since *
* it restricts the bishop's ability to move. We only do *
* this if there is only one bishop for this side. *
* *
************************************************************
*/
if (TotalPieces(side, bishop) == 1) {
if (dark_squares & SetMask(square))
tpawns = PopCnt(dark_squares & Pawns(side));
else
tpawns = PopCnt(~dark_squares & Pawns(side));
score_mg -= tpawns * bishop_pawns_on_color[mg];
score_eg -= tpawns * bishop_pawns_on_color[eg];
}
/*
************************************************************
* *
* Mobility counts the number of squares the bishop *
* attacks, excluding squares with friendly pieces, 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;
/*
************************************************************
* *
* Adjust the tropism count for this piece. *
* *
************************************************************
*/
if (tree->dangerous[side]) {
moves = king_attacks[KingSQ(enemy)];
i = ((bishop_attacks[square] & moves) &&
((BishopAttacks(square, OccupiedSquares & ~Queens(side))) & moves))
? 1 : Distance(square, KingSQ(enemy));
tree->tropism[side] += king_tropism_b[i];
}
}
/*
************************************************************
* *
* Add a bonus if this side has a pair of bishops, which *
* can become very strong in open positions. *
* *
************************************************************
*/
if (TotalPieces(side, 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 *
* *
************************************************************
*/
else {
if (tree->all_pawns & mask_fgh && tree->all_pawns & mask_abc) {
score_mg += bishop_wing_pawns[mg];
score_eg += bishop_wing_pawns[eg];
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 01/03/15 */
/*
*******************************************************************************
* *
* EvaluateCastling() is called when "side" has not castled at the root. *
* Its main purpose is to determine if it has either castled somewhere in *
* the tree, or else has lost all (or some) castling rights, which reduces *
* options significantly. *
* *
*******************************************************************************
*/
void EvaluateCastling(TREE * RESTRICT tree, int ply, int side) {
int enemy = Flip(side), oq, score_mg = 0;;
/*
************************************************************
* *
* If the king castled during the search, we are done and *
* we leave it to EvaluateKing() to figure out how safe it *
* is. If it has not castled, we give a significant *
* penalty if the king moves since that loses all castling *
* rights, otherwise we give a smaller penalty for moving *
* a rook and giving up castling rights to that side of *
* the board. The penalty is always increased if the *
* opponent has a queen since the position is much more *
* dangerous. *
* *
************************************************************
*/
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;
tree->score_mg += sign[side] * score_mg;
}
/* last modified 01/03/15 */
/*
*******************************************************************************
* *
* 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 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 the following are both true: *
* *
* black and white have less than a queen left (pieces *
* only). *
* *
* both have one bishop and they are opposite colored. *
* *
* then either *
* *
* (a) both have just one bishop, both have less than 4 *
* pawns or one side has only one more pawn than the *
* other side then score is divided by 2 with draw score *
* added in; or *
* *
* (b) pieces are equal, then score is reduced by 25% *
* with draw score added in. *
* *
************************************************************
*/
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);
}
}
/*
************************************************************
* *
* Final score adjustment. If the score says white is *
* better, but can_win says white can not win, or if the *
* score says black is better, but can_win says black can *
* not win, then we divide the score by 16, and then add *
* in the draw score. If the can_win says neither side *
* can win, we just set the score to draw score and exit. *
* *
************************************************************
*/
if (can_win != 3) {
if (can_win & 1) {
if (score > DrawScore(1))
score = score / 16 + DrawScore(1);
} else if (can_win & 2) {
if (score < DrawScore(1))
score = score / 16 + DrawScore(1);
} else
score = 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 closeness = 101 - Reversible(ply);
score = DrawScore(1) + score * closeness / 20;
}
return score;
}
/* last modified 01/03/15 */
/*
*******************************************************************************
* *
* 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 01/03/15 */
/*
*******************************************************************************
* *
* EvaluateKing() is used to evaluate a king. *
* *
*******************************************************************************
*/
void EvaluateKing(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. The basic idea is to centralize unless the *
* king is needed to deal with a passed enemy pawn. *
* *
************************************************************
*/
score_eg += kval[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)
defects = tree->pawn_score.defects_k[side];
else if (File(ksq) < FILED)
defects = tree->pawn_score.defects_q[side];
else
defects = tree->pawn_score.defects_m[side];
} else {
if (Castle(ply, side) == 3)
defects =
Min(Min(tree->pawn_score.defects_k[side],
tree->pawn_score.defects_m[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_m[side]);
else
defects =
Min(tree->pawn_score.defects_q[side],
tree->pawn_score.defects_m[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 01/03/15 */
/*
*******************************************************************************
* *
* 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 side, int first, int last) {
int defects = 0, file, enemy = Flip(side);
for (file = first; file <= last; 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(MostAdvanced(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 10/19/15 */
/*
*******************************************************************************
* *
* EvaluateKnights() is used to evaluate knights. *
* *
*******************************************************************************
*/
void EvaluateKnights(TREE * RESTRICT tree, int side) {
uint64_t temp;
int square, special, i, score_eg = 0, score_mg = 0, 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. *
* *
************************************************************
*/
special = knight_outpost[side][square];
if (special && !(mask_pattacks[enemy][square] & Pawns(enemy))) {
if (pawn_attacks[enemy][square] & Pawns(side)) {
special += special / 2;
if (!Knights(enemy) && !(Color(square) & Bishops(enemy)))
special += knight_outpost[side][square];
}
score_eg += special;
score_mg += special;
}
/*
************************************************************
* *
* Adjust the tropism count for this piece. *
* *
************************************************************
*/
if (tree->dangerous[side]) {
i = Distance(square, KingSQ(enemy));
tree->tropism[side] += king_tropism_n[i];
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 03/30/15 */
/*
*******************************************************************************
* *
* 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, enemy = Flip(side);
/*
************************************************************
* *
* If the winning 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) && TotalPieces(side, bishop) == 1 &&
TotalPieces(side, knight) == 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)];
}
/*
************************************************************
* *
* The winning side has to force the losing king to the *
* edge of the board. *
* *
************************************************************
*/
else
mate_score = mate[KingSQ(enemy)];
/*
************************************************************
* *
* And for either, it is important to bring the winning *
* king to help force mate. *
* *
************************************************************
*/
mate_score -= Distance(KingSQ(side), KingSQ(enemy)) * king_king_tropism;
tree->score_eg += sign[side] * mate_score;
}
/* last modified 10/19/15 */
/*
*******************************************************************************
* *
* 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;
/*
*************************************************************
* *
* 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]);
/*
*************************************************************
* *
* test 1. if Majors or Minors are not balanced, then if *
* one side is only an exchange up or down, we give a *
* penalty to the side that is an exchange down, but not as *
* big a penalty as the bad trade case below. *
* *
* test 2. if Majors or Minors are not balanced, then if *
* one side has more piece material points than the other *
* (using normal piece values of 3, 3, 5, 9 for N, B, R *
* and Q) then the side that is behind in piece material *
* gets a penalty. *
* *
*************************************************************
*/
majors =
TotalPieces(white, rook) + 2 * TotalPieces(white,
queen) - TotalPieces(black, rook) - 2 * TotalPieces(black, queen);
minors =
TotalPieces(white, knight) + TotalPieces(white,
bishop) - TotalPieces(black, knight) - TotalPieces(black, bishop);
if (majors || minors) {
if (Abs(TotalPieces(white, occupied) - TotalPieces(black, occupied)) != 2
&& TotalPieces(white, occupied) - TotalPieces(black, occupied) != 0) {
score_mg +=
Sign(TotalPieces(white, occupied) - TotalPieces(black,
occupied)) * bad_trade;
score_eg +=
Sign(TotalPieces(white, occupied) - TotalPieces(black,
occupied)) * bad_trade;
}
}
tree->score_mg += score_mg;
tree->score_eg += score_eg;
}
/* last modified 11/27/15 */
/*
*******************************************************************************
* *
* EvaluatePassedPawns() is used to evaluate passed pawns and the danger *
* they produce. This code considers pieces as well, so it MUST NOT be done *
* in the normal EvaluatePawns() code since that hashes information based *
* only on the position of pawns. *
* *
* This is a significant rewrite of passed pawn evaluation, with the primary *
* change being to collect the passed pawn scoring into one place, rather *
* than have it scattered around all over the place. One example is the old *
* rook_behind_passed_pawn scoring term that was done in rook scoring. It *
* is now done here along with other passed pawn terms such as blockaded and *
* the ability to advance or not. *
* *
*******************************************************************************
*/
void EvaluatePassedPawns(TREE * RESTRICT tree, int side, int wtm) {
uint64_t behind, forward, backward, attacked, defended, thispawn;
int file, square, score, score_mg = 0, score_eg = 0, next_sq;
int pawns, rank, mg_base, eg_base, bonus, enemy = Flip(side);
uint64_t fsliders = Queens(side) | Rooks(side);
uint64_t esliders = Queens(enemy) | Rooks(enemy);
/*
************************************************************
* *
* Initialize. The base value "passed_pawn[rank]" is *
* almost the "square" of the rank. That got a bit too *
* big, so a hand-tuned set of values, one per rank, *
* proved to be a better value. *
* *
************************************************************
*/
for (pawns = tree->pawn_score.passed[side]; pawns; pawns &= pawns - 1) {
file = LSB8Bit(pawns);
thispawn = Pawns(side) & file_mask[file];
if (thispawn) {
square = MostAdvanced(side, thispawn);
rank = rankflip[side][Rank(square)];
score = passed_pawn[rank];
/*
************************************************************
* *
* For endgame only, we add in a bonus based on how close *
* our king is to this pawn and a penalty based on how *
* close the enemy king is. We also try to keep our king *
* ahead of the pawn so it can escort it to promotion. We *
* only do this for passed pawns whose base score value is *
* greater than zero (ie pawns on ranks 4-7 since those *
* are the ones threatening to become a major problem.) *
* Also, if you happen to think that a small bonus for a *
* passed pawn on the 3rd rank might be useful, consider *
* speed. If the 3rd rank score is non-zero, that will *
* trigger a significant amount of work below. In testing *
* the additional cost more than offset the gain and so it *
* is basically ignored unless rank > 3. *
* *
************************************************************
*/
if (score) {
mg_base = score * passed_pawn_base[mg];
eg_base = score * passed_pawn_base[eg];
next_sq = square + direction[side];
eg_base +=
Distance(KingSQ(enemy),
next_sq) * 2 * score - Distance(KingSQ(side), next_sq) * score;
if (rank < RANK7)
eg_base -=
Distance(KingSQ(side), next_sq + direction[side]) * score / 2;
/*
************************************************************
* *
* If the pawn is not blockaded, we need to see whether it *
* can actually advance or not. Note that this directly *
* gives a bonus for blockading a passed pawn since the *
* mobility evaluation below will not be applied when the *
* pawn is blockaded by any piece. *
* *
* Step one is to determine if the squares in front of the *
* pawn are attacked by the enemy. If not, we add in a *
* significant score bonus. If some are attacked, we look *
* to see if at least the square directly in front of the *
* pawn is not attacked so that we can advance one square, *
* anyway. This gets a smaller score bonus. *
* *
************************************************************
*/
if (!(OccupiedSquares & SetMask(next_sq))) {
bonus = 0;
if (Pawns(side) & pawn_attacks[enemy][next_sq])
bonus = passed_pawn_free_advance;
else {
attacked = 0;
forward = (side) ? plus8dir[square] : minus8dir[square];
backward = (side) ? minus8dir[square] : plus8dir[square];
if ((behind = backward & esliders) &&
(FileAttacks(square) & behind))
attacked = forward;
else
attacked = Attacked(tree, enemy, forward);
if (!attacked)
bonus = passed_pawn_free_advance;
else if (!(attacked & SetMask(next_sq)))
bonus = passed_pawn_free_advance_1;
/*
************************************************************
* *
* Step two is to determine if the squares in front of the *
* pawn are are defended by the friendly side. If all are *
* defended (such as with a rook or queen behind the pawn *
* or the king in front and to one side of the pawn, then *
* we give a bonus (but smaller than the previous cases). *
* As a last resort, if we at least defend the square in *
* front of the pawn, we give a small bonus. *
* *
************************************************************
*/
if ((behind = backward & fsliders) &&
(FileAttacks(square) & behind))
defended = forward;
else
defended = Attacked(tree, side, forward);
if (defended == forward)
bonus += passed_pawn_defended;
else if (defended & SetMask(next_sq))
bonus += passed_pawn_defended_1;
}
/*
************************************************************
* *
* Fold in the bonus for this pawn and move on to the next *
* (if there is one). Note that the bonus computed above *
* is multiplied by the base passed pawn score for this *
* particular rank. *
* *
************************************************************
*/
mg_base += bonus * score;
eg_base += bonus * score;
}
score_mg += mg_base;
score_eg += eg_base;
} else
score_eg += 4;
}
}
/*
************************************************************
* *
* All pawns done, add score to the two evaluation values *
* and return. *
* *
************************************************************
*/
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 11/27/15 */
/*
*******************************************************************************
* *
* 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) {
uint64_t pawns, thispawn;
int file, square, queen_distance, pawnsq, passed, side, enemy;
int queener[2] = { 8, 8 };
/*
************************************************************
* *
* 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)) == rank6[side]) {
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);
thispawn = Pawns(side) & file_mask[file];
if (thispawn) {
square = MostAdvanced(side, thispawn);
if (!(pawn_race[side][wtm][square] & Kings(enemy))) {
queen_distance = Abs(rank8[side] - Rank(square));
if (Kings(side) & ((side) ? plus8dir[square] : minus8dir[square])) {
if (file == FILEA || file == FILEH)
queen_distance = 99;
queen_distance++;
}
if (Rank(square) == rank2[side])
queen_distance--;
if (queen_distance < queener[side])
queener[side] = queen_distance;
}
}
}
}
}
/*
************************************************************
* *
* Now that we know which pawns can outrun the kings for *
* each side, we need to do determine if one side queens *
* before the other. If so, that side wins. If they *
* queen at the same time, then we will have to rely on *
* the search to handle queening with check or queening *
* and attacking the opponent's queening square. *
* *
************************************************************
*/
if (queener[white] < queener[black])
tree->score_eg += pawn_can_promote + (5 - queener[white]) * 10;
else if (queener[black] < queener[white])
tree->score_eg -= pawn_can_promote + (5 - queener[black]) * 10;
}
/* last modified 11/27/15 */
/*
*******************************************************************************
* *
* 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 flags which pawns are passed, since this is scored in *
* another part of the evaluation because that includes piece information *
* which can not be used here since the pawn hash signature does not include *
* piece information of any kind. *
* *
* Note that a pawn is not penalized for two different reasons. If it is *
* isolated, it is not backward. Etc. This simplifies evaluation tuning *
* not to mention eliminating the overlap and interaction that was happening *
* previously when multiple penalties could be applied. *
* *
*******************************************************************************
*/
void EvaluatePawns(TREE * RESTRICT tree, int side) {
uint64_t pawns, attackers, defenders;
uint64_t doubled, supported, connected, passed, backward;
int square, file, rank, score_eg = 0, score_mg = 0, enemy = Flip(side);
unsigned int isolated, pawn_files = 0;
/*
************************************************************
* *
* Loop through all pawns for this side. *
* *
************************************************************
*/
tree->pawn_score.passed[side] = 0;
for (pawns = Pawns(side); pawns; pawns &= pawns - 1) {
square = LSB(pawns);
file = File(square);
rank = rankflip[side][Rank(square)];
pawn_files |= 1 << file;
/*
************************************************************
* *
* Evaluate pawn advances. Center pawns are encouraged to *
* occupy central squares, edge pawns are penalized on all *
* edge squares to encourage capture toward the center, *
* the rest are neutral. *
* *
************************************************************
*/
score_mg += pval[side][square];
/*
************************************************************
* *
* Evaluate isolated pawns, which are penalized based on *
* which file they occupy. *
* *
************************************************************
*/
isolated = !(Pawns(side) & mask_pawn_isolated[square]);
if (isolated) {
score_mg -= pawn_isolated[mg][file];
score_eg -= pawn_isolated[eg][file];
}
/*
************************************************************
* *
* Evaluate unsupported pawns, which provide a target *
* since they are undefended by a pawn. We exclude pawns *
* that are isolated since they have already been given a *
* penalty. *
* *
************************************************************
*/
supported = Pawns(side) & pawn_attacks[enemy][square];
if (!isolated && !supported) {
score_mg += pawn_unsupported[mg];
score_eg += pawn_unsupported[eg];
}
/*
************************************************************
* *
* Evaluate doubled pawns. If there are other pawns on *
* this file in front of this pawn, penalize this pawn. *
* Note that this will NOT penalize both pawns, just the *
* most rearward one that is really almost worthless. *
* *
* The farther apart two doubled pawns (same file) are, *
* the less weak they are, so the penalty is reduced as *
* this distance increases. *
* *
************************************************************
*/
doubled = Pawns(side) & ((side) ? plus8dir[square] : minus8dir[square]);
if (doubled) {
score_mg -=
pawn_doubled[mg][file] / RankDistance(square, MostAdvanced(side,
doubled));
score_eg -=
pawn_doubled[eg][file] / RankDistance(square, MostAdvanced(side,
doubled));
}
/*
************************************************************
* *
* Test the pawn to see if it is connected to a neighbor *
* which makes it easier to defend. *
* *
************************************************************
*/
connected = Pawns(side) & mask_pawn_connected[side][square];
if (connected) {
score_mg += pawn_connected[mg][rank][file];
score_eg += pawn_connected[eg][rank][file];
}
/*
************************************************************
* *
* Flag passed pawns for use later when we finally call *
* EvaluatePassedPawns. *
* *
************************************************************
*/
passed = !(Pawns(enemy) & mask_passed[side][square]);
if (passed)
tree->pawn_score.passed[side] |= 1 << file;
/*
************************************************************
* *
* Test the pawn to see if it is backward which makes it a *
* target that ties down pieces to defend it. *
* *
************************************************************
*/
backward = 0;
if (!(passed | isolated | connected | (Pawns(side) &
mask_pattacks[side][square]) | (Pawns(enemy) &
PawnAttacks(side, square))))
backward = Pawns(enemy) & PawnAttacks(side, square + direction[side]);
if (backward) {
score_mg -= pawn_backward[mg][file];
score_eg -= pawn_backward[eg][file];
}
/*
************************************************************
* *
* Determine if this pawn is a candidate passed pawn, *
* which 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 at least as many times as it *
* is attacked when it reaches that pawn, then it will end *
* up passed. *
* *
************************************************************
*/
if (!(passed | backward | isolated) &&
!(Pawns(enemy) & ((side) ? plus8dir[square] : minus8dir[square]))) {
defenders = mask_pattacks[side][square + direction[side]] & Pawns(side);
attackers = mask_pattacks[enemy][square] & Pawns(enemy);
if (PopCnt(defenders) >= PopCnt(attackers)) {
score_mg += passed_pawn_candidate[mg][rank];
score_eg += passed_pawn_candidate[eg][rank];
}
}
}
/*
************************************************************
* *
* Give a bonus for distance between left-most pawn and *
* right-most pawn. The idea is that the wider the gap *
* between the pawns, the harder they are for a lone king *
* to control in the endgame. Botvinnik referred to this *
* concept as "trousers" (pants with two legs, the farther *
* the legs are apart, the better for the side with those *
* pawns). *
* *
************************************************************
*/
score_eg += pawn_file_width * (MSB8Bit(pawn_files) - LSB8Bit(pawn_files));
/*
************************************************************
* *
* Evaluate king safety. *
* *
* This uses the function EvaluateKingsFile() and looks at *
* three 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, side, FILEA, FILEC);
tree->pawn_score.defects_m[side] =
EvaluateKingsFile(tree, side, FILEC, FILEF);
tree->pawn_score.defects_k[side] =
EvaluateKingsFile(tree, side, FILEF, FILEH);
/*
************************************************************
* *
* Done. Add mg/eg scores to final result (sign-corrected *
* so that black = -, white = +) and return. *
* *
************************************************************
*/
tree->pawn_score.score_mg += sign[side] * score_mg;
tree->pawn_score.score_eg += sign[side] * score_eg;
}
/* last modified 10/19/15 */
/*
*******************************************************************************
* *
* EvaluateQueens() is used to evaluate queens. *
* *
*******************************************************************************
*/
void EvaluateQueens(TREE * RESTRICT tree, int side) {
uint64_t temp;
int square, i, score_mg = 0, score_eg = 0, 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]) {
i = KingSQ(enemy);
tree->tropism[side] += king_tropism_q[Distance(square, i)];
i = 8 - (RankDistance(square, i) + FileDistance(square, i));
score_mg += i;
score_eg += i;
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 10/19/15 */
/*
*******************************************************************************
* *
* EvaluateRooks() is used to evaluate rooks. *
* *
*******************************************************************************
*/
void EvaluateRooks(TREE * RESTRICT tree, int side) {
uint64_t temp, moves;
int square, rank, file, i, mobility, 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);
/*
************************************************************
* *
* 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];
}
}
/*
************************************************************
* *
* Mobility counts the number of squares the rook attacks, *
* excluding squares with friendly pieces, and weighs each *
* square according to a complex formula that includes *
* files as well as total number of squares attacked. *
* *
************************************************************
*/
mobility = RookMobility(square, OccupiedSquares);
score_mg += mobility;
score_eg += mobility;
/*
************************************************************
* *
* 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. We only need to *
* check this if the rooks mobility is very low. *
* *
************************************************************
*/
if (mobility < 0 && rank == rank1[side] && rank == Rank(KingSQ(side))) {
i = File(KingSQ(side));
if (i > FILEE) {
if (file > i) {
score_mg += mobility * 3;
score_eg += mobility * 3;
}
} else if (i < FILED && file < i) {
score_mg += mobility * 3;
score_eg += mobility * 3;
}
}
/*
************************************************************
* *
* finally check to see if any rooks are on the 7th rank, *
* with the opponent having pawns on that rank or the *
* opponent's king being hemmed in on the 7th/8th rank. *
* If so, we give a bonus for the strong rook. If there *
* is another rook or queen on the 7th that is connected *
* with this one, then the positional advantage is even *
* stronger. *
* *
************************************************************
*/
else if (rank == rank7[side] && (Rank(KingSQ(enemy)) == rank8[side]
|| Pawns(enemy) & rank_mask[rank])) {
score_mg += rook_on_7th[mg];
score_eg += rook_on_7th[eg];
if (RankAttacks(square) & (Queens(side) | Rooks(side))) {
score_mg += rook_connected_7th[mg];
score_eg += rook_connected_7th[eg];
}
}
/*
************************************************************
* *
* Adjust the tropism count for this piece. *
* *
************************************************************
*/
if (tree->dangerous[side]) {
moves = king_attacks[KingSQ(enemy)];
i = (rook_attacks[square] & moves &&
RookAttacks(square,
OccupiedSquares & ~(Queens(side) | Rooks(side))) & moves) ? 1 :
Distance(square, KingSQ(enemy));
tree->tropism[side] += king_tropism_r[i];
}
}
tree->score_mg += sign[side] * score_mg;
tree->score_eg += sign[side] * score_eg;
}
/* last modified 01/03/15 */
/*
*******************************************************************************
* *
* 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, majors, minors, enemy = Flip(side);
if (!Pawns(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, occupied) <= 3)
return 0;
if (TotalPieces(side, occupied) - TotalPieces(enemy, occupied) <= 3 &&
mask_not_edge & Kings(enemy))
return 0;
/*
************************************************************
* *
* If one side is an exchange up, but has no pawns, then *
* that side can not possibly win. *
* *
************************************************************
*/
majors =
TotalPieces(white, rook) + 2 * TotalPieces(white,
queen) - TotalPieces(black, rook) - 2 * TotalPieces(black, queen);
if (Abs(majors) == 1) {
minors =
TotalPieces(black, knight) + TotalPieces(black,
bishop) - TotalPieces(white, knight) - TotalPieces(white, bishop);
if (majors == minors)
return 0;
}
} else {
/*
************************************************************
* *
* 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)
return 1;
}
/*
************************************************************
* *
* If "side" has a pawn, then either the pawn had better *
* not be a rook pawn, or else side 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. *
* *
************************************************************
*/
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 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) {
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;
/*
************************************************************
* *
* If we make it through all the above tests, then "side" *
* can win so we return 1. *
* *
************************************************************
*/
return 1;
}
/*
*******************************************************************************
* *
* InitializeKingSafety() is used to initialize the king safety matrix. *
* This is set so that the matrix, indexed by king safety pawn structure *
* index and by king safety piece tropism, combines the two indices to *
* produce a single score. As either index rises, the king safety score *
* tracks along, but as both rise, the king safety score rises much more *
* quickly. *
* *
*******************************************************************************
*/
void InitializeKingSafety() {
int safety, tropism;
for (safety = 0; safety < 16; safety++) {
for (tropism = 0; tropism < 16; tropism++) {
king_safety[safety][tropism] =
180 * ((safety_vector[safety] + 100) * (tropism_vector[tropism] +
100) / 100 - 100) / 100;
}
}
}