From f3cf782bebb35bc8ea0fa231682e61f30fda7c91 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 10 Jan 2015 11:36:44 +0000 Subject: [PATCH] Remove more unrelated files --- src/movegen.cpp | 418 --------------- src/movegen.h | 58 --- src/movepick.cpp | 385 -------------- src/movepick.h | 112 ---- src/pawns.cpp | 312 ----------- src/pawns.h | 87 ---- src/position.cpp | 1280 ---------------------------------------------- src/position.h | 439 ---------------- 8 files changed, 3091 deletions(-) delete mode 100644 src/movegen.cpp delete mode 100644 src/movegen.h delete mode 100644 src/movepick.cpp delete mode 100644 src/movepick.h delete mode 100644 src/pawns.cpp delete mode 100644 src/pawns.h delete mode 100644 src/position.cpp delete mode 100644 src/position.h diff --git a/src/movegen.cpp b/src/movegen.cpp deleted file mode 100644 index 6f4ed822..00000000 --- a/src/movegen.cpp +++ /dev/null @@ -1,418 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include - -#include "movegen.h" -#include "position.h" - -namespace { - - template - ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us, const CheckInfo* ci) { - - static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO); - - if (pos.castling_impeded(Cr) || !pos.can_castle(Cr)) - return moveList; - - // After castling, the rook and king final positions are the same in Chess960 - // as they would be in standard chess. - Square kfrom = pos.king_square(us); - Square rfrom = pos.castling_rook_square(Cr); - Square kto = relative_square(us, KingSide ? SQ_G1 : SQ_C1); - Bitboard enemies = pos.pieces(~us); - - assert(!pos.checkers()); - - const Square K = Chess960 ? kto > kfrom ? DELTA_W : DELTA_E - : KingSide ? DELTA_W : DELTA_E; - - for (Square s = kto; s != kfrom; s += K) - if (pos.attackers_to(s) & enemies) - return moveList; - - // Because we generate only legal castling moves we need to verify that - // when moving the castling rook we do not discover some hidden checker. - // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1. - if (Chess960 && (attacks_bb(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN))) - return moveList; - - Move m = make(kfrom, rfrom); - - if (Checks && !pos.gives_check(m, *ci)) - return moveList; - - (moveList++)->move = m; - - return moveList; - } - - - template - inline ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) { - - if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - (moveList++)->move = make(to - Delta, to, QUEEN); - - if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS) - { - (moveList++)->move = make(to - Delta, to, ROOK); - (moveList++)->move = make(to - Delta, to, BISHOP); - (moveList++)->move = make(to - Delta, to, KNIGHT); - } - - // Knight promotion is the only promotion that can give a direct check - // that's not already included in the queen promotion. - if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq)) - (moveList++)->move = make(to - Delta, to, KNIGHT); - else - (void)ci; // Silence a warning under MSVC - - return moveList; - } - - - template - ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, - Bitboard target, const CheckInfo* ci) { - - // Compute our parametrized parameters at compile time, named according to - // the point of view of white side. - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB); - const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); - const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); - const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); - const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); - const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); - - Bitboard emptySquares; - - Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB; - Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB; - - Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target: - Type == CAPTURES ? target : pos.pieces(Them)); - - // Single and double pawn pushes, no promotions - if (Type != CAPTURES) - { - emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()); - - Bitboard b1 = shift_bb(pawnsNotOn7) & emptySquares; - Bitboard b2 = shift_bb(b1 & TRank3BB) & emptySquares; - - if (Type == EVASIONS) // Consider only blocking squares - { - b1 &= target; - b2 &= target; - } - - if (Type == QUIET_CHECKS) - { - b1 &= pos.attacks_from(ci->ksq, Them); - b2 &= pos.attacks_from(ci->ksq, Them); - - // Add pawn pushes which give discovered check. This is possible only - // if the pawn is not on the same file as the enemy king, because we - // don't generate captures. Note that a possible discovery check - // promotion has been already generated amongst the captures. - if (pawnsNotOn7 & ci->dcCandidates) - { - Bitboard dc1 = shift_bb(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq); - Bitboard dc2 = shift_bb(dc1 & TRank3BB) & emptySquares; - - b1 |= dc1; - b2 |= dc2; - } - } - - while (b1) - { - Square to = pop_lsb(&b1); - (moveList++)->move = make_move(to - Up, to); - } - - while (b2) - { - Square to = pop_lsb(&b2); - (moveList++)->move = make_move(to - Up - Up, to); - } - } - - // Promotions and underpromotions - if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB))) - { - if (Type == CAPTURES) - emptySquares = ~pos.pieces(); - - if (Type == EVASIONS) - emptySquares &= target; - - Bitboard b1 = shift_bb(pawnsOn7) & enemies; - Bitboard b2 = shift_bb(pawnsOn7) & enemies; - Bitboard b3 = shift_bb(pawnsOn7) & emptySquares; - - while (b1) - moveList = make_promotions(moveList, pop_lsb(&b1), ci); - - while (b2) - moveList = make_promotions(moveList, pop_lsb(&b2), ci); - - while (b3) - moveList = make_promotions(moveList, pop_lsb(&b3), ci); - } - - // Standard and en-passant captures - if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) - { - Bitboard b1 = shift_bb(pawnsNotOn7) & enemies; - Bitboard b2 = shift_bb(pawnsNotOn7) & enemies; - - while (b1) - { - Square to = pop_lsb(&b1); - (moveList++)->move = make_move(to - Right, to); - } - - while (b2) - { - Square to = pop_lsb(&b2); - (moveList++)->move = make_move(to - Left, to); - } - - if (pos.ep_square() != SQ_NONE) - { - assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6)); - - // An en passant capture can be an evasion only if the checking piece - // is the double pushed pawn and so is in the target. Otherwise this - // is a discovery check and we are forced to do otherwise. - if (Type == EVASIONS && !(target & (pos.ep_square() - Up))) - return moveList; - - b1 = pawnsNotOn7 & pos.attacks_from(pos.ep_square(), Them); - - assert(b1); - - while (b1) - (moveList++)->move = make(pop_lsb(&b1), pos.ep_square()); - } - } - - return moveList; - } - - - template FORCE_INLINE - ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, - Bitboard target, const CheckInfo* ci) { - - assert(Pt != KING && Pt != PAWN); - - const Square* pl = pos.list(us); - - for (Square from = *pl; from != SQ_NONE; from = *++pl) - { - if (Checks) - { - if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN) - && !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt])) - continue; - - if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from)) - continue; - } - - Bitboard b = pos.attacks_from(from) & target; - - if (Checks) - b &= ci->checkSq[Pt]; - - while (b) - (moveList++)->move = make_move(from, pop_lsb(&b)); - } - - return moveList; - } - - - template FORCE_INLINE - ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target, - const CheckInfo* ci = NULL) { - - const bool Checks = Type == QUIET_CHECKS; - - moveList = generate_pawn_moves(pos, moveList, target, ci); - moveList = generate_moves(pos, moveList, Us, target, ci); - moveList = generate_moves(pos, moveList, Us, target, ci); - moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target, ci); - moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target, ci); - - if (Type != QUIET_CHECKS && Type != EVASIONS) - { - Square ksq = pos.king_square(Us); - Bitboard b = pos.attacks_from(ksq) & target; - while (b) - (moveList++)->move = make_move(ksq, pop_lsb(&b)); - } - - if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us)) - { - if (pos.is_chess960()) - { - moveList = generate_castling::right, Checks, true>(pos, moveList, Us, ci); - moveList = generate_castling::right, Checks, true>(pos, moveList, Us, ci); - } - else - { - moveList = generate_castling::right, Checks, false>(pos, moveList, Us, ci); - moveList = generate_castling::right, Checks, false>(pos, moveList, Us, ci); - } - } - - return moveList; - } - -} // namespace - - -/// generate generates all pseudo-legal captures and queen -/// promotions. Returns a pointer to the end of the move list. -/// -/// generate generates all pseudo-legal non-captures and -/// underpromotions. Returns a pointer to the end of the move list. -/// -/// generate generates all pseudo-legal captures and -/// non-captures. Returns a pointer to the end of the move list. - -template -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS); - assert(!pos.checkers()); - - Color us = pos.side_to_move(); - - Bitboard target = Type == CAPTURES ? pos.pieces(~us) - : Type == QUIETS ? ~pos.pieces() - : Type == NON_EVASIONS ? ~pos.pieces(us) : 0; - - return us == WHITE ? generate_all(pos, moveList, target) - : generate_all(pos, moveList, target); -} - -// Explicit template instantiations -template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); -template ExtMove* generate(const Position&, ExtMove*); - - -/// generate generates all pseudo-legal non-captures and knight -/// underpromotions that give check. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(!pos.checkers()); - - Color us = pos.side_to_move(); - CheckInfo ci(pos); - Bitboard dc = ci.dcCandidates; - - while (dc) - { - Square from = pop_lsb(&dc); - PieceType pt = type_of(pos.piece_on(from)); - - if (pt == PAWN) - continue; // Will be generated together with direct checks - - Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces(); - - if (pt == KING) - b &= ~PseudoAttacks[QUEEN][ci.ksq]; - - while (b) - (moveList++)->move = make_move(from, pop_lsb(&b)); - } - - return us == WHITE ? generate_all(pos, moveList, ~pos.pieces(), &ci) - : generate_all(pos, moveList, ~pos.pieces(), &ci); -} - - -/// generate generates all pseudo-legal check evasions when the side -/// to move is in check. Returns a pointer to the end of the move list. -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - assert(pos.checkers()); - - Color us = pos.side_to_move(); - Square ksq = pos.king_square(us); - Bitboard sliderAttacks = 0; - Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN); - - // Find all the squares attacked by slider checkers. We will remove them from - // the king evasions in order to skip known illegal moves, which avoids any - // useless legality checks later on. - while (sliders) - { - Square checksq = pop_lsb(&sliders); - sliderAttacks |= LineBB[checksq][ksq] ^ checksq; - } - - // Generate evasions for king, capture and non capture moves - Bitboard b = pos.attacks_from(ksq) & ~pos.pieces(us) & ~sliderAttacks; - while (b) - (moveList++)->move = make_move(ksq, pop_lsb(&b)); - - if (more_than_one(pos.checkers())) - return moveList; // Double check, only a king move can save the day - - // Generate blocking evasions or captures of the checking piece - Square checksq = lsb(pos.checkers()); - Bitboard target = between_bb(checksq, ksq) | checksq; - - return us == WHITE ? generate_all(pos, moveList, target) - : generate_all(pos, moveList, target); -} - - -/// generate generates all the legal moves in the given position - -template<> -ExtMove* generate(const Position& pos, ExtMove* moveList) { - - Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); - Square ksq = pos.king_square(pos.side_to_move()); - ExtMove* cur = moveList; - - moveList = pos.checkers() ? generate(pos, moveList) - : generate(pos, moveList); - while (cur != moveList) - if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT) - && !pos.legal(cur->move, pinned)) - cur->move = (--moveList)->move; - else - ++cur; - - return moveList; -} diff --git a/src/movegen.h b/src/movegen.h deleted file mode 100644 index fa697827..00000000 --- a/src/movegen.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef MOVEGEN_H_INCLUDED -#define MOVEGEN_H_INCLUDED - -#include "types.h" - -enum GenType { - CAPTURES, - QUIETS, - QUIET_CHECKS, - EVASIONS, - NON_EVASIONS, - LEGAL -}; - -class Position; - -template -ExtMove* generate(const Position& pos, ExtMove* moveList); - -/// The MoveList struct is a simple wrapper around generate(). It sometimes comes -/// in handy to use this class instead of the low level generate() function. -template -struct MoveList { - - explicit MoveList(const Position& pos) : cur(moveList), last(generate(pos, moveList)) { last->move = MOVE_NONE; } - void operator++() { ++cur; } - Move operator*() const { return cur->move; } - size_t size() const { return last - moveList; } - bool contains(Move m) const { - for (const ExtMove* it(moveList); it != last; ++it) if (it->move == m) return true; - return false; - } - -private: - ExtMove moveList[MAX_MOVES]; - ExtMove *cur, *last; -}; - -#endif // #ifndef MOVEGEN_H_INCLUDED diff --git a/src/movepick.cpp b/src/movepick.cpp deleted file mode 100644 index 9411b5a7..00000000 --- a/src/movepick.cpp +++ /dev/null @@ -1,385 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include - -#include "movepick.h" -#include "thread.h" - -namespace { - - enum Stages { - MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1, - EVASION, EVASIONS_S2, - QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3, - QSEARCH_1, CAPTURES_S4, - PROBCUT, CAPTURES_S5, - RECAPTURE, CAPTURES_S6, - STOP - }; - - // Our insertion sort, which is guaranteed (and also needed) to be stable - void insertion_sort(ExtMove* begin, ExtMove* end) - { - ExtMove tmp, *p, *q; - - for (p = begin + 1; p < end; ++p) - { - tmp = *p; - for (q = p; q != begin && *(q-1) < tmp; --q) - *q = *(q-1); - *q = tmp; - } - } - - // Unary predicate used by std::partition to split positive values from remaining - // ones so as to sort the two sets separately, with the second sort delayed. - inline bool has_positive_value(const ExtMove& move) { return move.value > 0; } - - // Picks the best move in the range (begin, end) and moves it to the front. - // It's faster than sorting all the moves in advance when there are few - // moves e.g. possible captures. - inline ExtMove* pick_best(ExtMove* begin, ExtMove* end) - { - std::swap(*begin, *std::max_element(begin, end)); - return begin; - } -} // namespace - - -/// Constructors of the MovePicker class. As arguments we pass information -/// to help it to return the (presumably) good moves first, to decide which -/// moves to return (in the quiescence search, for instance, we only want to -/// search captures, promotions and some checks) and how important good move -/// ordering is at the current node. - -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, - Move* cm, Move* fm, Search::Stack* s) : pos(p), history(h), depth(d) { - - assert(d > DEPTH_ZERO); - - cur = end = moves; - endBadCaptures = moves + MAX_MOVES - 1; - countermoves = cm; - followupmoves = fm; - ss = s; - - if (pos.checkers()) - stage = EVASION; - - else - stage = MAIN_SEARCH; - - ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE); - end += (ttMove != MOVE_NONE); -} - -MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h, - Square s) : pos(p), history(h), cur(moves), end(moves) { - - assert(d <= DEPTH_ZERO); - - if (pos.checkers()) - stage = EVASION; - - else if (d > DEPTH_QS_NO_CHECKS) - stage = QSEARCH_0; - - else if (d > DEPTH_QS_RECAPTURES) - stage = QSEARCH_1; - - else - { - stage = RECAPTURE; - recaptureSquare = s; - ttm = MOVE_NONE; - } - - ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE); - end += (ttMove != MOVE_NONE); -} - -MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt) - : pos(p), history(h), cur(moves), end(moves) { - - assert(!pos.checkers()); - - stage = PROBCUT; - - // In ProbCut we generate only captures that are better than the parent's - // captured piece. - captureThreshold = PieceValue[MG][pt]; - ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE); - - if (ttMove && (!pos.capture(ttMove) || pos.see(ttMove) <= captureThreshold)) - ttMove = MOVE_NONE; - - end += (ttMove != MOVE_NONE); -} - - -/// score() assign a numerical value to each move in a move list. The moves with -/// highest values will be picked first. -template<> -void MovePicker::score() { - // Winning and equal captures in the main search are ordered by MVV/LVA. - // Suprisingly, this appears to perform slightly better than SEE based - // move ordering. The reason is probably that in a position with a winning - // capture, capturing a more valuable (but sufficiently defended) piece - // first usually doesn't hurt. The opponent will have to recapture, and - // the hanging piece will still be hanging (except in the unusual cases - // where it is possible to recapture with the hanging piece). Exchanging - // big pieces before capturing a hanging piece probably helps to reduce - // the subtree size. - // In main search we want to push captures with negative SEE values to the - // badCaptures[] array, but instead of doing it now we delay until the move - // has been picked up in pick_move_from_list(). This way we save some SEE - // calls in case we get a cutoff. - Move m; - - for (ExtMove* it = moves; it != end; ++it) - { - m = it->move; - it->value = PieceValue[MG][pos.piece_on(to_sq(m))] - - Value(type_of(pos.moved_piece(m))); - - if (type_of(m) == ENPASSANT) - it->value += PieceValue[MG][PAWN]; - - else if (type_of(m) == PROMOTION) - it->value += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN]; - } -} - -template<> -void MovePicker::score() { - - Move m; - - for (ExtMove* it = moves; it != end; ++it) - { - m = it->move; - it->value = history[pos.moved_piece(m)][to_sq(m)]; - } -} - -template<> -void MovePicker::score() { - // Try good captures ordered by MVV/LVA, then non-captures if destination square - // is not under attack, ordered by history value, then bad-captures and quiet - // moves with a negative SEE. This last group is ordered by the SEE value. - Move m; - Value see; - - for (ExtMove* it = moves; it != end; ++it) - { - m = it->move; - if ((see = pos.see_sign(m)) < VALUE_ZERO) - it->value = see - HistoryStats::Max; // At the bottom - - else if (pos.capture(m)) - it->value = PieceValue[MG][pos.piece_on(to_sq(m))] - - Value(type_of(pos.moved_piece(m))) + HistoryStats::Max; - else - it->value = history[pos.moved_piece(m)][to_sq(m)]; - } -} - - -/// generate_next_stage() generates, scores and sorts the next bunch of moves, -/// when there are no more moves to try for the current stage. - -void MovePicker::generate_next_stage() { - - cur = moves; - - switch (++stage) { - - case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6: - end = generate(pos, moves); - score(); - return; - - case KILLERS_S1: - cur = killers; - end = cur + 2; - - killers[0].move = ss->killers[0]; - killers[1].move = ss->killers[1]; - killers[2].move = killers[3].move = MOVE_NONE; - killers[4].move = killers[5].move = MOVE_NONE; - - // Please note that following code is racy and could yield to rare (less - // than 1 out of a million) duplicated entries in SMP case. This is harmless. - - // Be sure countermoves are different from killers - for (int i = 0; i < 2; ++i) - if ( countermoves[i] != (cur+0)->move - && countermoves[i] != (cur+1)->move) - (end++)->move = countermoves[i]; - - // Be sure followupmoves are different from killers and countermoves - for (int i = 0; i < 2; ++i) - if ( followupmoves[i] != (cur+0)->move - && followupmoves[i] != (cur+1)->move - && followupmoves[i] != (cur+2)->move - && followupmoves[i] != (cur+3)->move) - (end++)->move = followupmoves[i]; - return; - - case QUIETS_1_S1: - endQuiets = end = generate(pos, moves); - score(); - end = std::partition(cur, end, has_positive_value); - insertion_sort(cur, end); - return; - - case QUIETS_2_S1: - cur = end; - end = endQuiets; - if (depth >= 3 * ONE_PLY) - insertion_sort(cur, end); - return; - - case BAD_CAPTURES_S1: - // Just pick them in reverse order to get MVV/LVA ordering - cur = moves + MAX_MOVES - 1; - end = endBadCaptures; - return; - - case EVASIONS_S2: - end = generate(pos, moves); - if (end > moves + 1) - score(); - return; - - case QUIET_CHECKS_S3: - end = generate(pos, moves); - return; - - case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE: - stage = STOP; - /* Fall through */ - - case STOP: - end = cur + 1; // Avoid another next_phase() call - return; - - default: - assert(false); - } -} - - -/// next_move() is the most important method of the MovePicker class. It returns -/// a new pseudo legal move every time it is called, until there are no more moves -/// left. It picks the move with the biggest value from a list of generated moves -/// taking care not to return the ttMove if it has already been searched. -template<> -Move MovePicker::next_move() { - - Move move; - - while (true) - { - while (cur == end) - generate_next_stage(); - - switch (stage) { - - case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: - ++cur; - return ttMove; - - case CAPTURES_S1: - move = pick_best(cur++, end)->move; - if (move != ttMove) - { - if (pos.see_sign(move) >= VALUE_ZERO) - return move; - - // Losing capture, move it to the tail of the array - (endBadCaptures--)->move = move; - } - break; - - case KILLERS_S1: - move = (cur++)->move; - if ( move != MOVE_NONE - && move != ttMove - && pos.pseudo_legal(move) - && !pos.capture(move)) - return move; - break; - - case QUIETS_1_S1: case QUIETS_2_S1: - move = (cur++)->move; - if ( move != ttMove - && move != killers[0].move - && move != killers[1].move - && move != killers[2].move - && move != killers[3].move - && move != killers[4].move - && move != killers[5].move) - return move; - break; - - case BAD_CAPTURES_S1: - return (cur--)->move; - - case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4: - move = pick_best(cur++, end)->move; - if (move != ttMove) - return move; - break; - - case CAPTURES_S5: - move = pick_best(cur++, end)->move; - if (move != ttMove && pos.see(move) > captureThreshold) - return move; - break; - - case CAPTURES_S6: - move = pick_best(cur++, end)->move; - if (to_sq(move) == recaptureSquare) - return move; - break; - - case QUIET_CHECKS_S3: - move = (cur++)->move; - if (move != ttMove) - return move; - break; - - case STOP: - return MOVE_NONE; - - default: - assert(false); - } - } -} - - -/// Version of next_move() to use at split point nodes where the move is grabbed -/// from the split point's shared MovePicker object. This function is not thread -/// safe so must be lock protected by the caller. -template<> -Move MovePicker::next_move() { return ss->splitPoint->movePicker->next_move(); } diff --git a/src/movepick.h b/src/movepick.h deleted file mode 100644 index b9d2594a..00000000 --- a/src/movepick.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef MOVEPICK_H_INCLUDED -#define MOVEPICK_H_INCLUDED - -#include // For std::max -#include // For std::memset - -#include "movegen.h" -#include "position.h" -#include "search.h" -#include "types.h" - - -/// The Stats struct stores moves statistics. According to the template parameter -/// the class can store History, Gains and Countermoves. History records how often -/// different moves have been successful or unsuccessful during the current search -/// and is used for reduction and move ordering decisions. Gains records the move's -/// best evaluation gain from one ply to the next and is used for pruning decisions. -/// Countermoves store the move that refute a previous one. Entries are stored -/// using only the moving piece and destination square, hence two moves with -/// different origin but same destination and piece will be considered identical. -template -struct Stats { - - static const Value Max = Value(250); - - const T* operator[](Piece pc) const { return table[pc]; } - void clear() { std::memset(table, 0, sizeof(table)); } - - void update(Piece pc, Square to, Move m) { - - if (m == table[pc][to].first) - return; - - table[pc][to].second = table[pc][to].first; - table[pc][to].first = m; - } - - void update(Piece pc, Square to, Value v) { - - if (Gain) - table[pc][to] = std::max(v, table[pc][to] - 1); - - else if (abs(table[pc][to] + v) < Max) - table[pc][to] += v; - } - -private: - T table[PIECE_NB][SQUARE_NB]; -}; - -typedef Stats< true, Value> GainsStats; -typedef Stats HistoryStats; -typedef Stats > MovesStats; - - -/// 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 alpha -/// beta algorithm, MovePicker attempts to return the moves which are most likely -/// to get a cut-off first. - -class MovePicker { - - MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC - -public: - MovePicker(const Position&, Move, Depth, const HistoryStats&, Square); - MovePicker(const Position&, Move, const HistoryStats&, PieceType); - MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Move*, Search::Stack*); - - template Move next_move(); - -private: - template void score(); - void generate_next_stage(); - - const Position& pos; - const HistoryStats& history; - Search::Stack* ss; - Move* countermoves; - Move* followupmoves; - Depth depth; - Move ttMove; - ExtMove killers[6]; - Square recaptureSquare; - Value captureThreshold; - int stage; - ExtMove *cur, *end, *endQuiets, *endBadCaptures; - ExtMove moves[MAX_MOVES]; -}; - -#endif // #ifndef MOVEPICK_H_INCLUDED diff --git a/src/pawns.cpp b/src/pawns.cpp deleted file mode 100644 index 0a227567..00000000 --- a/src/pawns.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include - -#include "bitboard.h" -#include "bitcount.h" -#include "pawns.h" -#include "position.h" -#include "thread.h" - -namespace { - - #define V Value - #define S(mg, eg) make_score(mg, eg) - - // Doubled pawn penalty by file - const Score Doubled[FILE_NB] = { - S(13, 43), S(20, 48), S(23, 48), S(23, 48), - S(23, 48), S(23, 48), S(20, 48), S(13, 43) }; - - // Isolated pawn penalty by opposed flag and file - const Score Isolated[2][FILE_NB] = { - { S(37, 45), S(54, 52), S(60, 52), S(60, 52), - S(60, 52), S(60, 52), S(54, 52), S(37, 45) }, - { S(25, 30), S(36, 35), S(40, 35), S(40, 35), - S(40, 35), S(40, 35), S(36, 35), S(25, 30) } }; - - // Backward pawn penalty by opposed flag and file - const Score Backward[2][FILE_NB] = { - { S(30, 42), S(43, 46), S(49, 46), S(49, 46), - S(49, 46), S(49, 46), S(43, 46), S(30, 42) }, - { S(20, 28), S(29, 31), S(33, 31), S(33, 31), - S(33, 31), S(33, 31), S(29, 31), S(20, 28) } }; - - // Connected pawn bonus by opposed, phalanx flags and rank - Score Connected[2][2][RANK_NB]; - - // Levers bonus by rank - const Score Lever[RANK_NB] = { - S( 0, 0), S( 0, 0), S(0, 0), S(0, 0), - S(20,20), S(40,40), S(0, 0), S(0, 0) }; - - // Unsupported pawn penalty - const Score UnsupportedPawnPenalty = S(20, 10); - - // Weakness of our pawn shelter in front of the king by [distance from edge][rank] - const Value ShelterWeakness[][RANK_NB] = { - { V(100), V(13), V(24), V(64), V(89), V( 93), V(104) }, - { V(110), V( 1), V(29), V(75), V(96), V(102), V(107) }, - { V(102), V( 0), V(39), V(74), V(88), V(101), V( 98) }, - { V( 88), V( 4), V(33), V(67), V(92), V( 94), V(107) } }; - - // Danger of enemy pawns moving toward our king by [type][distance from edge][rank] - const Value StormDanger[][4][RANK_NB] = { - { { V( 0), V( 63), V( 128), V(43), V(27) }, - { V( 0), V( 62), V( 131), V(44), V(26) }, - { V( 0), V( 59), V( 121), V(50), V(28) }, - { V( 0), V( 62), V( 127), V(54), V(28) } }, - { { V(24), V( 40), V( 93), V(42), V(22) }, - { V(24), V( 28), V( 101), V(38), V(20) }, - { V(24), V( 32), V( 95), V(36), V(23) }, - { V(27), V( 24), V( 99), V(36), V(24) } }, - { { V( 0), V( 0), V( 81), V(16), V( 6) }, - { V( 0), V( 0), V( 165), V(29), V( 9) }, - { V( 0), V( 0), V( 163), V(23), V(12) }, - { V( 0), V( 0), V( 161), V(28), V(13) } }, - { { V( 0), V(-296), V(-299), V(55), V(25) }, - { V( 0), V( 67), V( 131), V(46), V(21) }, - { V( 0), V( 65), V( 135), V(50), V(31) }, - { V( 0), V( 62), V( 128), V(51), V(24) } } }; - - // Max bonus for king safety. Corresponds to start position with all the pawns - // in front of the king and no enemy pawn on the horizon. - const Value MaxSafetyBonus = V(257); - - #undef S - #undef V - - template - Score evaluate(const Position& pos, Pawns::Entry* e) { - - const Color Them = (Us == WHITE ? BLACK : WHITE); - const Square Up = (Us == WHITE ? DELTA_N : DELTA_S); - const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW); - const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE); - - Bitboard b, p, doubled, connected; - Square s; - bool passed, isolated, opposed, phalanx, backward, unsupported, lever; - Score score = SCORE_ZERO; - const Square* pl = pos.list(Us); - const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)]; - - Bitboard ourPawns = pos.pieces(Us , PAWN); - Bitboard theirPawns = pos.pieces(Them, PAWN); - - e->passedPawns[Us] = 0; - e->kingSquares[Us] = SQ_NONE; - e->semiopenFiles[Us] = 0xFF; - e->pawnAttacks[Us] = shift_bb(ourPawns) | shift_bb(ourPawns); - e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares); - e->pawnsOnSquares[Us][WHITE] = pos.count(Us) - e->pawnsOnSquares[Us][BLACK]; - - // Loop through all pawns of the current color and score each pawn - while ((s = *pl++) != SQ_NONE) - { - assert(pos.piece_on(s) == make_piece(Us, PAWN)); - - File f = file_of(s); - - // This file cannot be semi-open - e->semiopenFiles[Us] &= ~(1 << f); - - // Previous rank - p = rank_bb(s - pawn_push(Us)); - - // Flag the pawn as passed, isolated, doubled, - // unsupported or connected (but not the backward one). - connected = ourPawns & adjacent_files_bb(f) & (rank_bb(s) | p); - phalanx = connected & rank_bb(s); - unsupported = !(ourPawns & adjacent_files_bb(f) & p); - isolated = !(ourPawns & adjacent_files_bb(f)); - doubled = ourPawns & forward_bb(Us, s); - opposed = theirPawns & forward_bb(Us, s); - passed = !(theirPawns & passed_pawn_mask(Us, s)); - lever = theirPawns & pawnAttacksBB[s]; - - // Test for backward pawn. - // If the pawn is passed, isolated, or connected it cannot be - // backward. If there are friendly pawns behind on adjacent files - // or if it can capture an enemy pawn it cannot be backward either. - if ( (passed | isolated | connected) - || (ourPawns & pawn_attack_span(Them, s)) - || (pos.attacks_from(s, Us) & theirPawns)) - backward = false; - else - { - // We now know that there are no friendly pawns beside or behind this - // pawn on adjacent files. We now check whether the pawn is - // backward by looking in the forward direction on the adjacent - // files, and picking the closest pawn there. - b = pawn_attack_span(Us, s) & (ourPawns | theirPawns); - b = pawn_attack_span(Us, s) & rank_bb(backmost_sq(Us, b)); - - // If we have an enemy pawn in the same or next rank, the pawn is - // backward because it cannot advance without being captured. - backward = (b | shift_bb(b)) & theirPawns; - } - - assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns)); - - // Passed pawns will be properly scored in evaluation because we need - // full attack info to evaluate passed pawns. Only the frontmost passed - // pawn on each file is considered a true passed pawn. - if (passed && !doubled) - e->passedPawns[Us] |= s; - - // Score this pawn - if (isolated) - score -= Isolated[opposed][f]; - - if (unsupported && !isolated) - score -= UnsupportedPawnPenalty; - - if (doubled) - score -= Doubled[f] / distance(s, frontmost_sq(Us, doubled)); - - if (backward) - score -= Backward[opposed][f]; - - if (connected) - score += Connected[opposed][phalanx][relative_rank(Us, s)]; - - if (lever) - score += Lever[relative_rank(Us, s)]; - } - - b = e->semiopenFiles[Us] ^ 0xFF; - e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0; - - return score; - } - -} // namespace - -namespace Pawns { - -/// Pawns::init() initializes some tables needed by evaluation. Instead of using -/// hard-coded tables, when makes sense, we prefer to calculate them with a formula -/// to reduce independent parameters and to allow easier tuning and better insight. - -void init() -{ - static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 }; - - for (int opposed = 0; opposed <= 1; ++opposed) - for (int phalanx = 0; phalanx <= 1; ++phalanx) - for (Rank r = RANK_2; r < RANK_8; ++r) - { - int bonus = Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0); - Connected[opposed][phalanx][r] = make_score(bonus / 2, bonus >> opposed); - } -} - - -/// Pawns::probe() looks up the current position's pawns configuration in -/// the pawns hash table. It returns a pointer to the Entry if the position -/// is found. Otherwise a new Entry is computed and stored there, so we don't -/// have to recompute all when the same pawns configuration occurs again. - -Entry* probe(const Position& pos) { - - Key key = pos.pawn_key(); - Entry* e = pos.this_thread()->pawnsTable[key]; - - if (e->key == key) - return e; - - e->key = key; - e->score = evaluate(pos, e) - evaluate(pos, e); - return e; -} - - -/// Entry::shelter_storm() calculates shelter and storm penalties for the file -/// the king is on, as well as the two adjacent files. - -template -Value Entry::shelter_storm(const Position& pos, Square ksq) { - - const Color Them = (Us == WHITE ? BLACK : WHITE); - - enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing }; - - Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq)); - Bitboard ourPawns = b & pos.pieces(Us); - Bitboard theirPawns = b & pos.pieces(Them); - Value safety = MaxSafetyBonus; - File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq))); - - for (File f = center - File(1); f <= center + File(1); ++f) - { - b = ourPawns & file_bb(f); - Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1; - - b = theirPawns & file_bb(f); - Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1; - - safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs] - + StormDanger - [f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing : - rkUs == RANK_1 ? NoFriendlyPawn : - rkThem == rkUs + 1 ? BlockedByPawn : Unblocked] - [std::min(f, FILE_H - f)][rkThem]; - } - - return safety; -} - - -/// Entry::do_king_safety() calculates a bonus for king safety. It is called only -/// when king square changes, which is about 20% of total king_safety() calls. - -template -Score Entry::do_king_safety(const Position& pos, Square ksq) { - - kingSquares[Us] = ksq; - castlingRights[Us] = pos.can_castle(Us); - minKingPawnDistance[Us] = 0; - - Bitboard pawns = pos.pieces(Us, PAWN); - if (pawns) - while (!(DistanceRingBB[ksq][minKingPawnDistance[Us]++] & pawns)) {} - - if (relative_rank(Us, ksq) > RANK_4) - return make_score(0, -16 * minKingPawnDistance[Us]); - - Value bonus = shelter_storm(pos, ksq); - - // If we can castle use the bonus after the castling if it is bigger - if (pos.can_castle(MakeCastling::right)) - bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_G1))); - - if (pos.can_castle(MakeCastling::right)) - bonus = std::max(bonus, shelter_storm(pos, relative_square(Us, SQ_C1))); - - return make_score(bonus, -16 * minKingPawnDistance[Us]); -} - -// Explicit template instantiation -template Score Entry::do_king_safety(const Position& pos, Square ksq); -template Score Entry::do_king_safety(const Position& pos, Square ksq); - -} // namespace Pawns diff --git a/src/pawns.h b/src/pawns.h deleted file mode 100644 index 40a40403..00000000 --- a/src/pawns.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef PAWNS_H_INCLUDED -#define PAWNS_H_INCLUDED - -#include "misc.h" -#include "position.h" -#include "types.h" - -namespace Pawns { - -/// Pawns::Entry contains various information about a pawn structure. A lookup -/// to the pawn hash table (performed by calling the probe function) returns a -/// pointer to an Entry object. - -struct Entry { - - Score pawns_score() const { return score; } - Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; } - Bitboard passed_pawns(Color c) const { return passedPawns[c]; } - - int semiopen_file(Color c, File f) const { - return semiopenFiles[c] & (1 << f); - } - - int semiopen_side(Color c, File f, bool leftSide) const { - return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1)); - } - - int pawn_span(Color c) const { - return pawnSpan[c]; - } - - int pawns_on_same_color_squares(Color c, Square s) const { - return pawnsOnSquares[c][!!(DarkSquares & s)]; - } - - template - Score king_safety(const Position& pos, Square ksq) { - return kingSquares[Us] == ksq && castlingRights[Us] == pos.can_castle(Us) - ? kingSafety[Us] : (kingSafety[Us] = do_king_safety(pos, ksq)); - } - - template - Score do_king_safety(const Position& pos, Square ksq); - - template - Value shelter_storm(const Position& pos, Square ksq); - - Key key; - Score score; - Bitboard passedPawns[COLOR_NB]; - Bitboard pawnAttacks[COLOR_NB]; - Square kingSquares[COLOR_NB]; - Score kingSafety[COLOR_NB]; - int minKingPawnDistance[COLOR_NB]; - int castlingRights[COLOR_NB]; - int semiopenFiles[COLOR_NB]; - int pawnSpan[COLOR_NB]; - int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares] -}; - -typedef HashTable Table; - -void init(); -Entry* probe(const Position& pos); - -} // namespace Pawns - -#endif // #ifndef PAWNS_H_INCLUDED diff --git a/src/position.cpp b/src/position.cpp deleted file mode 100644 index d1f31146..00000000 --- a/src/position.cpp +++ /dev/null @@ -1,1280 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#include -#include -#include // For std::memset -#include -#include - -#include "bitcount.h" -#include "misc.h" -#include "movegen.h" -#include "position.h" -#include "psqtab.h" -#include "thread.h" -#include "tt.h" -#include "uci.h" - -using std::string; - -Value PieceValue[PHASE_NB][PIECE_NB] = { -{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, -{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; - -namespace Zobrist { - - Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; - Key enpassant[FILE_NB]; - Key castling[CASTLING_RIGHT_NB]; - Key side; - Key exclusion; -} - -Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;} - -namespace { - -const string PieceToChar(" PNBRQK pnbrqk"); -Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; - -// min_attacker() is a helper function used by see() to locate the least -// valuable attacker for the side to move, remove the attacker we just found -// from the bitboards and scan for new X-ray attacks behind it. - -template FORCE_INLINE -PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers, - Bitboard& occupied, Bitboard& attackers) { - - Bitboard b = stmAttackers & bb[Pt]; - if (!b) - return min_attacker(bb, to, stmAttackers, occupied, attackers); - - occupied ^= b & ~(b - 1); - - if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN) - attackers |= attacks_bb(to, occupied) & (bb[BISHOP] | bb[QUEEN]); - - if (Pt == ROOK || Pt == QUEEN) - attackers |= attacks_bb(to, occupied) & (bb[ROOK] | bb[QUEEN]); - - attackers &= occupied; // After X-ray that may add already processed pieces - return (PieceType)Pt; -} - -template<> FORCE_INLINE -PieceType min_attacker(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) { - return KING; // No need to update bitboards: it is the last cycle -} - -} // namespace - - -/// CheckInfo c'tor - -CheckInfo::CheckInfo(const Position& pos) { - - Color them = ~pos.side_to_move(); - ksq = pos.king_square(them); - - pinned = pos.pinned_pieces(pos.side_to_move()); - dcCandidates = pos.discovered_check_candidates(); - - checkSq[PAWN] = pos.attacks_from(ksq, them); - checkSq[KNIGHT] = pos.attacks_from(ksq); - checkSq[BISHOP] = pos.attacks_from(ksq); - checkSq[ROOK] = pos.attacks_from(ksq); - checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK]; - checkSq[KING] = 0; -} - - -/// operator<<(Position) returns an ASCII representation of the position - -std::ostream& operator<<(std::ostream& os, const Position& pos) { - - os << "\n +---+---+---+---+---+---+---+---+\n"; - - for (Rank r = RANK_8; r >= RANK_1; --r) - { - for (File f = FILE_A; f <= FILE_H; ++f) - os << " | " << PieceToChar[pos.piece_on(make_square(f, r))]; - - os << " |\n +---+---+---+---+---+---+---+---+\n"; - } - - os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase - << std::setfill('0') << std::setw(16) << pos.st->key << std::dec << "\nCheckers: "; - - for (Bitboard b = pos.checkers(); b; ) - os << UCI::square(pop_lsb(&b)) << " "; - - return os; -} - - -/// Position::init() initializes at startup the various arrays used to compute -/// hash keys and the piece square tables. The latter is a two-step operation: -/// Firstly, the white halves of the tables are copied from PSQT[] tables. -/// Secondly, the black halves of the tables are initialized by flipping and -/// changing the sign of the white scores. - -void Position::init() { - - PRNG rng(1070372); - - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (Square s = SQ_A1; s <= SQ_H8; ++s) - Zobrist::psq[c][pt][s] = rng.rand(); - - for (File f = FILE_A; f <= FILE_H; ++f) - Zobrist::enpassant[f] = rng.rand(); - - for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr) - { - Bitboard b = cr; - while (b) - { - Key k = Zobrist::castling[1ULL << pop_lsb(&b)]; - Zobrist::castling[cr] ^= k ? k : rng.rand(); - } - } - - Zobrist::side = rng.rand(); - Zobrist::exclusion = rng.rand(); - - for (PieceType pt = PAWN; pt <= KING; ++pt) - { - PieceValue[MG][make_piece(BLACK, pt)] = PieceValue[MG][pt]; - PieceValue[EG][make_piece(BLACK, pt)] = PieceValue[EG][pt]; - - Score v = make_score(PieceValue[MG][pt], PieceValue[EG][pt]); - - for (Square s = SQ_A1; s <= SQ_H8; ++s) - { - psq[WHITE][pt][ s] = (v + PSQT[pt][s]); - psq[BLACK][pt][~s] = -(v + PSQT[pt][s]); - } - } -} - - -/// Position::operator=() creates a copy of 'pos' but detaching the state pointer -/// from the source to be self-consistent and not depending on any external data. - -Position& Position::operator=(const Position& pos) { - - std::memcpy(this, &pos, sizeof(Position)); - startState = *st; - st = &startState; - nodes = 0; - - assert(pos_is_ok()); - - return *this; -} - - -/// Position::clear() erases the position object to a pristine state, with an -/// empty board, white to move, and no castling rights. - -void Position::clear() { - - std::memset(this, 0, sizeof(Position)); - startState.epSquare = SQ_NONE; - st = &startState; - - for (int i = 0; i < PIECE_TYPE_NB; ++i) - for (int j = 0; j < 16; ++j) - pieceList[WHITE][i][j] = pieceList[BLACK][i][j] = SQ_NONE; -} - - -/// Position::set() initializes the position object with the given FEN string. -/// This function is not very robust - make sure that input FENs are correct, -/// this is assumed to be the responsibility of the GUI. - -void Position::set(const string& fenStr, bool isChess960, Thread* th) { -/* - A FEN string defines a particular position using only the ASCII character set. - - A FEN string contains six fields separated by a space. The fields are: - - 1) Piece placement (from white's perspective). Each rank is described, starting - with rank 8 and ending with rank 1. Within each rank, the contents of each - square are described from file A through file H. Following the Standard - Algebraic Notation (SAN), each piece is identified by a single letter taken - from the standard English names. White pieces are designated using upper-case - letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are - noted using digits 1 through 8 (the number of blank squares), and "/" - separates ranks. - - 2) Active color. "w" means white moves next, "b" means black. - - 3) Castling availability. If neither side can castle, this is "-". Otherwise, - this has one or more letters: "K" (White can castle kingside), "Q" (White - can castle queenside), "k" (Black can castle kingside), and/or "q" (Black - can castle queenside). - - 4) En passant target square (in algebraic notation). If there's no en passant - target square, this is "-". If a pawn has just made a 2-square move, this - is the position "behind" the pawn. This is recorded regardless of whether - there is a pawn in position to make an en passant capture. - - 5) Halfmove clock. This is the number of halfmoves since the last pawn advance - or capture. This is used to determine if a draw can be claimed under the - fifty-move rule. - - 6) Fullmove number. The number of the full move. It starts at 1, and is - incremented after Black's move. -*/ - - unsigned char col, row, token; - size_t idx; - Square sq = SQ_A8; - std::istringstream ss(fenStr); - - clear(); - ss >> std::noskipws; - - // 1. Piece placement - while ((ss >> token) && !isspace(token)) - { - if (isdigit(token)) - sq += Square(token - '0'); // Advance the given number of files - - else if (token == '/') - sq -= Square(16); - - else if ((idx = PieceToChar.find(token)) != string::npos) - { - put_piece(sq, color_of(Piece(idx)), type_of(Piece(idx))); - ++sq; - } - } - - // 2. Active color - ss >> token; - sideToMove = (token == 'w' ? WHITE : BLACK); - 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)) - { - Square rsq; - Color c = islower(token) ? BLACK : WHITE; - - token = char(toupper(token)); - - if (token == 'K') - for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; --rsq) {} - - else if (token == 'Q') - for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; ++rsq) {} - - else if (token >= 'A' && token <= 'H') - rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); - - else - continue; - - set_castling_right(c, rsq); - } - - // 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')); - - if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) - st->epSquare = SQ_NONE; - } - - // 5-6. Halfmove clock and fullmove number - ss >> std::skipws >> st->rule50 >> gamePly; - - // Convert from fullmove starting from 1 to ply starting from 0, - // handle also common incorrect FEN with fullmove = 0. - gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); - - chess960 = isChess960; - thisThread = th; - set_state(st); - - assert(pos_is_ok()); -} - - -/// Position::set_castling_right() is a helper function used to set castling -/// rights given the corresponding color and the rook starting square. - -void Position::set_castling_right(Color c, Square rfrom) { - - Square kfrom = king_square(c); - CastlingSide cs = kfrom < rfrom ? KING_SIDE : QUEEN_SIDE; - CastlingRight cr = (c | cs); - - st->castlingRights |= cr; - castlingRightsMask[kfrom] |= cr; - castlingRightsMask[rfrom] |= cr; - castlingRookSquare[cr] = rfrom; - - Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1); - Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1); - - for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s) - if (s != kfrom && s != rfrom) - castlingPath[cr] |= s; - - for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s) - if (s != kfrom && s != rfrom) - castlingPath[cr] |= s; -} - - -/// Position::set_state() computes the hash keys of the position, and other -/// data that once computed is updated incrementally as moves are made. -/// The function is only used when a new position is set up, and to verify -/// the correctness of the StateInfo data when running in debug mode. - -void Position::set_state(StateInfo* si) const { - - si->key = si->pawnKey = si->materialKey = 0; - si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO; - si->psq = SCORE_ZERO; - - si->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove); - - for (Bitboard b = pieces(); b; ) - { - Square s = pop_lsb(&b); - Piece pc = piece_on(s); - si->key ^= Zobrist::psq[color_of(pc)][type_of(pc)][s]; - si->psq += psq[color_of(pc)][type_of(pc)][s]; - } - - if (ep_square() != SQ_NONE) - si->key ^= Zobrist::enpassant[file_of(ep_square())]; - - if (sideToMove == BLACK) - si->key ^= Zobrist::side; - - si->key ^= Zobrist::castling[st->castlingRights]; - - for (Bitboard b = pieces(PAWN); b; ) - { - Square s = pop_lsb(&b); - si->pawnKey ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s]; - } - - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (int cnt = 0; cnt < pieceCount[c][pt]; ++cnt) - si->materialKey ^= Zobrist::psq[c][pt][cnt]; - - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = KNIGHT; pt <= QUEEN; ++pt) - si->nonPawnMaterial[c] += pieceCount[c][pt] * PieceValue[MG][pt]; -} - - -/// Position::fen() returns a FEN representation of the position. In case of -/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function. - -const string Position::fen() const { - - int emptyCnt; - std::ostringstream ss; - - for (Rank r = RANK_8; r >= RANK_1; --r) - { - for (File f = FILE_A; f <= FILE_H; ++f) - { - for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f) - ++emptyCnt; - - if (emptyCnt) - ss << emptyCnt; - - if (f <= FILE_H) - ss << PieceToChar[piece_on(make_square(f, r))]; - } - - if (r > RANK_1) - ss << '/'; - } - - ss << (sideToMove == WHITE ? " w " : " b "); - - if (can_castle(WHITE_OO)) - ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | KING_SIDE))) : 'K'); - - if (can_castle(WHITE_OOO)) - ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE))) : 'Q'); - - if (can_castle(BLACK_OO)) - ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | KING_SIDE))) : 'k'); - - if (can_castle(BLACK_OOO)) - ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q'); - - if (!can_castle(WHITE) && !can_castle(BLACK)) - ss << '-'; - - ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ") - << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; - - return ss.str(); -} - - -/// Position::game_phase() calculates the game phase interpolating total non-pawn -/// material between endgame and midgame limits. - -Phase Position::game_phase() const { - - Value npm = st->nonPawnMaterial[WHITE] + st->nonPawnMaterial[BLACK]; - - npm = std::max(EndgameLimit, std::min(npm, MidgameLimit)); - - return Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit)); -} - - -/// Position::check_blockers() returns a bitboard of all the pieces with color -/// 'c' that are blocking check on the king with color 'kingColor'. A piece -/// blocks a check if removing that piece from the board would result in a -/// position where the king is in check. A check blocking piece can be either a -/// pinned or a discovered check piece, according if its color 'c' is the same -/// or the opposite of 'kingColor'. - -Bitboard Position::check_blockers(Color c, Color kingColor) const { - - Bitboard b, pinners, result = 0; - Square ksq = king_square(kingColor); - - // Pinners are sliders that give check when a pinned piece is removed - pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq]) - | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(~kingColor); - - while (pinners) - { - b = between_bb(ksq, pop_lsb(&pinners)) & pieces(); - - if (!more_than_one(b)) - result |= b & pieces(c); - } - return result; -} - - -/// Position::attackers_to() computes a bitboard of all pieces which attack a -/// given square. Slider attacks use the occupied bitboard to indicate occupancy. - -Bitboard Position::attackers_to(Square s, Bitboard occupied) const { - - return (attacks_from(s, BLACK) & pieces(WHITE, PAWN)) - | (attacks_from(s, WHITE) & pieces(BLACK, PAWN)) - | (attacks_from(s) & pieces(KNIGHT)) - | (attacks_bb(s, occupied) & pieces(ROOK, QUEEN)) - | (attacks_bb(s, occupied) & pieces(BISHOP, QUEEN)) - | (attacks_from(s) & pieces(KING)); -} - - -/// Position::legal() tests whether a pseudo-legal move is legal - -bool Position::legal(Move m, Bitboard pinned) const { - - assert(is_ok(m)); - assert(pinned == pinned_pieces(sideToMove)); - - Color us = sideToMove; - Square from = from_sq(m); - - assert(color_of(moved_piece(m)) == us); - assert(piece_on(king_square(us)) == make_piece(us, KING)); - - // 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) == ENPASSANT) - { - Square ksq = king_square(us); - Square to = to_sq(m); - Square capsq = to - pawn_push(us); - Bitboard occupied = (pieces() ^ from ^ capsq) | to; - - assert(to == ep_square()); - assert(moved_piece(m) == make_piece(us, PAWN)); - assert(piece_on(capsq) == make_piece(~us, PAWN)); - assert(piece_on(to) == NO_PIECE); - - return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK)) - && !(attacks_bb(ksq, occupied) & pieces(~us, QUEEN, BISHOP)); - } - - // If the moving piece is a king, check whether the destination - // square is attacked by the opponent. Castling moves are checked - // for legality during move generation. - if (type_of(piece_on(from)) == KING) - return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us)); - - // A non-king move is legal if and only if it is not pinned or it - // is moving along the ray towards or away from the king. - return !pinned - || !(pinned & from) - || aligned(from, to_sq(m), king_square(us)); -} - - -/// Position::pseudo_legal() takes a random move and tests whether the move is -/// pseudo legal. It is used to validate moves from TT that can be corrupted -/// due to SMP concurrent access or hash position key aliasing. - -bool Position::pseudo_legal(const Move m) const { - - Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = moved_piece(m); - - // Use a slower but simpler function for uncommon cases - if (type_of(m) != NORMAL) - return MoveList(*this).contains(m); - - // Is not a promotion, so promotion piece must be empty - if (promotion_type(m) - 2 != NO_PIECE_TYPE) - return false; - - // If the 'from' square is not occupied by a piece belonging to the side to - // move, the move is obviously not legal. - if (pc == NO_PIECE || color_of(pc) != us) - return false; - - // The destination square cannot be occupied by a friendly piece - if (pieces(us) & to) - return false; - - // Handle the special case of a pawn move - if (type_of(pc) == PAWN) - { - // We have already handled promotion moves, so destination - // cannot be on the 8th/1st rank. - if (rank_of(to) == relative_rank(us, RANK_8)) - return false; - - if ( !(attacks_from(from, us) & pieces(~us) & to) // Not a capture - - && !((from + pawn_push(us) == to) && empty(to)) // Not a single push - - && !( (from + 2 * pawn_push(us) == to) // Not a double push - && (rank_of(from) == relative_rank(us, RANK_2)) - && empty(to) - && empty(to - pawn_push(us)))) - return false; - } - else if (!(attacks_from(pc, from) & to)) - return false; - - // Evasions generator already takes care to avoid some kind of illegal moves - // and legal() relies on this. We therefore have to take care that the same - // kind of moves are filtered out here. - if (checkers()) - { - if (type_of(pc) != KING) - { - // Double check? In this case a king move is required - if (more_than_one(checkers())) - return false; - - // Our move must be a blocking evasion or a capture of the checking piece - if (!((between_bb(lsb(checkers()), king_square(us)) | checkers()) & to)) - return false; - } - // In case of king moves under check we have to remove king so as to catch - // invalid moves like b1a1 when opposite queen is on c1. - else if (attackers_to(to, pieces() ^ from) & pieces(~us)) - return false; - } - - return true; -} - - -/// Position::gives_check() tests whether a pseudo-legal move gives a check - -bool Position::gives_check(Move m, const CheckInfo& ci) const { - - assert(is_ok(m)); - assert(ci.dcCandidates == discovered_check_candidates()); - assert(color_of(moved_piece(m)) == sideToMove); - - Square from = from_sq(m); - Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - - // Is there a direct check? - if (ci.checkSq[pt] & to) - return true; - - // Is there a discovered check? - if ( unlikely(ci.dcCandidates) - && (ci.dcCandidates & from) - && !aligned(from, to, ci.ksq)) - return true; - - switch (type_of(m)) - { - case NORMAL: - return false; - - case PROMOTION: - return attacks_bb(Piece(promotion_type(m)), to, pieces() ^ from) & ci.ksq; - - // 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 is the unusual case of a discovered check through - // the captured pawn. - case ENPASSANT: - { - Square capsq = make_square(file_of(to), rank_of(from)); - Bitboard b = (pieces() ^ from ^ capsq) | to; - - return (attacks_bb< ROOK>(ci.ksq, b) & pieces(sideToMove, QUEEN, ROOK)) - | (attacks_bb(ci.ksq, b) & pieces(sideToMove, QUEEN, BISHOP)); - } - case CASTLING: - { - Square kfrom = from; - Square rfrom = to; // Castling is encoded as 'King captures the rook' - Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1); - Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1); - - return (PseudoAttacks[ROOK][rto] & ci.ksq) - && (attacks_bb(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ci.ksq); - } - default: - assert(false); - return false; - } -} - - -/// Position::do_move() makes a move, and saves all information necessary -/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal -/// moves should be filtered out before this function is called. - -void Position::do_move(Move m, StateInfo& newSt) { - - CheckInfo ci(*this); - do_move(m, newSt, ci, gives_check(m, ci)); -} - -void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) { - - assert(is_ok(m)); - assert(&newSt != st); - - ++nodes; - Key k = st->key; - - // Copy some fields of the old state to our new StateInfo object except the - // ones which are going to be recalculated from scratch anyway and then switch - // our state pointer to point to the new (ready to be updated) state. - std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t)); - - newSt.previous = st; - st = &newSt; - - // Update side to move - k ^= Zobrist::side; - - // Increment ply counters. In particular, rule50 will be reset to zero later on - // in case of a capture or a pawn move. - ++gamePly; - ++st->rule50; - ++st->pliesFromNull; - - Color us = sideToMove; - Color them = ~us; - Square from = from_sq(m); - Square to = to_sq(m); - Piece pc = piece_on(from); - PieceType pt = type_of(pc); - PieceType captured = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to)); - - assert(color_of(pc) == us); - assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLING); - assert(captured != KING); - - if (type_of(m) == CASTLING) - { - assert(pc == make_piece(us, KING)); - - Square rfrom, rto; - do_castling(from, to, rfrom, rto); - - captured = NO_PIECE_TYPE; - st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom]; - k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto]; - } - - if (captured) - { - Square capsq = to; - - // If the captured piece is a pawn, update pawn hash key, otherwise - // update non-pawn material. - if (captured == PAWN) - { - if (type_of(m) == ENPASSANT) - { - capsq += pawn_push(them); - - assert(pt == PAWN); - assert(to == st->epSquare); - assert(relative_rank(us, to) == RANK_6); - assert(piece_on(to) == NO_PIECE); - assert(piece_on(capsq) == make_piece(them, PAWN)); - - board[capsq] = NO_PIECE; - } - - st->pawnKey ^= Zobrist::psq[them][PAWN][capsq]; - } - else - st->nonPawnMaterial[them] -= PieceValue[MG][captured]; - - // Update board and piece lists - remove_piece(capsq, them, captured); - - // Update material hash key and prefetch access to materialTable - k ^= Zobrist::psq[them][captured][capsq]; - st->materialKey ^= Zobrist::psq[them][captured][pieceCount[them][captured]]; - prefetch((char*)thisThread->materialTable[st->materialKey]); - - // Update incremental scores - st->psq -= psq[them][captured][capsq]; - - // Reset rule 50 counter - st->rule50 = 0; - } - - // Update hash key - k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to]; - - // Reset en passant square - if (st->epSquare != SQ_NONE) - { - k ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->epSquare = SQ_NONE; - } - - // Update castling rights if needed - if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to])) - { - int cr = castlingRightsMask[from] | castlingRightsMask[to]; - k ^= Zobrist::castling[st->castlingRights & cr]; - st->castlingRights &= ~cr; - } - - // Move the piece. The tricky Chess960 castling is handled earlier - if (type_of(m) != CASTLING) - move_piece(from, to, us, pt); - - // If the moving piece is a pawn do some special extra work - if (pt == PAWN) - { - // Set en-passant square if the moved pawn can be captured - if ( (int(to) ^ int(from)) == 16 - && (attacks_from(from + pawn_push(us), us) & pieces(them, PAWN))) - { - st->epSquare = Square((from + to) / 2); - k ^= Zobrist::enpassant[file_of(st->epSquare)]; - } - - else if (type_of(m) == PROMOTION) - { - PieceType promotion = promotion_type(m); - - assert(relative_rank(us, to) == RANK_8); - assert(promotion >= KNIGHT && promotion <= QUEEN); - - remove_piece(to, us, PAWN); - put_piece(to, us, promotion); - - // Update hash keys - k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us][promotion][to]; - st->pawnKey ^= Zobrist::psq[us][PAWN][to]; - st->materialKey ^= Zobrist::psq[us][promotion][pieceCount[us][promotion]-1] - ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]]; - - // Update incremental score - st->psq += psq[us][promotion][to] - psq[us][PAWN][to]; - - // Update material - st->nonPawnMaterial[us] += PieceValue[MG][promotion]; - } - - // Update pawn hash key and prefetch access to pawnsTable - st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to]; - prefetch((char*)thisThread->pawnsTable[st->pawnKey]); - - // Reset rule 50 draw counter - st->rule50 = 0; - } - - // Update incremental scores - st->psq += psq[us][pt][to] - psq[us][pt][from]; - - // Set capture piece - st->capturedType = captured; - - // Update the key with the final value - st->key = k; - - // Update checkers bitboard: piece must be already moved due to attacks_from() - st->checkersBB = 0; - - if (moveIsCheck) - { - if (type_of(m) != NORMAL) - st->checkersBB = attackers_to(king_square(them)) & pieces(us); - else - { - // Direct checks - if (ci.checkSq[pt] & to) - st->checkersBB |= to; - - // Discovered checks - if (unlikely(ci.dcCandidates) && (ci.dcCandidates & from)) - { - if (pt != ROOK) - st->checkersBB |= attacks_from(king_square(them)) & pieces(us, QUEEN, ROOK); - - if (pt != BISHOP) - st->checkersBB |= attacks_from(king_square(them)) & pieces(us, QUEEN, BISHOP); - } - } - } - - sideToMove = ~sideToMove; - - assert(pos_is_ok()); -} - - -/// Position::undo_move() unmakes a move. When it returns, the position should -/// be restored to exactly the same state as before the move was made. - -void Position::undo_move(Move m) { - - assert(is_ok(m)); - - sideToMove = ~sideToMove; - - Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); - PieceType pt = type_of(piece_on(to)); - - assert(empty(from) || type_of(m) == CASTLING); - assert(st->capturedType != KING); - - if (type_of(m) == PROMOTION) - { - assert(pt == promotion_type(m)); - assert(relative_rank(us, to) == RANK_8); - assert(promotion_type(m) >= KNIGHT && promotion_type(m) <= QUEEN); - - remove_piece(to, us, promotion_type(m)); - put_piece(to, us, PAWN); - pt = PAWN; - } - - if (type_of(m) == CASTLING) - { - Square rfrom, rto; - do_castling(from, to, rfrom, rto); - } - else - { - move_piece(to, from, us, pt); // Put the piece back at the source square - - if (st->capturedType) - { - Square capsq = to; - - if (type_of(m) == ENPASSANT) - { - capsq -= pawn_push(us); - - assert(pt == PAWN); - assert(to == st->previous->epSquare); - assert(relative_rank(us, to) == RANK_6); - assert(piece_on(capsq) == NO_PIECE); - } - - put_piece(capsq, ~us, st->capturedType); // Restore the captured piece - } - } - - // Finally point our state pointer back to the previous state - st = st->previous; - --gamePly; - - assert(pos_is_ok()); -} - - -/// Position::do_castling() is a helper used to do/undo a castling move. This -/// is a bit tricky, especially in Chess960. -template -void Position::do_castling(Square from, Square& to, Square& rfrom, Square& rto) { - - bool kingSide = to > from; - rfrom = to; // Castling is encoded as "king captures friendly rook" - rto = relative_square(sideToMove, kingSide ? SQ_F1 : SQ_D1); - to = relative_square(sideToMove, kingSide ? SQ_G1 : SQ_C1); - - // Remove both pieces first since squares could overlap in Chess960 - remove_piece(Do ? from : to, sideToMove, KING); - remove_piece(Do ? rfrom : rto, sideToMove, ROOK); - board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do it for us - put_piece(Do ? to : from, sideToMove, KING); - put_piece(Do ? rto : rfrom, sideToMove, ROOK); -} - - -/// Position::do(undo)_null_move() is used to do(undo) a "null move": It flips -/// the side to move without executing any move on the board. - -void Position::do_null_move(StateInfo& newSt) { - - assert(!checkers()); - - std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here - - newSt.previous = st; - st = &newSt; - - if (st->epSquare != SQ_NONE) - { - st->key ^= Zobrist::enpassant[file_of(st->epSquare)]; - st->epSquare = SQ_NONE; - } - - st->key ^= Zobrist::side; - prefetch((char*)TT.first_entry(st->key)); - - ++st->rule50; - st->pliesFromNull = 0; - - sideToMove = ~sideToMove; - - assert(pos_is_ok()); -} - -void Position::undo_null_move() { - - assert(!checkers()); - - st = st->previous; - sideToMove = ~sideToMove; -} - - -/// Position::key_after() computes the new hash key after the given move. Needed -/// for speculative prefetch. It doesn't recognize special moves like castling, -/// en-passant and promotions. - -Key Position::key_after(Move m) const { - - Color us = sideToMove; - Square from = from_sq(m); - Square to = to_sq(m); - PieceType pt = type_of(piece_on(from)); - PieceType captured = type_of(piece_on(to)); - Key k = st->key ^ Zobrist::side; - - if (captured) - k ^= Zobrist::psq[~us][captured][to]; - - return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from]; -} - - -/// Position::see() is a static exchange evaluator: It tries to estimate the -/// material gain or loss resulting from a move. - -Value Position::see_sign(Move m) const { - - assert(is_ok(m)); - - // Early return if SEE cannot be negative because captured piece value - // is not less then capturing one. Note that king moves always return - // here because king midgame value is set to 0. - if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG][piece_on(to_sq(m))]) - return VALUE_KNOWN_WIN; - - return see(m); -} - -Value Position::see(Move m) const { - - Square from, to; - Bitboard occupied, attackers, stmAttackers; - Value swapList[32]; - int slIndex = 1; - PieceType captured; - Color stm; - - assert(is_ok(m)); - - from = from_sq(m); - to = to_sq(m); - swapList[0] = PieceValue[MG][piece_on(to)]; - stm = color_of(piece_on(from)); - occupied = pieces() ^ from; - - // Castling moves are implemented as king capturing the rook so cannot be - // handled correctly. Simply return 0 that is always the correct value - // unless in the rare case the rook ends up under attack. - if (type_of(m) == CASTLING) - return VALUE_ZERO; - - if (type_of(m) == ENPASSANT) - { - occupied ^= to - pawn_push(stm); // Remove the captured pawn - swapList[0] = PieceValue[MG][PAWN]; - } - - // Find all attackers to the destination square, with the moving piece - // removed, but possibly an X-ray attacker added behind it. - attackers = attackers_to(to, occupied) & occupied; - - // If the opponent has no attackers we are finished - stm = ~stm; - stmAttackers = attackers & pieces(stm); - if (!stmAttackers) - return swapList[0]; - - // The destination square is defended, which makes things rather more - // difficult to compute. We proceed by building up a "swap list" containing - // the material gain or loss at each stop in a sequence of captures to the - // destination square, where the sides alternately capture, and always - // capture with the least valuable piece. After each capture, we look for - // new X-ray attacks from behind the capturing piece. - captured = type_of(piece_on(from)); - - do { - assert(slIndex < 32); - - // Add the new entry to the swap list - swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured]; - - // Locate and remove the next least valuable attacker - captured = min_attacker(byTypeBB, to, stmAttackers, occupied, attackers); - - // Stop before processing a king capture - if (captured == KING) - { - if (stmAttackers == attackers) - ++slIndex; - - break; - } - - stm = ~stm; - stmAttackers = attackers & pieces(stm); - ++slIndex; - - } while (stmAttackers); - - // Having built the swap list, we negamax through it to find the best - // achievable score from the point of view of the side to move. - while (--slIndex) - swapList[slIndex - 1] = std::min(-swapList[slIndex], swapList[slIndex - 1]); - - return swapList[0]; -} - - -/// Position::is_draw() tests whether the position is drawn by material, 50 moves -/// rule or repetition. It does not detect stalemates. - -bool Position::is_draw() const { - - if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) - return true; - - StateInfo* stp = st; - for (int i = 2, e = std::min(st->rule50, st->pliesFromNull); i <= e; i += 2) - { - stp = stp->previous->previous; - - if (stp->key == st->key) - return true; // Draw at first repetition - } - - return false; -} - - -/// Position::flip() flips position with the white and black sides reversed. This -/// is only useful for debugging e.g. for finding evaluation symmetry bugs. - -static char toggle_case(char c) { - return char(islower(c) ? toupper(c) : tolower(c)); -} - -void Position::flip() { - - string f, token; - std::stringstream ss(fen()); - - for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement - { - std::getline(ss, token, r > RANK_1 ? '/' : ' '); - f.insert(0, token + (f.empty() ? " " : "/")); - } - - ss >> token; // Active color - f += (token == "w" ? "B " : "W "); // Will be lowercased later - - ss >> token; // Castling availability - f += token + " "; - - std::transform(f.begin(), f.end(), f.begin(), toggle_case); - - ss >> token; // En passant square - f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3")); - - std::getline(ss, token); // Half and full moves - f += token; - - set(f, is_chess960(), this_thread()); - - assert(pos_is_ok()); -} - - -/// Position::pos_is_ok() performs some consistency checks for the position object. -/// This is meant to be helpful when debugging. - -bool Position::pos_is_ok(int* step) const { - - // Which parts of the position should be verified? - const bool all = false; - - const bool testBitboards = all || false; - const bool testState = all || false; - const bool testKingCount = all || false; - const bool testKingCapture = all || false; - const bool testPieceCounts = all || false; - const bool testPieceList = all || false; - const bool testCastlingSquares = all || false; - - if (step) - *step = 1; - - if ( (sideToMove != WHITE && sideToMove != BLACK) - || piece_on(king_square(WHITE)) != W_KING - || piece_on(king_square(BLACK)) != B_KING - || ( ep_square() != SQ_NONE - && relative_rank(sideToMove, ep_square()) != RANK_6)) - return false; - - if (step && ++*step, testBitboards) - { - // The intersection of the white and black pieces must be empty - if (pieces(WHITE) & pieces(BLACK)) - return false; - - // The union of the white and black pieces must be equal to all - // occupied squares - if ((pieces(WHITE) | pieces(BLACK)) != pieces()) - return false; - - // Separate piece type bitboards must have empty intersections - for (PieceType p1 = PAWN; p1 <= KING; ++p1) - for (PieceType p2 = PAWN; p2 <= KING; ++p2) - if (p1 != p2 && (pieces(p1) & pieces(p2))) - return false; - } - - if (step && ++*step, testState) - { - StateInfo si; - set_state(&si); - if ( st->key != si.key - || st->pawnKey != si.pawnKey - || st->materialKey != si.materialKey - || st->nonPawnMaterial[WHITE] != si.nonPawnMaterial[WHITE] - || st->nonPawnMaterial[BLACK] != si.nonPawnMaterial[BLACK] - || st->psq != si.psq - || st->checkersBB != si.checkersBB) - return false; - } - - if (step && ++*step, testKingCount) - if ( std::count(board, board + SQUARE_NB, W_KING) != 1 - || std::count(board, board + SQUARE_NB, B_KING) != 1) - return false; - - if (step && ++*step, testKingCapture) - if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove)) - return false; - - if (step && ++*step, testPieceCounts) - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - if (pieceCount[c][pt] != popcount(pieces(c, pt))) - return false; - - if (step && ++*step, testPieceList) - for (Color c = WHITE; c <= BLACK; ++c) - for (PieceType pt = PAWN; pt <= KING; ++pt) - for (int i = 0; i < pieceCount[c][pt]; ++i) - if ( board[pieceList[c][pt][i]] != make_piece(c, pt) - || index[pieceList[c][pt][i]] != i) - return false; - - if (step && ++*step, testCastlingSquares) - for (Color c = WHITE; c <= BLACK; ++c) - for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) - { - if (!can_castle(c | s)) - continue; - - if ( (castlingRightsMask[king_square(c)] & (c | s)) != (c | s) - || piece_on(castlingRookSquare[c | s]) != make_piece(c, ROOK) - || castlingRightsMask[castlingRookSquare[c | s]] != (c | s)) - return false; - } - - return true; -} diff --git a/src/position.h b/src/position.h deleted file mode 100644 index 3c7cf0f2..00000000 --- a/src/position.h +++ /dev/null @@ -1,439 +0,0 @@ -/* - Stockfish, a UCI chess playing engine derived from Glaurung 2.1 - Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord Romstad - - Stockfish is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Stockfish is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . -*/ - -#ifndef POSITION_H_INCLUDED -#define POSITION_H_INCLUDED - -#include -#include // For offsetof() - -#include "bitboard.h" -#include "types.h" - -class Position; -struct Thread; - -/// CheckInfo struct is initialized at c'tor time and keeps info used to detect -/// if a move gives check. - -struct CheckInfo { - - explicit CheckInfo(const Position&); - - Bitboard dcCandidates; - Bitboard pinned; - Bitboard checkSq[PIECE_TYPE_NB]; - Square ksq; -}; - - -/// StateInfo struct stores information needed to restore a Position object to -/// its previous state when we retract a move. Whenever a move is made on the -/// board (by calling Position::do_move), a StateInfo object must be passed. - -struct StateInfo { - - // Copied when making a move - Key pawnKey; - Key materialKey; - Value nonPawnMaterial[COLOR_NB]; - int castlingRights; - int rule50; - int pliesFromNull; - Score psq; - Square epSquare; - - // Not copied when making a move - Key key; - Bitboard checkersBB; - PieceType capturedType; - StateInfo* previous; -}; - - -/// When making a move the current StateInfo up to 'key' excluded is copied to -/// the new one. Here we calculate the quad words (64 bit) needed to be copied. -const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1; - - -/// Position class stores information regarding the board representation as -/// pieces, side to move, hash keys, castling info, etc. Important methods are -/// do_move() and undo_move(), used by the search to update node info when -/// traversing the search tree. - -class Position { - - friend std::ostream& operator<<(std::ostream&, const Position&); - - Position(const Position&); // Disable the default copy constructor - -public: - static void init(); - - Position() {} // To define the global object RootPos - Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; } - Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); } - Position& operator=(const Position&); // To assign RootPos from UCI - - // FEN string input/output - void set(const std::string& fenStr, bool isChess960, Thread* th); - const std::string fen() const; - - // Position representation - Bitboard pieces() const; - Bitboard pieces(PieceType pt) const; - Bitboard pieces(PieceType pt1, PieceType pt2) const; - Bitboard pieces(Color c) const; - Bitboard pieces(Color c, PieceType pt) const; - Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const; - Piece piece_on(Square s) const; - Square king_square(Color c) const; - Square ep_square() const; - bool empty(Square s) const; - template int count(Color c) const; - template const Square* list(Color c) const; - - // Castling - int can_castle(Color c) const; - int can_castle(CastlingRight cr) const; - bool castling_impeded(CastlingRight cr) const; - Square castling_rook_square(CastlingRight cr) const; - - // Checking - Bitboard checkers() const; - Bitboard discovered_check_candidates() const; - Bitboard pinned_pieces(Color c) const; - - // Attacks to/from a given square - Bitboard attackers_to(Square s) const; - Bitboard attackers_to(Square s, Bitboard occupied) const; - Bitboard attacks_from(Piece pc, Square s) const; - template Bitboard attacks_from(Square s) const; - template Bitboard attacks_from(Square s, Color c) const; - - // Properties of moves - bool legal(Move m, Bitboard pinned) const; - bool pseudo_legal(const Move m) const; - bool capture(Move m) const; - bool capture_or_promotion(Move m) const; - bool gives_check(Move m, const CheckInfo& ci) const; - bool advanced_pawn_push(Move m) const; - Piece moved_piece(Move m) const; - PieceType captured_piece_type() const; - - // Piece specific - bool pawn_passed(Color c, Square s) const; - bool pawn_on_7th(Color c) const; - bool opposite_bishops() const; - - // Doing and undoing moves - void do_move(Move m, StateInfo& st); - void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); - void undo_move(Move m); - void do_null_move(StateInfo& st); - void undo_null_move(); - - // Static exchange evaluation - Value see(Move m) const; - Value see_sign(Move m) const; - - // Accessing hash keys - Key key() const; - Key key_after(Move m) const; - Key exclusion_key() const; - Key material_key() const; - Key pawn_key() const; - - // Other properties of the position - Color side_to_move() const; - Phase game_phase() const; - int game_ply() const; - bool is_chess960() const; - Thread* this_thread() const; - uint64_t nodes_searched() const; - void set_nodes_searched(uint64_t n); - bool is_draw() const; - int rule50_count() const; - Score psq_score() const; - Value non_pawn_material(Color c) const; - - // Position consistency check, for debugging - bool pos_is_ok(int* step = NULL) const; - void flip(); - -private: - // Initialization helpers (used while setting up a position) - void clear(); - void set_castling_right(Color c, Square rfrom); - void set_state(StateInfo* si) const; - - // Other helpers - Bitboard check_blockers(Color c, Color kingColor) const; - void put_piece(Square s, Color c, PieceType pt); - void remove_piece(Square s, Color c, PieceType pt); - void move_piece(Square from, Square to, Color c, PieceType pt); - template - void do_castling(Square from, Square& to, Square& rfrom, Square& rto); - - // Data members - Piece board[SQUARE_NB]; - Bitboard byTypeBB[PIECE_TYPE_NB]; - Bitboard byColorBB[COLOR_NB]; - int pieceCount[COLOR_NB][PIECE_TYPE_NB]; - Square pieceList[COLOR_NB][PIECE_TYPE_NB][16]; - int index[SQUARE_NB]; - int castlingRightsMask[SQUARE_NB]; - Square castlingRookSquare[CASTLING_RIGHT_NB]; - Bitboard castlingPath[CASTLING_RIGHT_NB]; - StateInfo startState; - uint64_t nodes; - int gamePly; - Color sideToMove; - Thread* thisThread; - StateInfo* st; - bool chess960; -}; - -inline Color Position::side_to_move() const { - return sideToMove; -} - -inline bool Position::empty(Square s) const { - return board[s] == NO_PIECE; -} - -inline Piece Position::piece_on(Square s) const { - return board[s]; -} - -inline Piece Position::moved_piece(Move m) const { - return board[from_sq(m)]; -} - -inline Bitboard Position::pieces() const { - return byTypeBB[ALL_PIECES]; -} - -inline Bitboard Position::pieces(PieceType pt) const { - return byTypeBB[pt]; -} - -inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const { - return byTypeBB[pt1] | byTypeBB[pt2]; -} - -inline Bitboard Position::pieces(Color c) const { - return byColorBB[c]; -} - -inline Bitboard Position::pieces(Color c, PieceType pt) const { - return byColorBB[c] & byTypeBB[pt]; -} - -inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const { - return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]); -} - -template inline int Position::count(Color c) const { - return pieceCount[c][Pt]; -} - -template inline const Square* Position::list(Color c) const { - return pieceList[c][Pt]; -} - -inline Square Position::king_square(Color c) const { - return pieceList[c][KING][0]; -} - -inline Square Position::ep_square() const { - return st->epSquare; -} - -inline int Position::can_castle(CastlingRight cr) const { - return st->castlingRights & cr; -} - -inline int Position::can_castle(Color c) const { - return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c)); -} - -inline bool Position::castling_impeded(CastlingRight cr) const { - return byTypeBB[ALL_PIECES] & castlingPath[cr]; -} - -inline Square Position::castling_rook_square(CastlingRight cr) const { - return castlingRookSquare[cr]; -} - -template -inline Bitboard Position::attacks_from(Square s) const { - return Pt == BISHOP || Pt == ROOK ? attacks_bb(s, byTypeBB[ALL_PIECES]) - : Pt == QUEEN ? attacks_from(s) | attacks_from(s) - : StepAttacksBB[Pt][s]; -} - -template<> -inline Bitboard Position::attacks_from(Square s, Color c) const { - return StepAttacksBB[make_piece(c, PAWN)][s]; -} - -inline Bitboard Position::attacks_from(Piece pc, Square s) const { - return attacks_bb(pc, s, byTypeBB[ALL_PIECES]); -} - -inline Bitboard Position::attackers_to(Square s) const { - return attackers_to(s, byTypeBB[ALL_PIECES]); -} - -inline Bitboard Position::checkers() const { - return st->checkersBB; -} - -inline Bitboard Position::discovered_check_candidates() const { - return check_blockers(sideToMove, ~sideToMove); -} - -inline Bitboard Position::pinned_pieces(Color c) const { - return check_blockers(c, c); -} - -inline bool Position::pawn_passed(Color c, Square s) const { - return !(pieces(~c, PAWN) & passed_pawn_mask(c, s)); -} - -inline bool Position::advanced_pawn_push(Move m) const { - return type_of(moved_piece(m)) == PAWN - && relative_rank(sideToMove, from_sq(m)) > RANK_4; -} - -inline Key Position::key() const { - return st->key; -} - -inline Key Position::pawn_key() const { - return st->pawnKey; -} - -inline Key Position::material_key() const { - return st->materialKey; -} - -inline Score Position::psq_score() const { - return st->psq; -} - -inline Value Position::non_pawn_material(Color c) const { - return st->nonPawnMaterial[c]; -} - -inline int Position::game_ply() const { - return gamePly; -} - -inline int Position::rule50_count() const { - return st->rule50; -} - -inline uint64_t Position::nodes_searched() const { - return nodes; -} - -inline void Position::set_nodes_searched(uint64_t n) { - nodes = n; -} - -inline bool Position::opposite_bishops() const { - return pieceCount[WHITE][BISHOP] == 1 - && pieceCount[BLACK][BISHOP] == 1 - && opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]); -} - -inline bool Position::pawn_on_7th(Color c) const { - return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7)); -} - -inline bool Position::is_chess960() const { - return chess960; -} - -inline bool Position::capture_or_promotion(Move m) const { - - assert(is_ok(m)); - return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m)); -} - -inline bool Position::capture(Move m) const { - - // Castling is encoded as "king captures the rook" - assert(is_ok(m)); - return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT; -} - -inline PieceType Position::captured_piece_type() const { - return st->capturedType; -} - -inline Thread* Position::this_thread() const { - return thisThread; -} - -inline void Position::put_piece(Square s, Color c, PieceType pt) { - - board[s] = make_piece(c, pt); - byTypeBB[ALL_PIECES] |= s; - byTypeBB[pt] |= s; - byColorBB[c] |= s; - index[s] = pieceCount[c][pt]++; - pieceList[c][pt][index[s]] = s; - pieceCount[c][ALL_PIECES]++; -} - -inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) { - - // index[from] is not updated and becomes stale. This works as long as index[] - // is accessed just by known occupied squares. - Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to]; - byTypeBB[ALL_PIECES] ^= from_to_bb; - byTypeBB[pt] ^= from_to_bb; - byColorBB[c] ^= from_to_bb; - board[from] = NO_PIECE; - board[to] = make_piece(c, pt); - index[to] = index[from]; - pieceList[c][pt][index[to]] = to; -} - -inline void Position::remove_piece(Square s, Color c, PieceType pt) { - - // WARNING: This is not a reversible operation. If we remove a piece in - // do_move() and then replace it in undo_move() we will put it at the end of - // the list and not in its original place, it means index[] and pieceList[] - // are not guaranteed to be invariant to a do_move() + undo_move() sequence. - byTypeBB[ALL_PIECES] ^= s; - byTypeBB[pt] ^= s; - byColorBB[c] ^= s; - /* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */ - Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]]; - index[lastSquare] = index[s]; - pieceList[c][pt][index[lastSquare]] = lastSquare; - pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE; - pieceCount[c][ALL_PIECES]--; -} - -#endif // #ifndef POSITION_H_INCLUDED