Compare commits

..

20 Commits

Author SHA1 Message Date
Marco Costalba
f237e8b8ea Stockfish 2.2.2
Dedicated to the new Jim's super fast builds ! :-)

stockfish bench signature is: 5447426

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 15:01:21 +01:00
Marco Costalba
103b368ab7 Move struct RootMove to Search namespace
And directly pass RootMoves instead of SearchMoves
to main thread. A class declaration is better suited
in a header and slims a bit the fatty search.cpp

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 14:22:34 +01:00
Marco Costalba
a29dd88f75 Use a set to store SearchMoves
We just need to verify if a legal move is among the
SearchMoves, so we don't need a vector for this.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-14 13:06:01 +01:00
Marco Costalba
a1076cc68a Fix a gcc 4.7 warning
New gcc 4.7 complains about casting a volatile pointer
to void* so assign the variables directly.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-13 07:58:21 +01:00
Marco Costalba
78908b7aed Enable easy move detection only for recaptures
It could lead to terrible mistakes otherwise, as
it happened during a game on playchess when on
this position (after white's f4):

2q4r/4b1k1/p3rpp1/3np2p/PpNpNP1P/1P1P2PQ/2P1R3/4R1K1 b - - 0 1

SF moves immediately e5xf4 instead of the correct f5.
In general during engine matches it is impossible the
opponent leaves a piece hanging or anyhow starts a
clear losing sequence. So avoid to fall in subtle traps.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-13 07:27:01 +01:00
Marco Costalba
d98150dffc Use operator~ to flip colors and squares
More natural and nicer. Idea from Critter.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-12 19:37:44 +01:00
Marco Costalba
c549f71f64 Small touches in FEN decoding
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-12 18:40:58 +01:00
Marco Costalba
c19ea4b000 Retire is_mate()
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-10 21:53:07 +01:00
Marco Costalba
bede30e7a6 Introduce piece_moved() to simplify common code
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-09 22:36:45 +01:00
Marco Costalba
b05fbb3733 Unify PseudoAttacks arrays
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-09 22:08:37 +01:00
Marco Costalba
9b43fd7937 Use CheckInfo to generate checks
It should help to avoid recalculating check squares
of sliding attackers for queen when already done for
bishops and rooks. Of course this helps when there are
bishop, rook and queen on the board !

Fixed also a subtle bug (use of same variable b in while
condition and in condition body) introduced recently by
revision d655147e8c that triggers
in case we have at least 2 non-pawn discovered check pieces.
This is very rare that's why didn't show in the node count
verification where we actually have a case of 2 dc pieces
in position 14, but one is a pawn.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-08 16:09:18 +01:00
Marco Costalba
87fc9dcaa3 Add castling to generation of checking moves
During generation of non-captures checks (in qsearch)
we don't consider castling moves that give check,
this patch includes also this rare case. Verified with
perft that all the non-capture checks are now generated.

There should be a very little slowdown due to the extra
work, but actually I failed to measure it. I don't expect
any ELO improvment, there is even no functional change on
the standard depth 12 search, it is just to have a correct
move generator.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-08 12:28:51 +01:00
Marco Costalba
83f3ea7ab4 Last touches to movegen.cpp
The full movegen patch series shows a speed up of almost
6% (!) on perft, and code is much more readable too.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 13:13:28 +01:00
Marco Costalba
9c8c4ff46f Retire the redundant MV_CHECK
MV_CHECK is an alias of the more appropiate named
MV_NON_CAPTURE_CHECK so use only the latter.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:19 +01:00
Marco Costalba
d655147e8c Retire generate_discovered_checks
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:17 +01:00
Marco Costalba
3c675db3d0 Rearrange pawn moves generation
Functional change due only to rearrangement

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:16 +01:00
Marco Costalba
cf247e7e30 Reshuffle stuff in movegen.cpp
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 12:03:15 +01:00
Marco Costalba
0026c88b3a Retire OLD_LOCKS option
And make CRITICAL_SECTION locks the only option for Windows.
This guarantees backward compatibility with all the Windows
versions (even XP and older) and an hassle free experience
when compiling for Windows. Tests performed by Ingo and
reported on talkchess confirm there is no speed penalty
against the most modern SRW locks:

http://www.talkchess.com/forum/viewtopic.php?t=41835&start=20

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 11:18:09 +01:00
Marco Costalba
6482ce2bb2 Fix compile on HP-UX 11's HP's C++
On that platform non-bracketed casting are not supported.

Reported by Richard Lloyd.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-07 00:04:08 +01:00
Marco Costalba
867a5a8cd2 Restore development version
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2012-01-06 18:09:25 +01:00
21 changed files with 548 additions and 627 deletions

View File

@@ -28,7 +28,6 @@
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
using namespace Search;
static const char* Defaults[] = { static const char* Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
@@ -61,7 +60,7 @@ static const char* Defaults[] = {
void benchmark(int argc, char* argv[]) { void benchmark(int argc, char* argv[]) {
vector<string> fens; vector<string> fens;
LimitsType limits; Search::LimitsType limits;
int time; int time;
int64_t nodes = 0; int64_t nodes = 0;
@@ -115,14 +114,14 @@ void benchmark(int argc, char* argv[]) {
if (valType == "perft") if (valType == "perft")
{ {
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY); int64_t cnt = Search::perft(pos, limits.maxDepth * ONE_PLY);
cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl; cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl;
nodes += cnt; nodes += cnt;
} }
else else
{ {
Threads.start_thinking(pos, limits, vector<Move>(), false); Threads.start_thinking(pos, limits);
nodes += RootPosition.nodes_searched(); nodes += Search::RootPosition.nodes_searched();
} }
} }

View File

@@ -49,9 +49,7 @@ Bitboard SquaresInFrontMask[2][64];
Bitboard PassedPawnMask[2][64]; Bitboard PassedPawnMask[2][64];
Bitboard AttackSpanMask[2][64]; Bitboard AttackSpanMask[2][64];
Bitboard BishopPseudoAttacks[64]; Bitboard PseudoAttacks[6][64];
Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256]; uint8_t BitCount8Bit[256];
int SquareDistance[64][64]; int SquareDistance[64][64];
@@ -203,7 +201,7 @@ void bitboards_init() {
Bitboard b = 1ULL << i; Bitboard b = 1ULL << i;
b ^= b - 1; b ^= b - 1;
b ^= b >> 32; b ^= b >> 32;
BSFTable[uint32_t(b * 0x783A9B23) >> 26] = i; BSFTable[(uint32_t)(b * 0x783A9B23) >> 26] = i;
} }
else else
BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i; BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i;
@@ -227,14 +225,14 @@ void bitboards_init() {
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
BishopPseudoAttacks[s] = bishop_attacks_bb(s, 0); PseudoAttacks[BISHOP][s] = bishop_attacks_bb(s, 0);
RookPseudoAttacks[s] = rook_attacks_bb(s, 0); PseudoAttacks[ROOK][s] = rook_attacks_bb(s, 0);
QueenPseudoAttacks[s] = queen_attacks_bb(s, 0); PseudoAttacks[QUEEN][s] = queen_attacks_bb(s, 0);
} }
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++) for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++) for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2)) if (bit_is_set(PseudoAttacks[QUEEN][s1], s2))
{ {
Square delta = (s2 - s1) / square_distance(s1, s2); Square delta = (s2 - s1) / square_distance(s1, s2);

View File

@@ -49,9 +49,7 @@ extern int BShifts[64];
extern Bitboard BMasks[64]; extern Bitboard BMasks[64];
extern Bitboard* BAttacks[64]; extern Bitboard* BAttacks[64];
extern Bitboard BishopPseudoAttacks[64]; extern Bitboard PseudoAttacks[6][64];
extern Bitboard RookPseudoAttacks[64];
extern Bitboard QueenPseudoAttacks[64];
extern uint8_t BitCount8Bit[256]; extern uint8_t BitCount8Bit[256];

View File

@@ -205,10 +205,10 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
} }
else else
{ {
wksq = flip(pos.king_square(BLACK)); wksq = ~pos.king_square(BLACK);
bksq = flip(pos.king_square(WHITE)); bksq = ~pos.king_square(WHITE);
wpsq = flip(pos.piece_list(BLACK, PAWN)[0]); wpsq = ~pos.piece_list(BLACK, PAWN)[0];
stm = flip(pos.side_to_move()); stm = ~pos.side_to_move();
} }
if (file_of(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)
@@ -251,10 +251,10 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
wksq = flip(wksq); wksq = ~wksq;
wrsq = flip(wrsq); wrsq = ~wrsq;
bksq = flip(bksq); bksq = ~bksq;
bpsq = flip(bpsq); bpsq = ~bpsq;
} }
Square queeningSq = make_square(file_of(bpsq), RANK_1); Square queeningSq = make_square(file_of(bpsq), RANK_1);
@@ -491,11 +491,11 @@ ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
// pawn is on the left half of the board. // pawn is on the left half of the board.
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
wksq = flip(wksq); wksq = ~wksq;
wrsq = flip(wrsq); wrsq = ~wrsq;
wpsq = flip(wpsq); wpsq = ~wpsq;
bksq = flip(bksq); bksq = ~bksq;
brsq = flip(brsq); brsq = ~brsq;
} }
if (file_of(wpsq) > FILE_D) if (file_of(wpsq) > FILE_D)
{ {
@@ -867,10 +867,10 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
wksq = flip(wksq); wksq = ~wksq;
bksq = flip(bksq); bksq = ~bksq;
wpsq = flip(wpsq); wpsq = ~wpsq;
stm = flip(stm); stm = ~stm;
} }
if (file_of(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)

View File

@@ -82,7 +82,7 @@ struct EndgameBase {
template<EndgameType E, typename T = typename eg_family<E>::type> template<EndgameType E, typename T = typename eg_family<E>::type>
struct Endgame : public EndgameBase<T> { struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(flip(c)) {} explicit Endgame(Color c) : strongerSide(c), weakerSide(~c) {}
Color color() const { return strongerSide; } Color color() const { return strongerSide; }
T operator()(const Position&) const; T operator()(const Position&) const;

View File

@@ -714,7 +714,7 @@ namespace {
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them); b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
// Consider only squares where the enemy rook gives check // Consider only squares where the enemy rook gives check
b &= RookPseudoAttacks[ksq]; b &= PseudoAttacks[ROOK][ksq];
if (b) if (b)
{ {
@@ -887,7 +887,7 @@ namespace {
for (c = WHITE; c <= BLACK; c++) for (c = WHITE; c <= BLACK; c++)
{ {
// Skip if other side has non-pawn pieces // Skip if other side has non-pawn pieces
if (pos.non_pawn_material(flip(c))) if (pos.non_pawn_material(~c))
continue; continue;
b = ei.pi->passed_pawns(c); b = ei.pi->passed_pawns(c);
@@ -900,7 +900,7 @@ namespace {
// Compute plies to queening and check direct advancement // Compute plies to queening and check direct advancement
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
oppMovesToGo = square_distance(pos.king_square(flip(c)), queeningSquare) - int(c != pos.side_to_move()); oppMovesToGo = square_distance(pos.king_square(~c), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath); pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);
if (movesToGo >= oppMovesToGo && !pathDefended) if (movesToGo >= oppMovesToGo && !pathDefended)
@@ -928,7 +928,7 @@ namespace {
return SCORE_ZERO; return SCORE_ZERO;
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK); winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK);
loserSide = flip(winnerSide); loserSide = ~winnerSide;
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
b = candidates = pos.pieces(PAWN, loserSide); b = candidates = pos.pieces(PAWN, loserSide);

View File

@@ -45,26 +45,9 @@ typedef pthread_cond_t WaitCondition;
#undef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX #undef NOMINMAX
// Default fast and race free locks and condition variables // We use critical sections on Windows to support Windows XP and older versions,
#if !defined(OLD_LOCKS) // unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject()
// but apart from this they have the same speed performance of SRW locks.
typedef SRWLOCK Lock;
typedef CONDITION_VARIABLE WaitCondition;
# define lock_init(x) InitializeSRWLock(x)
# define lock_grab(x) AcquireSRWLockExclusive(x)
# define lock_release(x) ReleaseSRWLockExclusive(x)
# define lock_destroy(x) (x)
# define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(x)
# define cond_signal(x) WakeConditionVariable(x)
# define cond_wait(x,y) SleepConditionVariableSRW(x,y,INFINITE,0)
# define cond_timedwait(x,y,z) SleepConditionVariableSRW(x,y,z,0)
// Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject().
#else
typedef CRITICAL_SECTION Lock; typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition; typedef HANDLE WaitCondition;
@@ -80,6 +63,4 @@ typedef HANDLE WaitCondition;
#endif #endif
#endif
#endif // !defined(LOCK_H_INCLUDED) #endif // !defined(LOCK_H_INCLUDED)

View File

@@ -203,13 +203,13 @@ MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const {
// No pawns makes it difficult to win, even with a material advantage // No pawns makes it difficult to win, even with a material advantage
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame) if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
{ {
mi->factor[WHITE] = uint8_t mi->factor[WHITE] = (uint8_t)
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]); (npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
} }
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame) if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
{ {
mi->factor[BLACK] = uint8_t mi->factor[BLACK] = (uint8_t)
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]); (npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
} }
@@ -231,7 +231,7 @@ MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const {
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT), { pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } }; pos.piece_count(BLACK, BISHOP) , pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
mi->value = int16_t((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16); mi->value = (int16_t)((imbalance<WHITE>(pieceCount) - imbalance<BLACK>(pieceCount)) / 16);
return mi; return mi;
} }

View File

@@ -55,7 +55,7 @@ using namespace std;
/// Version number. If Version is left empty, then Tag plus current /// Version number. If Version is left empty, then Tag plus current
/// date (in the format YYMMDD) is used as a version number. /// date (in the format YYMMDD) is used as a version number.
static const string Version = "2.2.1"; static const string Version = "2.2.2";
static const string Tag = ""; static const string Tag = "";

View File

@@ -147,13 +147,13 @@ const string move_to_san(Position& pos, Move m) {
} }
} }
// The move gives check? We don't use pos.move_gives_check() here if (pos.move_gives_check(m, CheckInfo(pos)))
// because we need to test for a mate after the move is done. {
StateInfo st; StateInfo st;
pos.do_move(m, st); pos.do_move(m, st);
if (pos.in_check()) san += MoveList<MV_LEGAL>(pos).size() ? "+" : "#";
san += pos.is_mate() ? "#" : "+";
pos.undo_move(m); pos.undo_move(m);
}
return san; return san;
} }

