00001 #include "osl/record/opening/openingBook.h"
00002 #include "osl/record/compactBoard.h"
00003 #include "osl/record/csa.h"
00004 #include "osl/record/kanjiPrint.h"
00005 #include "osl/search/quiescenceSearch2.h"
00006 #include "osl/search/quiescenceSearch2.tcc"
00007 #include "osl/search/simpleHashTable.h"
00008 #include "osl/eval/pieceEval.h"
00009 #include "osl/stl/vector.h"
00010 #include "osl/misc/math.h"
00011 #include "osl/search/fixedEval.h"
00012 #include <boost/shared_ptr.hpp>
00013 #include <boost/program_options.hpp>
00014 #include <boost/progress.hpp>
00015 #include <boost/format.hpp>
00016 #include <boost/lambda/lambda.hpp>
00017 #include <boost/lambda/bind.hpp>
00018 #include <iostream>
00019 #include <deque>
00020 #include <vector>
00021
00022 #include "osl/move.h"
00023 #include "osl/record/csaRecord.h"
00024 #include "osl/record/record.h"
00025 #include "osl/state/numEffectState.h"
00026 #include "osl/apply_move/applyMove.h"
00027 #include <boost/shared_ptr.hpp>
00028
00029 using namespace boost::lambda;
00030 namespace bp = boost::program_options;
00031 bp::variables_map vm;
00032
00033 typedef std::vector<osl::record::opening::WMove> WMoveContainer;
00034
00035 osl::Player the_player = osl::BLACK;
00036 bool is_dump = false;
00037 int error_threshold = 500;
00038 int is_determinate = 0;
00039 int max_depth, non_determinate_depth;
00040 double ratio;
00041 bool is_quick = false;
00042
00043 boost::shared_ptr<osl::NumEffectState> state_to_compare;
00044 size_t state_count = 0;
00045
00053 int qsearch(const osl::state::SimpleState &s,
00054 const osl::Move& lastMove)
00055 {
00056 if (is_quick) return 0;
00057
00058 typedef osl::search::QuiescenceSearch2<osl::eval::PieceEval> qsearch_t;
00059 osl::state::NumEffectState state(s);
00060 osl::search::SimpleHashTable table(100000, -1, false);
00061 osl::search::SearchState2Core::checkmate_t checkmate_searcher;
00062 osl::search::SearchState2Core core(state, checkmate_searcher);
00063 qsearch_t qs(core, table);
00064 osl::eval::PieceEval ev(state);
00065 return qs.search(state.getTurn(), ev, lastMove, 4);
00066 }
00067
00068 void showStatistics(const std::deque<int>& src)
00069 {
00070 double sum, mean, var, dev, skew, kurt;
00071 osl::misc::computeStats(src.begin(), src.end(), sum, mean, var, dev, skew, kurt);
00072
00073 std::cout << boost::format(" total: %g\n") % src.size()
00074 << boost::format(" mean: %g\n") % mean
00075 << boost::format(" dev: %g\n") % dev;
00076 }
00077
00078 void printUsage(std::ostream& out,
00079 char **argv,
00080 const boost::program_options::options_description& command_line_options)
00081 {
00082 out <<
00083 "Usage: " << argv[0] << " [options] <a_joseki_file.dat>\n"
00084 << command_line_options
00085 << std::endl;
00086 }
00087
00088 void showInfoOfState(osl::record::opening::WeightedBook& book, const int state_index)
00089 {
00090 osl::record::KanjiPrint printer(std::cerr,
00091 boost::shared_ptr<osl::record::Characters>(
00092 new osl::record::KIFCharacters())
00093 );
00094
00095 std::cout << boost::format("state_index: %g\n") % state_index
00096 << boost::format("black win: %g\n") % book.getBlackWinCount(state_index)
00097 << boost::format("white win: %g\n") % book.getWhiteWinCount(state_index);
00098
00099 std::cout << "\nTarget state:\n";
00100 printer.print(book.getBoard(state_index));
00101 std::cout << "\n";
00102
00103 WMoveContainer moves = book.getMoves(state_index);
00104 std::cout << boost::format("found %g moves\n") % moves.size();
00105 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveSort());
00106 for (WMoveContainer::const_iterator each = moves.begin();
00107 each != moves.end(); ++each)
00108 {
00109 std::cout << boost::format("[%g] %g") % each->getWeight() % each->getMove();
00110 const int next_index = each->getStateIndex();
00111 std::cout << "\n";
00112 printer.print(book.getBoard(next_index));
00113 }
00114 }
00115
00116
00117 void doMain(const std::string& file_name)
00118 {
00119 osl::record::KanjiPrint printer(std::cerr,
00120 boost::shared_ptr<osl::record::Characters>(
00121 new osl::record::KIFCharacters())
00122 );
00123 if (vm.count("verbose"))
00124 std::cout << boost::format("Opening... %s\n ") % file_name;
00125 osl::record::opening::WeightedBook book(file_name.c_str());
00126
00127 if (vm.count("verbose"))
00128 std::cout << boost::format("Total states: %d\n") % book.getTotalState();
00129 bool states[book.getTotalState()];
00130 memset(states, 0, sizeof(bool) * book.getTotalState());
00131 boost::progress_display progress(book.getTotalState());
00132
00133 int state_index_to_compare = -1;
00134 if (state_to_compare)
00135 state_index_to_compare = book.getStateIndex(*state_to_compare);
00136
00137 typedef std::pair<int, int> state_depth_t;
00138 osl::stl::vector<state_depth_t> stateToVisit;
00139
00140 if (vm.count("verbose"))
00141 std::cout << boost::format("Start index: %d\n)") % book.getStartState();
00142 stateToVisit.push_back(state_depth_t(book.getStartState(), 1));
00143
00144
00145
00146 typedef std::pair<int, int> eval_depth_t;
00147 std::deque<eval_depth_t> evals;
00148 long finishing_games = 0;
00149
00150 while (!stateToVisit.empty())
00151 {
00152 const state_depth_t state_depth = stateToVisit.back();
00153 if (vm.count("verbose"))
00154 std::cout << boost::format("Visiting... %d\n") % state_depth.first;
00155 const int stateIndex = state_depth.first;
00156 const int depth = state_depth.second;
00157 stateToVisit.pop_back();
00158 states[stateIndex] = true;
00159 ++progress;
00160
00161
00162
00163 if (state_to_compare &&
00164 state_index_to_compare == stateIndex)
00165 ++state_count;
00166
00167 WMoveContainer moves = book.getMoves(stateIndex);
00168 if (vm.count("verbose"))
00169 std::cout << boost::format(" #moves... %d\n") % moves.size();
00170
00171
00172
00173 if ( !moves.empty() &&
00174 ((the_player == osl::BLACK && depth % 2 == 1) ||
00175 (the_player == osl::WHITE && depth % 2 == 0)) )
00176 {
00177 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveSort());
00178 int min = 1;
00179 if (is_determinate)
00180 {
00181 min = moves.at(0).getWeight();
00182 if (depth <= non_determinate_depth)
00183 {
00184 for (int i=1; i<=std::min(is_determinate, (int)moves.size()-1); ++i)
00185 {
00186 const int weight = moves.at(i).getWeight();
00187 if ((double)weight < (double)moves.at(i-1).getWeight()*ratio)
00188 break;
00189 min = weight;
00190 }
00191 }
00192 }
00193
00194 if (min == 0) min = 1;
00195
00196 WMoveContainer::iterator each = moves.begin();
00197 for (; each != moves.end(); ++each)
00198 {
00199 if (each->getWeight() < min)
00200 break;
00201 }
00202 moves.erase(each, moves.end());
00203 }
00204
00205 if (moves.empty() || depth > max_depth)
00206 {
00207 const osl::state::NumEffectState state(book.getBoard(stateIndex));
00208 const int value = qsearch(state, osl::Move::PASS(alt(state.getTurn())));
00209
00210 if ( (the_player == osl::BLACK && value < -1 * error_threshold) ||
00211 (the_player == osl::WHITE && value > error_threshold) )
00212 {
00213 ++finishing_games;
00214 if (is_dump)
00215 {
00216 std::cerr << std::endl;
00217 std::cerr << "eval: " << value << std::endl;
00218 printer.print(state);
00219 std::cerr << "piece value:" << osl::PieceEval(state).value() << "\n" << state;
00220 }
00221 }
00222 else
00223 {
00224 evals.push_back(eval_depth_t(value, depth));
00225 }
00226 continue;
00227 }
00228
00229
00230 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveMoveSort());
00231
00232
00233 for (std::vector<osl::record::opening::WMove>::const_iterator each = moves.begin();
00234 each != moves.end(); ++each)
00235 {
00236 const int nextIndex = each->getStateIndex();
00237 if (! states[nextIndex])
00238 stateToVisit.push_back(state_depth_t(nextIndex, depth+1));
00239 }
00240 }
00241
00242
00243 std::cout << std::endl;
00244 std::cout << boost::format("Book: %s\n") % file_name;
00245 std::cout << boost::format("Player: %s\n") % the_player;
00246 std::cout << "FU=128 points\n";
00247 std::cout <<
00248 boost::format("#states: %d (+ %d finishing games over %d points; max %d)\n")
00249 % evals.size()
00250 % finishing_games
00251 % error_threshold
00252 % max_depth;
00253 {
00254 std::cout << "Eval\n";
00255 std::deque<int> tmp;
00256 for (std::deque<eval_depth_t>::const_iterator each = evals.begin();
00257 each != evals.end(); ++each)
00258 tmp.push_back(each->first);
00259 showStatistics(tmp);
00260 }
00261 {
00262 std::cout << "Depth\n";
00263 std::deque<int> tmp;
00264 for (std::deque<eval_depth_t>::const_iterator each = evals.begin();
00265 each != evals.end(); ++each)
00266 tmp.push_back(each->second);
00267 showStatistics(tmp);
00268 }
00269 if (state_to_compare)
00270 {
00271 std::cout << "\nthe state hits: " << state_count << std::endl;
00272 printer.print(*state_to_compare);
00273
00274 const int stateIndex = book.getStateIndex(*state_to_compare);
00275 const std::vector<int> parents = book.getParents(stateIndex);
00276 if (parents.empty())
00277 {
00278 std::cout << "\nNo parent\n";
00279 }
00280 else
00281 {
00282 int i = 0;
00283 for (std::vector<int>::const_iterator each = parents.begin();
00284 each != parents.end(); ++each, ++i)
00285 {
00286 std::cout << boost::format("\n--- Parent: %g ---\n ") % i;
00287 showInfoOfState(book, *each);
00288 }
00289 }
00290
00291 }
00292 }
00293
00294
00295 int main(int argc, char **argv)
00296 {
00297 std::string player_str;
00298 std::string file_name;
00299 size_t csa_move_index;
00300 std::string csa_file_name;
00301
00302 bp::options_description command_line_options;
00303 command_line_options.add_options()
00304 ("player,p", bp::value<std::string>(&player_str)->default_value("black"),
00305 "specify a player, black or white, in whose point of view the book is validated. "
00306 "default black.")
00307 ("input-file,f", bp::value<std::string>(&file_name)->default_value("./joseki.dat"),
00308 "a joseki file to validate.")
00309 ("dump", bp::value<bool>(&is_dump)->default_value(false),
00310 "dump finishing games' states")
00311 ("threshold", bp::value<int>(&error_threshold)->default_value(500),
00312 "threshold of evaluatoin value to recognize a finishing game.")
00313 ("determinate", bp::value<int>(&is_determinate)->default_value(0),
00314 "only search the top n moves. (0 for all, 1 for determinate).")
00315 ("non-determinate-depth", bp::value<int>(&non_determinate_depth)->default_value(100),
00316 "use the best move where the depth is greater than this value")
00317 ("max-depth", bp::value<int>(&max_depth)->default_value(100),
00318 "do not go beyond this depth from the root")
00319 ("ratio", bp::value<double>(&ratio)->default_value(0.0),
00320 "skip move[i] (i >= n), if weight[n] < weight[n-1]*ratio")
00321 ("csa-move", bp::value<size_t>(&csa_move_index)->default_value(1),
00322 "n-th-move state in the csa file")
00323 ("csa", bp::value<std::string>(&csa_file_name)->default_value(""),
00324 "a csa file name. See if a state in the game exists in the book or not.")
00325 ("quick", bp::value<bool>(&is_quick)->default_value(false),
00326 "skip quiescence search.")
00327 ("verbose,v", "output verbose messages.")
00328 ("help,h", "show this help message.");
00329 bp::positional_options_description p;
00330 p.add("input-file", 1);
00331
00332 try
00333 {
00334 bp::store(
00335 bp::command_line_parser(
00336 argc, argv).options(command_line_options).positional(p).run(), vm);
00337 bp::notify(vm);
00338 if (vm.count("help"))
00339 {
00340 printUsage(std::cout, argv, command_line_options);
00341 return 0;
00342 }
00343 }
00344 catch (std::exception &e)
00345 {
00346 std::cerr << "error in parsing options\n"
00347 << e.what() << std::endl;
00348 printUsage(std::cerr, argv, command_line_options);
00349 return 1;
00350 }
00351
00352 if (player_str == "black")
00353 the_player = osl::BLACK;
00354 else if (player_str == "white")
00355 the_player = osl::WHITE;
00356 else
00357 {
00358 printUsage(std::cerr, argv, command_line_options);
00359 return 1;
00360 }
00361
00362 if (!csa_file_name.empty())
00363 {
00364 is_quick = true;
00365 const osl::record::csa::CsaFile csa(csa_file_name);
00366 const osl::record::Record record = csa.getRecord();
00367 const osl::stl::vector<osl::Move> moves = record.getMoves();
00368 const osl::SimpleState initialState = record.getInitialState();
00369 state_to_compare.reset(new osl::NumEffectState(initialState));
00370
00371 if (csa_move_index < 1) csa_move_index = 1;
00372 if (csa_move_index > moves.size()) csa_move_index = moves.size();
00373 if ( (the_player == osl::BLACK && csa_move_index%2 == 0) ||
00374 (the_player == osl::WHITE && csa_move_index%2 == 1) )
00375 {
00376 std::cout << "Invalid csa move index: " << csa_move_index << std::endl;
00377 return -1;
00378 }
00379 for (size_t i=0; i < csa_move_index; i++)
00380 {
00381 const osl::Move& move = moves[i];
00382 osl::ApplyMoveOfTurn::doMove(*state_to_compare, move);
00383 }
00384 }
00385
00386 doMain(file_name);
00387
00388 return 0;
00389 }
00390
00391
00392
00393