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