compare-book.cc Source File

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
compare-book.cc
Go to the documentation of this file.
1 // compare-book.cc
2 #include "osl/record/opening/openingBook.h"
3 #include "osl/record/compactBoard.h"
4 #include "osl/record/csa.h"
5 #include "osl/record/kanjiPrint.h"
6 #include "osl/search/quiescenceSearch2.h"
7 #include "osl/search/quiescenceSearch2.tcc"
8 #include "osl/search/simpleHashTable.h"
9 #include "osl/eval/pieceEval.h"
10 #include "osl/stl/vector.h"
11 #include "osl/stl/hash_map.h"
12 #include "osl/misc/math.h"
13 #include "osl/search/fixedEval.h"
14 #include <boost/shared_ptr.hpp>
15 #include <boost/program_options.hpp>
16 #include <boost/progress.hpp>
17 #include <boost/format.hpp>
18 #include <iostream>
19 #include <deque>
20
21 #include "osl/move.h"
22 #include "osl/record/csaRecord.h"
23 #include "osl/record/record.h"
24 #include "osl/state/numEffectState.h"
25 #include <boost/shared_ptr.hpp>
26
27 typedef std::vector<osl::record::opening::WMove> WMoveContainer;
28
29 osl::Player the_player = osl::BLACK;
30 std::string dump_mode = "none";
31 int is_determinate = 0; // test only top n moves. 0 for all
32 int max_depth, non_determinate_depth;
33 double ratio; // use moves[n+1] when the weight[n+1] >= ratio*weight[n]
34
35 size_t state_count = 0;
36
37 void printUsage(std::ostream& out,
38 char **argv,
39 const boost::program_options::options_description& command_line_options)
40 {
41 out << "Usage: " << argv[0] << " [options] <book-a.dat> <book-b.dat>\n"
42 << command_line_options
43 << std::endl;
44 }
45
46 typedef osl::hash_map<osl::HashKey,int> table_t;
47 void store(osl::record::opening::WeightedBook& book, table_t& table, osl::vector<int>& parents)
48 {
49 WMoveContainer moves = book.getMoves(book.getStartState());
50 parents.resize(book.getTotalState());
51 std::fill(parents.begin(), parents.end(), -1);
52 boost::progress_display progress(book.getTotalState());
53
54 typedef std::pair<int, int> state_depth_t;
55 std::deque<state_depth_t> stateToVisit;
56 stateToVisit.push_back(state_depth_t(book.getStartState(), 1)); // root is 1
57 // depth-1手目からdepth手目のstate。depth手目はまだ指されていない（これか
58 // らdepth手目）
59
60 typedef std::pair<int, int> eval_depth_t;
61 long leaves = 0;
62 int depth_found = 0;
63 while (!stateToVisit.empty())
64 {
65 const state_depth_t state_depth = stateToVisit.front();
66 const int stateIndex = state_depth.first;
67 const int depth = state_depth.second;
68 stateToVisit.pop_front();
69 ++progress;
70 assert(parents[stateIndex] >= 0 || stateIndex == book.getStartState());
71
72 depth_found = std::max(depth_found, depth);
73 WMoveContainer moves = book.getMoves(stateIndex);
74
75 // 自分（the_player）の手番では、良い手のみ指す
76 // 相手はどんな手を指してくるか分からない
77 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveWeightMoveSort());
78 if ( !moves.empty() &&
79 ((the_player == osl::BLACK && depth % 2 == 1) ||
80 (the_player == osl::WHITE && depth % 2 == 0)) )
81 {
82 int min = 1;
83 if (is_determinate)
84 {
85 min = moves.at(0).getWeight();
86 if (depth <= non_determinate_depth)
87 {
88 for (int i=1; i<=std::min(is_determinate, (int)moves.size()-1); ++i)
89 {
90 const int weight = moves.at(i).getWeight();
91 if ((double)weight < (double)moves.at(i-1).getWeight()*ratio)
92 break;
93 min = weight;
94 }
95 }
96 }
97 // Do not play 0-weighted moves.
98 if (min == 0) min = 1;
99
100 WMoveContainer::iterator each = moves.begin();
101 for (; each != moves.end(); ++each)
102 {
103 if (each->getWeight() < min)
104 break;
105 }
106 moves.erase(each, moves.end());
107 }
108
109 if (moves.empty() || depth > max_depth) // found leaves
110 {
111 ++leaves;
112 continue;
113 }
114
115 if (moves[0].getWeight()) {
116 // not leaf
117 const osl::state::NumEffectState state(book.getBoard(stateIndex));
118 const osl::HashKey key(state);
119 table[key] = stateIndex;
120 }
121
122
123 // recursively search the tree
124 for (std::vector<osl::record::opening::WMove>::const_iterator each = moves.begin();
125 each != moves.end(); ++each)
126 {
127 const int nextIndex = each->getStateIndex();
128 if (parents[nextIndex] < 0) {
129 parents[nextIndex] = stateIndex;
130 stateToVisit.push_back(state_depth_t(nextIndex, depth+1));
131 }
132 } // each wmove
133 } // while loop
134
135 // Show the result
136 std::cout << std::endl;
137 std::cout << boost::format("Player: %s\n") % the_player;
138 std::cout <<
139 boost::format("#leaves: %d, max_depth %d\n")
140 % leaves
141 % depth_found;
142 }
143
144 void show_moves(const char *name, osl::record::opening::WeightedBook& book, int node)
145 {
146 WMoveContainer moves = book.getMoves(node);
147 std::sort(moves.begin(), moves.end(), osl::record::opening::WMoveWeightMoveSort());
148
149 if (! moves.empty() && moves[0].getWeight()) {
150 std::cout << name;
151 for (size_t i=0; i<moves.size(); ++i) {
152 if (moves[i].getWeight() == 0)
153 break;
154 const int next_state_index = moves[i].getStateIndex();
155 const int black_win = book.getBlackWinCount(next_state_index);
156 const int white_win = book.getWhiteWinCount(next_state_index);
157 std::cout << " " << osl::record::csa::show(moves[i].getMove())
158 << "(" << moves[i].getWeight() << "," << black_win << "," << white_win << ")";
159 }
160 std::cout << "\n";
161 }
162 }
163
164 void show_history(const osl::MoveVector& history)
165 {
166 std::cout << "[" << history.size() << "]";
167 for (size_t i=0; i<history.size(); ++i)
168 std::cout << " " << osl::record::csa::show(history[i]);
169 std::cout << std::endl;
170 }
171
172 osl::MoveVector make_history(osl::record::opening::WeightedBook& book, const osl::vector<int>& parents, int node)
173 {
174 osl::vector<int> history;
175 history.push_back(node);
176 while (parents[node] >= 0) {
177 node = parents[node];
178 history.push_back(node);
179 }
180 std::reverse(history.begin(), history.end());
181 assert(book.getStartState() == history[0]);
182
183 osl::MoveVector result;
184 for (size_t i=0; i<history.size()-1; ++i) {
185 const WMoveContainer& moves = book.getMoves(history[i]);
186 for (WMoveContainer::const_iterator p=moves.begin(); p!=moves.end(); ++p) {
187 if (p->getStateIndex() != history[i+1])
188 continue;
189 result.push_back(p->getMove());
190 break;
191 }
192 }
193 return result;
194 }
195
196 void dump(osl::record::opening::WeightedBook& book_a, const osl::vector<int>& parents_a, int node_a,
197 osl::record::opening::WeightedBook& book_b, const osl::vector<int>& parents_b, int node_b)
198 {
199 const osl::state::NumEffectState state(book_a.getBoard(node_a));
200 osl::record::KanjiPrint printer(std::cout,
201 boost::shared_ptr<osl::record::Characters>(
202 new osl::record::KIFCharacters())
203 );
204 printer.print(state);
205 const osl::MoveVector history_a = make_history(book_a, parents_a, node_a);
206 const osl::MoveVector history_b = make_history(book_b, parents_b, node_b);
207 show_history(history_a);
208 if (! (history_a == history_b))
209 show_history(history_b);
210 show_moves("a", book_a, node_a);
211 show_moves("b", book_b, node_b);
212 }
213
214 void dump(const char *name, osl::record::opening::WeightedBook& book, const osl::vector<int>& parents, int node)
215 {
216 const osl::state::NumEffectState state(book.getBoard(node));
217 osl::record::KanjiPrint printer(std::cout,
218 boost::shared_ptr<osl::record::Characters>(
219 new osl::record::KIFCharacters())
220 );
221 printer.print(state);
222 show_history(make_history(book, parents, node));
223 show_moves(name, book, node);
224 }
225
226 bool is_same_node(osl::record::opening::WeightedBook& book_a, int node_a,
227 osl::record::opening::WeightedBook& book_b, int node_b)
228 {
229 WMoveContainer moves_a = book_a.getMoves(node_a);
230 WMoveContainer moves_b = book_b.getMoves(node_b);
231
232 std::sort(moves_a.begin(), moves_a.end(), osl::record::opening::WMoveWeightMoveSort());
233 std::sort(moves_b.begin(), moves_b.end(), osl::record::opening::WMoveWeightMoveSort());
234
235 size_t i=0;
236 for (; i<std::min(moves_a.size(), moves_b.size()); ++i) {
237 if (moves_a[i].getWeight() == 0)
238 return moves_b[i].getWeight() == 0;
239 if (moves_b[i].getWeight() == 0)
240 return false;
241 if (moves_a[i].getMove() != moves_b[i].getMove())
242 return false;
243 }
244 if (i == moves_a.size())
245 return i == moves_b.size() || moves_b[i].getWeight() == 0;
246 return moves_a[i].getWeight() == 0;
247 }
248
249 void compare(osl::record::opening::WeightedBook& book_a, const table_t& table_a, const osl::vector<int>& parents_a,
250 osl::record::opening::WeightedBook& book_b, const table_t& table_b, const osl::vector<int>& parents_b)
251 {
252 long only_a = 0, only_b = 0, same = 0, diff = 0;
253 for (table_t::const_iterator p=table_a.begin(); p!=table_a.end(); ++p) {
254 table_t::const_iterator q=table_b.find(p->first);
255 if (q == table_b.end()) {
256 ++only_a;
257 if (dump_mode == "a")
258 dump("a", book_a, parents_a, p->second);
259 continue;
260 }
261 if (is_same_node(book_a, p->second, book_b, q->second))
262 ++same;
263 else {
264 ++diff;
265 if (dump_mode == "common")
266 dump(book_a, parents_a, p->second,
267 book_b, parents_b, q->second);
268 }
269 }
270 for (table_t::const_iterator p=table_b.begin(); p!=table_b.end(); ++p) {
271 table_t::const_iterator q=table_a.find(p->first);
272 if (q == table_a.end()) {
273 ++only_b;
274 if (dump_mode == "b")
275 dump("b", book_b, parents_b, p->second);
276 continue;
277 }
278 }
279 std::cout << "same " << same << " diff " << diff
280 << " only-in-a " << only_a << " only-in-b " << only_b << std::endl;
281 }
282
283 int main(int argc, char **argv)
284 {
285 std::string player_str;
286
287 namespace bp = boost::program_options;
288 bp::variables_map vm;
289 bp::options_description command_line_options;
290 command_line_options.add_options()
291 ("player,p", bp::value<std::string>(&player_str)->default_value("black"),
292 "specify a player, black or white, in whose point of view the book is validated. "
293 "default black.")
294 ("input-file,f", bp::value<std::vector<std::string> >(),
295 "a joseki file to validate.")
296 ("dump", bp::value<std::string>(&dump_mode)->default_value(dump_mode),
297 "common: dump positions where two books have different moves\n"
298 "(a|b): dump positions registered to only book_[ab]\n")
299 ("determinate", bp::value<int>(&is_determinate)->default_value(0),
300 "only search the top n moves. (0 for all, 1 for determinate).")
301 ("non-determinate-depth", bp::value<int>(&non_determinate_depth)->default_value(100),
302 "use the best move where the depth is greater than this value")
303 ("max-depth", bp::value<int>(&max_depth)->default_value(100),
304 "do not go beyond this depth from the root")
305 ("ratio", bp::value<double>(&ratio)->default_value(0.0),
306 "skip move[i] (i >= n), if weight[n] < weight[n-1]*ratio")
307 ("help,h", "show this help message.");
308 bp::positional_options_description p;
309 p.add("input-file", -1);
310
311 std::vector<std::string> filenames;
312 try
313 {
314 bp::store(
315 bp::command_line_parser(
316 argc, argv).options(command_line_options).positional(p).run(), vm);
317 bp::notify(vm);
318 filenames = vm["input-file"].as<std::vector<std::string> >();
319 if (vm.count("help") || filenames.size() != 2
320 || (dump_mode != "none" && dump_mode != "a" && dump_mode != "b" && dump_mode != "common"))
321 {
322 printUsage(std::cout, argv, command_line_options);
323 return 0;
324 }
325 }
326 catch (std::exception &e)
327 {
328 std::cerr << "error in parsing options\n"
329 << e.what() << std::endl;
330 printUsage(std::cerr, argv, command_line_options);
331 return 1;
332 }
333
334 if (player_str == "black")
335 the_player = osl::BLACK;
336 else if (player_str == "white")
337 the_player = osl::WHITE;
338 else
339 {
340 printUsage(std::cerr, argv, command_line_options);
341 return 1;
342 }
343
344 osl::record::opening::WeightedBook book_a(filenames[0].c_str()), book_b(filenames[1].c_str());
345 osl::CArray<osl::vector<int>,2> parents;
346 osl::CArray<table_t,2> tables;
347 std::cout << boost::format("Book: %s\n") % filenames[0];
348 store(book_a, tables[0], parents[0]);
349 std::cout << boost::format("Book: %s\n") % filenames[1];
350 store(book_b, tables[1], parents[1]);
351
352 compare(book_a, tables[0], parents[0], book_b, tables[1], parents[1]);
353 return 0;
354 }
355 // ;;; Local Variables:
356 // ;;; mode:c++
357 // ;;; c-basic-offset:2
358 // ;;; End: