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 /*      Show status for a string, a dragon, etc in an SGF file.     */
26 /* ================================================================ */
27 
28 #include "gnugo.h"
29 
30 #include <stdio.h>
31 #include <string.h>
32 
33 #include "liberty.h"
34 #include "sgftree.h"
35 
36 
37 /*
38  * decide_string tries to attack and defend the string at (pos),
39  * and then writes the number of variations considered in the attack
40  * and defence to the sgf file.
41  */
42 
43 void
decide_string(int pos)44 decide_string(int pos)
45 {
46   int aa, dd;
47   int acode, dcode;
48   SGFTree tree;
49 
50   if (board[pos] == EMPTY) {
51     fprintf(stderr, "gnugo: --decide-string called on an empty vertex\n");
52     return;
53   }
54 
55   if (*outfilename)
56     sgffile_begindump(&tree);
57 
58   /* Prepare pattern matcher and reading code. */
59   reset_engine();
60 
61   count_variations = 1;
62   acode = attack(pos, &aa);
63   if (acode) {
64     if (acode == WIN)
65       gprintf("%1m can be attacked at %1m (%d variations)\n",
66 	      pos, aa, count_variations);
67     else if (acode == KO_A)
68 	gprintf("%1m can be attacked with ko (good) at %1m (%d variations)\n",
69 	      pos, aa, count_variations);
70     else if (acode == KO_B)
71 	gprintf("%1m can be attacked with ko (bad) at %1m (%d variations)\n",
72 		pos, aa, count_variations);
73 
74     if (debug & DEBUG_READING_PERFORMANCE) {
75       gprintf("Reading shadow: \n");
76       draw_reading_shadow();
77     }
78 
79     count_variations = 1;
80     dcode = find_defense(pos, &dd);
81     if (dcode) {
82       if (dcode == WIN)
83 	gprintf("%1m can be defended at %1m (%d variations)\n",
84 		pos, dd, count_variations);
85       else if (dcode == KO_A)
86 	gprintf("%1m can be defended with ko (good) at %1m (%d variations)\n",
87 		pos, dd, count_variations);
88       else if (dcode == KO_B)
89 	gprintf("%1m can be defended with ko (bad) at %1m (%d variations)\n",
90 		pos, dd, count_variations);
91     }
92     else
93       gprintf("%1m cannot be defended (%d variations)\n",
94 	      pos, count_variations);
95     if (debug & DEBUG_READING_PERFORMANCE) {
96       gprintf("Reading shadow: \n");
97       draw_reading_shadow();
98     }
99 
100   }
101   else {
102     gprintf("%1m cannot be attacked (%d variations)\n",
103 	    pos, count_variations);
104     if (debug & DEBUG_READING_PERFORMANCE) {
105       gprintf("Reading shadow: \n");
106       draw_reading_shadow();
107     }
108   }
109 
110   sgffile_enddump(outfilename);
111   count_variations = 0;
112 }
113 
114 
115 /*
116  * decide_connection tries to connect and disconnect the strings at
117  * (apos) and (bpos), and then writes the number of variations
118  * considered in the attack and defence to the sgf file.
119  */
120 
121 void
decide_connection(int apos,int bpos)122 decide_connection(int apos, int bpos)
123 {
124   int move;
125   int result;
126   SGFTree tree;
127 
128   ASSERT_ON_BOARD1(apos);
129   ASSERT_ON_BOARD1(bpos);
130 
131   if (board[apos] == EMPTY || board[bpos] == EMPTY) {
132     fprintf(stderr, "gnugo: --decide-connection called on an empty vertex\n");
133     return;
134   }
135 
136   if (board[apos] != board[bpos]) {
137     fprintf(stderr, "gnugo: --decide-connection called for strings of different colors\n");
138     return;
139   }
140 
141   if (*outfilename)
142     sgffile_begindump(&tree);
143 
144   /* Prepare pattern matcher and reading code. */
145   reset_engine();
146 
147   count_variations = 1;
148   result = string_connect(apos, bpos, &move);
149   if (result == WIN) {
150     if (move == NO_MOVE)
151       gprintf("%1m and %1m are connected as it stands (%d variations)\n",
152 	      apos, bpos, count_variations);
153     else
154 	gprintf("%1m and %1m can be connected at %1m (%d variations)\n",
155 		apos, bpos, move, count_variations);
156   }
157   else if (result == KO_A)
158     gprintf("%1m and %1m can be connected with ko (good) at %1m (%d variations)\n",
159 	    apos, bpos, move, count_variations);
160   else if (result == KO_B)
161     gprintf("%1m and %1m can be connected with ko (bad) at %1m (%d variations)\n",
162 	    apos, bpos, move, count_variations);
163   else
164     gprintf("%1m and %1m cannot be connected (%d variations)\n",
165 	    apos, bpos, count_variations);
166 
167   count_variations = 1;
168   result = disconnect(apos, bpos, &move);
169   if (result == WIN) {
170     if (move == NO_MOVE)
171       gprintf("%1m and %1m are disconnected as it stands (%d variations)\n",
172 	      apos, bpos, count_variations);
173     else
174 	gprintf("%1m and %1m can be disconnected at %1m (%d variations)\n",
175 		apos, bpos, move, count_variations);
176   }
177   else if (result == KO_A)
178     gprintf("%1m and %1m can be disconnected with ko (good) at %1m (%d variations)\n",
179 	    apos, bpos, move, count_variations);
180   else if (result == KO_B)
181     gprintf("%1m and %1m can be disconnected with ko (bad) at %1m (%d variations)\n",
182 	    apos, bpos, move, count_variations);
183   else
184     gprintf("%1m and %1m cannot be disconnected (%d variations)\n",
185 	    apos, bpos, count_variations);
186 
187   sgffile_enddump(outfilename);
188   count_variations = 0;
189 }
190 
191 
192 /*
193  * decide_owl (formerly called decide_dragon) tries to attack and defend
194  * the dragon at (pos), and then writes the number of variations considered
195  * in the attack and defence to the sgf file.
196  */
197 
198 void
decide_owl(int pos)199 decide_owl(int pos)
200 {
201   int move = NO_MOVE;
202   int acode, dcode;
203   SGFTree tree;
204   int result_certain;
205   int kworm;
206 
207   if (board[pos] == EMPTY) {
208     fprintf(stderr, "gnugo: --decide-dragon called on an empty vertex\n");
209     return;
210   }
211 
212   /* Prepare pattern matcher and reading code. */
213   reset_engine();
214 
215   silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL);
216   gprintf("finished examine_position\n");
217 
218   /* We want to see the reading performed, not just a result picked
219    * from the cache. Thus we clear the cache here.
220    */
221   reading_cache_clear();
222 
223   if (*outfilename)
224     sgffile_begindump(&tree);
225 
226   count_variations = 1;
227   acode = owl_attack(pos, &move, &result_certain, &kworm);
228   if (acode) {
229     if (acode == WIN) {
230       if (move == NO_MOVE)
231 	gprintf("%1m is dead as it stands", pos);
232       else
233 	gprintf("%1m can be attacked at %1m (%d variations)",
234 		pos, move, count_variations);
235     }
236     else if (acode == KO_A)
237       gprintf("%1m can be attacked with ko (good) at %1m (%d variations)",
238 	      pos, move, count_variations);
239     else if (acode == KO_B)
240       gprintf("%1m can be attacked with ko (bad) at %1m (%d variations)",
241 	      pos, move, count_variations);
242     else if (acode == GAIN)
243       gprintf("%1m can be attacked with gain (captures %1m) at %1m (%d variations)",
244 	      pos, kworm, move, count_variations);
245   }
246   else
247     gprintf("%1m cannot be attacked (%d variations)", pos, count_variations);
248 
249   if (result_certain)
250     gprintf("\n");
251   else
252     gprintf(" result uncertain\n");
253 
254   reading_cache_clear();
255   count_variations = 1;
256   dcode = owl_defend(pos, &move, &result_certain, &kworm);
257 
258   if (dcode) {
259     if (dcode == WIN) {
260       if (move == NO_MOVE)
261 	gprintf("%1m is alive as it stands", pos);
262       else
263 	gprintf("%1m can be defended at %1m (%d variations)",
264 		pos, move, count_variations);
265     }
266     else if (dcode == KO_A)
267       gprintf("%1m can be defended with ko (good) at %1m (%d variations)",
268 	      pos, move, count_variations);
269     else if (dcode == KO_B)
270       gprintf("%1m can be defended with ko (bad) at %1m (%d variations)",
271 	      pos, move, count_variations);
272     else if (dcode == LOSS)
273       gprintf("%1m can be defended with loss (loses %1m) at %1m (%d variations)",
274 	      pos, kworm, move, count_variations);
275   }
276   else
277     gprintf("%1m cannot be defended (%d variations)",
278 	    pos, count_variations);
279 
280   if (result_certain)
281     gprintf("\n");
282   else
283     gprintf(" result uncertain\n");
284 
285   sgffile_enddump(outfilename);
286   count_variations = 0;
287 }
288 
289 
290 /*
291  * decide_dragon_data prints the dragon data at (pos).
292  */
293 
294 void
decide_dragon_data(int pos)295 decide_dragon_data(int pos)
296 {
297   if (board[pos] == EMPTY) {
298     fprintf(stderr, "gnugo: --decide-dragon-data called on an empty vertex\n");
299     return;
300   }
301   reset_engine();
302   silent_examine_position(FULL_EXAMINE_DRAGONS);
303 
304   gprintf("Dragon at %1m:\n", pos);
305   report_dragon(stderr, pos);
306 }
307 
308 
309 /* Print the result of the semeai code on the semeai at apos/bpos,
310  * optionally writing an sgf file.
311  */
312 
313 void
decide_semeai(int apos,int bpos)314 decide_semeai(int apos, int bpos)
315 {
316   SGFTree tree;
317   int resulta, resultb, move, result_certain;
318   int color = board[apos];
319 
320   if (color == EMPTY || board[bpos] != OTHER_COLOR(color)) {
321     gprintf("gnugo: --decide-semeai called on invalid data\n");
322     return;
323   }
324 
325   /* Prepare pattern matcher and reading code. */
326   reset_engine();
327 
328   silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL);
329   gprintf("finished examine_position\n");
330   count_variations = 1;
331 
332   /* We want to see the reading performed, not just a result picked
333    * from the cache. Thus we clear the cache here. */
334   reading_cache_clear();
335 
336   if (*outfilename)
337     sgffile_begindump(&tree);
338 
339   gprintf("Analyzing semeai between %1m and %1m, %C moves first\n",
340 	  apos, bpos, board[apos]);
341   owl_analyze_semeai(apos, bpos, &resulta, &resultb, &move, 1,
342 		     &result_certain);
343   gprintf("Semeai defense of %1m: result %s %1m\n",
344 	  apos, result_to_string(resulta), move);
345   gprintf("Semeai attack of %1m: result %s %1m\n",
346 	  bpos, result_to_string(resultb), move);
347   gprintf("%d nodes%s\n\n", count_variations,
348 	  result_certain ? "" : ", uncertain result");
349 
350   gprintf("Analyzing semeai between %1m and %1m, %C moves first\n",
351 	  bpos, apos, board[bpos]);
352   owl_analyze_semeai(bpos, apos, &resultb, &resulta, &move, 1,
353 		     &result_certain);
354   gprintf("Semeai defense of %1m: result %s %1m\n",
355 	  bpos, result_to_string(resultb), move);
356   gprintf("Semeai attack of %1m: result %s %1m\n",
357 	  apos, result_to_string(resulta), move);
358   gprintf("%d nodes%s\n", count_variations,
359 	  result_certain ? "" : ", uncertain result");
360 
361   sgffile_enddump(outfilename);
362   count_variations = 0;
363 }
364 
365 
366 void
decide_tactical_semeai(int apos,int bpos)367 decide_tactical_semeai(int apos, int bpos)
368 {
369   SGFTree tree;
370   int resulta, resultb, move, dummy;
371   int color = board[apos];
372 
373   if (color == EMPTY || board[bpos] != OTHER_COLOR(color)) {
374     gprintf("gnugo: --decide-semeai called on invalid data\n");
375     return;
376   }
377 
378   /* Prepare pattern matcher and reading code. */
379   reset_engine();
380 
381   silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL);
382   gprintf("finished examine_position\n");
383   count_variations = 1;
384 
385   /* We want to see the reading performed, not just a result picked
386    * from the cache. Thus we clear the cache here. */
387   reading_cache_clear();
388 
389   if (*outfilename)
390     sgffile_begindump(&tree);
391 
392   /* FIXME: Calling status_to_string() with a result code as argument
393    * doesn't make sense. It could be changed to result_to_string() but
394    * the overall formatting needs change as well.
395    */
396   owl_analyze_semeai(apos, bpos, &resulta, &resultb, &move, 0, &dummy);
397   gprintf("After %s at %1m, %1m is %s, %1m is %s (%d nodes)\n",
398 	  color_to_string(color),
399 	  move,
400 	  apos, status_to_string(resulta),
401   	  bpos, status_to_string(resultb),
402 	  count_variations);
403   owl_analyze_semeai(bpos, apos, &resultb, &resulta, &move, 0, &dummy);
404   gprintf("After %s at %1m, %1m is %s, %1m is %s (%d nodes)\n",
405 	  color_to_string(color),
406 	  move,
407 	  apos, status_to_string(resulta),
408   	  bpos, status_to_string(resultb),
409 	  count_variations);
410 
411   sgffile_enddump(outfilename);
412   count_variations = 0;
413 }
414 
415 
416 /*
417  * decide_position tries to attack and defend every dragon with
418  * dragon.escape<6 and writes the variations to an sgf file.
419  */
420 
421 void
decide_position()422 decide_position()
423 {
424   int pos;
425   int move = NO_MOVE;
426   int acode = 0, dcode = 0;
427   int kworm;
428   static const char *snames[] = {"dead", "alive", "critical", "unknown"};
429   SGFTree tree;
430 
431   /* Prepare pattern matcher and reading code. */
432   reset_engine();
433 
434   silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL);
435 
436   /* We want to see the reading performed, not just a result picked
437    * from the cache. Thus we clear the cache here. */
438   reading_cache_clear();
439 
440   if (*outfilename)
441     sgffile_begindump(&tree);
442 
443   count_variations = 1;
444 
445   for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
446     if (!ON_BOARD(pos)
447 	|| dragon[pos].origin != pos
448 	|| board[pos] == EMPTY
449 	|| DRAGON2(pos).escape_route >= 6)
450       continue;
451 
452     gprintf("\nanalyzing %1m\n", pos);
453     gprintf("status=%s, escape=%d\n",
454 	    snames[dragon[pos].crude_status], DRAGON2(pos).escape_route);
455     acode = owl_attack(pos, &move, NULL, &kworm);
456     if (acode) {
457       if (acode == WIN) {
458 	if (move == NO_MOVE)
459 	  gprintf("%1m is dead as it stands\n", pos);
460 	else
461 	  gprintf("%1m can be attacked at %1m (%d variations)\n",
462 		  pos, move, count_variations);
463       }
464       else if (acode == KO_A)
465 	gprintf("%1m can be attacked with ko (good) at %1m (%d variations)\n",
466 		pos, move, count_variations);
467       else if (acode == KO_B)
468 	gprintf("%1m can be attacked with ko (bad) at %1m (%d variations)\n",
469 		pos, move, count_variations);
470       else if (acode == GAIN)
471 	gprintf("%1m can be attacked with gain (captures %1m) at %1m (%d variations)",
472 		pos, kworm, move, count_variations);
473 
474       count_variations = 1;
475       dcode = owl_defend(pos, &move, NULL, &kworm);
476       if (dcode) {
477 	if (dcode == WIN) {
478 	  if (move == NO_MOVE)
479 	    gprintf("%1m is alive as it stands\n", pos);
480 	  else
481 	    gprintf("%1m can be defended at %1m (%d variations)\n",
482 		    pos, move, count_variations);
483 	}
484 	else if (dcode == KO_A)
485 	  gprintf("%1m can be defended with ko (good) at %1m (%d variations)\n",
486 		  pos, move, count_variations);
487 	else if (dcode == KO_B)
488 	  gprintf("%1m can be defended with ko (bad) at %1m (%d variations)\n",
489 		  pos, move, count_variations);
490 	else if (dcode == LOSS)
491 	  gprintf("%1m can be defended with loss (loses %1m) at %1m (%d variations)",
492 		  pos, kworm, move, count_variations);
493       }
494       else
495 	gprintf("%1m cannot be defended (%d variations)\n",
496 		pos, count_variations);
497     }
498     else
499       gprintf("%1m cannot be attacked (%d variations)\n",
500 	      pos, count_variations);
501 
502     if (acode) {
503       if (dcode)
504 	gprintf("status of %1m revised to CRITICAL\n", pos);
505       else
506 	gprintf("status of %1m revised to DEAD\n", pos);
507     }
508     else
509       gprintf("status of %1m revised to ALIVE\n", pos);
510   }
511 
512   sgffile_enddump(outfilename);
513   count_variations = 0;
514 }
515 
516 
517 /*
518  * Evaluates the eyespace at (pos) and prints a report. You can get
519  * more information by adding -d0x02 to the command line.
520  */
521 
522 void
decide_eye(int pos)523 decide_eye(int pos)
524 {
525   int color;
526   struct eyevalue value;
527   int attack_point;
528   int defense_point;
529   int eyepos;
530   SGFTree tree;
531 
532   reset_engine();
533   silent_examine_position(EXAMINE_DRAGONS_WITHOUT_OWL);
534 
535   color = black_eye[pos].color;
536   if (!IS_STONE(color)) {
537     gprintf("The eye at %1m is not of a single color.\n", pos);
538     return;
539   }
540 
541   if (printboard)
542     showboard(0);
543 
544   /* Enable sgf output. */
545   if (*outfilename)
546     sgffile_begindump(&tree);
547   count_variations = 1;
548 
549   if (black_eye[pos].color == BLACK) {
550     eyepos = black_eye[pos].origin;
551     compute_eyes(eyepos, &value, &attack_point, &defense_point,
552 		 black_eye, half_eye, 0);
553     gprintf("Black eyespace at %1m: %s\n", eyepos, eyevalue_to_string(&value));
554     if (eye_move_urgency(&value) > 0) {
555       gprintf("  vital points: %1m (attack) %1m (defense)\n", attack_point,
556 	      defense_point);
557     }
558   }
559 
560   if (white_eye[pos].color == WHITE) {
561     eyepos = white_eye[pos].origin;
562     compute_eyes(eyepos, &value, &attack_point, &defense_point,
563 		 white_eye, half_eye, 0);
564     gprintf("White eyespace at %1m: %s\n", eyepos, eyevalue_to_string(&value));
565     if (eye_move_urgency(&value) > 0) {
566       gprintf("  vital points: %1m (attack) %1m (defense)\n", attack_point,
567 	      defense_point);
568     }
569   }
570 
571   /* Finish sgf output. */
572   sgffile_enddump(outfilename);
573   count_variations = 0;
574 }
575 
576 
577 /*
578  * decide_combination tries to find a combination attack for (color) by
579  * calling atari_atari().
580  */
581 
582 void
decide_combination(int color)583 decide_combination(int color)
584 {
585   int attack_move;
586   signed char defense_moves[BOARDMAX];
587   SGFTree tree;
588   int first = 1;
589   int pos;
590 
591   /* Prepare pattern matcher and reading code. */
592   reset_engine();
593 
594   silent_examine_position(EXAMINE_ALL);
595 
596   if (*outfilename)
597     sgffile_begindump(&tree);
598   count_variations = 1;
599 
600   if (atari_atari(color, &attack_move, defense_moves, verbose)) {
601     gprintf("Combination attack for %C at %1m, defense at ", color,
602 	    attack_move);
603     for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
604       if (ON_BOARD(pos) && defense_moves[pos]) {
605 	if (first)
606 	  first = 0;
607 	else
608 	  gprintf(", ");
609 	gprintf("%1m", pos);
610       }
611     }
612     gprintf("\n");
613   }
614   else
615     gprintf("No Combination attack for %C\n", color);
616 
617   sgffile_enddump(outfilename);
618   count_variations = 0;
619 }
620 
621 
622 void
decide_surrounded(int pos)623 decide_surrounded(int pos)
624 {
625   int surround_status;
626 
627   if (board[pos] == EMPTY) {
628     fprintf(stderr, "location must not be empty!\n");
629     return;
630   }
631 
632   /* Prepare pattern matcher and reading code. */
633   reset_engine();
634 
635   silent_examine_position(EXAMINE_ALL);
636   surround_status = compute_surroundings(pos, NO_MOVE, 1, NULL);
637   if (surround_status == 1)
638     gprintf("the dragon at %1m is SURROUNDED!\n", pos);
639   else if (surround_status == 2)
640     gprintf("the dragon at %1m is WEAKLY SURROUNDED!\n", pos);
641   else
642     gprintf("the dragon at %1m is not surrounded.\n", pos);
643 }
644 
645 
646 #if ORACLE
647 
648 void
decide_oracle(Gameinfo * gameinfo,char * infilename,char * untilstring)649 decide_oracle(Gameinfo *gameinfo, char *infilename, char *untilstring)
650 {
651   SGFTree tree;
652 
653   reset_engine();
654   if (*outfilename)
655     sgffile_begindump(&tree);
656 
657   count_variations = 1;
658   summon_oracle();
659   oracle_loadsgf(infilename, untilstring);
660   consult_oracle(gameinfo->to_move);
661   sgffile_enddump(outfilename);
662   dismiss_oracle();
663   count_variations = 0;
664 }
665 
666 #endif
667 
668 
669 /*
670  * Local Variables:
671  * tab-width: 8
672  * c-basic-offset: 2
673  * End:
674  */
675 
676