compare-book.cc
Go to the documentation of this file.
00001 // compare-book.cc
00002 #include "osl/record/opening/openingBook.h"
00003 #include "osl/record/compactBoard.h"
00004 #include "osl/record/csa.h"
00005 #include "osl/record/kanjiPrint.h"
00006 #include "osl/search/quiescenceSearch2.h"
00007 #include "osl/search/quiescenceSearch2.tcc"
00008 #include "osl/search/simpleHashTable.h"
00009 #include "osl/eval/pieceEval.h"
00010 #include "osl/stl/vector.h"
00011 #include "osl/stl/hash_map.h"
00012 #include "osl/misc/math.h"
00013 #include "osl/search/fixedEval.h"
00014 #include <boost/shared_ptr.hpp>
00015 #include <boost/program_options.hpp>
00016 #include <boost/progress.hpp>
00017 #include <boost/format.hpp>
00018 #include <iostream>
00019 #include <deque>
00020 
00021 #include "osl/move.h"
00022 #include "osl/record/csaRecord.h"
00023 #include "osl/record/record.h"
00024 #include "osl/state/numEffectState.h"
00025 #include <boost/shared_ptr.hpp>
00026 
00027 typedef std::vector<osl::record::opening::WMove> WMoveContainer;
00028 
00029 osl::Player the_player = osl::BLACK;
00030 std::string dump_mode = "none";
00031 int is_determinate = 0;    // test only top n moves.  0 for all
00032 int max_depth, non_determinate_depth;
00033 double ratio;              // use moves[n+1] when the weight[n+1] >= ratio*weight[n]
00034 
00035 size_t state_count = 0;
00036 
00037 void printUsage(std::ostream& out, 
00038                 char **argv,
00039                 const boost::program_options::options_description& command_line_options)
00040 {
00041   out << "Usage: " << argv[0] << " [options] <book-a.dat> <book-b.dat>\n"
00042       << command_line_options 
00043       << std::endl;
00044 }
00045 
00046 typedef osl::hash_map<osl::HashKey,int> table_t;
00047 void store(osl::record::opening::WeightedBook& book, table_t& table, osl::vector<int>& parents)
00048 {
00049   WMoveContainer moves = book.getMoves(book.getStartState());
00050   parents.resize(book.getTotalState());
00051   std::fill(parents.begin(), parents.end(), -1);
00052   boost::progress_display progress(book.getTotalState());
00053 
00054   typedef std::pair<int, int> state_depth_t;
00055   std::deque<state_depth_t> stateToVisit;
00056   stateToVisit.push_back(state_depth_t(book.getStartState(), 1)); // root is 1
00057   // depth-1手目からdepth手目のstate。depth手目はまだ指されていない(これか
00058   // らdepth手目)
00059 
00060   typedef std::pair<int, int> eval_depth_t;
00061   long leaves = 0;
00062   int depth_found = 0;
00063   while (!stateToVisit.empty())
00064   {
00065     const state_depth_t state_depth = stateToVisit.front();
00066     const int stateIndex = state_depth.first;
00067     const int depth      = state_depth.second;
00068     stateToVisit.pop_front();
00069     ++progress;
00070     assert(parents[stateIndex] >= 0 || stateIndex == book.getStartState());
00071 
00072     depth_found = std::max(depth_found, depth);
00073     WMoveContainer moves = book.getMoves(stateIndex);
00074     
00075     // 自分(the_player)の手番では、良い手のみ指す
00076     // 相手はどんな手を指してくるか分からない
00077     std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveWeightMoveSort());
00078     if ( !moves.empty() &&
00079           ((the_player == osl::BLACK && depth % 2 == 1) ||
00080            (the_player == osl::WHITE && depth % 2 == 0)) )
00081     {
00082       int min = 1;
00083       if (is_determinate) 
00084       {
00085         min = moves.at(0).getWeight();
00086         if (depth <= non_determinate_depth) 
00087         {
00088           for (int i=1; i<=std::min(is_determinate, (int)moves.size()-1); ++i) 
00089           {
00090             const int weight = moves.at(i).getWeight();
00091             if ((double)weight < (double)moves.at(i-1).getWeight()*ratio)
00092               break;
00093             min = weight;
00094           }
00095         }
00096       }
00097       // Do not play 0-weighted moves.
00098       if (min == 0) min = 1;
00099 
00100       WMoveContainer::iterator each = moves.begin();
00101       for (; each != moves.end(); ++each)
00102       {
00103         if (each->getWeight() < min)
00104           break;
00105       }
00106       moves.erase(each, moves.end());
00107     }
00108 
00109     if (moves.empty() || depth > max_depth) // found leaves
00110     {
00111       ++leaves;
00112       continue;
00113     }
00114 
00115     if (moves[0].getWeight()) {
00116       // not leaf
00117       const osl::state::NumEffectState state(book.getBoard(stateIndex));
00118       const osl::HashKey key(state);
00119       table[key] = stateIndex;
00120     }
00121 
00122 
00123     // recursively search the tree
00124     for (std::vector<osl::record::opening::WMove>::const_iterator each = moves.begin();
00125          each != moves.end(); ++each)
00126     {
00127       const int nextIndex = each->getStateIndex();
00128       if (parents[nextIndex] < 0) {
00129         parents[nextIndex] = stateIndex;
00130         stateToVisit.push_back(state_depth_t(nextIndex, depth+1));
00131       }
00132     } // each wmove
00133   } // while loop
00134 
00135   // Show the result
00136   std::cout << std::endl;
00137   std::cout << boost::format("Player: %s\n") % the_player;
00138   std::cout << 
00139     boost::format("#leaves: %d, max_depth %d\n") 
00140                   % leaves 
00141                   % depth_found;
00142 }
00143 
00144 void show_moves(const char *name, osl::record::opening::WeightedBook& book, int node)
00145 {
00146   WMoveContainer moves = book.getMoves(node);  
00147   std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveWeightMoveSort());
00148 
00149   if (! moves.empty() && moves[0].getWeight()) {
00150     std::cout << name;
00151     for (size_t i=0; i<moves.size(); ++i) {
00152       if (moves[i].getWeight() == 0)
00153         break;
00154       const int next_state_index = moves[i].getStateIndex();
00155       const int black_win        = book.getBlackWinCount(next_state_index);
00156       const int white_win        = book.getWhiteWinCount(next_state_index);
00157       std::cout << "  " << osl::record::csa::show(moves[i].getMove())
00158                 << "(" << moves[i].getWeight() << "," << black_win << "," << white_win << ")";
00159     }
00160     std::cout << "\n";
00161   }
00162 }
00163 
00164 void show_history(const osl::MoveVector& history)
00165 {
00166   std::cout << "[" << history.size() << "]";
00167   for (size_t i=0; i<history.size(); ++i)
00168     std::cout << " " << osl::record::csa::show(history[i]);
00169   std::cout << std::endl;
00170 }
00171 
00172 osl::MoveVector make_history(osl::record::opening::WeightedBook& book, const osl::vector<int>& parents, int node)
00173 {
00174   osl::vector<int> history;
00175   history.push_back(node);
00176   while (parents[node] >= 0) {
00177     node = parents[node];
00178     history.push_back(node);
00179   }
00180   std::reverse(history.begin(), history.end());
00181   assert(book.getStartState() == history[0]);
00182 
00183   osl::MoveVector result;  
00184   for (size_t i=0; i<history.size()-1; ++i) {
00185     const WMoveContainer& moves = book.getMoves(history[i]);  
00186     for (WMoveContainer::const_iterator p=moves.begin(); p!=moves.end(); ++p) {
00187       if (p->getStateIndex() != history[i+1])
00188         continue;
00189       result.push_back(p->getMove());
00190       break;
00191     }
00192   }
00193   return result;
00194 }
00195 
00196 void dump(osl::record::opening::WeightedBook& book_a, const osl::vector<int>& parents_a, int node_a, 
00197           osl::record::opening::WeightedBook& book_b, const osl::vector<int>& parents_b, int node_b)
00198 {
00199   const osl::state::NumEffectState state(book_a.getBoard(node_a));
00200   osl::record::KanjiPrint printer(std::cout, 
00201                                   boost::shared_ptr<osl::record::Characters>(
00202                                     new osl::record::KIFCharacters())
00203     );
00204   printer.print(state);
00205   const osl::MoveVector history_a = make_history(book_a, parents_a, node_a);
00206   const osl::MoveVector history_b = make_history(book_b, parents_b, node_b);
00207   show_history(history_a);
00208   if (! (history_a == history_b))
00209     show_history(history_b);
00210   show_moves("a", book_a, node_a);
00211   show_moves("b", book_b, node_b);
00212 }
00213 
00214 void dump(const char *name, osl::record::opening::WeightedBook& book, const osl::vector<int>& parents, int node)
00215 {
00216   const osl::state::NumEffectState state(book.getBoard(node));
00217   osl::record::KanjiPrint printer(std::cout, 
00218                                   boost::shared_ptr<osl::record::Characters>(
00219                                     new osl::record::KIFCharacters())
00220     );
00221   printer.print(state);
00222   show_history(make_history(book, parents, node));
00223   show_moves(name, book, node);
00224 }
00225 
00226 bool is_same_node(osl::record::opening::WeightedBook& book_a, int node_a, 
00227                   osl::record::opening::WeightedBook& book_b, int node_b)
00228 {
00229   WMoveContainer moves_a = book_a.getMoves(node_a);
00230   WMoveContainer moves_b = book_b.getMoves(node_b);
00231   
00232   std::sort(moves_a.begin(), moves_a.end(), osl::record::opening::WMoveWeightMoveSort());
00233   std::sort(moves_b.begin(), moves_b.end(), osl::record::opening::WMoveWeightMoveSort());
00234 
00235   size_t i=0;
00236   for (; i<std::min(moves_a.size(), moves_b.size()); ++i) {
00237     if (moves_a[i].getWeight() == 0)
00238       return moves_b[i].getWeight() == 0;
00239     if (moves_b[i].getWeight() == 0)
00240       return false;
00241     if (moves_a[i].getMove() != moves_b[i].getMove())
00242       return false;
00243   }
00244   if (i == moves_a.size())
00245     return i == moves_b.size() || moves_b[i].getWeight() == 0;
00246   return moves_a[i].getWeight() == 0;
00247 }
00248 
00249 void compare(osl::record::opening::WeightedBook& book_a, const table_t& table_a, const osl::vector<int>& parents_a,
00250              osl::record::opening::WeightedBook& book_b, const table_t& table_b, const osl::vector<int>& parents_b)
00251 {
00252   long only_a = 0, only_b = 0, same = 0, diff = 0;
00253   for (table_t::const_iterator p=table_a.begin(); p!=table_a.end(); ++p) {
00254     table_t::const_iterator q=table_b.find(p->first);
00255     if (q == table_b.end()) {
00256       ++only_a;
00257       if (dump_mode == "a")
00258         dump("a", book_a, parents_a, p->second);        
00259       continue;
00260     }
00261     if (is_same_node(book_a, p->second, book_b, q->second))
00262       ++same;
00263     else {
00264       ++diff;
00265       if (dump_mode == "common")
00266         dump(book_a, parents_a, p->second, 
00267              book_b, parents_b, q->second);     
00268     }
00269   }
00270   for (table_t::const_iterator p=table_b.begin(); p!=table_b.end(); ++p) {
00271     table_t::const_iterator q=table_a.find(p->first);
00272     if (q == table_a.end()) {
00273       ++only_b;
00274       if (dump_mode == "b")
00275         dump("b", book_b, parents_b, p->second);        
00276       continue;
00277     }
00278   }
00279   std::cout << "same " << same << " diff " << diff
00280             << " only-in-a " << only_a << " only-in-b " << only_b << std::endl;
00281 }
00282 
00283 int main(int argc, char **argv)
00284 {
00285   std::string player_str;
00286 
00287   namespace bp = boost::program_options;
00288   bp::variables_map vm;
00289   bp::options_description command_line_options;
00290   command_line_options.add_options()
00291     ("player,p", bp::value<std::string>(&player_str)->default_value("black"),
00292      "specify a player, black or white, in whose point of view the book is validated. "
00293      "default black.")
00294     ("input-file,f", bp::value<std::vector<std::string> >(),
00295      "a joseki file to validate.")
00296     ("dump", bp::value<std::string>(&dump_mode)->default_value(dump_mode),
00297      "common: dump positions where two books have different moves\n"
00298      "(a|b): dump positions registered to only book_[ab]\n")
00299     ("determinate", bp::value<int>(&is_determinate)->default_value(0),
00300      "only search the top n moves.  (0 for all,  1 for determinate).")
00301     ("non-determinate-depth", bp::value<int>(&non_determinate_depth)->default_value(100),
00302      "use the best move where the depth is greater than this value")
00303     ("max-depth", bp::value<int>(&max_depth)->default_value(100),
00304      "do not go beyond this depth from the root")
00305     ("ratio", bp::value<double>(&ratio)->default_value(0.0),
00306      "skip move[i] (i >= n), if weight[n] < weight[n-1]*ratio")
00307     ("help,h", "show this help message.");
00308   bp::positional_options_description p;
00309   p.add("input-file", -1);
00310 
00311   std::vector<std::string> filenames;
00312   try
00313   {
00314     bp::store(
00315       bp::command_line_parser(
00316         argc, argv).options(command_line_options).positional(p).run(), vm);
00317     bp::notify(vm);
00318     filenames = vm["input-file"].as<std::vector<std::string> >();
00319     if (vm.count("help") || filenames.size() != 2 
00320         || (dump_mode != "none" && dump_mode != "a" && dump_mode != "b" && dump_mode != "common"))
00321     {
00322       printUsage(std::cout, argv, command_line_options);
00323       return 0;
00324     }
00325   }
00326   catch (std::exception &e)
00327   {
00328     std::cerr << "error in parsing options\n"
00329               << e.what() << std::endl;
00330     printUsage(std::cerr, argv, command_line_options);
00331     return 1;
00332   }
00333 
00334   if (player_str == "black")
00335     the_player = osl::BLACK;
00336   else if (player_str == "white")
00337     the_player = osl::WHITE;
00338   else
00339   {
00340     printUsage(std::cerr, argv, command_line_options);
00341     return 1;
00342   }
00343 
00344   osl::record::opening::WeightedBook book_a(filenames[0].c_str()), book_b(filenames[1].c_str());
00345   osl::CArray<osl::vector<int>,2> parents;
00346   osl::CArray<table_t,2> tables;
00347   std::cout << boost::format("Book: %s\n") % filenames[0];
00348   store(book_a, tables[0], parents[0]);
00349   std::cout << boost::format("Book: %s\n") % filenames[1];
00350   store(book_b, tables[1], parents[1]);
00351 
00352   compare(book_a, tables[0], parents[0], book_b, tables[1], parents[1]);
00353   return 0;
00354 }
00355 // ;;; Local Variables:
00356 // ;;; mode:c++
00357 // ;;; c-basic-offset:2
00358 // ;;; End:
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines