Modified colors for compatibility with Tektronix 4107 terminal in ANSI mode.
[sgk-go] / interface / play_solo.c
CommitLineData
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
39void
40play_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
143void
144load_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
186void
187load_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 */