mirror of
https://github.com/sockspls/badfish
synced 2025-04-29 16:23:09 +00:00
Simplify Book APIs
Retire open(), close() and name() from public visibility and greately simplify the code. It is amazing how much can be squeezed out of an already mature code ! No functional change Signed-off-by: Marco Costalba <mcostalba@gmail.com>
This commit is contained in:
parent
c00443b19e
commit
8300ab149c
4 changed files with 100 additions and 147 deletions
223
src/book.cpp
223
src/book.cpp
|
@ -17,13 +17,13 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
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
|
The code in this file is based on the opening book code in PolyGlot
|
||||||
by Fabien Letouzey. PolyGlot is available under the GNU General
|
by Fabien Letouzey. PolyGlot is available under the GNU General
|
||||||
Public License, and can be downloaded from http://wbec-ridderkerk.nl
|
Public License, and can be downloaded from http://wbec-ridderkerk.nl
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
@ -306,130 +306,123 @@ namespace {
|
||||||
const Key* ZobEnPassant = PolyGlotRandoms + 772;
|
const Key* ZobEnPassant = PolyGlotRandoms + 772;
|
||||||
const Key* ZobTurn = PolyGlotRandoms + 780;
|
const Key* ZobTurn = PolyGlotRandoms + 780;
|
||||||
|
|
||||||
// Piece offset is calculated as 64 * (PolyPiece ^ 1) where
|
// PieceOffset is calculated as 64 * (PolyPiece ^ 1) where PolyPiece
|
||||||
// PolyPiece is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
|
// is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
|
||||||
const int PieceOfs[] = { 0, 64, 192, 320, 448, 576, 704, 0,
|
const int PieceOffset[] = { 0, 64, 192, 320, 448, 576, 704, 0,
|
||||||
0, 0, 128, 256, 384, 512, 640 };
|
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 book_key(const Position& pos) {
|
||||||
|
|
||||||
uint64_t result = 0;
|
uint64_t key = 0;
|
||||||
Bitboard b = pos.occupied_squares();
|
Bitboard b = pos.occupied_squares();
|
||||||
|
|
||||||
while (b)
|
while (b)
|
||||||
{
|
{
|
||||||
Square s = pop_1st_bit(&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))
|
b = (pos.can_castle(WHITE_OO) << 0) | (pos.can_castle(WHITE_OOO) << 1)
|
||||||
result ^= ZobCastle[0];
|
| (pos.can_castle(BLACK_OO) << 2) | (pos.can_castle(BLACK_OOO) << 3);
|
||||||
|
|
||||||
if (pos.can_castle(WHITE_OOO))
|
while (b)
|
||||||
result ^= ZobCastle[1];
|
key ^= ZobCastle[pop_1st_bit(&b)];
|
||||||
|
|
||||||
if (pos.can_castle(BLACK_OO))
|
|
||||||
result ^= ZobCastle[2];
|
|
||||||
|
|
||||||
if (pos.can_castle(BLACK_OOO))
|
|
||||||
result ^= ZobCastle[3];
|
|
||||||
|
|
||||||
if (pos.ep_square() != SQ_NONE)
|
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)
|
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() : size(0) {
|
||||||
Book::Book() : bookSize(0) {
|
|
||||||
|
|
||||||
for (int i = abs(system_time() % 10000); i > 0; i--)
|
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 = "";
|
||||||
}
|
|
||||||
|
|
||||||
|
if (is_open()) // Cannot close an already closed file
|
||||||
|
close();
|
||||||
|
|
||||||
/// Book::close() closes the file only if it is open, otherwise the call fails
|
ifstream::open(fName, ifstream::in | ifstream::binary | ios::ate);
|
||||||
/// and the failbit internal state flag is set.
|
|
||||||
|
|
||||||
void Book::close() {
|
if (!is_open())
|
||||||
|
return false; // Silently fail if the file is not found
|
||||||
|
|
||||||
if (bookFile.is_open())
|
// Get the book size in number of entries, we are already at the end of file
|
||||||
bookFile.close();
|
size = tellg() / sizeof(BookEntry);
|
||||||
|
|
||||||
bookName = "";
|
if (!good())
|
||||||
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
|
|
||||||
close();
|
|
||||||
|
|
||||||
bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary |ios::ate);
|
|
||||||
|
|
||||||
// Silently return when asked to open a non-exsistent file
|
|
||||||
if (!bookFile.is_open())
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get the book size in number of entries, we are already at the file end
|
|
||||||
bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
|
|
||||||
|
|
||||||
if (!bookFile.good())
|
|
||||||
{
|
{
|
||||||
cerr << "Failed to open book file " << fileName << endl;
|
cerr << "Failed to open book file " << fName << endl;
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set only if successful
|
fileName = fName; // Set only if successful
|
||||||
bookName = fileName;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Book::probe() gets a book move for a given position. Returns MOVE_NONE
|
/// Book::probe() tries to find a book move for the given position. If no move
|
||||||
/// if no book move is found. If findBest is true then returns always the
|
/// is found returns MOVE_NONE. If pickBest is true returns always the highest
|
||||||
/// highest rated move otherwise chooses randomly based on the move score.
|
/// 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, bookMove = 0;
|
||||||
|
uint64_t key = book_key(pos);
|
||||||
|
|
||||||
|
if (fileName != fName && !open(fName.c_str()))
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
|
|
||||||
BookEntry entry;
|
binary_search(key);
|
||||||
unsigned scoresSum = 0, bestScore = 0, bookMove = 0;
|
|
||||||
uint64_t key = book_key(pos);
|
|
||||||
int idx = first_entry(key) - 1;
|
|
||||||
|
|
||||||
// Choose a book move among the possible moves for the given position
|
while (*this >> e, e.key == key && good())
|
||||||
while (++idx < bookSize && (entry = read_entry(idx), entry.key == key))
|
|
||||||
{
|
{
|
||||||
scoresSum += entry.count;
|
best = max(best, e.count);
|
||||||
|
sum += e.count;
|
||||||
|
|
||||||
// Choose book move according to its score. If a move has a very
|
// 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
|
// high score it has higher probability to be choosen than a move
|
||||||
// with lower score. Note that first entry is always chosen.
|
// with lower score. Note that first entry is always chosen.
|
||||||
if ( RKiss.rand<unsigned>() % scoresSum < entry.count
|
if ( (RKiss.rand<unsigned>() % sum < e.count)
|
||||||
|| (findBest && entry.count > bestScore))
|
|| (pickBest && e.count == best))
|
||||||
bookMove = entry.move;
|
bookMove = e.move;
|
||||||
|
|
||||||
if (entry.count > bestScore)
|
|
||||||
bestScore = entry.count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bookMove)
|
if (!bookMove)
|
||||||
|
@ -439,49 +432,52 @@ Move Book::probe(const Position& pos, bool findBest) {
|
||||||
//
|
//
|
||||||
// bit 0- 5: destination square (from 0 to 63)
|
// bit 0- 5: destination square (from 0 to 63)
|
||||||
// bit 6-11: origin 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
|
// Castling moves follow "king captures rook" representation. So in case book
|
||||||
// book move is a promotion we have to convert to our representation, in
|
// move is a promotion we have to convert to our representation, in all the
|
||||||
// all other cases we can directly compare with a Move after having
|
// other cases we can directly compare with a Move after having masked out
|
||||||
// masked out special Move's flags that are not supported by PolyGlot.
|
// the special Move's flags (bit 14-15) that are not supported by PolyGlot.
|
||||||
int promotion = (bookMove >> 12) & 7;
|
int promotion = (bookMove >> 12) & 7;
|
||||||
|
|
||||||
if (promotion)
|
if (promotion)
|
||||||
bookMove = make_promotion_move(move_from(Move(bookMove)),
|
bookMove = make_promotion_move(move_from(Move(bookMove)),
|
||||||
move_to(Move(bookMove)),
|
move_to(Move(bookMove)),
|
||||||
PieceType(promotion + 1));
|
PieceType(promotion + 1));
|
||||||
// Verify the book move is legal
|
|
||||||
|
// Convert bookMove to our internal Move format (and verify it is legal)
|
||||||
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
|
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
|
||||||
if (unsigned(ml.move() & ~(3 << 14)) == bookMove) // Mask out special flags
|
if (bookMove == unsigned(ml.move() & ~(3 << 14)))
|
||||||
return ml.move();
|
return ml.move();
|
||||||
|
|
||||||
return MOVE_NONE;
|
return MOVE_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Book::first_entry() takes a book key as input, and does a binary search
|
/// Book::binary_search() 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)
|
/// through the book file for the given key. File stream current position is set
|
||||||
/// book entry with the same key as the input is returned. When the key is not
|
/// to the leftmost book entry with the same key as the input.
|
||||||
/// found in the book file, bookSize is returned.
|
|
||||||
|
|
||||||
int Book::first_entry(uint64_t key) {
|
void Book::binary_search(uint64_t key) {
|
||||||
|
|
||||||
int left, right, mid;
|
size_t left, right, mid;
|
||||||
|
BookEntry e;
|
||||||
|
|
||||||
// Binary search (finds the leftmost entry with given key)
|
|
||||||
left = 0;
|
left = 0;
|
||||||
right = bookSize - 1;
|
right = size - 1;
|
||||||
|
|
||||||
assert(left <= right);
|
assert(left <= right);
|
||||||
|
|
||||||
while (left < right)
|
while (left < right && good())
|
||||||
{
|
{
|
||||||
mid = (left + right) / 2;
|
mid = (left + right) / 2;
|
||||||
|
|
||||||
assert(mid >= left && mid < right);
|
assert(mid >= left && mid < right);
|
||||||
|
|
||||||
if (key <= read_entry(mid).key)
|
seekg(mid * sizeof(BookEntry), ios_base::beg);
|
||||||
|
*this >> e;
|
||||||
|
|
||||||
|
if (key <= e.key)
|
||||||
right = mid;
|
right = mid;
|
||||||
else
|
else
|
||||||
left = mid + 1;
|
left = mid + 1;
|
||||||
|
@ -489,42 +485,5 @@ int Book::first_entry(uint64_t key) {
|
||||||
|
|
||||||
assert(left == right);
|
assert(left == right);
|
||||||
|
|
||||||
return read_entry(left).key == key ? left : bookSize;
|
seekg(left * sizeof(BookEntry), ios_base::beg);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// 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());
|
|
||||||
|
|
||||||
BookEntry e;
|
|
||||||
|
|
||||||
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg);
|
|
||||||
|
|
||||||
*this >> e.key >> e.move >> e.count >> e.learn;
|
|
||||||
|
|
||||||
if (!bookFile.good())
|
|
||||||
{
|
|
||||||
cerr << "Failed to read book entry at index " << idx << endl;
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
17
src/book.h
17
src/book.h
|
@ -37,25 +37,22 @@ struct BookEntry {
|
||||||
uint32_t learn;
|
uint32_t learn;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Book {
|
|
||||||
|
class Book : private std::ifstream {
|
||||||
public:
|
public:
|
||||||
Book();
|
Book();
|
||||||
~Book();
|
~Book();
|
||||||
void open(const std::string& fileName);
|
Move probe(const Position& pos, const std::string& fName, bool pickBest);
|
||||||
void close();
|
|
||||||
Move probe(const Position& pos, bool findBestMove);
|
|
||||||
const std::string name() const { return bookName; }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template<typename T> Book& operator>>(T& n);
|
template<typename T> Book& operator>>(T& n);
|
||||||
|
|
||||||
BookEntry read_entry(int idx);
|
bool open(const char* fName);
|
||||||
int first_entry(uint64_t key);
|
void binary_search(uint64_t key);
|
||||||
|
|
||||||
RKISS RKiss;
|
RKISS RKiss;
|
||||||
std::ifstream bookFile;
|
std::string fileName;
|
||||||
std::string bookName;
|
size_t size;
|
||||||
int bookSize;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // !defined(BOOK_H_INCLUDED)
|
#endif // !defined(BOOK_H_INCLUDED)
|
||||||
|
|
|
@ -56,7 +56,7 @@ using namespace std;
|
||||||
/// date (in the format YYMMDD) is used as a version number.
|
/// date (in the format YYMMDD) is used as a version number.
|
||||||
|
|
||||||
static const string Version = "";
|
static const string Version = "";
|
||||||
static const string Tag = "";
|
static const string Tag = "";
|
||||||
|
|
||||||
|
|
||||||
/// engine_info() returns the full name of the current Stockfish version.
|
/// engine_info() returns the full name of the current Stockfish version.
|
||||||
|
|
|
@ -296,10 +296,7 @@ void Search::think() {
|
||||||
|
|
||||||
if (Options["OwnBook"])
|
if (Options["OwnBook"])
|
||||||
{
|
{
|
||||||
if (book.name() != (string)Options["Book File"])
|
Move bookMove = book.probe(pos, Options["Book File"], Options["Best Book Move"]);
|
||||||
book.open(Options["Book File"]);
|
|
||||||
|
|
||||||
Move bookMove = book.probe(pos, Options["Best Book Move"]);
|
|
||||||
|
|
||||||
if ( bookMove != MOVE_NONE
|
if ( bookMove != MOVE_NONE
|
||||||
&& count(RootMoves.begin(), RootMoves.end(), bookMove))
|
&& count(RootMoves.begin(), RootMoves.end(), bookMove))
|
||||||
|
|
Loading…
Add table
Reference in a new issue