View File

@@ -17,50 +17,238 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <algorithm> #include <algorithm>
#include <cassert>
#include "bitcount.h"
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility, /// Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'. /// hardcoded names 'mlist' and 'from'.
#define SERIALIZE_MOVES(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b)) #define SERIALIZE(b) while (b) (*mlist++).move = make_move(from, pop_1st_bit(&b))
// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_MOVES_D(b, d) while (b) { to = pop_1st_bit(&b); (*mlist++).move = make_move(to + (d), to); }
/// Version used for pawns, where the 'from' square is given as a delta from the 'to' square
#define SERIALIZE_PAWNS(b, d) while (b) { Square to = pop_1st_bit(&b); \
(*mlist++).move = make_move(to + (d), to); }
namespace { namespace {
enum CastlingSide { enum CastlingSide { KING_SIDE, QUEEN_SIDE };
KING_SIDE,
QUEEN_SIDE
};
template<CastlingSide> template<CastlingSide Side, bool OnlyChecks>
MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us); MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
template<Color, MoveType> const CastleRight CR[] = { Side ? WHITE_OOO : WHITE_OO,
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square); Side ? BLACK_OOO : BLACK_OO };
template<PieceType Pt> if (!pos.can_castle(CR[us]))
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) { return mlist;
assert(Pt != QUEEN && Pt != PAWN); // 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.castle_rook_square(CR[us]);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Bitboard enemies = pos.pieces(~us);
Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares(); assert(!pos.in_check());
assert(pos.piece_on(kfrom) == make_piece(us, KING));
assert(pos.piece_on(rfrom) == make_piece(us, ROOK));
if (Pt == KING) // Unimpeded rule: All the squares between the king's initial and final squares
b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))]; // (including the final square), and all the squares between the rook's initial
// and final squares (including the final square), must be vacant except for
// the king and castling rook.
for (Square s = std::min(rfrom, rto), e = std::max(rfrom, rto); s <= e; s++)
if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
return mlist;
SERIALIZE_MOVES(b); for (Square s = std::min(kfrom, kto), e = std::max(kfrom, kto); s <= e; s++)
if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s))
||(pos.attackers_to(s) & enemies))
return mlist;
// 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 (pos.is_chess960())
{
Bitboard occ = pos.occupied_squares();
clear_bit(&occ, rfrom);
if (pos.attackers_to(kto, occ) & enemies)
return mlist; return mlist;
} }
(*mlist++).move = make_castle(kfrom, rfrom);
if (OnlyChecks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--;
return mlist;
}
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 :
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
}
template<Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = ( Delta == DELTA_NE
|| Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b = move_pawns<Delta>(pawns) & target & ~TFileABB;
SERIALIZE_PAWNS(b, -Delta);
return mlist;
}
template<MoveType Type, Square Delta>
inline MoveStack* generate_promotions(MoveStack* mlist, Bitboard pawnsOn7, Bitboard target, Square ksq) {
const Bitboard TFileABB = ( Delta == DELTA_NE
|| Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b = move_pawns<Delta>(pawnsOn7) & target;
if (Delta != DELTA_N && Delta != DELTA_S)
b &= ~TFileABB;
while (b)
{
Square to = pop_1st_bit(&b);
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
(*mlist++).move = make_promotion(to - Delta, to, QUEEN);
if (Type == MV_NON_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{
(*mlist++).move = make_promotion(to - Delta, to, ROOK);
(*mlist++).move = make_promotion(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
}
// Knight-promotion is the only one that can give a check (direct or
// discovered) not already included in the queen-promotion.
if (Type == MV_NON_CAPTURE_CHECK && bit_is_set(StepAttacksBB[W_KNIGHT][to], ksq))
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
else
(void)ksq; // Silence a warning under MSVC
}
return mlist;
}
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate 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 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 b1, b2, dc1, dc2, emptySquares;
Bitboard pawnsOn7 = pos.pieces(PAWN, Us) & TRank7BB;
Bitboard pawnsNotOn7 = pos.pieces(PAWN, Us) & ~TRank7BB;
Bitboard enemies = (Type == MV_EVASION ? pos.pieces(Them) & target:
Type == MV_CAPTURE ? target : pos.pieces(Them));
// Single and double pawn pushes, no promotions
if (Type != MV_CAPTURE)
{
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares());
b1 = move_pawns<UP>(pawnsNotOn7) & emptySquares;
b2 = move_pawns<UP>(b1 & TRank3BB) & emptySquares;
if (Type == MV_EVASION) // Consider only blocking squares
{
b1 &= target;
b2 &= target;
}
if (Type == MV_NON_CAPTURE_CHECK)
{
// Consider only direct checks
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(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 among captures.
if (pawnsNotOn7 & target) // Target is dc bitboard
{
dc1 = move_pawns<UP>(pawnsNotOn7 & target) & emptySquares & ~file_bb(ksq);
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_PAWNS(b1, -UP);
SERIALIZE_PAWNS(b2, -UP -UP);
}
// Promotions and underpromotions
if (pawnsOn7)
{
if (Type == MV_CAPTURE)
emptySquares = pos.empty_squares();
if (Type == MV_EVASION)
emptySquares &= target;
mlist = generate_promotions<Type, RIGHT>(mlist, pawnsOn7, enemies, ksq);
mlist = generate_promotions<Type, LEFT>(mlist, pawnsOn7, enemies, ksq);
mlist = generate_promotions<Type, UP>(mlist, pawnsOn7, emptySquares, ksq);
}
// Standard and en-passant captures
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{
mlist = generate_pawn_captures<RIGHT>(mlist, pawnsNotOn7, enemies);
mlist = generate_pawn_captures<LEFT >(mlist, pawnsNotOn7, enemies);
if (pos.ep_square() != SQ_NONE)
{
assert(rank_of(pos.ep_square()) == (Us == WHITE ? RANK_6 : RANK_3));
// 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 == MV_EVASION && !bit_is_set(target, pos.ep_square() - UP))
return mlist;
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1);
while (b1)
(*mlist++).move = make_enpassant(pop_1st_bit(&b1), pos.ep_square());
}
}
return mlist;
}
template<PieceType Pt> template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us, inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist,
Bitboard dc, Square ksq) { Color us, const CheckInfo& ci) {
assert(Pt != KING && Pt != PAWN); assert(Pt != KING && Pt != PAWN);
Bitboard checkSqs, b; Bitboard checkSqs, b;
@@ -70,41 +258,44 @@ namespace {
if ((from = *pl++) == SQ_NONE) if ((from = *pl++) == SQ_NONE)
return mlist; return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares(); checkSqs = ci.checkSq[Pt] & pos.empty_squares();
do do
{ {
if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs)) if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
|| (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs)) && !(PseudoAttacks[Pt][from] & checkSqs))
|| (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
continue; continue;
if (dc && bit_is_set(dc, from)) if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from))
continue; continue;
b = pos.attacks_from<Pt>(from) & checkSqs; b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b); SERIALIZE(b);
} while ((from = *pl++) != SQ_NONE); } while ((from = *pl++) != SQ_NONE);
return mlist; return mlist;
} }
template<>
FORCE_INLINE MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
return (us == WHITE ? generate_pawn_moves<WHITE, MV_CHECK>(p, m, dc, ksq) template<>
: generate_pawn_moves<BLACK, MV_CHECK>(p, m, dc, ksq)); FORCE_INLINE MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m,
Color us, const CheckInfo& ci) {
return us == WHITE ? generate_pawn_moves<WHITE, MV_NON_CAPTURE_CHECK>(p, m, ci.dcCandidates, ci.ksq)
: generate_pawn_moves<BLACK, MV_NON_CAPTURE_CHECK>(p, m, ci.dcCandidates, ci.ksq);
} }
template<PieceType Pt, MoveType Type> template<PieceType Pt, MoveType Type>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) { FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Pt == PAWN); assert(Pt == PAWN);
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE) return us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE)); : generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE);
} }
template<PieceType Pt> template<PieceType Pt>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) { FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
@@ -117,24 +308,23 @@ namespace {
do { do {
from = *pl; from = *pl;
b = pos.attacks_from<Pt>(from) & target; b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b); SERIALIZE(b);
} while (*++pl != SQ_NONE); } while (*++pl != SQ_NONE);
} }
return mlist; return mlist;
} }
template<> template<>
FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) { FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from = pos.king_square(us); Square from = pos.king_square(us);
Bitboard b = pos.attacks_from<KING>(from) & target;
b = pos.attacks_from<KING>(from) & target; SERIALIZE(b);
SERIALIZE_MOVES(b);
return mlist; return mlist;
} }
} } // namespace
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen /// generate<MV_CAPTURE> generates all pseudo-legal captures and queen
@@ -156,13 +346,13 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) {
Bitboard target; Bitboard target;
if (Type == MV_CAPTURE) if (Type == MV_CAPTURE)
target = pos.pieces(flip(us)); target = pos.pieces(~us);
else if (Type == MV_NON_CAPTURE) else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares(); target = pos.empty_squares();
else if (Type == MV_NON_EVASION) else if (Type == MV_NON_EVASION)
target = pos.pieces(flip(us)) | pos.empty_squares(); target = pos.pieces(~us) | pos.empty_squares();
mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target); mlist = generate_piece_moves<PAWN, Type>(pos, mlist, us, target);
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target); mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
@@ -173,11 +363,8 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) {
if (Type != MV_CAPTURE && pos.can_castle(us)) if (Type != MV_CAPTURE && pos.can_castle(us))
{ {
if (pos.can_castle(us == WHITE ? WHITE_OO : BLACK_OO)) mlist = generate_castle_moves<KING_SIDE, false>(pos, mlist, us);
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us); mlist = generate_castle_moves<QUEEN_SIDE, false>(pos, mlist, us);
if (pos.can_castle(us == WHITE ? WHITE_OOO : BLACK_OOO))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
} }
return mlist; return mlist;
@@ -196,36 +383,39 @@ MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist)
assert(!pos.in_check()); assert(!pos.in_check());
Bitboard b, dc;
Square from;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(flip(us)); CheckInfo ci(pos);
Bitboard dc = ci.dcCandidates;
assert(pos.piece_on(ksq) == make_piece(flip(us), KING)); while (dc)
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates();
while (b)
{ {
from = pop_1st_bit(&b); Square from = pop_1st_bit(&dc);
switch (type_of(pos.piece_on(from))) PieceType pt = type_of(pos.piece_on(from));
{
case PAWN: /* Will be generated togheter with pawns direct checks */ break; if (pt == PAWN)
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break; continue; // Will be generated togheter with direct checks
case BISHOP: mlist = generate_discovered_checks<BISHOP>(pos, mlist, from); break;
case ROOK: mlist = generate_discovered_checks<ROOK>(pos, mlist, from); break; Bitboard b = pos.attacks_from(Piece(pt), from) & pos.empty_squares();
case KING: mlist = generate_discovered_checks<KING>(pos, mlist, from); break;
default: assert(false); break; if (pt == KING)
} b &= ~PseudoAttacks[QUEEN][ci.ksq];
SERIALIZE(b);
} }
// Direct non-capture checks mlist = generate_direct_checks<PAWN>(pos, mlist, us, ci);
mlist = generate_direct_checks<PAWN>(pos, mlist, us, dc, ksq); mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, ci);
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, dc, ksq); mlist = generate_direct_checks<BISHOP>(pos, mlist, us, ci);
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, dc, ksq); mlist = generate_direct_checks<ROOK>(pos, mlist, us, ci);
mlist = generate_direct_checks<ROOK>(pos, mlist, us, dc, ksq); mlist = generate_direct_checks<QUEEN>(pos, mlist, us, ci);
return generate_direct_checks<QUEEN>(pos, mlist, us, dc, ksq);
if (pos.can_castle(us))
{
mlist = generate_castle_moves<KING_SIDE, true>(pos, mlist, us);
mlist = generate_castle_moves<QUEEN_SIDE, true>(pos, mlist, us);
}
return mlist;
} }
@@ -241,38 +431,37 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
int checkersCnt = 0; int checkersCnt = 0;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us); Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = 0; Bitboard sliderAttacks = 0;
Bitboard checkers = pos.checkers();
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers); assert(checkers);
// Find squares attacked by slider checkers, we will remove // Find squares attacked by slider checkers, we will remove them from the king
// them from the king evasions set so to early skip known // evasions so to skip known illegal moves avoiding useless legality check later.
// illegal moves and avoid an useless legality check later.
b = checkers; b = checkers;
do do
{ {
checkersCnt++; checkersCnt++;
checksq = pop_1st_bit(&b); checksq = pop_1st_bit(&b);
assert(color_of(pos.piece_on(checksq)) == flip(us)); assert(color_of(pos.piece_on(checksq)) == ~us);
switch (type_of(pos.piece_on(checksq))) switch (type_of(pos.piece_on(checksq)))
{ {
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break; case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break; case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break;
case QUEEN: case QUEEN:
// If queen and king are far we can safely remove all the squares attacked // If queen and king are far or not on a diagonal line we can safely
// in the other direction becuase are not reachable by the king anyway. // remove all the squares attacked in the other direction becuase are
if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq))) // not reachable by the king anyway.
sliderAttacks |= QueenPseudoAttacks[checksq]; if (squares_between(ksq, checksq) || !bit_is_set(PseudoAttacks[BISHOP][checksq], ksq))
sliderAttacks |= PseudoAttacks[QUEEN][checksq];
// Otherwise, if king and queen are adjacent and on a diagonal line, we need to // Otherwise we need to use real rook attacks to check if king is safe
// use real rook attacks to check if king is safe to move in the other direction. // to move in the other direction. For example: king in B2, queen in A1
// For example: king in B2, queen in A1 a knight in B1, and we can safely move to C1. // a knight in B1, and we can safely move to C1.
else else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq); sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from<ROOK>(checksq);
default: default:
break; break;
@@ -282,14 +471,13 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks; b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq; from = ksq;
SERIALIZE_MOVES(b); SERIALIZE(b);
// Generate evasions for other pieces only if not double check // Generate evasions for other pieces only if not under a double check
if (checkersCnt > 1) if (checkersCnt > 1)
return mlist; return mlist;
// Find squares where a blocking evasion or a capture of the // Blocking evasions or captures of the checking piece
// checker piece is possible.
target = squares_between(checksq, ksq) | checkers; target = squares_between(checksq, ksq) | checkers;
mlist = generate_piece_moves<PAWN, MV_EVASION>(pos, mlist, us, target); mlist = generate_piece_moves<PAWN, MV_EVASION>(pos, mlist, us, target);
@@ -300,7 +488,7 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
} }
/// generate<MV_LEGAL> computes a complete list of legal moves in the current position /// generate<MV_LEGAL> generates all the legal moves in the given position
template<> template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) { MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
@@ -310,8 +498,6 @@ MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
last = pos.in_check() ? generate<MV_EVASION>(pos, mlist) last = pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist); : generate<MV_NON_EVASION>(pos, mlist);
// Remove illegal moves from the list
while (cur != last) while (cur != last)
if (!pos.pl_move_is_legal(cur->move, pinned)) if (!pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--last)->move; cur->move = (--last)->move;
@@ -320,218 +506,3 @@ MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
return last; return last;
} }
namespace {
template<Square Delta>
inline Bitboard move_pawns(Bitboard p) {
return Delta == DELTA_N ? p << 8 : Delta == DELTA_S ? p >> 8 :
Delta == DELTA_NE ? p << 9 : Delta == DELTA_SE ? p >> 7 :
Delta == DELTA_NW ? p << 7 : Delta == DELTA_SW ? p >> 9 : p;
}
template<Square Delta>
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b;
Square to;
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
b = move_pawns<Delta>(pawns) & target & ~TFileABB;
SERIALIZE_MOVES_D(b, -Delta);
return mlist;
}
template<MoveType Type, Square Delta>
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
Bitboard b;
Square to;
// Promotions and under-promotions, both captures and non-captures
b = move_pawns<Delta>(pawnsOn7) & target;
if (Delta != DELTA_N && Delta != DELTA_S)
b &= ~TFileABB;
while (b)
{
to = pop_1st_bit(&b);
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
(*mlist++).move = make_promotion(to - Delta, to, QUEEN);
if (Type == MV_NON_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{
(*mlist++).move = make_promotion(to - Delta, to, ROOK);
(*mlist++).move = make_promotion(to - Delta, to, BISHOP);
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
}
// This is the only possible under promotion that can give a check
// not already included in the queen-promotion.
if ( Type == MV_CHECK
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Delta > 0 ? BLACK : WHITE)))
(*mlist++).move = make_promotion(to - Delta, to, KNIGHT);
else (void)pos; // Silence a warning under MSVC
}
return mlist;
}
template<Color Us, MoveType Type>
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
// Calculate 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 TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT_UP = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT_UP = (Us == WHITE ? DELTA_NW : DELTA_SE);
Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces(Them));
// Pre-calculate pawn pushes before changing emptySquares definition
if (Type != MV_CAPTURE)
{
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<UP>(pawns & ~TRank7BB) & emptySquares;
}
if (Type == MV_EVASION)
{
emptySquares &= target; // Only blocking squares
enemyPieces &= target; // Capture only the checker piece
}
// Promotions and underpromotions
if (pawnsOn7)
{
if (Type == MV_CAPTURE)
emptySquares = pos.empty_squares();
pawns &= ~TRank7BB;
mlist = generate_promotions<Type, RIGHT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, LEFT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, UP>(pos, mlist, pawnsOn7, emptySquares);
}
// Standard captures
if (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
{
mlist = generate_pawn_captures<RIGHT_UP>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<LEFT_UP>(mlist, pawns, enemyPieces);
}
// Single and double pawn pushes
if (Type != MV_CAPTURE)
{
b1 = (Type != MV_EVASION ? pawnPushes : pawnPushes & emptySquares);
b2 = move_pawns<UP>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK)
{
// Consider only pawn moves which give direct checks
b1 &= pos.attacks_from<PAWN>(ksq, Them);
b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn moves which gives 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.
if (pawns & target) // For CHECK type target is dc bitboard
{
dc1 = move_pawns<UP>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_MOVES_D(b1, -UP);
SERIALIZE_MOVES_D(b2, -UP -UP);
}
// En passant captures
if ( (Type == MV_CAPTURE || Type == MV_EVASION || Type == MV_NON_EVASION)
&& pos.ep_square() != SQ_NONE)
{
assert(Us != WHITE || rank_of(pos.ep_square()) == RANK_6);
assert(Us != BLACK || rank_of(pos.ep_square()) == RANK_3);
// 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 == MV_EVASION && !bit_is_set(target, pos.ep_square() - UP))
return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1);
while (b1)
{
to = pop_1st_bit(&b1);
(*mlist++).move = make_enpassant(to, pos.ep_square());
}
}
return mlist;
}
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
CastleRight f = CastleRight((Side == KING_SIDE ? WHITE_OO : WHITE_OOO) << us);
Color them = flip(us);
// After castling, the rook and king's final positions are exactly the same
// in Chess960 as they would be in standard chess.
Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(f);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
assert(!pos.in_check());
assert(pos.piece_on(kfrom) == make_piece(us, KING));
assert(pos.piece_on(rfrom) == make_piece(us, ROOK));
// Unimpeded rule: All the squares between the king's initial and final squares
// (including the final square), and all the squares between the rook's initial
// and final squares (including the final square), must be vacant except for
// the king and castling rook.
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++)
if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s))
||(pos.attackers_to(s) & pos.pieces(them)))
return mlist;
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++)
if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
return mlist;
// 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 (pos.is_chess960())
{
Bitboard occ = pos.occupied_squares();
clear_bit(&occ, rfrom);
if (pos.attackers_to(kto, occ) & pos.pieces(them))
return mlist;
}
(*mlist++).move = make_castle(kfrom, rfrom);
return mlist;
}
} // namespace

