mirror of
https://github.com/sockspls/badfish
synced 2025-07-11 11:39:15 +00:00
Better naming in endgame code
And small clean-up of magic bitboards code. No functional change. Closes #1138
This commit is contained in:
parent
f907d5b7d9
commit
27ba611a3d
8 changed files with 75 additions and 92 deletions
|
@ -452,7 +452,7 @@ install:
|
||||||
|
|
||||||
#clean all
|
#clean all
|
||||||
clean: objclean profileclean
|
clean: objclean profileclean
|
||||||
@rm -f .depend *~ core
|
@rm -f .depend *~ core
|
||||||
|
|
||||||
# clean binaries and objects
|
# clean binaries and objects
|
||||||
objclean:
|
objclean:
|
||||||
|
|
|
@ -26,9 +26,6 @@
|
||||||
uint8_t PopCnt16[1 << 16];
|
uint8_t PopCnt16[1 << 16];
|
||||||
int SquareDistance[SQUARE_NB][SQUARE_NB];
|
int SquareDistance[SQUARE_NB][SQUARE_NB];
|
||||||
|
|
||||||
Magic RookMagics[SQUARE_NB];
|
|
||||||
Magic BishopMagics[SQUARE_NB];
|
|
||||||
|
|
||||||
Bitboard SquareBB[SQUARE_NB];
|
Bitboard SquareBB[SQUARE_NB];
|
||||||
Bitboard FileBB[FILE_NB];
|
Bitboard FileBB[FILE_NB];
|
||||||
Bitboard RankBB[RANK_NB];
|
Bitboard RankBB[RANK_NB];
|
||||||
|
@ -43,6 +40,9 @@ Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
|
||||||
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||||
|
|
||||||
|
Magic RookMagics[SQUARE_NB];
|
||||||
|
Magic BishopMagics[SQUARE_NB];
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
|
// De Bruijn sequences. See chessprogramming.wikispaces.com/BitScan
|
||||||
|
@ -54,7 +54,7 @@ namespace {
|
||||||
Bitboard RookTable[0x19000]; // To store rook attacks
|
Bitboard RookTable[0x19000]; // To store rook attacks
|
||||||
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
Bitboard BishopTable[0x1480]; // To store bishop attacks
|
||||||
|
|
||||||
typedef unsigned (Fn)(Square, Bitboard);
|
typedef unsigned (Fn)(const Magic&, Bitboard);
|
||||||
|
|
||||||
void init_magics(Bitboard table[], Magic magics[], Square deltas[], Fn index);
|
void init_magics(Bitboard table[], Magic magics[], Square deltas[], Fn index);
|
||||||
|
|
||||||
|
@ -204,8 +204,8 @@ void Bitboards::init() {
|
||||||
Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST };
|
Square RookDeltas[] = { NORTH, EAST, SOUTH, WEST };
|
||||||
Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
|
Square BishopDeltas[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
|
||||||
|
|
||||||
init_magics(RookTable, RookMagics, RookDeltas, magic_index<ROOK>);
|
init_magics(RookTable, RookMagics, RookDeltas, magic_index);
|
||||||
init_magics(BishopTable, BishopMagics, BishopDeltas, magic_index<BISHOP>);
|
init_magics(BishopTable, BishopMagics, BishopDeltas, magic_index);
|
||||||
|
|
||||||
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
||||||
{
|
{
|
||||||
|
@ -257,10 +257,7 @@ namespace {
|
||||||
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
|
{ 728, 10316, 55013, 32803, 12281, 15100, 16645, 255 } };
|
||||||
|
|
||||||
Bitboard occupancy[4096], reference[4096], edges, b;
|
Bitboard occupancy[4096], reference[4096], edges, b;
|
||||||
int age[4096] = {0}, current = 0, i, size;
|
int epoch[4096] = {}, cnt = 0, size = 0;
|
||||||
|
|
||||||
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
|
|
||||||
magics[SQ_A1].attacks = table;
|
|
||||||
|
|
||||||
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
||||||
{
|
{
|
||||||
|
@ -272,8 +269,13 @@ namespace {
|
||||||
// all the attacks for each possible subset of the mask and so is 2 power
|
// all the attacks for each possible subset of the mask and so is 2 power
|
||||||
// the number of 1s of the mask. Hence we deduce the size of the shift to
|
// the number of 1s of the mask. Hence we deduce the size of the shift to
|
||||||
// apply to the 64 or 32 bits word to get the index.
|
// apply to the 64 or 32 bits word to get the index.
|
||||||
magics[s].mask = sliding_attack(deltas, s, 0) & ~edges;
|
Magic& m = magics[s];
|
||||||
magics[s].shift = (Is64Bit ? 64 : 32) - popcount(magics[s].mask);
|
m.mask = sliding_attack(deltas, s, 0) & ~edges;
|
||||||
|
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
|
||||||
|
|
||||||
|
// Set the offset for the attacks table of the square. We have individual
|
||||||
|
// table sizes for each square with "Fancy Magic Bitboards".
|
||||||
|
m.attacks = s == SQ_A1 ? table : magics[s - 1].attacks + size;
|
||||||
|
|
||||||
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
|
||||||
// store the corresponding sliding attack bitboard in reference[].
|
// store the corresponding sliding attack bitboard in reference[].
|
||||||
|
@ -283,17 +285,12 @@ namespace {
|
||||||
reference[size] = sliding_attack(deltas, s, b);
|
reference[size] = sliding_attack(deltas, s, b);
|
||||||
|
|
||||||
if (HasPext)
|
if (HasPext)
|
||||||
magics[s].attacks[pext(b, magics[s].mask)] = reference[size];
|
m.attacks[pext(b, m.mask)] = reference[size];
|
||||||
|
|
||||||
size++;
|
size++;
|
||||||
b = (b - magics[s].mask) & magics[s].mask;
|
b = (b - m.mask) & m.mask;
|
||||||
} while (b);
|
} while (b);
|
||||||
|
|
||||||
// Set the offset for the table of the next square. We have individual
|
|
||||||
// table sizes for each square with "Fancy Magic Bitboards".
|
|
||||||
if (s < SQ_H8)
|
|
||||||
magics[s + 1].attacks = magics[s].attacks + size;
|
|
||||||
|
|
||||||
if (HasPext)
|
if (HasPext)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -301,28 +298,30 @@ namespace {
|
||||||
|
|
||||||
// Find a magic for square 's' picking up an (almost) random number
|
// Find a magic for square 's' picking up an (almost) random number
|
||||||
// until we find the one that passes the verification test.
|
// until we find the one that passes the verification test.
|
||||||
do {
|
for (int i = 0; i < size; )
|
||||||
do
|
{
|
||||||
magics[s].magic = rng.sparse_rand<Bitboard>();
|
for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6; )
|
||||||
while (popcount((magics[s].magic * magics[s].mask) >> 56) < 6);
|
m.magic = rng.sparse_rand<Bitboard>();
|
||||||
|
|
||||||
// A good magic must map every possible occupancy to an index that
|
// A good magic must map every possible occupancy to an index that
|
||||||
// looks up the correct sliding attack in the attacks[s] database.
|
// looks up the correct sliding attack in the attacks[s] database.
|
||||||
// Note that we build up the database for square 's' as a side
|
// Note that we build up the database for square 's' as a side
|
||||||
// effect of verifying the magic.
|
// effect of verifying the magic. Keep track of the attempt count
|
||||||
for (++current, i = 0; i < size; ++i)
|
// and save it in epoch[], little speed-up trick to avoid resetting
|
||||||
|
// m.attacks[] after every failed attempt.
|
||||||
|
for (++cnt, i = 0; i < size; ++i)
|
||||||
{
|
{
|
||||||
unsigned idx = index(s, occupancy[i]);
|
unsigned idx = index(m, occupancy[i]);
|
||||||
|
|
||||||
if (age[idx] < current)
|
if (epoch[idx] < cnt)
|
||||||
{
|
{
|
||||||
age[idx] = current;
|
epoch[idx] = cnt;
|
||||||
magics[s].attacks[idx] = reference[i];
|
m.attacks[idx] = reference[i];
|
||||||
}
|
}
|
||||||
else if (magics[s].attacks[idx] != reference[i])
|
else if (m.attacks[idx] != reference[i])
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (i < size);
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,18 @@ extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
|
||||||
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
|
||||||
|
|
||||||
|
|
||||||
|
/// Magic holds all magic bitboards relevant data for a single square
|
||||||
|
struct Magic {
|
||||||
|
Bitboard mask;
|
||||||
|
Bitboard magic;
|
||||||
|
Bitboard* attacks;
|
||||||
|
unsigned shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Magic RookMagics[SQUARE_NB];
|
||||||
|
extern Magic BishopMagics[SQUARE_NB];
|
||||||
|
|
||||||
|
|
||||||
/// Overloads of bitwise operators between a Bitboard and a Square for testing
|
/// Overloads of bitwise operators between a Bitboard and a Square for testing
|
||||||
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
|
||||||
|
|
||||||
|
@ -209,47 +221,27 @@ template<> inline int distance<File>(Square x, Square y) { return distance(file_
|
||||||
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
|
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
|
||||||
|
|
||||||
|
|
||||||
/// Magic holds all magic relevant data for a single square
|
|
||||||
struct Magic {
|
|
||||||
|
|
||||||
Bitboard mask;
|
|
||||||
Bitboard magic;
|
|
||||||
Bitboard* attacks;
|
|
||||||
unsigned shift;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// attacks_bb() returns a bitboard representing all the squares attacked by a
|
/// attacks_bb() returns a bitboard representing all the squares attacked by a
|
||||||
/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index()
|
/// piece of type Pt (bishop or rook) placed on 's'. The helper magic_index()
|
||||||
/// looks up the index using the 'magic bitboards' approach.
|
/// looks up the index using the 'magic bitboards' approach.
|
||||||
template<PieceType Pt>
|
inline unsigned magic_index(const Magic& m, Bitboard occupied) {
|
||||||
inline unsigned magic_index(Square s, Bitboard occupied) {
|
|
||||||
|
|
||||||
extern Magic RookMagics[SQUARE_NB];
|
|
||||||
extern Magic BishopMagics[SQUARE_NB];
|
|
||||||
|
|
||||||
const Magic* Magics = Pt == ROOK ? RookMagics : BishopMagics;
|
|
||||||
Bitboard mask = Magics[s].mask;
|
|
||||||
Bitboard magic = Magics[s].magic;
|
|
||||||
unsigned shift = Magics[s].shift;
|
|
||||||
|
|
||||||
if (HasPext)
|
if (HasPext)
|
||||||
return unsigned(pext(occupied, mask));
|
return unsigned(pext(occupied, m.mask));
|
||||||
|
|
||||||
if (Is64Bit)
|
if (Is64Bit)
|
||||||
return unsigned(((occupied & mask) * magic) >> shift);
|
return unsigned(((occupied & m.mask) * m.magic) >> m.shift);
|
||||||
|
|
||||||
unsigned lo = unsigned(occupied) & unsigned(mask);
|
unsigned lo = unsigned(occupied) & unsigned(m.mask);
|
||||||
unsigned hi = unsigned(occupied >> 32) & unsigned(mask >> 32);
|
unsigned hi = unsigned(occupied >> 32) & unsigned(m.mask >> 32);
|
||||||
return (lo * unsigned(magic) ^ hi * unsigned(magic >> 32)) >> shift;
|
return (lo * unsigned(m.magic) ^ hi * unsigned(m.magic >> 32)) >> m.shift;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<PieceType Pt>
|
template<PieceType Pt>
|
||||||
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
|
||||||
|
|
||||||
extern Magic RookMagics[SQUARE_NB];
|
const Magic& M = Pt == ROOK ? RookMagics[s] : BishopMagics[s];
|
||||||
extern Magic BishopMagics[SQUARE_NB];
|
return M.attacks[magic_index(M, occupied)];
|
||||||
|
|
||||||
return (Pt == ROOK ? RookMagics : BishopMagics)[s].attacks[magic_index<Pt>(s, occupied)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
|
inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
|
||||||
|
|
|
@ -110,14 +110,6 @@ Endgames::Endgames() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<EndgameType E, typename T>
|
|
||||||
void Endgames::add(const string& code) {
|
|
||||||
StateInfo st;
|
|
||||||
map<T>()[Position().set(code, WHITE, &st).material_key()] = std::unique_ptr<EndgameBase<T>>(new Endgame<E>(WHITE));
|
|
||||||
map<T>()[Position().set(code, BLACK, &st).material_key()] = std::unique_ptr<EndgameBase<T>>(new Endgame<E>(BLACK));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Mate with KX vs K. This function is used to evaluate positions with
|
/// Mate with KX vs K. This function is used to evaluate positions with
|
||||||
/// king and plenty of material vs a lone king. It simply gives the
|
/// king and plenty of material vs a lone king. It simply gives the
|
||||||
/// attacking side a bonus for driving the defending king towards the edge
|
/// attacking side a bonus for driving the defending king towards the edge
|
||||||
|
|
|
@ -31,12 +31,11 @@
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
|
||||||
/// EndgameType lists all supported endgames
|
/// EndgameCode lists all supported endgame functions by corresponding codes
|
||||||
|
|
||||||
enum EndgameType {
|
enum EndgameCode {
|
||||||
|
|
||||||
// Evaluation functions
|
|
||||||
|
|
||||||
|
EVALUATION_FUNCTIONS,
|
||||||
KNNK, // KNN vs K
|
KNNK, // KNN vs K
|
||||||
KXK, // Generic "mate lone king" eval
|
KXK, // Generic "mate lone king" eval
|
||||||
KBNK, // KBN vs K
|
KBNK, // KBN vs K
|
||||||
|
@ -47,10 +46,7 @@ enum EndgameType {
|
||||||
KQKP, // KQ vs KP
|
KQKP, // KQ vs KP
|
||||||
KQKR, // KQ vs KR
|
KQKR, // KQ vs KR
|
||||||
|
|
||||||
|
|
||||||
// Scaling functions
|
|
||||||
SCALING_FUNCTIONS,
|
SCALING_FUNCTIONS,
|
||||||
|
|
||||||
KBPsK, // KB and pawns vs K
|
KBPsK, // KB and pawns vs K
|
||||||
KQKRPs, // KQ vs KR and pawns
|
KQKRPs, // KQ vs KR and pawns
|
||||||
KRPKR, // KRP vs KR
|
KRPKR, // KRP vs KR
|
||||||
|
@ -68,30 +64,28 @@ enum EndgameType {
|
||||||
|
|
||||||
/// Endgame functions can be of two types depending on whether they return a
|
/// Endgame functions can be of two types depending on whether they return a
|
||||||
/// Value or a ScaleFactor.
|
/// Value or a ScaleFactor.
|
||||||
template<EndgameType E> using
|
template<EndgameCode E> using
|
||||||
eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
|
eg_type = typename std::conditional<(E < SCALING_FUNCTIONS), Value, ScaleFactor>::type;
|
||||||
|
|
||||||
|
|
||||||
/// Base and derived templates for endgame evaluation and scaling functions
|
/// Base and derived functors for endgame evaluation and scaling functions
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct EndgameBase {
|
struct EndgameBase {
|
||||||
|
|
||||||
|
explicit EndgameBase(Color c) : strongSide(c), weakSide(~c) {}
|
||||||
virtual ~EndgameBase() = default;
|
virtual ~EndgameBase() = default;
|
||||||
virtual Color strong_side() const = 0;
|
|
||||||
virtual T operator()(const Position&) const = 0;
|
virtual T operator()(const Position&) const = 0;
|
||||||
|
|
||||||
|
const Color strongSide, weakSide;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
template<EndgameType E, typename T = eg_type<E>>
|
template<EndgameCode E, typename T = eg_type<E>>
|
||||||
struct Endgame : public EndgameBase<T> {
|
struct Endgame : public EndgameBase<T> {
|
||||||
|
|
||||||
explicit Endgame(Color c) : strongSide(c), weakSide(~c) {}
|
explicit Endgame(Color c) : EndgameBase<T>(c) {}
|
||||||
Color strong_side() const { return strongSide; }
|
|
||||||
T operator()(const Position&) const;
|
T operator()(const Position&) const;
|
||||||
|
|
||||||
private:
|
|
||||||
Color strongSide, weakSide;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -101,16 +95,22 @@ private:
|
||||||
|
|
||||||
class Endgames {
|
class Endgames {
|
||||||
|
|
||||||
template<typename T> using Map = std::map<Key, std::unique_ptr<EndgameBase<T>>>;
|
template<typename T> using Ptr = std::unique_ptr<EndgameBase<T>>;
|
||||||
|
template<typename T> using Map = std::map<Key, Ptr<T>>;
|
||||||
template<EndgameType E, typename T = eg_type<E>>
|
|
||||||
void add(const std::string& code);
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
Map<T>& map() {
|
Map<T>& map() {
|
||||||
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
|
return std::get<std::is_same<T, ScaleFactor>::value>(maps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<EndgameCode E, typename T = eg_type<E>, typename P = Ptr<T>>
|
||||||
|
void add(const std::string& code) {
|
||||||
|
|
||||||
|
StateInfo st;
|
||||||
|
map<T>()[Position().set(code, WHITE, &st).material_key()] = P(new Endgame<E>(WHITE));
|
||||||
|
map<T>()[Position().set(code, BLACK, &st).material_key()] = P(new Endgame<E>(BLACK));
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<Map<Value>, Map<ScaleFactor>> maps;
|
std::pair<Map<Value>, Map<ScaleFactor>> maps;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -155,7 +155,7 @@ Entry* probe(const Position& pos) {
|
||||||
|
|
||||||
if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
|
if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
|
||||||
{
|
{
|
||||||
e->scalingFunction[sf->strong_side()] = sf; // Only strong color assigned
|
e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,7 +335,7 @@ void Thread::search() {
|
||||||
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
|
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
|
||||||
|
|
||||||
std::memset(ss-4, 0, 7 * sizeof(Stack));
|
std::memset(ss-4, 0, 7 * sizeof(Stack));
|
||||||
for(int i = 4; i > 0; i--)
|
for (int i = 4; i > 0; i--)
|
||||||
(ss-i)->history = &this->counterMoveHistory[NO_PIECE][0]; // Use as sentinel
|
(ss-i)->history = &this->counterMoveHistory[NO_PIECE][0]; // Use as sentinel
|
||||||
|
|
||||||
bestValue = delta = alpha = -VALUE_INFINITE;
|
bestValue = delta = alpha = -VALUE_INFINITE;
|
||||||
|
|
|
@ -531,14 +531,14 @@ int decompress_pairs(PairsData* d, uint64_t idx) {
|
||||||
//
|
//
|
||||||
// I(k) = k * d->span + d->span / 2 (1)
|
// I(k) = k * d->span + d->span / 2 (1)
|
||||||
|
|
||||||
// First step is to get the 'k' of the I(k) nearest to our idx, using defintion (1)
|
// First step is to get the 'k' of the I(k) nearest to our idx, using definition (1)
|
||||||
uint32_t k = idx / d->span;
|
uint32_t k = idx / d->span;
|
||||||
|
|
||||||
// Then we read the corresponding SparseIndex[] entry
|
// Then we read the corresponding SparseIndex[] entry
|
||||||
uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block);
|
uint32_t block = number<uint32_t, LittleEndian>(&d->sparseIndex[k].block);
|
||||||
int offset = number<uint16_t, LittleEndian>(&d->sparseIndex[k].offset);
|
int offset = number<uint16_t, LittleEndian>(&d->sparseIndex[k].offset);
|
||||||
|
|
||||||
// Now compute the difference idx - I(k). From defintion of k we know that
|
// Now compute the difference idx - I(k). From definition of k we know that
|
||||||
//
|
//
|
||||||
// idx = k * d->span + idx % d->span (2)
|
// idx = k * d->span + idx % d->span (2)
|
||||||
//
|
//
|
||||||
|
|
Loading…
Add table
Reference in a new issue