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