1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-04-30 00:33:09 +00:00

Introduce Score struct

Save mid and end game scores in an union so to
operate on both values in one instruction.

This patch just introduces the infrastructure and changes
EvalInfo to use a single Score value instead of mgValue
and egValue.

Speed is more or less the same because we still don't use
unified midgame-endgame tables where the single assignment
optimization can prove effective.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
This commit is contained in:
Marco Costalba 2009-11-07 11:15:55 +01:00
parent 2f5ee9e4e8
commit 06f06a9be8
3 changed files with 85 additions and 67 deletions

View file

@ -320,7 +320,7 @@ namespace {
void evaluate_trapped_bishop_a7h7(const Position& pos, Square s, Color us, EvalInfo& ei);
void evaluate_trapped_bishop_a1h1(const Position& pos, Square s, 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[]);
Value scale_by_game_phase(const Score& v, Phase ph, const ScaleFactor sf[]);
int weight_option(const std::string& opt, int weight);
void init_safety();
}
@ -352,13 +352,11 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
// Initialize by reading the incrementally updated scores included in the
// position object (material + piece square tables)
ei.mgValue = pos.mg_value();
ei.egValue = pos.eg_value();
ei.value = Score(pos.mg_value(), pos.eg_value());
// Probe the material hash table
ei.mi = MaterialTable[threadID]->get_material_info(pos);
ei.mgValue += ei.mi->material_value();
ei.egValue += ei.mi->material_value();
ei.value += Score(ei.mi->material_value(), ei.mi->material_value());
// If we have a specialized evaluation function for the current material
// configuration, call it and return
@ -372,8 +370,8 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
// Probe the pawn hash table
ei.pi = PawnTable[threadID]->get_pawn_info(pos);
ei.mgValue += apply_weight(ei.pi->mg_value(), WeightPawnStructureMidgame);
ei.egValue += apply_weight(ei.pi->eg_value(), WeightPawnStructureEndgame);
ei.value += Score(apply_weight(ei.pi->mg_value(), WeightPawnStructureMidgame),
apply_weight(ei.pi->eg_value(), WeightPawnStructureEndgame));
// Initialize king attack bitboards and king attack zones for both sides
ei.attackedBy[WHITE][KING] = pos.attacks_from<KING>(pos.king_square(WHITE));
@ -422,14 +420,12 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
if ( square_file(pos.king_square(WHITE)) >= FILE_E
&& square_file(pos.king_square(BLACK)) <= FILE_D)
ei.mgValue += ei.pi->queenside_storm_value(WHITE)
- ei.pi->kingside_storm_value(BLACK);
ei.value += Score(ei.pi->queenside_storm_value(WHITE) - ei.pi->kingside_storm_value(BLACK), 0);
else if ( square_file(pos.king_square(WHITE)) <= FILE_D
&& square_file(pos.king_square(BLACK)) >= FILE_E)
ei.mgValue += ei.pi->kingside_storm_value(WHITE)
- ei.pi->queenside_storm_value(BLACK);
ei.value += Score(ei.pi->kingside_storm_value(WHITE) - ei.pi->queenside_storm_value(BLACK), 0);
// Evaluate space for both sides
if (ei.mi->space_weight() > 0)
@ -440,15 +436,15 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
}
// Mobility
ei.mgValue += apply_weight(ei.mgMobility, WeightMobilityMidgame);
ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame);
ei.value += Score(apply_weight(ei.mgMobility, WeightMobilityMidgame),
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
if ( phase < PHASE_MIDGAME
&& pos.opposite_colored_bishops()
&& ( (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0))
|| (factor[BLACK] == SCALE_FACTOR_NORMAL && ei.egValue < Value(0))))
&& ( (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.value.eg() > Value(0))
|| (factor[BLACK] == SCALE_FACTOR_NORMAL && ei.value.eg() < Value(0))))
{
ScaleFactor sf;
@ -475,7 +471,7 @@ Value do_evaluate(const Position& pos, EvalInfo& ei, int threadID) {
// Interpolate between the middle game and the endgame score
Color stm = pos.side_to_move();
Value v = Sign[stm] * scale_by_game_phase(ei.mgValue, ei.egValue, phase, factor);
Value v = Sign[stm] * scale_by_game_phase(ei.value, phase, factor);
return (ei.mateThreat[stm] == MOVE_NONE ? v : 8 * QueenValueMidgame - v);
}
@ -493,12 +489,11 @@ Value quick_evaluate(const Position &pos) {
static const
ScaleFactor sf[2] = {SCALE_FACTOR_NORMAL, SCALE_FACTOR_NORMAL};
Value mgv = pos.mg_value();
Value egv = pos.eg_value();
Score v = Score(pos.mg_value(), pos.eg_value());
Phase ph = pos.game_phase();
Color stm = pos.side_to_move();
return Sign[stm] * scale_by_game_phase(mgv, egv, ph, sf);
return Sign[stm] * scale_by_game_phase(v, ph, sf);
}
@ -623,8 +618,7 @@ namespace {
else
bonus += bonus / 2;
}
ei.mgValue += Sign[Us] * bonus;
ei.egValue += Sign[Us] * bonus;
ei.value += Sign[Us] * Score(bonus, bonus);
}
@ -661,10 +655,7 @@ namespace {
// Decrease score if we are attacked by an enemy pawn. Remaining part
// of threat evaluation must be done later when we have full attack info.
if (bit_is_set(ei.attackedBy[Them][PAWN], s))
{
ei.mgValue -= Sign[Us] * MidgameThreatedByPawnPenalty[Piece];
ei.egValue -= Sign[Us] * EndgameThreatedByPawnPenalty[Piece];
}
ei.value -= Sign[Us] * Score(MidgameThreatedByPawnPenalty[Piece], EndgameThreatedByPawnPenalty[Piece]);
// Bishop and knight outposts squares
if ((Piece == BISHOP || Piece == KNIGHT) && pos.square_is_weak(s, Them))
@ -687,8 +678,8 @@ namespace {
if ( relative_rank(Us, s) == RANK_7
&& relative_rank(Us, pos.king_square(Them)) == RANK_8)
{
ei.mgValue += Sign[Us] * (Piece == ROOK ? MidgameRookOn7thBonus : MidgameQueenOn7thBonus);
ei.egValue += Sign[Us] * (Piece == ROOK ? EndgameRookOn7thBonus : EndgameQueenOn7thBonus);
ei.value += Sign[Us] * (Piece == ROOK ? Score(MidgameRookOn7thBonus, EndgameRookOn7thBonus)
: Score(MidgameQueenOn7thBonus, EndgameQueenOn7thBonus));
}
}
@ -700,15 +691,9 @@ namespace {
if (ei.pi->file_is_half_open(Us, f))
{
if (ei.pi->file_is_half_open(Them, f))
{
ei.mgValue += Sign[Us] * RookOpenFileBonus;
ei.egValue += Sign[Us] * RookOpenFileBonus;
}
ei.value += Sign[Us] * Score(RookOpenFileBonus, RookOpenFileBonus);
else
{
ei.mgValue += Sign[Us] * RookHalfOpenFileBonus;
ei.egValue += Sign[Us] * RookHalfOpenFileBonus;
}
ei.value += Sign[Us] * Score(RookHalfOpenFileBonus, RookHalfOpenFileBonus);
}
// Penalize rooks which are trapped inside a king. Penalize more if
@ -724,8 +709,8 @@ namespace {
{
// Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_right(Us, square_file(ksq)))
ei.mgValue -= pos.can_castle(Us)? Sign[Us] * ((TrappedRookPenalty - mob * 16) / 2)
: Sign[Us] * (TrappedRookPenalty - mob * 16);
ei.value -= Sign[Us] * Score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
}
else if ( square_file(ksq) <= FILE_D
&& square_file(s) < square_file(ksq)
@ -733,8 +718,8 @@ namespace {
{
// Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_left(Us, square_file(ksq)))
ei.mgValue -= pos.can_castle(Us)? Sign[Us] * ((TrappedRookPenalty - mob * 16) / 2)
: Sign[Us] * (TrappedRookPenalty - mob * 16);
ei.value -= Sign[Us] * Score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0);
}
}
}
@ -750,8 +735,7 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
Value mgBonus = Value(0);
Value egBonus = Value(0);
Score bonus(0, 0);
// Enemy pieces not defended by a pawn and under our attack
Bitboard weakEnemies = pos.pieces_of_color(Them)
@ -769,13 +753,9 @@ namespace {
if (b)
for (PieceType pt2 = PAWN; pt2 < KING; pt2++)
if (b & pos.pieces(pt2))
{
mgBonus += MidgameThreatBonus[pt1][pt2];
egBonus += EndgameThreatBonus[pt1][pt2];
}
bonus += Score(MidgameThreatBonus[pt1][pt2], EndgameThreatBonus[pt1][pt2]);
}
ei.mgValue += Sign[Us] * mgBonus;
ei.egValue += Sign[Us] * egBonus;
ei.value += Sign[Us] * bonus;
}
@ -810,7 +790,7 @@ namespace {
if (relative_rank(Us, s) <= RANK_4)
{
shelter = ei.pi->get_king_shelter(pos, Us, s);
ei.mgValue += Sign[Us] * Value(shelter);
ei.value += Score(Sign[Us] * Value(shelter), 0);
}
// King safety. This is quite complicated, and is almost certainly far
@ -959,7 +939,7 @@ namespace {
// change far bigger than the value of the captured piece.
Value v = apply_weight(SafetyTable[attackUnits], WeightKingSafety[Us]);
ei.mgValue -= Sign[Us] * v;
ei.value -= Score(Sign[Us] * v, 0);
if (Us == pos.side_to_move())
ei.futilityMargin += v;
@ -1090,8 +1070,8 @@ namespace {
}
// Add the scores for this pawn to the middle game and endgame eval.
ei.mgValue += apply_weight(Sign[Us] * mbonus, WeightPassedPawnsMidgame);
ei.egValue += apply_weight(Sign[Us] * ebonus, WeightPassedPawnsEndgame);
ei.value += Score(apply_weight(Sign[Us] * mbonus, WeightPassedPawnsMidgame),
apply_weight(Sign[Us] * ebonus, WeightPassedPawnsEndgame));
} // while
}
@ -1116,7 +1096,7 @@ namespace {
if (!movesToGo[WHITE] || !movesToGo[BLACK])
{
Color winnerSide = movesToGo[WHITE] ? WHITE : BLACK;
ei.egValue += Sign[winnerSide] * (UnstoppablePawnValue - Value(0x40 * movesToGo[winnerSide]));
ei.value += Score(0, Sign[winnerSide] * (UnstoppablePawnValue - Value(0x40 * movesToGo[winnerSide])));
}
else
{ // Both sides have unstoppable pawns! Try to find out who queens
@ -1131,7 +1111,7 @@ namespace {
// If one side queens at least three plies before the other, that side wins
if (movesToGo[winnerSide] <= movesToGo[loserSide] - 3)
ei.egValue += Sign[winnerSide] * (UnstoppablePawnValue - Value(0x40 * (movesToGo[winnerSide]/2)));
ei.value += Score(0, Sign[winnerSide] * (UnstoppablePawnValue - Value(0x40 * (movesToGo[winnerSide]/2))));
// If one side queens one ply before the other and checks the king or attacks
// the undefended opponent's queening square, that side wins. To avoid cases
@ -1152,7 +1132,7 @@ namespace {
if ( (b & pos.pieces(KING, loserSide))
||(bit_is_set(b, loserQSq) && !bit_is_set(ei.attacked_by(loserSide), loserQSq)))
ei.egValue += Sign[winnerSide] * (UnstoppablePawnValue - Value(0x40 * (movesToGo[winnerSide]/2)));
ei.value += Score(0, Sign[winnerSide] * (UnstoppablePawnValue - Value(0x40 * (movesToGo[winnerSide]/2))));
}
}
}
@ -1174,8 +1154,7 @@ namespace {
&& pos.see(s, b6) < 0
&& pos.see(s, b8) < 0)
{
ei.mgValue -= Sign[us] * TrappedBishopA7H7Penalty;
ei.egValue -= Sign[us] * TrappedBishopA7H7Penalty;
ei.value -= Sign[us] * Score(TrappedBishopA7H7Penalty, TrappedBishopA7H7Penalty);
}
}
@ -1218,8 +1197,7 @@ namespace {
else
penalty = TrappedBishopA1H1Penalty / 2;
ei.mgValue -= Sign[us] * penalty;
ei.egValue -= Sign[us] * penalty;
ei.value -= Sign[us] * Score(penalty, penalty);
}
}
@ -1253,7 +1231,7 @@ namespace {
int space = count_1s_max_15<HasPopCnt>(safeSquares)
+ count_1s_max_15<HasPopCnt>(behindFriendlyPawns & safeSquares);
ei.mgValue += Sign[Us] * apply_weight(Value(space * ei.mi->space_weight()), WeightSpace);
ei.value += Sign[Us] * Score(apply_weight(Value(space * ei.mi->space_weight()), WeightSpace), 0);
}
@ -1268,15 +1246,15 @@ namespace {
// score, based on game phase. It also scales the return value by a
// ScaleFactor array.
Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]) {
Value scale_by_game_phase(const Score& v, Phase ph, const ScaleFactor sf[]) {
assert(mv > -VALUE_INFINITE && mv < VALUE_INFINITE);
assert(ev > -VALUE_INFINITE && ev < VALUE_INFINITE);
assert(v.mg() > -VALUE_INFINITE && v.mg() < VALUE_INFINITE);
assert(v.eg() > -VALUE_INFINITE && v.eg() < VALUE_INFINITE);
assert(ph >= PHASE_ENDGAME && ph <= PHASE_MIDGAME);
ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
Value ev = apply_scale_factor(v.eg(), sf[(v.eg() > Value(0) ? WHITE : BLACK)]);
Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
Value result = Value(int((v.mg() * ph + ev * (128 - ph)) / 128));
return Value(int(result) & ~(GrainSize - 1));
}
@ -1333,3 +1311,8 @@ namespace {
}
}
}
std::ostream& operator<<(std::ostream &os, Score s) {
return os << "(" << s.mg() << ", " << s.eg() << ")";
}

