mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-22 01:56:58 +08:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f237e8b8ea | ||
|
|
103b368ab7 | ||
|
|
a29dd88f75 | ||
|
|
a1076cc68a | ||
|
|
78908b7aed | ||
|
|
d98150dffc | ||
|
|
c549f71f64 | ||
|
|
c19ea4b000 | ||
|
|
bede30e7a6 | ||
|
|
b05fbb3733 | ||
|
|
9b43fd7937 | ||
|
|
87fc9dcaa3 | ||
|
|
83f3ea7ab4 | ||
|
|
9c8c4ff46f | ||
|
|
d655147e8c | ||
|
|
3c675db3d0 | ||
|
|
cf247e7e30 | ||
|
|
0026c88b3a | ||
|
|
6482ce2bb2 | ||
|
|
867a5a8cd2 | ||
|
|
78e6b361c5 | ||
|
|
aa392c366e | ||
|
|
f80c50bcdd | ||
|
|
d282cf6964 | ||
|
|
b1fcfe4c5d | ||
|
|
30418a3cfc | ||
|
|
cb1709ef5e | ||
|
|
67338e6f32 | ||
|
|
8300ab149c | ||
|
|
c00443b19e |
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
|
||||
251
src/book.cpp
251
src/book.cpp
@@ -17,13 +17,13 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
The code in this file is based on the opening book code in PolyGlot
|
||||
by Fabien Letouzey. PolyGlot is available under the GNU General
|
||||
Public License, and can be downloaded from http://wbec-ridderkerk.nl
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
@@ -306,225 +306,182 @@ namespace {
|
||||
const Key* ZobEnPassant = PolyGlotRandoms + 772;
|
||||
const Key* ZobTurn = PolyGlotRandoms + 780;
|
||||
|
||||
// Piece offset is calculated as 64 * (PolyPiece ^ 1) where
|
||||
// PolyPiece is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
|
||||
const int PieceOfs[] = { 0, 64, 192, 320, 448, 576, 704, 0,
|
||||
// PieceOffset is calculated as 64 * (PolyPiece ^ 1) where PolyPiece
|
||||
// is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
|
||||
const int PieceOffset[] = { 0, 64, 192, 320, 448, 576, 704, 0,
|
||||
0, 0, 128, 256, 384, 512, 640 };
|
||||
|
||||
// book_key() builds up a PolyGlot hash key out of a position
|
||||
// book_key() returns the PolyGlot hash key of the given position
|
||||
uint64_t book_key(const Position& pos) {
|
||||
|
||||
uint64_t result = 0;
|
||||
uint64_t key = 0;
|
||||
Bitboard b = pos.occupied_squares();
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square s = pop_1st_bit(&b);
|
||||
result ^= ZobPiece[PieceOfs[pos.piece_on(s)] + s];
|
||||
key ^= ZobPiece[PieceOffset[pos.piece_on(s)] + s];
|
||||
}
|
||||
|
||||
if (pos.can_castle(WHITE_OO))
|
||||
result ^= ZobCastle[0];
|
||||
b = (pos.can_castle(WHITE_OO) << 0) | (pos.can_castle(WHITE_OOO) << 1)
|
||||
| (pos.can_castle(BLACK_OO) << 2) | (pos.can_castle(BLACK_OOO) << 3);
|
||||
|
||||
if (pos.can_castle(WHITE_OOO))
|
||||
result ^= ZobCastle[1];
|
||||
|
||||
if (pos.can_castle(BLACK_OO))
|
||||
result ^= ZobCastle[2];
|
||||
|
||||
if (pos.can_castle(BLACK_OOO))
|
||||
result ^= ZobCastle[3];
|
||||
while (b)
|
||||
key ^= ZobCastle[pop_1st_bit(&b)];
|
||||
|
||||
if (pos.ep_square() != SQ_NONE)
|
||||
result ^= ZobEnPassant[file_of(pos.ep_square())];
|
||||
key ^= ZobEnPassant[file_of(pos.ep_square())];
|
||||
|
||||
if (pos.side_to_move() == WHITE)
|
||||
result ^= ZobTurn[0];
|
||||
key ^= ZobTurn[0];
|
||||
|
||||
return result;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
/// Book c'tor. Make random number generation less deterministic, for book moves
|
||||
Book::Book() : bookSize(0) {
|
||||
Book::Book() : size(0) {
|
||||
|
||||
for (int i = abs(system_time() % 10000); i > 0; i--)
|
||||
RKiss.rand<unsigned>();
|
||||
RKiss.rand<unsigned>(); // Make random number generation less deterministic
|
||||
}
|
||||
|
||||
Book::~Book() { if (is_open()) close(); }
|
||||
|
||||
|
||||
/// Book::operator>>() reads sizeof(T) chars from the file's binary byte stream
|
||||
/// and converts them in a number of type T. A Polyglot book stores numbers in
|
||||
/// big-endian format.
|
||||
|
||||
template<typename T> Book& Book::operator>>(T& n) {
|
||||
|
||||
n = 0;
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
n = T((n << 8) + ifstream::get());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<> Book& Book::operator>>(BookEntry& e) {
|
||||
return *this >> e.key >> e.move >> e.count >> e.learn;
|
||||
}
|
||||
|
||||
|
||||
/// Book destructor. Be sure file is closed before we leave.
|
||||
/// Book::open() tries to open a book file with the given name after closing
|
||||
/// any exsisting one.
|
||||
|
||||
Book::~Book() {
|
||||
bool Book::open(const char* fName) {
|
||||
|
||||
close();
|
||||
}
|
||||
fileName = "";
|
||||
|
||||
|
||||
/// Book::close() closes the file only if it is open, otherwise the call fails
|
||||
/// and the failbit internal state flag is set.
|
||||
|
||||
void Book::close() {
|
||||
|
||||
if (bookFile.is_open())
|
||||
bookFile.close();
|
||||
|
||||
bookName = "";
|
||||
bookSize = 0;
|
||||
}
|
||||
|
||||
|
||||
/// Book::open() opens a book file with a given name
|
||||
|
||||
void Book::open(const string& fileName) {
|
||||
|
||||
// Close old file before opening the new
|
||||
if (is_open()) // Cannot close an already closed file
|
||||
close();
|
||||
|
||||
bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary |ios::ate);
|
||||
ifstream::open(fName, ifstream::in | ifstream::binary | ios::ate);
|
||||
|
||||
// Silently return when asked to open a non-exsistent file
|
||||
if (!bookFile.is_open())
|
||||
return;
|
||||
if (!is_open())
|
||||
return false; // Silently fail if the file is not found
|
||||
|
||||
// Get the book size in number of entries, we are already at the file end
|
||||
bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
|
||||
// Get the book size in number of entries, we are already at the end of file
|
||||
size = tellg() / sizeof(BookEntry);
|
||||
|
||||
if (!bookFile.good())
|
||||
if (!good())
|
||||
{
|
||||
cerr << "Failed to open book file " << fileName << endl;
|
||||
cerr << "Failed to open book file " << fName << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Set only if successful
|
||||
bookName = fileName;
|
||||
fileName = fName; // Set only if successful
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// Book::probe() gets a book move for a given position. Returns MOVE_NONE
|
||||
/// if no book move is found. If findBest is true then returns always the
|
||||
/// highest rated move otherwise chooses randomly based on the move score.
|
||||
/// Book::probe() tries to find a book move for the given position. If no move
|
||||
/// is found returns MOVE_NONE. If pickBest is true returns always the highest
|
||||
/// rated move, otherwise randomly chooses one, based on the move score.
|
||||
|
||||
Move Book::probe(const Position& pos, bool findBest) {
|
||||
Move Book::probe(const Position& pos, const string& fName, bool pickBest) {
|
||||
|
||||
if (!bookSize || !bookFile.is_open())
|
||||
BookEntry e;
|
||||
uint16_t best = 0;
|
||||
unsigned sum = 0;
|
||||
Move move = MOVE_NONE;
|
||||
uint64_t key = book_key(pos);
|
||||
|
||||
if (fileName != fName && !open(fName.c_str()))
|
||||
return MOVE_NONE;
|
||||
|
||||
BookEntry entry;
|
||||
unsigned scoresSum = 0, bestScore = 0, bookMove = 0;
|
||||
uint64_t key = book_key(pos);
|
||||
int idx = first_entry(key) - 1;
|
||||
binary_search(key);
|
||||
|
||||
// Choose a book move among the possible moves for the given position
|
||||
while (++idx < bookSize && (entry = read_entry(idx), entry.key == key))
|
||||
while (*this >> e, e.key == key && good())
|
||||
{
|
||||
scoresSum += entry.count;
|
||||
best = max(best, e.count);
|
||||
sum += e.count;
|
||||
|
||||
// Choose book move according to its score. If a move has a very
|
||||
// high score it has higher probability to be choosen than a move
|
||||
// with lower score. Note that first entry is always chosen.
|
||||
if ( RKiss.rand<unsigned>() % scoresSum < entry.count
|
||||
|| (findBest && entry.count > bestScore))
|
||||
bookMove = entry.move;
|
||||
|
||||
if (entry.count > bestScore)
|
||||
bestScore = entry.count;
|
||||
if ( (RKiss.rand<unsigned>() % sum < e.count)
|
||||
|| (pickBest && e.count == best))
|
||||
move = Move(e.move);
|
||||
}
|
||||
|
||||
if (!bookMove)
|
||||
if (!move)
|
||||
return MOVE_NONE;
|
||||
|
||||
// A PolyGlot book move is encoded as follows:
|
||||
//
|
||||
// bit 0- 5: destination square (from 0 to 63)
|
||||
// bit 6-11: origin square (from 0 to 63)
|
||||
// bit 12-13-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
|
||||
// bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4)
|
||||
//
|
||||
// Castling moves follow "king captures rook" representation. So in case
|
||||
// book move is a promotion we have to convert to our representation, in
|
||||
// all other cases we can directly compare with a Move after having
|
||||
// masked out special Move's flags that are not supported by PolyGlot.
|
||||
int promotion = (bookMove >> 12) & 7;
|
||||
// Castling moves follow "king captures rook" representation. So in case book
|
||||
// move is a promotion we have to convert to our representation, in all the
|
||||
// other cases we can directly compare with a Move after having masked out
|
||||
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
|
||||
int pt = (move >> 12) & 7;
|
||||
if (pt)
|
||||
move = make_promotion(from_sq(move), to_sq(move), PieceType(pt + 1));
|
||||
|
||||
if (promotion)
|
||||
bookMove = make_promotion_move(move_from(Move(bookMove)),
|
||||
move_to(Move(bookMove)),
|
||||
PieceType(promotion + 1));
|
||||
// Verify the book move is legal
|
||||
// Add 'special move' flags and verify it is legal
|
||||
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
|
||||
if (unsigned(ml.move() & ~(3 << 14)) == bookMove) // Mask out special flags
|
||||
if (move == (ml.move() & 0x3FFF))
|
||||
return ml.move();
|
||||
|
||||
return MOVE_NONE;
|
||||
}
|
||||
|
||||
|
||||
/// Book::first_entry() takes a book key as input, and does a binary search
|
||||
/// through the book file for the given key. The index to the first (leftmost)
|
||||
/// book entry with the same key as the input is returned. When the key is not
|
||||
/// found in the book file, bookSize is returned.
|
||||
/// Book::binary_search() takes a book key as input, and does a binary search
|
||||
/// through the book file for the given key. File stream current position is set
|
||||
/// to the leftmost book entry with the same key as the input.
|
||||
|
||||
int Book::first_entry(uint64_t key) {
|
||||
|
||||
int left, right, mid;
|
||||
|
||||
// Binary search (finds the leftmost entry with given key)
|
||||
left = 0;
|
||||
right = bookSize - 1;
|
||||
|
||||
assert(left <= right);
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
mid = (left + right) / 2;
|
||||
|
||||
assert(mid >= left && mid < right);
|
||||
|
||||
if (key <= read_entry(mid).key)
|
||||
right = mid;
|
||||
else
|
||||
left = mid + 1;
|
||||
}
|
||||
|
||||
assert(left == right);
|
||||
|
||||
return read_entry(left).key == key ? left : bookSize;
|
||||
}
|
||||
|
||||
|
||||
/// Book::operator>>() reads sizeof(T) chars from the file's binary byte
|
||||
/// stream and converts them in a number of type T.
|
||||
template<typename T>
|
||||
Book& Book::operator>>(T& n) {
|
||||
|
||||
n = 0;
|
||||
|
||||
for (size_t i = 0; i < sizeof(T); i++)
|
||||
n = T((n << 8) + bookFile.get());
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/// Book::read_entry() takes an integer index, and returns the BookEntry
|
||||
/// at the given index in the book file.
|
||||
|
||||
BookEntry Book::read_entry(int idx) {
|
||||
|
||||
assert(idx >= 0 && idx < bookSize);
|
||||
assert(bookFile.is_open());
|
||||
void Book::binary_search(uint64_t key) {
|
||||
|
||||
size_t low, high, mid;
|
||||
BookEntry e;
|
||||
|
||||
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg);
|
||||
low = 0;
|
||||
high = size - 1;
|
||||
|
||||
*this >> e.key >> e.move >> e.count >> e.learn;
|
||||
assert(low <= high);
|
||||
|
||||
if (!bookFile.good())
|
||||
while (low < high && good())
|
||||
{
|
||||
cerr << "Failed to read book entry at index " << idx << endl;
|
||||
exit(EXIT_FAILURE);
|
||||
mid = (low + high) / 2;
|
||||
|
||||
assert(mid >= low && mid < high);
|
||||
|
||||
seekg(mid * sizeof(BookEntry), ios_base::beg);
|
||||
*this >> e;
|
||||
|
||||
if (key <= e.key)
|
||||
high = mid;
|
||||
else
|
||||
low = mid + 1;
|
||||
}
|
||||
return e;
|
||||
|
||||
assert(low == high);
|
||||
|
||||
seekg(low * sizeof(BookEntry), ios_base::beg);
|
||||
}
|
||||
|
||||
17
src/book.h
17
src/book.h
@@ -37,25 +37,22 @@ struct BookEntry {
|
||||
uint32_t learn;
|
||||
};
|
||||
|
||||
class Book {
|
||||
|
||||
class Book : private std::ifstream {
|
||||
public:
|
||||
Book();
|
||||
~Book();
|
||||
void open(const std::string& fileName);
|
||||
void close();
|
||||
Move probe(const Position& pos, bool findBestMove);
|
||||
const std::string name() const { return bookName; }
|
||||
Move probe(const Position& pos, const std::string& fName, bool pickBest);
|
||||
|
||||
private:
|
||||
template<typename T> Book& operator>>(T& n);
|
||||
|
||||
BookEntry read_entry(int idx);
|
||||
int first_entry(uint64_t key);
|
||||
bool open(const char* fName);
|
||||
void binary_search(uint64_t key);
|
||||
|
||||
RKISS RKiss;
|
||||
std::ifstream bookFile;
|
||||
std::string bookName;
|
||||
int bookSize;
|
||||
std::string fileName;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
#endif // !defined(BOOK_H_INCLUDED)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
29
src/lock.h
29
src/lock.h
@@ -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;
|
||||
|
||||
@@ -75,10 +58,8 @@ typedef HANDLE WaitCondition;
|
||||
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
|
||||
# define cond_destroy(x) CloseHandle(*x)
|
||||
# define cond_signal(x) SetEvent(*x)
|
||||
# define cond_wait(x,y) { ResetEvent(*x); lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
|
||||
# define cond_timedwait(x,y,z) { ResetEvent(*x); lock_release(y); WaitForSingleObject(*x,z); lock_grab(y); }
|
||||
|
||||
#endif
|
||||
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
|
||||
# define cond_timedwait(x,y,z) { lock_release(y); WaitForSingleObject(*x,z); lock_grab(y); }
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
static const string Version = "2.2.2";
|
||||
static const string Tag = "";
|
||||
|
||||
|
||||
|
||||
18
src/move.cpp
18
src/move.cpp
@@ -33,8 +33,8 @@ using std::string;
|
||||
|
||||
const string move_to_uci(Move m, bool chess960) {
|
||||
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
string promotion;
|
||||
|
||||
if (m == MOVE_NONE)
|
||||
@@ -83,13 +83,13 @@ const string move_to_san(Position& pos, Move m) {
|
||||
|
||||
Bitboard attackers;
|
||||
bool ambiguousMove, ambiguousFile, ambiguousRank;
|
||||
Square sq, from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Square sq, from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
PieceType pt = type_of(pos.piece_on(from));
|
||||
string san;
|
||||
|
||||
if (is_castle(m))
|
||||
san = (move_to(m) < move_from(m) ? "O-O-O" : "O-O");
|
||||
san = (to_sq(m) < from_sq(m) ? "O-O-O" : "O-O");
|
||||
else
|
||||
{
|
||||
if (pt != PAWN)
|
||||
@@ -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.
|
||||
if (pos.move_gives_check(m, CheckInfo(pos)))
|
||||
{
|
||||
StateInfo st;
|
||||
pos.do_move(m, st);
|
||||
if (pos.in_check())
|
||||
san += pos.is_mate() ? "#" : "+";
|
||||
san += MoveList<MV_LEGAL>(pos).size() ? "+" : "#";
|
||||
pos.undo_move(m);
|
||||
}
|
||||
|
||||
return san;
|
||||
}
|
||||
|
||||
613
src/movegen.cpp
613
src/movegen.cpp
@@ -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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
(*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>
|
||||
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,43 +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);
|
||||
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_EVASION);
|
||||
|
||||
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) {
|
||||
|
||||
@@ -119,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
|
||||
@@ -151,27 +339,22 @@ namespace {
|
||||
template<MoveType Type>
|
||||
MoveStack* generate(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(Type == MV_CAPTURE || Type == MV_NON_CAPTURE || Type == MV_NON_EVASION);
|
||||
assert(!pos.in_check());
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard target;
|
||||
|
||||
if (Type == MV_CAPTURE || Type == MV_NON_EVASION)
|
||||
target = pos.pieces(flip(us));
|
||||
if (Type == MV_CAPTURE)
|
||||
target = pos.pieces(~us);
|
||||
|
||||
else if (Type == MV_NON_CAPTURE)
|
||||
target = pos.empty_squares();
|
||||
else
|
||||
assert(false);
|
||||
|
||||
if (Type == MV_NON_EVASION)
|
||||
{
|
||||
mlist = generate_piece_moves<PAWN, MV_CAPTURE>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<PAWN, MV_NON_CAPTURE>(pos, mlist, us, pos.empty_squares());
|
||||
target |= pos.empty_squares();
|
||||
}
|
||||
else
|
||||
else if (Type == MV_NON_EVASION)
|
||||
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);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
@@ -180,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;
|
||||
@@ -203,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -248,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;
|
||||
@@ -289,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);
|
||||
@@ -307,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) {
|
||||
@@ -317,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;
|
||||
@@ -327,217 +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<MoveType Type, 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)
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, QUEEN);
|
||||
|
||||
if (Type == MV_NON_CAPTURE || Type == MV_EVASION)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - Delta, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(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_move(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)
|
||||
{
|
||||
mlist = generate_pawn_captures<Type, RIGHT_UP>(mlist, pawns, enemyPieces);
|
||||
mlist = generate_pawn_captures<Type, 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) && 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_move(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_move(kfrom, rfrom);
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
enum MoveType {
|
||||
MV_CAPTURE,
|
||||
MV_NON_CAPTURE,
|
||||
MV_CHECK,
|
||||
MV_NON_CAPTURE_CHECK,
|
||||
MV_EVASION,
|
||||
MV_NON_EVASION,
|
||||
|
||||
@@ -254,8 +254,8 @@ void MovePicker::score_captures() {
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
{
|
||||
m = cur->move;
|
||||
cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
|
||||
- type_of(pos.piece_on(move_from(m)));
|
||||
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
|
||||
- type_of(pos.piece_moved(m));
|
||||
|
||||
if (is_promotion(m))
|
||||
cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))];
|
||||
@@ -270,8 +270,8 @@ void MovePicker::score_noncaptures() {
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
{
|
||||
m = cur->move;
|
||||
from = move_from(m);
|
||||
cur->score = H.value(pos.piece_on(from), move_to(m));
|
||||
from = from_sq(m);
|
||||
cur->score = H.value(pos.piece_on(from), to_sq(m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,10 +293,10 @@ void MovePicker::score_evasions() {
|
||||
if ((seeScore = pos.see_sign(m)) < 0)
|
||||
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
|
||||
else if (pos.is_capture(m))
|
||||
cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
|
||||
- type_of(pos.piece_on(move_from(m))) + History::MaxValue;
|
||||
cur->score = PieceValueMidgame[pos.piece_on(to_sq(m))]
|
||||
- type_of(pos.piece_moved(m)) + History::MaxValue;
|
||||
else
|
||||
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m));
|
||||
cur->score = H.value(pos.piece_moved(m), to_sq(m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ Move MovePicker::next_move() {
|
||||
|
||||
case PH_QRECAPTURES:
|
||||
move = (curMove++)->move;
|
||||
if (move_to(move) == recaptureSquare)
|
||||
if (to_sq(move) == recaptureSquare)
|
||||
return move;
|
||||
break;
|
||||
|
||||
|
||||
164
src/position.cpp
164
src/position.cpp
@@ -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)
|
||||
{
|
||||
@@ -420,8 +419,8 @@ bool Position::move_attacks_square(Move m, Square s) const {
|
||||
assert(square_is_ok(s));
|
||||
|
||||
Bitboard occ, xray;
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
Piece piece = piece_on(from);
|
||||
|
||||
assert(!square_is_empty(from));
|
||||
@@ -450,8 +449,8 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
assert(is_ok(m));
|
||||
assert(pinned == pinned_pieces());
|
||||
|
||||
Color us = side_to_move();
|
||||
Square from = move_from(m);
|
||||
Color us = sideToMove;
|
||||
Square from = from_sq(m);
|
||||
|
||||
assert(color_of(piece_on(from)) == us);
|
||||
assert(piece_on(king_square(us)) == make_piece(us, KING));
|
||||
@@ -461,8 +460,8 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
// the move is made.
|
||||
if (is_enpassant(m))
|
||||
{
|
||||
Color them = flip(us);
|
||||
Square to = move_to(m);
|
||||
Color them = ~us;
|
||||
Square to = to_sq(m);
|
||||
Square capsq = to + pawn_push(them);
|
||||
Square ksq = king_square(us);
|
||||
Bitboard b = occupied_squares();
|
||||
@@ -484,13 +483,13 @@ 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(move_to(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.
|
||||
return !pinned
|
||||
|| !bit_is_set(pinned, from)
|
||||
|| squares_aligned(from, move_to(m), king_square(us));
|
||||
|| squares_aligned(from, to_sq(m), king_square(us));
|
||||
}
|
||||
|
||||
|
||||
@@ -515,9 +514,9 @@ 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);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Color them = ~sideToMove;
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
Piece pc = piece_on(from);
|
||||
|
||||
// Use a slower but simpler function for uncommon cases
|
||||
@@ -613,7 +612,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
||||
{
|
||||
Bitboard b = occupied_squares();
|
||||
clear_bit(&b, from);
|
||||
if (attackers_to(move_to(m), b) & pieces(flip(us)))
|
||||
if (attackers_to(to_sq(m), b) & pieces(~us))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
@@ -626,7 +625,7 @@ bool Position::is_pseudo_legal(const Move m) const {
|
||||
|
||||
// Our move must be a blocking evasion or a capture of the checking piece
|
||||
target = squares_between(checksq, king_square(us)) | checkers();
|
||||
if (!bit_is_set(target, move_to(m)))
|
||||
if (!bit_is_set(target, to_sq(m)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -641,10 +640,10 @@ 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(move_from(m))) == side_to_move());
|
||||
assert(color_of(piece_moved(m)) == sideToMove);
|
||||
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
PieceType pt = type_of(piece_on(from));
|
||||
|
||||
// Direct check ?
|
||||
@@ -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,10 +763,10 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI
|
||||
return;
|
||||
}
|
||||
|
||||
Color us = side_to_move();
|
||||
Color them = flip(us);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Color us = sideToMove;
|
||||
Color them = ~us;
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
Piece piece = piece_on(from);
|
||||
PieceType pt = type_of(piece);
|
||||
PieceType capture = is_enpassant(m) ? PAWN : type_of(piece_on(to));
|
||||
@@ -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,10 +979,10 @@ void Position::undo_move(Move m) {
|
||||
return;
|
||||
}
|
||||
|
||||
Color us = side_to_move();
|
||||
Color them = flip(us);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Color us = sideToMove;
|
||||
Color them = ~us;
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
Piece piece = piece_on(to);
|
||||
PieceType pt = type_of(piece);
|
||||
PieceType capture = st->capturedType;
|
||||
@@ -1076,9 +1075,9 @@ void Position::do_castle_move(Move m) {
|
||||
|
||||
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
|
||||
|
||||
Color us = side_to_move();
|
||||
Square kBefore = move_from(m);
|
||||
Square rBefore = move_to(m);
|
||||
Color us = sideToMove;
|
||||
Square kBefore = from_sq(m);
|
||||
Square rBefore = to_sq(m);
|
||||
|
||||
// Find after-castle squares for king and rook
|
||||
if (rBefore > kBefore) // O-O
|
||||
@@ -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)
|
||||
{
|
||||
@@ -1228,8 +1227,8 @@ int Position::see_sign(Move m) const {
|
||||
|
||||
assert(is_ok(m));
|
||||
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Square from = from_sq(m);
|
||||
Square to = to_sq(m);
|
||||
|
||||
// Early return if SEE cannot be negative because captured piece value
|
||||
// is not less then capturing one. Note that king moves always return
|
||||
@@ -1256,17 +1255,17 @@ int Position::see(Move m) const {
|
||||
if (is_castle(m))
|
||||
return 0;
|
||||
|
||||
from = move_from(m);
|
||||
to = move_to(m);
|
||||
from = from_sq(m);
|
||||
to = to_sq(m);
|
||||
capturedType = type_of(piece_on(to));
|
||||
occ = occupied_squares();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -428,8 +429,8 @@ inline Value Position::non_pawn_material(Color c) const {
|
||||
|
||||
inline bool Position::is_passed_pawn_push(Move m) const {
|
||||
|
||||
return board[move_from(m)] == make_piece(sideToMove, PAWN)
|
||||
&& pawn_is_passed(sideToMove, move_to(m));
|
||||
return board[from_sq(m)] == make_piece(sideToMove, PAWN)
|
||||
&& pawn_is_passed(sideToMove, to_sq(m));
|
||||
}
|
||||
|
||||
inline int Position::startpos_ply_counter() const {
|
||||
@@ -454,14 +455,14 @@ inline bool Position::is_chess960() const {
|
||||
inline bool Position::is_capture_or_promotion(Move m) const {
|
||||
|
||||
assert(is_ok(m));
|
||||
return is_special(m) ? !is_castle(m) : !square_is_empty(move_to(m));
|
||||
return is_special(m) ? !is_castle(m) : !square_is_empty(to_sq(m));
|
||||
}
|
||||
|
||||
inline bool Position::is_capture(Move m) const {
|
||||
|
||||
// Note that castle is coded as "king captures the rook"
|
||||
assert(is_ok(m));
|
||||
return (!square_is_empty(move_to(m)) && !is_castle(m)) || is_enpassant(m);
|
||||
return (!square_is_empty(to_sq(m)) && !is_castle(m)) || is_enpassant(m);
|
||||
}
|
||||
|
||||
inline PieceType Position::captured_piece_type() const {
|
||||
|
||||
178
src/search.cpp
178
src/search.cpp
@@ -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,14 +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;
|
||||
@@ -151,8 +123,6 @@ namespace {
|
||||
History H;
|
||||
|
||||
|
||||
/// Local functions
|
||||
|
||||
template <NodeType NT>
|
||||
Value search(Position& pos, Stack* ss, Value alpha, Value beta, Depth depth);
|
||||
|
||||
@@ -197,19 +167,19 @@ 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(move_from(m))) == PAWN)
|
||||
if (type_of(pos.piece_moved(m)) == PAWN)
|
||||
{
|
||||
Color c = pos.side_to_move();
|
||||
if ( relative_rank(c, move_to(m)) == RANK_7
|
||||
|| pos.pawn_is_passed(c, move_to(m)))
|
||||
if ( relative_rank(c, to_sq(m)) == RANK_7
|
||||
|| pos.pawn_is_passed(c, to_sq(m)))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Test for a capture that triggers a pawn endgame
|
||||
if ( captureOrPromotion
|
||||
&& type_of(pos.piece_on(move_to(m))) != PAWN
|
||||
&& type_of(pos.piece_on(to_sq(m))) != PAWN
|
||||
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
|
||||
- PieceValueMidgame[pos.piece_on(move_to(m))] == VALUE_ZERO)
|
||||
- PieceValueMidgame[pos.piece_on(to_sq(m))] == VALUE_ZERO)
|
||||
&& !is_special(m))
|
||||
return true;
|
||||
|
||||
@@ -279,34 +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())
|
||||
{
|
||||
if (book.name() != (string)Options["Book File"])
|
||||
book.open(Options["Book File"]);
|
||||
cout << "info depth 0 score "
|
||||
<< score_to_uci(pos.in_check() ? -VALUE_MATE : VALUE_DRAW) << endl;
|
||||
|
||||
Move bookMove = book.probe(pos, Options["Best Book Move"]);
|
||||
|
||||
if ( bookMove != MOVE_NONE
|
||||
&& count(RootMoves.begin(), RootMoves.end(), bookMove))
|
||||
{
|
||||
std::swap(RootMoves[0], *find(RootMoves.begin(), RootMoves.end(), bookMove));
|
||||
goto finish;
|
||||
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
|
||||
@@ -348,8 +313,8 @@ void Search::think() {
|
||||
|
||||
// Set best timer interval to avoid lagging under time pressure. Timer is
|
||||
// used to check for remaining available thinking time.
|
||||
if (TimeMgr.available_time())
|
||||
Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 8, 20)));
|
||||
if (Limits.use_time_management())
|
||||
Threads.set_timer(std::min(100, std::max(TimeMgr.available_time() / 16, TimerResolution)));
|
||||
else
|
||||
Threads.set_timer(100);
|
||||
|
||||
@@ -375,11 +340,11 @@ void Search::think() {
|
||||
pos.undo_move(RootMoves[0].pv[0]);
|
||||
}
|
||||
|
||||
finish:
|
||||
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();
|
||||
|
||||
@@ -408,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))
|
||||
{
|
||||
@@ -518,7 +473,7 @@ namespace {
|
||||
bestMoveNeverChanged = false;
|
||||
|
||||
// Do we have time for the next iteration? Can we stop searching now?
|
||||
if (!Signals.stop && !Signals.stopOnPonderhit && Limits.useTimeManagement())
|
||||
if (!Signals.stop && !Signals.stopOnPonderhit && Limits.use_time_management())
|
||||
{
|
||||
bool stop = false; // Local variable, not the volatile Signals.stop
|
||||
|
||||
@@ -533,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;
|
||||
|
||||
@@ -703,10 +658,10 @@ 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 = move_to(move);
|
||||
Square to = to_sq(move);
|
||||
H.update_gain(pos.piece_on(to), to, -(ss-1)->eval - ss->eval);
|
||||
}
|
||||
|
||||
@@ -870,7 +825,8 @@ split_point_start: // At split points actual search starts from here
|
||||
// Loop through all pseudo-legal moves until no moves remain or a beta cutoff occurs
|
||||
while ( bestValue < beta
|
||||
&& (move = mp.next_move()) != MOVE_NONE
|
||||
&& !thread.cutoff_occurred())
|
||||
&& !thread.cutoff_occurred()
|
||||
&& !Signals.stop)
|
||||
{
|
||||
assert(is_ok(move));
|
||||
|
||||
@@ -971,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(move_from(move)), move_to(move));
|
||||
+ H.gain(pos.piece_moved(move), to_sq(move));
|
||||
|
||||
if (futilityValue < beta)
|
||||
{
|
||||
@@ -1155,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(move_from(move)), move_to(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(move_from(m)), move_to(m), -bonus);
|
||||
H.add(pos.piece_moved(m), to_sq(m), -bonus);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1267,7 +1223,7 @@ split_point_start: // At split points actual search starts from here
|
||||
// to search the moves. Because the depth is <= 0 here, only captures,
|
||||
// queen promotions and checks (only if depth >= DEPTH_QS_CHECKS) will
|
||||
// be generated.
|
||||
MovePicker mp(pos, ttMove, depth, H, move_to((ss-1)->currentMove));
|
||||
MovePicker mp(pos, ttMove, depth, H, to_sq((ss-1)->currentMove));
|
||||
CheckInfo ci(pos);
|
||||
|
||||
// Loop through the moves until no moves remain or a beta cutoff occurs
|
||||
@@ -1288,7 +1244,7 @@ split_point_start: // At split points actual search starts from here
|
||||
&& !pos.is_passed_pawn_push(move))
|
||||
{
|
||||
futilityValue = futilityBase
|
||||
+ PieceValueEndgame[pos.piece_on(move_to(move))]
|
||||
+ PieceValueEndgame[pos.piece_on(to_sq(move))]
|
||||
+ (is_enpassant(move) ? PawnValueEndgame : VALUE_ZERO);
|
||||
|
||||
if (futilityValue < beta)
|
||||
@@ -1392,9 +1348,9 @@ split_point_start: // At split points actual search starts from here
|
||||
Color them;
|
||||
Value futilityValue, bv = *bestValue;
|
||||
|
||||
from = move_from(move);
|
||||
to = move_to(move);
|
||||
them = flip(pos.side_to_move());
|
||||
from = from_sq(move);
|
||||
to = to_sq(move);
|
||||
them = ~pos.side_to_move();
|
||||
ksq = pos.king_square(them);
|
||||
kingAtt = pos.attacks_from<KING>(ksq);
|
||||
pc = pos.piece_on(from);
|
||||
@@ -1453,14 +1409,14 @@ split_point_start: // At split points actual search starts from here
|
||||
assert(is_ok(m2));
|
||||
|
||||
// Case 1: The moving piece is the same in both moves
|
||||
f2 = move_from(m2);
|
||||
t1 = move_to(m1);
|
||||
f2 = from_sq(m2);
|
||||
t1 = to_sq(m1);
|
||||
if (f2 == t1)
|
||||
return true;
|
||||
|
||||
// Case 2: The destination square for m2 was vacated by m1
|
||||
t2 = move_to(m2);
|
||||
f1 = move_from(m1);
|
||||
t2 = to_sq(m2);
|
||||
f1 = from_sq(m1);
|
||||
if (t2 == f1)
|
||||
return true;
|
||||
|
||||
@@ -1533,10 +1489,10 @@ split_point_start: // At split points actual search starts from here
|
||||
|
||||
Square mfrom, mto, tfrom, tto;
|
||||
|
||||
mfrom = move_from(m);
|
||||
mto = move_to(m);
|
||||
tfrom = move_from(threat);
|
||||
tto = move_to(threat);
|
||||
mfrom = from_sq(m);
|
||||
mto = to_sq(m);
|
||||
tfrom = from_sq(threat);
|
||||
tto = to_sq(threat);
|
||||
|
||||
// Case 1: Don't prune moves which move the threatened piece
|
||||
if (mfrom == tto)
|
||||
@@ -1802,11 +1758,13 @@ split_point_start: // At split points actual search starts from here
|
||||
return best;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// extract_pv_from_tt() builds a PV by adding moves from the transposition table.
|
||||
// We consider also failing high nodes and not only VALUE_TYPE_EXACT nodes. This
|
||||
// allow to always have a ponder move even when we fail high at root and also a
|
||||
// long PV to print that is important for position analysis.
|
||||
|
||||
/// 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) {
|
||||
|
||||
@@ -1838,9 +1796,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
|
||||
// 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.
|
||||
/// 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) {
|
||||
|
||||
@@ -1869,8 +1827,6 @@ split_point_start: // At split points actual search starts from here
|
||||
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.
|
||||
/// The parameter 'sp', if non-NULL, is a pointer to an active SplitPoint object
|
||||
@@ -1962,11 +1918,11 @@ void Thread::idle_loop(SplitPoint* sp) {
|
||||
}
|
||||
|
||||
|
||||
/// do_timer_event() is called by the timer thread when the timer triggers. It
|
||||
/// is used to print debug info and, more important, to detect when we are out of
|
||||
/// check_time() is called by the timer thread when the timer triggers. It is
|
||||
/// used to print debug info and, more important, to detect when we are out of
|
||||
/// available time and so stop the search.
|
||||
|
||||
void do_timer_event() {
|
||||
void check_time() {
|
||||
|
||||
static int lastInfoTime;
|
||||
int e = elapsed_time();
|
||||
@@ -1984,10 +1940,10 @@ void do_timer_event() {
|
||||
&& !Signals.failedLowAtRoot
|
||||
&& e > TimeMgr.available_time();
|
||||
|
||||
bool noMoreTime = e > TimeMgr.maximum_time()
|
||||
bool noMoreTime = e > TimeMgr.maximum_time() - 2 * TimerResolution
|
||||
|| stillAtFirstMove;
|
||||
|
||||
if ( (Limits.useTimeManagement() && noMoreTime)
|
||||
if ( (Limits.use_time_management() && noMoreTime)
|
||||
|| (Limits.maxTime && e >= Limits.maxTime)
|
||||
/* missing nodes limit */ ) // FIXME
|
||||
Signals.stop = true;
|
||||
|
||||
29
src/search.h
29
src/search.h
@@ -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
|
||||
/// 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.
|
||||
@@ -55,7 +78,7 @@ struct Stack {
|
||||
struct LimitsType {
|
||||
|
||||
LimitsType() { memset(this, 0, sizeof(LimitsType)); }
|
||||
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | infinite); }
|
||||
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)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "movegen.h"
|
||||
#include "search.h"
|
||||
#include "thread.h"
|
||||
#include "ucioption.h"
|
||||
@@ -172,7 +173,7 @@ void ThreadsManager::init() {
|
||||
for (int i = 0; i <= MAX_THREADS; i++)
|
||||
{
|
||||
threads[i].is_searching = false;
|
||||
threads[i].do_sleep = true;
|
||||
threads[i].do_sleep = (i != 0); // Avoid a race with start_thinking()
|
||||
threads[i].threadID = i;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
@@ -202,7 +203,7 @@ void ThreadsManager::exit() {
|
||||
|
||||
// Wait for thread termination
|
||||
#if defined(_MSC_VER)
|
||||
WaitForSingleObject(threads[i].handle, 0);
|
||||
WaitForSingleObject(threads[i].handle, INFINITE);
|
||||
CloseHandle(threads[i].handle);
|
||||
#else
|
||||
pthread_join(threads[i].handle, NULL);
|
||||
@@ -367,7 +368,7 @@ template Value ThreadsManager::split<true>(Position&, Stack*, Value, Value, Valu
|
||||
|
||||
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
|
||||
// then calls do_timer_event(). If maxPly is 0 thread sleeps until is woken up.
|
||||
extern void do_timer_event();
|
||||
extern void check_time();
|
||||
|
||||
void Thread::timer_loop() {
|
||||
|
||||
@@ -376,7 +377,7 @@ void Thread::timer_loop() {
|
||||
lock_grab(&sleepLock);
|
||||
timed_wait(&sleepCond, &sleepLock, maxPly ? maxPly : INT_MAX);
|
||||
lock_release(&sleepLock);
|
||||
do_timer_event();
|
||||
check_time();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,23 @@ 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);
|
||||
|
||||
lock_release(&main.sleepLock);
|
||||
|
||||
@@ -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,
|
||||
|
||||
35
src/types.h
35
src/types.h
@@ -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);
|
||||
}
|
||||
@@ -432,11 +427,11 @@ inline Square pawn_push(Color c) {
|
||||
return c == WHITE ? DELTA_N : DELTA_S;
|
||||
}
|
||||
|
||||
inline Square move_from(Move m) {
|
||||
inline Square from_sq(Move m) {
|
||||
return Square((m >> 6) & 0x3F);
|
||||
}
|
||||
|
||||
inline Square move_to(Move m) {
|
||||
inline Square to_sq(Move m) {
|
||||
return Square(m & 0x3F);
|
||||
}
|
||||
|
||||
@@ -464,20 +459,20 @@ inline Move make_move(Square from, Square to) {
|
||||
return Move(to | (from << 6));
|
||||
}
|
||||
|
||||
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
|
||||
return Move(to | (from << 6) | (1 << 14) | ((promotion - 2) << 12)) ;
|
||||
inline Move make_promotion(Square from, Square to, PieceType pt) {
|
||||
return Move(to | (from << 6) | (1 << 14) | ((pt - 2) << 12)) ;
|
||||
}
|
||||
|
||||
inline Move make_enpassant_move(Square from, Square to) {
|
||||
inline Move make_enpassant(Square from, Square to) {
|
||||
return Move(to | (from << 6) | (2 << 14));
|
||||
}
|
||||
|
||||
inline Move make_castle_move(Square from, Square to) {
|
||||
inline Move make_castle(Square from, Square to) {
|
||||
return Move(to | (from << 6) | (3 << 14));
|
||||
}
|
||||
|
||||
inline bool is_ok(Move m) {
|
||||
return move_from(m) != move_to(m); // Catches also MOVE_NULL and MOVE_NONE
|
||||
return from_sq(m) != to_sq(m); // Catches also MOVE_NULL and MOVE_NONE
|
||||
}
|
||||
|
||||
#include <string>
|
||||
|
||||
@@ -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()];
|
||||
|
||||
Reference in New Issue
Block a user