constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type
- constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 77, 55, 44, 10, 40 };
- constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
++ constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10, 40 };
// Penalties for enemy's safe checks
constexpr int QueenSafeCheck = 780;
// Bonus for rook on an open or semi-open file
if (pos.is_on_semiopen_file(Us, s))
- score += RookOnFile[bool(pos.is_on_semiopen_file(Them, s))];
+ score += RookOnFile[pos.is_on_semiopen_file(Them, s)];
// Penalty when trapped by the king, even more if the king cannot castle
- else if (mob <= 3)
+ else if (mob <= 3 && pos.count<KING>(Us))
{
File kf = file_of(pos.square<KING>(Us));
if ((kf < FILE_E) == (file_of(s) < kf))
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
+ 148 * popcount(unsafeChecks)
+ 98 * popcount(pos.blockers_for_king(Us))
- - 873 * !pos.count<QUEEN>(Them)
+ - 873 * !(pos.major_pieces(Them) || pos.captures_to_hand()) / (1 + pos.check_counting())
- 6 * mg_value(score) / 8
+ mg_value(mobility[Them] - mobility[Us])
- + 5 * kingFlankAttacks * kingFlankAttacks / 16
+ + 3 * kingFlankAttacks * kingFlankAttacks / 8
- 7;
// Transform the kingDanger units into a Score, and subtract it from the evaluation
if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
{
if (!pos.castling_impeded(OO) && pos.can_castle(OO))
- *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OO));
+ moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, ksq, pos.castling_rook_square(OO));
if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
- *moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(OOO));
+ moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, ksq, pos.castling_rook_square(OOO));
+ }
+ }
+
+ // Castling with non-king piece
+ if (!pos.count<KING>(Us) && Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
+ {
- Square from = make_square(FILE_E, relative_rank(Us, pos.castling_rank(), pos.max_rank()));
++ Square from = make_square(FILE_E, pos.castling_rank(Us));
+ if (!pos.castling_impeded(OO) && pos.can_castle(OO))
+ moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, from, pos.castling_rook_square(OO));
+
+ if (!pos.castling_impeded(OOO) && pos.can_castle(OOO))
+ moveList = make_move_and_gating<CASTLING>(pos, moveList, Us, from, pos.castling_rook_square(OOO));
+ }
+
+ // Special moves
+ if (pos.cambodian_moves() && pos.gates(Us))
+ {
+ if (Type != CAPTURES && Type != EVASIONS && (pos.pieces(Us, KING) & pos.gates(Us)))
+ {
+ Square from = pos.square<KING>(Us);
+ Bitboard b = PseudoAttacks[WHITE][KNIGHT][from] & rank_bb(rank_of(from + (Us == WHITE ? NORTH : SOUTH)))
+ & target & ~pos.pieces();
+ while (b)
+ moveList = make_move_and_gating<SPECIAL>(pos, moveList, Us, from, pop_lsb(&b));
+ }
+
+ Bitboard b = pos.pieces(Us, FERS) & pos.gates(Us);
+ while (b)
+ {
+ Square from = pop_lsb(&b);
+ Square to = from + 2 * (Us == WHITE ? NORTH : SOUTH);
+ if (is_ok(to) && (target & to))
+ moveList = make_move_and_gating<SPECIAL>(pos, moveList, Us, from, to);
}
}
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))];
else if (Type == QUIETS)
- m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
- + (*continuationHistory[0])[history_slot(pos.moved_piece(m))][to_sq(m)]
- + (*continuationHistory[1])[history_slot(pos.moved_piece(m))][to_sq(m)]
- + (*continuationHistory[3])[history_slot(pos.moved_piece(m))][to_sq(m)]
- + (*continuationHistory[5])[history_slot(pos.moved_piece(m))][to_sq(m)] / 2;
+ m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
- + 2 * (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
- + 2 * (*continuationHistory[1])[pos.moved_piece(m)][to_sq(m)]
- + 2 * (*continuationHistory[3])[pos.moved_piece(m)][to_sq(m)]
- + (*continuationHistory[5])[pos.moved_piece(m)][to_sq(m)];
++ + 2 * (*continuationHistory[0])[history_slot(pos.moved_piece(m))][to_sq(m)]
++ + 2 * (*continuationHistory[1])[history_slot(pos.moved_piece(m))][to_sq(m)]
++ + 2 * (*continuationHistory[3])[history_slot(pos.moved_piece(m))][to_sq(m)]
++ + (*continuationHistory[5])[history_slot(pos.moved_piece(m))][to_sq(m)];
else // Type == EVASIONS
{
struct Stats<T, D, Size> : public std::array<StatsEntry<T, D>, Size> {};
/// In stats table, D=0 means that the template parameter is not used
-enum StatsParams { NOT_USED = 0 };
+enum StatsParams { NOT_USED = 0, PIECE_SLOTS = 8 };
-
+ enum StatsType { NoCaptures, Captures };
/// ButterflyHistory records how often quiet moves have been successful or
/// unsuccessful during the current search, and is used for reduction and move
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
- Rank r = relative_rank(Us, s);
+ Rank r = relative_rank(Us, s, pos.max_rank());
- e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
-
// Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s);
+ blocked = theirPawns & (s + Up);
stoppers = theirPawns & passed_pawn_span(Us, s);
- lever = theirPawns & PawnAttacks[Us][s];
- leverPush = theirPawns & PawnAttacks[Us][s + Up];
- doubled = ourPawns & (s - Up);
+ lever = theirPawns & PseudoAttacks[Us][PAWN][s];
+ leverPush = relative_rank(Them, s, pos.max_rank()) > RANK_1 ? theirPawns & PseudoAttacks[Us][PAWN][s + Up] : Bitboard(0);
+ doubled = r > RANK_1 ? ourPawns & (s - Up) : Bitboard(0);
neighbours = ourPawns & adjacent_files_bb(s);
phalanx = neighbours & rank_bb(s);
- support = neighbours & rank_bb(s - Up);
+ support = r > RANK_1 ? neighbours & rank_bb(s - Up) : Bitboard(0);
// A pawn is backward when it is behind all pawns of the same color on
- // the adjacent files and cannot safely advance. Phalanx and isolated
- // pawns will be excluded when the pawn is scored.
- backward = !(neighbours & forward_ranks_bb(Them, s))
- && is_ok(s + Up)
- && (stoppers & (leverPush | (s + Up)));
+ // the adjacent files and cannot safely advance.
- backward = !(neighbours & forward_ranks_bb(Them, s + Up))
++ backward = is_ok(s + Up)
++ && !(neighbours & forward_ranks_bb(Them, s + Up))
+ && (stoppers & (leverPush | blocked));
+
+ // Compute additional span if pawn is not backward nor blocked
+ if (!backward && !blocked)
+ e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// A pawn is passed if one of the three following conditions is true:
// (a) there is no stoppers except some levers
// Score this pawn
if (support | phalanx)
{
- int v = Connected[r] * (phalanx ? 3 : 2) * (r == RANK_2 && pos.captures_to_hand() ? 3 : 1) / (opposed ? 2 : 1)
- + 17 * popcount(support);
- int v = Connected[r] * (2 + bool(phalanx) - bool(opposed))
++ int v = Connected[r] * (2 + bool(phalanx) - bool(opposed)) * (r == RANK_2 && pos.captures_to_hand() ? 3 : 1)
+ + 21 * popcount(support);
+ if (r >= RANK_4 && pos.count<PAWN>(Us) > popcount(pos.board_bb()) / 4)
+ v = std::max(v, popcount(support | phalanx) * 50) / (opposed ? 2 : 1);
+
score += make_score(v, v * (r - 2) / 4);
}
else if (!neighbours)
- score -= Isolated * (1 + 2 * pos.must_capture()) + WeakUnopposed * !opposed;
- score -= Isolated
++ score -= Isolated * (1 + 2 * pos.must_capture())
+ + WeakUnopposed * !opposed;
else if (backward)
- score -= Backward + WeakUnopposed * !opposed;
+ score -= Backward
+ + WeakUnopposed * !opposed;
if (!support)
score -= Doubled * doubled
Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq;
castlingRights[Us] = pos.castling_rights(Us);
+ auto compare = [](Score a, Score b) { return mg_value(a) < mg_value(b); };
- Score shelters[3] = { evaluate_shelter<Us>(pos, ksq),
- make_score(-VALUE_INFINITE, 0),
- make_score(-VALUE_INFINITE, 0) };
+ Score shelter = evaluate_shelter<Us>(pos, ksq);
// If we can castle use the bonus after castling if it is bigger
+
if (pos.can_castle(Us & KING_SIDE))
- shelters[1] = evaluate_shelter<Us>(pos, make_square(pos.castling_kingside_file(), Us == WHITE ? RANK_1 : pos.max_rank()));
- shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)), compare);
++ shelter = std::max(shelter, evaluate_shelter<Us>(pos, make_square(pos.castling_kingside_file(), pos.castling_rank(Us))), compare);
if (pos.can_castle(Us & QUEEN_SIDE))
- shelters[2] = evaluate_shelter<Us>(pos, make_square(pos.castling_queenside_file(), Us == WHITE ? RANK_1 : pos.max_rank()));
-
- for (int i : {1, 2})
- if (mg_value(shelters[i]) > mg_value(shelters[0]))
- shelters[0] = shelters[i];
- shelter = std::max(shelter, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)), compare);
++ shelter = std::max(shelter, evaluate_shelter<Us>(pos, make_square(pos.castling_queenside_file(), pos.castling_rank(Us))), compare);
// In endgame we like to bring our king near our closest pawn
Bitboard pawns = pos.pieces(Us, PAWN);
// 2. Active color
ss >> token;
sideToMove = (token == 'w' ? WHITE : BLACK);
+ // Invert side to move for SFEN
+ if (sfen)
+ sideToMove = ~sideToMove;
ss >> token;
- // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
- // Shredder-FEN that uses the letters of the columns on which the rooks began
- // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
- // if an inner rook is associated with the castling right, the castling tag is
- // replaced by the file letter of the involved rook, as for the Shredder-FEN.
- while ((ss >> token) && !isspace(token))
+ // 3-4. Skip parsing castling and en passant flags if not present
+ st->epSquare = SQ_NONE;
+ if (!isdigit(ss.peek()) && !sfen)
{
- Square rsq;
- Color c = islower(token) ? BLACK : WHITE;
- Piece rook = make_piece(c, ROOK);
+ // 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
+ // Shredder-FEN that uses the letters of the columns on which the rooks began
+ // the game instead of KQkq and also X-FEN standard that, in case of Chess960,
+ // if an inner rook is associated with the castling right, the castling tag is
+ // replaced by the file letter of the involved rook, as for the Shredder-FEN.
+ while ((ss >> token) && !isspace(token))
+ {
+ Square rsq;
+ Color c = islower(token) ? BLACK : WHITE;
+ Piece rook = make_piece(c, ROOK);
- token = char(toupper(token));
+ token = char(toupper(token));
- if (token == 'K')
- for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
+ if (token == 'K')
- for (rsq = make_square(FILE_MAX, relative_rank(c, castling_rank(), max_rank())); piece_on(rsq) != rook; --rsq) {}
++ for (rsq = make_square(FILE_MAX, castling_rank(c)); piece_on(rsq) != rook; --rsq) {}
- else if (token == 'Q')
- for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
+ else if (token == 'Q')
- for (rsq = make_square(FILE_A, relative_rank(c, castling_rank(), max_rank())); piece_on(rsq) != rook; ++rsq) {}
++ for (rsq = make_square(FILE_A, castling_rank(c)); piece_on(rsq) != rook; ++rsq) {}
- else if (token >= 'A' && token <= 'H')
- rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
+ else if (token >= 'A' && token <= 'A' + max_file())
- rsq = make_square(File(token - 'A'), relative_rank(c, castling_rank(), max_rank()));
++ rsq = make_square(File(token - 'A'), castling_rank(c));
- else
- continue;
+ else
+ continue;
- set_castling_right(c, rsq);
+ // Set gates (and skip castling rights)
+ if (gating())
+ {
+ st->gatesBB[c] |= rsq;
+ if (token == 'K' || token == 'Q')
- st->gatesBB[c] |= count<KING>(c) ? square<KING>(c) : make_square(FILE_E, relative_rank(c, castling_rank(), max_rank()));
++ st->gatesBB[c] |= count<KING>(c) ? square<KING>(c) : make_square(FILE_E, castling_rank(c));
+ // Do not set castling rights for gates unless there are no pieces in hand,
+ // which means that the file is referring to a chess960 castling right.
+ else if (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand())
+ continue;
+ }
+
+ if (castling_enabled())
+ set_castling_right(c, rsq);
+ }
+
+ // Set castling rights for 960 gating variants
+ if (gating() && castling_enabled())
+ for (Color c : {WHITE, BLACK})
+ if ((gates(c) & pieces(KING)) && !castling_rights(c) && (!seirawan_gating() || count_in_hand(c, ALL_PIECES) || captures_to_hand()))
+ {
+ Bitboard castling_rooks = gates(c) & pieces(ROOK);
+ while (castling_rooks)
+ set_castling_right(c, pop_lsb(&castling_rooks));
+ }
+
+ // counting limit
+ if (counting_rule() && isdigit(ss.peek()))
+ ss >> st->countingLimit;
+
+ // 4. En passant square. Ignore if no pawn capture is possible
+ else if ( ((ss >> col) && (col >= 'a' && col <= 'a' + max_file()))
+ && ((ss >> row) && (row >= '1' && row <= '1' + max_rank())))
+ {
+ st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+
+ if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
+ || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
+ st->epSquare = SQ_NONE;
+ }
}
- // 4. En passant square. Ignore if no pawn capture is possible
- if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
- && ((ss >> row) && (row == '3' || row == '6')))
- {
- st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+ // Check counter for nCheck
+ ss >> std::skipws >> token >> std::noskipws;
- if ( !(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))
- || !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
- st->epSquare = SQ_NONE;
+ if (check_counting())
+ {
+ if (ss.peek() == '+')
+ {
+ st->checksRemaining[WHITE] = CheckCount(std::max(token - '0', 0));
+ ss >> token >> token;
+ st->checksRemaining[BLACK] = CheckCount(std::max(token - '0', 0));
+ }
+ else
+ {
+ // If check count is not provided, assume that the next check wins
+ st->checksRemaining[WHITE] = CheckCount(1);
+ st->checksRemaining[BLACK] = CheckCount(1);
+ ss.putback(token);
+ }
}
else
- st->epSquare = SQ_NONE;
+ ss.putback(token);
// 5-6. Halfmove clock and fullmove number
- ss >> std::skipws >> st->rule50 >> gamePly;
+ if (sfen)
+ {
+ // Pieces in hand for SFEN
+ while ((ss >> token) && !isspace(token))
+ {
+ if (token == '-')
+ continue;
+ else if ((idx = piece_to_char().find(token)) != string::npos)
+ add_to_hand(Piece(idx));
+ }
+ // Move count is in ply for SFEN
+ ss >> std::skipws >> gamePly;
+ gamePly = std::max(gamePly - 1, 0);
+ }
+ else
+ {
+ ss >> std::skipws >> st->rule50 >> gamePly;
- // Convert from fullmove starting from 1 to gamePly starting from 0,
- // handle also common incorrect FEN with fullmove = 0.
- gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ // Convert from fullmove starting from 1 to gamePly starting from 0,
+ // handle also common incorrect FEN with fullmove = 0.
+ gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
+ }
- chess960 = isChess960;
+ // counting rules
+ if (st->countingLimit && st->rule50)
+ {
+ st->countingPly = st->rule50;
+ st->rule50 = 0;
+ }
+
+ chess960 = isChess960 || v->chess960;
thisThread = th;
set_state(st);
void Position::set_castling_right(Color c, Square rfrom) {
- Square kfrom = count<KING>(c) ? square<KING>(c) : make_square(FILE_E, relative_rank(c, castling_rank(), max_rank()));
- Square kfrom = square<KING>(c);
++ Square kfrom = count<KING>(c) ? square<KING>(c) : make_square(FILE_E, castling_rank(c));
CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
st->castlingRights |= cr;
castlingRightsMask[rfrom] |= cr;
castlingRookSquare[cr] = rfrom;
- Square kto = make_square(cr & KING_SIDE ? castling_kingside_file() : castling_queenside_file(),
- relative_rank(c, castling_rank(), max_rank()));
- Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
- Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
++ Square kto = make_square(cr & KING_SIDE ? castling_kingside_file() : castling_queenside_file(), castling_rank(c));
+ Square rto = kto + (cr & KING_SIDE ? WEST : EAST);
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom);
// enemy attacks, it is delayed at a later time: now!
if (type_of(m) == CASTLING)
{
+ // Non-royal pieces can not be impeded from castling
+ if (type_of(piece_on(from)) != KING)
+ return true;
+
// After castling, the rook and king final positions are the same in
// Chess960 as they would be in standard chess.
- to = make_square(to > from ? castling_kingside_file() : castling_queenside_file(), relative_rank(us, castling_rank(), max_rank()));
- to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
++ to = make_square(to > from ? castling_kingside_file() : castling_queenside_file(), castling_rank(us));
Direction step = to > from ? WEST : EAST;
for (Square s = to; s != from; s += step)
{
Square kfrom = from;
Square rfrom = to; // Castling is encoded as 'King captures the rook'
- Square kto = make_square(rfrom > kfrom ? castling_kingside_file() : castling_queenside_file(),
- relative_rank(sideToMove, castling_rank(), max_rank()));
- Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
- Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
++ Square kto = make_square(rfrom > kfrom ? castling_kingside_file() : castling_queenside_file(), castling_rank(sideToMove));
+ Square rto = kto + (rfrom > kfrom ? WEST : EAST);
- return (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
+ return (PseudoAttacks[sideToMove][ROOK][rto] & square<KING>(~sideToMove))
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
}
default:
}
// Move the piece. The tricky Chess960 castling is handled earlier
- if (type_of(m) != CASTLING)
+ if (type_of(m) == DROP)
+ {
+ drop_piece(make_piece(us, in_hand_piece_type(m)), pc, to);
+ st->materialKey ^= Zobrist::psq[pc][pieceCount[pc]-1];
+ if (type_of(pc) != PAWN)
+ st->nonPawnMaterial[us] += PieceValue[MG][pc];
+ // Set castling rights for dropped king or rook
- if (castling_dropped_piece() && relative_rank(us, to, max_rank()) == castling_rank())
++ if (castling_dropped_piece() && rank_of(to) == castling_rank(us))
+ {
+ if (type_of(pc) == KING && file_of(to) == FILE_E)
+ {
+ Bitboard castling_rooks = pieces(us, ROOK)
- & rank_bb(relative_rank(us, castling_rank(), max_rank()))
++ & rank_bb(castling_rank(us))
+ & (file_bb(FILE_A) | file_bb(max_file()));
+ while (castling_rooks)
+ set_castling_right(us, pop_lsb(&castling_rooks));
+ }
+ else if (type_of(pc) == ROOK)
+ {
+ if ( (file_of(to) == FILE_A || file_of(to) == max_file())
- && piece_on(make_square(FILE_E, relative_rank(us, castling_rank(), max_rank()))) == make_piece(us, KING))
++ && piece_on(make_square(FILE_E, castling_rank(us))) == make_piece(us, KING))
+ set_castling_right(us, to);
+ }
+ }
+ }
+ else if (type_of(m) != CASTLING)
move_piece(pc, from, to);
// If the moving piece is a pawn do some special extra work
bool kingSide = to > from;
rfrom = to; // Castling is encoded as "king captures friendly rook"
- to = make_square(kingSide ? castling_kingside_file() : castling_queenside_file(),
- relative_rank(us, castling_rank(), max_rank()));
- rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
- to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
++ to = make_square(kingSide ? castling_kingside_file() : castling_queenside_file(), castling_rank(us));
+ rto = to + (kingSide ? WEST : EAST);
// Remove both pieces first since squares could overlap in Chess960
- remove_piece(make_piece(us, KING), Do ? from : to);
+ Piece castling_piece = piece_on(Do ? from : to);
+ remove_piece(castling_piece, Do ? from : to);
remove_piece(make_piece(us, ROOK), Do ? rfrom : rto);
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us
- put_piece(make_piece(us, KING), Do ? to : from);
+ put_piece(castling_piece, Do ? to : from);
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
}
Position& operator=(const Position&) = delete;
// FEN string input/output
- Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
+ Position& set(const Variant* v, const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th, bool sfen = false);
Position& set(const std::string& code, Color c, StateInfo* si);
- const std::string fen() const;
+ const std::string fen(bool sfen = false) const;
+
+ // Variant rule properties
+ const Variant* variant() const;
+ Rank max_rank() const;
+ File max_file() const;
+ Bitboard board_bb() const;
+ const std::set<PieceType>& piece_types() const;
+ const std::string piece_to_char() const;
+ Rank promotion_rank() const;
+ const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types() const;
+ bool sittuyin_promotion() const;
+ int promotion_limit(PieceType pt) const;
+ PieceType promoted_piece_type(PieceType pt) const;
+ bool piece_promotion_on_capture() const;
+ bool mandatory_pawn_promotion() const;
+ bool mandatory_piece_promotion() const;
+ bool piece_demotion() const;
+ bool endgame_eval() const;
+ bool double_step_enabled() const;
+ Rank double_step_rank() const;
+ bool first_rank_double_steps() const;
+ bool castling_enabled() const;
+ bool castling_dropped_piece() const;
+ File castling_kingside_file() const;
+ File castling_queenside_file() const;
- Rank castling_rank() const;
++ Rank castling_rank(Color c) const;
+ bool checking_permitted() const;
+ bool must_capture() const;
+ bool must_drop() const;
+ bool piece_drops() const;
+ bool drop_loop() const;
+ bool captures_to_hand() const;
+ bool first_rank_drops() const;
+ bool drop_on_top() const;
+ Bitboard drop_region(Color c) const;
+ Bitboard drop_region(Color c, PieceType pt) const;
+ bool sittuyin_rook_drop() const;
+ bool drop_opposite_colored_bishop() const;
+ bool drop_promoted() const;
+ bool shogi_doubled_pawn() const;
+ bool immobility_illegal() const;
+ bool gating() const;
+ bool seirawan_gating() const;
+ bool cambodian_moves() const;
+ // winning conditions
+ int n_move_rule() const;
+ int n_fold_rule() const;
+ Value stalemate_value(int ply = 0) const;
+ Value checkmate_value(int ply = 0) const;
+ Value bare_king_value(int ply = 0) const;
+ Value extinction_value(int ply = 0) const;
+ bool bare_king_move() const;
+ const std::set<PieceType>& extinction_piece_types() const;
+ PieceType capture_the_flag_piece() const;
+ Bitboard capture_the_flag(Color c) const;
+ bool flag_move() const;
+ bool check_counting() const;
+ int connect_n() const;
+ CheckCount checks_remaining(Color c) const;
+ CountingRule counting_rule() const;
+
+ // Variant-specific properties
+ int count_in_hand(Color c, PieceType pt) const;
// Position representation
Bitboard pieces() const;
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
+inline const Variant* Position::variant() const {
+ assert(var != nullptr);
+ return var;
+}
+
+inline Rank Position::max_rank() const {
+ assert(var != nullptr);
+ return var->maxRank;
+}
+
+inline File Position::max_file() const {
+ assert(var != nullptr);
+ return var->maxFile;
+}
+
+inline Bitboard Position::board_bb() const {
+ assert(var != nullptr);
+ return board_size_bb(var->maxFile, var->maxRank);
+}
+
+inline const std::set<PieceType>& Position::piece_types() const {
+ assert(var != nullptr);
+ return var->pieceTypes;
+}
+
+inline const std::string Position::piece_to_char() const {
+ assert(var != nullptr);
+ return var->pieceToChar;
+}
+
+inline Rank Position::promotion_rank() const {
+ assert(var != nullptr);
+ return var->promotionRank;
+}
+
+inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types() const {
+ assert(var != nullptr);
+ return var->promotionPieceTypes;
+}
+
+inline bool Position::sittuyin_promotion() const {
+ assert(var != nullptr);
+ return var->sittuyinPromotion;
+}
+
+inline int Position::promotion_limit(PieceType pt) const {
+ assert(var != nullptr);
+ return var->promotionLimit[pt];
+}
+
+inline PieceType Position::promoted_piece_type(PieceType pt) const {
+ assert(var != nullptr);
+ return var->promotedPieceType[pt];
+}
+
+inline bool Position::piece_promotion_on_capture() const {
+ assert(var != nullptr);
+ return var->piecePromotionOnCapture;
+}
+
+inline bool Position::mandatory_pawn_promotion() const {
+ assert(var != nullptr);
+ return var->mandatoryPawnPromotion;
+}
+
+inline bool Position::mandatory_piece_promotion() const {
+ assert(var != nullptr);
+ return var->mandatoryPiecePromotion;
+}
+
+inline bool Position::piece_demotion() const {
+ assert(var != nullptr);
+ return var->pieceDemotion;
+}
+
+inline bool Position::endgame_eval() const {
+ assert(var != nullptr);
+ return var->endgameEval;
+}
+
+inline bool Position::double_step_enabled() const {
+ assert(var != nullptr);
+ return var->doubleStep;
+}
+
+inline Rank Position::double_step_rank() const {
+ assert(var != nullptr);
+ return var->doubleStepRank;
+}
+
+inline bool Position::first_rank_double_steps() const {
+ assert(var != nullptr);
+ return var->firstRankDoubleSteps;
+}
+
+inline bool Position::castling_enabled() const {
+ assert(var != nullptr);
+ return var->castling;
+}
+
+inline bool Position::castling_dropped_piece() const {
+ assert(var != nullptr);
+ return var->castlingDroppedPiece;
+}
+
+inline File Position::castling_kingside_file() const {
+ assert(var != nullptr);
+ return var->castlingKingsideFile;
+}
+
+inline File Position::castling_queenside_file() const {
+ assert(var != nullptr);
+ return var->castlingQueensideFile;
+}
+
- inline Rank Position::castling_rank() const {
++inline Rank Position::castling_rank(Color c) const {
+ assert(var != nullptr);
- return var->castlingRank;
++ return relative_rank(c, var->castlingRank, max_rank());
+}
+
+inline bool Position::checking_permitted() const {
+ assert(var != nullptr);
+ return var->checking;
+}
+
+inline bool Position::must_capture() const {
+ assert(var != nullptr);
+ return var->mustCapture;
+}
+
+inline bool Position::must_drop() const {
+ assert(var != nullptr);
+ return var->mustDrop;
+}
+
+inline bool Position::piece_drops() const {
+ assert(var != nullptr);
+ return var->pieceDrops;
+}
+
+inline bool Position::drop_loop() const {
+ assert(var != nullptr);
+ return var->dropLoop;
+}
+
+inline bool Position::captures_to_hand() const {
+ assert(var != nullptr);
+ return var->capturesToHand;
+}
+
+inline bool Position::first_rank_drops() const {
+ assert(var != nullptr);
+ return var->firstRankDrops;
+}
+
+inline bool Position::drop_on_top() const {
+ assert(var != nullptr);
+ return var->dropOnTop;
+}
+
+inline Bitboard Position::drop_region(Color c) const {
+ assert(var != nullptr);
+ return c == WHITE ? var->whiteDropRegion : var->blackDropRegion;
+}
+
+inline Bitboard Position::drop_region(Color c, PieceType pt) const {
+ Bitboard b = drop_region(c) & board_bb();
+
+ // Connect4-style drops
+ if (drop_on_top())
+ b &= shift<NORTH>(pieces()) | Rank1BB;
+ // Pawns on back ranks
+ if (pt == PAWN)
+ {
+ b &= ~promotion_zone_bb(c, promotion_rank(), max_rank());
+ if (!first_rank_drops())
+ b &= ~rank_bb(relative_rank(c, RANK_1, max_rank()));
+ }
+ // Doubled shogi pawns
+ if (pt == SHOGI_PAWN && !shogi_doubled_pawn())
+ for (File f = FILE_A; f <= max_file(); ++f)
+ if (file_bb(f) & pieces(c, pt))
+ b &= ~file_bb(f);
+ // Sittuyin rook drops
+ if (pt == ROOK && sittuyin_rook_drop())
+ b &= rank_bb(relative_rank(c, RANK_1, max_rank()));
+
+ return b;
+}
+
+inline bool Position::sittuyin_rook_drop() const {
+ assert(var != nullptr);
+ return var->sittuyinRookDrop;
+}
+
+inline bool Position::drop_opposite_colored_bishop() const {
+ assert(var != nullptr);
+ return var->dropOppositeColoredBishop;
+}
+
+inline bool Position::drop_promoted() const {
+ assert(var != nullptr);
+ return var->dropPromoted;
+}
+
+inline bool Position::shogi_doubled_pawn() const {
+ assert(var != nullptr);
+ return var->shogiDoubledPawn;
+}
+
+inline bool Position::immobility_illegal() const {
+ assert(var != nullptr);
+ return var->immobilityIllegal;
+}
+
+inline bool Position::gating() const {
+ assert(var != nullptr);
+ return var->gating;
+}
+
+inline bool Position::seirawan_gating() const {
+ assert(var != nullptr);
+ return var->seirawanGating;
+}
+
+inline bool Position::cambodian_moves() const {
+ assert(var != nullptr);
+ return var->cambodianMoves;
+}
+
+inline int Position::n_move_rule() const {
+ assert(var != nullptr);
+ return var->nMoveRule;
+}
+
+inline int Position::n_fold_rule() const {
+ assert(var != nullptr);
+ return var->nFoldRule;
+}
+
+inline Value Position::stalemate_value(int ply) const {
+ assert(var != nullptr);
+ return convert_mate_value(var->stalemateValue, ply);
+}
+
+inline Value Position::checkmate_value(int ply) const {
+ assert(var != nullptr);
+ // Check for illegal mate by shogi pawn drop
+ if ( var->shogiPawnDropMateIllegal
+ && !(checkers() & ~pieces(SHOGI_PAWN))
+ && !st->capturedPiece
+ && st->pliesFromNull > 0
+ && (st->materialKey != st->previous->materialKey))
+ {
+ return mate_in(ply);
+ }
+ // Check for shatar mate rule
+ if (var->shatarMateRule)
+ {
+ // Mate by knight is illegal
+ if (!(checkers() & ~pieces(KNIGHT)))
+ return mate_in(ply);
+
+ StateInfo* stp = st;
+ while (stp->checkersBB)
+ {
+ // Return mate score if there is at least one shak in series of checks
+ if (stp->shak)
+ return convert_mate_value(var->checkmateValue, ply);
+
+ if (stp->pliesFromNull < 2)
+ break;
+
+ stp = stp->previous->previous;
+ }
+ // Niol
+ return VALUE_DRAW;
+ }
+ // Return mate value
+ return convert_mate_value(var->checkmateValue, ply);
+}
+
+inline Value Position::bare_king_value(int ply) const {
+ assert(var != nullptr);
+ return convert_mate_value(var->bareKingValue, ply);
+}
+
+inline Value Position::extinction_value(int ply) const {
+ assert(var != nullptr);
+ return convert_mate_value(var->extinctionValue, ply);
+}
+
+inline bool Position::bare_king_move() const {
+ assert(var != nullptr);
+ return var->bareKingMove;
+}
+
+inline const std::set<PieceType>& Position::extinction_piece_types() const {
+ assert(var != nullptr);
+ return var->extinctionPieceTypes;
+}
+
+inline PieceType Position::capture_the_flag_piece() const {
+ assert(var != nullptr);
+ return var->flagPiece;
+}
+
+inline Bitboard Position::capture_the_flag(Color c) const {
+ assert(var != nullptr);
+ return c == WHITE ? var->whiteFlag : var->blackFlag;
+}
+
+inline bool Position::flag_move() const {
+ assert(var != nullptr);
+ return var->flagMove;
+}
+
+inline bool Position::check_counting() const {
+ assert(var != nullptr);
+ return var->checkCounting;
+}
+
+inline int Position::connect_n() const {
+ assert(var != nullptr);
+ return var->connectN;
+}
+
+inline CheckCount Position::checks_remaining(Color c) const {
+ return st->checksRemaining[c];
+}
+
+inline CountingRule Position::counting_rule() const {
+ assert(var != nullptr);
+ return var->countingRule;
+}
+
+inline bool Position::is_immediate_game_end() const {
+ Value result;
+ return is_immediate_game_end(result);
+}
+
+inline bool Position::is_game_end(Value& result, int ply) const {
+ return is_immediate_game_end(result, ply) || is_optional_game_end(result, ply);
+}
+
inline Color Position::side_to_move() const {
return sideToMove;
}
for (const auto& m : MoveList<LEGAL>(pos))
{
+ assert(pos.pseudo_legal(m));
- if (Root && depth <= ONE_PLY)
+ if (Root && depth <= 1)
cnt = 1, nodes++;
else
{
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 && !inCheck) ? evaluate(pos)
- : value_draw(depth, 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
// Step 7. Razoring (~2 Elo)
if ( !rootNode // The required rootNode PV handling is not available in qsearch
- && depth < 2 * ONE_PLY
+ && depth < 2
+ && !pos.must_capture()
+ && !pos.capture_the_flag_piece()
+ && !pos.check_counting()
&& eval <= alpha - RazorMargin)
return qsearch<NT>(pos, ss, alpha, beta);
improving = ss->staticEval >= (ss-2)->staticEval
|| (ss-2)->staticEval == VALUE_NONE;
+ // Skip early pruning in case of mandatory capture
+ if (pos.must_capture() && MoveList<CAPTURES>(pos).size())
+ goto moves_loop;
+
// Step 8. Futility pruning: child node (~30 Elo)
if ( !PvNode
- && depth < 7 * ONE_PLY
+ && depth < 7
- && eval - futility_margin(depth, improving) >= beta
+ && !( pos.extinction_value() == -VALUE_MATE
+ && pos.extinction_piece_types().find(ALL_PIECES) == pos.extinction_piece_types().end())
+ && (pos.checking_permitted() || !pos.capture_the_flag_piece())
+ && eval - futility_margin(depth, improving) * (1 + pos.check_counting()) >= beta
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
return eval;
&& (ss-1)->statScore < 22661
&& eval >= beta
&& eval >= ss->staticEval
- && ss->staticEval >= beta - 33 * depth / ONE_PLY + 299 - improving * 30
+ && ss->staticEval >= beta - 33 * depth + 299 - improving * 30
&& !excludedMove
&& pos.non_pawn_material(us)
+ && (pos.pieces(~us) ^ pos.pieces(~us, PAWN))
+ && (pos.pieces() ^ pos.pieces(BREAKTHROUGH_PIECE) ^ pos.pieces(CLOBBER_PIECE))
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
{
assert(eval - beta >= 0);
// Null move dynamic reduction based on depth and value
- Depth R = ((835 - 150 * !pos.checking_permitted() + 70 * depth / ONE_PLY) / 256 + std::min(int(eval - beta) / 185, 3)) * ONE_PLY;
- Depth R = (835 + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3);
++ Depth R = (835 - 150 * !pos.checking_permitted() + 70 * depth) / 256 + std::min(int(eval - beta) / 185, 3);
ss->currentMove = MOVE_NULL;
- ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
+ ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
pos.do_null_move(st);
// If we have a good enough capture and a reduced search returns a value
// much above beta, we can (almost) safely prune the previous move.
if ( !PvNode
- && depth >= 5 * ONE_PLY
+ && depth >= 5
+ && (pos.pieces() ^ pos.pieces(CLOBBER_PIECE))
&& abs(beta) < VALUE_MATE_IN_MAX_PLY)
{
- Value raisedBeta = std::min(beta + 191 - 46 * improving, VALUE_INFINITE);
+ Value raisedBeta = std::min(beta + 191 * (1 + pos.check_counting() + (pos.extinction_value() != VALUE_NONE)) - 46 * improving, VALUE_INFINITE);
MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory);
int probCutCount = 0;
probCutCount++;
ss->currentMove = move;
- ss->continuationHistory = &thisThread->continuationHistory[history_slot(pos.moved_piece(move))][to_sq(move)];
-
- assert(depth >= 5 * ONE_PLY);
+ ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+ [captureOrPromotion]
- [pos.moved_piece(move)]
++ [history_slot(pos.moved_piece(move))]
+ [to_sq(move)];
pos.do_move(move, st);
}
// Step 11. Internal iterative deepening (~2 Elo)
- if (depth >= (7 - 2 * pos.captures_to_hand()) * ONE_PLY && !ttMove)
- if (depth >= 7 && !ttMove)
++ if (depth >= (7 - 2 * pos.captures_to_hand()) && !ttMove)
{
- search<NT>(pos, ss, alpha, beta, depth - (7 - 2 * pos.captures_to_hand()) * ONE_PLY, cutNode);
- search<NT>(pos, ss, alpha, beta, depth - 7, cutNode);
++ search<NT>(pos, ss, alpha, beta, depth - (7 - 2 * pos.captures_to_hand()), cutNode);
tte = TT.probe(posKey, ttHit);
ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
ss->moveCount = ++moveCount;
if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
- sync_cout << "info depth " << depth / ONE_PLY
+ sync_cout << "info depth " << depth
- << " currmove " << UCI::move(move, pos.is_chess960())
+ << " currmove " << UCI::move(pos, move)
<< " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
if (PvNode)
(ss+1)->pv = nullptr;
// Shuffle extension
else if ( PvNode
+ && pos.n_move_rule()
&& pos.rule50_count() > 18
- && depth < 3 * ONE_PLY
+ && depth < 3
&& ++thisThread->shuffleExts < thisThread->nodes.load(std::memory_order_relaxed) / 4) // To avoid too many extensions
- extension = ONE_PLY;
+ extension = 1;
// Passed pawn extension
else if ( move == ss->killers[0]
&& pos.advanced_pawn_push(move)
&& pos.pawn_passed(us, to_sq(move)))
- extension = ONE_PLY;
+ extension = 1;
+
+ // Castling extension
+ if (type_of(move) == CASTLING)
+ extension = 1;
+ // Losing chess capture extension
+ else if ( pos.must_capture()
+ && pos.capture(move)
+ && MoveList<CAPTURES>(pos).size() == 1)
- extension = ONE_PLY;
++ extension = 1;
+
// Calculate new depth for this move
- newDepth = depth - ONE_PLY + extension;
+ newDepth = depth - 1 + extension;
// Step 14. Pruning at shallow depth (~170 Elo)
if ( !rootNode
&& bestValue > VALUE_MATED_IN_MAX_PLY)
{
// Skip quiet moves if movecount exceeds our FutilityMoveCount threshold
- moveCountPruning = moveCount >= futility_move_count(improving, depth / ONE_PLY)
- moveCountPruning = moveCount >= futility_move_count(improving, depth);
++ moveCountPruning = moveCount >= futility_move_count(improving, depth)
+ || (pos.must_capture() && (moveCountPruning || (pos.capture(move) && pos.legal(move))));
if ( !captureOrPromotion
&& !givesCheck
continue;
}
else if ( !(givesCheck && extension)
- && !pos.see_ge(move, Value(-199) * depth)) // (~20 Elo)
+ && !pos.must_capture()
- && !pos.see_ge(move, Value(-199 - 120 * pos.captures_to_hand()) * (depth / ONE_PLY))) // (~20 Elo)
++ && !pos.see_ge(move, Value(-199 - 120 * pos.captures_to_hand()) * depth)) // (~20 Elo)
continue;
}
// Update the current move (this must be done after singular extension search)
ss->currentMove = move;
- ss->continuationHistory = &thisThread->continuationHistory[history_slot(movedPiece)][to_sq(move)];
+ ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+ [captureOrPromotion]
- [movedPiece]
++ [history_slot(movedPiece)]
+ [to_sq(move)];
// Step 15. Make the move
pos.do_move(move, st, givesCheck);
// Decrease reduction if opponent's move count is high (~10 Elo)
if ((ss-1)->moveCount > 15)
- r -= ONE_PLY;
+ r--;
// Decrease reduction if ttMove has been singularly extended
- r -= singularLMR * ONE_PLY;
+ if (singularLMR)
+ r -= 2;
- if (!captureOrPromotion)
+ if (!captureOrPromotion && !(pos.must_capture() && MoveList<CAPTURES>(pos).size()))
{
// Increase reduction if ttMove is a capture (~0 Elo)
if (ttCapture)
// hence break make_move(). (~5 Elo)
else if ( type_of(move) == NORMAL
&& !pos.see_ge(reverse_move(move)))
- r -= 2 * ONE_PLY;
+ r -= 2;
ss->statScore = thisThread->mainHistory[us][from_to(move)]
- + (*contHist[0])[movedPiece][to_sq(move)]
- + (*contHist[1])[movedPiece][to_sq(move)]
- + (*contHist[3])[movedPiece][to_sq(move)]
+ + (*contHist[0])[history_slot(movedPiece)][to_sq(move)]
+ + (*contHist[1])[history_slot(movedPiece)][to_sq(move)]
+ + (*contHist[3])[history_slot(movedPiece)][to_sq(move)]
- 4729;
// Reset statScore to zero if negative and most stats shows >= 0
}
ss->currentMove = move;
- ss->continuationHistory = &thisThread->continuationHistory[history_slot(pos.moved_piece(move))][to_sq(move)];
+ ss->continuationHistory = &thisThread->continuationHistory[inCheck]
+ [captureOrPromotion]
- [pos.moved_piece(move)]
++ [history_slot(pos.moved_piece(move))]
+ [to_sq(move)];
// Make and search the move
pos.do_move(move, st, givesCheck);
for (Thread* th : *this)
{
th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0;
- th->rootDepth = th->completedDepth = DEPTH_ZERO;
+ th->rootDepth = th->completedDepth = 0;
th->rootMoves = rootMoves;
- th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
+ th->rootPos.set(pos.variant(), pos.fen(), pos.is_chess960(), &setupStates->back(), th);
}
setupStates->back() = tmp;
void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev) {
- assert(d / ONE_PLY * ONE_PLY == d);
-
// Preserve any existing move for the same position
if (m || (k >> 48) != key16)
- move16 = (uint16_t)m;
+ move32 = (uint32_t)m;
// Overwrite less valuable entries
if ( (k >> 48) != key16
struct TTEntry {
- Move move() const { return (Move )move16; }
+ Move move() const { return (Move )move32; }
Value value() const { return (Value)value16; }
Value eval() const { return (Value)eval16; }
- Depth depth() const { return (Depth)(depth8 * int(ONE_PLY)) + DEPTH_OFFSET; }
+ Depth depth() const { return (Depth)depth8 + DEPTH_OFFSET; }
bool is_pv() const { return (bool)(genBound8 & 0x4); }
Bound bound() const { return (Bound)(genBound8 & 0x3); }
void save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev);
extern Value PieceValue[PHASE_NB][PIECE_NB];
- enum Depth : int {
+ typedef int Depth;
- ONE_PLY = 1,
+ enum : int {
- DEPTH_ZERO = 0 * ONE_PLY,
- DEPTH_QS_CHECKS = 0 * ONE_PLY,
- DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
- DEPTH_QS_RECAPTURES = -5 * ONE_PLY,
+ DEPTH_QS_CHECKS = 0,
+ DEPTH_QS_NO_CHECKS = -1,
+ DEPTH_QS_RECAPTURES = -5,
- DEPTH_NONE = -6 * ONE_PLY,
+ DEPTH_NONE = -6,
DEPTH_OFFSET = DEPTH_NONE,
- DEPTH_MAX = MAX_PLY * ONE_PLY
};
- static_assert(!(ONE_PLY & (ONE_PLY - 1)), "ONE_PLY is not a power of 2");
-
enum Square : int {
+#ifdef LARGEBOARDS
+ SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_I1, SQ_J1, SQ_K1, SQ_L1,
+ SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2, SQ_I2, SQ_J2, SQ_K2, SQ_L2,
+ SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3, SQ_I3, SQ_J3, SQ_K3, SQ_L3,
+ SQ_A4, SQ_B4, SQ_C4, SQ_D4, SQ_E4, SQ_F4, SQ_G4, SQ_H4, SQ_I4, SQ_J4, SQ_K4, SQ_L4,
+ SQ_A5, SQ_B5, SQ_C5, SQ_D5, SQ_E5, SQ_F5, SQ_G5, SQ_H5, SQ_I5, SQ_J5, SQ_K5, SQ_L5,
+ SQ_A6, SQ_B6, SQ_C6, SQ_D6, SQ_E6, SQ_F6, SQ_G6, SQ_H6, SQ_I6, SQ_J6, SQ_K6, SQ_L6,
+ SQ_A7, SQ_B7, SQ_C7, SQ_D7, SQ_E7, SQ_F7, SQ_G7, SQ_H7, SQ_I7, SQ_J7, SQ_K7, SQ_L7,
+ SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_E8, SQ_F8, SQ_G8, SQ_H8, SQ_I8, SQ_J8, SQ_K8, SQ_L8,
+ SQ_A9, SQ_B9, SQ_C9, SQ_D9, SQ_E9, SQ_F9, SQ_G9, SQ_H9, SQ_I9, SQ_J9, SQ_K9, SQ_L9,
+ SQ_A10, SQ_B10, SQ_C10, SQ_D10, SQ_E10, SQ_F10, SQ_G10, SQ_H10, SQ_I10, SQ_J10, SQ_K10, SQ_L10,
+#else
SQ_A1, SQ_B1, SQ_C1, SQ_D1, SQ_E1, SQ_F1, SQ_G1, SQ_H1,
SQ_A2, SQ_B2, SQ_C2, SQ_D2, SQ_E2, SQ_F2, SQ_G2, SQ_H2,
SQ_A3, SQ_B3, SQ_C3, SQ_D3, SQ_E3, SQ_F3, SQ_G3, SQ_H3,
}
constexpr Square operator~(Square s) {
+#ifdef LARGEBOARDS
+ return Square(s - FILE_NB * (s / FILE_NB * 2 - RANK_MAX)); // Vertical flip SQ_A1 -> SQ_A10
+#else
return Square(s ^ SQ_A8); // Vertical flip SQ_A1 -> SQ_A8
+#endif
}
- constexpr File operator~(File f) {
- return File(FILE_MAX - f); // Horizontal flip FILE_A -> FILE_H
- }
-
- constexpr Rank operator~(Rank r) {
- return Rank(RANK_MAX - r); // Vertical flip Rank_1 -> Rank_8
- }
-
constexpr Piece operator~(Piece pc) {
- return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
+ return Piece(pc ^ PIECE_TYPE_NB); // Swap color of piece BLACK KNIGHT -> WHITE KNIGHT
}
+ inline File map_to_queenside(File f) {
+ return std::min(f, File(FILE_H - f)); // Map files ABCDEFGH to files ABCDDCBA
+ }
+
constexpr CastlingRights operator&(Color c, CastlingRights cr) {
return CastlingRights((c == WHITE ? WHITE_CASTLING : BLACK_CASTLING) & cr);
}
Position pos;
string token, cmd;
StateListPtr states(new std::deque<StateInfo>(1));
- auto uiThread = std::make_shared<Thread>(0);
- pos.set(StartFEN, false, &states->back(), Threads.main());
+ assert(variants.find(Options["UCI_Variant"])->second != nullptr);
- pos.set(variants.find(Options["UCI_Variant"])->second, variants.find(Options["UCI_Variant"])->second->startFen, false, &states->back(), uiThread.get());
++ pos.set(variants.find(Options["UCI_Variant"])->second, variants.find(Options["UCI_Variant"])->second->startFen, false, &states->back(), Threads.main());
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
else if (token == "setoption") setoption(is);
else if (token == "go") go(pos, is, states);
else if (token == "position") position(pos, is, states);
- else if (token == "ucinewgame") Search::clear();
+ else if (token == "ucinewgame" || token == "usinewgame") Search::clear();
else if (token == "isready") sync_cout << "readyok" << sync_endl;
- // Additional custom non-UCI commands, mainly for debugging
+ // Additional custom non-UCI commands, mainly for debugging.
+ // Do not use these commands during a search!
else if (token == "flip") pos.flip();
else if (token == "bench") bench(pos, is, states);
else if (token == "d") sync_cout << pos << sync_endl;