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 
25 /* The functions in this file implement a mechanism whereby
26  * GNU Go can fork a second gnugo process, called the oracle.
27  * The two processes communicate by means of the GTP.
28  * The functions oracle_trymove() and oracle_popgo() call
29  * trymove and popgo in the primary gnugo processes but
30  * actually play and undo the move in the oracle. This
31  * the oracle can be queried for information which is
32  * normally only available at the top level.
33  */
34 
35 #include "config.h"
36 
37 #if ORACLE
38 
39 #include "gnugo.h"
40 #include "liberty.h"
41 #include "patterns.h"
42 
43 #include <stdio.h>
44 #include <unistd.h>
45 #include <string.h>
46 #include <stdlib.h>
47 #include <stdarg.h>
48 
49 #define USE_POSIX 1
50 
51 FILE *to_gnugo_stream, *from_gnugo_stream;
52 char gnugo_line[128];
53 int gnugo_line_length;
54 
55 int pfd_a[2];
56 int pfd_b[2];
57 
58 #define TELL_ORACLE(x, args...) do { \
59               if (debug & DEBUG_ORACLE_STREAM) fprintf(stderr, x, ##args);  \
60                 if (fprintf(to_gnugo_stream, x, ##args) < 0) \
61                    error("can't write command in to_gnugo_stream"); \
62                 fflush(to_gnugo_stream); \
63               } while (0)
64 
65 #define ASK_ORACLE    do { \
66                         gnugo_line_length = 0;   \
67                         while (gnugo_line_length != 1) { \
68                           if (!fgets(gnugo_line, 128, from_gnugo_stream)) \
69                              error("can't get response"); \
70                           gnugo_line_length = strlen(gnugo_line); \
71                           if (debug & DEBUG_ORACLE_STREAM) \
72                             fprintf(stderr, gnugo_line); \
73                           } \
74                         } while (0)
75 
76 #define MAX_ORACLE_MOVES 10
77 
78 struct oracle_move_data {
79   int pos;            /* move coordinate */
80   int color;          /* color to play */
81   int value;          /* value */
82   int ab_value;       /* alpha-beta value */
83   const char *reason; /* why this move */
84 };
85 
86 static void oracle_callback(int anchor, int color, struct pattern *pattern,
87 			    int ll, void *data);
88 static void oracle_add_move(struct oracle_move_data *moves,
89 			    int this_move, int this_value,
90 			    const char *this_reason);
91 void do_consult_oracle(int color);
92 void error(const char *msg);
93 static int oracle_trymove(int pos, int color, const char *message, int str,
94 			  int komaster, int kom_pos);
95 static void oracle_popgo(void);
96 static void tell_oracle(const char *fmt, ...);
97 static void ask_oracle(void);
98 static int search_width(void);
99 
100 
101 /*** * *** * *** * *** * *** * *** * *** * *** * *** * *** * *** * ***\
102  *                    Primary Oracle Functions                       *
103 \*** * *** * *** * *** * *** * *** * *** * *** * *** * *** * *** * ***/
104 
105 /* Forks and attaches pipes to a new GNU Go process in gtp mode.
106  * Loads the sgf file
107  */
108 
109 void
summon_oracle(void)110 summon_oracle(void)
111 {
112   if (pipe(pfd_a) == -1)
113     error("can't open pipe a");
114   if (pipe(pfd_b) == -1)
115     error("can't open pipe b");
116   switch (fork()) {
117   case -1:
118     error("fork failed (try chopsticks)");
119   case 0:
120     /* Attach pipe a to stdin */
121     if (dup2(pfd_a[0], 0) == -1)
122       error("dup pfd_a[0] failed");
123     /* attach pipe b to stdout" */
124     if (dup2(pfd_b[1], 1) == -1)
125       error("dup pfd_b[1] failed");
126     execlp("gnugo", "gnugo", "--mode", "gtp", "--quiet", NULL);
127     error("execlp failed");
128   }
129   oracle_exists = 1;
130   /* Attach pipe a to to_gnugo_stream  */
131   to_gnugo_stream = (FILE *) fdopen(pfd_a[1], "w");
132   /* Attach pipe b to from_gnugo_stream */
133   from_gnugo_stream = (FILE *) fdopen(pfd_b[0], "r");
134 }
135 
136 /* load an sgf file */
137 
138 void
oracle_loadsgf(char * infilename,char * untilstring)139 oracle_loadsgf(char *infilename, char *untilstring)
140 {
141   if (untilstring)
142     TELL_ORACLE("loadsgf %s %s\n", infilename, untilstring);
143   else
144     TELL_ORACLE("loadsgf %s\n", infilename);
145   ASK_ORACLE;
146   fflush(to_gnugo_stream);
147   gnugo_line_length = 0;
148 }
149 
150 /* Tell the oracle to go away. */
151 
152 void
dismiss_oracle(void)153 dismiss_oracle(void)
154 {
155   if (oracle_exists)
156     TELL_ORACLE("quit\n");
157   oracle_exists = 0;
158 }
159 
160 /* complain and die! */
161 
162 void
error(const char * msg)163 error(const char *msg)
164 {
165   fprintf(stderr, "oracle: %s\n", msg);
166   abort();
167 }
168 
169 /* Call trymove in the primary process, and have the oracle actually
170  * play the move.
171  */
172 static int
oracle_trymove(int pos,int color,const char * message,int str,int komaster,int kom_pos)173 oracle_trymove(int pos, int color, const char *message, int str,
174 	       int komaster, int kom_pos)
175 {
176   if (!trymove(pos, color, message, str))
177     return 0;
178   if (debug & DEBUG_ORACLE_STREAM)
179     gfprintf(stderr, "%o%s %1m\n",
180 	     color == BLACK ? "black" : "white", pos);
181   gfprintf(to_gnugo_stream, "%o%s %1m\n",
182 	   color == BLACK ? "black" : "white", pos);
183   fflush(to_gnugo_stream);
184   ASK_ORACLE;
185   return 1;
186 }
187 
188 /* Undo the move.
189  */
190 static void
oracle_popgo(void)191 oracle_popgo(void)
192 {
193   popgo();
194   TELL_ORACLE("undo\n");
195   ASK_ORACLE;
196 }
197 
198 /* Play the move.
199  */
200 
201 int
oracle_play_move(int pos,int color)202 oracle_play_move(int pos, int color)
203 {
204   play_move(pos, color);
205 
206   if (debug & DEBUG_ORACLE_STREAM)
207     gfprintf(stderr, "%o%s %1m\n",
208 	     color == BLACK ? "black" : "white", pos);
209   gfprintf(to_gnugo_stream, "%o%s %1m\n",
210 	   color == BLACK ? "black" : "white", pos);
211   fflush(to_gnugo_stream);
212   ASK_ORACLE;
213   return 1;
214 }
215 
216 /* FIXME: Debugging needed. This variadic function doesn't work right if we
217  * try to pass a const *char argument, like the infilename in
218  * oracle_loadsgf. So for the time being we stick with the variadic macro
219  * TELL_ORACLE.
220  */
221 static void
tell_oracle(const char * fmt,...)222 tell_oracle(const char *fmt, ...)
223 {
224   va_list ap;
225   va_start(ap, fmt);
226   if (debug & DEBUG_ORACLE_STREAM) fprintf(stderr, fmt, ap);
227   if (fprintf(to_gnugo_stream, fmt, ap) < 0)
228     error("can't write command in to_gnugo_stream");
229   fflush(to_gnugo_stream);
230   va_end(ap);
231 }
232 
233 /* FIXME: Debugging needed. This variadic function seems a little more
234  * reliable than the corresponding variadic macro ASK_ORACLE.
235  */
236 
237 static void
ask_oracle(void)238 ask_oracle(void)
239 {
240   int line_length = 0;
241   char line[128];
242 
243   while (line_length != 1) {
244     if (!fgets(line, 128, from_gnugo_stream))
245       error("can't get response");
246     line_length = strlen(line);
247     if (line_length > 1
248 	&& (line[0] == '=' || line[0] == '?'))
249       strncpy(gnugo_line, line, 128);
250     if (debug & DEBUG_ORACLE_STREAM) {
251       fprintf(stderr, line);
252       fflush(stderr);
253     }
254   }
255 }
256 
257 
258 /* clear the oracle's board and set the boardsize */
259 
260 void
oracle_clear_board(int boardsize)261 oracle_clear_board(int boardsize)
262 {
263   TELL_ORACLE("boardsize %d\n", boardsize);
264   ASK_ORACLE;
265 }
266 
267 /*** * *** * *** * *** * *** * *** * *** * *** * *** * *** * *** * ***\
268  *              Demonstration: a pattern matcher                     *
269 \*** * *** * *** * *** * *** * *** * *** * *** * *** * *** * *** * ***/
270 
271 /* Call the pattern matcher */
272 
273 void
consult_oracle(int color)274 consult_oracle(int color)
275 {
276   do_consult_oracle(color);
277 }
278 
279 void
do_consult_oracle(int color)280 do_consult_oracle(int color)
281 {
282   struct oracle_move_data oracle_moves[MAX_ORACLE_MOVES];
283   int k;
284 
285   for (k = 0; k < MAX_ORACLE_MOVES; k++)
286     oracle_moves[k].value = -1;
287 
288   matchpat(oracle_callback, color, &oracle_db, oracle_moves, NULL);
289   for (k = 0; k < MAX_ORACLE_MOVES; k++)
290     if (oracle_moves[k].value > -1) {
291       oracle_trymove(oracle_moves[k].pos, color, oracle_moves[k].reason,
292 		     0, 0, NO_MOVE);
293       do_consult_oracle(OTHER_COLOR(color));
294       oracle_popgo();
295     }
296 }
297 
298 static void
oracle_callback(int anchor,int color,struct pattern * pattern,int ll,void * data)299 oracle_callback(int anchor, int color, struct pattern *pattern,
300 		int ll, void *data)
301 {
302   int this_move;
303   struct oracle_move_data *moves = data;
304   UNUSED(color);
305 
306   this_move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);
307   if (within_search_area(this_move))
308     oracle_add_move(moves, this_move, pattern->value, pattern->name);
309   else
310     gprintf("outside the area\n");
311 }
312 
313 /* Add a move to a list */
314 
315 static void
oracle_add_move(struct oracle_move_data moves[MAX_ORACLE_MOVES],int this_move,int this_value,const char * this_reason)316 oracle_add_move(struct oracle_move_data moves[MAX_ORACLE_MOVES],
317 		int this_move, int this_value, const char *this_reason)
318 {
319   int k, l;
320 
321   for (k = 0; k < MAX_ORACLE_MOVES; k++)
322     if (moves[k].value == -1
323 	|| this_value >= moves[k].value)
324       break;
325   for (l = MAX_ORACLE_MOVES-1; l > k; l--) {
326     moves[l].pos = moves[l-1].pos;
327     moves[l].value = moves[l-1].value;
328     moves[l].reason = moves[l-1].reason;
329   }
330   moves[k].pos = this_move;
331   moves[k].value = this_value;
332   moves[k].reason = this_reason;
333 }
334 
335 /*** * *** * *** * *** * *** * *** * *** * *** * *** * *** * *** * ***\
336  *              Demonstration: metamachine                           *
337 \*** * *** * *** * *** * *** * *** * *** * *** * *** * *** * *** * ***/
338 
339 #define FIRST_LEVEL_MOVES 3
340 #define SECOND_LEVEL_MOVES 2
341 
342 static int
343 do_metamachine_genmove(int color, int width, float *value);
344 
345 int
metamachine_genmove(int color,float * value,int limit_search)346 metamachine_genmove(int color, float *value, int limit_search)
347 {
348   int move;
349   int pos;
350 
351   if (limit_search) {
352     TELL_ORACLE("limit_search 1\n");
353     ASK_ORACLE;
354     for (pos = BOARDMIN; pos < BOARDMAX; pos++)
355       if (within_search_area(pos)) {
356 	if (debug & DEBUG_ORACLE_STREAM)
357 	  gfprintf(stderr, "%oset_search_limit %1m\n", pos);
358 	gfprintf(to_gnugo_stream, "%oset_search_limit %1m\n", pos);
359 	fflush(to_gnugo_stream);
360 	ASK_ORACLE;
361       }
362   }
363   count_variations = 1;
364   move = do_metamachine_genmove(color, search_width(), value);
365   sgffile_enddump(outfilename);
366   count_variations = 0;
367   return move;
368 }
369 
370 static int
do_metamachine_genmove(int color,int width,float * value)371 do_metamachine_genmove(int color, int width, float *value)
372 {
373   int k, moves_considered;
374   float move_value[10];
375   float best_score = 0.;
376   int best_move = -1;
377   char *token;
378   int moves[10];
379   float score[10];
380   char delimiters[] = " \t\r\n";
381   char buf[100];
382   int i, j;
383 
384   if (color == BLACK)
385     TELL_ORACLE("top_moves_black\n");
386   else
387     TELL_ORACLE("top_moves_white\n");
388   ask_oracle();
389   token = strtok(gnugo_line, delimiters);
390   for (k = 0; k < 10; k++) {
391     moves[k] = PASS_MOVE;
392     move_value[k] = 0.0;
393   }
394   moves_considered = width;
395   if (verbose)
396     dump_stack();
397   for (k = 0; k < moves_considered; k++) {
398     token = strtok(NULL, delimiters);
399     if (!token)
400       break;
401     moves[k] = string_to_location(board_size, token);
402     token = strtok(NULL, delimiters);
403     if (!token)
404       break;
405     sscanf(token, "%f", move_value + k);
406     TRACE("move %d: %1m valued %f\n", k, moves[k], move_value[k]);
407   }
408   /* if we left the loop early, k is the number of valid moves */
409   moves_considered = k;
410   if (moves_considered == 0) {
411     *value = 0.0;
412     return PASS_MOVE;
413   }
414   if (moves_considered == 1) {
415     *value = 1.0;
416     return moves[k];
417   }
418   for (k = 0; k < moves_considered; k++) {
419     if (oracle_trymove(moves[k], color, "", 0, 0, NO_MOVE)) {
420       int new_width = search_width();
421 
422       if (new_width == 0) {
423 	TELL_ORACLE("experimental_score %s\n",
424 		    color == BLACK ? "black" : "white");
425 	ask_oracle();
426 	sscanf(gnugo_line, "= %f", score + k);
427       }
428       else {
429 	do_metamachine_genmove(OTHER_COLOR(color), new_width, &score[k]);
430       }
431       if (verbose)
432 	dump_stack();
433       TRACE("score: %f\n", color == WHITE ? score[k] : -score[k]);
434       sprintf(buf, "value %.2f", color == WHITE ? score[k] : -score[k]);
435       if (sgf_dumptree)
436 	sgftreeAddComment(sgf_dumptree, buf);
437       oracle_popgo();
438     }
439     if (best_move == -1
440 	|| (color == WHITE && score[k] > best_score)
441 	|| (color == BLACK && score[k] < best_score)) {
442       best_move = k;
443       best_score = score[k];
444     }
445   }
446   TRACE("best: %f at %1m\n", best_score, moves[best_move]);
447   *value = score[best_move];
448   return moves[best_move];
449 }
450 
451 /* decide how wide to search */
452 
453 static int
search_width(void)454 search_width(void)
455 {
456   if (stackp == 0)
457     return 3;
458   else if (stackp == 1)
459     return 2;
460   else
461     return 0;
462 }
463 
464 
465 #endif
466 
467 
468 
469 /*
470  * Local Variables:
471  * tab-width: 8
472  * c-basic-offset: 2
473  * End:
474  */
475