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
play_solo(Gameinfo * gameinfo,int moves)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
load_and_analyze_sgf_file(Gameinfo * gameinfo)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
load_and_score_sgf_file(SGFTree * tree,Gameinfo * gameinfo,const char * scoringmode)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  */
324