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"
using namespace std;
using namespace Search;
static const char* Defaults[] = {
"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[]) {
vector<string> fens;
LimitsType limits;
Search::LimitsType limits;
int time;
int64_t nodes = 0;
@@ -115,14 +114,14 @@ void benchmark(int argc, char* argv[]) {
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;
nodes += cnt;
}
else
{
Threads.start_thinking(pos, limits, vector<Move>(), false);
nodes += RootPosition.nodes_searched();
Threads.start_thinking(pos, limits);
nodes += Search::RootPosition.nodes_searched();
}
}

View File

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

View File

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

View File

@@ -205,10 +205,10 @@ Value Endgame<KPK>::operator()(const Position& pos) const {
}
else
{
wksq = flip(pos.king_square(BLACK));
bksq = flip(pos.king_square(WHITE));
wpsq = flip(pos.piece_list(BLACK, PAWN)[0]);
stm = flip(pos.side_to_move());
wksq = ~pos.king_square(BLACK);
bksq = ~pos.king_square(WHITE);
wpsq = ~pos.piece_list(BLACK, PAWN)[0];
stm = ~pos.side_to_move();
}
if (file_of(wpsq) >= FILE_E)
@@ -251,10 +251,10 @@ Value Endgame<KRKP>::operator()(const Position& pos) const {
if (strongerSide == BLACK)
{
wksq = flip(wksq);
wrsq = flip(wrsq);
bksq = flip(bksq);
bpsq = flip(bpsq);
wksq = ~wksq;
wrsq = ~wrsq;
bksq = ~bksq;
bpsq = ~bpsq;
}
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.
if (strongerSide == BLACK)
{
wksq = flip(wksq);
wrsq = flip(wrsq);
wpsq = flip(wpsq);
bksq = flip(bksq);
brsq = flip(brsq);
wksq = ~wksq;
wrsq = ~wrsq;
wpsq = ~wpsq;
bksq = ~bksq;
brsq = ~brsq;
}
if (file_of(wpsq) > FILE_D)
{
@@ -867,10 +867,10 @@ ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
if (strongerSide == BLACK)
{
wksq = flip(wksq);
bksq = flip(bksq);
wpsq = flip(wpsq);
stm = flip(stm);
wksq = ~wksq;
bksq = ~bksq;
wpsq = ~wpsq;
stm = ~stm;
}
if (file_of(wpsq) >= FILE_E)

View File

@@ -82,7 +82,7 @@ struct EndgameBase {
template<EndgameType E, typename T = typename eg_family<E>::type>
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; }
T operator()(const Position&) const;

View File

@@ -714,7 +714,7 @@ namespace {
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
// Consider only squares where the enemy rook gives check
b &= RookPseudoAttacks[ksq];
b &= PseudoAttacks[ROOK][ksq];
if (b)
{
@@ -887,7 +887,7 @@ namespace {
for (c = WHITE; c <= BLACK; c++)
{
// Skip if other side has non-pawn pieces
if (pos.non_pawn_material(flip(c)))
if (pos.non_pawn_material(~c))
continue;
b = ei.pi->passed_pawns(c);
@@ -900,7 +900,7 @@ namespace {
// Compute plies to queening and check direct advancement
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);
if (movesToGo >= oppMovesToGo && !pathDefended)
@@ -928,7 +928,7 @@ namespace {
return SCORE_ZERO;
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?
b = candidates = pos.pieces(PAWN, loserSide);

View File

@@ -45,26 +45,9 @@ typedef pthread_cond_t WaitCondition;
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// Default fast and race free locks and condition variables
#if !defined(OLD_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
// We use critical sections on Windows to support Windows XP and older versions,
// unfortunatly cond_wait() is racy between lock_release() and WaitForSingleObject()
// but apart from this they have the same speed performance of SRW locks.
typedef CRITICAL_SECTION Lock;
typedef HANDLE WaitCondition;
@@ -80,6 +63,4 @@ typedef HANDLE WaitCondition;
#endif
#endif
#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
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)]);
}
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)]);
}
@@ -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) , 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;
}

View File

