Restore perft
authorMarco Costalba <mcostalba@gmail.com>
Tue, 15 Aug 2017 08:05:22 +0000 (01:05 -0700)
committerMarco Costalba <mcostalba@gmail.com>
Fri, 18 Aug 2017 16:04:38 +0000 (09:04 -0700)
Rewrite perft to be placed naturally inside new
bench code. In particular we don't have special
custom code to run perft anymore but perft is
just a new parameter of 'go' command.

So user API is now changed, old style command:

$perft 5

becomes

$go perft 4

No functional change.

src/benchmark.cpp
src/search.cpp
src/search.h
src/uci.cpp
tests/perft.sh

index c1c74f7..c9a5933 100644 (file)
@@ -86,11 +86,16 @@ const vector<string> Defaults = {
 } // namespace
 
 /// setup_bench() builds a list of UCI commands to be run by bench. There
-/// are five parameters: TT size, number of search threads that should
-/// be used, the limit value spent for each position (optional, default is
-/// depth 13), an optional file name where to look for positions in FEN
-/// format (defaults are the positions defined above) and the type of the
-/// limit value: depth (default), time in millisecs or number of nodes.
+/// are five parameters: TT size in MB, number of search threads that
+/// should be used, the limit value spent for each position, a file name
+/// where to look for positions in FEN format and the type of the limit:
+/// depth, perft, nodes and movetime (in millisecs).
+///
+/// bench -> search default positions up to depth 13
+/// bench 64 1 15 -> search default positions up to depth 15 (TT = 64MB)
+/// bench 64 4 5000 current movetime -> search current position with 4 threads for 5 sec
+/// bench 64 1 100000 default nodes -> search default positions for 100K nodes each
+/// bench 16 1 5 default perft -> run a perft 5 on default positions
 
 std::vector<string> setup_bench(const Position& current , istream& is) {
 
index 7b3fd18..6bd8422 100644 (file)
@@ -154,6 +154,32 @@ namespace {
   void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus);
   void update_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietsCnt, int bonus);
 
+  // perft() is our utility to verify move generation. All the leaf nodes up
+  // to the given depth are generated and counted, and the sum is returned.
+  template<bool Root>
+  uint64_t perft(Position& pos, Depth depth) {
+
+    StateInfo st;
+    uint64_t cnt, nodes = 0;
+    const bool leaf = (depth == 2 * ONE_PLY);
+
+    for (const auto& m : MoveList<LEGAL>(pos))
+    {
+        if (Root && depth <= ONE_PLY)
+            cnt = 1, nodes++;
+        else
+        {
+            pos.do_move(m, st);
+            cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
+            nodes += cnt;
+            pos.undo_move(m);
+        }
+        if (Root)
+            sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
+    }
+    return nodes;
+  }
+
 } // namespace
 
 
@@ -209,40 +235,18 @@ void Search::clear() {
 }
 
 
