No functional change.
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
-inline int edge_distance(File f) { return std::min(f, File(FILE_H - f)); }
-inline int edge_distance(Rank r) { return std::min(r, Rank(RANK_8 - r)); }
+inline int edge_distance(File f, File maxFile = FILE_H) { return std::min(f, File(maxFile - f)); }
+inline int edge_distance(Rank r, Rank maxRank = RANK_8) { return std::min(r, Rank(maxRank - r)); }
- /// Return the target square bitboard if we do not step off the board, empty otherwise
+
+ /// safe_destination() returns the bitboard of target square for the given step
+ /// from the given square. If the step is off the board, returns empty bitboard.
inline Bitboard safe_destination(Square s, int step)
{
Square to = Square(s + step);
- return is_ok(to) && distance(s, to) <= 2 ? square_bb(to) : Bitboard(0);
+ return is_ok(to) && distance(s, to) <= 3 ? square_bb(to) : Bitboard(0);
+}
+
+template<RiderType R>
+inline Bitboard rider_attacks_bb(Square s, Bitboard occupied) {
+
+ assert(R == RIDER_BISHOP || R == RIDER_ROOK_H || R == RIDER_ROOK_V || R == RIDER_CANNON_H || R == RIDER_CANNON_V
+ || R == RIDER_HORSE || R == RIDER_ELEPHANT || R == RIDER_JANGGI_ELEPHANT);
+ const Magic& m = R == RIDER_ROOK_H ? RookMagicsH[s]
+ : R == RIDER_ROOK_V ? RookMagicsV[s]
+ : R == RIDER_CANNON_H ? CannonMagicsH[s]
+ : R == RIDER_CANNON_V ? CannonMagicsV[s]
+ : R == RIDER_HORSE ? HorseMagics[s]
+ : R == RIDER_ELEPHANT ? ElephantMagics[s]
+ : R == RIDER_JANGGI_ELEPHANT ? JanggiElephantMagics[s]
+ : BishopMagics[s];
+ return m.attacks[m.index(occupied)];
}
+
/// attacks_bb(Square) returns the pseudo attacks of the give piece type
/// assuming an empty board.
assert((Pt != PAWN) && (is_ok(s)));
- return PseudoAttacks[Pt][s];
+ return PseudoAttacks[WHITE][Pt][s];
}
+
/// attacks_bb(Square, Bitboard) returns the attacks by the given piece
/// assuming the board is occupied according to the passed Bitboard.
/// Sliding piece attacks do not continue passed an occupied square.
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
+ // KingProximity contains a penalty according to distance from king
+ constexpr Score KingProximity = S(2, 4);
+ constexpr Score EndgameKingProximity = S(0, 10);
+
// Assorted bonuses and penalties
- constexpr Score BishopPawns = S( 3, 7);
+ constexpr Score BishopKingProtector = S( 6, 9);
constexpr Score BishopOnKingRing = S( 24, 0);
+ constexpr Score BishopOutpost = S( 30, 23);
+ constexpr Score BishopPawns = S( 3, 7);
constexpr Score BishopXRayPawns = S( 4, 5);
constexpr Score CorneredBishop = S( 50, 50);
constexpr Score FlankAttacks = S( 8, 0);
// Evaluation::pieces() scores pieces of a given color and type
+
- template<Tracing T> template<Color Us, PieceType Pt>
- Score Evaluation<T>::pieces() {
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::pieces(PieceType Pt) {
constexpr Color Them = ~Us;
constexpr Direction Down = -pawn_push(Us);
return score;
}
+ // Evaluation::hand() scores pieces of a given color and type in hand
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::hand(PieceType pt) {
+
+ constexpr Color Them = ~Us;
+
+ Score score = SCORE_ZERO;
+
+ if (pos.count_in_hand(Us, pt))
+ {
+ Bitboard b = pos.drop_region(Us, pt) & ~pos.pieces() & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]);
+ if ((b & kingRing[Them]) && pt != SHOGI_PAWN)
+ {
+ kingAttackersCountInHand[Us] += pos.count_in_hand(Us, pt);
+ kingAttackersWeightInHand[Us] += KingAttackWeights[std::min(int(pt), QUEEN + 1)] * pos.count_in_hand(Us, pt);
+ kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
+ }
+ Bitboard theirHalf = pos.board_bb() & ~forward_ranks_bb(Them, relative_rank(Them, Rank((pos.max_rank() - 1) / 2), pos.max_rank()));
+ mobility[Us] += DropMobility * popcount(b & theirHalf & ~attackedBy[Them][ALL_PIECES]);
+ if (pos.promoted_piece_type(pt) != NO_PIECE_TYPE && pos.drop_promoted())
+ score += make_score(std::max(PieceValue[MG][pos.promoted_piece_type(pt)] - PieceValue[MG][pt], VALUE_ZERO),
+ std::max(PieceValue[EG][pos.promoted_piece_type(pt)] - PieceValue[EG][pt], VALUE_ZERO)) / 4 * pos.count_in_hand(Us, pt);
+ if (pos.enclosing_drop())
+ mobility[Us] += make_score(500, 500) * popcount(b);
+
+ // Reduce score if there is a deficit of gates
+ if (pos.seirawan_gating() && !pos.piece_drops() && pos.count_in_hand(Us, ALL_PIECES) > popcount(pos.gates(Us)))
+ score -= make_score(200, 900) / pos.count_in_hand(Us, ALL_PIECES) * (pos.count_in_hand(Us, ALL_PIECES) - popcount(pos.gates(Us)));
+
+ if (pt == SHOGI_PAWN && !pos.shogi_doubled_pawn())
+ score -= make_score(50, 20) * std::max(pos.count_with_hand(Us, SHOGI_PAWN) - pos.max_file() - 1, 0);
+ }
+
+ return score;
+ }
// Evaluation::king() assigns bonuses and penalties to a king of a given color
+
template<Tracing T> template<Color Us>
Score Evaluation<T>::king() const {
}
+ // 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::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.
+ // known attacking/defending status of the players. A single value is derived
+ // by interpolation from the mg and eg values and returned.
template<Tracing T>
Value Evaluation<T>::winnable(Score score) const {
initialize<WHITE>();
initialize<BLACK>();
- // Pieces should be evaluated first (populate attack tables).
+ // Pieces evaluated first (also populates attackedBy, attackedBy2).
- // Note that the order of evaluation of the terms is left unspecified.
- score += pieces<WHITE, KNIGHT>() - pieces<BLACK, KNIGHT>()
- + pieces<WHITE, BISHOP>() - pieces<BLACK, BISHOP>()
- + pieces<WHITE, ROOK >() - pieces<BLACK, ROOK >()
- + pieces<WHITE, QUEEN >() - pieces<BLACK, QUEEN >();
+ // For unused piece types, we still need to set attack bitboard to zero.
+ for (PieceType pt = KNIGHT; pt < KING; ++pt)
+ if (pt != SHOGI_PAWN)
+ score += pieces<WHITE>(pt) - pieces<BLACK>(pt);
+
+ // Evaluate pieces in hand once attack tables are complete
+ if (pos.piece_drops() || pos.seirawan_gating())
+ for (PieceType pt = PAWN; pt < KING; ++pt)
+ score += hand<WHITE>(pt) - hand<BLACK>(pt);
- score += mobility[WHITE] - mobility[BLACK];
+ score += (mobility[WHITE] - mobility[BLACK]) * (1 + pos.captures_to_hand() + pos.must_capture() + pos.check_counting());
// More complex interactions that require fully populated attack bitboards
score += king< WHITE>() - king< BLACK>()
&& pos.count<PAWN>(~us) >= 1;
}
+
/// imbalance() calculates the imbalance by comparing the piece count of each
/// piece type for both colors.
+
template<Color Us>
- int imbalance(const int pieceCount[][PIECE_TYPE_NB]) {
+ int imbalance(const Position& pos, const int pieceCount[][PIECE_TYPE_NB]) {
constexpr Color Them = ~Us;
// Second-degree polynomial material imbalance, by Tord Romstad
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; ++pt1)
{
- if (!pieceCount[Us][pt1])
+ if (!pieceCount[Us][pt1] || (pos.extinction_value() == VALUE_MATE && pt1 != KNIGHT))
continue;
- int v = 0;
+ int v = QuadraticOurs[pt1][pt1] * pieceCount[Us][pt1];
- for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; ++pt2)
+ for (int pt2 = NO_PIECE_TYPE; pt2 < pt1; ++pt2)
- v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2]
+ v += QuadraticOurs[pt1][pt2] * pieceCount[Us][pt2] * (pos.must_capture() && pt1 == KNIGHT && pt2 == PAWN ? 2 : pos.check_counting() && pt1 <= BISHOP ? 0 : 1)
+ QuadraticTheirs[pt1][pt2] * pieceCount[Them][pt2];
bonus += pieceCount[Us][pt1] * v;
// https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
// First and second hash functions for indexing the cuckoo tables
+#ifdef LARGEBOARDS
+inline int H1(Key h) { return h & 0x7fff; }
+inline int H2(Key h) { return (h >> 16) & 0x7fff; }
+#else
inline int H1(Key h) { return h & 0x1fff; }
inline int H2(Key h) { return (h >> 16) & 0x1fff; }
+#endif
// Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves
+#ifdef LARGEBOARDS
+Key cuckoo[65536];
+Move cuckooMove[65536];
+#else
Key cuckoo[8192];
Move cuckooMove[8192];
+#endif
- /// Position::init() initializes at startup the various arrays used to compute
- /// hash keys.
+ /// Position::init() initializes at startup the various arrays used to compute hash keys
void Position::init() {
#undef S
-Score psq[PIECE_NB][SQUARE_NB];
-
+Score psq[PIECE_NB][SQUARE_NB + 1];
- // init() initializes piece-square tables: the white halves of the tables are
- // copied from Bonus[] adding the piece value, then the black halves of the
- // tables are initialized by flipping and changing the sign of the white scores.
+ // PSQT::init() initializes piece-square tables: the white halves of the tables are
+ // copied from Bonus[] and PBonus[], adding the piece value, then the black halves of
+ // the tables are initialized by flipping and changing the sign of the white scores.
-void init() {
+void init(const Variant* v) {
+
+ PieceType strongestPiece = NO_PIECE_TYPE;
+ for (PieceType pt : v->pieceTypes)
+ if (PieceValue[MG][pt] > PieceValue[MG][strongestPiece])
+ strongestPiece = pt;
+
+ Value maxPromotion = VALUE_ZERO;
+ for (PieceType pt : v->promotionPieceTypes)
+ maxPromotion = std::max(maxPromotion, PieceValue[EG][pt]);
- for (Piece pc : {W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING})
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
{
+ Piece pc = make_piece(WHITE, pt);
+
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
- for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ // Consider promotion types in pawn score
+ if (pt == PAWN)
+ score -= make_score(0, (QueenValueEg - maxPromotion) / 100);
+
+ // Scale slider piece values with board size
+ const PieceInfo* pi = pieceMap.find(pt)->second;
+ bool isSlider = pi->sliderQuiet.size() || pi->sliderCapture.size() || pi->hopperQuiet.size() || pi->hopperCapture.size();
+ bool isPawn = !isSlider && pi->stepsQuiet.size() && !std::any_of(pi->stepsQuiet.begin(), pi->stepsQuiet.end(), [](Direction d) { return d < SOUTH / 2; });
+ bool isSlowLeaper = !isSlider && !std::any_of(pi->stepsQuiet.begin(), pi->stepsQuiet.end(), [](Direction d) { return dist(d) > 1; });
+
+ if (isSlider)
+ {
+ constexpr int lc = 5;
+ constexpr int rm = 5;
+ constexpr int r0 = rm + RANK_8;
+ int r1 = rm + (v->maxRank + v->maxFile) / 2;
+ int leaper = pi->stepsQuiet.size() + pi->stepsCapture.size();
+ int slider = pi->sliderQuiet.size() + pi->sliderCapture.size() + pi->hopperQuiet.size() + pi->hopperCapture.size();
+ score = make_score(mg_value(score) * (lc * leaper + r1 * slider) / (lc * leaper + r0 * slider),
+ eg_value(score) * (lc * leaper + r1 * slider) / (lc * leaper + r0 * slider));
+ }
+
+ // Increase leapers' value in makpong
+ if (v->makpongRule)
+ {
+ if (std::any_of(pi->stepsCapture.begin(), pi->stepsCapture.end(), [](Direction d) { return dist(d) > 1; })
+ && !pi->lameLeaper)
+ score = make_score(mg_value(score) * 4200 / (3500 + mg_value(score)),
+ eg_value(score) * 4700 / (3500 + mg_value(score)));
+ }
+
+ // For drop variants, halve the piece values
+ if (v->capturesToHand)
+ score = make_score(mg_value(score) * 3500 / (7000 + mg_value(score)),
+ eg_value(score) * 3500 / (7000 + eg_value(score)));
+ else if (!v->checking)
+ score = make_score(mg_value(score) * 2000 / (3500 + mg_value(score)),
+ eg_value(score) * 2200 / (3500 + eg_value(score)));
+ else if (v->twoBoards)
+ score = make_score(mg_value(score) * 7000 / (7000 + mg_value(score)),
+ eg_value(score) * 7000 / (7000 + eg_value(score)));
+ else if (v->checkCounting)
+ score = make_score(mg_value(score) * (40000 + mg_value(score)) / 41000,
+ eg_value(score) * (30000 + eg_value(score)) / 31000);
+ else if (pt == strongestPiece)
+ score += make_score(std::max(QueenValueMg - PieceValue[MG][pt], VALUE_ZERO) / 20,
+ std::max(QueenValueEg - PieceValue[EG][pt], VALUE_ZERO) / 20);
+
+ // For antichess variants, use negative piece values
+ if ( v->extinctionValue == VALUE_MATE
+ && v->extinctionPieceTypes.find(ALL_PIECES) != v->extinctionPieceTypes.end())
+ score = -make_score(mg_value(score) / 8, eg_value(score) / 8 / (1 + !pi->sliderCapture.size()));
+
+ // Determine pawn rank
+ std::istringstream ss(v->startFen);
+ unsigned char token;
+ Rank rc = v->maxRank;
+ Rank pawnRank = RANK_2;
+ while ((ss >> token) && !isspace(token))
+ {
+ if (token == '/')
+ --rc;
+ else if (token == v->pieceToChar[PAWN] || token == v->pieceToChar[SHOGI_PAWN])
+ pawnRank = rc;
+ }
+
+ for (Square s = SQ_A1; s <= SQ_MAX; ++s)
{
- File f = File(edge_distance(file_of(s)));
- psq[ pc][s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
- : Bonus[pc][rank_of(s)][f]);
- psq[~pc][flip_rank(s)] = -psq[pc][s];
+ File f = std::max(File(edge_distance(file_of(s), v->maxFile)), FILE_A);
+ Rank r = rank_of(s);
- psq[ pc][ s] = score + ( pt == PAWN ? PBonus[std::min(r, RANK_8)][std::min(file_of(s), FILE_H)]
- : pt == KING ? KingBonus[Utility::clamp(Rank(r - pawnRank + 1), RANK_1, RANK_8)][std::min(f, FILE_D)] * (1 + v->capturesToHand)
- : pt <= QUEEN ? Bonus[pc][std::min(r, RANK_8)][std::min(f, FILE_D)]
- : pt == HORSE ? Bonus[KNIGHT][std::min(r, RANK_8)][std::min(f, FILE_D)]
- : isSlider ? make_score(5, 5) * (2 * f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile - 1)
- : isPawn ? make_score(5, 5) * (2 * f - v->maxFile)
- : make_score(10, 10) * (1 + isSlowLeaper) * (f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile / 2));
++ psq[ pc][s] = score + ( pt == PAWN ? PBonus[std::min(r, RANK_8)][std::min(file_of(s), FILE_H)]
++ : pt == KING ? KingBonus[Utility::clamp(Rank(r - pawnRank + 1), RANK_1, RANK_8)][std::min(f, FILE_D)] * (1 + v->capturesToHand)
++ : pt <= QUEEN ? Bonus[pc][std::min(r, RANK_8)][std::min(f, FILE_D)]
++ : pt == HORSE ? Bonus[KNIGHT][std::min(r, RANK_8)][std::min(f, FILE_D)]
++ : isSlider ? make_score(5, 5) * (2 * f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile - 1)
++ : isPawn ? make_score(5, 5) * (2 * f - v->maxFile)
++ : make_score(10, 10) * (1 + isSlowLeaper) * (f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - v->maxFile / 2));
+ if (pt == SOLDIER && r < v->soldierPromotionRank)
+ psq[pc][s] -= score * (v->soldierPromotionRank - r) / (4 + f);
+ if (v->enclosingDrop == REVERSI)
+ {
+ if (f == FILE_A && (r == RANK_1 || r == v->maxRank))
+ psq[pc][s] += make_score(1000, 1000);
+ }
+ psq[~pc][rank_of(s) <= v->maxRank ? flip_rank(s, v->maxRank) : s] = -psq[pc][s];
}
+ // pieces in pocket
+ psq[ pc][SQ_NONE] = score + make_score(35, 10) * (1 + !isSlider);
+ psq[~pc][SQ_NONE] = -psq[pc][SQ_NONE];
}
}
double totalTime = rootMoves.size() == 1 ? 0 :
Time.optimum() * fallingEval * reduction * bestMoveInstability;
+ if (completedDepth >= 8 && rootPos.two_boards() && Options["Protocol"] == "xboard")
+ {
+ if (Limits.time[us])
+ Partner.ptell<FAIRY>("time " + std::to_string((Limits.time[us] - Time.elapsed()) / 10));
+ if (Limits.time[~us])
+ Partner.ptell<FAIRY>("otim " + std::to_string(Limits.time[~us] / 10));
+ if (!Partner.weDead && bestValue <= VALUE_MATED_IN_MAX_PLY)
+ {
+ Partner.ptell("dead");
+ Partner.weDead = true;
+ }
+ else if (Partner.weDead && bestValue > VALUE_MATED_IN_MAX_PLY)
+ {
+ Partner.ptell("x");
+ Partner.weDead = false;
+ }
+ else if (!Partner.weWin && bestValue >= VALUE_MATE_IN_MAX_PLY && Limits.time[~us] < Partner.time * 10)
+ {
+ Partner.ptell("sit");
+ Partner.weWin = true;
+ }
+ else if (Partner.weWin && (bestValue < VALUE_MATE_IN_MAX_PLY || Limits.time[~us] > Partner.time * 10))
+ {
+ Partner.ptell("x");
+ Partner.weWin = false;
+ }
+ }
+
- // Stop the search if we have exceeded the totalTime, at least 1ms search.
+ // Stop the search if we have exceeded the totalTime, at least 1ms search
if (Time.elapsed() > totalTime)
{
// If we are allowed to ponder do not stop the search now but
if (!rootNode)
{
+ Value variantResult;
+ if (pos.is_game_end(variantResult, ss->ply))
+ return variantResult;
+
// Step 2. Check for aborted search and immediate draw
if ( Threads.stop.load(std::memory_order_relaxed)
- || pos.is_draw(ss->ply)
|| ss->ply >= MAX_PLY)
return (ss->ply >= MAX_PLY && !ss->inCheck) ? evaluate(pos)
- : value_draw(pos.this_thread());
+ : value_draw(pos.this_thread());
// Step 3. Mate distance pruning. Even if we mate at the next move our score
// would be at best mate_in(ss->ply+1), but if alpha is already bigger because
}
}
- // All legal moves have been searched. A special case: If we're in check
+ // All legal moves have been searched. A special case: if we're in check
// and no legal moves were found, it is checkmate.
if (ss->inCheck && bestValue == -VALUE_INFINITE)
- return mated_in(ss->ply); // Plies to mate from the root
+ return pos.checkmate_value(ss->ply); // Plies to mate from the root
tte->save(posKey, value_to_tt(bestValue, ss->ply), pvHit,
bestValue >= beta ? BOUND_LOWER :
TimeManagement Time; // Our global time management object
- /// init() is called at the beginning of the search and calculates the bounds
- /// of time allowed for the current game ply. We currently support:
- // 1) x basetime (+z increment)
- // 2) x moves in y seconds (+z increment)
+
+ /// TimeManagement::init() is called at the beginning of the search and calculates
+ /// the bounds of time allowed for the current game ply. We currently support:
+ // 1) x basetime (+ z increment)
+ // 2) x moves in y seconds (+ z increment)
-void TimeManagement::init(Search::LimitsType& limits, Color us, int ply) {
+void TimeManagement::init(const Position& pos, Search::LimitsType& limits, Color us, int ply) {
TimePoint moveOverhead = TimePoint(Options["Move Overhead"]);
TimePoint slowMover = TimePoint(Options["Slow Mover"]);
return Color(c ^ BLACK); // Toggle color
}
- constexpr Square flip_rank(Square s, Rank maxRank = RANK_8) {
-constexpr Square flip_rank(Square s) { // Swap A1 <-> A8
- return Square(s ^ SQ_A8);
++constexpr Square flip_rank(Square s, Rank maxRank = RANK_8) { // Swap A1 <-> A8
+ return Square(s + NORTH * (maxRank - 2 * (s / NORTH)));
}
- constexpr Square flip_file(Square s, File maxFile = FILE_H) {
-constexpr Square flip_file(Square s) { // Swap A1 <-> H1
- return Square(s ^ SQ_H1);
++constexpr Square flip_file(Square s, File maxFile = FILE_H) { // Swap A1 <-> H1
+ return Square(s + maxFile - 2 * (s % NORTH));
}
constexpr Piece operator~(Piece pc) {
- return Piece(pc ^ PIECE_TYPE_NB); // Swap color of piece BLACK KNIGHT -> WHITE KNIGHT
- return Piece(pc ^ 8); // Swap color of piece B_KNIGHT <-> W_KNIGHT
++ return Piece(pc ^ PIECE_TYPE_NB); // Swap color of piece B_KNIGHT <-> W_KNIGHT
}
constexpr CastlingRights operator&(Color c, CastlingRights cr) {