@@ -55,7 +55,7 @@ using namespace std;
/// Version number. If Version is left empty, then Tag plus current
/// 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 = "";

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
// because we need to test for a mate after the move is done.
StateInfo st;
pos.do_move(m, st);
if (pos.in_check())
san += pos.is_mate() ? "#" : "+";
pos.undo_move(m);
if (pos.move_gives_check(m, CheckInfo(pos)))
{
StateInfo st;
pos.do_move(m, st);
san += MoveList<MV_LEGAL>(pos).size() ? "+" : "#";
pos.undo_move(m);
}
return san;
}

View File

@@ -17,50 +17,238 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <algorithm>
#include <cassert>
#include "bitcount.h"
#include "movegen.h"
#include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'.
#define SERIALIZE_MOVES(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); }
/// Simple macro to wrap a very common while loop, no facny, no flexibility,
/// hardcoded names 'mlist' and 'from'.
#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_PAWNS(b, d) while (b) { Square to = pop_1st_bit(&b); \
(*mlist++).move = make_move(to + (d), to); }
namespace {
enum CastlingSide {
KING_SIDE,
QUEEN_SIDE
};
enum CastlingSide { KING_SIDE, QUEEN_SIDE };
template<CastlingSide>
MoveStack* generate_castle_moves(const Position&, MoveStack*, Color us);
template<CastlingSide Side, bool OnlyChecks>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
template<Color, MoveType>
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
const CastleRight CR[] = { Side ? WHITE_OOO : WHITE_OO,
Side ? BLACK_OOO : BLACK_OO };
template<PieceType Pt>
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
if (!pos.can_castle(CR[us]))
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)
b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))];
// 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(rfrom, rto), e = std::max(rfrom, rto); s <= e; s++)
if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
return mlist;
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;
}
(*mlist++).move = make_castle(kfrom, rfrom);
if (OnlyChecks && !pos.move_gives_check((mlist - 1)->move, CheckInfo(pos)))
mlist--;
SERIALIZE_MOVES(b);
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>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist,
Color us, const CheckInfo& ci) {
assert(Pt != KING && Pt != PAWN);
Bitboard checkSqs, b;
@@ -70,41 +258,44 @@ namespace {
if ((from = *pl++) == SQ_NONE)
return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
checkSqs = ci.checkSq[Pt] & pos.empty_squares();
do
{
if ( (Pt == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|| (Pt == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|| (Pt == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
&& !(PseudoAttacks[Pt][from] & checkSqs))
continue;
if (dc && bit_is_set(dc, from))
if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from))
continue;
b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b);
SERIALIZE(b);
} while ((from = *pl++) != SQ_NONE);
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)
: generate_pawn_moves<BLACK, MV_CHECK>(p, m, dc, ksq));
template<>
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>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
assert(Pt == PAWN);
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
: generate_pawn_moves<BLACK, 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);
}
template<PieceType Pt>
FORCE_INLINE MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
@@ -117,24 +308,23 @@ namespace {
do {
from = *pl;
b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b);
SERIALIZE(b);
} while (*++pl != SQ_NONE);
}
return mlist;
}
template<>
FORCE_INLINE MoveStack* generate_piece_moves<KING>(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
Bitboard b;
Square from = pos.king_square(us);
b = pos.attacks_from<KING>(from) & target;
SERIALIZE_MOVES(b);
Bitboard b = pos.attacks_from<KING>(from) & target;
SERIALIZE(b);
return mlist;
}
}
} // namespace
/// generate<MV_CAPTURE> generates all pseudo-legal captures and queen
@@ -156,13 +346,13 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) {
Bitboard target;
if (Type == MV_CAPTURE)
target = pos.pieces(flip(us));
target = pos.pieces(~us);
else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares();
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<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 (pos.can_castle(us == WHITE ? WHITE_OO : BLACK_OO))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle(us == WHITE ? WHITE_OOO : BLACK_OOO))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
mlist = generate_castle_moves<KING_SIDE, false>(pos, mlist, us);
mlist = generate_castle_moves<QUEEN_SIDE, false>(pos, mlist, us);
}
return mlist;
@@ -196,36 +383,39 @@ MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist)
assert(!pos.in_check());
Bitboard b, dc;
Square from;
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));
// Discovered non-capture checks
b = dc = pos.discovered_check_candidates();
while (b)
while (dc)
{
from = pop_1st_bit(&b);
switch (type_of(pos.piece_on(from)))
{
case PAWN: /* Will be generated togheter with pawns direct checks */ break;
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break;
case BISHOP: mlist = generate_discovered_checks<BISHOP>(pos, mlist, from); break;
case ROOK: mlist = generate_discovered_checks<ROOK>(pos, mlist, from); break;
case KING: mlist = generate_discovered_checks<KING>(pos, mlist, from); break;
default: assert(false); break;
}
Square from = pop_1st_bit(&dc);
PieceType pt = type_of(pos.piece_on(from));
if (pt == PAWN)
continue; // Will be generated togheter with direct checks
Bitboard b = pos.attacks_from(Piece(pt), from) & pos.empty_squares();
if (pt == KING)
b &= ~PseudoAttacks[QUEEN][ci.ksq];
SERIALIZE(b);
}
// Direct non-capture checks
mlist = generate_direct_checks<PAWN>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<ROOK>(pos, mlist, us, dc, ksq);
return generate_direct_checks<QUEEN>(pos, mlist, us, dc, ksq);
mlist = generate_direct_checks<PAWN>(pos, mlist, us, ci);
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, ci);
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, ci);
mlist = generate_direct_checks<ROOK>(pos, mlist, us, ci);
mlist = generate_direct_checks<QUEEN>(pos, mlist, us, ci);
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;
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = 0;
Bitboard checkers = pos.checkers();
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers);
// Find squares attacked by slider checkers, we will remove
// them from the king evasions set so to early skip known
// illegal moves and avoid an useless legality check later.
// Find squares attacked by slider checkers, we will remove them from the king
// evasions so to skip known illegal moves avoiding useless legality check later.
b = checkers;
do
{
checkersCnt++;
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)))
{
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case BISHOP: sliderAttacks |= PseudoAttacks[BISHOP][checksq]; break;
case ROOK: sliderAttacks |= PseudoAttacks[ROOK][checksq]; break;
case QUEEN:
// If queen and king are far we can safely remove all the squares attacked
// in the other direction becuase are not reachable by the king anyway.
if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq)))
sliderAttacks |= QueenPseudoAttacks[checksq];
// If queen and king are far or not on a diagonal line we can safely
// remove all the squares attacked in the other direction becuase are
// not reachable by the king anyway.
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
// use real rook attacks to check if king is safe to move in the other direction.
// For example: king in B2, queen in A1 a knight in B1, and we can safely move to C1.
// Otherwise we need to use real rook attacks to check if king is safe
// to move in the other direction. For example: king in B2, queen in A1
// a knight in B1, and we can safely move to C1.
else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
sliderAttacks |= PseudoAttacks[BISHOP][checksq] | pos.attacks_from<ROOK>(checksq);
default:
break;
@@ -282,14 +471,13 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
// Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
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)
return mlist;
// Find squares where a blocking evasion or a capture of the
// checker piece is possible.
// Blocking evasions or captures of the checking piece
target = squares_between(checksq, ksq) | checkers;
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<>
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)
: generate<MV_NON_EVASION>(pos, mlist);
// Remove illegal moves from the list
while (cur != last)
if (!pos.pl_move_is_legal(cur->move, pinned))
cur->move = (--last)->move;
@@ -320,218 +506,3 @@ MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
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 {
MV_CAPTURE,
MV_NON_CAPTURE,
MV_CHECK,
MV_NON_CAPTURE_CHECK,
MV_EVASION,
MV_NON_EVASION,

View File

@@ -255,7 +255,7 @@ void MovePicker::score_captures() {
{
m = cur->move;
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))
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
else if (pos.is_capture(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
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) {
Color them = flip(pos.side_to_move());
Square ksq = pos.king_square(them);
Color them = ~pos.side_to_move();
ksq = pos.king_square(them);
pinned = pos.pinned_pieces();
dcCandidates = pos.discovered_check_candidates();
@@ -165,11 +165,11 @@ void Position::from_fen(const string& fenStr, bool isChess960) {
// 1. Piece placement
while ((fen >> token) && !isspace(token))
{
if (token == '/')
sq -= Square(16); // Jump back of 2 rows
if (isdigit(token))
sq += Square(token - '0'); // Advance the given number of files
else if (isdigit(token))
sq += Square(token - '0'); // Skip the given number of files
else if (token == '/')
sq = make_square(FILE_A, rank_of(sq) - Rank(2));
else if ((p = PieceToChar.find(token)) != string::npos)
{
@@ -192,15 +192,14 @@ void Position::from_fen(const string& fenStr, bool isChess960) {
{
Square rsq;
Color c = islower(token) ? BLACK : WHITE;
Piece rook = make_piece(c, ROOK);
token = char(toupper(token));
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')
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')
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
continue;
set_castle_right(king_square(c), rsq);
set_castle_right(c, rsq);
}
// 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->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
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;
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
/// 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;
castleRightsMask[ksq] ^= f;
castleRightsMask[king_square(c)] ^= f;
castleRightsMask[rsq] ^= f;
castleRookSquare[f] = rsq;
}
@@ -357,12 +356,12 @@ Bitboard Position::hidden_checkers() const {
// Pinned pieces protect our king, dicovery checks attack the enemy king
Bitboard b, result = 0;
Bitboard pinners = pieces(FindPinned ? flip(sideToMove) : sideToMove);
Square ksq = king_square(FindPinned ? sideToMove : flip(sideToMove));
Bitboard pinners = pieces(FindPinned ? ~sideToMove : sideToMove);
Square ksq = king_square(FindPinned ? sideToMove : ~sideToMove);
// Pinners are sliders, that give check when candidate pinned is removed
pinners &= (pieces(ROOK, QUEEN) & RookPseudoAttacks[ksq])
| (pieces(BISHOP, QUEEN) & BishopPseudoAttacks[ksq]);
pinners &= (pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq])
| (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq]);
while (pinners)
{
@@ -450,7 +449,7 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
assert(is_ok(m));
assert(pinned == pinned_pieces());
Color us = side_to_move();
Color us = sideToMove;
Square from = from_sq(m);
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.
if (is_enpassant(m))
{
Color them = flip(us);
Color them = ~us;
Square to = to_sq(m);
Square capsq = to + pawn_push(them);
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
// for legality during move generation.
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
// 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 {
Color us = sideToMove;
Color them = flip(sideToMove);
Color them = ~sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = piece_on(from);
@@ -613,7 +612,7 @@ bool Position::is_pseudo_legal(const Move m) const {
{
Bitboard b = occupied_squares();
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;
}
else
@@ -641,7 +640,7 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
assert(is_ok(m));
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 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
if ( (pt != PAWN && pt != KING)
|| !squares_aligned(from, to, king_square(flip(side_to_move()))))
|| !squares_aligned(from, to, king_square(~sideToMove)))
return true;
}
@@ -664,9 +663,9 @@ bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
if (!is_special(m))
return false;
Color us = side_to_move();
Color us = sideToMove;
Bitboard b = occupied_squares();
Square ksq = king_square(flip(us));
Square ksq = king_square(~us);
// Promotion with check ?
if (is_promotion(m))
@@ -764,8 +763,8 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
return;
}
Color us = side_to_move();
Color them = flip(us);
Color us = sideToMove;
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
Piece piece = piece_on(from);
@@ -958,7 +957,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
}
// Finish
sideToMove = flip(sideToMove);
sideToMove = ~sideToMove;
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
assert(pos_is_ok());
@@ -972,7 +971,7 @@ void Position::undo_move(Move m) {
assert(is_ok(m));
sideToMove = flip(sideToMove);
sideToMove = ~sideToMove;
if (is_castle(m))
{
@@ -980,8 +979,8 @@ void Position::undo_move(Move m) {
return;
}
Color us = side_to_move();
Color them = flip(us);
Color us = sideToMove;
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
Piece piece = piece_on(to);
@@ -1076,7 +1075,7 @@ void Position::do_castle_move(Move m) {
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
Color us = side_to_move();
Color us = sideToMove;
Square kBefore = from_sq(m);
Square rBefore = to_sq(m);
@@ -1160,10 +1159,10 @@ void Position::do_castle_move(Move m) {
st->rule50 = 0;
// Update checkers BB
st->checkersBB = attackers_to(king_square(flip(us))) & pieces(us);
st->checkersBB = attackers_to(king_square(~us)) & pieces(us);
// Finish
sideToMove = flip(sideToMove);
sideToMove = ~sideToMove;
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
}
else
@@ -1194,7 +1193,7 @@ void Position::do_null_move(StateInfo& backupSt) {
dst->rule50 = src->rule50;
dst->pliesFromNull = src->pliesFromNull;
sideToMove = flip(sideToMove);
sideToMove = ~sideToMove;
if (Do)
{
@@ -1264,9 +1263,9 @@ int Position::see(Move m) const {
// Handle en passant moves
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);
// Remove the captured pawn
@@ -1280,7 +1279,7 @@ int Position::see(Move m) const {
attackers = attackers_to(to, occ);
// 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);
if (!stmAttackers)
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
// move before beginning the next iteration.
capturedType = pt;
stm = flip(stm);
stm = ~stm;
stmAttackers = attackers & pieces(stm);
// Stop before processing a king capture
@@ -1402,7 +1401,7 @@ Key Position::compute_key() const {
if (ep_square() != SQ_NONE)
result ^= zobEp[ep_square()];
if (side_to_move() == BLACK)
if (sideToMove == BLACK)
result ^= zobSideToMove;
return result;
@@ -1466,7 +1465,7 @@ Score Position::compute_value() const {
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;
}
@@ -1499,7 +1498,7 @@ bool Position::is_draw() const {
return true;
// 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;
// Draw by repetition?
@@ -1531,15 +1530,6 @@ template bool Position::is_draw<false>() 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
/// 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
@@ -1571,7 +1561,7 @@ void Position::init() {
for (Square s = SQ_A1; s <= SQ_H8; 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
for (Square s = SQ_A1; s <= SQ_H8; 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
sideToMove = flip(pos.side_to_move());
sideToMove = ~pos.side_to_move();
// Castling rights
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))
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))
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))
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
if (pos.st->epSquare != SQ_NONE)
st->epSquare = flip(pos.st->epSquare);
st->epSquare = ~pos.st->epSquare;
// Checkers
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(flip(sideToMove));
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
// Hash keys
st->key = compute_key();
@@ -1653,7 +1643,7 @@ bool Position::pos_is_ok(int* failedStep) const {
if (failedStep) *failedStep = 1;
// Side to move OK?
if (side_to_move() != WHITE && side_to_move() != BLACK)
if (sideToMove != WHITE && sideToMove != BLACK)
return false;
// Are the king squares in the position correct?
@@ -1682,8 +1672,8 @@ bool Position::pos_is_ok(int* failedStep) const {
if (failedStep) (*failedStep)++;
if (debugKingCapture)
{
Color us = side_to_move();
Color them = flip(us);
Color us = sideToMove;
Color them = ~us;
Square ksq = king_square(them);
if (attackers_to(ksq) & pieces(us))
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
// side to move.
if (relative_rank(side_to_move(), ep_square()) != RANK_6)
if (relative_rank(sideToMove, ep_square()) != RANK_6)
return false;
}

View File

@@ -37,6 +37,7 @@ struct CheckInfo {
Bitboard dcCandidates;
Bitboard pinned;
Bitboard checkSq[8];
Square ksq;
};
@@ -100,6 +101,7 @@ public:
// The piece on a given square
Piece piece_on(Square s) const;
Piece piece_moved(Move m) const;
bool square_is_empty(Square s) const;
// Side to move
@@ -183,14 +185,9 @@ public:
Value non_pawn_material(Color c) 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
template<bool SkipRepetition> bool is_draw() const;
int startpos_ply_counter() const;
bool opposite_colored_bishops() const;
bool has_pawn_on_7th(Color c) const;
bool is_chess960() const;
@@ -213,7 +210,7 @@ private:
// Initialization helper functions (used while setting up a position)
void clear();
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;
// Helper template functions
@@ -277,6 +274,10 @@ inline Piece Position::piece_on(Square s) const {
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 {
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 {
return !(pieces(PAWN, flip(c)) & passed_pawn_mask(c, s));
return !(pieces(PAWN, ~c) & passed_pawn_mask(c, s));
}
inline Key Position::key() const {

View File

@@ -24,7 +24,6 @@
#include <iomanip>
#include <iostream>
#include <sstream>
#include <vector>
#include "book.h"
#include "evaluate.h"
@@ -42,7 +41,7 @@ namespace Search {
volatile SignalsType Signals;
LimitsType Limits;
std::vector<Move> SearchMoves;
std::vector<RootMove> RootMoves;
Position RootPosition;
}
@@ -59,33 +58,6 @@ namespace {
// Different node types, used as template parameter
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
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]; }
@@ -135,17 +107,14 @@ namespace {
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
// better than the second best move.
// Easy move margin. An easy move candidate must be at least this much better
// than the second best move.
const Value EasyMoveMargin = Value(0x150);
// This is the minimum interval in msec between two check_time() calls
const int TimerResolution = 5;
/// Namespace variables
std::vector<RootMove> RootMoves;
size_t MultiPV, UCIMultiPV, PVIdx;
TimeManager TimeMgr;
int BestMoveChanges;
@@ -154,8 +123,6 @@ namespace {
History H;
/// Local functions
template <NodeType NT>
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) {
// 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();
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
Move bm;
Position& pos = RootPosition;
Chess960 = pos.is_chess960();
elapsed_time(true);
TimeMgr.init(Limits, pos.startpos_ply_counter());
TT.new_search();
H.clear();
RootMoves.clear();
// Populate RootMoves with all the legal moves (default) or, if a SearchMoves
// 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"])
if (RootMoves.empty())
{
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))
{
std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove));
goto finalize;
}
RootMoves.push_back(MOVE_NONE);
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
@@ -375,9 +342,9 @@ void Search::think() {
finalize:
// When we reach max depth we arrive here even without a StopRequest, but if
// we are pondering or in infinite search, we shouldn't print the best move
// before we are told to do so.
// When we reach max depth we arrive here even without Signals.stop is raised,
// but if we are pondering or in infinite search, we shouldn't print the best
// move before we are told to do so.
if (!Signals.stop && (Limits.ponder || Limits.infinite))
Threads.wait_for_stop_or_ponderhit();
@@ -406,16 +373,6 @@ namespace {
bestValue = delta = -VALUE_INFINITE;
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
while (!Signals.stop && ++depth <= MAX_PLY && (!Limits.maxDepth || depth <= Limits.maxDepth))
{
@@ -531,15 +488,15 @@ namespace {
stop = true;
// Stop search early if one move seems to be much better than others
if ( depth >= 10
if ( depth >= 12
&& !stop
&& ( bestMoveNeverChanged
&& ( (bestMoveNeverChanged && pos.captured_piece_type())
|| elapsed_time() > (TimeMgr.available_time() * 40) / 100))
{
Value rBeta = bestValue - EasyMoveMargin;
(ss+1)->excludedMove = RootMoves[0].pv[0];
(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)->excludedMove = MOVE_NONE;
@@ -701,7 +658,7 @@ namespace {
if ( (move = (ss-1)->currentMove) != MOVE_NULL
&& (ss-1)->eval != VALUE_NONE
&& ss->eval != VALUE_NONE
&& pos.captured_piece_type() == NO_PIECE_TYPE
&& !pos.captured_piece_type()
&& !is_special(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.
Depth predictedDepth = newDepth - reduction<PvNode>(depth, 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)
{
@@ -1154,13 +1111,13 @@ split_point_start: // At split points actual search starts from here
// Increase history value of the cut-off move
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
for (int i = 0; i < playedMoveCount - 1; 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);
to = to_sq(move);
them = flip(pos.side_to_move());
them = ~pos.side_to_move();
ksq = pos.king_square(them);
kingAtt = pos.attacks_from<KING>(ksq);
pc = pos.piece_on(from);
@@ -1801,76 +1758,76 @@ split_point_start: // At split points actual search starts from here
return best;
}
// 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) {
StateInfo state[MAX_PLY_PLUS_2], *st = state;
TTEntry* tte;
int ply = 1;
Move m = pv[0];
assert(m != MOVE_NONE && pos.is_pseudo_legal(m));
pv.clear();
pv.push_back(m);
pos.do_move(m, *st++);
while ( (tte = TT.probe(pos.key())) != NULL
&& tte->move() != MOVE_NONE
&& pos.is_pseudo_legal(tte->move())
&& pos.pl_move_is_legal(tte->move(), pos.pinned_pieces())
&& ply < MAX_PLY
&& (!pos.is_draw<false>() || ply < 2))
{
pv.push_back(tte->move());
pos.do_move(tte->move(), *st++);
ply++;
}
pv.push_back(MOVE_NONE);
do pos.undo_move(pv[--ply]); while (ply);
}
// insert_pv_in_tt() is called at the end of a search iteration, and 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.
void RootMove::insert_pv_in_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state;
TTEntry* tte;
Key k;
Value v, m = VALUE_NONE;
int ply = 0;
assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply]));
do {
k = pos.key();
tte = TT.probe(k);
// Don't overwrite existing correct entries
if (!tte || tte->move() != pv[ply])
{
v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m));
TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m);
}
pos.do_move(pv[ply], *st++);
} while (pv[++ply] != MOVE_NONE);
do pos.undo_move(pv[--ply]); while (ply);
}
} // namespace
/// 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;
TTEntry* tte;
int ply = 1;
Move m = pv[0];
assert(m != MOVE_NONE && pos.is_pseudo_legal(m));
pv.clear();
pv.push_back(m);
pos.do_move(m, *st++);
while ( (tte = TT.probe(pos.key())) != NULL
&& tte->move() != MOVE_NONE
&& pos.is_pseudo_legal(tte->move())
&& pos.pl_move_is_legal(tte->move(), pos.pinned_pieces())
&& ply < MAX_PLY
&& (!pos.is_draw<false>() || ply < 2))
{
pv.push_back(tte->move());
pos.do_move(tte->move(), *st++);
ply++;
}
pv.push_back(MOVE_NONE);
do pos.undo_move(pv[--ply]); while (ply);
}
/// RootMove::insert_pv_in_tt() is called at the end of a search iteration, and
/// 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.
void RootMove::insert_pv_in_tt(Position& pos) {
StateInfo state[MAX_PLY_PLUS_2], *st = state;
TTEntry* tte;
Key k;
Value v, m = VALUE_NONE;
int ply = 0;
assert(pv[ply] != MOVE_NONE && pos.is_pseudo_legal(pv[ply]));
do {
k = pos.key();
tte = TT.probe(k);
// Don't overwrite existing correct entries
if (!tte || tte->move() != pv[ply])
{
v = (pos.in_check() ? VALUE_NONE : evaluate(pos, m));
TT.store(k, VALUE_NONE, VALUE_TYPE_NONE, DEPTH_NONE, pv[ply], v, m);
}
pos.do_move(pv[ply], *st++);
} while (pv[++ply] != MOVE_NONE);
do pos.undo_move(pv[--ply]); while (ply);
}
/// Thread::idle_loop() is where the thread is parked when it has no work to do.
/// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object
/// for which the thread is the master.

View File

@@ -48,13 +48,36 @@ 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
/// 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.
struct LimitsType {
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool use_time_management() const { return !(maxTime | maxDepth | maxNodes | infinite); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
@@ -70,13 +93,13 @@ struct SignalsType {
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<Move> SearchMoves;
extern std::vector<RootMove> RootMoves;
extern Position RootPosition;
extern void init();
extern int64_t perft(Position& pos, Depth depth);
extern void think();
} // namespace
} // namespace Search
#endif // !defined(SEARCH_H_INCLUDED)

View File

@@ -19,6 +19,7 @@
#include <iostream>
#include "movegen.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
@@ -420,7 +421,7 @@ void Thread::main_loop() {
if (do_terminate)
return;
think(); // This is the search entry point
Search::think();
}
}
@@ -431,7 +432,7 @@ void Thread::main_loop() {
// the search to finish.
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];
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
RootPosition.copy(pos, 0);
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
memset((void*)&Signals, 0, sizeof(Signals));
Signals.stopOnPonderhit = Signals.firstRootMove = false;
Signals.stop = Signals.failedLowAtRoot = false;
main.do_sleep = false;
cond_signal(&main.sleepCond); // Wake up main thread and start searching
if (!asyncMode)
if (!async)
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);

View File

@@ -21,6 +21,7 @@
#define THREAD_H_INCLUDED
#include <cstring>
#include <set>
#include "lock.h"
#include "material.h"
@@ -120,7 +121,7 @@ public:
void wait_for_stop_or_ponderhit();
void stop_thinking();
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>
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
/// | only in 64-bit mode. For compiling requires hardware with
/// | 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 <cstdlib>
@@ -339,6 +334,14 @@ extern const Value PieceValueMidgame[17];
extern const Value PieceValueEndgame[17];
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) {
return VALUE_MATE - ply;
}
@@ -359,10 +362,6 @@ inline Color color_of(Piece p) {
return Color(p >> 3);
}
inline Color flip(Color c) {
return Color(c ^ 1);
}
inline Square make_square(File f, Rank r) {
return Square((r << 3) | f);
}
@@ -379,10 +378,6 @@ inline Rank rank_of(Square s) {
return Rank(s >> 3);
}
inline Square flip(Square s) {
return Square(s ^ 56);
}
inline Square mirror(Square s) {
return Square(s ^ 7);
}

View File

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