summaryrefslogtreecommitdiff
path: root/src/eval.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2016-06-01 18:58:58 -0700
committerScott Gasch <[email protected]>2016-06-01 18:58:58 -0700
commit3fd43cd5fcb22bb65bf2a92a25d95d801b11c9e0 (patch)
tree9b6443235d16ba17f094a1a1c7ae53d2bcb9267b /src/eval.c
Initial checkin for typhoon chess engine.
Diffstat (limited to 'src/eval.c')
-rwxr-xr-xsrc/eval.c6118
1 files changed, 6118 insertions, 0 deletions
diff --git a/src/eval.c b/src/eval.c
new file mode 100755
index 0000000..944642a
--- /dev/null
+++ b/src/eval.c
@@ -0,0 +1,6118 @@
+/**
+Copyright (c) Scott Gasch
+
+Module Name:
+
+ eval.c
+
+Abstract:
+
+ Position evaluation routines.
+
+Author:
+
+ Scott Gasch ([email protected]) 14 Jun 2004
+
+Revision History:
+
+ $Id: eval.c 357 2008-07-03 16:18:11Z scott $
+
+**/
+
+#include "chess.h"
+
+typedef void (*PEVAL_HELPER)(POSITION *, COOR, PAWN_HASH_ENTRY *);
+typedef FLAG (FASTCALL *PMOBILITY_HELPER)(POSITION *, COOR, ULONG *, ULONG *);
+
+//
+// To simplify code / maintenance I use the same loop for both colors
+// in some places. These globals coorespond to "ahead of the piece"
+// or "behind the piece" for each color.
+//
+const int g_iAhead[2] = { +1, -1 };
+const int g_iBehind[2] = { -1, +1 };
+
+//
+// General eval terms
+// ---------------------------------------------------------------------------
+//
+static SCORE TRADE_PIECES[3][17] =
+{// 0 1 2 3 4 5 6 7 8 | 9..15 -- down piece count
+ { -1, 90, 81, 73, 67, 62, 57, 52, 50, 50,50,50,50,50,50,50,-1},
+ { -1, 125, 117, 109, 100, 91, 84, 78, 71, 66,66,66,66,66,66,66,-1},
+ { -1, 150, 132, 124, 116, 109, 102, 94, 87, 77,77,77,77,77,77,77,-1},
+};
+
+static SCORE DONT_TRADE_PAWNS[3][9] =
+{// 0 1 2 3 4 5 6 7 8 -- up pawn count
+ { -43, -15, 0, +3, +6, +10, +15, +21, +28 }, // 0
+ { -63, -25, 0, +4, +9, +14, +19, +25, +32 }, // 1
+ { -10, 0, 0, +7, +15, +20, +24, +29, +36 }, // 2
+};
+
+static ULONG REDUCED_MATERIAL_DOWN_SCALER[32] =
+{
+// none na na m na R 2m na
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+// Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m
+ 0, 1, 1, 1, 1, 1, 2, 3,
+
+// 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m
+// Q2R Q2Rm
+ 4, 5, 6, 6, 7, 7, 7, 7,
+
+// na Q2R2m QR4m na Q2R3m na na full
+ 8, 8, 8, 8, 8, 8, 8, 8,
+};
+
+static ULONG REDUCED_MATERIAL_UP_SCALER[32] =
+{
+// none na na m na R 2m na
+ 8, 8, 8, 8, 8, 8, 8, 8,
+
+// Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m
+ 8, 7, 7, 7, 7, 6, 6, 5,
+
+// 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m
+// Q2R Q2Rm
+ 4, 3, 3, 3, 2, 2, 2, 1,
+
+// na Q2R2m QR4m na Q2R3m na na full
+ 1, 1, 0, 0, 0, 0, 0, 0,
+};
+
+static ULONG PASSER_MATERIAL_UP_SCALER[32] =
+{
+// none na na m na R 2m na
+ 8, 8, 8, 8, 8, 8, 7, 7,
+
+// Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m
+ 7, 6, 7, 5, 4, 5, 4, 3,
+
+// 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m
+// Q2R Q2Rm
+ 2, 2, 1, 1, 0, 0, 0, 0,
+
+// na Q2R2m QR4m na Q2R3m na na full
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+COOR QUEENING_RANK[2] = { 1, 8 };
+COOR JUMPING_RANK[2] = { 7, 2 };
+
+//
+// Pawn eval terms
+// ---------------------------------------------------------------------------
+//
+// "There is one case which can be treated as positional or material,
+// namely the rook's pawn, which differs from other pawns in that it
+// can only capture one way instead of two. Since this handicap cannot
+// be corrected without the opponent's help, I teach my students to
+// regard the rook's pawn as a different piece type, a crippled
+// pawn. Database statistics indicate that it is on average worth
+// about 15% less than a normal pawn. The difference is enough so that
+// it is usually advantageous to make a capture with a rook's pawn,
+// promoting it to a knights pawn, even if that produces doubled pawns
+// and even if there is no longer a rook on the newly opened rook's
+// file."
+// --Larry Kaufman, IM
+// "Evaluation of Material Imbalance"
+//
+static SCORE PAWN_CENTRALITY_BONUS[128] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0,
+ -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0,
+ -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0,
+ -8, 0, 5, 9, 9, 5, 0, -8, 0,0,0,0,0,0,0,0,
+// -------------------------------------------------------------------
+ -8, 0, 5, 9, 9, 5, 0, -8, 0,0,0,0,0,0,0,0,
+ -8, 0, 5, 5, 5, 5, 0, -8, 0,0,0,0,0,0,0,0,
+ -8, 0, 0, 0, 0, 0, 0, -8, 0,0,0,0,0,0,0,0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,0,0,0,0,0,0,0
+};
+
+static SCORE BACKWARD_SHIELDED_BY_LOCATION[128] =
+{
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0,
+ -7, -5, -5, -7, -7, -5, -5, -7, 0,0,0,0,0,0,0,0,
+ -5, -4, -4, -5, -5, -4, -4, -5, 0,0,0,0,0,0,0,0,
+ -4, -3, -3, -4, -4, -3, -3, -4, 0,0,0,0,0,0,0,0,
+// -------------------------------------------------------------------
+ -4, -3, -3, -4, -4, -3, -3, -4, 0,0,0,0,0,0,0,0,
+ -5, -4, -4, -5, -5, -4, -4, -5, 0,0,0,0,0,0,0,0,
+ -7, -5, -5, -7, -7, -5, -5, -7, 0,0,0,0,0,0,0,0,
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0
+};
+
+static SCORE BACKWARD_EXPOSED_BY_LOCATION[128] =
+{
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0,
+ -12, -9, -9, -11, -11, -9, -9, -12, 0,0,0,0,0,0,0,0,
+ -9, -7, -7, -9, -9, -7, -7, -9, 0,0,0,0,0,0,0,0,
+ -7, -6, -6, -7, -7, -6, -6, -7, 0,0,0,0,0,0,0,0,
+// -------------------------------------------------------------------
+ -7, -6, -6, -7, -7, -6, -6, -7, 0,0,0,0,0,0,0,0,
+ -9, -7, -7, -9, -9, -7, -7, -9, 0,0,0,0,0,0,0,0,
+ -12, -9, -9, -11, -11, -9, -9, -12, 0,0,0,0,0,0,0,0,
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0
+};
+
+//
+// "The first statement I can confirm is that doubled pawns are indeed
+// on average undesirable. However this statistic needs to be broken
+// down to be useful. Doubled pawns themselves are really more
+// serious than this generally, but when your pawns are doubled you
+// automatically get an extra half-open file for your rooks (or queen,
+// but the queen can also use diagonals). So it follows logically
+// that the net cost of doubled pawns is much greater in the absence
+// of major pieces. The database shows that with all rooks present,
+// doubled pawns "cost" only about 1/16th of a pawn on average. With
+// one rook each the cost rises to 1/4th of a pawn and with no rooks
+// present to 3/8ths of a pawn. With the queens present the cost is
+// again only 1/16th of a pawn; without them it's a quarter pawn. So
+// the lessons are clear; beware of doubled pawns when major pieces
+// have been exchanged, and beware of exchanging major pieces when you
+// are the one with the doubled pawns."
+// --Larry Kaufman, IM
+// "All About Doubled Pawns"
+//
+// Note: indexed by number of files with 1+ pawn on them.
+static SCORE DOUBLED_PAWN_PENALTY_BY_COUNT[4][9] =
+{ // ------------ count of doubled+ pawns ------------
+ {// 0 1 2 3 4 5 6 7 8 : no majors alive
+ +0, -32, -65, -99, -134, -170, -207, -222, -250
+ },
+ {// : 1 rook
+ +0, -23, -47, -76, -108, -144, -184, -200, -216
+ },
+ {// : 2 rooks or 1 queen
+ +0, -13, -23, -34, -46, -64, -86, -111, -138
+ },
+ {// : all majors alive
+ +0, -7, -13, -25, -39, -55, -73, -95, -121
+ },
+};
+
+static SCORE ISOLATED_PAWN_PENALTY_BY_COUNT[9] =
+{// 0 1 2 3 4 5 6 7 8
+ +0, -3, -7, -9, -16, -35, -50, -85, -100
+};
+
+static SCORE ISOLATED_PAWN_BY_PAWNFILE[9] =
+{
+ 0, -7, -8, -9, -10, -10, -9, -8, -7
+};
+
+static SCORE ISOLATED_EXPOSED_PAWN = -5;
+
+static SCORE ISOLATED_DOUBLED_PAWN = -11;
+
+//
+// Note: -25% to -33% if the enemy occupies or controls the next sq.
+//
+static SCORE PASSER_BY_RANK[2][9] =
+{// 0 1 2 3 4 5 6 7 8
+ { +0, +0,+162,+111, +62, +36, +18, +13, +0 }, // black
+ { +0, +0, +13, +18, +36, +62,+111,+162, +0 } // white
+};
+
+static SCORE CANDIDATE_PASSER_BY_RANK[2][9] =
+{// 0 1 2 3 4 5 6 7 8
+ { +0, +0, +0, +48, +34, +22, +13, +9, +0 }, // black
+ { +0, +0, +9, +13, +22, +34, +48, +0, +0 } // white
+};
+
+// Note: x2
+static SCORE CONNECTED_PASSERS_BY_RANK[2][9] =
+{// 0 1 2 3 4 5 6 7 8
+ { +0, +0, +96, +74, +48, +21, +10, +5, +0 }, // black
+ { +0, +0, +5, +10, +21, +48, +74, +96, +0 } // white
+};
+
+static SCORE SUPPORTED_PASSER_BY_RANK[2][9] =
+{// 0 1 2 3 4 5 6 7 8
+ { +0, +0, +60, +40, +13, +6, +3, +1, +0 }, // black
+ { +0, +0, +1, +3, +6, +13, +40, +60, +0 } // white
+};
+
+static SCORE OUTSIDE_PASSER_BY_DISTANCE[9] =
+{// 0 1 2 3 4 5 6 7 8
+ +0, +0, +7, +14, +21, +28, +34, +41, +55
+};
+
+static SCORE PASSER_BONUS_AS_MATERIAL_COMES_OFF[32] =
+{
+// none na na m na R 2m na
+ +60, -1, -1, +37, -1, +43, +19, -1,
+
+// Rm 3m/Q 2R R2m 4m/Qm 2Rm R3m/QR Q2m
+ +14, +9, +20, +4, +3, +3, 0, 0,
+
+// 2R2m R4m/QRm Q3m 2R3m/ QR2m Q4m 2R4m/ QR3m
+// Q2R Q2Rm
+ 0, 0, 0, 0, 0, 0, 0, 0,
+
+// na Q2R2m QR4m na Q2R3m na na full
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+SCORE RACER_WINS_RACE = +800;
+
+static SCORE UNDEVELOPED_MINORS_IN_OPENING[5] =
+{// 0 1 2 3 4
+ 0, -6, -10, -16, -22
+};
+
+//
+// Bishop eval terms
+// ---------------------------------------------------------------------------
+//
+static SCORE BISHOP_OVER_KNIGHT_IN_ENDGAME = +33;
+
+//
+// "The bishop pair has an average value of half a pawn (more when the
+// opponent has no minor pieces to exchange for one of the bishops),
+// enough to regard it as part of the material evaluation of the
+// position, and enough to overwhelm most positional considerations.
+// Moreover, this substantial bishop pair value holds up in all
+// situations tested, regardless of what else is on the board...
+//
+// ...One rule which I often teach to students is that if you have the
+// bishop pair, and your opponent's single bishop is a bad bishop
+// (hemmed in by his own pawns), you already have full compensation
+// for a pawn... Kasparov has said something similar...
+//
+// As noted before, the bishop pair is worth more with fewer pawns on
+// the board. Aside from this factor, the half pawn value of the
+// bishop pair is remarkably constant, applying even when there are no
+// pieces on the board except two minors each."
+// --Larry Kaufman, IM
+// "Evaluation of Material Imbalance"
+static SCORE BISHOP_PAIR[2][17] =
+{
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 53, 52, 51, 50, 49, 48, 47, 45, 43, 41, 40, 40, 40, 40, 40, 40, 40 }
+};
+
+static SCORE STATIONARY_PAWN_ON_BISHOP_COLOR[128] =
+{
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0,
+ -4, -6, -7, -7, -7, -7, -6, -4, 0,0,0,0,0,0,0,0,
+ -6, -7, -9, -8, -8, -9, -7, -6, 0,0,0,0,0,0,0,0,
+ -7, -8, -10, -11, -11, -10, -8, -7, 0,0,0,0,0,0,0,0,
+// -------------------------------------------------------------------
+ -7, -8, -10, -11, -11, -10, -8, -7, 0,0,0,0,0,0,0,0,
+ -6, -7, -9, -8, -8, -9, -7, -6, 0,0,0,0,0,0,0,0,
+ -4, -6, -7, -7, -7, -7, -6, -4, 0,0,0,0,0,0,0,0,
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0
+};
+
+static SCORE TRANSIENT_PAWN_ON_BISHOP_COLOR[128] =
+{
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0,
+ -1, -1, -2, -2, -2, -2, -1, -1, 0,0,0,0,0,0,0,0,
+ -3, -4, -4, -4, -4, -4, -4, -3, 0,0,0,0,0,0,0,0,
+ -4, -5, -7, -8, -8, -7, -5, -4, 0,0,0,0,0,0,0,0,
+// -------------------------------------------------------------------
+ -4, -5, -7, -8, -8, -7, -5, -4, 0,0,0,0,0,0,0,0,
+ -3, -4, -4, -4, -4, -4, -4, -3, 0,0,0,0,0,0,0,0,
+ -1, -1, -2, -2, -2, -2, -1, -1, 0,0,0,0,0,0,0,0,
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0
+};
+
+//
+// A bishop can move to between 0..13 squares.
+//
+static SCORE BISHOP_MOBILITY_BY_SQUARES[14] =
+{// 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ -22, -14, -10, -5, -1, 0, +1, +3, +4, +5, +6, +7, +8, +9
+};// ^ |
+
+static SCORE BISHOP_MAX_MOBILITY_IN_A_ROW_BONUS[8] =
+{// 0 1 2 3 4 5 6 7
+ -10, -4, +1, +3, +4, +5, +5, +5
+};
+
+static SCORE BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING[9] =
+{// 0 1 2 3 4 5 6 7 8
+ +0, +33, +28, +18, +8, +4, +0, -10, -16
+};
+
+static SCORE BISHOP_IN_CLOSED_POSITION[33] =
+{// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ 10, 10, 10, 9, 9, 8, 8, 7, 7, 6, 5, 4, 3, 2, 1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+// 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
+};
+
+//
+// Knight eval terms
+// ---------------------------------------------------------------------------
+//
+static SCORE KNIGHT_CENTRALITY_BONUS[128] =
+{
+ -12, -8, -8, -8, -8, -8, -8, -12, 0,0,0,0,0,0,0,0,
+ -8, -2, -2, 0, 0, -2, -2, -8, 0,0,0,0,0,0,0,0,
+ -8, -2, +4, +5, +5, +4, -2, -8, 0,0,0,0,0,0,0,0,
+ -8, -2, +5, +8, +8, +5, -2, -8, 0,0,0,0,0,0,0,0,
+// -------------------------------------------------------------------
+ -8, -2, +5, +8, +8, +5, -2, -8, 0,0,0,0,0,0,0,0,
+ -8, -2, +4, +5, +5, +4, -2, -8, 0,0,0,0,0,0,0,0,
+ -8, -2, -2, 0, 0, -2, -2, -8, 0,0,0,0,0,0,0,0,
+ -12, -8, -8, -8, -8, -8, -8, -12, 0,0,0,0,0,0,0,0
+};
+
+static SCORE KNIGHT_KING_TROPISM_BONUS[9] =
+{// 0 1 2 3 4 5 6 7 8
+ 0, +15, +12, +9, +4, +0, +0, +0, +0
+};
+
+
+static SCORE KNIGHT_UNASSAILABLE_BY_DIST_FROM_EKING[9] =
+{// 0 1 2 3 4 5 6 7 8
+ +0, +25, +19, +14, +6, +0, +0, +0, +0
+};
+
+static SCORE KNIGHT_ON_INTERESTING_SQUARE_BY_RANK[2][9] =
+{// 0 1 2 3 4 5 6 7 8
+ { +0, +12, +11, +9, +6, +3, +0, +0, +0 }, // black
+ { +0, +0, +0, +0, +3, +6, +9, +11, +12 } // white
+};
+
+//
+// A knight can move to between 0..8 squares
+// -----------------------------------------
+// 0 = supported enemy pawn
+// 1 = unsupported enemy pawn, friend pawn, enemy B/N
+// enemy controlled empty sq.
+// 2 = enemy >B, friend controlled empty sq, no one controlled empty sq
+//
+// Total: 16 mobility max
+//
+static SCORE KNIGHT_MOBILITY_BY_COUNT[9] =
+{// 0 1 2 3 4 5 6 7 8
+ -17, -10, -6, 0, +3, +5, +7, +8, +8
+};// ^ |
+
+static SCORE KNIGHT_WITH_N_PAWNS_SUPPORTING[3] =
+{
+ +0, +4, +8
+};
+
+static SCORE KNIGHT_IN_CLOSED_POSITION[33] =
+{// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 4, 6, 9, 11,
+ 12, 13, 15, 17, 19, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 27
+// 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
+};
+
+//
+// Rook eval terms
+// ---------------------------------------------------------------------------
+//
+static SCORE ROOK_ON_FULL_OPEN_BY_DIST_FROM_EKING[8] =
+{// 0 1 2 3 4 5 6 7
+ +24, +22, +17, +14, +13, +13, +12, +12
+};
+
+static SCORE ROOK_ON_HALF_OPEN_WITH_ENEMY_BY_DIST_FROM_EKING[8] =
+{// 0 1 2 3 4 5 6 7
+ +12, +11, +9, +9, +8, +8, +8, +7
+};
+
+static SCORE ROOK_ON_HALF_OPEN_WITH_FRIEND_BY_DIST_FROM_EKING[8] =
+{// 0 1 2 3 4 5 6 7
+ +13, +12, +11, +11, +9, +9, +9, +8
+};
+
+static SCORE ROOK_BEHIND_PASSER_BY_PASSER_RANK[2][9] =
+{// 0 1 2 3 4 5 6 7 8
+ { +0, +0, +25, +17, +12, +6, +1, +0, +0 }, // black
+ { +0, +0, +0, +1, +6, +12, +17, +25, +0 } // white
+};
+
+static SCORE ROOK_LEADS_PASSER_BY_PASSER_RANK[2][9] =
+{// 0 1 2 3 4 5 6 7 8
+ { +0, +0, -22, -16, -13, -9, -5, -3, +0 }, // black
+ { +0, +0, -3, -5, -9, -13, -16, -22, +0 } // white
+};
+
+static SCORE KING_TRAPPING_ROOK = -40;
+
+static SCORE ROOK_TRAPPING_EKING = +22;
+
+static SCORE ROOK_VALUE_AS_PAWNS_COME_OFF[17] =
+{// 0 1 2 3 4 5 6 7 8
+ +55, +51, +44, +38, +33, +27, +22, +16, +7,
+ // 9 10 11 12 13 14 15 16
+ +1, -3, -6, -9, -12, -18, -22, -25
+};
+
+//
+// Note: these are multiplied by two (one per rook).
+//
+static SCORE ROOK_CONNECTED_VERT = +7; // x2
+static SCORE ROOK_CONNECTED_HORIZ = +4; // x2
+
+//
+// A rook can move to between 0..14 squares
+// ----------------------------------------
+// 0 = supported enemy P/N/B, friend piece
+// 1 = unsupported enemy piece, enemy controlled empty sq, enemy R
+// 2 = enemy >R, friend controlled empty sq, no one controlled empty sq
+//
+// Total: 28 mobility max
+//
+static SCORE ROOK_MOBILITY_BY_SQUARES[15] =
+{// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ -28, -24, -20, -14, -7, -2, +0, +4, +8, +12, +15, +17, +19, +21, +22
+};// ^ |
+
+static SCORE ROOK_MAX_MOBILITY_IN_A_ROW_BONUS[8] =
+{// 0 1 2 3 4 5 6 7
+ -15, -6, +0, +4, +8, +8, +8, +8
+};
+
+//
+// Queen eval terms
+// ---------------------------------------------------------------------------
+//
+
+
+//
+// A queen can move to between 0..27 squares
+// -----------------------------------------
+// 0 = supported enemy P/N/B/R, friend piece
+// 1 = unsupported enemy piece, enemy controlled empty sq, enemy Q
+// 2 = enemy K, no one controlled empty sq, friend controlled empty sq
+//
+// Total: 54 mobility max
+//
+static SCORE QUEEN_MOBILITY_BY_SQUARES[28] =
+{// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+ -30, -26, -22, -17, -11, -8, -4, -2, -1, +2, +4, +7, +10, +12, +14,
+// |
+
+ //15 16 17 18 19 20 21 22 23 24 25 26 27
+ +15, +16, +17, +18, +19, +20, +20, +21, +21, +22, +22, +23, +23
+};
+
+static SCORE QUEEN_OUT_EARLY[5] =
+{// 0 1 2 3 4 : num unmoved minors
+ 0, -14, -22, -26, -33
+};
+
+//
+// In "Evaluation of Material Imbalance", IM Larry Kaufman makes the
+// point that queens are worth more then the standard nine "points".
+// Thus, this scale is biased upwards by about 0.15 pawn.
+//
+static SCORE QUEEN_KING_TROPISM[8] =
+{// 0 1 2 3 4 5 6 7
+ 0, +34, +28, +22, +19, +17, +16, +15
+};
+
+static SCORE QUEEN_ATTACKS_SQ_NEXT_TO_KING = 8; // x #sq_attacked
+
+//
+// King eval terms
+// ---------------------------------------------------------------------------
+//
+static ULONG KING_INITIAL_COUNTER_BY_LOCATION[2][128] =
+{
+ {
+ 1, 0, 0, 0, 1, 0, 0, 1, 0,0,0,0,0,0,0,0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0,0,0,0,0,0,0,0,
+ 3, 3, 3, 3, 3, 3, 3, 3, 0,0,0,0,0,0,0,0,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ // -------------------------------------------------------------------
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0
+ },
+ {
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ // -------------------------------------------------------------------
+ 4, 4, 4, 4, 4, 4, 4, 4, 0,0,0,0,0,0,0,0,
+ 3, 3, 3, 3, 3, 3, 3, 3, 0,0,0,0,0,0,0,0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 0,0,0,0,0,0,0,0,
+ 1, 0, 0, 0, 1, 0, 0, 1, 0,0,0,0,0,0,0,0
+ }
+};
+
+static SCORE KING_TO_CENTER[128] =
+{
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0,
+ +1, +3, +5, +7, +7, +5, +3, +1, 0,0,0,0,0,0,0,0,
+ +3, +5, +13, +15, +15, +13, +5, +3, 0,0,0,0,0,0,0,0,
+ +5, +7, +17, +23, +23, +17, +7, +5, 0,0,0,0,0,0,0,0,
+// ------------------------------------------------------------------
+ +5, +7, +17, +23, +23, +17, +7, +5, 0,0,0,0,0,0,0,0,
+ +3, +5, +13, +15, +15, +13, +5, +3, 0,0,0,0,0,0,0,0,
+ +1, +3, +5, +7, +7, +5, +3, +1, 0,0,0,0,0,0,0,0,
+ +0, +0, +0, +0, +0, +0, +0, +0, 0,0,0,0,0,0,0,0
+};
+
+//
+// If a side has less than this much material we don't bother scoring
+// the other side's king safety. Note: this doesn't incl pawn
+// material.
+//
+#define DO_KING_SAFETY_THRESHOLD \
+ (VALUE_ROOK + VALUE_BISHOP + VALUE_KING + 1)
+
+//
+// If a side has less than this much material the other side's king
+// can come out. Note: this doesn't incl pawn material.
+//
+#define KEEP_KING_AT_HOME_THRESHOLD \
+ (VALUE_ROOK + VALUE_ROOK + VALUE_BISHOP + VALUE_KING + 1)
+
+static ULONG KING_COUNTER_BY_ATTACK_PATTERN[32] =
+{
+//p 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
+//m 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
+//r 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1
+//q 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1 0 0 1 1
+//k 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1
+ 0,0,2,2,1,1,3,3,1,1,3,3,2,2,4,4,2,2,3,3,2,2,4,4,2,2,4,4,3,4,5,5
+};
+
+static SCORE KING_SAFETY_BY_COUNTER[42] =
+{
+ -0, -4, -8, -12, -16, -20, -25, -31, -38, -50, -63, -76, -90,
+ -105,-120,-135,-150,-165,-180,-195,-210,-225,-240,-255,-270,-290,
+ -310,-330,-350,-370,-390,-410,-440,-470,-500,-500,-500,-500,-500,
+ -500,-500,-500
+};
+
+static SCORE KING_MISSING_ONE_CASTLE_OPTION = -23;
+typedef struct _DNA_BASE_SIZE {
+ SCORE *pBase;
+ ULONG uCount;
+} DNA_BASE_SIZE;
+
+#define DNA_VAR(x) {(SCORE *)&(x), 1}
+#define DNA_ARRAY(x) {(SCORE *)(x), ARRAY_LENGTH(x)}
+#define DNA_MATRIX(x) {(SCORE *)(x), ARRAY_LENGTH(x) * ARRAY_LENGTH((x)[0])}
+static DNA_BASE_SIZE g_EvalDNA[] = {
+ DNA_MATRIX(TRADE_PIECES),
+ DNA_MATRIX(DONT_TRADE_PAWNS),
+ DNA_ARRAY(REDUCED_MATERIAL_DOWN_SCALER),
+ DNA_ARRAY(REDUCED_MATERIAL_UP_SCALER),
+ DNA_ARRAY(PASSER_MATERIAL_UP_SCALER),
+ DNA_ARRAY(PAWN_CENTRALITY_BONUS),
+ DNA_ARRAY(BACKWARD_SHIELDED_BY_LOCATION),
+ DNA_ARRAY(BACKWARD_EXPOSED_BY_LOCATION),
+ DNA_MATRIX(DOUBLED_PAWN_PENALTY_BY_COUNT),
+ DNA_ARRAY(ISOLATED_PAWN_PENALTY_BY_COUNT),
+ DNA_ARRAY(ISOLATED_PAWN_BY_PAWNFILE),
+ DNA_VAR(ISOLATED_EXPOSED_PAWN),
+ DNA_VAR(ISOLATED_DOUBLED_PAWN),
+ DNA_MATRIX(PASSER_BY_RANK),
+ DNA_MATRIX(CANDIDATE_PASSER_BY_RANK),
+ DNA_MATRIX(CONNECTED_PASSERS_BY_RANK),
+ DNA_MATRIX(SUPPORTED_PASSER_BY_RANK),
+ DNA_ARRAY(OUTSIDE_PASSER_BY_DISTANCE),
+ DNA_ARRAY(PASSER_BONUS_AS_MATERIAL_COMES_OFF),
+ DNA_VAR(RACER_WINS_RACE),
+ DNA_ARRAY(UNDEVELOPED_MINORS_IN_OPENING),
+ DNA_VAR(BISHOP_OVER_KNIGHT_IN_ENDGAME),
+ DNA_MATRIX(BISHOP_PAIR),
+ DNA_ARRAY(STATIONARY_PAWN_ON_BISHOP_COLOR),
+ DNA_ARRAY(TRANSIENT_PAWN_ON_BISHOP_COLOR),
+ DNA_ARRAY(BISHOP_MOBILITY_BY_SQUARES),
+ DNA_ARRAY(BISHOP_MAX_MOBILITY_IN_A_ROW_BONUS),
+ DNA_ARRAY(BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING),
+ DNA_ARRAY(BISHOP_IN_CLOSED_POSITION),
+ DNA_ARRAY(KNIGHT_CENTRALITY_BONUS),
+ DNA_ARRAY(KNIGHT_KING_TROPISM_BONUS),
+ DNA_ARRAY(KNIGHT_UNASSAILABLE_BY_DIST_FROM_EKING),
+ DNA_MATRIX(KNIGHT_ON_INTERESTING_SQUARE_BY_RANK),
+ DNA_ARRAY(KNIGHT_MOBILITY_BY_COUNT),
+ DNA_ARRAY(KNIGHT_WITH_N_PAWNS_SUPPORTING),
+ DNA_ARRAY(KNIGHT_IN_CLOSED_POSITION),
+ DNA_ARRAY(ROOK_ON_FULL_OPEN_BY_DIST_FROM_EKING),
+ DNA_ARRAY(ROOK_ON_HALF_OPEN_WITH_ENEMY_BY_DIST_FROM_EKING),
+ DNA_ARRAY(ROOK_ON_HALF_OPEN_WITH_FRIEND_BY_DIST_FROM_EKING),
+ DNA_MATRIX(ROOK_BEHIND_PASSER_BY_PASSER_RANK),
+ DNA_MATRIX(ROOK_LEADS_PASSER_BY_PASSER_RANK),
+ DNA_VAR(KING_TRAPPING_ROOK),
+ DNA_VAR(ROOK_TRAPPING_EKING),
+ DNA_ARRAY(ROOK_VALUE_AS_PAWNS_COME_OFF),
+ DNA_VAR(ROOK_CONNECTED_VERT),
+ DNA_VAR(ROOK_CONNECTED_HORIZ),
+ DNA_ARRAY(ROOK_MOBILITY_BY_SQUARES),
+ DNA_ARRAY(ROOK_MAX_MOBILITY_IN_A_ROW_BONUS),
+ DNA_ARRAY(QUEEN_MOBILITY_BY_SQUARES),
+ DNA_ARRAY(QUEEN_OUT_EARLY),
+ DNA_ARRAY(QUEEN_KING_TROPISM),
+ DNA_VAR(QUEEN_ATTACKS_SQ_NEXT_TO_KING),
+ DNA_MATRIX(KING_INITIAL_COUNTER_BY_LOCATION),
+ DNA_ARRAY(KING_TO_CENTER),
+ DNA_ARRAY(KING_SAFETY_BY_COUNTER),
+ DNA_VAR(KING_MISSING_ONE_CASTLE_OPTION)
+};
+
+ULONG
+DNABufferSizeBytes()
+{
+ ULONG u;
+ ULONG uSize = 0;
+ for (u = 0; u < ARRAY_LENGTH(g_EvalDNA); u++) {
+ uSize += 10 * g_EvalDNA[u].uCount;
+ }
+ return uSize + 1;
+}
+
+
+char *
+ExportEvalDNA()
+{
+ ULONG uSize = DNABufferSizeBytes();
+ char *p = malloc(uSize);
+ ULONG u, v;
+ SCORE *q;
+ FLAG fFirst = TRUE;
+
+ memset(p, 0, uSize);
+ for (u = 0; u < ARRAY_LENGTH(g_EvalDNA); u++) {
+ q = g_EvalDNA[u].pBase;
+ for (v = 0; v < g_EvalDNA[u].uCount; v++) {
+ if (!fFirst) {
+ sprintf(p, "%s,%d", p, *q);
+ } else {
+ sprintf(p, "%s%d", p, *q);
+ fFirst = FALSE;
+ }
+ q++;
+ }
+ strcat(p, "\n");
+ fFirst = TRUE;
+ }
+ return p; // caller must free
+}
+
+FLAG
+WriteEvalDNA(char *szFilename)
+{
+ FILE *p = NULL;
+ char *q = NULL;
+ FLAG fRet = FALSE;
+
+ if (SystemDoesFileExist(szFilename)) goto end;
+ p = fopen(szFilename, "a+b");
+ if (!p) goto end;
+ q = ExportEvalDNA();
+ if (!q) goto end;
+ fprintf(p, q);
+ fRet = TRUE;
+ end:
+ if (p) fclose(p);
+ if (q) free(q);
+ return fRet;
+}
+
+FLAG
+ImportEvalDNA(char *p)
+{
+ ULONG u, v;
+
+ for (u = 0; u < ARRAY_LENGTH(g_EvalDNA); u++) {
+ for (v = 0; v < g_EvalDNA[u].uCount; v++) {
+ while(*p && (!isdigit(*p) && (*p != '-'))) p++;
+ if (*p == '\0') return FALSE;
+ *(g_EvalDNA[u].pBase + v) = atoi(p);
+ while(*p && (isdigit(*p) || (*p == '-'))) p++;
+ }
+ }
+ return TRUE;
+}
+
+FLAG
+ReadEvalDNA(char *szFilename)
+{
+ FILE *p = NULL;
+ ULONG uSize = DNABufferSizeBytes();
+ char *q = malloc(uSize);
+ FLAG fRet = FALSE;
+ static char line[1024];
+ char *c;
+
+ if (!q) goto end;
+ memset(q, 0, uSize);
+ p = fopen(szFilename, "rb");
+ if (!p) {
+ Trace("Failed to open file \"%s\"\n", szFilename);
+ goto end;
+ }
+ while(fgets(line, 1024, p) != NULL) {
+ c = strchr(line, '#');
+ if (c) *c = '\0';
+ c = line;
+ strcat(q, c);
+ }
+ fRet = ImportEvalDNA(q);
+ end:
+ if (q) free(q);
+ if (p) fclose(p);
+ return fRet;
+}
+
+
+
+//
+// Misc stuff
+// ---------------------------------------------------------------------------
+//
+
+//
+// The three ranks "around" a rank (indexed by rank, used in eval.c)
+//
+BITBOARD BBADJACENT_RANKS[9] =
+{
+ 0,
+ BBRANK11 | BBRANK22,
+ BBRANK11 | BBRANK22 | BBRANK33,
+ BBRANK22 | BBRANK33 | BBRANK44,
+ BBRANK33 | BBRANK44 | BBRANK55,
+ BBRANK44 | BBRANK55 | BBRANK66,
+ BBRANK55 | BBRANK66 | BBRANK77,
+ BBRANK66 | BBRANK77 | BBRANK88,
+ BBRANK77 | BBRANK88
+};
+
+
+//
+// The files "around" one, indexed by file (used in eval.c)
+//
+BITBOARD BBADJACENT_FILES[8] =
+{
+ // A = 0
+ BBFILEB,
+
+ // B = 1
+ BBFILEA | BBFILEC,
+
+ // C = 2
+ BBFILEB | BBFILED,
+
+ // D = 3
+ BBFILEC | BBFILEE,
+
+ // E = 4
+ BBFILED | BBFILEF,
+
+ // F = 5
+ BBFILEE | BBFILEG,
+
+ // G = 6
+ BBFILEF | BBFILEH,
+
+ // H = 7
+ BBFILEG
+};
+
+//
+// The ranks preceeding one, indexed by rank/color of pawn (used in
+// eval.c)
+//
+BITBOARD BBPRECEEDING_RANKS[8][2] =
+{
+ // BLACK WHITE
+ // ----- -----
+ { // 0 == RANK8
+ BBRANK72, 0
+ },
+ { // 1 == RANK7
+ BBRANK62, 0
+ },
+ { // 2 == RANK6
+ BBRANK52, BBRANK77
+ },
+ { // 3 == RANK5
+ BBRANK42, BBRANK67
+ },
+ { // 4 == RANK4
+ BBRANK32, BBRANK57
+ },
+ { // 5 == RANK3
+ BBRANK22, BBRANK47
+ },
+ { // 6 == RANK2
+ 0, BBRANK37
+ },
+ { // 7 == RANK1
+ 0, BBRANK27
+ }
+};
+
+
+static FLAG
+_IsSquareSafeFromEnemyPawn(IN POSITION *pos,
+ IN COOR c,
+ IN BITBOARD bb)
+/**
+
+Routine description:
+
+ Determine if a piece in square c is "outposted". A piece is
+ outposted if no enemy pawns can advance to attack it / drive
+ it away.
+
+Parameters:
+
+ POSITION *pos : the board
+ COOR c : the square (must not be empty!)
+ BITBOARD bb : the bitboard of pawns you want to consider
+
+Return value:
+
+ FLAG : TRUE if the piece is safe/outposted, FALSE otherwise
+
+**/
+{
+ ULONG uColor = GET_COLOR(pos->rgSquare[c].pPiece);
+ ULONG uFile, uRank;
+#ifdef DEBUG
+ COOR cSquare;
+ PIECE p = pos->rgSquare[c].pPiece;
+ BITBOARD dbb = bb;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(p && (IS_BISHOP(p) || IS_KNIGHT(p) || IS_PAWN(p)));
+ ASSERT(IS_VALID_COLOR(uColor));
+#endif
+
+ uFile = FILE(c);
+ ASSERT(uFile < 8);
+ bb &= BBADJACENT_FILES[uFile];
+
+ ASSERT(((c & 0x70) >> 4) == (c >> 4));
+ uRank = c >> 4;
+ ASSERT(uRank < 8);
+ bb &= BBPRECEEDING_RANKS[uRank][uColor];
+
+#ifdef DEBUG
+ if (bb != 0)
+ {
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&dbb)))
+ {
+ ASSERT(pos->rgSquare[cSquare].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ if ((FILE(cSquare) == (FILE(c) - 1)) ||
+ (FILE(cSquare) == (FILE(c) + 1)))
+ {
+ switch(uColor)
+ {
+ case WHITE:
+ if (RANK(cSquare) > RANK(c))
+ {
+ return(FALSE);
+ }
+ break;
+ case BLACK:
+ if (RANK(cSquare) < RANK(c))
+ {
+ return(FALSE);
+ }
+ break;
+ }
+ }
+ }
+ ASSERT(FALSE);
+ }
+ else
+ {
+ ASSERT(bb == 0);
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&dbb)))
+ {
+ ASSERT(pos->rgSquare[cSquare].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ if ((FILE(cSquare) == (FILE(c) - 1)) ||
+ (FILE(cSquare) == (FILE(c) + 1)))
+ {
+ switch(uColor)
+ {
+ case WHITE:
+ if (RANK(cSquare) > RANK(c))
+ {
+ ASSERT(FALSE);
+ }
+ break;
+ case BLACK:
+ if (RANK(cSquare) < RANK(c))
+ {
+ ASSERT(FALSE);
+ }
+ break;
+ }
+ }
+ }
+ }
+#endif
+ return(bb == 0);
+}
+
+
+static ULONG
+_WhoControlsSquareFast(IN POSITION *pos,
+ IN COOR c)
+/**
+
+Routine description:
+
+ Determine which side controls a board square.
+
+Parameters:
+
+ POSITION *pos : the board
+ COOR c : the square in question
+
+Return value:
+
+ static ULONG : (ULONG)-1 if neither side controls it or the sides
+ control it evenly, WHITE if white controls the square, or BLACK if
+ black controls the square.
+
+ TODO: fix this to use counts / xrays
+
+**/
+{
+ ULONG uWhite = pos->rgSquare[c|8].bvAttacks[WHITE].uSmall |
+ pos->rgSquare[c|8].bvAttacks[WHITE].uXray;
+ ULONG uBlack = pos->rgSquare[c|8].bvAttacks[BLACK].uSmall |
+ pos->rgSquare[c|8].bvAttacks[BLACK].uXray;
+ ULONG u;
+ PIECE p;
+ CHAR ch;
+
+ ASSERT((c + 8) == (c | 8));
+ ASSERT((uWhite & 0xFFFFFF00) == 0);
+ ASSERT((uBlack & 0xFFFFFF00) == 0);
+
+ // TODO: keep these and update the table to use them
+ uWhite >>= 3;
+ uBlack >>= 3;
+
+ p = pos->rgSquare[c].pPiece;
+ // p -= 2;
+ ch = g_SwapTable[p][uWhite][uBlack];
+ if (ch != 0)
+ {
+ u = ch;
+ u >>= 7;
+ u = u & 1;
+ ASSERT(((u == 1) && (ch < 0)) ||
+ ((u == 0) && (ch > 0)));
+ return(FLIP(u));
+ }
+ return((ULONG)-1);
+}
+
+/**
+
+Routine description:
+
+ Zero out the attack table before building it.
+
+Parameters:
+
+ POSITION *pos
+
+Return value:
+
+ void
+
+**/
+#define CLEAR_A_SQ \
+ pos->rgSquare[c].bvAttacks[0].uWholeThing = 0; \
+ pos->rgSquare[c].bvAttacks[1].uWholeThing = 0;
+
+#define CLEAR_A_RANK \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c += 9;
+
+#define CLEAR_SHORT_RANK \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c++; \
+ CLEAR_A_SQ; c += 10;
+
+static void
+_ClearAttackTables(IN OUT POSITION *pos)
+{
+ register COOR c = 8;
+#if 1
+ CLEAR_A_SQ; c += 16;
+ CLEAR_A_SQ; c += 16;
+ CLEAR_A_SQ; c += 16;
+ CLEAR_A_SQ; c += 16;
+ CLEAR_A_SQ; c += 16;
+ CLEAR_A_SQ; c += 16;
+ CLEAR_A_SQ; c += 16;
+ CLEAR_A_SQ; c = 9;
+ CLEAR_SHORT_RANK;
+ CLEAR_SHORT_RANK;
+ CLEAR_SHORT_RANK;
+ CLEAR_SHORT_RANK;
+ CLEAR_SHORT_RANK;
+ CLEAR_SHORT_RANK;
+ CLEAR_SHORT_RANK;
+ CLEAR_SHORT_RANK;
+#else
+ CLEAR_A_RANK;
+ CLEAR_A_RANK;
+ CLEAR_A_RANK;
+ CLEAR_A_RANK;
+ CLEAR_A_RANK;
+ CLEAR_A_RANK;
+ CLEAR_A_RANK;
+ CLEAR_A_RANK;
+#endif
+}
+
+
+static INLINE void
+_InitializePawnHashEntry(IN OUT PAWN_HASH_ENTRY *pHash,
+ IN POSITION *pos)
+/**
+
+Routine description:
+
+Parameters:
+
+ PAWN_HASH_ENTRY *pHash,
+ POSITION *pos
+
+Return value:
+
+ static INLINE void
+
+**/
+{
+ memset(pHash, 0, sizeof(PAWN_HASH_ENTRY));
+ pHash->u64Key = pos->u64PawnSig;
+}
+
+
+static INLINE void
+_EvaluateCandidatePasser(IN POSITION *pos,
+ IN OUT PAWN_HASH_ENTRY *pHash,
+ IN COOR c)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ PAWN_HASH_ENTRY *pHash,
+ COOR c
+
+Return value:
+
+ static INLINE
+
+**/
+{
+ COOR c1, cSquare;
+ PIECE pSentry, pHelper;
+ ULONG uPawnFile = FILE(c) + 1;
+ BITBOARD bb;
+ ULONG uSentries, uHelpers;
+ ULONG uColor;
+ int d1;
+#ifdef DEBUG
+ COOR cVerifySquare;
+ ULONG uVerifySentries;
+#endif
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(RANK(c) >= 2);
+ ASSERT(RANK(c) <= 7);
+ pHelper = pos->rgSquare[c].pPiece;
+ ASSERT(IS_PAWN(pHelper));
+ uColor = GET_COLOR(pHelper);
+ ASSERT(IS_VALID_COLOR(uColor));
+
+ if (pHash->uCountPerFile[FLIP(uColor)][uPawnFile] != 0)
+ {
+ ASSERT((pHash->bbPawnLocations[FLIP(uColor)] & BBFILE[FILE(c)]) != 0);
+
+ //
+ // The only way a pawn can be a passer/candidate if the other
+ // side has a pawn on its same file is if that enemy pawn is
+ // behind it. This is a corner case but it is important to
+ // detect all passers.
+ //
+ switch(uColor)
+ {
+ case WHITE:
+ bb = pHash->bbPawnLocations[BLACK] & BBFILE[FILE(c)];
+ ASSERT(bb);
+ while(IS_ON_BOARD(c1 = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ ASSERT(FILE(c1) == FILE(c));
+ if (c1 < c) return;
+ }
+ break;
+ case BLACK:
+ bb = pHash->bbPawnLocations[WHITE] & BBFILE[FILE(c)];
+ ASSERT(bb);
+ while(IS_ON_BOARD(c1 = CoorFromBitBoardRank1ToRank8(&bb)))
+ {
+ ASSERT(FILE(c1) == FILE(c));
+ if (c1 > c) return;
+ }
+ break;
+ }
+ }
+
+ //
+ // Count FLIP(uColor)'s sentries and determine the location of the
+ // critical square. Note if there are no sentries then this
+ // pawn (on square c) is a passer already, not a candidate.
+ //
+ bb = pHash->bbPawnLocations[FLIP(uColor)] & BBADJACENT_FILES[FILE(c)];
+ bb &= BBPRECEEDING_RANKS[(c & 0x70) >> 4][uColor];
+ if (!bb)
+ {
+ ASSERT(CountBits(bb) == 0);
+
+ //
+ // There are no sentries so this pawn is a passer.
+ //
+ pHash->bbPasserLocations[uColor] |= COOR_TO_BB(c);
+ ASSERT(CountBits(pHash->bbPasserLocations[uColor]) > 0);
+ ASSERT(CountBits(pHash->bbPasserLocations[uColor]) <= 8);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ PASSER_BY_RANK[uColor][RANK(c)],
+ "passed pawn");
+
+ //
+ // However, don't give doubled passers such a big bonus.
+ //
+ if (IS_PAWN(pos->rgSquare[c - 16 * g_iAhead[uColor]].pPiece))
+ {
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ -(PASSER_BY_RANK[uColor][RANK(c)] / 2),
+ "doubled passer");
+ }
+ return;
+ }
+ uSentries = CountBits(bb);
+
+ //
+ // There's one or more sentry pawns so we'll look for helpers to
+ // decide if this pawn is a candidate passer.
+ //
+ if (uColor == WHITE)
+ {
+ pSentry = BLACK_PAWN;
+ cSquare = CoorFromBitBoardRank1ToRank8(&bb);
+ cSquare += 0x10;
+ }
+ else
+ {
+ pSentry = WHITE_PAWN;
+ cSquare = CoorFromBitBoardRank8ToRank1(&bb);
+ cSquare -= 0x10;
+ }
+ cSquare &= 0xF0;
+ cSquare |= FILE(c);
+ ASSERT(IS_ON_BOARD(cSquare));
+ ASSERT(FILE(cSquare) == FILE(c));
+
+#ifdef DEBUG
+ uVerifySentries = 0;
+ cVerifySquare = 0;
+ d1 = 16 * g_iAhead[uColor];
+ c1 = c + d1;
+ do
+ {
+ if (IS_ON_BOARD(c1 - 1))
+ {
+ if (pos->rgSquare[c1 - 1].pPiece == pSentry)
+ {
+ uVerifySentries++;
+ if (0 == cVerifySquare)
+ {
+ cVerifySquare = c1 - d1;
+ ASSERT(cVerifySquare == cSquare);
+ }
+ }
+ }
+ if (IS_ON_BOARD(c1 + 1))
+ {
+ if (pos->rgSquare[c1 + 1].pPiece == pSentry)
+ {
+ uVerifySentries++;
+ if (0 == cVerifySquare)
+ {
+ cVerifySquare = c1 - d1;
+ ASSERT(cVerifySquare == cSquare);
+ }
+ }
+ }
+ c1 = c1 + d1;
+ }
+ while(!RANK8(c1) && !RANK1(c1));
+ ASSERT(uVerifySentries == uSentries);
+#endif
+
+ //
+ // Note: we don't do this with bitboards because we want to not
+ // consider a pawn to be a potential passer if it can't safely
+ // advance to get into helper position.
+ //
+ // IDEA: scale the candidate passer bonus based on rank AND on
+ // the distance the helper(s) have to go to get into position.
+ //
+ uHelpers = 0;
+ d1 = 16 * g_iAhead[uColor];
+ ASSERT(-d1 == 16 * g_iBehind[uColor]);
+ c1 = cSquare + 1 - d1;
+ if ((IS_ON_BOARD(c1)) && (pHash->uCountPerFile[uColor][FILE(c1) + 1]))
+ {
+ ASSERT(pHash->bbPawnLocations[uColor] & BBFILE[FILE(c1)]);
+ if (!(pos->rgSquare[c1 + 8].bvAttacks[FLIP(uColor)].uWholeThing) ||
+ (pos->rgSquare[c1 + 8].bvAttacks[uColor].uWholeThing))
+ {
+ //
+ // The square c1 the place a helper pawn must get to in
+ // order to aide the candidate past a sentry.
+ //
+ if (pos->rgSquare[c1].pPiece == pHelper)
+ {
+ uHelpers = 1;
+ goto do_left;
+ }
+
+ //
+ // There is no helper pawn in the support position yet.
+ // See if one can get there.
+ //
+ c1 = c1 - d1;
+ while (IS_ON_BOARD(c1) &&
+ ((!(pos->rgSquare[c1+8].bvAttacks[FLIP(uColor)].uWholeThing)) ||
+ (pos->rgSquare[c1+8].bvAttacks[uColor].uWholeThing)))
+ {
+ if (pos->rgSquare[c1].pPiece == pHelper)
+ {
+ uHelpers = 1;
+ break;
+ }
+ else if (pos->rgSquare[c1].pPiece == pSentry)
+ {
+ break;
+ }
+ c1 = c1 - d1;
+ }
+ }
+ }
+
+ do_left:
+ c1 = cSquare - 1 - d1;
+ if ((IS_ON_BOARD(c1)) && (pHash->uCountPerFile[uColor][FILE(c1) + 1]))
+ {
+ ASSERT(pHash->bbPawnLocations[uColor] & BBFILE[FILE(c1)]);
+
+ if (!(pos->rgSquare[c1 + 8].bvAttacks[FLIP(uColor)].uWholeThing) ||
+ (pos->rgSquare[c1 + 8].bvAttacks[uColor].uWholeThing))
+ {
+ //
+ // The square c1 is the place a helper pawn must get to in
+ // order to aide the candidate.
+ //
+ if (pos->rgSquare[c1].pPiece == pHelper)
+ {
+ uHelpers++;
+ goto done_helpers;
+ }
+
+ //
+ // There is no pawn in the left support position yet. See
+ // if one can get there.
+ //
+ c1 -= d1;
+ while (IS_ON_BOARD(c1) &&
+ ((!(pos->rgSquare[c1+8].bvAttacks[FLIP(uColor)].uWholeThing)) ||
+ (pos->rgSquare[c1 + 8].bvAttacks[uColor].uWholeThing)))
+ {
+ if (pos->rgSquare[c1].pPiece == pHelper)
+ {
+ uHelpers++;
+ break;
+ }
+ else if (pos->rgSquare[c1].pPiece == pSentry)
+ {
+ break;
+ }
+ c1 -= d1;
+ }
+ }
+ }
+
+ done_helpers:
+ if ((uHelpers >= uSentries) ||
+ ((pHash->uCountPerFile[uColor][uPawnFile + 1] +
+ pHash->uCountPerFile[uColor][uPawnFile - 1]) &&
+ (((WHITE == uColor) && (RANK(c) > 5)) ||
+ ((BLACK == uColor) && (RANK(c) < 4)))))
+ {
+ ASSERT(CANDIDATE_PASSER_BY_RANK[uColor][RANK(c)] > 0);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ CANDIDATE_PASSER_BY_RANK[uColor][RANK(c)],
+ "candidate passer");
+
+ //
+ // If the other side has no pieces then give this candidate an
+ // extra bonus.
+ //
+ if (pos->uNonPawnCount[FLIP(uColor)][0] == 1)
+ {
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ CANDIDATE_PASSER_BY_RANK[uColor][RANK(c)],
+ "candidate passer in endgame");
+ }
+ }
+}
+
+static void
+_EvaluateConnectedSupportedOutsidePassers(IN POSITION *pos,
+ IN OUT PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ PAWN_HASH_ENTRY *pHash
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG uColor;
+ ULONG u, v;
+ COOR c;
+ COOR cLeftmostPasser, cRightmostPasser;
+ PIECE pFriend;
+ COOR cSupport;
+ BITBOARD bb;
+ static const INT iDelta[5] =
+ {
+ -1, +1, +17, +15, 0
+ };
+
+ FOREACH_COLOR(uColor)
+ {
+ ASSERT(IS_VALID_COLOR(uColor));
+
+ if (pHash->bbPasserLocations[uColor])
+ {
+ ASSERT(pos->uPawnCount[uColor] > 0);
+ pFriend = BLACK_PAWN | uColor;
+ cLeftmostPasser = cRightmostPasser = ILLEGAL_COOR;
+ for (u = A; u <= H; u++)
+ {
+ bb = pHash->bbPasserLocations[uColor] & BBFILE[u];
+ if (bb != 0)
+ {
+ ASSERT(pHash->uCountPerFile[uColor][u+1] != 0);
+ while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ //
+ // Keep track of leftmost/rightmost passer for
+ // outside passer code later on.
+ //
+ ASSERT(FILE(c) == u);
+ ASSERT(RANK(c) > 1);
+ ASSERT(RANK(c) < 8);
+ if (cLeftmostPasser == ILLEGAL_COOR)
+ {
+ cLeftmostPasser = c;
+ }
+ cRightmostPasser = c;
+
+ v = 0;
+ while(iDelta[v] != 0)
+ {
+ cSupport = c + iDelta[v] * g_iBehind[uColor];
+ if (IS_ON_BOARD(cSupport))
+ {
+ if (pHash->bbPasserLocations[uColor] &
+ COOR_TO_BB(cSupport))
+ {
+ ASSERT(CONNECTED_PASSERS_BY_RANK[uColor]
+ [RANK(c)] > 0);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ CONNECTED_PASSERS_BY_RANK[uColor]
+ [RANK(c)],
+ "connected passers");
+
+ //
+ // TODO: connected passers vs a R is
+ // strong.
+ //
+ }
+ else if (pos->rgSquare[cSupport].pPiece ==
+ pFriend)
+ {
+ ASSERT(SUPPORTED_PASSER_BY_RANK[uColor]
+ [RANK(c)] > 0);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ SUPPORTED_PASSER_BY_RANK[uColor]
+ [RANK(c)],
+ "supported passer");
+ }
+ }
+ v++;
+ }
+ }
+ }
+ }
+
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(cLeftmostPasser));
+ ASSERT(IS_ON_BOARD(cRightmostPasser));
+ ASSERT(RANK(cLeftmostPasser) > 1);
+ ASSERT(RANK(cRightmostPasser) > 1);
+ ASSERT(RANK(cLeftmostPasser) < 8);
+ ASSERT(RANK(cRightmostPasser) < 8);
+ if (CountBits(pHash->bbPasserLocations[uColor]) == 1)
+ {
+ ASSERT(cLeftmostPasser == cRightmostPasser);
+ }
+#endif
+ for (u = A; u <= D; u++)
+ {
+ if (pHash->uCountPerFile[FLIP(uColor)][u + 1] != 0)
+ {
+ ASSERT(pHash->bbPawnLocations[FLIP(uColor)] & BBFILE[u]);
+ if (!(pHash->bbPasserLocations[FLIP(uColor)] & BBFILE[u]))
+ {
+ break;
+ }
+ }
+ }
+ if (u > FILE(cLeftmostPasser))
+ {
+ EVAL_TERM(uColor,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[uColor],
+ OUTSIDE_PASSER_BY_DISTANCE[u-FILE(cLeftmostPasser)],
+ "left outside passer");
+ }
+
+ for (u = H; u >= E; u--)
+ {
+ if (pHash->uCountPerFile[FLIP(uColor)][u + 1] != 0)
+ {
+ ASSERT(pHash->bbPawnLocations[FLIP(uColor)] & BBFILE[u]);
+ if (!(pHash->bbPasserLocations[FLIP(uColor)] & BBFILE[u]))
+ {
+ break;
+ }
+ }
+ }
+ if (u < FILE(cRightmostPasser))
+ {
+ EVAL_TERM(uColor,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[uColor],
+ OUTSIDE_PASSER_BY_DISTANCE[FILE(cRightmostPasser)-u],
+ "right outside passer");
+ }
+ }
+ }
+}
+
+
+#define PAWN_ATTACK_BLACK_DELTA (+15)
+#define PAWN_ATTACK_WHITE_DELTA (-17)
+
+static void
+_PopulatePawnAttackBits(IN OUT POSITION *pos)
+/**
+
+Routine description:
+
+ Populate the attack table with pawn bits.
+
+Parameters:
+
+ POSITION *pos : the board
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG u;
+ COOR c;
+ COOR cAttack;
+
+ //
+ // IDEA: combine clearing and initial population somehow?
+ //
+ // r - - - - - - l : only check L- and R-
+ // r - - - - - - l
+ // R A A A A A A L
+ // R A A A A A A L
+ // R A A A A A A L
+ // R A A A A A A L
+ // r - - - - - - l
+ // r - - - - - - l : only check L+ and R+
+ _ClearAttackTables(pos);
+
+ ASSERT(pos->uPawnCount[BLACK] <= 8);
+ for (u = 0;
+ u < pos->uPawnCount[BLACK];
+ u++)
+ {
+ c = pos->cPawns[BLACK][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(pos->rgSquare[c].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[c].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == BLACK);
+
+ //
+ // IDEA: These IS_ON_BOARD checks can be removed by a lookup table.
+ //
+ cAttack = c + PAWN_ATTACK_BLACK_DELTA;
+ if (IS_ON_BOARD(cAttack))
+ {
+ ASSERT((cAttack + 8) == (cAttack|8));
+ ASSERT(!IS_ON_BOARD(cAttack|8));
+ pos->rgSquare[cAttack|8].bvAttacks[BLACK].small.uPawn = 1;
+ }
+ cAttack += 2;
+ if (IS_ON_BOARD(cAttack))
+ {
+ ASSERT((cAttack + 8) == (cAttack|8));
+ cAttack |= 8;
+ ASSERT(!IS_ON_BOARD(cAttack));
+ pos->rgSquare[cAttack].bvAttacks[BLACK].small.uPawn = 1;
+ }
+ }
+
+ ASSERT(pos->uPawnCount[WHITE] <= 8);
+ for (u = 0;
+ u < pos->uPawnCount[WHITE];
+ u++)
+ {
+ c = pos->cPawns[WHITE][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(pos->rgSquare[c].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[c].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == WHITE);
+
+ cAttack = c + PAWN_ATTACK_WHITE_DELTA;
+ if (IS_ON_BOARD(cAttack))
+ {
+ ASSERT((cAttack + 8) == (cAttack|8));
+ ASSERT(!IS_ON_BOARD(cAttack|8));
+ pos->rgSquare[cAttack|8].bvAttacks[WHITE].small.uPawn = 1;
+ }
+ cAttack += 2;
+ if (IS_ON_BOARD(cAttack))
+ {
+ ASSERT((cAttack + 8) == (cAttack|8));
+ cAttack |= 8;
+ ASSERT(!IS_ON_BOARD(cAttack));
+ pos->rgSquare[cAttack].bvAttacks[WHITE].small.uPawn = 1;
+ }
+ }
+}
+
+
+static PAWN_HASH_ENTRY *
+_EvalPawns(IN OUT SEARCHER_THREAD_CONTEXT *ctx,
+ OUT FLAG *pfDeferred)
+/**
+
+Routine description:
+
+ Evaluate pawn structures; return a ptr to a pawn hash entry.
+
+Parameters:
+
+ SEARCHER_THREAD_CONTEXT *ctx : the searcher thread context
+
+Return value:
+
+ PAWN_HASH_ENTRY *
+
+**/
+{
+ static ULONG uUnmovedRank[2] = { 0x10, 0x60 };
+ POSITION *pos = &ctx->sPosition;
+ PAWN_HASH_ENTRY *pHash;
+ COOR c, cSquare;
+ PIECE p;
+ ULONG uIsolated[2];
+ ULONG uDoubled[2];
+ SCORE iDuos[2];
+ ULONG u, uPawnFile;
+ ULONG uColor;
+ ULONG uUnsupportable;
+ BITBOARD bb;
+ int d1;
+#ifdef DEBUG
+ SCORE t;
+#endif
+
+ //
+ // Now, look up the hash entry for this position.
+ //
+ INC(ctx->sCounters.pawnhash.u64Probes);
+ pHash = PawnHashLookup(ctx);
+ ASSERT(NULL != pHash);
+ if (pHash->u64Key == pos->u64PawnSig)
+ {
+ *pfDeferred = TRUE;
+ INC(ctx->sCounters.pawnhash.u64Hits);
+ return(pHash);
+ }
+
+ //
+ // We need the attack table to evaluate the pawns... and we missed
+ // the hash table so we have to populate it now. Can't defer this
+ // one. :(
+ //
+ *pfDeferred = FALSE;
+ _InitializePawnHashEntry(pHash, pos);
+ _PopulatePawnAttackBits(pos);
+ uIsolated[BLACK] = uIsolated[WHITE] = 0;
+ uDoubled[BLACK] = uDoubled[WHITE] = 0;
+ iDuos[BLACK] = iDuos[WHITE] = 0;
+
+ //
+ // First pass
+ //
+ FOREACH_COLOR(uColor)
+ {
+ ASSERT(IS_VALID_COLOR(uColor));
+ ASSERT(pos->uPawnCount[uColor] <= 8);
+
+ d1 = 16 * g_iAhead[uColor];
+ for (u = 0;
+ u < pos->uPawnCount[uColor];
+ u++)
+ {
+ c = pos->cPawns[uColor][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(pos->rgSquare[c].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[c].pPiece));
+ uPawnFile = FILE(c) + 1;
+ ASSERT((1 <= uPawnFile) && (uPawnFile <= 8));
+
+ //
+ // Update files counter
+ //
+ ASSERT(pHash->uCountPerFile[uColor][uPawnFile] < 6);
+ pHash->uCountPerFile[uColor][uPawnFile]++;
+
+ //
+ // Update bitboard bit
+ //
+ ASSERT(CountBits(pHash->bbPawnLocations[uColor] &
+ BBFILE[uPawnFile-1]) < 6);
+ ASSERT((pHash->bbPawnLocations[uColor] & COOR_TO_BB(c)) == 0);
+ pHash->bbPawnLocations[uColor] |= COOR_TO_BB(c);
+ ASSERT(CountBits(pHash->bbPawnLocations[uColor] &
+ BBFILE[uPawnFile-1]) ==
+ pHash->uCountPerFile[uColor][uPawnFile]);
+
+ //
+ // Count unmoved pawns
+ //
+ pHash->uNumUnmovedPawns[uColor] +=
+ ((c & 0xF0) == uUnmovedRank[uColor]);
+ ASSERT(pHash->uNumUnmovedPawns[uColor] <= 8);
+
+ //
+ // Detect rammed and other stationary pawns.
+ //
+ cSquare = c + d1;
+ ASSERT(IS_ON_BOARD(cSquare));
+ p = pos->rgSquare[cSquare].pPiece;
+ if (IS_PAWN(p))
+ {
+ pHash->bbStationaryPawns[uColor] |= COOR_TO_BB(c);
+ ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8);
+ pHash->uNumRammedPawns += OPPOSITE_COLORS(p, uColor);
+ ASSERT(pHash->uNumRammedPawns <= 16);
+ }
+
+ //
+ // Give central pawns a small bonus; penalize rook pawns:
+ //
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ PAWN_CENTRALITY_BONUS[c],
+ "centrality");
+ }
+ }
+
+ //
+ // We counted rammed pawns twice (once for the black pawn and once
+ // for the white one). Fix this now.
+ //
+ ASSERT(!(pHash->uNumRammedPawns & 1));
+ pHash->uNumRammedPawns /= 2;
+
+ //
+ // Second pass
+ //
+ FOREACH_COLOR(uColor)
+ {
+ ASSERT(IS_VALID_COLOR(uColor));
+ ASSERT(pos->uPawnCount[uColor] <= 8);
+ ASSERT(CountBits(pHash->bbPawnLocations[uColor]) <= 8);
+
+ d1 = 16 * g_iAhead[uColor];
+ for (u = 0;
+ u < pos->uPawnCount[uColor];
+ u++)
+ {
+ c = pos->cPawns[uColor][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_PAWN(pos->rgSquare[c].pPiece));
+ ASSERT((1 < RANK(c)) && (RANK(c) < 8));
+
+ //
+ // Find weak pawns... pawns that are isolated are weak as
+ // are pawns that have advanced beyond their support.
+ // Both of these can tie up one or more pieces in order to
+ // defend them.
+ //
+ uUnsupportable = 0;
+ uPawnFile = FILE(c) - 1;
+ if (pHash->uCountPerFile[uColor][uPawnFile + 1] > 0)
+ {
+ bb = (pHash->bbPawnLocations[uColor] &
+ BBFILE[uPawnFile] &
+ BBADJACENT_RANKS[RANK(c)]);
+ if (!bb)
+ {
+ bb = (pHash->bbPawnLocations[FLIP(uColor)] &
+ BBFILE[uPawnFile]);
+ while(IS_ON_BOARD(cSquare =
+ CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ if (uColor == WHITE)
+ {
+ if ((cSquare & 0xF0) >= (c & 0xF0))
+ {
+ ASSERT(RANK(cSquare) <= RANK(c));
+ uUnsupportable = 1;
+ break;
+ }
+ }
+ else
+ {
+ ASSERT(uColor == BLACK);
+ if ((cSquare & 0xF0) <= (c & 0xF0))
+ {
+ ASSERT(RANK(cSquare) >= RANK(c));
+ uUnsupportable = 1;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ uUnsupportable = 1; // no friend pawn on that file
+ }
+ if (0 == uUnsupportable)
+ {
+ goto fast_skip;
+ }
+ ASSERT(1 == uUnsupportable);
+
+ uPawnFile = FILE(c) + 1;
+ if (pHash->uCountPerFile[uColor][uPawnFile + 1] > 0)
+ {
+ bb = (pHash->bbPawnLocations[uColor] &
+ BBFILE[uPawnFile] &
+ BBADJACENT_RANKS[RANK(c)]);
+ if (!bb)
+ {
+ bb = (pHash->bbPawnLocations[FLIP(uColor)] &
+ BBFILE[uPawnFile]);
+ while(IS_ON_BOARD(cSquare =
+ CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ if (uColor == WHITE)
+ {
+ if ((cSquare & 0xF0) >= (c & 0xF0))
+ {
+ ASSERT(RANK(cSquare) <= RANK(c));
+ uUnsupportable = 2;
+ break;
+ }
+ }
+ else
+ {
+ ASSERT(uColor == BLACK);
+ if ((cSquare & 0xF0) <= (c & 0xF0))
+ {
+ ASSERT(RANK(cSquare) >= RANK(c));
+ uUnsupportable = 2;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ uUnsupportable = 2;
+ }
+
+ //
+ // If this pawn is not supportable from either side then
+ // it is either isolated or has been pushed too far ahead
+ // of its support. Either way it's a target -- penalize
+ // it.
+ //
+ if (2 == uUnsupportable)
+ {
+ uPawnFile = FILE(c) + 1;
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ ISOLATED_PAWN_BY_PAWNFILE[uPawnFile],
+ "target/isolated pawn");
+
+ //
+ // Isolated + exposed? Extra penalty.
+ //
+ ASSERT(ISOLATED_EXPOSED_PAWN < 0);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ ((pHash->uCountPerFile[FLIP(uColor)][uPawnFile]==0)*
+ ISOLATED_EXPOSED_PAWN),
+ "exposed target");
+
+ //
+ // Exponential penalty term
+ //
+ uIsolated[uColor] += 1;
+ ASSERT(uIsolated[uColor] > 0);
+ ASSERT(uIsolated[uColor] <= 8);
+
+ //
+ // Isolated + doubled? Extra penalty.
+ //
+ ASSERT(ISOLATED_DOUBLED_PAWN < 0);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ ((pHash->uCountPerFile[uColor][uPawnFile] > 1) *
+ ISOLATED_DOUBLED_PAWN),
+ "doubled + target");
+ }
+
+ fast_skip:
+ uPawnFile = FILE(c) + 1;
+ ASSERT((1 <= uPawnFile) && (uPawnFile <= 8));
+ ASSERT(CountBits(pHash->bbPawnLocations[uColor] & BBFILE[FILE(c)])
+ == pHash->uCountPerFile[uColor][uPawnFile]);
+
+ //
+ // Keep count of doubled pawns
+ //
+ uDoubled[uColor] += (pHash->uCountPerFile[uColor][uPawnFile] > 1);
+ ASSERT((0 <= uDoubled[uColor]) && (uDoubled[uColor] <= 8));
+
+ //
+ // We can detect pawn duos (triads, etc...) and backward pawns
+ // by considering the control of the pawn's stopsquare.
+ //
+ cSquare = c + d1;
+ ASSERT(IS_ON_BOARD(cSquare));
+ ASSERT((cSquare + 8) == (cSquare | 8));
+ if (pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing)
+ {
+ //
+ // Count pawn duos. See "Pawn Power in Chess" pp 10-16
+ //
+#ifdef DEBUG
+ t = ((uColor * RANK(cSquare)) +
+ (FLIP(uColor) * (9 - RANK(cSquare))));
+ if (uColor == WHITE)
+ {
+ ASSERT(t == RANK(cSquare));
+ }
+ else
+ {
+ ASSERT(uColor == BLACK);
+ ASSERT(t == (9 - RANK(cSquare)));
+ }
+#endif
+ iDuos[uColor] += ((uColor * RANK(cSquare)) +
+ (FLIP(uColor) * (9 - RANK(cSquare))));
+ ASSERT(iDuos[uColor] <= (7 * 8));
+ }
+ else if (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].uWholeThing)
+ {
+ //
+ // Detect backwards pawns. See "Pawn Power in Chess" pp 25-27
+ //
+ if (!IS_PAWN(pos->rgSquare[cSquare].pPiece))
+ {
+ p = (BLACK_PAWN | uColor);
+ if (((WHITE == uColor) && (RANK(c) < 4)) ||
+ ((BLACK == uColor) && (RANK(c) > 5)))
+ {
+ if ((IS_ON_BOARD(cSquare - 1) &&
+ pos->rgSquare[cSquare - 1].pPiece == p) ||
+ (IS_ON_BOARD(cSquare + 1) &&
+ pos->rgSquare[cSquare + 1].pPiece == p))
+ {
+ //
+ // The pawn at c is backward. Determine
+ // whether it is shielded or exposed.
+ //
+ pHash->bbStationaryPawns[uColor] |= COOR_TO_BB(c);
+ ASSERT(CountBits(pHash->bbStationaryPawns[uColor])
+ <= 8);
+ if (pHash->uCountPerFile[FLIP(uColor)][uPawnFile])
+ {
+ ASSERT(BACKWARD_SHIELDED_BY_LOCATION[c] < 0);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ BACKWARD_SHIELDED_BY_LOCATION[c],
+ "shielded backward pawn");
+ }
+ else
+ {
+ ASSERT(BACKWARD_EXPOSED_BY_LOCATION[c] < 0);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pHash->iScore[uColor],
+ BACKWARD_EXPOSED_BY_LOCATION[c],
+ "exposed backward pawn");
+ }
+ }
+ }
+ }
+ //
+ // TODO: Think about backward doubled pawns; read Kauffman's
+ // paper "All About Doubled Pawns".
+ //
+ }
+
+ //
+ // Handle passers / candidate passers
+ //
+ _EvaluateCandidatePasser(pos, pHash, c);
+ }
+ }
+
+ //
+ // Reward pawn duos, see "Pawn Power in Chess" pp. 10-16
+ //
+ ASSERT(iDuos[WHITE] >= 0);
+ ASSERT(iDuos[WHITE] <= 56);
+ ASSERT(iDuos[BLACK] >= 0);
+ ASSERT(iDuos[BLACK] <= 56);
+ EVAL_TERM(WHITE,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[WHITE],
+ iDuos[WHITE] / 2,
+ "pawn duos");
+ EVAL_TERM(BLACK,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[BLACK],
+ iDuos[BLACK] / 2,
+ "pawn duos");
+
+ //
+ // The penalty for doubled pawns is scaled based on two primary
+ // factors: the presence of major pieces for the side with doubled
+ // pawns and the number of doubled pawns on the board. The
+ // presence of major pieces makes the doubled pawns less severe
+ // while the presence of many doubled pawns makes the penalty for
+ // each more severe.
+ //
+ ASSERT((uDoubled[WHITE] >= 0) && (uDoubled[WHITE] <= 8));
+ ASSERT((uDoubled[BLACK] >= 0) && (uDoubled[BLACK] <= 8));
+ u = pos->uNonPawnCount[BLACK][ROOK] + pos->uNonPawnCount[BLACK][QUEEN] * 2;
+ u = MINU(u, 3);
+ EVAL_TERM(BLACK,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[BLACK],
+ DOUBLED_PAWN_PENALTY_BY_COUNT[u][uDoubled[BLACK]],
+ "exponential doubled");
+ u = pos->uNonPawnCount[WHITE][ROOK] + pos->uNonPawnCount[WHITE][QUEEN] * 2;
+ u = MINU(u, 3);
+ EVAL_TERM(WHITE,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[WHITE],
+ DOUBLED_PAWN_PENALTY_BY_COUNT[u][uDoubled[WHITE]],
+ "exponential doubled");
+
+ ASSERT(uIsolated[WHITE] >= 0);
+ ASSERT(uIsolated[WHITE] <= 8);
+ ASSERT(uIsolated[BLACK] >= 0);
+ ASSERT(uIsolated[BLACK] <= 8);
+ EVAL_TERM(BLACK,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[BLACK],
+ ISOLATED_PAWN_PENALTY_BY_COUNT[uIsolated[BLACK]],
+ "exponential isolated");
+ EVAL_TERM(WHITE,
+ PAWN,
+ ILLEGAL_COOR,
+ pHash->iScore[WHITE],
+ ISOLATED_PAWN_PENALTY_BY_COUNT[uIsolated[WHITE]],
+ "exponential isolated");
+
+ //
+ // Look for connected, supported and outside passed pawns to give
+ // extra bonuses.
+ //
+ _EvaluateConnectedSupportedOutsidePassers(pos, pHash);
+
+ //
+ // TODO: recognize quartgrips and stonewalls
+ //
+ return(pHash);
+}
+
+
+ULONG
+CountKingSafetyDefects(IN OUT POSITION *pos,
+ IN ULONG uSide)
+/**
+
+Routine description:
+
+ Determine how many defects uSide's king position has _quickly_.
+ TODO: add more knowledge as cheaply as possible...
+
+Parameters:
+
+ POSITION *pos,
+ ULONG uSide
+
+Return value:
+
+ ULONG
+
+**/
+{
+ ULONG uCounter = 0;
+ ULONG xSide = FLIP(uSide);
+ COOR cKing;
+ COOR c;
+ int i;
+ PIECE p;
+ ULONG u;
+
+ //
+ // Don't count king safety defects if the real eval code in
+ // _EvalKing would not...
+ //
+ if (pos->uNonPawnMaterial[xSide] < DO_KING_SAFETY_THRESHOLD) {
+ return 0;
+ }
+
+ cKing = pos->cNonPawns[uSide][0];
+ uCounter = KING_INITIAL_COUNTER_BY_LOCATION[uSide][cKing] >> 1;
+ ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
+ ASSERT(pos->rgSquare[cKing].uIndex == 0);
+ ASSERT(IS_ON_BOARD(cKing));
+ ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uSide);
+
+ //
+ // Make sure cKing - 1, cKing and cKing + 1 are on the board
+ //
+ cKing += (!IS_ON_BOARD(cKing - 1));
+ cKing -= (!IS_ON_BOARD(cKing + 1));
+ ASSERT(IS_ON_BOARD(cKing));
+ ASSERT(IS_ON_BOARD(cKing + 1));
+ ASSERT(IS_ON_BOARD(cKing - 1));
+
+ //
+ // Consider all enemy pieces except the king (not including pawns)
+ //
+ for (u = 1;
+ u < pos->uNonPawnCount[xSide][0];
+ u++)
+ {
+ c = pos->cNonPawns[xSide][u];
+ ASSERT(IS_ON_BOARD(c));
+ i = (int)c - (int)(cKing + 1);
+ ASSERT((i >= -128) && (i <= 125));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(pos->rgSquare[c].uIndex == u);
+ ASSERT(!IS_KING(p));
+ ASSERT(GET_COLOR(p) == xSide);
+ p = 1 << PIECE_TYPE(p);
+
+ uCounter += ((i == 0) | (i == -2) |
+ ((CHECK_VECTOR_WITH_INDEX(i, xSide) & p) != 0) |
+ ((CHECK_VECTOR_WITH_INDEX(i + 1, xSide) & p) != 0) |
+ ((CHECK_VECTOR_WITH_INDEX(i + 2, xSide) & p) != 0));
+ }
+ ASSERT(uCounter < 15);
+
+ // Save the number of enemy pieces pointing at this king for later use.
+ pos->uPiecesPointingAtKing[uSide] =
+ (uCounter - (KING_INITIAL_COUNTER_BY_LOCATION[uSide][cKing] >> 1));
+ return uCounter;
+}
+
+
+static void
+_QuicklyEstimateKingSafetyTerm(IN POSITION *pos,
+ IN OUT SCORE *piAlphaMargin,
+ IN OUT SCORE *piBetaMargin)
+/**
+
+Routine description:
+
+ Before doing an early lazy eval, look at the position and try to
+ quickly see if there is a large king safety positional component
+ to the score.
+
+Parameters:
+
+ POSITION *pos
+
+Return value:
+
+ void
+
+**/
+{
+ //
+ // IDEA: make this stuff dynamic like ctx->uPositional
+ //
+ static const SCORE iScoreByDefectCount[15] = {
+ 33, 50, 88, 146, 215, 245, 280, 430, 550, 630, 646, 646, 646, 646, 646
+ };
+ SCORE iPenaltyEst[2];
+ SCORE iRet;
+
+ ASSERT(CountKingSafetyDefects(pos, pos->uToMove) < 15);
+ iPenaltyEst[WHITE] =
+ iScoreByDefectCount[CountKingSafetyDefects(pos, WHITE)];
+ ASSERT(iPenaltyEst[WHITE] > 0);
+
+ ASSERT(CountKingSafetyDefects(pos, BLACK) < 15);
+ iPenaltyEst[BLACK] =
+ iScoreByDefectCount[CountKingSafetyDefects(pos, BLACK)];
+ ASSERT(iPenaltyEst[BLACK] > 0);
+
+ //
+ // For the beta margin, assume our king safety penalty will stand
+ // and our opponents will be less severe than we guess... net bonus
+ // to them / our bonus not as high.
+ //
+ iRet = iPenaltyEst[pos->uToMove] - iPenaltyEst[FLIP(pos->uToMove)] / 2;
+ iRet = MAX0(iRet);
+ ASSERT(iRet >= 0);
+ *piBetaMargin += iRet;
+
+ //
+ // For the alpha margin, assume our king safety penalty will not
+ // be too bad and our opponents will be as bad as we guess... net
+ // bonus to us / their offsetting bonus not as high.
+ //
+ iRet = iPenaltyEst[FLIP(pos->uToMove)] - iPenaltyEst[pos->uToMove] / 2;
+ iRet = MAX0(iRet);
+ ASSERT(iRet >= 0);
+ *piAlphaMargin += iRet;
+}
+
+
+static void
+_QuicklyEstimatePasserBonuses(POSITION *pos,
+ PAWN_HASH_ENTRY *pHash,
+ SCORE *piAlphaMargin,
+ SCORE *piBetaMargin)
+{
+ SCORE iBonusEst[2] = {0, 0};
+ SCORE iRet;
+ ULONG u;
+
+ //
+ // Guess about the passer bonuses
+ //
+ if (pHash->bbPasserLocations[WHITE])
+ {
+ u = pos->uNonPawnMaterial[BLACK] - VALUE_KING;
+ u /= VALUE_PAWN;
+ u = MINU(31, u);
+ ASSERT(PASSER_BONUS_AS_MATERIAL_COMES_OFF[u] >= 0);
+ iBonusEst[WHITE] =
+ (CountBits(pHash->bbPasserLocations[WHITE]) *
+ PASSER_BONUS_AS_MATERIAL_COMES_OFF[u]);
+ }
+ if (pHash->bbPasserLocations[BLACK])
+ {
+ u = pos->uNonPawnMaterial[WHITE] - VALUE_KING;
+ u /= VALUE_PAWN;
+ u = MINU(31, u);
+ ASSERT(PASSER_BONUS_AS_MATERIAL_COMES_OFF[u] >= 0);
+ iBonusEst[BLACK] =
+ (CountBits(pHash->bbPasserLocations[BLACK]) *
+ PASSER_BONUS_AS_MATERIAL_COMES_OFF[u]);
+ }
+
+ //
+ // For the beta margin, assume our passer bonus gets reduced and
+ // our opponent's is enhanced... net bonus to them / our bonus
+ // not as high.
+ //
+ iRet = iBonusEst[pos->uToMove] / 2 - iBonusEst[FLIP(pos->uToMove)];
+ iRet = MAX0(iRet);
+ *piBetaMargin += iRet;
+
+ //
+ // For the alpha bonus, assume our passer bonus gets doubled and
+ // our opponent's is reduced. Net bonus to us / their bonus not
+ // as high.
+ //
+ iRet = iBonusEst[pos->uToMove] - iBonusEst[FLIP(pos->uToMove)] / 2;
+ iRet = MAX0(iRet);
+ *piAlphaMargin += iRet;
+}
+
+
+static void
+_InvalidEvaluator(UNUSED POSITION *pos,
+ UNUSED COOR c,
+ UNUSED PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+ This code should never be called
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ void
+
+**/
+{
+ UtilPanic(SHOULD_NOT_GET_HERE,
+ NULL, NULL, NULL, NULL,
+ __FILE__, __LINE__);
+}
+
+//
+// ======================================================================
+//
+
+static FLAG FASTCALL
+_InvalidMobilityHelper(UNUSED POSITION *pos,
+ UNUSED COOR c,
+ UNUSED ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+ This code should never be called
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+ UtilPanic(SHOULD_NOT_GET_HERE,
+ NULL, NULL, NULL, NULL,
+ __FILE__, __LINE__);
+ return(TRUE);
+}
+
+// ----------------------------------------------------------------------
+
+static FLAG FASTCALL
+_BMinorToEnemyLess(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+ Consider a black minor (knight | bishop) taking an enemy pawn
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL : always TRUE, stop scanning in this direction
+
+**/
+{
+#ifdef DEBUG
+ PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(PIECE_VALUE(p) < VALUE_BISHOP);
+ ASSERT(!IS_EMPTY(pMinor));
+ ASSERT(IS_PAWN(p));
+ ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor));
+ ASSERT(OPPOSITE_COLORS(p, pMinor));
+ ASSERT(GET_COLOR(pMinor) == BLACK);
+#endif
+ *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[WHITE]));
+ ASSERT(*puMobility <= 8);
+ return(TRUE); // stop scanning this dir
+}
+
+static FLAG FASTCALL
+_WMinorToEnemyLess(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(IS_PAWN(p));
+ ASSERT(PIECE_VALUE(p) < VALUE_BISHOP);
+ ASSERT(!IS_EMPTY(pMinor));
+ ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor));
+ ASSERT(OPPOSITE_COLORS(p, pMinor));
+ ASSERT(GET_COLOR(pMinor) == WHITE);
+#endif
+ *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[BLACK]));
+ ASSERT(*puMobility <= 8);
+ return(TRUE); // stop scanning this dir
+}
+
+static FLAG FASTCALL
+_WRookToEnemyLess(POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(OPPOSITE_COLORS(p, pRook));
+ ASSERT(PIECE_VALUE(p) < VALUE_ROOK);
+ ASSERT(WHITE == GET_COLOR(pRook));
+#endif
+ *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[BLACK]));
+ ASSERT(*puMobility <= 7);
+ return(TRUE); // stop scanning this dir
+}
+
+static FLAG FASTCALL
+_BRookToEnemyLess(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(OPPOSITE_COLORS(p, pRook));
+ ASSERT(PIECE_VALUE(p) < VALUE_ROOK);
+ ASSERT(BLACK == GET_COLOR(pRook));
+#endif
+ *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[WHITE]));
+ ASSERT(*puMobility <= 7);
+ return(TRUE); // stop scanning this dir
+}
+
+
+static FLAG FASTCALL
+_BQueenToEnemyLess(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(PIECE_VALUE(p) < VALUE_QUEEN);
+ ASSERT(GET_COLOR(pQueen) == BLACK);
+ ASSERT(OPPOSITE_COLORS(pQueen, p));
+#endif
+ *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[WHITE]));
+ ASSERT(*puMobility <= 27);
+ return(TRUE); // stop scanning this dir
+}
+
+
+static FLAG FASTCALL
+_WQueenToEnemyLess(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(PIECE_VALUE(p) < VALUE_QUEEN);
+ ASSERT(GET_COLOR(pQueen) == WHITE);
+ ASSERT(OPPOSITE_COLORS(pQueen, p));
+#endif
+ *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[BLACK]));
+ ASSERT(*puMobility <= 27);
+ return(TRUE); // stop scanning this dir
+}
+
+// ----------------------------------------------------------------------
+
+static FLAG FASTCALL
+_EMinorToEnemySame(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(!IS_EMPTY(pMinor));
+ ASSERT(OPPOSITE_COLORS(p, pMinor));
+
+ if (IS_BISHOP(pMinor))
+ {
+ ASSERT(PIECE_VALUE(p) == VALUE_BISHOP);
+ }
+ else
+ {
+ ASSERT(IS_KNIGHT(pMinor));
+ ASSERT(PIECE_VALUE(p) >= VALUE_KNIGHT);
+ }
+#endif
+ *puMobility += 1;
+ ASSERT(*puMobility <= 8);
+ return(TRUE); // stop scanning this dir
+}
+
+
+static FLAG FASTCALL
+_ERookToEnemySame(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(IS_ROOK(p));
+ ASSERT(OPPOSITE_COLORS(p, pRook));
+#endif
+ *puMobility += 1;
+ ASSERT(*puMobility <= 7);
+ return(TRUE); // stop scanning this dir
+}
+
+
+static FLAG FASTCALL
+_EQueenToEnemyGreaterEqual(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(PIECE_VALUE(p) >= VALUE_QUEEN);
+ ASSERT(OPPOSITE_COLORS(pQueen, p));
+#endif
+ *puMobility += 1;
+ ASSERT(*puMobility <= 27);
+ return(TRUE); // stop scanning this dir
+}
+
+// ----------------------------------------------------------------------
+
+static FLAG FASTCALL
+_EMinorToEnemyGreater(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(PIECE_VALUE(p) > VALUE_BISHOP);
+ ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor));
+ ASSERT(OPPOSITE_COLORS(p, pMinor));
+#endif
+ *puMobility += 1;
+ ASSERT(*puMobility <= 8);
+ *puBit = MINOR_XRAY_BIT;
+ return(FALSE); // keep scanning this dir
+}
+
+static FLAG FASTCALL
+_ERookToEnemyGreater(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(OPPOSITE_COLORS(p, pRook));
+ ASSERT(PIECE_VALUE(p) > VALUE_ROOK);
+#endif
+ *puMobility += 1;
+ ASSERT(*puMobility <= 7);
+ *puBit = ROOK_XRAY_BIT;
+ return(FALSE); // keep scanning this dir
+}
+
+// ----------------------------------------------------------------------
+
+static FLAG FASTCALL
+_AnythingToFriendNoXray(IN POSITION *pos,
+ IN COOR c,
+ UNUSED ULONG *puMobility,
+ UNUSED ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ COOR cMover = pos->cPiece;
+ PIECE pMover = pos->rgSquare[cMover].pPiece;
+ PIECE pFriend = pos->rgSquare[c].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(cMover));
+ ASSERT(!IS_EMPTY(pFriend));
+ ASSERT(!IS_EMPTY(pMover));
+ ASSERT(GET_COLOR(pMover) == GET_COLOR(pFriend));
+ if (IS_BISHOP(pMover))
+ {
+ ASSERT(!IS_BISHOP(pFriend));
+ ASSERT(!IS_QUEEN(pFriend));
+ }
+ else if (IS_ROOK(pMover))
+ {
+ ASSERT(!IS_QUEEN(pFriend));
+ ASSERT(!IS_ROOK(pFriend));
+ }
+ else if (IS_QUEEN(pMover))
+ {
+ ASSERT(!IS_BISHOP(pFriend));
+ ASSERT(!IS_ROOK(pFriend));
+ ASSERT(!IS_QUEEN(pFriend));
+ }
+#endif
+ return(TRUE); // stop scanning this dir
+}
+
+// ----------------------------------------------------------------------
+
+static FLAG FASTCALL
+_EMinorToFriendXray(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(!IS_EMPTY(pMinor));
+ ASSERT(IS_BISHOP(pMinor));
+ ASSERT(GET_COLOR(p) == GET_COLOR(pMinor));
+#endif
+ *puBit = MINOR_XRAY_BIT;
+ return(FALSE);
+}
+
+static FLAG FASTCALL
+_BRookToFriendRook(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+ COOR cRook = pos->cPiece;
+ FLAG fHoriz = ((c & 0xF0) == (cRook & 0xF0));
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[cRook].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(cRook));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(IS_ROOK(p));
+ ASSERT(GET_COLOR(p) == GET_COLOR(pRook));
+ ASSERT(GET_COLOR(p) == BLACK);
+ ASSERT((fHoriz && (RANK(c) == RANK(pos->cPiece))) ||
+ (!fHoriz && (FILE(c) == FILE(pos->cPiece))));
+#endif
+ EVAL_TERM(BLACK,
+ ROOK,
+ c,
+ pos->iScore[BLACK],
+ (ROOK_CONNECTED_HORIZ * fHoriz +
+ ROOK_CONNECTED_VERT * FLIP(fHoriz)),
+ "rook connected");
+ *puBit = ROOK_XRAY_BIT;
+ return(FALSE);
+}
+
+
+static FLAG FASTCALL
+_WRookToFriendRook(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+ COOR cRook = pos->cPiece;
+ FLAG fHoriz = ((c & 0xF0) == (cRook & 0xF0));
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[cRook].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(cRook));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(IS_ROOK(p));
+ ASSERT(GET_COLOR(p) == GET_COLOR(pRook));
+ ASSERT(GET_COLOR(p) == WHITE);
+ ASSERT((fHoriz && (RANK(c) == RANK(pos->cPiece))) ||
+ (!fHoriz && (FILE(c) == FILE(pos->cPiece))));
+#endif
+ EVAL_TERM(WHITE,
+ ROOK,
+ c,
+ pos->iScore[WHITE],
+ (ROOK_CONNECTED_HORIZ * fHoriz +
+ ROOK_CONNECTED_VERT * FLIP(fHoriz)),
+ "rook connected");
+ *puBit = ROOK_XRAY_BIT;
+ return(FALSE);
+}
+
+
+static FLAG FASTCALL
+_ERookToFriendQueen(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(!IS_EMPTY(p));
+ ASSERT(IS_QUEEN(p));
+ ASSERT(GET_COLOR(p) == GET_COLOR(pRook));
+#endif
+ *puBit = ROOK_XRAY_BIT;
+ return(FALSE);
+}
+
+static FLAG FASTCALL
+_EQueenToFriendBishop(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+ COOR cQueen = pos->cPiece;
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[cQueen].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(cQueen));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(IS_BISHOP(p));
+ ASSERT(GET_COLOR(p) == GET_COLOR(pQueen));
+#endif
+ *puBit = QUEEN_XRAY_BIT;
+ return((((c & 0xF0) == (cQueen & 0xF0)) ||
+ ((c & 0x0F) == (cQueen & 0x0F))));
+}
+
+
+static FLAG FASTCALL
+_EQueenToFriendRook(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+ COOR cQueen = pos->cPiece;
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[cQueen].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(cQueen));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(IS_ROOK(p));
+ ASSERT(GET_COLOR(p) == GET_COLOR(pQueen));
+#endif
+ *puBit = QUEEN_XRAY_BIT;
+ return(FLIP((((c & 0xF0) == (cQueen & 0xF0)) ||
+ ((c & 0x0F) == (cQueen & 0x0F)))));
+}
+
+
+static FLAG FASTCALL
+_EQueenToFriendQueen(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ COOR cQueen = pos->cPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[cQueen].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(cQueen));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(IS_QUEEN(p));
+ ASSERT(GET_COLOR(p) == GET_COLOR(pQueen));
+#endif
+ *puBit = QUEEN_XRAY_BIT;
+ return(FALSE);
+}
+
+
+// ----------------------------------------------------------------------
+
+static FLAG FASTCALL
+_BMinorToEmpty(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+
+ ASSERT(IS_EMPTY(p));
+ ASSERT(!IS_EMPTY(pMinor));
+ ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor));
+ ASSERT(GET_COLOR(pMinor) == BLACK);
+#endif
+ *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[WHITE]));
+ ASSERT(*puMobility <= 8);
+ return(FALSE);
+}
+
+static FLAG FASTCALL
+_WMinorToEmpty(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE pMinor = pos->rgSquare[pos->cPiece].pPiece;
+ PIECE p = pos->rgSquare[c].pPiece;
+
+ ASSERT(IS_EMPTY(p));
+ ASSERT(!IS_EMPTY(pMinor));
+ ASSERT(IS_BISHOP(pMinor) || IS_KNIGHT(pMinor));
+ ASSERT(GET_COLOR(pMinor) == WHITE);
+#endif
+ *puMobility += (!UNSAFE_FOR_MINOR(pos->rgSquare[c|8].bvAttacks[BLACK]));
+ ASSERT(*puMobility <= 8);
+ return(FALSE);
+}
+
+static FLAG FASTCALL
+_WRookToEmpty(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(IS_EMPTY(p));
+ ASSERT(GET_COLOR(pRook) == WHITE);
+#endif
+ *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[BLACK]));
+ ASSERT(*puMobility <= 7);
+ return(FALSE);
+}
+
+static FLAG FASTCALL
+_BRookToEmpty(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pRook = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_ROOK(pRook));
+ ASSERT(IS_EMPTY(p));
+ ASSERT(GET_COLOR(pRook) == BLACK);
+#endif
+ *puMobility += (!UNSAFE_FOR_ROOK(pos->rgSquare[c|8].bvAttacks[WHITE]));
+ ASSERT(*puMobility <= 7);
+ return(FALSE);
+}
+
+static FLAG FASTCALL
+_BQueenToEmpty(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(IS_EMPTY(p));
+ ASSERT(GET_COLOR(pQueen) == BLACK);
+#endif
+ *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[WHITE]));
+ ASSERT(*puMobility <= 27);
+ return(FALSE);
+}
+
+static FLAG FASTCALL
+_WQueenToEmpty(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE p = pos->rgSquare[c].pPiece;
+ PIECE pQueen = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_QUEEN(pQueen));
+ ASSERT(IS_EMPTY(p));
+ ASSERT(GET_COLOR(pQueen) == WHITE);
+#endif
+ *puMobility += (!UNSAFE_FOR_QUEEN(pos->rgSquare[c|8].bvAttacks[BLACK]));
+ ASSERT(*puMobility <= 27);
+ return(FALSE);
+}
+
+
+
+static FLAG FASTCALL
+_EBishopToFriendPawn(IN POSITION *pos,
+ IN COOR c,
+ IN OUT ULONG *puMobility,
+ IN OUT ULONG *puBit)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ ULONG *puMobility,
+ ULONG *puBit
+
+Return value:
+
+ static FLAG FASTCALL
+
+**/
+{
+#ifdef DEBUG
+ PIECE pPawn = pos->rgSquare[c].pPiece;
+ PIECE pBishop = pos->rgSquare[pos->cPiece].pPiece;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ON_BOARD(pos->cPiece));
+ ASSERT(IS_BISHOP(pBishop));
+ ASSERT(IS_PAWN(pPawn));
+ ASSERT(GET_COLOR(pBishop) == GET_COLOR(pPawn));
+#endif
+ *puMobility += ((pos->bb & COOR_TO_BB(c)) != 0);
+ return(TRUE);
+}
+
+
+
+
+//
+// ======================================================================
+//
+
+
+static void
+_EvalBishop(IN OUT POSITION *pos,
+ IN COOR c,
+ IN PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ FLAG
+
+**/
+{
+ static const BITBOARD bbColorSq[2] = { 0x55aa55aa55aa55aaULL,
+ 0xaa55aa55aa55aa55ULL };
+ static const PMOBILITY_HELPER BMobJumpTable[2][14] =
+ {
+ {// (black)
+ _BMinorToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _EBishopToFriendPawn, // BLACK_PAWN (2)
+ _BMinorToEnemyLess, // WHITE_PAWN (3)
+ _AnythingToFriendNoXray, // BLACK_KNIGHT (4)
+ _EMinorToEnemySame, // WHITE_KNIGHT (5)
+ _EMinorToFriendXray, // BLACK_BISHOP (6)
+ _EMinorToEnemySame, // WHITE_BISHOP (7)
+ _AnythingToFriendNoXray, // BLACK_ROOK (8)
+ _EMinorToEnemyGreater, // WHITE_ROOK (9)
+ _EMinorToFriendXray, // BLACK_QUEEN (10)
+ _EMinorToEnemyGreater, // WHITE_QUEEN (11)
+ _AnythingToFriendNoXray, // BLACK_KING (12)
+ _EMinorToEnemyGreater, // WHITE_KING (13)
+ },
+ {// (white)
+ _WMinorToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _WMinorToEnemyLess, // BLACK_PAWN (2)
+ _EBishopToFriendPawn, // WHITE_PAWN (3)
+ _EMinorToEnemySame, // BLACK_KNIGHT (4)
+ _AnythingToFriendNoXray, // WHITE_KNIGHT (5)
+ _EMinorToEnemySame, // BLACK_BISHOP (6)
+ _EMinorToFriendXray, // WHITE_BISHOP (7)
+ _EMinorToEnemyGreater, // BLACK_ROOK (8)
+ _AnythingToFriendNoXray, // WHITE_ROOK (9)
+ _EMinorToEnemyGreater, // BLACK_QUEEN (10)
+ _EMinorToFriendXray, // WHITE_QUEEN (11)
+ _EMinorToEnemyGreater, // BLACK_KING (12)
+ _AnythingToFriendNoXray, // WHITE_KING (13)
+ }
+ };
+ static const COOR cBishopAtHome[2][2] =
+ {
+ { C8, F8 }, // BLACK
+ { C1, F1 } // WHITE
+ };
+ ULONG uColor;
+ BITBOARD bb;
+ BITBOARD bbMask;
+ BITBOARD bbPc;
+ COOR cSquare;
+ SCORE i;
+ ULONG u;
+ ULONG uTotalMobility;
+ ULONG uMaxMobility;
+ ULONG uCurrentMobility;
+ ULONG uBit;
+ PIECE p;
+ PMOBILITY_HELPER pFun;
+
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(p && IS_BISHOP(p));
+ uColor = GET_COLOR(p);
+
+#ifdef DEBUG
+ pos->cPiece = c;
+#endif
+
+ //
+ // Unmoved piece
+ //
+ pos->uMinorsAtHome[uColor] += (c == cBishopAtHome[uColor][0]);
+ pos->uMinorsAtHome[uColor] += (c == cBishopAtHome[uColor][1]);
+ ASSERT(pos->uMinorsAtHome[uColor] <= 4);
+
+ //
+ // Good and bad bishops
+ //
+ i = 16;
+ bbMask = bbColorSq[IS_SQUARE_WHITE(c)];
+ bb = pHash->bbStationaryPawns[uColor] & bbMask;
+ ASSERT(CountBits(bb) <= 16);
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ //
+ // Consider stationary (rammed or backward) pawns of the same
+ // color as the bishop.
+ //
+ ASSERT(pos->rgSquare[cSquare].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == uColor);
+ i += STATIONARY_PAWN_ON_BISHOP_COLOR[cSquare];
+ }
+ EVAL_TERM(uColor,
+ BISHOP,
+ c,
+ pos->iScore[uColor],
+ i,
+ "good/bad stationary pawns ++");
+
+ i = 0;
+ bbPc = ~(pHash->bbStationaryPawns[WHITE] |
+ pHash->bbStationaryPawns[BLACK]);
+ bbPc &= bbMask;
+ bb = pHash->bbPawnLocations[uColor] & bbPc;
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ ASSERT(pos->rgSquare[cSquare].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == uColor);
+ ASSERT(pHash->bbPawnLocations[uColor] & COOR_TO_BB(cSquare));
+ i += TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare];
+ }
+
+ bb = pHash->bbPawnLocations[FLIP(uColor)] & bbPc;
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ //
+ // N.B. Only count enemy pawns that are supported by another
+ // pawn.
+ //
+ ASSERT(pos->rgSquare[cSquare].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == FLIP(uColor));
+ ASSERT(pHash->bbPawnLocations[FLIP(uColor)] & COOR_TO_BB(cSquare));
+ i +=
+ (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].small.uPawn != 0) *
+ TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare] / 2;
+ }
+ EVAL_TERM(uColor,
+ BISHOP,
+ c,
+ pos->iScore[uColor],
+ i,
+ "good/bad transient pawns");
+
+ //
+ // Bonus to bishops in very open positions.
+ //
+ ASSERT(pos->uClosedScaler <= 32);
+ EVAL_TERM(uColor,
+ BISHOP,
+ c,
+ pos->iScore[uColor],
+ BISHOP_IN_CLOSED_POSITION[pos->uClosedScaler],
+ "in closed/open position");
+
+ //
+ // Bishop mobility (and update attack table bits)
+ //
+ u = uMaxMobility = uTotalMobility = 0;
+ pos->bb = bbPc;
+ ASSERT(g_iBDeltas[u] != 0);
+ do
+ {
+ uCurrentMobility = 0;
+ uBit = MINOR_BIT;
+ cSquare = c + g_iBDeltas[u];
+
+ while(IS_ON_BOARD(cSquare))
+ {
+ //
+ // Always toggle attack table bits.
+ //
+ ASSERT((cSquare|8) == (cSquare + 8));
+ pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit;
+
+ //
+ // What did we hit?
+ //
+ p = pos->rgSquare[cSquare].pPiece;
+ pFun = BMobJumpTable[uColor][p];
+ ASSERT(pFun);
+ if (TRUE == (*pFun)(pos,
+ cSquare,
+ &uCurrentMobility,
+ &uBit))
+ {
+ break;
+ }
+ cSquare += g_iBDeltas[u];
+ }
+ uTotalMobility += uCurrentMobility;
+ ASSERT((uMaxMobility & 0x80000000) == 0);
+ ASSERT((uCurrentMobility & 0x80000000) == 0);
+ uMaxMobility = MAXU(uMaxMobility, uCurrentMobility);
+ ASSERT(uMaxMobility <= 7);
+ u++;
+ }
+ while(g_iBDeltas[u] != 0);
+ ASSERT(uTotalMobility <= 13);
+ EVAL_TERM(uColor,
+ BISHOP,
+ c,
+ pos->iScore[uColor],
+ BISHOP_MOBILITY_BY_SQUARES[uTotalMobility],
+ "bishop mobility");
+ EVAL_TERM(uColor,
+ BISHOP,
+ c,
+ pos->iScore[uColor],
+ BISHOP_MAX_MOBILITY_IN_A_ROW_BONUS[uMaxMobility],
+ "consecutive bishop mobility");
+
+ //
+ // Look for bishops with no mobility who are under attack. These
+ // pieces are trapped!
+ //
+ if (uTotalMobility == 0)
+ {
+ pos->cTrapped[uColor] = c;
+ }
+ uTotalMobility /= 2;
+ ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0);
+ ASSERT((uTotalMobility & 0x80000000) == 0);
+ pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor],
+ uTotalMobility);
+
+ //
+ // Look for "active bad bishops". See if square c is safe from
+ // enemy pawns.
+ //
+ bb = pHash->bbPawnLocations[FLIP(uColor)] &
+ (~pHash->bbStationaryPawns[FLIP(uColor)]);
+ if (TRUE == _IsSquareSafeFromEnemyPawn(pos, c, bb))
+ {
+ //
+ // Give a bonus based on distance from enemy king
+ //
+ if (pos->rgSquare[c|8].bvAttacks[uColor].small.uPawn)
+ {
+#ifdef DEBUG
+ p = BLACK_PAWN | uColor;
+ ASSERT(((IS_ON_BOARD(c + 17 * g_iBehind[uColor]) &&
+ (pos->rgSquare[c + 17 * g_iBehind[uColor]].pPiece==p)) ||
+ ((IS_ON_BOARD(c + 15 * g_iBehind[uColor])) &&
+ (pos->rgSquare[c + 15 * g_iBehind[uColor]].pPiece==p))));
+#endif
+ u = DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]);
+ i = BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING[u];
+ EVAL_TERM(uColor,
+ BISHOP,
+ c,
+ pos->iReducedMaterialDownScaler[uColor],
+ i,
+ "[scaled] unassailable");
+ }
+ }
+}
+
+static void
+_EvalKnight(IN OUT POSITION *pos,
+ IN COOR c,
+ IN PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ FLAG
+
+**/
+{
+ static const int iPawnStart[2] = { -17, +15 };
+ static const PMOBILITY_HELPER NMobJumpTable[2][14] =
+ {
+ {// (black)
+ _BMinorToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _AnythingToFriendNoXray, // BLACK_PAWN (2)
+ _BMinorToEnemyLess, // WHITE_PAWN (3)
+ _AnythingToFriendNoXray, // BLACK_KNIGHT (4)
+ _EMinorToEnemySame, // WHITE_KNIGHT (5)
+ _AnythingToFriendNoXray, // BLACK_BISHOP (6)
+ _EMinorToEnemySame, // WHITE_BISHOP (7)
+ _AnythingToFriendNoXray, // BLACK_ROOK (8)
+ _EMinorToEnemySame, // WHITE_ROOK (9)
+ _AnythingToFriendNoXray, // BLACK_QUEEN (10)
+ _EMinorToEnemySame, // WHITE_QUEEN (11)
+ _AnythingToFriendNoXray, // BLACK_KING (12)
+ _EMinorToEnemySame, // WHITE_KING (13)
+ },
+ {// (white)
+ _WMinorToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _WMinorToEnemyLess, // BLACK_PAWN (2)
+ _AnythingToFriendNoXray, // WHITE_PAWN (3)
+ _EMinorToEnemySame, // BLACK_KNIGHT (4)
+ _AnythingToFriendNoXray, // WHITE_KNIGHT (5)
+ _EMinorToEnemySame, // BLACK_BISHOP (6)
+ _AnythingToFriendNoXray, // WHITE_BISHOP (7)
+ _EMinorToEnemySame, // BLACK_ROOK (8)
+ _AnythingToFriendNoXray, // WHITE_ROOK (9)
+ _EMinorToEnemySame, // BLACK_QUEEN (10)
+ _AnythingToFriendNoXray, // WHITE_QUEEN (11)
+ _EMinorToEnemySame, // BLACK_KING (12)
+ _AnythingToFriendNoXray, // WHITE_KING (13)
+ }
+ };
+ static const COOR cKnightAtHome[2][2] =
+ {
+ { B8, G8 }, // BLACK
+ { B1, G1 } // WHITE
+ };
+ PIECE p;
+ COOR cSquare;
+ BITBOARD bb;
+ ULONG uColor;
+ ULONG uPawnsSupporting;
+ ULONG u;
+ ULONG uPawnFile;
+ ULONG uMobilitySquares;
+ SCORE i;
+ ULONG uDist;
+ PMOBILITY_HELPER pFun;
+
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(p && IS_KNIGHT(p));
+ uColor = GET_COLOR(p);
+ ASSERT(IS_VALID_COLOR(uColor));
+#ifdef DEBUG
+ pos->cPiece = c;
+#endif
+
+ //
+ // Unmoved piece
+ //
+ pos->uMinorsAtHome[uColor] += (c == cKnightAtHome[uColor][0]);
+ pos->uMinorsAtHome[uColor] += (c == cKnightAtHome[uColor][1]);
+ ASSERT(pos->uMinorsAtHome[uColor] <= 4);
+
+ //
+ // TODO: Don't block unmoved E2/D2 pawns
+ //
+
+ //
+ // Centrality bonus
+ //
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iScore[uColor],
+ KNIGHT_CENTRALITY_BONUS[c],
+ "board centrality");
+
+ //
+ // Give a bonus to knights on a closed / busy board
+ //
+ ASSERT(pos->uClosedScaler <= 32);
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iScore[uColor],
+ KNIGHT_IN_CLOSED_POSITION[pos->uClosedScaler],
+ "in closed/open position");
+
+ //
+ // See if square c is safe from enemy pawns; if so give bonus for
+ // outposted knight which increases the closer it is to the enemy
+ // king and the more pawns it has supporting it.
+ //
+ uDist = DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]);
+ ASSERT((uDist > 0) && (uDist <= 8));
+ bb = pHash->bbPawnLocations[FLIP(uColor)] &
+ (~pHash->bbStationaryPawns[FLIP(uColor)]);
+ if (TRUE == _IsSquareSafeFromEnemyPawn(pos, c, bb))
+ {
+ //
+ // Count the number of supporting pawns the knight has
+ //
+ uPawnsSupporting = 0;
+ p = BLACK_PAWN | uColor;
+ cSquare = c + iPawnStart[uColor];
+ uPawnsSupporting = (IS_ON_BOARD(cSquare) &&
+ (pos->rgSquare[cSquare].pPiece == p));
+ cSquare += 2;
+ uPawnsSupporting += (IS_ON_BOARD(cSquare) &&
+ (pos->rgSquare[cSquare].pPiece == p));
+ ASSERT(uPawnsSupporting <= 2);
+
+ //
+ // Give a bonus based on distance from enemy king
+ //
+ i = KNIGHT_UNASSAILABLE_BY_DIST_FROM_EKING[uDist] +
+ KNIGHT_WITH_N_PAWNS_SUPPORTING[uPawnsSupporting];
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iReducedMaterialDownScaler[uColor],
+ i,
+ "[scaled] unassailable/support");
+
+ //
+ // Now, if there's zero or one supporter see if the enemy has a
+ // bishop the color of the knight it could capture with.
+ //
+ if (uPawnsSupporting != 2)
+ {
+ if (IS_SQUARE_WHITE(c))
+ {
+ if (pos->uWhiteSqBishopCount[FLIP(uColor)])
+ {
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iScore[uColor],
+ -(i / 2),
+ "not safe from enemy B");
+ }
+ }
+ else
+ {
+ if (pos->uNonPawnCount[FLIP(uColor)][BISHOP] -
+ pos->uWhiteSqBishopCount[FLIP(uColor)])
+ {
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iScore[uColor],
+ -(i / 2),
+ "not safe from enemy B");
+ }
+ }
+ }
+ }
+ else // not outposted
+ {
+ //
+ // Still good to be close to the enemy king.
+ //
+ i = KNIGHT_KING_TROPISM_BONUS[uDist];
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iReducedMaterialDownScaler[uColor],
+ i,
+ "[scaled] enemy king tropism");
+ }
+
+ //
+ // Give a bonus for blockading an enemy backward pawn. Note:
+ // we also reward pieces for blocking enemy passers in the
+ // passer code.
+ //
+ cSquare = c + 16 * g_iAhead[uColor];
+ bb = pHash->bbStationaryPawns[FLIP(uColor)];
+ if (bb & COOR_TO_BB(cSquare))
+ {
+ ASSERT(pos->rgSquare[cSquare].pPiece);
+ ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iScore[uColor],
+ KNIGHT_ON_INTERESTING_SQUARE_BY_RANK[uColor][RANK(c)],
+ "blockades enemy pawn");
+ }
+
+ //
+ // A knight with an open file behind it is good.
+ //
+ uPawnFile = FILE(c) + 1;
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iScore[uColor],
+ ((pHash->uCountPerFile[uColor][uPawnFile] == 0) *
+ KNIGHT_ON_INTERESTING_SQUARE_BY_RANK[uColor][RANK(c)]),
+ "open file behind");
+
+ //
+ // Do mobilility and piece relevance. Also update attack tables.
+ //
+ uMobilitySquares = 0;
+ u = 0;
+ ASSERT(g_iNDeltas[u] != 0);
+ do
+ {
+ cSquare = c + g_iNDeltas[u];
+ if (IS_ON_BOARD(cSquare))
+ {
+ //
+ // Always update the attack bits
+ //
+ ASSERT((cSquare + 8) == (cSquare | 8));
+ pos->rgSquare[cSquare|8].bvAttacks[uColor].small.uMinor = 1;
+
+ //
+ // See what we hit
+ //
+ p = pos->rgSquare[cSquare].pPiece;
+ pFun = NMobJumpTable[uColor][p];
+ if (pFun != _AnythingToFriendNoXray)
+ {
+ (void)(*pFun)(pos,
+ cSquare,
+ &uMobilitySquares,
+ NULL);
+ }
+
+ //
+ // IDEA: bonus for hitting friendly pawn?
+ //
+ }
+ u++;
+ }
+ while(g_iNDeltas[u] != 0);
+ ASSERT(uMobilitySquares >= 0);
+ ASSERT(uMobilitySquares <= 8);
+ EVAL_TERM(uColor,
+ KNIGHT,
+ c,
+ pos->iScore[uColor],
+ KNIGHT_MOBILITY_BY_COUNT[uMobilitySquares],
+ "knight mobility");
+ if (uMobilitySquares == 0)
+ {
+ pos->cTrapped[uColor] = c;
+ }
+ ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0);
+ ASSERT((uMobilitySquares & 0x80000000) == 0);
+ pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor],
+ uMobilitySquares);
+}
+
+static void
+_EvalRook(IN OUT POSITION *pos,
+ IN COOR c,
+ IN PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+Parameters:
+
+ IN POSITION *pos,
+ IN COOR c,
+ IN PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ FLAG
+
+**/
+{
+ static const PMOBILITY_HELPER RMobJumpTable[2][14] =
+ {
+ {// (black)
+ _BRookToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _AnythingToFriendNoXray, // BLACK_PAWN (2)
+ _BRookToEnemyLess, // WHITE_PAWN (3)
+ _AnythingToFriendNoXray, // BLACK_KNIGHT (4)
+ _BRookToEnemyLess, // WHITE_KNIGHT (5)
+ _AnythingToFriendNoXray, // BLACK_BISHOP (6)
+ _BRookToEnemyLess, // WHITE_BISHOP (7)
+ _BRookToFriendRook, // BLACK_ROOK (8)
+ _ERookToEnemySame, // WHITE_ROOK (9)
+ _ERookToFriendQueen, // BLACK_QUEEN (10)
+ _ERookToEnemyGreater, // WHITE_QUEEN (11)
+ _AnythingToFriendNoXray, // BLACK_KING (12)
+ _ERookToEnemyGreater, // WHITE_KING (13)
+ },
+ {// (white)
+ _WRookToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _WRookToEnemyLess, // BLACK_PAWN (2)
+ _AnythingToFriendNoXray, // WHITE_PAWN (3)
+ _WRookToEnemyLess, // BLACK_KNIGHT (4)
+ _AnythingToFriendNoXray, // WHITE_KNIGHT (5)
+ _WRookToEnemyLess, // BLACK_BISHOP (6)
+ _AnythingToFriendNoXray, // WHITE_BISHOP (7)
+ _ERookToEnemySame, // BLACK_ROOK (8)
+ _WRookToFriendRook, // WHITE_ROOK (9)
+ _ERookToEnemyGreater, // BLACK_QUEEN (10)
+ _ERookToFriendQueen, // WHITE_QUEEN (11)
+ _ERookToEnemyGreater, // BLACK_KING (12)
+ _AnythingToFriendNoXray, // WHITE_KING (13)
+ },
+ };
+
+ PIECE p;
+ PIECE pFriendRook;
+ ULONG uPawnFile = FILE(c) + 1;
+ ULONG uColor;
+ ULONG u;
+ ULONG uCurrentMobility;
+ ULONG uMaxMobility;
+ ULONG uTotalMobility;
+ COOR cSquare;
+ BITBOARD bb;
+ ULONG uBit;
+ PMOBILITY_HELPER pFun;
+ SCORE i;
+
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(p && IS_ROOK(p));
+ uColor = GET_COLOR(p);
+ ASSERT(IS_VALID_COLOR(uColor));
+ pos->cPiece = c;
+
+ //
+ // Reward being on a half open or full open file.
+ //
+ if (0 == pHash->uCountPerFile[uColor][uPawnFile])
+ {
+ u = FILE_DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]);
+ if (0 == pHash->uCountPerFile[FLIP(uColor)][uPawnFile])
+ {
+ //
+ // Full open
+ //
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ ROOK_ON_FULL_OPEN_BY_DIST_FROM_EKING[u],
+ "on full open");
+ }
+ else
+ {
+ //
+ // Half open, no friendly, just enemy.
+ //
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ ROOK_ON_HALF_OPEN_WITH_ENEMY_BY_DIST_FROM_EKING[u],
+ "on half open");
+
+ //
+ // Added bonus if the enemy pawn is a passer.
+ //
+ bb = pHash->bbPasserLocations[FLIP(uColor)] & BBFILE[FILE(c)];
+ if (bb)
+ {
+ cSquare = CoorFromBitBoardRank8ToRank1(&bb);
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ PASSER_BY_RANK[FLIP(uColor)][RANK(cSquare)] / 4,
+ "hassles enemy passer");
+ }
+ }
+ }
+ else if (0 == pHash->uCountPerFile[FLIP(uColor)][uPawnFile])
+ {
+ //
+ // Half open, no enemy pawn, just a friendly
+ //
+ u = FILE_DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]);
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ ROOK_ON_HALF_OPEN_WITH_FRIEND_BY_DIST_FROM_EKING[u],
+ "on half open");
+
+ //
+ // See if the friend is a passed pawn and if the rook's in
+ // front of it or behind it.
+ //
+ bb = pHash->bbPasserLocations[uColor] & BBFILE[FILE(c)];
+ if (bb)
+ {
+ //
+ // IDEA: Rook behind candidates, helpers or sentries is good
+ // because the file may open soon.
+ //
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ if (uColor == WHITE)
+ {
+ if (cSquare < c)
+ {
+ EVAL_TERM(WHITE,
+ ROOK,
+ c,
+ pos->iScore[WHITE],
+ ROOK_BEHIND_PASSER_BY_PASSER_RANK[WHITE]
+ [RANK(cSquare)],
+ "behind own passer");
+ }
+ else
+ {
+ EVAL_TERM(WHITE,
+ ROOK,
+ c,
+ pos->iScore[WHITE],
+ ROOK_LEADS_PASSER_BY_PASSER_RANK[WHITE]
+ [RANK(cSquare)],
+ "in the way of passer");
+ }
+ } else {
+ ASSERT(uColor == BLACK);
+ if (cSquare > c)
+ {
+ EVAL_TERM(BLACK,
+ ROOK,
+ c,
+ pos->iScore[BLACK],
+ ROOK_BEHIND_PASSER_BY_PASSER_RANK[BLACK]
+ [RANK(cSquare)],
+ "behind own passer");
+ }
+ else
+ {
+ EVAL_TERM(BLACK,
+ ROOK,
+ c,
+ pos->iScore[BLACK],
+ ROOK_LEADS_PASSER_BY_PASSER_RANK[BLACK]
+ [RANK(cSquare)],
+ "in the way of passer");
+ }
+ }
+ }
+ }
+ }
+
+ //
+ // Look for rooks that are trapping enemy kings.
+ //
+ if (uColor == WHITE)
+ {
+ pFriendRook = WHITE_ROOK;
+
+ //
+ // Rook on 7th/8th rank with an enemy king back there.
+ //
+ if ((c < A6) && (pos->cNonPawns[BLACK][0] < A6))
+ {
+ ASSERT(RANK(c) > 6);
+ ASSERT(RANK(pos->cNonPawns[BLACK][0]) > 6);
+
+ EVAL_TERM(WHITE,
+ ROOK,
+ c,
+ pos->iScore[WHITE],
+ ROOK_TRAPPING_EKING,
+ "rook trapping enemy king");
+ }
+ }
+ else
+ {
+ pFriendRook = BLACK_ROOK;
+
+ //
+ // Rook on 7th/8th rank with an enemy king back there.
+ //
+ if ((c > H3) && (pos->cNonPawns[WHITE][0] > H3))
+ {
+ ASSERT(RANK(c) < 3);
+ ASSERT(RANK(pos->cNonPawns[WHITE][0]) < 3);
+ EVAL_TERM(BLACK,
+ ROOK,
+ c,
+ pos->iScore[BLACK],
+ ROOK_TRAPPING_EKING,
+ "rook trapping enemy king");
+ }
+ }
+
+ //
+ // Rooks increase in value as pawns come off:
+ //
+ // "A further refinement would be to raise the knight's value by
+ // 1/16 and lower the rook's value by 1/8 for each pawn above five
+ // of the side being valued, with the opposite adjustment for each
+ // pawn short of five."
+ // --Larry Kaufman, IM
+ // "Evaluation of Material Imbalance"
+ //
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ ROOK_VALUE_AS_PAWNS_COME_OFF[pos->uPawnCount[uColor] +
+ pos->uPawnCount[FLIP(uColor)]],
+ "increase in value/pawns");
+
+ //
+ // Rook mobility
+ //
+ u = uMaxMobility = uTotalMobility = 0;
+ ASSERT(g_iRDeltas[u] != 0);
+ do
+ {
+ uCurrentMobility = 0;
+ uBit = ROOK_BIT;
+ cSquare = c + g_iRDeltas[u];
+
+ while(IS_ON_BOARD(cSquare))
+ {
+ //
+ // Twiddle our attack table bits.
+ //
+ pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit;
+ ASSERT((cSquare | 8) == (cSquare + 8));
+
+ //
+ // What did we hit?
+ //
+ p = pos->rgSquare[cSquare].pPiece;
+ pFun = RMobJumpTable[uColor][p];
+ if (TRUE == (*pFun)(pos,
+ cSquare,
+ &uCurrentMobility,
+ &uBit))
+ {
+ break;
+ }
+ cSquare += g_iRDeltas[u];
+ }
+ uTotalMobility += uCurrentMobility;
+ ASSERT(uTotalMobility <= 14);
+ ASSERT((uMaxMobility & 0x80000000) == 0);
+ ASSERT((uCurrentMobility & 0x80000000) == 0);
+ uMaxMobility = MAXU(uCurrentMobility, uMaxMobility);
+ ASSERT(uMaxMobility <= 7);
+ u++;
+ }
+ while(g_iRDeltas[u] != 0);
+ ASSERT(uTotalMobility <= 14);
+
+ ASSERT(pos->uArmyScaler[FLIP(uColor)] <= 31);
+ ASSERT(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[FLIP(uColor)]] <= 8);
+ i = ROOK_MOBILITY_BY_SQUARES[uTotalMobility];
+ i *= (int)(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[FLIP(uColor)]] + 1);
+ i /= 8;
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ i,
+ "rook mobility");
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ ROOK_MAX_MOBILITY_IN_A_ROW_BONUS[uMaxMobility],
+ "consecutive rook mobility");
+
+ ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0);
+ ASSERT((uTotalMobility & 0x80000000) == 0);
+ pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor],
+ uTotalMobility);
+ if (uTotalMobility < 3)
+ {
+ //
+ // Look for rooks with no mobility who are under attack. These
+ // pieces are trapped!
+ //
+ if (uTotalMobility == 0)
+ {
+ pos->cTrapped[uColor] = c;
+ }
+
+ //
+ // Rook trapped in the corner by a stupid friendly king?
+ //
+ ASSERT(IS_VALID_COLOR(uColor));
+ if (uColor == WHITE)
+ {
+ ASSERT(IS_ON_BOARD(c));
+ if (RANK1(c))
+ {
+ cSquare = pos->cNonPawns[WHITE][0];
+ ASSERT(IS_ON_BOARD(cSquare));
+ if (RANK1(cSquare))
+ {
+ if (((cSquare > E1) && (c > cSquare)) ||
+ ((cSquare < D1) && (c < cSquare)))
+ {
+ EVAL_TERM(WHITE,
+ ROOK,
+ c,
+ pos->iScore[WHITE],
+ KING_TRAPPING_ROOK,
+ "king trapping rook");
+ }
+ }
+ }
+ }
+ else
+ {
+ ASSERT(uColor == BLACK);
+ ASSERT(IS_ON_BOARD(c));
+ if (RANK8(c))
+ {
+ cSquare = pos->cNonPawns[BLACK][0];
+ ASSERT(IS_ON_BOARD(cSquare));
+
+ if (RANK8(cSquare))
+ {
+ if (((cSquare > E8) && (c > cSquare)) ||
+ ((cSquare < D8) && (c < cSquare)))
+ {
+ EVAL_TERM(BLACK,
+ ROOK,
+ c,
+ pos->iScore[BLACK],
+ KING_TRAPPING_ROOK,
+ "king trapping rook");
+ }
+ }
+ }
+ }
+ } // if low mobility
+}
+
+
+static void
+_EvalQueen(IN OUT POSITION *pos,
+ IN COOR c,
+ IN PAWN_HASH_ENTRY *pHash)
+/**
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ FLAG
+
+**/
+{
+ static const PMOBILITY_HELPER QMobJumpTable[2][14] =
+ {
+ {
+ _BQueenToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _AnythingToFriendNoXray, // BLACK_PAWN (2)
+ _BQueenToEnemyLess, // WHITE_PAWN (3)
+ _AnythingToFriendNoXray, // BLACK_KNIGHT (4)
+ _BQueenToEnemyLess, // WHITE_KNIGHT (5)
+ _EQueenToFriendBishop, // BLACK_BISHOP (6)
+ _BQueenToEnemyLess, // WHITE_BISHOP (7)
+ _EQueenToFriendRook, // BLACK_ROOK (8)
+ _BQueenToEnemyLess, // WHITE_ROOK (9)
+ _EQueenToFriendQueen, // BLACK_QUEEN (10)
+ _EQueenToEnemyGreaterEqual, // WHITE_QUEEN (11)
+ _AnythingToFriendNoXray, // BLACK_KING (12)
+ _EQueenToEnemyGreaterEqual, // WHITE_KING (13)
+ },
+ {
+ _WQueenToEmpty, // EMPTY_SQUARE (0)
+ _InvalidMobilityHelper, // INVALID_PIECE (1)
+ _WQueenToEnemyLess, // BLACK_PAWN (2)
+ _AnythingToFriendNoXray, // WHITE_PAWN (3)
+ _WQueenToEnemyLess, // BLACK_KNIGHT (4)
+ _AnythingToFriendNoXray, // WHITE_KNIGHT (5)
+ _WQueenToEnemyLess, // BLACK_BISHOP (6)
+ _EQueenToFriendBishop, // WHITE_BISHOP (7)
+ _WQueenToEnemyLess, // BLACK_ROOK (8)
+ _EQueenToFriendRook, // WHITE_ROOK (9)
+ _EQueenToEnemyGreaterEqual, // BLACK_QUEEN (10)
+ _EQueenToFriendQueen, // WHITE_QUEEN (11)
+ _EQueenToEnemyGreaterEqual, // BLACK_KING (12)
+ _AnythingToFriendNoXray, // WHITE_KING (13)
+ },
+ };
+
+ PIECE p = pos->rgSquare[c].pPiece;
+ ULONG uColor;
+ COOR cSquare;
+ ULONG uTotalMobility;
+ ULONG u;
+ ULONG uBit;
+ PMOBILITY_HELPER pFun;
+ ULONG uNearKing = 0;
+ COOR cKing;
+
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(p && IS_QUEEN(p));
+ uColor = GET_COLOR(p);
+ ASSERT(IS_VALID_COLOR(uColor));
+ pos->cPiece = c;
+ cKing = pos->cNonPawns[FLIP(uColor)][0];
+ ASSERT(IS_ON_BOARD(cKing));
+ ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
+
+ if ((FALSE == pos->fCastled[uColor]) && (pos->uMinorsAtHome[uColor] > 1))
+ {
+ //
+ // Discourage queen being out too early
+ //
+ if (!RANK1(c) && !RANK8(c))
+ {
+ ASSERT(QUEEN_OUT_EARLY[pos->uMinorsAtHome[uColor]] < 0);
+ ASSERT(pos->uMinorsAtHome[uColor] <= 4);
+ EVAL_TERM(uColor,
+ QUEEN,
+ c,
+ pos->iScore[uColor],
+ QUEEN_OUT_EARLY[pos->uMinorsAtHome[uColor]],
+ "queen out too early");
+ }
+ }
+ else
+ {
+ //
+ // Encourage enemy king tropism
+ //
+ u = DISTANCE(c, cKing);
+ ASSERT((u <= 7) && (u > 0));
+ EVAL_TERM(uColor,
+ QUEEN,
+ c,
+ pos->iScore[uColor],
+ QUEEN_KING_TROPISM[u],
+ "enemy king tropism");
+ }
+
+ //
+ // Do queen mobility
+ //
+ uTotalMobility = 0;
+ u = 0;
+ ASSERT(g_iQKDeltas[u] != 0);
+ do
+ {
+ uBit = QUEEN_BIT;
+ cSquare = c + g_iQKDeltas[u];
+ while(IS_ON_BOARD(cSquare))
+ {
+ //
+ // Toggle attack table bits.
+ //
+ pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing |= uBit;
+
+ //
+ // What did we hit?
+ //
+ p = pos->rgSquare[cSquare].pPiece;
+ uNearKing += (DISTANCE(cSquare, cKing) <= 1);
+ pFun = QMobJumpTable[uColor][p];
+ if (TRUE == (*pFun)(pos,
+ cSquare,
+ &uTotalMobility,
+ &uBit))
+ {
+ break;
+ }
+ cSquare += g_iQKDeltas[u];
+ }
+ u++;
+ }
+ while(g_iQKDeltas[u] != 0);
+
+ ASSERT(uTotalMobility <= 27);
+ EVAL_TERM(uColor,
+ QUEEN,
+ c,
+ pos->iScore[uColor],
+ QUEEN_MOBILITY_BY_SQUARES[uTotalMobility],
+ "queen mobility");
+
+ //
+ // Look for queens with no mobility who are under attack. These
+ // pieces are trapped!
+ //
+ if (uTotalMobility == 0)
+ {
+ pos->cTrapped[uColor] = c;
+ }
+ ASSERT((pos->uMinMobility[uColor] & 0x80000000) == 0);
+ ASSERT((uTotalMobility & 0x80000000) == 0);
+ pos->uMinMobility[uColor] = MINU(pos->uMinMobility[uColor],
+ uTotalMobility);
+
+ //
+ // If queen points near enemy king, that's a good thing too.
+ //
+ ASSERT(uNearKing <= 6);
+ EVAL_TERM(uColor,
+ QUEEN,
+ c,
+ pos->iScore[uColor],
+ (uNearKing * QUEEN_ATTACKS_SQ_NEXT_TO_KING),
+ "pointing near enemy K");
+}
+
+static void
+_EvalKing(IN OUT POSITION *pos,
+ IN const COOR c,
+ IN const PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ COOR c,
+ PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ FLAG
+
+**/
+{
+ PIECE p;
+ ULONG uColor, ufColor;
+ COOR cSquare;
+ ULONG u, v;
+ ULONG uCounter;
+ BITV bvAttack;
+ BITV bvXray;
+ BITV bvDefend;
+ BITV bvPattern = 0;
+ ULONG uFlightSquares;
+ BITBOARD bb;
+ SCORE i;
+ SCORE iKingScore = 0;
+
+ static ULONG KingFlightDefects[9] =
+ {
+ +4, +2, +1, 0, 0, 0, 0, 0, 0
+ };
+
+ static ULONG KingStormingPawnDefects[8] =
+ {
+ +0, +4, +3, +1, +0, +0, +0, +0
+ };
+
+ static ULONG KingFileDefects[3] =
+ {
+ +2, +1, +0
+ };
+
+ static INT KingSafetyDeltas[11] =
+ {
+ -17, -16, -15,
+ -1, +1,
+ +15, +16, +17, -2, +2, 0
+ };
+
+ static ULONG EmptyAttackedSquare[2][11] =
+ {
+ {
+ 1, 1, 1,
+ 1, 1,
+ 2, 2, 2, 1, 1, 0
+ },
+ {
+ 2, 2, 2,
+ 1, 1,
+ 1, 1, 1, 1, 1, 0
+ },
+ };
+
+ static ULONG EmptyUnattackedSquare[2][11] =
+ {
+ {
+ 0, 0, 0,
+ 0, 0,
+ 1, 1, 1, 0, 0, 0
+ },
+ {
+ 1, 1, 1,
+ 0, 0,
+ 0, 0, 0, 0, 0, 0
+ },
+ };
+
+ static ULONG OccupiedAttackedSquare[2] =
+ {
+ 1,
+ 2
+ };
+
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(p && IS_KING(p));
+ uColor = GET_COLOR(p);
+ ufColor = FLIP(uColor);
+ ASSERT(IS_VALID_COLOR(uColor));
+
+ u = pos->uNonPawnMaterial[ufColor];
+ ASSERT(u >= VALUE_KING);
+ if (u < DO_KING_SAFETY_THRESHOLD)
+ {
+ u = 0;
+ ASSERT(g_iQKDeltas[u] != 0);
+ do
+ {
+ cSquare = c + g_iQKDeltas[u];
+ if (IS_ON_BOARD(cSquare))
+ {
+ pos->rgSquare[cSquare|8].bvAttacks[uColor].small.uKing = 1;
+ }
+ u++;
+ }
+ while(g_iQKDeltas[u] != 0);
+ goto skip_safety;
+ }
+
+ // Prepare to do king safety
+ uCounter = (u >= KEEP_KING_AT_HOME_THRESHOLD) *
+ KING_INITIAL_COUNTER_BY_LOCATION[uColor][c];
+#ifdef EVAL_DUMP
+ Trace("%s Initial KS Counter: %u\n", COLOR_NAME(uColor), uCounter);
+#endif
+
+ uCounter += pos->uPiecesPointingAtKing[uColor] / 2;
+#ifdef EVAL_DUMP
+ Trace("%s KS Counter after pieces pointing: %u\n",
+ COLOR_NAME(uColor), uCounter);
+#endif
+ uFlightSquares = 0;
+ u = 0;
+ ASSERT(KingSafetyDeltas[u] != 0);
+ do
+ {
+ cSquare = c + KingSafetyDeltas[u];
+ if (IS_ON_BOARD(cSquare))
+ {
+ p = pos->rgSquare[cSquare].pPiece;
+
+ cSquare |= 8;
+ bvAttack = pos->rgSquare[cSquare].bvAttacks[ufColor].uSmall;
+ bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray;
+ pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1;
+ bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall;
+
+ if (bvAttack != 0)
+ {
+ bvPattern |= (bvAttack | bvXray);
+ if (bvXray || (bvAttack & (bvAttack - 1)))
+ {
+ bvDefend &= ~8;
+ }
+ if (IS_EMPTY(p))
+ {
+ ASSERT(u < 11);
+ uCounter += EmptyAttackedSquare[uColor][u];
+ }
+ else
+ {
+ uCounter +=
+ OccupiedAttackedSquare[OPPOSITE_COLORS(p, uColor)];
+ }
+ uCounter += (bvDefend == 0);
+ }
+ else
+ {
+ uCounter += (bvXray != 0);
+ ASSERT(u < 11);
+ uCounter += EmptyUnattackedSquare[uColor][u];
+ ASSERT((IS_EMPTY(p) == 0) || (IS_EMPTY(p) == 1));
+ uFlightSquares += ((IS_EMPTY(p)) && (u < 8));
+ }
+ }
+ u++;
+ }
+ while(KingSafetyDeltas[u] != 0);
+#ifdef EVAL_DUMP
+ Trace("%s KS Counter post-squares: %u\n", COLOR_NAME(uColor), uCounter);
+#endif
+
+ if (IS_ON_BOARD(c - 1))
+ {
+ u = FILE(c - 1) + 1;
+ v = (pHash->uCountPerFile[WHITE][u] > 0) +
+ (pHash->uCountPerFile[BLACK][u] > 0);
+ ASSERT((v >= 0) && (v <= 2));
+
+ // Note: open rook files are worse than open interior files
+ uCounter += (KingFileDefects[v] + ((v < 2) && ((u == 1) || (u == 8))));
+
+ bb = pHash->bbPawnLocations[ufColor] & BBFILE[u - 1];
+ if (bb)
+ {
+ if (uColor == WHITE)
+ {
+ cSquare = CoorFromBitBoardRank1ToRank8(&bb);
+ }
+ else
+ {
+ cSquare = CoorFromBitBoardRank8ToRank1(&bb);
+ }
+ ASSERT(IS_ON_BOARD(cSquare));
+ uCounter += KingStormingPawnDefects[DISTANCE(cSquare, c)];
+ }
+ }
+
+ u = FILE(c) + 1;
+ v = (pHash->uCountPerFile[WHITE][u] > 0) +
+ (pHash->uCountPerFile[BLACK][u] > 0);
+ ASSERT((v >= 0) && (v <= 2));
+
+ // Note: open rook files are worse than open interior files
+ uCounter += (KingFileDefects[v] + ((v < 2) && ((u == 1) || (u == 8))));
+ bb = pHash->bbPawnLocations[ufColor] & BBFILE[u - 1];
+ if (bb)
+ {
+ if (uColor == WHITE)
+ {
+ cSquare = CoorFromBitBoardRank1ToRank8(&bb);
+ }
+ else
+ {
+ cSquare = CoorFromBitBoardRank8ToRank1(&bb);
+ }
+ ASSERT(IS_ON_BOARD(cSquare));
+ uCounter += KingStormingPawnDefects[DISTANCE(cSquare, c)];
+ }
+
+ if (IS_ON_BOARD(c + 1))
+ {
+ ASSERT((FILE(c + 1) + 1) == (u + 1));
+ u += 1;
+ v = (pHash->uCountPerFile[WHITE][u] > 0) +
+ (pHash->uCountPerFile[BLACK][u] > 0);
+ ASSERT((v >= 0) && (v <= 2));
+
+ // Note: open rook files are worse than open interior files
+ uCounter += (KingFileDefects[v] + ((v < 2) && ((u == 1) || (u == 8))));
+
+ bb = pHash->bbPawnLocations[ufColor] & BBFILE[u - 1];
+ if (bb)
+ {
+ if (uColor == WHITE)
+ {
+ cSquare = CoorFromBitBoardRank1ToRank8(&bb);
+ }
+ else
+ {
+ cSquare = CoorFromBitBoardRank8ToRank1(&bb);
+ }
+ ASSERT(IS_ON_BOARD(cSquare));
+ uCounter += KingStormingPawnDefects[DISTANCE(cSquare, c)];
+ }
+ }
+#ifdef EVAL_DUMP
+ Trace("%s KS Counter post-open file/stormers: %u\n", COLOR_NAME(uColor),
+ uCounter);
+#endif
+
+ bvPattern >>= 3;
+ ASSERT(bvPattern >= 0);
+ ASSERT(bvPattern < 32);
+ v = KING_COUNTER_BY_ATTACK_PATTERN[bvPattern];
+ uCounter += v;
+#ifdef EVAL_DUMP
+ Trace("%s KS Counter post-attack pattern: %u\n", COLOR_NAME(uColor),
+ uCounter);
+#endif
+
+ ASSERT(uFlightSquares <= 8);
+ uCounter += KingFlightDefects[uFlightSquares] * (v > 1);
+#ifdef EVAL_DUMP
+ Trace("%s KS Counter post-flight sq: %u\n", COLOR_NAME(uColor), uCounter);
+#endif
+
+ //
+ // Note: can't use pos->iReducedMaterialDownScaler here because we
+ // are scaling it based on the _other_ side's material.
+ //
+ uCounter = MINU(uCounter, 41);
+ i = KING_SAFETY_BY_COUNTER[uCounter];
+ i *= REDUCED_MATERIAL_DOWN_SCALER[pos->uArmyScaler[ufColor]];
+ i /= 8;
+ EVAL_TERM(uColor,
+ KING,
+ c,
+ iKingScore,
+ i,
+ "king safety");
+
+ //
+ // Bonus for castling / penalty for loss of castle. Also, if side has
+ // not yet castled, be concerned with undeveloped minor pieces.
+ //
+ if (FALSE == pos->fCastled[uColor])
+ {
+ if (pos->uNonPawnCount[uColor][0] > 4)
+ {
+ ASSERT(pos->uMinorsAtHome[uColor] <= 4);
+ EVAL_TERM(uColor,
+ 0,
+ ILLEGAL_COOR,
+ pos->iScore[uColor],
+ UNDEVELOPED_MINORS_IN_OPENING[pos->uMinorsAtHome[uColor]],
+ "development");
+ if (uColor == BLACK)
+ {
+ EVAL_TERM(BLACK,
+ KING,
+ c,
+ iKingScore,
+ (KING_MISSING_ONE_CASTLE_OPTION *
+ ((CASTLE_BLACK_SHORT & pos->bvCastleInfo) == 0)),
+ "can't castle short");
+ EVAL_TERM(BLACK,
+ KING,
+ c,
+ iKingScore,
+ (KING_MISSING_ONE_CASTLE_OPTION *
+ ((CASTLE_BLACK_LONG & pos->bvCastleInfo) == 0)),
+ "can't castle long");
+ }
+ else
+ {
+ ASSERT(uColor == WHITE);
+ EVAL_TERM(WHITE,
+ KING,
+ c,
+ iKingScore,
+ (KING_MISSING_ONE_CASTLE_OPTION *
+ ((CASTLE_WHITE_SHORT & pos->bvCastleInfo) == 0)),
+ "can't castle short");
+ EVAL_TERM(WHITE,
+ KING,
+ c,
+ iKingScore,
+ (KING_MISSING_ONE_CASTLE_OPTION *
+ ((CASTLE_WHITE_LONG & pos->bvCastleInfo) == 0)),
+ "can't castle long");
+ }
+ }
+ }
+
+ skip_safety:
+ //
+ // Special code for kings in the endgame
+ //
+ u = pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0];
+ if (u < 8)
+ {
+ //
+ // Encourage kings to come to the center
+ //
+ i = KING_TO_CENTER[c];
+ EVAL_TERM(uColor,
+ KING,
+ c,
+ iKingScore,
+ i,
+ "centralize king");
+
+ //
+ // Kings in front of passers in the late endgame are strong...
+ //
+ cSquare = FILE(c);
+ bb = pHash->bbPasserLocations[uColor] &
+ (BBADJACENT_FILES[cSquare] | BBFILE[cSquare]);
+ if (bb)
+ {
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ if (DISTANCE(cSquare, c) == 1)
+ {
+ ASSERT(abs((INT)FILE(c) - (INT)FILE(cSquare)) <= 1);
+ ASSERT(abs((INT)RANK(c) - (INT)RANK(cSquare)) <= 1);
+
+ i = 2;
+ i += SUPPORTED_PASSER_BY_RANK[uColor][RANK(cSquare)];
+ switch(uColor)
+ {
+ case WHITE:
+ i += 8 * (c < (cSquare - 1));
+ break;
+
+ case BLACK:
+ i += 8 * (c > (cSquare + 1));
+ break;
+ }
+ EVAL_TERM(uColor,
+ KING,
+ c,
+ iKingScore,
+ i,
+ "supporting own passer");
+ }
+ }
+ }
+
+ //
+ // Detect kings way out of the action in KP endgames:
+ //
+ // 8/8/1p6/p6k/P3K3/8/1P6/8 w - - 0 11
+ //
+ if ((u == 2) && (uColor == WHITE))
+ {
+ i = 0;
+ bb = (pHash->bbPawnLocations[WHITE] |
+ pHash->bbPawnLocations[BLACK]);
+ while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ i += (DISTANCE(cSquare, pos->cNonPawns[BLACK][0]) -
+ DISTANCE(cSquare, pos->cNonPawns[WHITE][0]));
+ }
+ EVAL_TERM(WHITE,
+ KING,
+ c,
+ iKingScore,
+ i * 12,
+ "in/out of \"action\"");
+ }
+ }
+ pos->iScore[uColor] += iKingScore;
+ pos->iTempScore = iKingScore;
+}
+
+
+FLAG
+EvalPasserRaces(IN OUT POSITION *pos,
+ IN PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+ Determine if one side has a pawn that is unstoppable.
+
+ This is broken for positions like:
+
+Parameters:
+
+ POSITION *pos,
+ PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ void
+
+**/
+{
+ BITBOARD bb[2];
+ ULONG uColor;
+ int d1;
+ COOR cQueen;
+ COOR cKing, c;
+ ULONG uKingDist, uPawnDist, uFriendDist;
+ ULONG uRacerDist[2] = { 99, 99 };
+ FLAG fDontCountMeOut[2] = { ((RANK(pos->cNonPawns[BLACK][0]) <= 3) &&
+ (pos->uPawnCount[BLACK] != 0)),
+ ((RANK(pos->cNonPawns[WHITE][0]) >= 6) &&
+ (pos->uPawnCount[WHITE] != 0)) };
+ bb[BLACK] = pHash->bbPasserLocations[BLACK];
+ bb[WHITE] = pHash->bbPasserLocations[WHITE];
+ if (!(bb[BLACK] | bb[WHITE]))
+ {
+ return(FALSE);
+ }
+ ASSERT((pos->uNonPawnCount[WHITE][0] == 1) ||
+ (pos->uNonPawnCount[BLACK][0] == 1))
+ FOREACH_COLOR(uColor)
+ {
+ if (pos->uNonPawnCount[FLIP(uColor)][0] == 1)
+ {
+ d1 = 16 * g_iAhead[uColor];
+ ASSERT((d1 * 2) == (32 * g_iAhead[uColor]));
+ if (bb[uColor])
+ {
+ while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(
+ &(bb[uColor]))))
+ {
+ // If the enemy king can take this passer and is
+ // on the move, all bets are off!
+ cKing = pos->cNonPawns[FLIP(uColor)][0];
+ ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) != uColor);
+ if (pos->uToMove == FLIP(uColor) &&
+ (DISTANCE(cKing, c) == 1))
+ {
+ // TODO: something clever here about protected pawns
+ continue;
+ }
+
+ cQueen = FILE_RANK_TO_COOR(FILE(c), QUEENING_RANK[uColor]);
+ ASSERT(FILE(cQueen) == FILE(c));
+ uPawnDist = RANK_DISTANCE(cQueen, c);
+ if ((RANK(c) == JUMPING_RANK[uColor]) &&
+ (IS_EMPTY(pos->rgSquare[c + d1].pPiece) &&
+ IS_EMPTY(pos->rgSquare[c + d1 * 2].pPiece)))
+ {
+ uPawnDist--;
+ }
+ ASSERT((uPawnDist >= 1) && (uPawnDist <= 6));
+
+ uKingDist = DISTANCE(cKing, cQueen);
+ if (uKingDist > 0)
+ {
+ uKingDist -= (pos->uToMove == FLIP(uColor));
+ }
+ ASSERT((uKingDist >= 0) && (uPawnDist <= 7));
+
+ if (uPawnDist < uKingDist)
+ {
+ uRacerDist[uColor] = MINU(uRacerDist[uColor],
+ uPawnDist);
+ }
+
+ cKing = pos->cNonPawns[uColor][0];
+ ASSERT(IS_KING(pos->rgSquare[cKing].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uColor);
+
+ uFriendDist = DISTANCE(cKing, cQueen);
+ if ((uFriendDist <= 1) && (DISTANCE(cKing, c) <= 1))
+ {
+ if (FILE(c) != FILE(cKing))
+ {
+ uRacerDist[uColor] = MINU(uRacerDist[uColor],
+ uPawnDist);
+ }
+ else if ((FILE(cKing) != A) &&
+ (FILE(cKing) != H))
+ {
+ uRacerDist[uColor] = MINU(uRacerDist[uColor],
+ (uPawnDist + 1));
+ }
+ }
+ fDontCountMeOut[uColor] = TRUE;
+
+ //
+ // TODO: winning connected passers
+ //
+ }
+ }
+ }
+ }
+
+ if ((uRacerDist[WHITE] < uRacerDist[BLACK]) &&
+ (FALSE == fDontCountMeOut[BLACK]))
+ {
+ EVAL_TERM(WHITE,
+ PAWN,
+ ILLEGAL_COOR,
+ pos->iScore[WHITE],
+ RACER_WINS_RACE - uRacerDist[WHITE] * 4,
+ "winning racer pawn");
+ return(TRUE);
+ }
+ else if ((uRacerDist[BLACK] < uRacerDist[WHITE]) &&
+ (FALSE == fDontCountMeOut[WHITE]))
+ {
+ EVAL_TERM(BLACK,
+ PAWN,
+ ILLEGAL_COOR,
+ pos->iScore[BLACK],
+ RACER_WINS_RACE - uRacerDist[BLACK] * 4,
+ "winning racer pawn");
+ return(TRUE);
+ }
+ return(FALSE);
+}
+
+
+static void
+_EvalPassers(IN OUT POSITION *pos,
+ IN PAWN_HASH_ENTRY *pHash)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+ PAWN_HASH_ENTRY *pHash,
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG u;
+ ULONG uColor;
+ COOR c;
+ COOR cSquare;
+ BITBOARD bb;
+ PIECE p;
+ SCORE i;
+ ULONG uNumPassers[2] = { 0, 0 };
+ int d1;
+
+ ASSERT(pHash->bbPasserLocations[WHITE] |
+ pHash->bbPasserLocations[BLACK]);
+
+ FOREACH_COLOR(uColor)
+ {
+ bb = pHash->bbPasserLocations[uColor];
+ if (bb != 0)
+ {
+ uNumPassers[uColor] = CountBits(bb);
+ ASSERT(uNumPassers[uColor] > 0);
+ d1 = 16 * g_iAhead[uColor];
+ ASSERT((d1 * 2) == (32 * g_iAhead[uColor]));
+
+ while(IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ //
+ // Consider the control of the square in front of the passer
+ //
+ cSquare = c + d1;
+ ASSERT(IS_ON_BOARD(cSquare));
+ u = _WhoControlsSquareFast(pos, cSquare);
+ if (u == FLIP(uColor))
+ {
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pos->iScore[uColor],
+ -((PASSER_BY_RANK[uColor][RANK(c)] / 4) +
+ (PASSER_BY_RANK[uColor][RANK(c)] / 16)),
+ "enemy controls sq ahead");
+ }
+ else if (u == (ULONG)-1)
+ {
+ p = pos->rgSquare[cSquare].pPiece;
+ if (!IS_EMPTY(p) && (OPPOSITE_COLORS(p, uColor)))
+ {
+ ASSERT(GET_COLOR(p) != uColor);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pos->iScore[uColor],
+ -(PASSER_BY_RANK[uColor][RANK(c)] / 4),
+ "enemy occupies sq ahead");
+ }
+ }
+#ifdef DEBUG
+ else
+ {
+ ASSERT(u == uColor);
+ }
+#endif
+
+ //
+ // Consider distance from friend king / enemy king
+ //
+ i = (8 - DISTANCE(c, pos->cNonPawns[uColor][0]));
+ if (((uColor == WHITE) &&
+ ((pos->cNonPawns[BLACK][0] >> 4) <= (c >> 4))) ||
+ ((uColor == BLACK) &&
+ ((pos->cNonPawns[WHITE][0] >> 4) >= (c >> 4)))) {
+ //
+ // The enemy king is ahead of the passer
+ //
+ i += FILE_DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]) * 4;
+ }
+ else
+ {
+ //
+ // The enemy king is behind the passer
+ //
+ i += 10 + DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]) * 4;
+ }
+ i *= PASSER_MATERIAL_UP_SCALER[pos->uArmyScaler[FLIP(uColor)]];
+ i /= 8;
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pos->iScore[uColor],
+ i,
+ "passer distance to kings");
+
+ //
+ // If a side is a piece down then the other side's passers
+ // are even more dangerous.
+ //
+ if (pos->uNonPawnCount[uColor][0] >
+ pos->uNonPawnCount[FLIP(uColor)][0])
+ {
+ i = (PASSER_BY_RANK[uColor][RANK(c)] / 2);
+ EVAL_TERM(uColor,
+ PAWN,
+ c,
+ pos->iScore[uColor],
+ i,
+ "passer and piece up");
+ }
+
+ //
+ // TODO: For connected passers vs KR, consult the oracle
+ //
+ }
+ }
+ }
+
+ //
+ // Game stage bonus
+ //
+ u = pos->uArmyScaler[BLACK];
+ EVAL_TERM(WHITE,
+ PAWN,
+ ILLEGAL_COOR,
+ pos->iScore[WHITE],
+ uNumPassers[WHITE] * PASSER_BONUS_AS_MATERIAL_COMES_OFF[u],
+ "passer value material");
+ ASSERT(PASSER_BONUS_AS_MATERIAL_COMES_OFF[u] >= 0);
+ u = pos->uArmyScaler[WHITE];
+ EVAL_TERM(BLACK,
+ PAWN,
+ ILLEGAL_COOR,
+ pos->iScore[BLACK],
+ uNumPassers[BLACK] * PASSER_BONUS_AS_MATERIAL_COMES_OFF[u],
+ "passer value material");
+ ASSERT(PASSER_BONUS_AS_MATERIAL_COMES_OFF[u] >= 0);
+}
+
+
+static void
+_EvalBadTrades(IN OUT POSITION *pos)
+/**
+
+Routine description:
+
+Parameters:
+
+ POSITION *pos,
+
+Return value:
+
+ void
+
+**/
+{
+ ULONG uAhead, uBehind;
+ ULONG uMagnitude;
+
+ //
+ // See who is ahead
+ //
+ ASSERT(pos->uNonPawnMaterial[WHITE] != pos->uNonPawnMaterial[BLACK]);
+ uAhead = (pos->uNonPawnMaterial[WHITE] > pos->uNonPawnMaterial[BLACK]);
+ uBehind = FLIP(uAhead);
+#ifdef DEBUG
+ if (pos->uNonPawnMaterial[WHITE] > pos->uNonPawnMaterial[BLACK])
+ {
+ ASSERT(uAhead == WHITE);
+ ASSERT(uBehind == BLACK);
+ }
+ else
+ {
+ ASSERT(pos->uNonPawnMaterial[BLACK] > pos->uNonPawnMaterial[WHITE]);
+ ASSERT(uAhead == BLACK);
+ ASSERT(uBehind == WHITE);
+ }
+#endif
+ uMagnitude = ((pos->uNonPawnMaterial[uAhead] +
+ pos->uNonPawnCount[uAhead][0] * 128) -
+ (pos->uNonPawnMaterial[uBehind] +
+ pos->uNonPawnCount[uBehind][0] * 128));
+ uMagnitude /= 128;
+ uMagnitude -= (uMagnitude != 0);
+ ASSERT(!(uMagnitude & 0x80000000));
+ uMagnitude = MINU(2, uMagnitude);
+ ASSERT(uMagnitude <= 2);
+
+ //
+ // Encourage the side that is ahead in piece material to continue
+ // trading pieces and not to trade pawns. This also has the
+ // effect of making the side that is behind in material want to
+ // trade pawns and not pieces. This also gives the side that is
+ // ahead a bit of a "bad trade" bonus. Note that the bonus is not
+ // given if the side "ahead" has no pawns; this is so that the
+ // engine will not like positions like KNB vs KRP - the side with
+ // the pawn has the winning chances.
+ //
+ EVAL_TERM(uAhead,
+ 0,
+ ILLEGAL_COOR,
+ pos->iScore[uAhead],
+ ((pos->uPawnCount[uAhead] != 0) *
+ TRADE_PIECES[uMagnitude][pos->uNonPawnCount[uBehind][0]]),
+ "trade pieces");
+ ASSERT(TRADE_PIECES[uMagnitude][pos->uNonPawnCount[uBehind][0]] > 0);
+ EVAL_TERM(uAhead,
+ 0,
+ ILLEGAL_COOR,
+ pos->iScore[uAhead],
+ DONT_TRADE_PAWNS[uMagnitude][pos->uPawnCount[uAhead]],
+ "don't trade pawns");
+}
+
+static void
+_EvalLookForDanger(IN OUT SEARCHER_THREAD_CONTEXT *ctx)
+/**
+
+Routine description:
+
+ This routine looks for squares on the board where the side to move
+ has a piece that is en prise. So far in eval we will have tagged
+ pieces in situations where they are attacked by lesser-valued
+ enemy pieces as "in danger". However, because of the order in
+ which the attack table was generated, we will have missed
+ situations where a piece is undefended (or underdefended) and
+ attacked by an enemy piece that is the same (or greater) value.
+
+ Note: This routine only considers side to move.
+
+ Also Note: this routine does not find pawns that are en prise, only
+ pieces.
+
+Parameters:
+
+ SEARCHER_THREAD_CONTEXT *ctx
+
+Return value:
+
+ void
+
+**/
+{
+ COOR c;
+ ULONG u;
+ POSITION *pos = &ctx->sPosition;
+ ULONG uSide = pos->uToMove;
+#ifdef DEBUG
+ PIECE p;
+#endif
+
+ for (u = 1;
+ u < pos->uNonPawnCount[uSide][0];
+ u++)
+ {
+ c = pos->cNonPawns[uSide][u];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(p);
+ ASSERT(GET_COLOR(p) == uSide);
+ ASSERT(!IS_PAWN(p));
+#endif
+ if (_WhoControlsSquareFast(pos, c) == FLIP(uSide))
+ {
+ StoreEnprisePiece(pos, c);
+ }
+ }
+}
+
+
+static void
+_EvalTrappedPieces(IN OUT SEARCHER_THREAD_CONTEXT *ctx)
+/**
+
+Routine description:
+
+Parameters:
+
+ SEARCHER_THREAD_CONTEXT *ctx
+
+Return value:
+
+ static void
+
+**/
+{
+ COOR c;
+ ULONG uColor;
+ POSITION *pos = &ctx->sPosition;
+#ifdef DEBUG
+ PIECE p;
+#endif
+
+ FOREACH_COLOR(uColor)
+ {
+ c = pos->cTrapped[uColor];
+ if (IS_ON_BOARD(c))
+ {
+ if (_WhoControlsSquareFast(pos, c) == FLIP(uColor))
+ {
+#ifdef DEBUG
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(p);
+ ASSERT(!IS_PAWN(p));
+ ASSERT(GET_COLOR(p) == uColor);
+#endif
+ if (OPPOSITE_COLORS(uColor, pos->uToMove))
+ {
+ StoreEnprisePiece(pos, c);
+ }
+ else
+ {
+ StoreTrappedPiece(pos, c);
+ }
+ }
+ }
+ }
+}
+
+
+static void
+_EvalBishopPairs(IN POSITION *pos)
+/*++
+
+Routine description:
+
+ Give a bonus to sides that have a viable bishop pair based; scale
+ the bonus by the number of pawns remaining on the board.
+
+Parameters:
+
+ IN POSITION *pos - position
+
+Return value:
+
+ static void
+
+--*/
+{
+ ULONG uPawnSum = pos->uPawnCount[WHITE] + pos->uPawnCount[BLACK];
+ FLAG fPair;
+ ULONG uBishopCount = pos->uNonPawnCount[BLACK][BISHOP];
+ ULONG uWhiteSqBishopCount = pos->uWhiteSqBishopCount[BLACK];
+ ASSERT(uPawnSum <= 16);
+ ASSERT(uBishopCount <= 10);
+ ASSERT(uWhiteSqBishopCount <= 10);
+ fPair = ((uBishopCount > 1) &
+ (uWhiteSqBishopCount != 0) &
+ (uWhiteSqBishopCount != uBishopCount));
+ EVAL_TERM(BLACK,
+ 0,
+ ILLEGAL_COOR,
+ pos->iScore[BLACK],
+ BISHOP_PAIR[fPair][uPawnSum],
+ "bishop pair");
+ uBishopCount = pos->uNonPawnCount[WHITE][BISHOP];
+ uWhiteSqBishopCount = pos->uWhiteSqBishopCount[WHITE];
+ ASSERT(uBishopCount <= 10);
+ ASSERT(uWhiteSqBishopCount <= 10);
+ fPair = ((uBishopCount > 1) &
+ (uWhiteSqBishopCount != 0) &
+ (uWhiteSqBishopCount != uBishopCount));
+ EVAL_TERM(WHITE,
+ 0,
+ ILLEGAL_COOR,
+ pos->iScore[WHITE],
+ BISHOP_PAIR[fPair][uPawnSum],
+ "bishop pair");
+}
+
+
+
+SCORE
+Eval(IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN SCORE iAlpha,
+ IN SCORE iBeta)
+/**
+
+Routine description:
+
+Parameters:
+
+ SEARCHER_THREAD_CONTEXT *ctx,
+ SCORE iAlpha,
+ SCORE iBeta,
+
+Return value:
+
+ SCORE
+
+**/
+{
+ static const PEVAL_HELPER JumpTable[] =
+ {
+ _InvalidEvaluator, // EMPTY_SQUARE (0)
+ _InvalidEvaluator, // INVALID_PIECE (1)
+ _InvalidEvaluator, // BLACK_PAWN (2)
+ _InvalidEvaluator, // WHITE_PAWN (3)
+ _EvalKnight, // BLACK_KNIGHT (4)
+ _EvalKnight, // WHITE_KNIGHT (5)
+ _EvalBishop, // BLACK_BISHOP (6)
+ _EvalBishop, // WHITE_BISHOP (7)
+ _InvalidEvaluator, // BLACK_ROOK (8)
+ _InvalidEvaluator, // WHITE_ROOK (9)
+ _InvalidEvaluator, // BLACK_QUEEN (10)
+ _InvalidEvaluator, // WHITE_QUEEN (11)
+ _InvalidEvaluator, // BLACK_KING (12)
+ _InvalidEvaluator // WHITE_KING (13)
+ };
+ ULONG uDefer[2][2];
+ COOR cDefer[2][2][10];
+ POSITION *pos = &(ctx->sPosition);
+ SCORE iScoreForSideToMove;
+ SCORE iAlphaMargin, iBetaMargin;
+ PAWN_HASH_ENTRY *pHash;
+ COOR c;
+ PIECE p;
+ ULONG u;
+ ULONG uColor;
+ BITBOARD bb;
+ FLAG fDeferred;
+#ifdef EVAL_TIME
+ UINT64 uTimer = SystemReadTimeStampCounter();
+#endif
+ ASSERT(IS_VALID_SCORE(iAlpha));
+ ASSERT(IS_VALID_SCORE(iBeta));
+ ASSERT(iAlpha < iBeta);
+ ASSERT((pos->iMaterialBalance[WHITE] * -1) ==
+ pos->iMaterialBalance[BLACK]);
+ // ASSERT(!InCheck(pos, pos->uToMove));
+
+ pos->uMinMobility[BLACK] = pos->uMinMobility[WHITE] = 100;
+ pos->cTrapped[BLACK] = pos->cTrapped[WHITE] = ILLEGAL_COOR;
+
+#ifdef EVAL_HASH
+ //
+ // Check the eval hash
+ //
+ iScoreForSideToMove = ProbeEvalHash(ctx);
+ if (iScoreForSideToMove != INVALID_SCORE)
+ {
+ INC(ctx->sCounters.tree.u64EvalHashHits);
+ goto end;
+ }
+#endif
+
+ pos->iScore[BLACK] =
+ (pos->uPawnMaterial[BLACK] + pos->uNonPawnMaterial[BLACK]);
+ pos->iScore[WHITE] =
+ (pos->uPawnMaterial[WHITE] + pos->uNonPawnMaterial[WHITE]);
+#ifdef EVAL_DUMP
+ EvalTraceClear();
+ Trace("Material:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+
+ //
+ // Pawn eval. Note: if fDeferred comes back as TRUE then we have
+ // neither cleared nor initialized the attack tables. This is
+ // because we hope that we can get a lazy eval cutoff here and
+ // save some time. BEFORE ANY CODE BELOW TOUCHES THE ATTACK
+ // TABLES IT NEEDS TO CLEAR/POPULATE THEM THOUGH!!!
+ //
+ pHash = _EvalPawns(ctx, &fDeferred);
+ ASSERT(NULL != pHash);
+ ASSERT(IS_VALID_FLAG(fDeferred));
+ pos->iScore[WHITE] += pHash->iScore[WHITE];
+ pos->iScore[BLACK] += pHash->iScore[BLACK];
+ ASSERT(IS_VALID_SCORE(pos->iScore[WHITE] - pos->iScore[BLACK]));
+#ifdef EVAL_DUMP
+ Trace("After pawns:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+
+ //
+ // Look for won passers races. Note: This code cannot trust the
+ // state of the attack tables yet!
+ //
+ if ((pos->uNonPawnCount[WHITE][0] == 1) ||
+ (pos->uNonPawnCount[BLACK][0] == 1))
+ {
+ (void)EvalPasserRaces(pos, pHash);
+#ifdef EVAL_DUMP
+ Trace("After passer races:\n%d\t\t%d\n", pos->iScore[WHITE],
+ pos->iScore[BLACK]);
+#endif
+ }
+
+ //
+ // Bad trade code. Right. Note: this code cannot trust the state
+ // of the attack tables...
+ //
+ if (pos->uNonPawnMaterial[WHITE] != pos->uNonPawnMaterial[BLACK])
+ {
+ _EvalBadTrades(pos);
+#ifdef EVAL_DUMP
+ Trace("After bad trades:\n%d\t\t%d\n", pos->iScore[WHITE],
+ pos->iScore[BLACK]);
+#endif
+ }
+
+ //
+ // Bishop pairs. Again... Note: this code cannot trust the state
+ // of the attack tables.
+ //
+ _EvalBishopPairs(pos);
+#ifdef EVAL_DUMP
+ Trace("After bishop pair bonus:\n%d\t\t%d\n", pos->iScore[WHITE],
+ pos->iScore[BLACK]);
+#endif
+
+#ifdef LAZY_EVAL
+ //
+ // Compute an estimate of the score for the side to move based on the
+ // eval terms we have already considered:
+ //
+ // 1. Material balance
+ // 2. Pawn structure bonuses/penalties (incl passers/candidates)
+ // 3. Passer races are detected already
+ // 4. "Bad trade" code has already run
+ // 5. We've already detected unwinnable endgames
+ // 6. Bishop pairs
+ //
+ iScoreForSideToMove = (pos->iScore[pos->uToMove] -
+ pos->iScore[FLIP(pos->uToMove)]);
+ ASSERT(IS_VALID_SCORE(iScoreForSideToMove));
+
+ //
+ // Eval has not considered several potentially large terms:
+ //
+ // 1. Piece positional components (such as mobility, trapped)
+ // 2. King safety penalties
+ // 3. Other miscellaneous bonuses/penalties
+ //
+ // We build two "margins" to account for these components of the
+ // score.
+ //
+ iAlphaMargin = iBetaMargin = (50 + (SCORE)ctx->uPositional);
+
+ //
+ // If (score + alpha_margin) is already > alpha -OR-
+ // (score - beta_margin) is already < beta
+ //
+ // ...then we can stop thinking about lazy eval; the rest of the
+ // computation only increases the margin so we know LE will fail
+ // and can save some work here.
+ //
+ if ((iScoreForSideToMove + iAlphaMargin < iAlpha) ||
+ (iScoreForSideToMove - iBetaMargin > iBeta))
+ {
+ //
+ // Ok, we can't say for sure that we won't take a lazy exit
+ // yet. So do the expensive part of lazy eval estimation and
+ // widen the margin further for king safety issues and passed
+ // pawns.
+ //
+ _QuicklyEstimateKingSafetyTerm(pos, &iAlphaMargin, &iBetaMargin);
+ _QuicklyEstimatePasserBonuses(pos, pHash, &iAlphaMargin, &iBetaMargin);
+
+ if ((iScoreForSideToMove + iAlphaMargin <= iAlpha) ||
+ (iScoreForSideToMove - iBetaMargin >= iBeta))
+ {
+ ctx->uPositional = MINU(200, ctx->uPositional);
+ INC(ctx->sCounters.tree.u64LazyEvals);
+ goto end;
+ }
+ }
+#endif
+
+ //
+ // If we have to clear/populate the attack table, do it now that
+ // we know we aren't taking a lazy exit.
+ //
+ if (TRUE == fDeferred)
+ {
+ _PopulatePawnAttackBits(pos);
+ }
+
+ //
+ // Pre-compute some common terms used in per-piece evals:
+ //
+ // This is a scaler based on the size of the army for each side.
+ //
+ pos->uArmyScaler[BLACK] = pos->uNonPawnMaterial[BLACK] - VALUE_KING;
+ pos->uArmyScaler[WHITE] = pos->uNonPawnMaterial[WHITE] - VALUE_KING;
+ pos->uArmyScaler[BLACK] /= VALUE_PAWN;
+ pos->uArmyScaler[WHITE] /= VALUE_PAWN;
+ ASSERT(!(pos->uArmyScaler[BLACK] & 0x80000000));
+ ASSERT(!(pos->uArmyScaler[WHITE] & 0x80000000));
+ pos->uArmyScaler[BLACK] = MINU(31, pos->uArmyScaler[BLACK]);
+ pos->uArmyScaler[WHITE] = MINU(31, pos->uArmyScaler[WHITE]);
+ ASSERT(pos->uArmyScaler[BLACK] >= 0);
+ ASSERT(pos->uArmyScaler[BLACK] <= 31);
+ ASSERT(pos->uArmyScaler[WHITE] >= 0);
+ ASSERT(pos->uArmyScaler[WHITE] <= 31);
+ pos->iReducedMaterialDownScaler[BLACK] =
+ pos->iReducedMaterialDownScaler[WHITE] = 0;
+
+ //
+ // This is a "position is closed|open" number.
+ //
+ pos->uClosedScaler = (pos->uPawnCount[WHITE] + pos->uPawnCount[BLACK]) -
+ (pHash->uNumUnmovedPawns[WHITE] + pHash->uNumUnmovedPawns[BLACK]) +
+ (pHash->uNumRammedPawns);
+ ASSERT(pos->uClosedScaler >= 0);
+ ASSERT(pos->uClosedScaler <= 32);
+
+ //
+ // Evaluate individual pieces.
+ //
+ pos->uMinorsAtHome[BLACK] = pos->uMinorsAtHome[WHITE] = 0;
+ uDefer[BLACK][0] = uDefer[BLACK][1] =
+ uDefer[WHITE][0] = uDefer[WHITE][1] = 0;
+ uColor = pos->uToMove;
+ for (u = 1; // skips the king
+ u < pos->uNonPawnCount[uColor][0];
+ u++)
+ {
+ c = pos->cNonPawns[uColor][u];
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(IS_VALID_PIECE(p));
+ ASSERT(GET_COLOR(p) == uColor);
+ ASSERT(!IS_PAWN(p) && !IS_KING(p));
+
+ if (p & 0x4)
+ {
+ ASSERT(IS_BISHOP(p) || IS_KNIGHT(p));
+
+ //
+ // Note: The side on move's pieces of value X are
+ // evaluated and have their mobility counted and added to
+ // the attack table before the other side's pieces. Also,
+ // we do not care if JumpTable tells us this piece is in
+ // danger b/c we will find out later on in
+ // EvalLookForDanger anyway.
+ //
+ ASSERT(JumpTable[p]);
+ (void)(JumpTable[p])(pos, c, pHash);
+#ifdef EVAL_DUMP
+ Trace("After %s:\n%d\t\t%d\n", PieceAbbrev(p),
+ pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+ }
+ else
+ {
+ ASSERT(IS_ROOK(p) || IS_QUEEN(p));
+ cDefer[uColor][IS_QUEEN(p)][uDefer[uColor][IS_QUEEN(p)]++] = c;
+ }
+ }
+
+ uColor = FLIP(uColor);
+ ASSERT(uColor != pos->uToMove);
+ for (u = 1;
+ u < pos->uNonPawnCount[uColor][0];
+ u++)
+ {
+ c = pos->cNonPawns[uColor][u];
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(IS_VALID_PIECE(p));
+ ASSERT(GET_COLOR(p) == uColor);
+ ASSERT(!IS_PAWN(p) && !IS_KING(p));
+ if (p & 0x4)
+ {
+ //
+ // This time we are evaluating a piece from the side not
+ // on move. We will not go back later and look at this
+ // piece again so if JumpTable tells us it's in danger
+ // then record it. The drawback, of course, is that the
+ // JumpTable routines only detect danger if a piece is
+ // attacked by an enemy of lesser value!
+ //
+ ASSERT(JumpTable[p]);
+ (void)(JumpTable[p])(pos, c, pHash);
+#ifdef EVAL_DUMP
+ Trace("After %s:\n%d\t\t%d\n", PieceAbbrev(p),
+ pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+ }
+ else
+ {
+ ASSERT(IS_ROOK(p) || IS_QUEEN(p));
+ cDefer[uColor][IS_QUEEN(p)][uDefer[uColor][IS_QUEEN(p)]++] = c;
+ }
+ }
+
+ //
+ // Evaluate any rook(s) for side on move then for side not on move.
+ //
+ uColor = FLIP(uColor);
+ for (u = 0;
+ u < uDefer[uColor][0];
+ u++)
+ {
+ c = cDefer[uColor][0][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ROOK(pos->rgSquare[c].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == uColor);
+ _EvalRook(pos, c, pHash);
+#ifdef EVAL_DUMP
+ p = BLACK_ROOK | uColor;
+ Trace("After %s:\n%d\t\t%d\n", PieceAbbrev(p),
+ pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+ }
+
+ uColor = FLIP(uColor);
+ for (u = 0;
+ u < uDefer[uColor][0];
+ u++)
+ {
+ c = cDefer[uColor][0][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_ROOK(pos->rgSquare[c].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == uColor);
+ _EvalRook(pos, c, pHash);
+#ifdef EVAL_DUMP
+ p = BLACK_ROOK | uColor;
+ Trace("After %s:\n%d\t\t%d\n", PieceAbbrev(p),
+ pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+ }
+
+ //
+ // Evaluate any queen(s) for side on move then for side not on move.
+ //
+ uColor = FLIP(uColor);
+ for (u = 0;
+ u < uDefer[uColor][1];
+ u++)
+ {
+ c = cDefer[uColor][1][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_QUEEN(pos->rgSquare[c].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == uColor);
+ _EvalQueen(pos, c, pHash);
+#ifdef EVAL_DUMP
+ p = BLACK_ROOK | uColor;
+ Trace("After %s:\n%d\t\t%d\n", PieceAbbrev(p),
+ pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+ }
+
+ uColor = FLIP(uColor);
+ for (u = 0;
+ u < uDefer[uColor][1];
+ u++)
+ {
+ c = cDefer[uColor][1][u];
+ ASSERT(IS_ON_BOARD(c));
+ ASSERT(IS_QUEEN(pos->rgSquare[c].pPiece));
+ ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == uColor);
+ _EvalQueen(pos, c, pHash);
+#ifdef EVAL_DUMP
+ p = BLACK_ROOK | uColor;
+ Trace("After %s:\n%d\t\t%d\n", PieceAbbrev(p),
+ pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+ }
+
+ //
+ // Evaluate the two kings last.
+ //
+ c = pos->cNonPawns[BLACK][0];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(IS_VALID_PIECE(p));
+ ASSERT(GET_COLOR(p) == BLACK);
+ ASSERT(IS_KING(p));
+ ASSERT(JumpTable[p]);
+#endif
+ _EvalKing(pos, c, pHash);
+ ctx->sPlyInfo[ctx->uPly].iKingScore[BLACK] = pos->iTempScore;
+#ifdef EVAL_DUMP
+ Trace("After *k:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+
+ c = pos->cNonPawns[WHITE][0];
+#ifdef DEBUG
+ ASSERT(IS_ON_BOARD(c));
+ p = pos->rgSquare[c].pPiece;
+ ASSERT(IS_VALID_PIECE(p));
+ ASSERT(GET_COLOR(p) == WHITE);
+ ASSERT(IS_KING(p));
+ ASSERT(JumpTable[p]);
+#endif
+ _EvalKing(pos, c, pHash);
+ ctx->sPlyInfo[ctx->uPly].iKingScore[WHITE] = pos->iTempScore;
+#ifdef EVAL_DUMP
+ Trace("After .k:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+
+ //
+ // Now that we have the whole attack table generated, think about
+ // passed pawns identified by the pawn eval routine again. Also
+ // see if the side not on move has a trapped piece.
+ //
+ bb = (pHash->bbPasserLocations[WHITE] | pHash->bbPasserLocations[BLACK]);
+ if (0 != bb)
+ {
+ _EvalPassers(pos, pHash);
+#ifdef EVAL_DUMP
+ Trace("After passers:\n%d\t\t%d\n", pos->iScore[WHITE],
+ pos->iScore[BLACK]);
+#endif
+ }
+
+ //
+ // Make one more pass over the piece list for the side on move now
+ // that the full attack table is computed to detect
+ // under/unprotected pieces en prise to more valuable enemy pieces
+ // which we missed when building the attack table incrementally.
+ //
+ ctx->sPlyInfo[ctx->uPly].uMinMobility[BLACK] = pos->uMinMobility[BLACK];
+ ctx->sPlyInfo[ctx->uPly].uMinMobility[WHITE] = pos->uMinMobility[WHITE];
+ _EvalLookForDanger(ctx);
+ _EvalTrappedPieces(ctx);
+
+ //
+ // B over N in the endgame with 2 pawn wings.
+ //
+ if ((pos->uNonPawnCount[WHITE][0] <= 2) &&
+ (pos->uNonPawnCount[BLACK][0] <= 2))
+ {
+ if ((pos->uNonPawnCount[WHITE][BISHOP] > 0) &&
+ (pos->uNonPawnCount[BLACK][BISHOP] > 0))
+ {
+ if ((pos->uNonPawnCount[BLACK][0] == 2) &&
+ (pos->uNonPawnCount[WHITE][0] == 2))
+ {
+ ASSERT(pos->uNonPawnCount[BLACK][BISHOP] == 1);
+ ASSERT(pos->uNonPawnCount[WHITE][BISHOP] == 1);
+ if (pos->uWhiteSqBishopCount[BLACK] +
+ pos->uWhiteSqBishopCount[WHITE] == 1)
+ {
+ pos->iScore[WHITE] /= 2;
+ pos->iScore[BLACK] /= 2;
+ }
+ }
+ }
+ else
+ {
+ //
+ // At least one side has no bishop. Look for positions
+ // with two pawn wings where having a bishop is an
+ // advantage.
+ //
+ bb = (pHash->bbPawnLocations[WHITE] |
+ pHash->bbPawnLocations[BLACK]);
+ ASSERT((BBFILE[A] | BBFILE[B] | BBFILE[C]) ==
+ 0x0707070707070707ULL);
+ ASSERT((BBFILE[F] | BBFILE[G] | BBFILE[H]) ==
+ 0xe0e0e0e0e0e0e0e0ULL);
+ if ((bb & 0x0707070707070707ULL) &&
+ (bb & 0xe0e0e0e0e0e0e0e0ULL))
+ {
+ if ((pos->uNonPawnCount[BLACK][BISHOP] == 0) &&
+ (pos->uNonPawnCount[WHITE][BISHOP] > 0))
+ {
+ EVAL_TERM(WHITE,
+ BISHOP,
+ 0x88,
+ pos->iScore[WHITE],
+ BISHOP_OVER_KNIGHT_IN_ENDGAME *
+ pos->uNonPawnCount[WHITE][BISHOP],
+ "endgame w/ 2 pawn flanks");
+ }
+ else if ((pos->uNonPawnCount[BLACK][BISHOP] > 0) &&
+ (pos->uNonPawnCount[WHITE][BISHOP] == 0))
+ {
+ EVAL_TERM(BLACK,
+ BISHOP,
+ 0x88,
+ pos->iScore[BLACK],
+ BISHOP_OVER_KNIGHT_IN_ENDGAME *
+ pos->uNonPawnCount[BLACK][BISHOP],
+ "endgame w/ 2 pawn flanks");
+ }
+ }
+ }
+#ifdef EVAL_DUMP
+ Trace("After BOOC / BvsN:\n%d\t\t%d\n",
+ pos->iScore[WHITE], pos->iScore[BLACK]);
+#endif
+ }
+
+ //
+ // Roll in the reduced material down scaler terms.
+ //
+ ASSERT(pos->iReducedMaterialDownScaler[BLACK] > -200);
+ ASSERT(pos->iReducedMaterialDownScaler[BLACK] < +200);
+ iAlphaMargin = (pos->iReducedMaterialDownScaler[BLACK] *
+ (SCORE)REDUCED_MATERIAL_DOWN_SCALER[pos->uArmyScaler[BLACK]]) / 8;
+ pos->iScore[BLACK] += iAlphaMargin;
+ ASSERT(pos->iReducedMaterialDownScaler[WHITE] > -200);
+ ASSERT(pos->iReducedMaterialDownScaler[WHITE] < +200);
+ iAlphaMargin = (pos->iReducedMaterialDownScaler[WHITE] *
+ (SCORE)REDUCED_MATERIAL_DOWN_SCALER[pos->uArmyScaler[WHITE]]) / 8;
+ pos->iScore[WHITE] += iAlphaMargin;
+
+ //
+ // Almost done
+ //
+ iScoreForSideToMove = (pos->iScore[pos->uToMove] -
+ pos->iScore[FLIP(pos->uToMove)]);
+#ifdef EVAL_DUMP
+ Trace("At the end:\n%d\t\t%d\n", pos->iScore[WHITE],
+ pos->iScore[BLACK]);
+#endif
+
+ //
+ // TODO: detect and discourage blocked positions?
+ //
+
+ //
+ // TODO: drive the score towards zero as we approach a 50 move w/o
+ // progress draw.
+ //
+
+ //
+ // Adjust dynamic positional component.
+ //
+ iAlphaMargin = abs(pos->iMaterialBalance[pos->uToMove] - iScoreForSideToMove);
+ if ((ULONG)iAlphaMargin > ctx->uPositional)
+ {
+ ctx->uPositional = (ULONG)iAlphaMargin;
+ }
+ else
+ {
+ ctx->uPositional -= (ctx->uPositional - (ULONG)iAlphaMargin) / 4;
+ ctx->uPositional = MAXU(ctx->uPositional, VALUE_PAWN);
+ }
+ g_Options.iLastEvalScore = iScoreForSideToMove;
+
+#ifdef EVAL_TIME
+ ctx->sCounters.tree.u64CyclesInEval += (SystemReadTimeStampCounter() -
+ uTimer);
+#endif
+ INC(ctx->sCounters.tree.u64FullEvals);
+
+#ifdef EVAL_HASH
+ //
+ // Store in eval hash.
+ //
+ StoreEvalHash(ctx, iScoreForSideToMove);
+#endif
+
+ end:
+ ASSERT(IS_VALID_SCORE(iScoreForSideToMove));
+ ASSERT((iScoreForSideToMove > -NMATE) &&
+ (iScoreForSideToMove < +NMATE));
+ ASSERT(abs(ctx->sPlyInfo[ctx->uPly].iKingScore[BLACK]) < 700);
+ ASSERT(abs(ctx->sPlyInfo[ctx->uPly].iKingScore[WHITE]) < 700);
+ ASSERT(ctx->sPlyInfo[ctx->uPly].uMinMobility[BLACK] <= 100);
+ ASSERT(ctx->sPlyInfo[ctx->uPly].uMinMobility[WHITE] <= 100);
+ ASSERT((ctx->uPositional < 2500) && (ctx->uPositional >= VALUE_PAWN));
+ return(iScoreForSideToMove);
+}