DroidFish: Updated stockfish to version 2.2.2.

This commit is contained in:
Peter Osterlund
2012-01-15 01:13:33 +00:00
parent ff40f9adc2
commit ca1482a80a
25 changed files with 555 additions and 638 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

@@ -19,14 +19,13 @@
#include <algorithm> #include <algorithm>
#include <cassert> #include <cassert>
#include <ctype.h>
#include "bitcount.h" #include "bitcount.h"
#include "endgame.h" #include "endgame.h"
#include "pawns.h" #include "pawns.h"
using std::string; using std::string;
using std::transform; using namespace std;
extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm); extern uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm);
@@ -207,10 +206,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)
@@ -253,10 +252,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);
@@ -493,11 +492,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)
{ {
@@ -869,10 +868,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

@@ -20,7 +20,6 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <string> #include <string>
#include <ctype.h>
#include "movegen.h" #include "movegen.h"
#include "position.h" #include "position.h"
@@ -148,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

@@ -23,7 +23,6 @@
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <algorithm> #include <algorithm>
#include <ctype.h>
#include "bitcount.h" #include "bitcount.h"
#include "movegen.h" #include "movegen.h"
@@ -79,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();
@@ -166,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)
{ {
@@ -193,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));
@@ -209,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
@@ -235,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());
@@ -243,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;
} }
@@ -358,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)
{ {
@@ -451,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);
@@ -462,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);
@@ -485,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.
@@ -516,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);
@@ -614,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
@@ -642,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);
@@ -657,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;
} }
@@ -665,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))
@@ -765,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);
@@ -959,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());
@@ -973,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))
{ {
@@ -981,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);
@@ -1077,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);
@@ -1161,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
@@ -1195,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)
{ {
@@ -1265,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
@@ -1281,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];
@@ -1319,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
@@ -1403,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;
@@ -1467,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;
} }
@@ -1500,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?
@@ -1532,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
@@ -1572,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];
} }
} }
} }
@@ -1592,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();
@@ -1654,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?
@@ -1683,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;
@@ -1721,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,15 +41,14 @@ namespace Search {
volatile SignalsType Signals; volatile SignalsType Signals;
LimitsType Limits; LimitsType Limits;
std::vector<Move> SearchMoves; std::vector<RootMove> RootMoves;
Position RootPosition; Position RootPosition;
} }
using std::string; using std::string;
using std::cout; using std::cout;
using std::endl; using std::endl;
using std::count; using namespace std;
using std::find;
using namespace Search; using namespace Search;
namespace { namespace {
@@ -61,33 +59,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]; }
@@ -137,17 +108,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;
@@ -156,8 +124,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);
@@ -202,7 +168,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
@@ -284,29 +250,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
@@ -377,9 +343,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();
@@ -408,16 +374,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))
{ {
@@ -533,15 +489,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;
@@ -703,7 +659,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);
@@ -972,7 +928,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)
{ {
@@ -1156,13 +1112,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);
} }
} }
} }
@@ -1395,7 +1351,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);
@@ -1803,11 +1759,13 @@ 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 /// RootMove::extract_pv_from_tt() builds a PV by adding moves from the TT table.
// allow to always have a ponder move even when we fail high at root and also a /// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes so
// long PV to print that is important for position analysis. /// 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) { void RootMove::extract_pv_from_tt(Position& pos) {
@@ -1839,9 +1797,9 @@ split_point_start: // At split points actual search starts from here
} }
// 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) {
@@ -1870,8 +1828,6 @@ split_point_start: // At split points actual search starts from here
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.
/// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object /// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object

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,14 +34,10 @@
/// -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>
#include <ctype.h>
#if defined(_MSC_VER) #if defined(_MSC_VER)
@@ -339,6 +335,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 +363,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 +379,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()];

View File

@@ -19,14 +19,13 @@
#include <algorithm> #include <algorithm>
#include <sstream> #include <sstream>
#include <ctype.h>
#include "misc.h" #include "misc.h"
#include "thread.h" #include "thread.h"
#include "ucioption.h" #include "ucioption.h"
using std::string; using std::string;
using std::lexicographical_compare; using namespace std;
OptionsMap Options; // Global object OptionsMap Options; // Global object

View File

@@ -175,7 +175,7 @@
<string name="about_info">\ <string name="about_info">\
<b>Information</b>\n\ <b>Information</b>\n\
<i>DroidFish</i> ist ein Schachprogramm mit vielfältigen Funktionen \ <i>DroidFish</i> ist ein Schachprogramm mit vielfältigen Funktionen \
und der sehr spielstarken Schach-Engine <i>Stockfish 2.2.1</i>. \n\ und der sehr spielstarken Schach-Engine <i>Stockfish 2.2.2</i>. \n\
\n\ \n\
<b>Funktionen</b>\n\ <b>Funktionen</b>\n\
* Eröffnungsbuch\n\ * Eröffnungsbuch\n\

View File

@@ -175,7 +175,7 @@
<string name="about_info">\ <string name="about_info">\
<b>About</b>\n\ <b>About</b>\n\
<i>DroidFish</i> incorpora una interfaz gráfica de ajedrez de usuario muy completa, \ <i>DroidFish</i> incorpora una interfaz gráfica de ajedrez de usuario muy completa, \
combinada con el muy potente motor de ajedrez <i>Stockfish 2.2.1</i>. combinada con el muy potente motor de ajedrez <i>Stockfish 2.2.2</i>.
\n\ \n\
<b>Características</b>\n\ <b>Características</b>\n\
* Libro de Aperturas\n\ * Libro de Aperturas\n\

View File

@@ -175,7 +175,7 @@
<string name="about_info">\ <string name="about_info">\
<b>About</b>\n\ <b>About</b>\n\
<i>DroidFish</i> is a feature-rich graphical chess user interface, \ <i>DroidFish</i> is a feature-rich graphical chess user interface, \
combined with the very strong <i>Stockfish 2.2.1</i> chess engine.\n\ combined with the very strong <i>Stockfish 2.2.2</i> chess engine.\n\
\n\ \n\
<b>Features</b>\n\ <b>Features</b>\n\
* Opening book\n\ * Opening book\n\