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