View File

@@ -25,7 +25,6 @@
enum MoveType { enum MoveType {
MV_CAPTURE, MV_CAPTURE,
MV_NON_CAPTURE, MV_NON_CAPTURE,
MV_CHECK,
MV_NON_CAPTURE_CHECK, MV_NON_CAPTURE_CHECK,
MV_EVASION, MV_EVASION,
MV_NON_EVASION, MV_NON_EVASION,

View File

@@ -255,7 +255,7 @@ void MovePicker::score_captures() {
{ {
m = cur->move; m = cur->move;
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))] cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
- type_of(pos.piece_on(from_sq(m))); - type_of(pos.piece_moved(m));
if (is_promotion(m)) if (is_promotion(m))
cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))]; cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))];
@@ -294,9 +294,9 @@ void MovePicker::score_evasions() {
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
else if (pos.is_capture(m)) else if (pos.is_capture(m))
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))] cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
- type_of(pos.piece_on(from_sq(m))) + History::MaxValue; - type_of(pos.piece_moved(m)) + History::MaxValue;
else else
cur->score = H.value(pos.piece_on(from_sq(m)), to_sq(m)); cur->score = H.value(pos.piece_moved(m), to_sq(m));
} }
} }

View File

@@ -78,8 +78,8 @@ namespace {
CheckInfo::CheckInfo(const Position& pos) { CheckInfo::CheckInfo(const Position& pos) {
Color them = flip(pos.side_to_move()); Color them = ~pos.side_to_move();
Square ksq = pos.king_square(them); ksq = pos.king_square(them);
pinned = pos.pinned_pieces(); pinned = pos.pinned_pieces();
dcCandidates = pos.discovered_check_candidates(); dcCandidates = pos.discovered_check_candidates();
@@ -165,11 +165,11 @@ void Position::from_fen(const string& fenStr, bool isChess960) {
// 1. Piece placement // 1. Piece placement
while ((fen >> token) && !isspace(token)) while ((fen >> token) && !isspace(token))
{ {
if (token == '/') if (isdigit(token))
sq -= Square(16); // Jump back of 2 rows sq += Square(token - '0'); // Advance the given number of files
else if (isdigit(token)) else if (token == '/')
sq += Square(token - '0'); // Skip the given number of files sq = make_square(FILE_A, rank_of(sq) - Rank(2));
else if ((p = PieceToChar.find(token)) != string::npos) else if ((p = PieceToChar.find(token)) != string::npos)
{ {
@@ -192,15 +192,14 @@ void Position::from_fen(const string& fenStr, bool isChess960) {
{ {
Square rsq; Square rsq;
Color c = islower(token) ? BLACK : WHITE; Color c = islower(token) ? BLACK : WHITE;
Piece rook = make_piece(c, ROOK);
token = char(toupper(token)); token = char(toupper(token));
if (token == 'K') if (token == 'K')
for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; rsq--) {} for (rsq = relative_square(c, SQ_H1); type_of(piece_on(rsq)) != ROOK; rsq--) {}
else if (token == 'Q') else if (token == 'Q')
for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; rsq++) {} for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; rsq++) {}
else if (token >= 'A' && token <= 'H') else if (token >= 'A' && token <= 'H')
rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
@@ -208,7 +207,7 @@ void Position::from_fen(const string& fenStr, bool isChess960) {
else else
continue; continue;
set_castle_right(king_square(c), rsq); set_castle_right(c, rsq);
} }
// 4. En passant square. Ignore if no pawn capture is possible // 4. En passant square. Ignore if no pawn capture is possible
@@ -234,7 +233,7 @@ void Position::from_fen(const string& fenStr, bool isChess960) {
st->value = compute_value(); st->value = compute_value();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE); st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK); st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(flip(sideToMove)); st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
chess960 = isChess960; chess960 = isChess960;
assert(pos_is_ok()); assert(pos_is_ok());
@@ -242,14 +241,14 @@ void Position::from_fen(const string& fenStr, bool isChess960) {
/// Position::set_castle_right() is an helper function used to set castling /// Position::set_castle_right() is an helper function used to set castling
/// rights given the corresponding king and rook starting squares. /// rights given the corresponding color and the rook starting square.
void Position::set_castle_right(Square ksq, Square rsq) { void Position::set_castle_right(Color c, Square rsq) {
int f = (rsq < ksq ? WHITE_OOO : WHITE_OO) << color_of(piece_on(ksq)); int f = (rsq < king_square(c) ? WHITE_OOO : WHITE_OO) << c;
st->castleRights |= f; st->castleRights |= f;
castleRightsMask[ksq] ^= f; castleRightsMask[king_square(c)] ^= f;
castleRightsMask[rsq] ^= f; castleRightsMask[rsq] ^= f;
castleRookSquare[f] = rsq; castleRookSquare[f] = rsq;
} }
@@ -357,12 +356,12 @@ Bitboard Position::hidden_checkers() const {
// Pinned pieces protect our king, dicovery checks attack the enemy king // Pinned pieces protect our king, dicovery checks attack the enemy king
Bitboard b, result = 0; Bitboard b, result = 0;
Bitboard pinners = pieces(FindPinned ? flip(sideToMove) : sideToMove); Bitboard pinners = pieces(FindPinned ? ~sideToMove : sideToMove);
Square ksq = king_square(FindPinned ? sideToMove : flip(sideToMove)); Square ksq = king_square(FindPinned ? sideToMove : ~sideToMove);
// Pinners are sliders, that give check when candidate pinned is removed // Pinners are sliders, that give check when candidate pinned is removed
pinners &= (pieces(ROOK, QUEEN) & RookPseudoAttacks[ksq]) pinners &= (pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq])
| (pieces(BISHOP, QUEEN) & BishopPseudoAttacks[ksq]); | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq]);
while (pinners) while (pinners)
{ {
@@ -450,7 +449,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
assert(is_ok(m)); assert(is_ok(m));
assert(pinned == pinned_pieces()); assert(pinned == pinned_pieces());
Color us = side_to_move(); Color us = sideToMove;
Square from = from_sq(m); Square from = from_sq(m);
assert(color_of(piece_on(from)) == us); assert(color_of(piece_on(from)) == us);
@@ -461,7 +460,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
// the move is made. // the move is made.
if (is_enpassant(m)) if (is_enpassant(m))
{ {
Color them = flip(us); Color them = ~us;
Square to = to_sq(m); Square to = to_sq(m);
Square capsq = to + pawn_push(them); Square capsq = to + pawn_push(them);
Square ksq = king_square(us); Square ksq = king_square(us);
@@ -484,7 +483,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
// square is attacked by the opponent. Castling moves are checked // square is attacked by the opponent. Castling moves are checked
// for legality during move generation. // for legality during move generation.
if (type_of(piece_on(from)) == KING) if (type_of(piece_on(from)) == KING)
return is_castle(m) || !(attackers_to(to_sq(m)) & pieces(flip(us))); return is_castle(m) || !(attackers_to(to_sq(m)) & pieces(~us));
// A non-king move is legal if and only if it is not pinned or it // 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. // is moving along the ray towards or away from the king.
@@ -515,7 +514,7 @@ bool Position::move_is_legal(const Move m) const {
bool Position::is_pseudo_legal(const Move m) const { bool Position::is_pseudo_legal(const Move m) const {
Color us = sideToMove; Color us = sideToMove;
Color them = flip(sideToMove); Color them = ~sideToMove;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece pc = piece_on(from); Piece pc = piece_on(from);
@@ -613,7 +612,7 @@ bool Position::is_pseudo_legal(const Move m) const {
{ {
Bitboard b = occupied_squares(); Bitboard b = occupied_squares();
clear_bit(&b, from); clear_bit(&b, from);
if (attackers_to(to_sq(m), b) & pieces(flip(us))) if (attackers_to(to_sq(m), b) & pieces(~us))
return false; return false;
} }
else else
@@ -641,7 +640,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
assert(is_ok(m)); assert(is_ok(m));
assert(ci.dcCandidates == discovered_check_candidates()); assert(ci.dcCandidates == discovered_check_candidates());
assert(color_of(piece_on(from_sq(m))) == side_to_move()); assert(color_of(piece_moved(m)) == sideToMove);
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
@@ -656,7 +655,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
{ {
// For pawn and king moves we need to verify also direction // For pawn and king moves we need to verify also direction
if ( (pt != PAWN && pt != KING) if ( (pt != PAWN && pt != KING)
|| !squares_aligned(from, to, king_square(flip(side_to_move())))) || !squares_aligned(from, to, king_square(~sideToMove)))
return true; return true;
} }
@@ -664,9 +663,9 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
if (!is_special(m)) if (!is_special(m))
return false; return false;
Color us = side_to_move(); Color us = sideToMove;
Bitboard b = occupied_squares(); Bitboard b = occupied_squares();
Square ksq = king_square(flip(us)); Square ksq = king_square(~us);
// Promotion with check ? // Promotion with check ?
if (is_promotion(m)) if (is_promotion(m))
@@ -764,8 +763,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
return; return;
} }
Color us = side_to_move(); Color us = sideToMove;
Color them = flip(us); Color them = ~us;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece piece = piece_on(from); Piece piece = piece_on(from);
@@ -958,7 +957,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
} }
// Finish // Finish
sideToMove = flip(sideToMove); sideToMove = ~sideToMove;
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue); st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
assert(pos_is_ok()); assert(pos_is_ok());
@@ -972,7 +971,7 @@ void Position::undo_move(Move m) {
assert(is_ok(m)); assert(is_ok(m));
sideToMove = flip(sideToMove); sideToMove = ~sideToMove;
if (is_castle(m)) if (is_castle(m))
{ {
@@ -980,8 +979,8 @@ void Position::undo_move(Move m) {
return; return;
} }
Color us = side_to_move(); Color us = sideToMove;
Color them = flip(us); Color them = ~us;
Square from = from_sq(m); Square from = from_sq(m);
Square to = to_sq(m); Square to = to_sq(m);
Piece piece = piece_on(to); Piece piece = piece_on(to);
@@ -1076,7 +1075,7 @@ void Position::do_castle_move(Move m) {
Square kto, kfrom, rfrom, rto, kAfter, rAfter; Square kto, kfrom, rfrom, rto, kAfter, rAfter;
Color us = side_to_move(); Color us = sideToMove;
Square kBefore = from_sq(m); Square kBefore = from_sq(m);
Square rBefore = to_sq(m); Square rBefore = to_sq(m);
@@ -1160,10 +1159,10 @@ void Position::do_castle_move(Move m) {
st->rule50 = 0; st->rule50 = 0;
// Update checkers BB // Update checkers BB
st->checkersBB = attackers_to(king_square(flip(us))) & pieces(us); st->checkersBB = attackers_to(king_square(~us)) & pieces(us);
// Finish // Finish
sideToMove = flip(sideToMove); sideToMove = ~sideToMove;
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue); st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
} }
else else
@@ -1194,7 +1193,7 @@ void Position::do_null_move(StateInfo& backupSt) {
dst->rule50 = src->rule50; dst->rule50 = src->rule50;
dst->pliesFromNull = src->pliesFromNull; dst->pliesFromNull = src->pliesFromNull;
sideToMove = flip(sideToMove); sideToMove = ~sideToMove;
if (Do) if (Do)
{ {
@@ -1264,9 +1263,9 @@ int Position::see(Move m) const {
// Handle en passant moves // Handle en passant moves
if (is_enpassant(m)) if (is_enpassant(m))
{ {
Square capQq = to - pawn_push(side_to_move()); Square capQq = to - pawn_push(sideToMove);
assert(capturedType == NO_PIECE_TYPE); assert(!capturedType);
assert(type_of(piece_on(capQq)) == PAWN); assert(type_of(piece_on(capQq)) == PAWN);
// Remove the captured pawn // Remove the captured pawn
@@ -1280,7 +1279,7 @@ int Position::see(Move m) const {
attackers = attackers_to(to, occ); attackers = attackers_to(to, occ);
// If the opponent has no attackers we are finished // If the opponent has no attackers we are finished
stm = flip(color_of(piece_on(from))); stm = ~color_of(piece_on(from));
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
if (!stmAttackers) if (!stmAttackers)
return PieceValueMidgame[capturedType]; return PieceValueMidgame[capturedType];
@@ -1318,7 +1317,7 @@ int Position::see(Move m) const {
// Remember the value of the capturing piece, and change the side to // Remember the value of the capturing piece, and change the side to
// move before beginning the next iteration. // move before beginning the next iteration.
capturedType = pt; capturedType = pt;
stm = flip(stm); stm = ~stm;
stmAttackers = attackers & pieces(stm); stmAttackers = attackers & pieces(stm);
// Stop before processing a king capture // Stop before processing a king capture
@@ -1402,7 +1401,7 @@ Key Position::compute_key() const {
if (ep_square() != SQ_NONE) if (ep_square() != SQ_NONE)
result ^= zobEp[ep_square()]; result ^= zobEp[ep_square()];
if (side_to_move() == BLACK) if (sideToMove == BLACK)
result ^= zobSideToMove; result ^= zobSideToMove;
return result; return result;
@@ -1466,7 +1465,7 @@ Score Position::compute_value() const {
result += pst(make_piece(c, pt), pop_1st_bit(&b)); result += pst(make_piece(c, pt), pop_1st_bit(&b));
} }
result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2); result += (sideToMove == WHITE ? TempoValue / 2 : -TempoValue / 2);
return result; return result;
} }
@@ -1499,7 +1498,7 @@ bool Position::is_draw() const {
return true; return true;
// Draw by the 50 moves rule? // Draw by the 50 moves rule?
if (st->rule50 > 99 && !is_mate()) if (st->rule50 > 99 && (!in_check() || MoveList<MV_LEGAL>(*this).size()))
return true; return true;
// Draw by repetition? // Draw by repetition?
@@ -1531,15 +1530,6 @@ template bool Position::is_draw<false>() const;
template bool Position::is_draw<true>() const; template bool Position::is_draw<true>() const;
/// Position::is_mate() returns true or false depending on whether the
/// side to move is checkmated.
bool Position::is_mate() const {
return in_check() && !MoveList<MV_LEGAL>(*this).size();
}
/// Position::init() is a static member function which initializes at startup /// Position::init() is a static member function which initializes at startup
/// the various arrays used to compute hash keys and the piece square tables. /// the various arrays used to compute hash keys and the piece square tables.
/// The latter is a two-step operation: First, the white halves of the tables /// The latter is a two-step operation: First, the white halves of the tables
@@ -1571,7 +1561,7 @@ void Position::init() {
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
pieceSquareTable[p][s] = ps + PSQT[p][s]; pieceSquareTable[p][s] = ps + PSQT[p][s];
pieceSquareTable[p+8][flip(s)] = -pieceSquareTable[p][s]; pieceSquareTable[p+8][~s] = -pieceSquareTable[p][s];
} }
} }
} }
@@ -1591,27 +1581,27 @@ void Position::flip_me() {
// Board // Board
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
if (!pos.square_is_empty(s)) if (!pos.square_is_empty(s))
put_piece(Piece(pos.piece_on(s) ^ 8), flip(s)); put_piece(Piece(pos.piece_on(s) ^ 8), ~s);
// Side to move // Side to move
sideToMove = flip(pos.side_to_move()); sideToMove = ~pos.side_to_move();
// Castling rights // Castling rights
if (pos.can_castle(WHITE_OO)) if (pos.can_castle(WHITE_OO))
set_castle_right(king_square(BLACK), flip(pos.castle_rook_square(WHITE_OO))); set_castle_right(BLACK, ~pos.castle_rook_square(WHITE_OO));
if (pos.can_castle(WHITE_OOO)) if (pos.can_castle(WHITE_OOO))
set_castle_right(king_square(BLACK), flip(pos.castle_rook_square(WHITE_OOO))); set_castle_right(BLACK, ~pos.castle_rook_square(WHITE_OOO));
if (pos.can_castle(BLACK_OO)) if (pos.can_castle(BLACK_OO))
set_castle_right(king_square(WHITE), flip(pos.castle_rook_square(BLACK_OO))); set_castle_right(WHITE, ~pos.castle_rook_square(BLACK_OO));
if (pos.can_castle(BLACK_OOO)) if (pos.can_castle(BLACK_OOO))
set_castle_right(king_square(WHITE), flip(pos.castle_rook_square(BLACK_OOO))); set_castle_right(WHITE, ~pos.castle_rook_square(BLACK_OOO));
// En passant square // En passant square
if (pos.st->epSquare != SQ_NONE) if (pos.st->epSquare != SQ_NONE)
st->epSquare = flip(pos.st->epSquare); st->epSquare = ~pos.st->epSquare;
// Checkers // Checkers
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(flip(sideToMove)); st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
// Hash keys // Hash keys
st->key = compute_key(); st->key = compute_key();
@@ -1653,7 +1643,7 @@ bool Position::pos_is_ok(int* failedStep) const {
if (failedStep) *failedStep = 1; if (failedStep) *failedStep = 1;
// Side to move OK? // Side to move OK?
if (side_to_move() != WHITE && side_to_move() != BLACK) if (sideToMove != WHITE && sideToMove != BLACK)
return false; return false;
// Are the king squares in the position correct? // Are the king squares in the position correct?
@@ -1682,8 +1672,8 @@ bool Position::pos_is_ok(int* failedStep) const {
if (failedStep) (*failedStep)++; if (failedStep) (*failedStep)++;
if (debugKingCapture) if (debugKingCapture)
{ {
Color us = side_to_move(); Color us = sideToMove;
Color them = flip(us); Color them = ~us;
Square ksq = king_square(them); Square ksq = king_square(them);
if (attackers_to(ksq) & pieces(us)) if (attackers_to(ksq) & pieces(us))
return false; return false;
@@ -1720,7 +1710,7 @@ bool Position::pos_is_ok(int* failedStep) const {
{ {
// The en passant square must be on rank 6, from the point of view of the // The en passant square must be on rank 6, from the point of view of the
// side to move. // side to move.
if (relative_rank(side_to_move(), ep_square()) != RANK_6) if (relative_rank(sideToMove, ep_square()) != RANK_6)
return false; return false;
} }

View File

@@ -37,6 +37,7 @@ struct CheckInfo {
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard pinned; Bitboard pinned;
Bitboard checkSq[8]; Bitboard checkSq[8];
Square ksq;
}; };
@@ -100,6 +101,7 @@ public:
// The piece on a given square // The piece on a given square
Piece piece_on(Square s) const; Piece piece_on(Square s) const;
Piece piece_moved(Move m) const;
bool square_is_empty(Square s) const; bool square_is_empty(Square s) const;
// Side to move // Side to move
@@ -183,14 +185,9 @@ public:
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
Score pst_delta(Piece piece, Square from, Square to) const; Score pst_delta(Piece piece, Square from, Square to) const;
// Game termination checks
bool is_mate() const;
template<bool SkipRepetition> bool is_draw() const;
// Plies from start position to the beginning of search
int startpos_ply_counter() const;
// Other properties of the position // Other properties of the position
template<bool SkipRepetition> bool is_draw() const;
int startpos_ply_counter() const;
bool opposite_colored_bishops() const; bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const; bool has_pawn_on_7th(Color c) const;
bool is_chess960() const; bool is_chess960() const;
@@ -213,7 +210,7 @@ private:
// Initialization helper functions (used while setting up a position) // Initialization helper functions (used while setting up a position)
void clear(); void clear();
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void set_castle_right(Square ksq, Square rsq); void set_castle_right(Color c, Square rsq);
bool move_is_legal(const Move m) const; bool move_is_legal(const Move m) const;
// Helper template functions // Helper template functions
@@ -277,6 +274,10 @@ inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
inline Piece Position::piece_moved(Move m) const {
return board[from_sq(m)];
}
inline bool Position::square_is_empty(Square s) const { inline bool Position::square_is_empty(Square s) const {
return board[s] == NO_PIECE; return board[s] == NO_PIECE;
} }
@@ -391,7 +392,7 @@ inline Bitboard Position::pinned_pieces() const {
} }
inline bool Position::pawn_is_passed(Color c, Square s) const { inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, flip(c)) & passed_pawn_mask(c, s)); return !(pieces(PAWN, ~c) & passed_pawn_mask(c, s));
} }
inline Key Position::key() const { inline Key Position::key() const {

View File

@@ -24,7 +24,6 @@
#include <iomanip> #include <iomanip>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <vector>
#include "book.h" #include "book.h"
#include "evaluate.h" #include "evaluate.h"
@@ -42,7 +41,7 @@ namespace Search {
volatile SignalsType Signals; volatile SignalsType Signals;
LimitsType Limits; LimitsType Limits;
std::vector<Move> SearchMoves; std::vector<RootMove> RootMoves;
Position RootPosition; Position RootPosition;
} }
@@ -59,33 +58,6 @@ namespace {
// Different node types, used as template parameter // Different node types, used as template parameter
enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV }; enum NodeType { Root, PV, NonPV, SplitPointRoot, SplitPointPV, SplitPointNonPV };
// RootMove struct is used for moves at the root of the tree. For each root
// move we store a score, a node count, and a PV (really a refutation in the
// case of moves which fail low). Score is normally set at -VALUE_INFINITE for
// all non-pv moves.
struct RootMove {
RootMove(){}
RootMove(Move m) {
score = prevScore = -VALUE_INFINITE;
pv.push_back(m);
pv.push_back(MOVE_NONE);
}
bool operator<(const RootMove& m) const { return score < m.score; }
bool operator==(const Move& m) const { return pv[0] == m; }
void extract_pv_from_tt(Position& pos);
void insert_pv_in_tt(Position& pos);
Value score;
Value prevScore;
std::vector<Move> pv;
};
/// Constants
// Lookup table to check if a Piece is a slider and its access function // Lookup table to check if a Piece is a slider and its access function
const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 }; const bool Slidings[18] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1 };
inline bool piece_is_slider(Piece p) { return Slidings[p]; } inline bool piece_is_slider(Piece p) { return Slidings[p]; }
@@ -135,17 +107,14 @@ namespace {
return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)]; return (Depth) Reductions[PvNode][std::min(int(d) / ONE_PLY, 63)][std::min(mn, 63)];
} }
// Easy move margin. An easy move candidate must be at least this much // Easy move margin. An easy move candidate must be at least this much better
// better than the second best move. // than the second best move.
const Value EasyMoveMargin = Value(0x150); const Value EasyMoveMargin = Value(0x150);
// This is the minimum interval in msec between two check_time() calls // This is the minimum interval in msec between two check_time() calls
const int TimerResolution = 5; const int TimerResolution = 5;
/// Namespace variables
std::vector<RootMove> RootMoves;
size_t MultiPV, UCIMultiPV, PVIdx; size_t MultiPV, UCIMultiPV, PVIdx;
TimeManager TimeMgr; TimeManager TimeMgr;
int BestMoveChanges; int BestMoveChanges;
@@ -154,8 +123,6 @@ namespace {
History H; History H;
/// Local functions
template <NodeType NT> template <NodeType NT>
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth); Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth);
@@ -200,7 +167,7 @@ namespace {
FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) { FORCE_INLINE bool is_dangerous(const Position& pos, Move m, bool captureOrPromotion) {
// Test for a pawn pushed to 7th or a passed pawn move // Test for a pawn pushed to 7th or a passed pawn move
if (type_of(pos.piece_on(from_sq(m))) == PAWN) if (type_of(pos.piece_moved(m)) == PAWN)
{ {
Color c = pos.side_to_move(); Color c = pos.side_to_move();
if ( relative_rank(c, to_sq(m)) == RANK_7 if ( relative_rank(c, to_sq(m)) == RANK_7
@@ -282,29 +249,29 @@ void Search::think() {
static Book book; // Defined static to initialize the PRNG only once static Book book; // Defined static to initialize the PRNG only once
Move bm;
Position& pos = RootPosition; Position& pos = RootPosition;
Chess960 = pos.is_chess960(); Chess960 = pos.is_chess960();
elapsed_time(true); elapsed_time(true);
TimeMgr.init(Limits, pos.startpos_ply_counter()); TimeMgr.init(Limits, pos.startpos_ply_counter());
TT.new_search(); TT.new_search();
H.clear(); H.clear();
RootMoves.clear();
// Populate RootMoves with all the legal moves (default) or, if a SearchMoves if (RootMoves.empty())
// is given, with the subset of legal moves to search.
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (SearchMoves.empty() || count(SearchMoves.begin(), SearchMoves.end(), ml.move()))
RootMoves.push_back(RootMove(ml.move()));
if (Options["OwnBook"])
{ {
Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]); cout << "info depth 0 score "
<< score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl;
if (bookMove && count(RootMoves.begin(), RootMoves.end(), bookMove)) RootMoves.push_back(MOVE_NONE);
{
std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove));
goto finalize; goto finalize;
} }
if ( Options["OwnBook"]
&& (bm = book.probe(pos, Options["Book File"], Options["Best Book Move"])) != MOVE_NONE
&& count(RootMoves.begin(), RootMoves.end(), bm))
{
std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bm));
goto finalize;
} }
// Read UCI options: GUI could change UCI parameters during the game // Read UCI options: GUI could change UCI parameters during the game
@@ -375,9 +342,9 @@ void Search::think() {
finalize: finalize:
// When we reach max depth we arrive here even without a StopRequest, but if // When we reach max depth we arrive here even without Signals.stop is raised,
// we are pondering or in infinite search, we shouldn't print the best move // but if we are pondering or in infinite search, we shouldn't print the best
// before we are told to do so. // move before we are told to do so.
if (!Signals.stop && (Limits.ponder || Limits.infinite)) if (!Signals.stop && (Limits.ponder || Limits.infinite))
Threads.wait_for_stop_or_ponderhit(); Threads.wait_for_stop_or_ponderhit();
@@ -406,16 +373,6 @@ namespace {
bestValue = delta = -VALUE_INFINITE; bestValue = delta = -VALUE_INFINITE;
ss->currentMove = MOVE_NULL; // Hack to skip update gains ss->currentMove = MOVE_NULL; // Hack to skip update gains
// Handle the special case of a mated/stalemate position
if (RootMoves.empty())
{
cout << "info depth 0 score "
<< score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl;
RootMoves.push_back(MOVE_NONE);
return;
}
// Iterative deepening loop until requested to stop or target depth reached // Iterative deepening loop until requested to stop or target depth reached
while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth)) while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth))
{ {
@@ -531,15 +488,15 @@ namespace {
stop = true; stop = true;
// Stop search early if one move seems to be much better than others // Stop search early if one move seems to be much better than others
if ( depth >= 10 if ( depth >= 12
&& !stop && !stop
&& ( bestMoveNeverChanged && ( (bestMoveNeverChanged && pos.captured_piece_type())
|| elapsed_time() > (TimeMgr.available_time() * 40) / 100)) || elapsed_time() > (TimeMgr.available_time() * 40) / 100))
{ {
Value rBeta = bestValue - EasyMoveMargin; Value rBeta = bestValue - EasyMoveMargin;
(ss+1)->excludedMove = RootMoves[0].pv[0]; (ss+1)->excludedMove = RootMoves[0].pv[0];
(ss+1)->skipNullMove = true; (ss+1)->skipNullMove = true;
Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth * ONE_PLY) / 2); Value v = search<NonPV>(pos, ss+1, rBeta - 1, rBeta, (depth - 3) * ONE_PLY);
(ss+1)->skipNullMove = false; (ss+1)->skipNullMove = false;
(ss+1)->excludedMove = MOVE_NONE; (ss+1)->excludedMove = MOVE_NONE;
@@ -701,7 +658,7 @@ namespace {
if ( (move = (ss-1)->currentMove) != MOVE_NULL if ( (move = (ss-1)->currentMove) != MOVE_NULL
&& (ss-1)->eval != VALUE_NONE && (ss-1)->eval != VALUE_NONE
&& ss->eval != VALUE_NONE && ss->eval != VALUE_NONE
&& pos.captured_piece_type() == NO_PIECE_TYPE && !pos.captured_piece_type()
&& !is_special(move)) && !is_special(move))
{ {
Square to = to_sq(move); Square to = to_sq(move);
@@ -970,7 +927,7 @@ split_point_start: // At split points actual search starts from here
// but fixing this made program slightly weaker. // but fixing this made program slightly weaker.
Depth predictedDepth = newDepth - reduction<PvNode>(depth, moveCount); Depth predictedDepth = newDepth - reduction<PvNode>(depth, moveCount);
futilityValue = futilityBase + futility_margin(predictedDepth, moveCount) futilityValue = futilityBase + futility_margin(predictedDepth, moveCount)
+ H.gain(pos.piece_on(from_sq(move)), to_sq(move)); + H.gain(pos.piece_moved(move), to_sq(move));
if (futilityValue < beta) if (futilityValue < beta)
{ {
@@ -1154,13 +1111,13 @@ split_point_start: // At split points actual search starts from here
// Increase history value of the cut-off move // Increase history value of the cut-off move
Value bonus = Value(int(depth) * int(depth)); Value bonus = Value(int(depth) * int(depth));
H.add(pos.piece_on(from_sq(move)), to_sq(move), bonus); H.add(pos.piece_moved(move), to_sq(move), bonus);
// Decrease history of all the other played non-capture moves // Decrease history of all the other played non-capture moves
for (int i = 0; i < playedMoveCount - 1; i++) for (int i = 0; i < playedMoveCount - 1; i++)
{ {
Move m = movesSearched[i]; Move m = movesSearched[i];
H.add(pos.piece_on(from_sq(m)), to_sq(m), -bonus); H.add(pos.piece_moved(m), to_sq(m), -bonus);
} }
} }
} }
@@ -1393,7 +1350,7 @@ split_point_start: // At split points actual search starts from here
from = from_sq(move); from = from_sq(move);
to = to_sq(move); to = to_sq(move);
them = flip(pos.side_to_move()); them = ~pos.side_to_move();
ksq = pos.king_square(them); ksq = pos.king_square(them);
kingAtt = pos.attacks_from<KING>(ksq); kingAtt = pos.attacks_from<KING>(ksq);
pc = pos.piece_on(from); pc = pos.piece_on(from);
@@ -1801,13 +1758,15 @@ split_point_start: // At split points actual search starts from here
return best; return best;
} }
} // namespace
// extract_pv_from_tt() builds a PV by adding moves from the transposition table.
// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
// allow to always have a ponder move even when we fail high at root and also a
// long PV to print that is important for position analysis.
void RootMove::extract_pv_from_tt(Position& pos) { /// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table.
/// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes so
/// to allow to always have a ponder move even when we fail high at root, and
/// a long PV to print that is important for position analysis.
void RootMove::extract_pv_from_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state; StateInfo state[MAX_PLY_PLUS_2], *st = state;
TTEntry* tte; TTEntry* tte;
@@ -1834,14 +1793,14 @@ split_point_start: // At split points actual search starts from here
pv.push_back(MOVE_NONE); pv.push_back(MOVE_NONE);
do pos.undo_move(pv[--ply]); while (ply); do pos.undo_move(pv[--ply]); while (ply);
} }
// insert_pv_in_tt() is called at the end of a search iteration, and inserts /// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and
// the PV back into the TT. This makes sure the old PV moves are searched /// inserts the PV back into the TT. This makes sure the old PV moves are searched
// first, even if the old TT entries have been overwritten. /// first, even if the old TT entries have been overwritten.
void RootMove::insert_pv_in_tt(Position& pos) { void RootMove::insert_pv_in_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state; StateInfo state[MAX_PLY_PLUS_2], *st = state;
TTEntry* tte; TTEntry* tte;
@@ -1866,9 +1825,7 @@ split_point_start: // At split points actual search starts from here
} while (pv[++ply] != MOVE_NONE); } while (pv[++ply] != MOVE_NONE);
do pos.undo_move(pv[--ply]); while (ply); do pos.undo_move(pv[--ply]); while (ply);
} }
} // namespace
/// Thread::idle_loop() is where the thread is parked when it has no work to do. /// Thread::idle_loop() is where the thread is parked when it has no work to do.

View File

@@ -48,6 +48,29 @@ struct Stack {
}; };
/// RootMove struct is used for moves at the root of the tree. For each root
/// move we store a score, a node count, and a PV (really a refutation in the
/// case of moves which fail low). Score is normally set at -VALUE_INFINITE for
/// all non-pv moves.
struct RootMove {
RootMove(){} // Needed by sort()
RootMove(Move m) : score(-VALUE_INFINITE), prevScore(-VALUE_INFINITE) {
pv.push_back(m); pv.push_back(MOVE_NONE);
}
bool operator<(const RootMove& m) const { return score < m.score; }
bool operator==(const Move& m) const { return pv[0] == m; }
void extract_pv_from_tt(Position& pos);
void insert_pv_in_tt(Position& pos);
Value score;
Value prevScore;
std::vector<Move> pv;
};
/// The LimitsType struct stores information sent by GUI about available time /// The LimitsType struct stores information sent by GUI about available time
/// to search the current move, maximum depth/time, if we are in analysis mode /// to search the current move, maximum depth/time, if we are in analysis mode
/// or if we have to ponder while is our opponent's side to move. /// or if we have to ponder while is our opponent's side to move.
@@ -70,13 +93,13 @@ struct SignalsType {
extern volatile SignalsType Signals; extern volatile SignalsType Signals;
extern LimitsType Limits; extern LimitsType Limits;
extern std::vector<Move> SearchMoves; extern std::vector<RootMove> RootMoves;
extern Position RootPosition; extern Position RootPosition;
extern void init(); extern void init();
extern int64_t perft(Position& pos, Depth depth); extern int64_t perft(Position& pos, Depth depth);
extern void think(); extern void think();
} // namespace } // namespace Search
#endif // !defined(SEARCH_H_INCLUDED) #endif // !defined(SEARCH_H_INCLUDED)