-/// Search::perft() is our utility to verify move generation. All the leaf nodes
-/// up to the given depth are generated and counted, and the sum is returned.
-template<bool Root>
-uint64_t Search::perft(Position& pos, Depth depth) {
-
-  StateInfo st;
-  uint64_t cnt, nodes = 0;
-  const bool leaf = (depth == 2 * ONE_PLY);
-
-  for (const auto& m : MoveList<LEGAL>(pos))
-  {
-      if (Root && depth <= ONE_PLY)
-          cnt = 1, nodes++;
-      else
-      {
-          pos.do_move(m, st);
-          cnt = leaf ? MoveList<LEGAL>(pos).size() : perft<false>(pos, depth - ONE_PLY);
-          nodes += cnt;
-          pos.undo_move(m);
-      }
-      if (Root)
-          sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
-  }
-  return nodes;
-}
-
-template uint64_t Search::perft<true>(Position&, Depth);
-
-
 /// MainThread::search() is called by the main thread when the program receives
 /// the UCI 'go' command. It searches from the root position and outputs the "bestmove".
 
 void MainThread::search() {
 
+  if (Limits.perft)
+  {
+      nodes = perft<true>(rootPos, Limits.perft * ONE_PLY);
+      sync_cout << "\nNodes searched: " << nodes << "\n" << sync_endl;
+      return;
+  }
+
   Color us = rootPos.side_to_move();
   Time.init(Limits, us, rootPos.game_ply());
   TT.new_search();
index bbdbdfd..2e08b55 100644 (file)
@@ -78,15 +78,16 @@ struct LimitsType {
 
   LimitsType() { // Init explicitly due to broken value-initialization of non POD in MSVC
     nodes = time[WHITE] = time[BLACK] = inc[WHITE] = inc[BLACK] =
-    npmsec = movestogo = depth = movetime = mate = infinite = 0;
+    npmsec = movestogo = depth = movetime = mate = perft = infinite = 0;
   }
 
   bool use_time_management() const {
-    return !(mate | movetime | depth | nodes | infinite);
+    return !(mate | movetime | depth | nodes | perft | infinite);
   }
 
   std::vector<Move> searchmoves;
-  int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth, movetime, mate, infinite;
+  int time[COLOR_NB], inc[COLOR_NB], npmsec, movestogo, depth,
+      movetime, mate, perft, infinite;
   int64_t nodes;
   TimePoint startTime;
 };
@@ -95,7 +96,6 @@ extern LimitsType Limits;
 
 void init();
 void clear();
-template<bool Root = true> uint64_t perft(Position& pos, Depth depth);
 
 } // namespace Search
 
index cf54497..4a7a945 100644 (file)
@@ -128,6 +128,7 @@ namespace {
         else if (token == "nodes")     is >> limits.nodes;
         else if (token == "movetime")  is >> limits.movetime;
         else if (token == "mate")      is >> limits.mate;
+        else if (token == "perft")     is >> limits.perft;
         else if (token == "infinite")  limits.infinite = 1;
         else if (token == "ponder")    ponderMode = true;
 
@@ -137,7 +138,7 @@ namespace {
 
   // bench() is called when engine receives the "bench" command. Firstly
   // a list of UCI commands is setup according to bench parameters, then
-  // it is run one by one printing summaries at the end.
+  // it is run one by one printing a summary at the end.
 
   void bench(Position& pos, istream& args, StateListPtr& states) {
 
@@ -145,7 +146,7 @@ namespace {
     uint64_t num, nodes = 0, cnt = 1;
 
     vector<string> list = setup_bench(pos, args);
-    num = count_if (list.begin(), list.end(), [](string s) { return s.find("go ") == 0; });
+    num = count_if(list.begin(), list.end(), [](string s) { return s.find("go ") == 0; });
 
     TimePoint elapsed = now();
 
@@ -235,17 +236,6 @@ void UCI::loop(int argc, char* argv[]) {
       else if (token == "bench") bench(pos, is, states);
       else if (token == "d")     sync_cout << pos << sync_endl;
       else if (token == "eval")  sync_cout << Eval::trace(pos) << sync_endl;
-      else if (token == "perft")
-      {
-          int depth;
-          stringstream ss;
-
-          is >> depth;
-          ss << Options["Hash"] << " " << Options["Threads"]
-             << " " << depth << " current perft";
-
-          // TODO benchmark(pos, ss);
-      }
       else
           sync_cout << "Unknown command: " << cmd << sync_endl;
 
index f3d5b88..d402221 100755 (executable)
@@ -14,8 +14,8 @@ cat << EOF > perft.exp
    set timeout 10
    lassign \$argv pos depth result
    spawn ./stockfish
-   send "position \$pos\\n perft \$depth\\n"
-   expect "Nodes searched  ? \$result" {} timeout {exit 1}
+   send "position \$pos\\ngo perft \$depth\\n"
+   expect "Nodes searched? \$result" {} timeout {exit 1}
    send "quit\\n"
    expect eof
 EOF