openingStatistics.cc Source File

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
openingStatistics.cc
Go to the documentation of this file.
1 #include "osl/move.h"
2 #include "osl/eval/pieceEval.h"
3 #include "osl/hash/hashKey.h"
4 #include "osl/misc/math.h"
5 #include "osl/record/csa.h"
6 #include "osl/record/csaRecord.h"
7 #include "osl/record/kanjiPrint.h"
8 #include "osl/record/record.h"
9 #include "osl/record/opening/openingBook.h"
10 #include "osl/search/fixedEval.h"
11 #include "osl/search/quiescenceSearch2.h"
12 #include "osl/search/quiescenceSearch2.tcc"
13 #include "osl/search/simpleHashTable.h"
14 #include "osl/state/numEffectState.h"
15 #include "osl/stl/vector.h"
16 #include <boost/format.hpp>
17 #include <boost/lambda/lambda.hpp>
18 #include <boost/lambda/bind.hpp>
19 #include <boost/program_options.hpp>
20 #include <boost/progress.hpp>
21 #include <boost/shared_ptr.hpp>
22 #include <deque>
23 #include <iostream>
24 #include <vector>
25
26
27 using namespace boost::lambda;
28 namespace bp = boost::program_options;
29 bp::variables_map vm;
30
31 typedef std::vector<osl::record::opening::WMove> WMoveContainer;
32
33 osl::Player the_player = osl::BLACK;
34 bool is_dump = false;
35 int error_threshold = 500;
36 int is_determinate = 0; // test only top n moves. 0 for all
37 int max_depth, non_determinate_depth;
38 double ratio; // use moves[n+1] when the weight[n+1] >= ratio*weight[n]
39 bool is_quick = false;
40
41 boost::shared_ptr<osl::NumEffectState> state_to_compare;
42 size_t state_count = 0;
43
51 int qsearch(const osl::state::SimpleState &s,
52 const osl::Move& lastMove)
53 {
54 if (is_quick) return 0;
55
56 typedef osl::search::QuiescenceSearch2<osl::eval::PieceEval> qsearch_t;
57 osl::state::NumEffectState state(s);
58 osl::search::SimpleHashTable table(100000, -1, false);
59 osl::search::SearchState2Core::checkmate_t checkmate_searcher;
60 osl::search::SearchState2Core core(state, checkmate_searcher);
61 qsearch_t qs(core, table);
62 osl::eval::PieceEval ev(state);
63 return qs.search(state.turn(), ev, lastMove, 4);
64 }
65
66 void showStatistics(const std::deque<int>& src)
67 {
68 double sum, mean, var, dev, skew, kurt;
69 osl::misc::computeStats(src.begin(), src.end(), sum, mean, var, dev, skew, kurt);
70
71 std::cout << boost::format(" total: %g\n") % src.size()
72 << boost::format(" mean: %g\n") % mean
73 << boost::format(" dev: %g\n") % dev;
74 }
75
76 void printUsage(std::ostream& out,
77 char **argv,
78 const boost::program_options::options_description& command_line_options)
79 {
80 out <<
81 "Usage: " << argv[0] << " [options] <a_joseki_file.dat>\n"
82 << command_line_options
83 << std::endl;
84 }
85
86 void showInfoOfState(osl::record::opening::WeightedBook& book, const int state_index)
87 {
88 osl::record::KanjiPrint printer(std::cerr,
89 boost::shared_ptr<osl::record::Characters>(
90 new osl::record::KIFCharacters())
91 );
92
93 std::cout << boost::format("state_index: %g\n") % state_index
94 << boost::format("black win: %g\n") % book.getBlackWinCount(state_index)
95 << boost::format("white win: %g\n") % book.getWhiteWinCount(state_index);
96
97 std::cout << "\nTarget state:\n";
98 printer.print(book.getBoard(state_index));
99 std::cout << "\n";
100
101 WMoveContainer moves = book.getMoves(state_index);
102 std::cout << boost::format("found %g moves\n") % moves.size();
103 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveSort());
104 for (WMoveContainer::const_iterator each = moves.begin();
105 each != moves.end(); ++each)
106 {
107 std::cout << boost::format("[%g] %g") % each->getWeight() % each->getMove();
108 const int next_index = each->getStateIndex();
109 std::cout << "\n";
110 printer.print(book.getBoard(next_index));
111 }
112 }
113
114
115 void doMain(const std::string& file_name)
116 {
117 osl::record::KanjiPrint printer(std::cerr,
118 boost::shared_ptr<osl::record::Characters>(
119 new osl::record::KIFCharacters())
120 );
121 if (vm.count("verbose"))
122 std::cout << boost::format("Opening... %s\n ") % file_name;
123 osl::record::opening::WeightedBook book(file_name.c_str());
124
125 if (vm.count("verbose"))
126 std::cout << boost::format("Total states: %d\n") % book.getTotalState();
127 bool states[book.getTotalState()]; // mark states that have been visited.
128 memset(states, 0, sizeof(bool) * book.getTotalState());
129 boost::progress_display progress(book.getTotalState());
130
131 int state_index_to_compare = -1;
132 if (state_to_compare)
133 state_index_to_compare = book.getStateIndex(*state_to_compare);
134
135 typedef std::pair<int, int> state_depth_t;
136 osl::stl::vector<state_depth_t> stateToVisit;
137
138 if (vm.count("verbose"))
139 std::cout << boost::format("Start index: %d\n)") % book.getStartState();
140 stateToVisit.push_back(state_depth_t(book.getStartState(), 1)); // root is 1
141 // depth-1手目からdepth手目のstate。depth手目はまだ指されていない（これか
142 // らdepth手目）
143
144 typedef std::pair<int, int> eval_depth_t;
145 std::deque<eval_depth_t> evals;
146 long finishing_games = 0;
147
148 while (!stateToVisit.empty())
149 {
150 const state_depth_t state_depth = stateToVisit.back();
151 if (vm.count("verbose"))
152 std::cout << boost::format("Visiting... %d\n") % state_depth.first;
153 const int stateIndex = state_depth.first;
154 const int depth = state_depth.second;
155 stateToVisit.pop_back();
156 states[stateIndex] = true;
157 ++progress;
158
159
160 // see if the state presents in the book
161 if (state_to_compare &&
162 state_index_to_compare == stateIndex)
163 ++state_count;
164
165 WMoveContainer moves = book.getMoves(stateIndex);
166 if (vm.count("verbose"))
167 std::cout << boost::format(" #moves... %d\n") % moves.size();
168
169 // 自分（the_player）の手番では、良い手のみ指す
170 // 相手はどんな手を指してくるか分からない
171 if ( !moves.empty() &&
172 ((the_player == osl::BLACK && depth % 2 == 1) ||
173 (the_player == osl::WHITE && depth % 2 == 0)) )
174 {
175 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveSort());
176 int min = 1;
177 if (is_determinate)
178 {
179 min = moves.at(0).getWeight();
180 if (depth <= non_determinate_depth)
181 {
182 for (int i=1; i<=std::min(is_determinate, (int)moves.size()-1); ++i)
183 {
184 const int weight = moves.at(i).getWeight();
185 if ((double)weight < (double)moves.at(i-1).getWeight()*ratio)
186 break;
187 min = weight;
188 }
189 }
190 }
191 // Do not play 0-weighted moves.
192 if (min == 0) min = 1;
193
194 WMoveContainer::iterator each = moves.begin();
195 for (; each != moves.end(); ++each)
196 {
197 if (each->getWeight() < min)
198 break;
199 }
200 moves.erase(each, moves.end());
201 }
202
203 if (moves.empty() || depth > max_depth) // found leaves
204 {
205 const osl::state::NumEffectState state(book.getBoard(stateIndex));
206 const int value = qsearch(state, osl::Move::PASS(alt(state.turn())));
207
208 if ( (the_player == osl::BLACK && value < -1 * error_threshold) ||
209 (the_player == osl::WHITE && value > error_threshold) )
210 {
211 ++finishing_games;
212 if (is_dump)
213 {
214 std::cerr << std::endl;
215 std::cerr << "eval: " << value << std::endl;
216 printer.print(state);
217 std::cerr << "piece value:" << osl::PieceEval(state).value() << "\n" << state;
218 }
219 }
220 else
221 {
222 evals.push_back(eval_depth_t(value, depth));
223 }
224 continue;
225 }
226
227 // 結果の再現性を高めるため、visitの順番を決める
228 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveMoveSort());
229
230 // recursively search the tree
231 for (std::vector<osl::record::opening::WMove>::const_iterator each = moves.begin();
232 each != moves.end(); ++each)
233 {
234 // consistancy check
235 const osl::SimpleState state(book.getBoard(stateIndex));
236 const osl::hash::HashKey hash(state);
237 const int nextIndex = each->getStateIndex();
238 const osl::SimpleState next_state(book.getBoard(nextIndex));
239 const osl::hash::HashKey next_hash(next_state);
240 const osl::hash::HashKey moved_hash = hash.newMakeMove(each->getMove());
241 if (moved_hash != next_hash)
242 throw std::string("Illegal move found.");
243
244 if (! states[nextIndex])
245 stateToVisit.push_back(state_depth_t(nextIndex, depth+1));
246 } // each wmove
247 } // while loop
248
249 // Show the result
250 std::cout << std::endl;
251 std::cout << boost::format("Book: %s\n") % file_name;
252 std::cout << boost::format("Player: %s\n") % the_player;
253 std::cout << "FU=128 points\n";
254 std::cout <<
255 boost::format("#states: %d (+ %d finishing games over %d points; max %d)\n")
256 % evals.size()
257 % finishing_games
258 % error_threshold
259 % max_depth;
260 {
261 std::cout << "Eval\n";
262 std::deque<int> tmp;
263 for (std::deque<eval_depth_t>::const_iterator each = evals.begin();
264 each != evals.end(); ++each)
265 tmp.push_back(each->first);
266 showStatistics(tmp);
267 }
268 {
269 std::cout << "Depth\n";
270 std::deque<int> tmp;
271 for (std::deque<eval_depth_t>::const_iterator each = evals.begin();
272 each != evals.end(); ++each)
273 tmp.push_back(each->second);
274 showStatistics(tmp);
275 }
276 if (state_to_compare)
277 {
278 std::cout << "\nthe state hits: " << state_count << std::endl;
279 printer.print(*state_to_compare);
280
281 const int stateIndex = book.getStateIndex(*state_to_compare);
282 const std::vector<int> parents = book.getParents(stateIndex);
283 if (parents.empty())
284 {
285 std::cout << "\nNo parent\n";
286 }
287 else
288 {
289 int i = 0;
290 for (std::vector<int>::const_iterator each = parents.begin();
291 each != parents.end(); ++each, ++i)
292 {
293 std::cout << boost::format("\n--- Parent: %g ---\n ") % i;
294 showInfoOfState(book, *each);
295 }
296 }
297
298 }
299 }
300
301
302 int main(int argc, char **argv)
303 {
304 std::string player_str;
305 std::string file_name;
306 size_t csa_move_index;
307 std::string csa_file_name;
308
309 bp::options_description command_line_options;
310 command_line_options.add_options()
311 ("player,p", bp::value<std::string>(&player_str)->default_value("black"),
312 "specify a player, black or white, in whose point of view the book is validated. "
313 "default black.")
314 ("input-file,f", bp::value<std::string>(&file_name)->default_value("./joseki.dat"),
315 "a joseki file to validate.")
316 ("dump", bp::value<bool>(&is_dump)->default_value(false),
317 "dump finishing games' states")
318 ("threshold", bp::value<int>(&error_threshold)->default_value(500),
319 "threshold of evaluatoin value to recognize a finishing game.")
320 ("determinate", bp::value<int>(&is_determinate)->default_value(0),
321 "only search the top n moves. (0 for all, 1 for determinate).")
322 ("non-determinate-depth", bp::value<int>(&non_determinate_depth)->default_value(100),
323 "use the best move where the depth is greater than this value")
324 ("max-depth", bp::value<int>(&max_depth)->default_value(100),
325 "do not go beyond this depth from the root")
326 ("ratio", bp::value<double>(&ratio)->default_value(0.0),
327 "skip move[i] (i >= n), if weight[n] < weight[n-1]*ratio")
328 ("csa-move", bp::value<size_t>(&csa_move_index)->default_value(1),
329 "n-th-move state in the csa file")
330 ("csa", bp::value<std::string>(&csa_file_name)->default_value(""),
331 "a csa file name. See if a state in the game exists in the book or not.")
332 ("quick", bp::value<bool>(&is_quick)->default_value(false),
333 "skip quiescence search.")
334 ("verbose,v", "output verbose messages.")
335 ("help,h", "show this help message.");
336 bp::positional_options_description p;
337 p.add("input-file", 1);
338
339 try
340 {
341 bp::store(
342 bp::command_line_parser(
343 argc, argv).options(command_line_options).positional(p).run(), vm);
344 bp::notify(vm);
345 if (vm.count("help"))
346 {
347 printUsage(std::cout, argv, command_line_options);
348 return 0;
349 }
350 }
351 catch (std::exception &e)
352 {
353 std::cerr << "error in parsing options\n"
354 << e.what() << std::endl;
355 printUsage(std::cerr, argv, command_line_options);
356 return 1;
357 }
358
359 if (player_str == "black")
360 the_player = osl::BLACK;
361 else if (player_str == "white")
362 the_player = osl::WHITE;
363 else
364 {
365 printUsage(std::cerr, argv, command_line_options);
366 return 1;
367 }
368
369 if (!csa_file_name.empty())
370 {
371 is_quick = true;
372 const osl::record::csa::CsaFile csa(csa_file_name);
373 const osl::record::Record record = csa.getRecord();
374 const osl::stl::vector<osl::Move> moves = record.getMoves();
375 const osl::SimpleState initialState = record.getInitialState();
376 state_to_compare.reset(new osl::NumEffectState(initialState));
377
378 if (csa_move_index < 1) csa_move_index = 1;
379 if (csa_move_index > moves.size()) csa_move_index = moves.size();
380 if ( (the_player == osl::BLACK && csa_move_index%2 == 0) ||
381 (the_player == osl::WHITE && csa_move_index%2 == 1) )
382 {
383 std::cout << "Invalid csa move index: " << csa_move_index << std::endl;
384 return -1;
385 }
386 for (size_t i=0; i < csa_move_index; i++)
387 {
388 const osl::Move& move = moves[i];
389 state_to_compare->makeMove(move);
390 }
391 }
392
393 doMain(file_name);
394
395 return 0;
396 }
397 // ;;; Local Variables:
398 // ;;; mode:c++
399 // ;;; c-basic-offset:2
400 // ;;; End: