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