/*
- Glaurung, a UCI chess playing engine.
- Copyright (C) 2004-2008 Tord Romstad
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008 Marco Costalba
- Glaurung is free software: you can redistribute it and/or modify
+ Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
- Glaurung is distributed in the hope that it will be useful,
+ Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
int WeightPassedPawnsMidgame = 0x100;
int WeightPassedPawnsEndgame = 0x100;
int WeightKingSafety[2] = { 0x100, 0x100 };
+ int WeightSpace;
// Internal evaluation weights. These are applied on top of the evaluation
// weights read from UCI parameters. The purpose is to be able to change
const int WeightPassedPawnsEndgameInternal = 0x100;
const int WeightKingSafetyInternal = 0x100;
const int WeightKingOppSafetyInternal = 0x100;
+ const int WeightSpaceInternal = 0x30;
// Visually better to define tables constants
typedef Value V;
((1ULL << SQ_A8) | (1ULL << SQ_H8))
};
+ // The SpaceMask[color] contains area of the board which is consdered by
+ // the space evaluation. In the middle game, each side is given a bonus
+ // based on how many squares inside this area are safe and available for
+ // friendly minor pieces.
+ const Bitboard SpaceMask[2] = {
+ (1ULL<<SQ_C2) | (1ULL<<SQ_D2) | (1ULL<<SQ_E2) | (1ULL<<SQ_F2) |
+ (1ULL<<SQ_C3) | (1ULL<<SQ_D3) | (1ULL<<SQ_E3) | (1ULL<<SQ_F3) |
+ (1ULL<<SQ_C4) | (1ULL<<SQ_D4) | (1ULL<<SQ_E4) | (1ULL<<SQ_F4),
+ (1ULL<<SQ_C7) | (1ULL<<SQ_D7) | (1ULL<<SQ_E7) | (1ULL<<SQ_F7) |
+ (1ULL<<SQ_C6) | (1ULL<<SQ_D6) | (1ULL<<SQ_E6) | (1ULL<<SQ_F6) |
+ (1ULL<<SQ_C5) | (1ULL<<SQ_D5) | (1ULL<<SQ_E5) | (1ULL<<SQ_F5)
+ };
+
/// King safety constants and variables. The king safety scores are taken
/// from the array SafetyTable[]. Various little "meta-bonuses" measuring
/// the strength of the attack are added up into an integer, which is used
// in init_safety().
Value SafetyTable[100];
- // Pawn and material hash tables, indexed by the current thread id:
+ // Pawn and material hash tables, indexed by the current thread id
PawnInfoTable *PawnTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
MaterialInfoTable *MaterialTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
- // Sizes of pawn and material hash tables:
+ // Sizes of pawn and material hash tables
const int PawnTableSize = 16384;
const int MaterialTableSize = 1024;
// Array which gives the number of nonzero bits in an 8-bit integer:
uint8_t BitCount8Bit[256];
- // Function prototypes:
+ // Function prototypes
void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei);
void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei);
void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei);
void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us,
EvalInfo &ei);
+ void evaluate_space(const Position &p, Color us, EvalInfo &ei);
inline Value apply_weight(Value v, int w);
Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]);
for (Color c = WHITE; c <= BLACK; c++)
{
// Knights
- for (int i = 0; i < pos.knight_count(c); i++)
- evaluate_knight(pos, pos.knight_list(c, i), c, ei);
+ for (int i = 0; i < pos.piece_count(c, KNIGHT); i++)
+ evaluate_knight(pos, pos.piece_list(c, KNIGHT, i), c, ei);
// Bishops
- for (int i = 0; i < pos.bishop_count(c); i++)
- evaluate_bishop(pos, pos.bishop_list(c, i), c, ei);
+ for (int i = 0; i < pos.piece_count(c, BISHOP); i++)
+ evaluate_bishop(pos, pos.piece_list(c, BISHOP, i), c, ei);
// Rooks
- for (int i = 0; i < pos.rook_count(c); i++)
- evaluate_rook(pos, pos.rook_list(c, i), c, ei);
+ for (int i = 0; i < pos.piece_count(c, ROOK); i++)
+ evaluate_rook(pos, pos.piece_list(c, ROOK, i), c, ei);
// Queens
- for(int i = 0; i < pos.queen_count(c); i++)
- evaluate_queen(pos, pos.queen_list(c, i), c, ei);
+ for(int i = 0; i < pos.piece_count(c, QUEEN); i++)
+ evaluate_queen(pos, pos.piece_list(c, QUEEN, i), c, ei);
// Special pattern: trapped bishops on a7/h7/a2/h2
Bitboard b = pos.bishops(c) & MaskA7H7[c];
ei.mgValue += ei.pi->kingside_storm_value(WHITE)
- ei.pi->queenside_storm_value(BLACK);
+
+ // Evaluate space for both sides
+ if (ei.mi->space_weight() > 0)
+ {
+ evaluate_space(pos, WHITE, ei);
+ evaluate_space(pos, BLACK, ei);
+ }
}
// Mobility
ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame);
// If we don't already have an unusual scale factor, check for opposite
- // colored bishop endgames, and use a lower scale for those:
+ // colored bishop endgames, and use a lower scale for those
if ( phase < PHASE_MIDGAME
&& pos.opposite_colored_bishops()
&& ( (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0))
{
// Check for KBP vs KB with only a single pawn that is almost
// certainly a draw or at least two pawns.
- bool one_pawn = (pos.pawn_count(WHITE) + pos.pawn_count(BLACK) == 1);
+ bool one_pawn = (pos.piece_count(WHITE, PAWN) + pos.piece_count(BLACK, PAWN) == 1);
sf = one_pawn ? ScaleFactor(8) : ScaleFactor(32);
}
else
WeightKingSafety[us] = weight_option("Cowardice", WeightKingSafetyInternal);
WeightKingSafety[them] = weight_option("Aggressiveness", WeightKingOppSafetyInternal);
+ WeightSpace = weight_option("Space", WeightSpaceInternal);
init_safety();
}
if (v && (p.pawn_attacks(them, s) & p.pawns(us)))
{
bonus += v / 2;
- if ( p.knight_count(them) == 0
+ if ( p.piece_count(them, KNIGHT) == 0
&& (SquaresByColorBB[square_color(s)] & p.bishops(them)) == EmptyBoardBB)
bonus += v;
}
void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei) {
- //Bitboard b = p.rook_attacks(s);
Bitboard b = rook_attacks_bb(s, p.occupied_squares() & ~p.rooks_and_queens(us));
ei.attackedBy[us][ROOK] |= b;
// from optimally tuned.
Color them = opposite_color(us);
- if ( p.queen_count(them) >= 1
+ if ( p.piece_count(them, QUEEN) >= 1
&& ei.kingAttackersCount[them] >= 2
&& p.non_pawn_material(them) >= QueenValueMidgame + RookValueMidgame
&& ei.kingAdjacentZoneAttacksCount[them])
Bitboard occ = p.occupied_squares(), b, b2;
// Initialize the 'attackUnits' variable, which is used later on as an
- // index to the SafetyTable[] array. The initial is based on the number
- // and types of the attacking pieces, the number of attacked and
+ // index to the SafetyTable[] array. The initial value is based on the
+ // number and types of the attacking pieces, the number of attacked and
// undefended squares around the king, the square of the king, and the
// quality of the pawn shelter.
int attackUnits =
Min((ei.kingAttackersCount[them] * ei.kingAttackersWeight[them]) / 2, 25)
+ (ei.kingAdjacentZoneAttacksCount[them] + count_1s_max_15(undefended)) * 3
- + InitKingDanger[relative_square(us, s)] - shelter / 32;
+ + InitKingDanger[relative_square(us, s)] - (shelter >> 5);
// Analyse safe queen contact checks
b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them);
while (b)
{
Square from, to = pop_1st_bit(&b);
- if (!(escapeSquares & ~queen_attacks_bb(to, occ & clear_mask_bb(s))))
+ if (!(escapeSquares & ~queen_attacks_bb(to, occ & ClearMaskBB[s])))
{
// We have a mate, unless the queen is pinned or there
// is an X-ray attack through the queen.
- for (int i = 0; i < p.queen_count(them); i++)
+ for (int i = 0; i < p.piece_count(them, QUEEN); i++)
{
- from = p.queen_list(them, i);
+ from = p.piece_list(them, QUEEN, i);
if ( bit_is_set(p.piece_attacks<QUEEN>(from), to)
&& !bit_is_set(p.pinned_pieces(them), from)
- && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) & p.rooks_and_queens(us))
- && !(rook_attacks_bb(to, occ & clear_mask_bb(from)) & p.rooks_and_queens(us)))
+ && !(rook_attacks_bb(to, occ & ClearMaskBB[from]) & p.rooks_and_queens(us))
+ && !(rook_attacks_bb(to, occ & ClearMaskBB[from]) & p.rooks_and_queens(us)))
ei.mateThreat[them] = make_move(from, to);
}
// value if the other side has a rook or queen.
if(square_file(s) == FILE_A || square_file(s) == FILE_H) {
if(pos.non_pawn_material(them) == KnightValueMidgame
- && pos.knight_count(them) == 1)
+ && pos.piece_count(them, KNIGHT) == 1)
ebonus += ebonus / 4;
else if(pos.rooks_and_queens(them))
ebonus -= ebonus / 4;
}
- // apply_weight applies an evaluation weight to a value.
+ // evaluate_space() computes the space evaluation for a given side. The
+ // space evaluation is a simple bonus based on the number of safe squares
+ // available for minor pieces on the central four files on ranks 2--4. Safe
+ // squares one, two or three squares behind a friendly pawn are counted
+ // twice. Finally, the space bonus is scaled by a weight taken from the
+ // material hash table.
+
+ void evaluate_space(const Position &pos, Color us, EvalInfo &ei) {
+
+ Color them = opposite_color(us);
+
+ // Find the safe squares for our pieces inside the area defined by
+ // SpaceMask[us]. A square is unsafe it is attacked by an enemy
+ // pawn, or if it is undefended and attacked by an enemy piece.
+
+ Bitboard safeSquares =
+ SpaceMask[us] & ~pos.pawns(us) & ~ei.attacked_by(them, PAWN)
+ & ~(~ei.attacked_by(us) & ei.attacked_by(them));
+
+ // Find all squares which are at most three squares behind some friendly
+ // pawn.
+ Bitboard behindFriendlyPawns = pos.pawns(us);
+ if(us == WHITE) {
+ behindFriendlyPawns |= (behindFriendlyPawns >> 8);
+ behindFriendlyPawns |= (behindFriendlyPawns >> 16);
+ }
+ else {
+ behindFriendlyPawns |= (behindFriendlyPawns << 8);
+ behindFriendlyPawns |= (behindFriendlyPawns << 16);
+ }
+
+ int space =
+ count_1s_max_15(safeSquares)
+ + count_1s_max_15(behindFriendlyPawns & safeSquares);
+
+ ei.mgValue += Sign[us] *
+ apply_weight(Value(space * ei.mi->space_weight()), WeightSpace);
+ }
+
+
+ // apply_weight() applies an evaluation weight to a value
inline Value apply_weight(Value v, int w) {
return (v*w) / 0x100;
}
- // scale_by_game_phase interpolates between a middle game and an endgame
+ // scale_by_game_phase() interpolates between a middle game and an endgame
// score, based on game phase. It also scales the return value by a
// ScaleFactor array.
ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
- // Linearized sigmoid interpolator
- int sph = int(ph);
- sph -= (64 - sph) / 4;
- sph = Min(PHASE_MIDGAME, Max(PHASE_ENDGAME, sph));
-
- Value result = Value(int((mv * sph + ev * (128 - sph)) / 128));
-
+ Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
return Value(int(result) & ~(GrainSize - 1));
}