View File

@@ -19,6 +19,7 @@
#include <iostream> #include <iostream>
#include "movegen.h"
#include "search.h" #include "search.h"
#include "thread.h" #include "thread.h"
#include "ucioption.h" #include "ucioption.h"
@@ -420,7 +421,7 @@ void Thread::main_loop() {
if (do_terminate) if (do_terminate)
return; return;
think(); // This is the search entry point Search::think();
} }
} }
@@ -431,7 +432,7 @@ void Thread::main_loop() {
// the search to finish. // the search to finish.
void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits, void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode) { const std::set<Move>& searchMoves, bool async) {
Thread& main = threads[0]; Thread& main = threads[0];
lock_grab(&main.sleepLock); lock_grab(&main.sleepLock);
@@ -443,15 +444,22 @@ void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limit
// Copy input arguments to initialize the search // Copy input arguments to initialize the search
RootPosition.copy(pos, 0); RootPosition.copy(pos, 0);
Limits = limits; Limits = limits;
SearchMoves = searchMoves; RootMoves.clear();
// Populate RootMoves with all the legal moves (default) or, if a searchMoves
// set is given, with the subset of legal moves to search.
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (searchMoves.empty() || searchMoves.count(ml.move()))
RootMoves.push_back(RootMove(ml.move()));
// Reset signals before to start the new search // Reset signals before to start the new search
memset((void*)&Signals, 0, sizeof(Signals)); Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
main.do_sleep = false; main.do_sleep = false;
cond_signal(&main.sleepCond); // Wake up main thread and start searching cond_signal(&main.sleepCond); // Wake up main thread and start searching
if (!asyncMode) if (!async)
while (!main.do_sleep) while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock); cond_wait(&sleepCond, &main.sleepLock);

