Updated README: Equal sign not required with `--mode` flag.
[sgk-go] / engine / sgfdecide.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/* ================================================================ */
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
43void
44decide_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
121void
122decide_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
198void
199decide_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
294void
295decide_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
313void
314decide_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
366void
367decide_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
421void
422decide_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
522void
523decide_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
582void
583decide_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
622void
623decide_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
648void
649decide_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