View file

@ -25,6 +25,8 @@
//// Includes
////
#include <iostream>
#include "material.h"
#include "pawns.h"
@ -46,7 +48,7 @@ class Position;
struct EvalInfo {
// Middle game and endgame evaluations
Value mgValue, egValue;
Score value;
// Pointers to material and pawn hash table entries
MaterialInfo* mi;

View file

@ -52,6 +52,40 @@ enum Value {
};
/// Score struct keeps a midgame and an endgame value in a single
/// ScoreValue 64 bit union.
union ScoreValue {
int64_t v64;
struct {
int32_t mgv;
int32_t egv;
} v32;
};
struct Score {
Score() {}
Score(const Score& s) { v = s.v; }
Score(int mg, int eg) { v.v32.mgv = int32_t(mg); v.v32.egv = int32_t(eg); }
Score& operator=(const Score& s) { v = s.v; return *this; }
Score& operator+=(const Score& s) { v.v32.mgv += s.v.v32.mgv; v.v32.egv += s.v.v32.egv; return *this; }
Score& operator-=(const Score& s) { v.v32.mgv -= s.v.v32.mgv; v.v32.egv -= s.v.v32.egv; return *this; }
Value mg() const { return Value(v.v32.mgv); }
Value eg() const { return Value(v.v32.egv); }
private:
ScoreValue v;
};
inline Score operator*(int i, Score s) { return Score(i * s.mg(), i * s.eg()); }
inline Score operator*(Score s, int i) { return s * i; }
inline Score operator-(Score s) { return Score(-s.mg(), -s.eg()); }
extern std::ostream& operator<<(std::ostream& os, Score s);
////
//// Constants and variables
////
@ -97,8 +131,7 @@ const Value PieceValueEndgame[17] = {
/// Bonus for having the side to move (modified by Joona Kiiski)
const Value TempoValueMidgame = Value(48);
const Value TempoValueEndgame = Value(22);
const Score TempoValue = Score(48, 22);
////