View File

@@ -21,6 +21,7 @@
#define THREAD_H_INCLUDED #define THREAD_H_INCLUDED
#include <cstring> #include <cstring>
#include <set>
#include "lock.h" #include "lock.h"
#include "material.h" #include "material.h"
@@ -120,7 +121,7 @@ public:
void wait_for_stop_or_ponderhit(); void wait_for_stop_or_ponderhit();
void stop_thinking(); void stop_thinking();
void start_thinking(const Position& pos, const Search::LimitsType& limits, void start_thinking(const Position& pos, const Search::LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode); const std::set<Move>& = std::set<Move>(), bool async = false);
template <bool Fake> template <bool Fake>
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue, Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue,

View File

@@ -34,11 +34,6 @@
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works /// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode. For compiling requires hardware with /// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support. /// | popcnt support.
///
/// -DOLD_LOCKS | Under Windows are used the fast Slim Reader/Writer (SRW)
/// | Locks and Condition Variables: these are not supported by
/// | Windows XP and older, to compile for those platforms you
/// | should enable OLD_LOCKS.
#include <climits> #include <climits>
#include <cstdlib> #include <cstdlib>
@@ -339,6 +334,14 @@ extern const Value PieceValueMidgame[17];
extern const Value PieceValueEndgame[17]; extern const Value PieceValueEndgame[17];
extern int SquareDistance[64][64]; extern int SquareDistance[64][64];
inline Color operator~(Color c) {
return Color(c ^ 1);
}
inline Square operator~(Square s) {
return Square(s ^ 56);
}
inline Value mate_in(int ply) { inline Value mate_in(int ply) {
return VALUE_MATE - ply; return VALUE_MATE - ply;
} }
@@ -359,10 +362,6 @@ inline Color color_of(Piece p) {
return Color(p >> 3); return Color(p >> 3);
} }
inline Color flip(Color c) {
return Color(c ^ 1);
}
inline Square make_square(File f, Rank r) { inline Square make_square(File f, Rank r) {
return Square((r << 3) | f); return Square((r << 3) | f);
} }
@@ -379,10 +378,6 @@ inline Rank rank_of(Square s) {
return Rank(s >> 3); return Rank(s >> 3);
} }
inline Square flip(Square s) {
return Square(s ^ 56);
}
inline Square mirror(Square s) { inline Square mirror(Square s) {
return Square(s ^ 7); return Square(s ^ 7);
} }

View File

@@ -201,7 +201,7 @@ namespace {
string token; string token;
Search::LimitsType limits; Search::LimitsType limits;
std::vector<Move> searchMoves; std::set<Move> searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 }; int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (is >> token) while (is >> token)
@@ -228,7 +228,7 @@ namespace {
is >> limits.maxTime; is >> limits.maxTime;
else if (token == "searchmoves") else if (token == "searchmoves")
while (is >> token) while (is >> token)
searchMoves.push_back(move_from_uci(pos, token)); searchMoves.insert(move_from_uci(pos, token));
} }
limits.time = time[pos.side_to_move()]; limits.time = time[pos.side_to_move()];