Rewrite syzygy in C++

Rewrite the code in SF style, simplify and
document it.

Code is now much clear and bug free (no mem-leaks and
other small issues) and is also smaller (more than
600 lines of code removed).

All the code has been rewritten but root_probe() and
root_probe_wdl() that are completely misplaced and should
be retired altogheter. For now just leave them in the
original version.

Code is fully and deeply tested for equivalency both in
functionality and in speed with hundreds of games and
test positions and is guaranteed to be 100% equivalent
to the original.

Tested with tb_dbg branch for functional equivalency on
more than 12M positions.

stockfish.exe bench 128 1 16 syzygy.epd

Position: 2016/2016
Total 12121156 Hits 0 hit rate (%) 0
Total time (ms) : 4417851
Nodes searched : 1100151204
Nodes/second : 249024

Tested with 5,000 games match against master, 1 Thread,
128 MB Hash each, tc 40+0.4, which is almost equivalent
to LTC in Fishtest on this machine. 3-, 4- and 5-men syzygy
bases on SSD, 12-moves opening book to emphasize mid- and endgame.

Score of SF-SyzygyC++ vs SF-Master: 633 - 617 - 3750  [0.502] 5000
ELO difference: 1

No functional change.
This commit is contained in:
Marco Costalba
2016-07-16 08:10:45 +02:00
parent 369eff437c
commit c0bb041539
9 changed files with 1678 additions and 2287 deletions

View File

@@ -32,6 +32,7 @@
#include "thread.h"
#include "tt.h"
#include "uci.h"
#include "syzygy/tbprobe.h"
using std::string;
@@ -85,7 +86,7 @@ PieceType min_attacker<KING>(const Bitboard*, Square, Bitboard, Bitboard&, Bitbo
/// operator<<(Position) returns an ASCII representation of the position
std::ostream& operator<<(std::ostream& os, const Position& pos) {
std::ostream& operator<<(std::ostream& os, Position& pos) {
os << "\n +---+---+---+---+---+---+---+---+\n";
@@ -98,11 +99,22 @@ std::ostream& operator<<(std::ostream& os, const Position& pos) {
}
os << "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
<< std::setfill('0') << std::setw(16) << pos.key() << std::dec << "\nCheckers: ";
<< std::setfill('0') << std::setw(16) << pos.key()
<< std::setfill(' ') << std::dec << "\nCheckers: ";
for (Bitboard b = pos.checkers(); b; )
os << UCI::square(pop_lsb(&b)) << " ";
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
&& !pos.can_castle(ANY_CASTLING))
{
Tablebases::ProbeState s1, s2;
Tablebases::WDLScore wdl = Tablebases::probe_wdl(pos, &s1);
int dtz = Tablebases::probe_dtz(pos, &s2);
os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
<< "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")";
}
return os;
}
@@ -357,6 +369,28 @@ void Position::set_state(StateInfo* si) const {
}
/// Position::set() is an overload to initialize the position object with
/// the given endgame code string like "KBPKN". It is manily an helper to
/// get the material key out of an endgame code. Position is not playable,
/// indeed is even not guaranteed to be legal.
Position& Position::set(const string& code, Color c, StateInfo* si) {
assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K');
string sides[] = { code.substr(code.find('K', 1)), // Weak
code.substr(0, code.find('K', 1)) }; // Strong
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
string fenStr = sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/8/8/"
+ sides[1] + char(8 - sides[1].length() + '0') + " w - - 0 10";
return set(fenStr, false, si, nullptr);
}
/// Position::fen() returns a FEN representation of the position. In case of
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.