Commit | Line | Data |
---|---|---|
7eeb782e AT |
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ |
2 | * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * | |
3 | * http://www.gnu.org/software/gnugo/ for more information. * | |
4 | * * | |
5 | * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * | |
6 | * 2008 and 2009 by the Free Software Foundation. * | |
7 | * * | |
8 | * This program is free software; you can redistribute it and/or * | |
9 | * modify it under the terms of the GNU General Public License as * | |
10 | * published by the Free Software Foundation - version 3 or * | |
11 | * (at your option) any later version. * | |
12 | * * | |
13 | * This program is distributed in the hope that it will be useful, * | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
16 | * GNU General Public License in file COPYING for more details. * | |
17 | * * | |
18 | * You should have received a copy of the GNU General Public * | |
19 | * License along with this program; if not, write to the Free * | |
20 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * | |
21 | * Boston, MA 02111, USA. * | |
22 | \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
23 | ||
24 | #include "gnugo.h" | |
25 | ||
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
29 | #include <math.h> | |
30 | ||
31 | #include "interface.h" | |
32 | ||
33 | #include "liberty.h" /* to get to the stats */ | |
34 | ||
35 | #include "sgftree.h" | |
36 | #include "random.h" | |
37 | #include "gg_utils.h" | |
38 | ||
39 | void | |
40 | play_solo(Gameinfo *gameinfo, int moves) | |
41 | { | |
42 | SGFTree sgftree; | |
43 | int passes = 0; /* num. consecutive passes */ | |
44 | float move_value; | |
45 | double t1, t2; | |
46 | int save_moves = moves; | |
47 | ||
48 | struct stats_data totalstats; | |
49 | int total_owl_count = 0; | |
50 | ||
51 | /* It tends not to be very imaginative in the opening, | |
52 | * so we scatter a few stones randomly to start with. | |
53 | * We add two random numbers to reduce the probability | |
54 | * of playing stones near the edge. | |
55 | */ | |
56 | ||
57 | int n = 6 + 2*gg_rand()%5; | |
58 | int i, j; | |
59 | ||
60 | komi = 5.5; | |
61 | ||
62 | sgftree_clear(&sgftree); | |
63 | sgftreeCreateHeaderNode(&sgftree, board_size, komi, handicap); | |
64 | sgf_write_header(sgftree.root, 1, get_random_seed(), 5.5, handicap, | |
65 | get_level(), chinese_rules); | |
66 | ||
67 | /* Generate some random moves. */ | |
68 | if (board_size > 6) { | |
69 | do { | |
70 | do { | |
71 | i = (gg_rand() % 4) + (gg_rand() % (board_size - 4)); | |
72 | j = (gg_rand() % 4) + (gg_rand() % (board_size - 4)); | |
73 | } while (!is_allowed_move(POS(i, j), gameinfo->to_move)); | |
74 | ||
75 | gnugo_play_move(POS(i, j), gameinfo->to_move); | |
76 | sgftreeAddPlay(&sgftree, gameinfo->to_move, i, j); | |
77 | sgftreeAddComment(&sgftree, "random move"); | |
78 | gameinfo->to_move = OTHER_COLOR(gameinfo->to_move); | |
79 | } while (--n > 0); | |
80 | } | |
81 | ||
82 | t1 = gg_cputime(); | |
83 | memset(&totalstats, '\0', sizeof(totalstats)); | |
84 | while (passes < 2 && --moves >= 0) { | |
85 | int move; | |
86 | reset_owl_node_counter(); | |
87 | move = genmove(gameinfo->to_move, &move_value, NULL); | |
88 | ||
89 | gnugo_play_move(move, gameinfo->to_move); | |
90 | sgffile_add_debuginfo(sgftree.lastnode, move_value); | |
91 | sgftreeAddPlay(&sgftree, gameinfo->to_move, I(move), J(move)); | |
92 | sgffile_output(&sgftree); | |
93 | gameinfo->to_move = OTHER_COLOR(gameinfo->to_move); | |
94 | ||
95 | if (move == PASS_MOVE) { | |
96 | passes++; | |
97 | printf("%s(%d): Pass\n", gameinfo->to_move == BLACK ? "Black" : "White", | |
98 | movenum); | |
99 | } | |
100 | else { | |
101 | passes = 0; | |
102 | gprintf("%s(%d): %1m\n", gameinfo->to_move == BLACK ? "Black" : "White", | |
103 | movenum, move); | |
104 | } | |
105 | ||
106 | totalstats.nodes += stats.nodes; | |
107 | totalstats.read_result_entered += stats.read_result_entered; | |
108 | totalstats.read_result_hits += stats.read_result_hits; | |
109 | totalstats.trusted_read_result_hits += stats.trusted_read_result_hits; | |
110 | total_owl_count += get_owl_node_counter(); | |
111 | } | |
112 | t2 = gg_cputime(); | |
113 | ||
114 | /* Two passes and it's over. (EMPTY == BOTH) */ | |
115 | who_wins(EMPTY, stdout); | |
116 | ||
117 | { | |
118 | float score = gnugo_estimate_score(NULL, NULL); | |
119 | sgfWriteResult(sgftree.root, score, 1); | |
120 | } | |
121 | sgffile_output(&sgftree); | |
122 | ||
123 | printf("%10d moves played in %0.3f seconds\n", save_moves-moves, t2-t1); | |
124 | if (save_moves != moves) | |
125 | printf("%10.3f seconds/move\n", (t2-t1)/(save_moves-moves)); | |
126 | ||
127 | printf("%10d nodes\n", totalstats.nodes); | |
128 | printf("%10d read results entered\n", totalstats.read_result_entered); | |
129 | printf("%10d read result hits\n", totalstats.read_result_hits); | |
130 | printf("%10d trusted read result hits\n", | |
131 | totalstats.trusted_read_result_hits); | |
132 | printf("%10d owl nodes\n", total_owl_count); | |
133 | } | |
134 | ||
135 | ||
136 | /* ================================================================ */ | |
137 | ||
138 | ||
139 | /* | |
140 | * Load SGF file and run genmove(). | |
141 | */ | |
142 | ||
143 | void | |
144 | load_and_analyze_sgf_file(Gameinfo *gameinfo) | |
145 | { | |
146 | SGFTree sgftree; | |
147 | int move; | |
148 | int next; | |
149 | float move_value; | |
150 | ||
151 | next = gameinfo->to_move; | |
152 | sgftree = gameinfo->game_record; | |
153 | ||
154 | if (metamachine) | |
155 | sgffile_begindump(&sgftree); | |
156 | ||
157 | move = genmove(next, &move_value, NULL); | |
158 | ||
159 | gprintf("%s move %1m\n", next == WHITE ? "white (O)" : "black (X)", move); | |
160 | ||
161 | if (metamachine) | |
162 | sgffile_enddump(outfilename); | |
163 | else { | |
164 | gnugo_play_move(move, next); | |
165 | sgftreeAddPlay(&sgftree, next, I(move), J(move)); | |
166 | sgftreeAddComment(&sgftree, "load and analyze mode"); | |
167 | sgffile_add_debuginfo(sgftree.lastnode, move_value); | |
168 | sgffile_output(&sgftree); | |
169 | } | |
170 | } | |
171 | ||
172 | ||
173 | /* | |
174 | * Load SGF file and score the game | |
175 | * scoringmode: | |
176 | * estimate - estimate territorial balance | |
177 | * finish - finish the game by selfplaying and then count the score quickly | |
178 | * aftermath - like 'finish' but also play out the aftermath in order to | |
179 | * get an accurate score | |
180 | */ | |
181 | ||
182 | #define ESTIMATE 0 | |
183 | #define FINISH 1 | |
184 | #define AFTERMATH 2 | |
185 | ||
186 | void | |
187 | load_and_score_sgf_file(SGFTree *tree, Gameinfo *gameinfo, | |
188 | const char *scoringmode) | |
189 | { | |
190 | int move; | |
191 | float move_value; | |
192 | char *tempc = NULL; | |
193 | char text[250]; | |
194 | char winner; | |
195 | int next; | |
196 | int pass = 0; | |
197 | int method; | |
198 | float score; | |
199 | SGFTree local_tree; | |
200 | SGFTree *score_tree = tree; | |
201 | ||
202 | /* Default scoring method is ESTIMATE since it's fastest. */ | |
203 | method = ESTIMATE; | |
204 | if (strcmp(scoringmode, "finish") == 0) | |
205 | method = FINISH; | |
206 | else if (strcmp(scoringmode, "aftermath") == 0) | |
207 | method = AFTERMATH; | |
208 | ||
209 | /* For aftermath scoring we compress the previous moves to a static | |
210 | * board position in the output sgf. This helps a lot when debugging | |
211 | * scoring mistakes. We don't do this for the finish method, | |
212 | * however, since users may be better served by having GNU Go's | |
213 | * selfplay added to the original game record. | |
214 | */ | |
215 | if (method == AFTERMATH) { | |
216 | sgftree_clear(&local_tree); | |
217 | /* Modify komi to compensate for captured stones. We start at a | |
218 | * setup position and since there is no standard sgf property to | |
219 | * tell the number of captured stones, a modified komi is the best | |
220 | * available solution. | |
221 | */ | |
222 | sgftreeCreateHeaderNode(&local_tree, board_size, | |
223 | komi + black_captured - white_captured, handicap); | |
224 | sgffile_printboard(&local_tree); | |
225 | sgfAddProperty(local_tree.lastnode, "PL", | |
226 | gameinfo->to_move == WHITE ? "W" : "B"); | |
227 | score_tree = &local_tree; | |
228 | } | |
229 | ||
230 | next = gameinfo->to_move; | |
231 | reset_engine(); | |
232 | ||
233 | /* Complete the game by selfplay for the finish and aftermath methods. */ | |
234 | if (method != ESTIMATE) { | |
235 | doing_scoring = 1; | |
236 | while (pass < 2) { | |
237 | move = genmove_conservative(next, &move_value); | |
238 | if (move != PASS_MOVE) { | |
239 | pass = 0; | |
240 | gprintf("%d %s move %1m\n", movenum, | |
241 | next == WHITE ? "white (O)" : "black (X)", move); | |
242 | } | |
243 | else { | |
244 | pass++; | |
245 | gprintf("%d %s move PASS\n", movenum, | |
246 | next == WHITE ? "white (O)" : "black (X)"); | |
247 | } | |
248 | play_move(move, next); | |
249 | sgffile_add_debuginfo(score_tree->lastnode, move_value); | |
250 | sgftreeAddPlay(score_tree, next, I(move), J(move)); | |
251 | sgffile_output(score_tree); | |
252 | next = OTHER_COLOR(next); | |
253 | } | |
254 | doing_scoring = 0; | |
255 | } | |
256 | ||
257 | /* Calculate the score. */ | |
258 | if (method == AFTERMATH) | |
259 | score = aftermath_compute_score(next, score_tree); | |
260 | else | |
261 | score = gnugo_estimate_score(NULL, NULL); | |
262 | ||
263 | if (score < 0.0) { | |
264 | sprintf(text, "Black wins by %1.1f points\n", -score); | |
265 | winner = 'B'; | |
266 | } | |
267 | else if (score > 0.0) { | |
268 | sprintf(text, "White wins by %1.1f points\n", score); | |
269 | winner = 'W'; | |
270 | } | |
271 | else { | |
272 | sprintf(text, "Jigo\n"); | |
273 | winner = '0'; | |
274 | } | |
275 | fputs(text, stdout); | |
276 | sgftreeAddComment(score_tree, text); | |
277 | ||
278 | /* For the finish and aftermath methods we compare the score with | |
279 | * what's stored in the game record. | |
280 | * | |
281 | * FIXME: No comparison is made if the stored result was 0. Wins by | |
282 | * time or forfeit are not handled either. | |
283 | * | |
284 | * FIXME: Does anybody actually care about this information? Just | |
285 | * removing this piece of code is a tempting alternative. | |
286 | */ | |
287 | if (method != ESTIMATE && sgfGetCharProperty(tree->root, "RE", &tempc)) { | |
288 | char dummy; | |
289 | float result; | |
290 | if (sscanf(tempc, "%1c%f", &dummy, &result) == 2) { | |
291 | fprintf(stdout, "Result from file: %c+%1.1f\n", dummy, result); | |
292 | fputs("GNU Go result and result from file are ", stdout); | |
293 | if (result == fabs(score) && winner == dummy) | |
294 | fputs("identical\n", stdout); | |
295 | else | |
296 | fputs("different\n", stdout); | |
297 | ||
298 | } | |
299 | else { | |
300 | if (tempc[2] == 'R') { | |
301 | fprintf(stdout, "Result from file: Resign\n"); | |
302 | fputs("GNU Go result and result from file are ", stdout); | |
303 | if (tempc[0] == winner) | |
304 | fputs("identical\n", stdout); | |
305 | else | |
306 | fputs("different\n", stdout); | |
307 | } | |
308 | } | |
309 | } | |
310 | ||
311 | if (method != ESTIMATE) | |
312 | sgfWriteResult(score_tree->root, score, 1); | |
313 | ||
314 | sgffile_output(score_tree); | |
315 | } | |
316 | ||
317 | ||
318 | /* | |
319 | * Local Variables: | |
320 | * tab-width: 8 | |
321 | * c-basic-offset: 2 | |
322 | * End: | |
323 | */ |