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

Change the Move enum to a class

This changes the Move enum to a class, this way
all move related functions can be moved into the class
and be more self contained.

closes https://github.com/official-stockfish/Stockfish/pull/4958

No functional change
This commit is contained in:
Disservin 2024-01-01 23:13:18 +01:00
parent 28f8663f39
commit cafbe8e8e8
12 changed files with 241 additions and 222 deletions

View file

@ -34,13 +34,13 @@ ExtMove* make_promotions(ExtMove* moveList, [[maybe_unused]] Square to) {
constexpr bool all = Type == EVASIONS || Type == NON_EVASIONS;
if constexpr (Type == CAPTURES || all)
*moveList++ = make<PROMOTION>(to - D, to, QUEEN);
*moveList++ = Move::make<PROMOTION>(to - D, to, QUEEN);
if constexpr ((Type == CAPTURES && Enemy) || (Type == QUIETS && !Enemy) || all)
{
*moveList++ = make<PROMOTION>(to - D, to, ROOK);
*moveList++ = make<PROMOTION>(to - D, to, BISHOP);
*moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
*moveList++ = Move::make<PROMOTION>(to - D, to, ROOK);
*moveList++ = Move::make<PROMOTION>(to - D, to, BISHOP);
*moveList++ = Move::make<PROMOTION>(to - D, to, KNIGHT);
}
return moveList;
@ -89,13 +89,13 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
while (b1)
{
Square to = pop_lsb(b1);
*moveList++ = make_move(to - Up, to);
*moveList++ = Move(to - Up, to);
}
while (b2)
{
Square to = pop_lsb(b2);
*moveList++ = make_move(to - Up - Up, to);
*moveList++ = Move(to - Up - Up, to);
}
}
@ -128,13 +128,13 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
while (b1)
{
Square to = pop_lsb(b1);
*moveList++ = make_move(to - UpRight, to);
*moveList++ = Move(to - UpRight, to);
}
while (b2)
{
Square to = pop_lsb(b2);
*moveList++ = make_move(to - UpLeft, to);
*moveList++ = Move(to - UpLeft, to);
}
if (pos.ep_square() != SQ_NONE)
@ -150,7 +150,7 @@ ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard ta
assert(b1);
while (b1)
*moveList++ = make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
*moveList++ = Move::make<EN_PASSANT>(pop_lsb(b1), pos.ep_square());
}
}
@ -175,7 +175,7 @@ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Bitboard target)
b &= pos.check_squares(Pt);
while (b)
*moveList++ = make_move(from, pop_lsb(b));
*moveList++ = Move(from, pop_lsb(b));
}
return moveList;
@ -213,12 +213,12 @@ ExtMove* generate_all(const Position& pos, ExtMove* moveList) {
b &= ~attacks_bb<QUEEN>(pos.square<KING>(~Us));
while (b)
*moveList++ = make_move(ksq, pop_lsb(b));
*moveList++ = Move(ksq, pop_lsb(b));
if ((Type == QUIETS || Type == NON_EVASIONS) && pos.can_castle(Us & ANY_CASTLING))
for (CastlingRights cr : {Us & KING_SIDE, Us & QUEEN_SIDE})
if (!pos.castling_impeded(cr) && pos.can_castle(cr))
*moveList++ = make<CASTLING>(ksq, pos.castling_rook_square(cr));
*moveList++ = Move::make<CASTLING>(ksq, pos.castling_rook_square(cr));
}
return moveList;
@ -268,9 +268,9 @@ ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
moveList =
pos.checkers() ? generate<EVASIONS>(pos, moveList) : generate<NON_EVASIONS>(pos, moveList);
while (cur != moveList)
if (((pinned & from_sq(*cur)) || from_sq(*cur) == ksq || type_of(*cur) == EN_PASSANT)
if (((pinned & cur->from_sq()) || cur->from_sq() == ksq || cur->type_of() == EN_PASSANT)
&& !pos.legal(*cur))
*cur = (--moveList)->move;
*cur = *(--moveList);
else
++cur;

View file

@ -37,12 +37,10 @@ enum GenType {
LEGAL
};
struct ExtMove {
Move move;
struct ExtMove: public Move {
int value;
operator Move() const { return move; }
void operator=(Move m) { move = m; }
void operator=(Move m) { data = m.raw(); }
// Inhibit unwanted implicit conversions to Move
// with an ambiguity that yields to a compile error.

View file

@ -166,19 +166,19 @@ void MovePicker::score() {
for (auto& m : *this)
if constexpr (Type == CAPTURES)
m.value =
(7 * int(PieceValue[pos.piece_on(to_sq(m))])
+ (*captureHistory)[pos.moved_piece(m)][to_sq(m)][type_of(pos.piece_on(to_sq(m)))])
(7 * int(PieceValue[pos.piece_on(m.to_sq())])
+ (*captureHistory)[pos.moved_piece(m)][m.to_sq()][type_of(pos.piece_on(m.to_sq()))])
/ 16;
else if constexpr (Type == QUIETS)
{
Piece pc = pos.moved_piece(m);
PieceType pt = type_of(pos.moved_piece(m));
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
// histories
m.value = 2 * (*mainHistory)[pos.side_to_move()][from_to(m)];
m.value = 2 * (*mainHistory)[pos.side_to_move()][m.from_to()];
m.value += 2 * (*pawnHistory)[pawn_structure_index(pos)][pc][to];
m.value += 2 * (*continuationHistory[0])[pc][to];
m.value += (*continuationHistory[1])[pc][to];
@ -211,12 +211,12 @@ void MovePicker::score() {
else // Type == EVASIONS
{
if (pos.capture_stage(m))
m.value = PieceValue[pos.piece_on(to_sq(m))] - Value(type_of(pos.moved_piece(m)))
m.value = PieceValue[pos.piece_on(m.to_sq())] - Value(type_of(pos.moved_piece(m)))
+ (1 << 28);
else
m.value = (*mainHistory)[pos.side_to_move()][from_to(m)]
+ (*continuationHistory[0])[pos.moved_piece(m)][to_sq(m)]
+ (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][to_sq(m)];
m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]
+ (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]
+ (*pawnHistory)[pawn_structure_index(pos)][pos.moved_piece(m)][m.to_sq()];
}
}
@ -235,7 +235,7 @@ Move MovePicker::select(Pred filter) {
cur++;
}
return MOVE_NONE;
return Move::none();
}
// Most important method of the MovePicker class. It
@ -278,8 +278,7 @@ top:
endMoves = std::end(refutations);
// If the countermove is the same as a killer, skip it
if (refutations[0].move == refutations[2].move
|| refutations[1].move == refutations[2].move)
if (refutations[0] == refutations[2] || refutations[1] == refutations[2])
--endMoves;
++stage;
@ -287,7 +286,7 @@ top:
case REFUTATION :
if (select<Next>([&]() {
return *cur != MOVE_NONE && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur);
return *cur != Move::none() && !pos.capture_stage(*cur) && pos.pseudo_legal(*cur);
}))
return *(cur - 1);
++stage;
@ -308,8 +307,7 @@ top:
case QUIET :
if (!skipQuiets && select<Next>([&]() {
return *cur != refutations[0].move && *cur != refutations[1].move
&& *cur != refutations[2].move;
return *cur != refutations[0] && *cur != refutations[1] && *cur != refutations[2];
}))
return *(cur - 1);
@ -343,7 +341,7 @@ top:
// If we did not find any move and we do not try checks, we have finished
if (depth != DEPTH_QS_CHECKS)
return MOVE_NONE;
return Move::none();
++stage;
[[fallthrough]];
@ -360,7 +358,7 @@ top:
}
assert(false);
return MOVE_NONE; // Silence warning
return Move::none(); // Silence warning
}
} // namespace Stockfish

View file

@ -142,7 +142,7 @@ using CorrectionHistory =
// MovePicker class is used to pick one pseudo-legal move at a time from the
// current position. The most important method is next_move(), which returns a
// new pseudo-legal move each time it is called, until there are no moves left,
// when MOVE_NONE is returned. In order to improve the efficiency of the
// when Move::none() is returned. In order to improve the efficiency of the
// alpha-beta algorithm, MovePicker attempts to return the moves which are most
// likely to get a cut-off first.
class MovePicker {

View file

@ -140,14 +140,14 @@ void Position::init() {
for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2))
{
Move move = make_move(s1, s2);
Move move = Move(s1, s2);
Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side;
int i = H1(key);
while (true)
{
std::swap(cuckoo[i], key);
std::swap(cuckooMove[i], move);
if (move == MOVE_NONE) // Arrived at empty slot?
if (move == Move::none()) // Arrived at empty slot?
break;
i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot
}
@ -487,11 +487,11 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
// Tests whether a pseudo-legal move is legal
bool Position::legal(Move m) const {
assert(is_ok(m));
assert(m.is_ok());
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
@ -499,7 +499,7 @@ bool Position::legal(Move m) const {
// En passant captures are a tricky special case. Because they are rather
// uncommon, we do it simply by testing whether the king is attacked after
// the move is made.
if (type_of(m) == EN_PASSANT)
if (m.type_of() == EN_PASSANT)
{
Square ksq = square<KING>(us);
Square capsq = to - pawn_push(us);
@ -516,7 +516,7 @@ bool Position::legal(Move m) const {
// Castling moves generation does not check if the castling path is clear of
// enemy attacks, it is delayed at a later time: now!
if (type_of(m) == CASTLING)
if (m.type_of() == CASTLING)
{
// After castling, the rook and king final positions are the same in
// Chess960 as they would be in standard chess.
@ -529,7 +529,7 @@ bool Position::legal(Move m) const {
// In case of Chess960, verify if the Rook blocks some checks.
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
return !chess960 || !(blockers_for_king(us) & to_sq(m));
return !chess960 || !(blockers_for_king(us) & m.to_sq());
}
// If the moving piece is a king, check whether the destination square is
@ -549,18 +549,18 @@ bool Position::legal(Move m) const {
bool Position::pseudo_legal(const Move m) const {
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
Piece pc = moved_piece(m);
// Use a slower but simpler function for uncommon cases
// yet we skip the legality check of MoveList<LEGAL>().
if (type_of(m) != NORMAL)
if (m.type_of() != NORMAL)
return checkers() ? MoveList<EVASIONS>(*this).contains(m)
: MoveList<NON_EVASIONS>(*this).contains(m);
// Is not a promotion, so the promotion piece must be empty
assert(promotion_type(m) - KNIGHT == NO_PIECE_TYPE);
assert(m.promotion_type() - KNIGHT == NO_PIECE_TYPE);
// If the 'from' square is not occupied by a piece belonging to the side to
// move, the move is obviously not legal.
@ -615,11 +615,11 @@ bool Position::pseudo_legal(const Move m) const {
// Tests whether a pseudo-legal move gives a check
bool Position::gives_check(Move m) const {
assert(is_ok(m));
assert(m.is_ok());
assert(color_of(moved_piece(m)) == sideToMove);
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
// Is there a direct check?
if (check_squares(type_of(piece_on(from))) & to)
@ -627,15 +627,15 @@ bool Position::gives_check(Move m) const {
// Is there a discovered check?
if (blockers_for_king(~sideToMove) & from)
return !aligned(from, to, square<KING>(~sideToMove)) || type_of(m) == CASTLING;
return !aligned(from, to, square<KING>(~sideToMove)) || m.type_of() == CASTLING;
switch (type_of(m))
switch (m.type_of())
{
case NORMAL :
return false;
case PROMOTION :
return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
return attacks_bb(m.promotion_type(), to, pieces() ^ from) & square<KING>(~sideToMove);
// En passant capture with check? We have already handled the case of direct
// checks and ordinary discovered check, so the only case we need to handle
@ -664,7 +664,7 @@ bool Position::gives_check(Move m) const {
// moves should be filtered out before this function is called.
void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
assert(is_ok(m));
assert(m.is_ok());
assert(&newSt != st);
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
@ -691,16 +691,16 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
Color us = sideToMove;
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
Piece pc = piece_on(from);
Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
Piece captured = m.type_of() == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
assert(color_of(pc) == us);
assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
assert(captured == NO_PIECE || color_of(captured) == (m.type_of() != CASTLING ? them : us));
assert(type_of(captured) != KING);
if (type_of(m) == CASTLING)
if (m.type_of() == CASTLING)
{
assert(pc == make_piece(us, KING));
assert(captured == make_piece(us, ROOK));
@ -720,7 +720,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// update non-pawn material.
if (type_of(captured) == PAWN)
{
if (type_of(m) == EN_PASSANT)
if (m.type_of() == EN_PASSANT)
{
capsq -= pawn_push(us);
@ -771,7 +771,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
}
// Move the piece. The tricky Chess960 castling is handled earlier
if (type_of(m) != CASTLING)
if (m.type_of() != CASTLING)
{
dp.piece[0] = pc;
dp.from[0] = from;
@ -791,9 +791,9 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
else if (type_of(m) == PROMOTION)
else if (m.type_of() == PROMOTION)
{
Piece promotion = make_piece(us, promotion_type(m));
Piece promotion = make_piece(us, m.promotion_type());
assert(relative_rank(us, to) == RANK_8);
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
@ -866,22 +866,22 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
// be restored to exactly the same state as before the move was made.
void Position::undo_move(Move m) {
assert(is_ok(m));
assert(m.is_ok());
sideToMove = ~sideToMove;
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
Piece pc = piece_on(to);
assert(empty(from) || type_of(m) == CASTLING);
assert(empty(from) || m.type_of() == CASTLING);
assert(type_of(st->capturedPiece) != KING);
if (type_of(m) == PROMOTION)
if (m.type_of() == PROMOTION)
{
assert(relative_rank(us, to) == RANK_8);
assert(type_of(pc) == promotion_type(m));
assert(type_of(pc) == m.promotion_type());
assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
remove_piece(to);
@ -889,7 +889,7 @@ void Position::undo_move(Move m) {
put_piece(pc, to);
}
if (type_of(m) == CASTLING)
if (m.type_of() == CASTLING)
{
Square rfrom, rto;
do_castling<false>(us, from, to, rfrom, rto);
@ -902,7 +902,7 @@ void Position::undo_move(Move m) {
{
Square capsq = to;
if (type_of(m) == EN_PASSANT)
if (m.type_of() == EN_PASSANT)
{
capsq -= pawn_push(us);
@ -1011,8 +1011,8 @@ void Position::undo_null_move() {
// en passant and promotions.
Key Position::key_after(Move m) const {
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
Piece pc = piece_on(from);
Piece captured = piece_on(to);
Key k = st->key ^ Zobrist::side;
@ -1031,13 +1031,13 @@ Key Position::key_after(Move m) const {
// algorithm similar to alpha-beta pruning with a null window.
bool Position::see_ge(Move m, Value threshold) const {
assert(is_ok(m));
assert(m.is_ok());
// Only deal with normal moves, assume others pass a simple SEE
if (type_of(m) != NORMAL)
if (m.type_of() != NORMAL)
return VALUE_ZERO >= threshold;
Square from = from_sq(m), to = to_sq(m);
Square from = m.from_sq(), to = m.to_sq();
int swap = PieceValue[piece_on(to)] - threshold;
if (swap < 0)
@ -1182,8 +1182,8 @@ bool Position::has_game_cycle(int ply) const {
if ((j = H1(moveKey), cuckoo[j] == moveKey) || (j = H2(moveKey), cuckoo[j] == moveKey))
{
Move move = cuckooMove[j];
Square s1 = from_sq(move);
Square s2 = to_sq(move);
Square s1 = move.from_sq();
Square s2 = move.to_sq();
if (!((between_bb(s1, s2) ^ s2) & pieces()))
{

View file

@ -210,7 +210,7 @@ inline Piece Position::piece_on(Square s) const {
inline bool Position::empty(Square s) const { return piece_on(s) == NO_PIECE; }
inline Piece Position::moved_piece(Move m) const { return piece_on(from_sq(m)); }
inline Piece Position::moved_piece(Move m) const { return piece_on(m.from_sq()); }
inline Bitboard Position::pieces(PieceType pt) const { return byTypeBB[pt]; }
@ -312,16 +312,16 @@ inline int Position::rule50_count() const { return st->rule50; }
inline bool Position::is_chess960() const { return chess960; }
inline bool Position::capture(Move m) const {
assert(is_ok(m));
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == EN_PASSANT;
assert(m.is_ok());
return (!empty(m.to_sq()) && m.type_of() != CASTLING) || m.type_of() == EN_PASSANT;
}
// Returns true if a move is generated from the capture stage, having also
// queen promotions covered, i.e. consistency with the capture stage move generation
// is needed to avoid the generation of duplicate moves.
inline bool Position::capture_stage(Move m) const {
assert(is_ok(m));
return capture(m) || promotion_type(m) == QUEEN;
assert(m.is_ok());
return capture(m) || m.promotion_type() == QUEEN;
}
inline Piece Position::captured_piece() const { return st->capturedPiece; }

View file

@ -130,7 +130,7 @@ struct Skill {
Move pick_best(size_t multiPV);
double level;
Move best = MOVE_NONE;
Move best = Move::none();
};
template<NodeType nodeType>
@ -226,7 +226,7 @@ void MainThread::search() {
if (rootMoves.empty())
{
rootMoves.emplace_back(MOVE_NONE);
rootMoves.emplace_back(Move::none());
sync_cout << "info depth 0 score "
<< UCI::value(rootPos.checkers() ? -VALUE_MATE : VALUE_DRAW) << sync_endl;
}
@ -262,7 +262,7 @@ void MainThread::search() {
Skill(Options["Skill Level"], Options["UCI_LimitStrength"] ? int(Options["UCI_Elo"]) : 0);
if (int(Options["MultiPV"]) == 1 && !Limits.depth && !skill.enabled()
&& rootMoves[0].pv[0] != MOVE_NONE)
&& rootMoves[0].pv[0] != Move::none())
bestThread = Threads.get_best_thread();
bestPreviousScore = bestThread->rootMoves[0].score;
@ -293,7 +293,7 @@ void Thread::search() {
Stack stack[MAX_PLY + 10], *ss = stack + 7;
Move pv[MAX_PLY + 1];
Value alpha, beta, delta;
Move lastBestMove = MOVE_NONE;
Move lastBestMove = Move::none();
Depth lastBestMoveDepth = 0;
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
double timeReduction = 1, totBestMoveChanges = 0;
@ -604,11 +604,11 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
assert(0 <= ss->ply && ss->ply < MAX_PLY);
(ss + 1)->excludedMove = bestMove = MOVE_NONE;
(ss + 2)->killers[0] = (ss + 2)->killers[1] = MOVE_NONE;
(ss + 1)->excludedMove = bestMove = Move::none();
(ss + 2)->killers[0] = (ss + 2)->killers[1] = Move::none();
(ss + 2)->cutoffCnt = 0;
ss->doubleExtensions = (ss - 1)->doubleExtensions;
Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE;
Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE;
ss->statScore = 0;
// Step 4. Transposition table lookup.
@ -618,7 +618,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
: ss->ttHit ? tte->move()
: MOVE_NONE;
: Move::none();
ttCapture = ttMove && pos.capture_stage(ttMove);
// At this point, if excluded, skip straight to step 6, static eval. However,
@ -650,8 +650,8 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
else if (!ttCapture)
{
int penalty = -stat_malus(depth);
thisThread->mainHistory[us][from_to(ttMove)] << penalty;
update_continuation_histories(ss, pos.moved_piece(ttMove), to_sq(ttMove), penalty);
thisThread->mainHistory[us][ttMove.from_to()] << penalty;
update_continuation_histories(ss, pos.moved_piece(ttMove), ttMove.to_sq(), penalty);
}
}
@ -699,7 +699,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
if (b == BOUND_EXACT || (b == BOUND_LOWER ? value >= beta : value <= alpha))
{
tte->save(posKey, value_to_tt(value, ss->ply), ss->ttPv, b,
std::min(MAX_PLY - 1, depth + 6), MOVE_NONE, VALUE_NONE);
std::min(MAX_PLY - 1, depth + 6), Move::none(), VALUE_NONE);
return value;
}
@ -764,17 +764,17 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
ss->staticEval = eval = to_static_eval(newEval);
// Static evaluation is saved as it was before adjustment by correction history
tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
tte->save(posKey, VALUE_NONE, ss->ttPv, BOUND_NONE, DEPTH_NONE, Move::none(),
unadjustedStaticEval);
}
// Use static evaluation difference to improve quiet move ordering (~9 Elo)
if (is_ok((ss - 1)->currentMove) && !(ss - 1)->inCheck && !priorCapture)
if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture)
{
int bonus = std::clamp(-13 * int((ss - 1)->staticEval + ss->staticEval), -1652, 1546);
bonus = bonus > 0 ? 2 * bonus : bonus / 2;
thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)] << bonus;
if (type_of(pos.piece_on(prevSq)) != PAWN && type_of((ss - 1)->currentMove) != PROMOTION)
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus;
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq]
<< bonus / 4;
}
@ -810,9 +810,9 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
return beta > VALUE_TB_LOSS_IN_MAX_PLY ? (eval + beta) / 2 : eval;
// Step 9. Null move search with verification search (~35 Elo)
if (!PvNode && (ss - 1)->currentMove != MOVE_NULL && (ss - 1)->statScore < 17496 && eval >= beta
&& eval >= ss->staticEval && ss->staticEval >= beta - 23 * depth + 304 && !excludedMove
&& pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly
if (!PvNode && (ss - 1)->currentMove != Move::null() && (ss - 1)->statScore < 17496
&& eval >= beta && eval >= ss->staticEval && ss->staticEval >= beta - 23 * depth + 304
&& !excludedMove && pos.non_pawn_material(us) && ss->ply >= thisThread->nmpMinPly
&& beta > VALUE_TB_LOSS_IN_MAX_PLY)
{
assert(eval - beta >= 0);
@ -820,7 +820,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
// Null move dynamic reduction based on depth and eval
Depth R = std::min(int(eval - beta) / 144, 6) + depth / 3 + 4;
ss->currentMove = MOVE_NULL;
ss->currentMove = Move::null();
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
pos.do_null_move(st);
@ -883,7 +883,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
MovePicker mp(pos, ttMove, probCutBeta - ss->staticEval, &captureHistory);
while ((move = mp.next_move()) != MOVE_NONE)
while ((move = mp.next_move()) != Move::none())
if (move != excludedMove && pos.legal(move))
{
assert(pos.capture_stage(move));
@ -894,7 +894,7 @@ Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth, boo
ss->currentMove = move;
ss->continuationHistory =
&thisThread
->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][to_sq(move)];
->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()];
pos.do_move(move, st);
@ -938,7 +938,7 @@ moves_loop: // When in check, search starts here
(ss - 6)->continuationHistory};
Move countermove =
prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : MOVE_NONE;
prevSq != SQ_NONE ? thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] : Move::none();
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &captureHistory, contHist,
&thisThread->pawnHistory, countermove, ss->killers);
@ -953,9 +953,9 @@ moves_loop: // When in check, search starts here
// Step 13. Loop through all pseudo-legal moves until no moves remain
// or a beta cutoff occurs.
while ((move = mp.next_move(moveCountPruning)) != MOVE_NONE)
while ((move = mp.next_move(moveCountPruning)) != Move::none())
{
assert(is_ok(move));
assert(move.is_ok());
if (move == excludedMove)
continue;
@ -1009,10 +1009,10 @@ moves_loop: // When in check, search starts here
// Futility pruning for captures (~2 Elo)
if (!givesCheck && lmrDepth < 7 && !ss->inCheck)
{
Piece capturedPiece = pos.piece_on(to_sq(move));
Piece capturedPiece = pos.piece_on(move.to_sq());
int futilityEval =
ss->staticEval + 238 + 305 * lmrDepth + PieceValue[capturedPiece]
+ captureHistory[movedPiece][to_sq(move)][type_of(capturedPiece)] / 7;
+ captureHistory[movedPiece][move.to_sq()][type_of(capturedPiece)] / 7;
if (futilityEval < alpha)
continue;
}
@ -1024,15 +1024,16 @@ moves_loop: // When in check, search starts here
else
{
int history =
(*contHist[0])[movedPiece][to_sq(move)] + (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)]
+ thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][to_sq(move)];
(*contHist[0])[movedPiece][move.to_sq()]
+ (*contHist[1])[movedPiece][move.to_sq()]
+ (*contHist[3])[movedPiece][move.to_sq()]
+ thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()];
// Continuation history based pruning (~2 Elo)
if (lmrDepth < 6 && history < -3752 * depth)
continue;
history += 2 * thisThread->mainHistory[us][from_to(move)];
history += 2 * thisThread->mainHistory[us][move.from_to()];
lmrDepth += history / 7838;
lmrDepth = std::max(lmrDepth, -1);
@ -1077,7 +1078,7 @@ moves_loop: // When in check, search starts here
ss->excludedMove = move;
value =
search<NonPV>(pos, ss, singularBeta - 1, singularBeta, singularDepth, cutNode);
ss->excludedMove = MOVE_NONE;
ss->excludedMove = Move::none();
if (value < singularBeta)
{
@ -1125,12 +1126,13 @@ moves_loop: // When in check, search starts here
// Quiet ttMove extensions (~1 Elo)
else if (PvNode && move == ttMove && move == ss->killers[0]
&& (*contHist[0])[movedPiece][to_sq(move)] >= 4325)
&& (*contHist[0])[movedPiece][move.to_sq()] >= 4325)
extension = 1;
// Recapture extensions (~1 Elo)
else if (PvNode && move == ttMove && to_sq(move) == prevSq
&& captureHistory[movedPiece][to_sq(move)][type_of(pos.piece_on(to_sq(move)))]
else if (PvNode && move == ttMove && move.to_sq() == prevSq
&& captureHistory[movedPiece][move.to_sq()]
[type_of(pos.piece_on(move.to_sq()))]
> 4146)
extension = 1;
}
@ -1145,7 +1147,7 @@ moves_loop: // When in check, search starts here
// Update the current move (this must be done after singular extension search)
ss->currentMove = move;
ss->continuationHistory =
&thisThread->continuationHistory[ss->inCheck][capture][movedPiece][to_sq(move)];
&thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()];
// Step 16. Make the move
pos.do_move(move, st, givesCheck);
@ -1187,10 +1189,10 @@ moves_loop: // When in check, search starts here
else if (move == ttMove)
r = 0;
ss->statScore = 2 * thisThread->mainHistory[us][from_to(move)]
+ (*contHist[0])[movedPiece][to_sq(move)]
+ (*contHist[1])[movedPiece][to_sq(move)]
+ (*contHist[3])[movedPiece][to_sq(move)] - 3817;
ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()]
+ (*contHist[0])[movedPiece][move.to_sq()]
+ (*contHist[1])[movedPiece][move.to_sq()]
+ (*contHist[3])[movedPiece][move.to_sq()] - 3817;
// Decrease/increase reduction for moves with a good/bad history (~25 Elo)
r -= ss->statScore / 14767;
@ -1229,7 +1231,7 @@ moves_loop: // When in check, search starts here
: value >= beta ? stat_bonus(newDepth)
: 0;
update_continuation_histories(ss, movedPiece, to_sq(move), bonus);
update_continuation_histories(ss, movedPiece, move.to_sq(), bonus);
}
}
@ -1249,7 +1251,7 @@ moves_loop: // When in check, search starts here
if (PvNode && (moveCount == 1 || value > alpha))
{
(ss + 1)->pv = pv;
(ss + 1)->pv[0] = MOVE_NONE;
(ss + 1)->pv[0] = Move::none();
value = -search<PV>(pos, ss + 1, -beta, -alpha, newDepth, false);
}
@ -1296,7 +1298,7 @@ moves_loop: // When in check, search starts here
assert((ss + 1)->pv);
for (Move* m = (ss + 1)->pv; *m != MOVE_NONE; ++m)
for (Move* m = (ss + 1)->pv; *m != Move::none(); ++m)
rm.pv.push_back(*m);
// We record how often the best move has been changed in each iteration.
@ -1375,7 +1377,7 @@ moves_loop: // When in check, search starts here
+ ((ss - 1)->moveCount > 10);
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
stat_bonus(depth) * bonus);
thisThread->mainHistory[~us][from_to((ss - 1)->currentMove)]
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()]
<< stat_bonus(depth) * bonus / 2;
}
@ -1451,11 +1453,11 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
if (PvNode)
{
(ss + 1)->pv = pv;
ss->pv[0] = MOVE_NONE;
ss->pv[0] = Move::none();
}
Thread* thisThread = pos.this_thread();
bestMove = MOVE_NONE;
bestMove = Move::none();
ss->inCheck = pos.checkers();
moveCount = 0;
@ -1476,7 +1478,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
posKey = pos.key();
tte = TT.probe(posKey, ss->ttHit);
ttValue = ss->ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = ss->ttHit ? tte->move() : MOVE_NONE;
ttMove = ss->ttHit ? tte->move() : Move::none();
pvHit = ss->ttHit && tte->is_pv();
// At non-PV nodes we check for an early TT cutoff
@ -1513,7 +1515,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
{
// In case of null move search, use previous static eval with a different sign
unadjustedStaticEval = ss->staticEval = bestValue =
(ss - 1)->currentMove != MOVE_NULL ? evaluate(pos) : -(ss - 1)->staticEval;
(ss - 1)->currentMove != Move::null() ? evaluate(pos) : -(ss - 1)->staticEval;
Value newEval =
ss->staticEval
@ -1527,7 +1529,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
{
if (!ss->ttHit)
tte->save(posKey, value_to_tt(bestValue, ss->ply), false, BOUND_LOWER, DEPTH_NONE,
MOVE_NONE, unadjustedStaticEval);
Move::none(), unadjustedStaticEval);
return bestValue;
}
@ -1545,7 +1547,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions, and other checks (only if depth >= DEPTH_QS_CHECKS)
// will be generated.
Square prevSq = is_ok((ss - 1)->currentMove) ? to_sq((ss - 1)->currentMove) : SQ_NONE;
Square prevSq = ((ss - 1)->currentMove).is_ok() ? ((ss - 1)->currentMove).to_sq() : SQ_NONE;
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory, &thisThread->captureHistory,
contHist, &thisThread->pawnHistory);
@ -1553,9 +1555,9 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
// Step 5. Loop through all pseudo-legal moves until no moves remain
// or a beta cutoff occurs.
while ((move = mp.next_move()) != MOVE_NONE)
while ((move = mp.next_move()) != Move::none())
{
assert(is_ok(move));
assert(move.is_ok());
// Check for legality
if (!pos.legal(move))
@ -1570,13 +1572,13 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
if (bestValue > VALUE_TB_LOSS_IN_MAX_PLY && pos.non_pawn_material(us))
{
// Futility pruning and moveCount pruning (~10 Elo)
if (!givesCheck && to_sq(move) != prevSq && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY
&& type_of(move) != PROMOTION)
if (!givesCheck && move.to_sq() != prevSq && futilityBase > VALUE_TB_LOSS_IN_MAX_PLY
&& move.type_of() != PROMOTION)
{
if (moveCount > 2)
continue;
futilityValue = futilityBase + PieceValue[pos.piece_on(to_sq(move))];
futilityValue = futilityBase + PieceValue[pos.piece_on(move.to_sq())];
// If static eval + value of piece we are going to capture is much lower
// than alpha we can prune this move.
@ -1610,8 +1612,8 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
break;
// Continuation history based pruning (~3 Elo)
if (!capture && (*contHist[0])[pos.moved_piece(move)][to_sq(move)] < 0
&& (*contHist[1])[pos.moved_piece(move)][to_sq(move)] < 0)
if (!capture && (*contHist[0])[pos.moved_piece(move)][move.to_sq()] < 0
&& (*contHist[1])[pos.moved_piece(move)][move.to_sq()] < 0)
continue;
// Do not search moves with bad enough SEE values (~5 Elo)
@ -1626,7 +1628,7 @@ Value qsearch(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth) {
ss->currentMove = move;
ss->continuationHistory =
&thisThread
->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][to_sq(move)];
->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()];
quietCheckEvasions += !capture && ss->inCheck;
@ -1738,9 +1740,9 @@ Value value_from_tt(Value v, int ply, int r50c) {
// Adds current move and appends child pv[]
void update_pv(Move* pv, Move move, const Move* childPv) {
for (*pv++ = move; childPv && *childPv != MOVE_NONE;)
for (*pv++ = move; childPv && *childPv != Move::none();)
*pv++ = *childPv++;
*pv = MOVE_NONE;
*pv = Move::none();
}
@ -1775,25 +1777,25 @@ void update_all_stats(const Position& pos,
update_quiet_stats(pos, ss, bestMove, bestMoveBonus);
int pIndex = pawn_structure_index(pos);
thisThread->pawnHistory[pIndex][moved_piece][to_sq(bestMove)] << quietMoveBonus;
thisThread->pawnHistory[pIndex][moved_piece][bestMove.to_sq()] << quietMoveBonus;
// Decrease stats for all non-best quiet moves
for (int i = 0; i < quietCount; ++i)
{
thisThread
->pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][to_sq(quietsSearched[i])]
->pawnHistory[pIndex][pos.moved_piece(quietsSearched[i])][quietsSearched[i].to_sq()]
<< -quietMoveMalus;
thisThread->mainHistory[us][from_to(quietsSearched[i])] << -quietMoveMalus;
thisThread->mainHistory[us][quietsSearched[i].from_to()] << -quietMoveMalus;
update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]),
to_sq(quietsSearched[i]), -quietMoveMalus);
quietsSearched[i].to_sq(), -quietMoveMalus);
}
}
else
{
// Increase stats for the best move in case it was a capture move
captured = type_of(pos.piece_on(to_sq(bestMove)));
captureHistory[moved_piece][to_sq(bestMove)][captured] << quietMoveBonus;
captured = type_of(pos.piece_on(bestMove.to_sq()));
captureHistory[moved_piece][bestMove.to_sq()][captured] << quietMoveBonus;
}
// Extra penalty for a quiet early move that was not a TT move or
@ -1808,8 +1810,8 @@ void update_all_stats(const Position& pos,
for (int i = 0; i < captureCount; ++i)
{
moved_piece = pos.moved_piece(capturesSearched[i]);
captured = type_of(pos.piece_on(to_sq(capturesSearched[i])));
captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -quietMoveMalus;
captured = type_of(pos.piece_on(capturesSearched[i].to_sq()));
captureHistory[moved_piece][capturesSearched[i].to_sq()][captured] << -quietMoveMalus;
}
}
@ -1823,7 +1825,7 @@ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
// Only update the first 2 continuation histories if we are in check
if (ss->inCheck && i > 2)
break;
if (is_ok((ss - i)->currentMove))
if (((ss - i)->currentMove).is_ok())
(*(ss - i)->continuationHistory)[pc][to] << bonus / (1 + 3 * (i == 3));
}
}
@ -1841,13 +1843,13 @@ void update_quiet_stats(const Position& pos, Stack* ss, Move move, int bonus) {
Color us = pos.side_to_move();
Thread* thisThread = pos.this_thread();
thisThread->mainHistory[us][from_to(move)] << bonus;
update_continuation_histories(ss, pos.moved_piece(move), to_sq(move), bonus);
thisThread->mainHistory[us][move.from_to()] << bonus;
update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus);
// Update countermove history
if (is_ok((ss - 1)->currentMove))
if (((ss - 1)->currentMove).is_ok())
{
Square prevSq = to_sq((ss - 1)->currentMove);
Square prevSq = ((ss - 1)->currentMove).to_sq();
thisThread->counterMoves[pos.piece_on(prevSq)][prevSq] = move;
}
}
@ -1987,7 +1989,7 @@ bool RootMove::extract_ponder_from_tt(Position& pos) {
assert(pv.size() == 1);
if (pv[0] == MOVE_NONE)
if (pv[0] == Move::none())
return false;
pos.do_move(pv[0], st);

View file

@ -24,7 +24,7 @@
#include <cstdlib>
#include <deque>
#include <initializer_list>
#include <map>
#include <unordered_map>
#include <memory>
#include <utility>
@ -66,7 +66,7 @@ Thread::~Thread() {
// Reset histories, usually before a new game
void Thread::clear() {
counterMoves.fill(MOVE_NONE);
counterMoves.fill(Move::none());
mainHistory.fill(0);
captureHistory.fill(0);
pawnHistory.fill(0);
@ -221,7 +221,7 @@ void ThreadPool::start_thinking(Position& pos,
Thread* ThreadPool::get_best_thread() const {
Thread* bestThread = threads.front();
std::map<Move, int64_t> votes;
std::unordered_map<Move, int64_t, Move::MoveHash> votes;
Value minScore = VALUE_NONE;
// Find the minimum score of all threads

View file

@ -39,7 +39,7 @@ void TTEntry::save(Key k, Value v, bool pv, Bound b, Depth d, Move m, Value ev)
// Preserve any existing move for the same position
if (m || uint16_t(k) != key16)
move16 = uint16_t(m);
move16 = m;
// Overwrite less valuable entries (cheapest checks first)
if (b == BOUND_EXACT || uint16_t(k) != key16 || d - DEPTH_OFFSET + 2 * pv > depth8 - 4)

View file

@ -53,7 +53,7 @@ struct TTEntry {
uint16_t key16;
uint8_t depth8;
uint8_t genBound8;
uint16_t move16;
Move move16;
int16_t value16;
int16_t eval16;
};

View file

@ -108,30 +108,6 @@ using Bitboard = uint64_t;
constexpr int MAX_MOVES = 256;
constexpr int MAX_PLY = 246;
// A move needs 16 bits to be stored
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
// NOTE: en passant bit is set only when a pawn can be captured
//
// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
// any normal move destination square is always different from origin square
// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move : int {
MOVE_NONE,
MOVE_NULL = 65
};
enum MoveType {
NORMAL,
PROMOTION = 1 << 14,
EN_PASSANT = 2 << 14,
CASTLING = 3 << 14
};
enum Color {
WHITE,
BLACK,
@ -353,8 +329,6 @@ inline Color color_of(Piece pc) {
return Color(pc >> 3);
}
constexpr bool is_ok(Move m) { return m != MOVE_NONE && m != MOVE_NULL; }
constexpr bool is_ok(Square s) { return s >= SQ_A1 && s <= SQ_H8; }
constexpr File file_of(Square s) { return File(s & 7); }
@ -369,34 +343,81 @@ constexpr Rank relative_rank(Color c, Square s) { return relative_rank(c, rank_o
constexpr Direction pawn_push(Color c) { return c == WHITE ? NORTH : SOUTH; }
constexpr Square from_sq(Move m) {
assert(is_ok(m));
return Square((m >> 6) & 0x3F);
}
constexpr Square to_sq(Move m) {
assert(is_ok(m));
return Square(m & 0x3F);
}
constexpr int from_to(Move m) { return m & 0xFFF; }
constexpr MoveType type_of(Move m) { return MoveType(m & (3 << 14)); }
constexpr PieceType promotion_type(Move m) { return PieceType(((m >> 12) & 3) + KNIGHT); }
constexpr Move make_move(Square from, Square to) { return Move((from << 6) + to); }
template<MoveType T>
constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
}
// Based on a congruential pseudo-random number generator
constexpr Key make_key(uint64_t seed) {
return seed * 6364136223846793005ULL + 1442695040888963407ULL;
}
enum MoveType {
NORMAL,
PROMOTION = 1 << 14,
EN_PASSANT = 2 << 14,
CASTLING = 3 << 14
};
// A move needs 16 bits to be stored
//
// bit 0- 5: destination square (from 0 to 63)
// bit 6-11: origin square (from 0 to 63)
// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
// bit 14-15: special move flag: promotion (1), en passant (2), castling (3)
// NOTE: en passant bit is set only when a pawn can be captured
//
// Special cases are Move::none() and Move::null(). We can sneak these in because in
// any normal move destination square is always different from origin square
// while Move::none() and Move::null() have the same origin and destination square.
class Move {
public:
Move() = default;
constexpr explicit Move(std::uint16_t d) :
data(d) {}
constexpr Move(Square from, Square to) :
data((from << 6) + to) {}
template<MoveType T>
static constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
}
constexpr Square from_sq() const {
assert(is_ok());
return Square((data >> 6) & 0x3F);
}
constexpr Square to_sq() const {
assert(is_ok());
return Square(data & 0x3F);
}
constexpr int from_to() const { return data & 0xFFF; }
constexpr MoveType type_of() const { return MoveType(data & (3 << 14)); }
constexpr PieceType promotion_type() const { return PieceType(((data >> 12) & 3) + KNIGHT); }
constexpr bool is_ok() const { return none().data != data && null().data != data; }
static constexpr Move null() { return Move(65); }
static constexpr Move none() { return Move(0); }
constexpr bool operator==(const Move& m) const { return data == m.data; }
constexpr bool operator!=(const Move& m) const { return data != m.data; }
constexpr explicit operator bool() const { return data != 0; }
constexpr std::uint16_t raw() const { return data; }
struct MoveHash {
std::size_t operator()(const Move& m) const { return m.data; }
};
protected:
std::uint16_t data;
};
} // namespace Stockfish
#endif // #ifndef TYPES_H_INCLUDED

View file

@ -75,7 +75,7 @@ void position(Position& pos, std::istringstream& is, StateListPtr& states) {
pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main());
// Parse the move list, if any
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
while (is >> token && (m = UCI::to_move(pos, token)) != Move::none())
{
states->emplace_back();
pos.do_move(m, states->back());
@ -395,22 +395,22 @@ std::string UCI::square(Square s) {
// Internally, all castling moves are always encoded as 'king captures rook'.
std::string UCI::move(Move m, bool chess960) {
if (m == MOVE_NONE)
if (m == Move::none())
return "(none)";
if (m == MOVE_NULL)
if (m == Move::null())
return "0000";
Square from = from_sq(m);
Square to = to_sq(m);
Square from = m.from_sq();
Square to = m.to_sq();
if (type_of(m) == CASTLING && !chess960)
if (m.type_of() == CASTLING && !chess960)
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
std::string move = UCI::square(from) + UCI::square(to);
if (type_of(m) == PROMOTION)
move += " pnbrqk"[promotion_type(m)];
if (m.type_of() == PROMOTION)
move += " pnbrqk"[m.promotion_type()];
return move;
}
@ -427,7 +427,7 @@ Move UCI::to_move(const Position& pos, std::string& str) {
if (str == UCI::move(m, pos.is_chess960()))
return m;
return MOVE_NONE;
return Move::none();
}
} // namespace Stockfish