enum Tracing { NO_TRACE, TRACE };
- enum Term { // The first 8 entries are reserved for PieceType
- MATERIAL = 8, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, WINNABLE, TOTAL, TERM_NB
+ enum Term { // The first PIECE_TYPE_NB entries are reserved for PieceType
- MATERIAL = PIECE_TYPE_NB, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, INITIATIVE, VARIANT, TOTAL, TERM_NB
++ MATERIAL = PIECE_TYPE_NB, IMBALANCE, MOBILITY, THREAT, PASSED, SPACE, VARIANT, WINNABLE, TOTAL, TERM_NB
};
Score scores[TERM_NB][COLOR_NB];
template<Color Us> Score threats() const;
template<Color Us> Score passed() const;
template<Color Us> Score space() const;
+ template<Color Us> Score variant() const;
- ScaleFactor scale_factor(Value eg) const;
- Score initiative(Score score) const;
+ Value winnable(Score score) const;
const Position& pos;
Material::Entry* me;
}
+ // Evaluation::variant() computes variant-specific evaluation bonuses for a given side.
+
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::variant() const {
+
+ constexpr Color Them = ~Us;
+ constexpr Direction Down = pawn_push(Them);
+
+ Score score = SCORE_ZERO;
+
+ // Capture the flag
+ if (pos.capture_the_flag(Us))
+ {
+ PieceType ptCtf = pos.capture_the_flag_piece();
+ Bitboard ctfPieces = pos.pieces(Us, ptCtf);
+ Bitboard ctfTargets = pos.capture_the_flag(Us) & pos.board_bb();
+ Bitboard onHold = 0;
+ Bitboard onHold2 = 0;
+ Bitboard processed = 0;
+ Bitboard blocked = pos.pieces(Us, PAWN) | attackedBy[Them][ALL_PIECES];
+ Bitboard doubleBlocked = attackedBy2[Them]
+ | (pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | attackedBy[Them][ALL_PIECES]))
+ | (pos.pieces(Them) & pe->pawn_attacks(Them))
+ | (pawn_attacks_bb<Them>(pos.pieces(Them, PAWN) & pe->pawn_attacks(Them)));
+ Bitboard inaccessible = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces(Them, PAWN));
+ // Traverse all paths of the CTF pieces to the CTF targets.
+ // Put squares that are attacked or occupied on hold for one iteration.
+ for (int dist = 0; (ctfPieces || onHold || onHold2) && (ctfTargets & ~processed); dist++)
+ {
+ int wins = popcount(ctfTargets & ctfPieces);
+ if (wins)
+ score += make_score(4000, 4000) * wins / (wins + dist * dist);
+ Bitboard current = ctfPieces & ~ctfTargets;
+ processed |= ctfPieces;
+ ctfPieces = onHold & ~processed;
+ onHold = onHold2 & ~processed;
+ onHold2 = 0;
+ while (current)
+ {
+ Square s = pop_lsb(¤t);
+ Bitboard attacks = ( (PseudoAttacks[Us][ptCtf][s] & pos.pieces())
+ | (PseudoMoves[Us][ptCtf][s] & ~pos.pieces())) & ~processed & pos.board_bb();
+ ctfPieces |= attacks & ~blocked;
+ onHold |= attacks & ~doubleBlocked;
+ onHold2 |= attacks & ~inaccessible;
+ }
+ }
+ }
+
+ // nCheck
+ if (pos.check_counting())
+ {
+ int remainingChecks = pos.checks_remaining(Us);
+ assert(remainingChecks > 0);
+ score += make_score(3600, 1000) / (remainingChecks * remainingChecks);
+ }
+
+ // Extinction
+ if (pos.extinction_value() != VALUE_NONE)
+ {
+ for (PieceType pt : pos.extinction_piece_types())
+ if (pt != ALL_PIECES)
+ {
+ int denom = std::max(pos.count(Us, pt) - pos.extinction_piece_count(), 1);
+ if (pos.count(Them, pt) >= pos.extinction_opponent_piece_count() || pos.two_boards())
+ score += make_score(1000000 / (500 + PieceValue[MG][pt]),
+ 1000000 / (500 + PieceValue[EG][pt])) / (denom * denom)
+ * (pos.extinction_value() / VALUE_MATE);
+ }
+ else if (pos.extinction_value() == VALUE_MATE)
+ score += make_score(pos.non_pawn_material(Us), pos.non_pawn_material(Us)) / pos.count<ALL_PIECES>(Us);
+ }
+
+ // Connect-n
+ if (pos.connect_n() > 0)
+ {
+ for (Direction d : {NORTH, NORTH_EAST, EAST, SOUTH_EAST})
+ {
+ // Find sufficiently large gaps
+ Bitboard b = pos.board_bb() & ~pos.pieces(Them);
+ for (int i = 1; i < pos.connect_n(); i++)
+ b &= shift(d, b);
+ // Count number of pieces per gap
+ while (b)
+ {
+ Square s = pop_lsb(&b);
+ int c = 0;
+ for (int j = 0; j < pos.connect_n(); j++)
+ if (pos.pieces(Us) & (s - j * d))
+ c++;
+ score += make_score(200, 200) * c / (pos.connect_n() - c) / (pos.connect_n() - c);
+ }
+ }
+ }
+
+ // Potential piece flips
+ if (pos.flip_enclosed_pieces())
+ {
+ // Stable pieces
+ if (pos.flip_enclosed_pieces() == REVERSI)
+ {
+ Bitboard edges = (FileABB | file_bb(pos.max_file()) | Rank1BB | rank_bb(pos.max_rank())) & pos.board_bb();
+ Bitboard edgePieces = pos.pieces(Us) & edges;
+ while (edgePieces)
+ {
+ Bitboard connectedEdge = attacks_bb(Us, ROOK, pop_lsb(&edgePieces), ~(pos.pieces(Us) & edges)) & edges;
+ if (!more_than_one(connectedEdge & ~pos.pieces(Us)))
+ score += make_score(300, 300);
+ else if (!(connectedEdge & ~pos.pieces()))
+ score += make_score(200, 200);
+ }
+ }
+
+ // Unstable
+ Bitboard unstable = 0;
+ Bitboard drops = pos.drop_region(Them, IMMOBILE_PIECE);
+ while (drops)
+ {
+ Square s = pop_lsb(&drops);
+ if (pos.flip_enclosed_pieces() == REVERSI)
+ {
+ Bitboard b = attacks_bb(Them, QUEEN, s, ~pos.pieces(Us)) & ~PseudoAttacks[Them][KING][s] & pos.pieces(Them);
+ while(b)
+ unstable |= between_bb(s, pop_lsb(&b));
+ }
+ else
+ unstable |= PseudoAttacks[Them][KING][s] & pos.pieces(Us);
+ }
+ score -= make_score(200, 200) * popcount(unstable);
+ }
+
+ if (T)
+ Trace::add(VARIANT, Us, score);
+
+ return score;
+ }
+
+
- // Evaluation::initiative() computes the initiative correction value
- // for the position. It is a second order bonus/malus based on the
+ // Evaluation::winnable() adjusts the mg and eg score components based on the
// known attacking/defending status of the players.
+ // A single value is derived from the mg and eg values and returned.
template<Tracing T>
- Score Evaluation<T>::initiative(Score score) const {
+ Value Evaluation<T>::winnable(Score score) const {
- int outflanking = distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
- - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
+ // No initiative bonus for extinction variants
- if (pos.extinction_value() != VALUE_NONE || pos.captures_to_hand() || pos.connect_n())
- return SCORE_ZERO;
-
++ int complexity = 0;
++ if (pos.extinction_value() == VALUE_NONE && !pos.captures_to_hand() && !pos.connect_n())
++ {
+ int outflanking = !pos.count<KING>(WHITE) || !pos.count<KING>(BLACK) ? 0
+ : distance<File>(pos.square<KING>(WHITE), pos.square<KING>(BLACK))
+ - distance<Rank>(pos.square<KING>(WHITE), pos.square<KING>(BLACK));
bool pawnsOnBothFlanks = (pos.pieces(PAWN) & QueenSide)
&& (pos.pieces(PAWN) & KingSide);
bool almostUnwinnable = outflanking < 0
+ && pos.stalemate_value() == VALUE_DRAW
&& !pawnsOnBothFlanks;
- bool infiltration = rank_of(pos.square<KING>(WHITE)) > RANK_4
- || rank_of(pos.square<KING>(BLACK)) < RANK_5;
+ bool infiltration = (pos.count<KING>(WHITE) && rank_of(pos.square<KING>(WHITE)) > RANK_4)
+ || (pos.count<KING>(BLACK) && rank_of(pos.square<KING>(BLACK)) < RANK_5);
// Compute the initiative bonus for the attacking side
-- int complexity = 9 * pe->passed_count()
++ complexity = 9 * pe->passed_count()
+ 12 * pos.count<PAWN>()
+ + 15 * pos.count<SOLDIER>()
+ 9 * outflanking
+ 21 * pawnsOnBothFlanks
+ 24 * infiltration
- 43 * almostUnwinnable
- 2 * pos.rule50_count()
-110 ;
++ }
Value mg = mg_value(score);
Value eg = eg_value(score);
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
}
else
- sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
+ sf = std::min(sf, 36 + 7 * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide)));
}
- return ScaleFactor(sf);
+ // Interpolate between the middlegame and (scaled by 'sf') endgame score
+ v = mg * int(me->game_phase())
+ + eg * int(PHASE_MIDGAME - me->game_phase()) * ScaleFactor(sf) / SCALE_FACTOR_NORMAL;
+ v /= PHASE_MIDGAME;
+
+ if (T)
+ {
+ Trace::add(WINNABLE, make_score(u, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL - eg_value(score)));
+ Trace::add(TOTAL, make_score(mg, eg * ScaleFactor(sf) / SCALE_FACTOR_NORMAL));
+ }
+
+ return Value(v);
}
score += king< WHITE>() - king< BLACK>()
+ threats<WHITE>() - threats<BLACK>()
+ passed< WHITE>() - passed< BLACK>()
- + space< WHITE>() - space< BLACK>();
+ + space< WHITE>() - space< BLACK>()
+ + variant<WHITE>() - variant<BLACK>();
- score += initiative(score);
-
- // Interpolate between a middlegame and a (scaled by 'sf') endgame score
- ScaleFactor sf = scale_factor(eg_value(score));
- v = mg_value(score) * int(me->game_phase())
- + eg_value(score) * int(PHASE_MIDGAME - me->game_phase()) * sf / SCALE_FACTOR_NORMAL;
-
- v /= PHASE_MIDGAME;
+ // Derive single value from mg and eg parts of score
+ v = winnable(score);
// In case of tracing add all remaining individual evaluation terms
if (T)
<< " Threats | " << Term(THREAT)
<< " Passed | " << Term(PASSED)
<< " Space | " << Term(SPACE)
- << " Initiative | " << Term(INITIATIVE)
+ << " Variant | " << Term(VARIANT)
+ << " Winnable | " << Term(WINNABLE)
<< " ------------+-------------+-------------+------------\n"
<< " Total | " << Term(TOTAL);