Updated README: Equal sign not required with `--mode` flag.
[sgk-go] / engine / move_reasons.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#include "gnugo.h"
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <math.h>
30
31#include "liberty.h"
32#include "gg_utils.h"
33#include "random.h"
34#include "move_reasons.h"
35
36
37/* All these data structures are declared in move_reasons.h */
38
39struct move_data move[BOARDMAX];
40struct move_reason move_reasons[MAX_MOVE_REASONS];
41int next_reason;
42
43/* Connections */
44int conn_worm1[MAX_CONNECTIONS];
45int conn_worm2[MAX_CONNECTIONS];
46int next_connection;
47
48/* Potential semeai moves. */
49int semeai_target1[MAX_POTENTIAL_SEMEAI];
50int semeai_target2[MAX_POTENTIAL_SEMEAI];
51static int next_semeai;
52
53/* Unordered sets (currently pairs) of move reasons / targets */
54Reason_set either_data[MAX_EITHER];
55int next_either;
56Reason_set all_data[MAX_ALL];
57int next_all;
58
59/* Eye shapes */
60int eyes[MAX_EYES];
61int eyecolor[MAX_EYES];
62int next_eye;
63
64/* Lunches */
65int lunch_dragon[MAX_LUNCHES]; /* eater */
66int lunch_worm[MAX_LUNCHES]; /* food */
67int next_lunch;
68
69/* Point redistribution */
70int replacement_map[BOARDMAX];
71
72/* The color for which we are evaluating moves. */
73int current_color;
74
75/* Attack threats that are known to be sente locally. */
76static int known_good_attack_threats[BOARDMAX][MAX_ATTACK_THREATS];
77
78/* Moves that are known to be safe (in the sense that played stones can
79 * be captured, but opponent loses much more when attempting to do so)
80 */
81static int known_safe_moves[BOARDMAX];
82
83/* Helper functions to check conditions in discard rules. */
84typedef int (*discard_condition_fn_ptr)(int pos, int what);
85
86struct discard_rule {
87 int reason_type[MAX_REASONS];
88 discard_condition_fn_ptr condition;
89 int flags;
90 char trace_message[MAX_TRACE_LENGTH];
91};
92
93
94/* Initialize move reason data structures. */
95void
96clear_move_reasons(void)
97{
98 int pos;
99 int k;
100 next_reason = 0;
101 next_connection = 0;
102 next_semeai = 0;
103 next_either = 0;
104 next_all = 0;
105 next_eye = 0;
106 next_lunch = 0;
107
108 for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
109 if (ON_BOARD(pos)) {
110 move[pos].value = 0.0;
111 move[pos].final_value = 0.0;
112 move[pos].additional_ko_value = 0.0;
113 move[pos].territorial_value = 0.0;
114 move[pos].strategical_value = 0.0;
115 move[pos].maxpos_shape = 0.0;
116 move[pos].numpos_shape = 0;
117 move[pos].maxneg_shape = 0.0;
118 move[pos].numneg_shape = 0;
119 move[pos].followup_value = 0.0;
120 move[pos].influence_followup_value = 0.0;
121 move[pos].reverse_followup_value = 0.0;
122 move[pos].secondary_value = 0.0;
123 move[pos].min_value = 0.0;
124 move[pos].max_value = HUGE_MOVE_VALUE;
125 move[pos].min_territory = 0.0;
126 move[pos].max_territory = HUGE_MOVE_VALUE;
127 for (k = 0; k < MAX_REASONS; k++)
128 move[pos].reason[k] = -1;
129 move[pos].move_safety = 0;
130 move[pos].worthwhile_threat = 0;
131 move[pos].randomness_scaling = 1.0;
132 /* The reason we assign a random number to each move immediately
133 * is to avoid dependence on which moves are evaluated when it
134 * comes to choosing between multiple moves of the same value.
135 * In this way we can get consistent results for use in the
136 * regression tests.
137 */
138 move[pos].random_number = gg_drand();
139
140 /* Do not send away the points (yet). */
141 replacement_map[pos] = NO_MOVE;
142 }
143 }
144
145 for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
146 known_safe_moves[pos] = 0;
147 for (k = 0; k < MAX_ATTACK_THREATS; k++)
148 known_good_attack_threats[pos][k] = NO_MOVE;
149 }
150}
151
152
153/*
154 * Find the index of a connection in the list of connections.
155 * If necessary, add a new entry.
156 */
157int
158find_connection(int worm1, int worm2)
159{
160 int k;
161
162 if (worm1 > worm2) {
163 /* Swap to canonical order. */
164 int tmp = worm1;
165 worm1 = worm2;
166 worm2 = tmp;
167 }
168
169 for (k = 0; k < next_connection; k++)
170 if (conn_worm1[k] == worm1 && conn_worm2[k] == worm2)
171 return k;
172
173 /* Add a new entry. */
174 gg_assert(next_connection < MAX_CONNECTIONS);
175 conn_worm1[next_connection] = worm1;
176 conn_worm2[next_connection] = worm2;
177 next_connection++;
178 return next_connection - 1;
179}
180
181
182static int
183find_either_data(int reason1, int what1, int reason2, int what2)
184{
185 int k;
186
187 /* Make sure the worms are ordered canonically. */
188 if (what1 > what2) {
189 int tmp = what1;
190 what1 = what2;
191 what2 = tmp;
192 }
193
194 for (k = 0; k < next_either; k++)
195 if (either_data[k].reason1 == reason1
196 && either_data[k].what1 == what1
197 && either_data[k].reason2 == reason2
198 && either_data[k].what2 == what2)
199 return k;
200
201 /* Add a new entry. */
202 gg_assert(next_either < MAX_EITHER);
203 either_data[next_either].reason1 = reason1;
204 either_data[next_either].what1 = what1;
205 either_data[next_either].reason2 = reason2;
206 either_data[next_either].what2 = what2;
207 next_either++;
208 return next_either - 1;
209}
210
211static int
212find_all_data(int reason1, int what1, int reason2, int what2)
213{
214 int k;
215
216 /* Make sure the worms are ordered canonically. */
217 if (what1 > what2) {
218 int tmp = what1;
219 what1 = what2;
220 what2 = tmp;
221 }
222
223 for (k = 0; k < next_all; k++)
224 if (all_data[k].reason1 == reason1
225 && all_data[k].what1 == what1
226 && all_data[k].reason2 == reason2
227 && all_data[k].what2 == what2)
228 return k;
229
230 /* Add a new entry. */
231 gg_assert(next_all < MAX_ALL);
232 all_data[next_all].reason1 = reason1;
233 all_data[next_all].what1 = what1;
234 all_data[next_all].reason2 = reason2;
235 all_data[next_all].what2 = what2;
236 next_all++;
237 return next_all - 1;
238}
239
240static int
241find_pair_data(int what1, int what2)
242{
243 int k;
244
245 for (k = 0; k < next_either; k++)
246 if (either_data[k].what1 == what1
247 && either_data[k].what2 == what2)
248 return k;
249
250 /* Add a new entry. */
251 gg_assert(next_either < MAX_EITHER);
252 either_data[next_either].what1 = what1;
253 either_data[next_either].what2 = what2;
254 next_either++;
255 return next_either - 1;
256}
257
258
259/* Interprets the object of a reason and returns its position.
260 * If the object is a pair (of worms or dragons), the position of the first
261 * object is returned. (This is only used for trace outputs.) Returns
262 * NO_MOVE if move does not point to a location.
263 * FIXME: This new function produces some code duplication with other
264 * trace output function. Do some code cleanup here.
265 */
266static int
267get_pos(int reason, int what)
268{
269 switch (reason) {
270 case ATTACK_MOVE:
271 case DEFEND_MOVE:
272 case ATTACK_THREAT:
273 case DEFEND_THREAT:
274 case ATTACK_MOVE_GOOD_KO:
275 case ATTACK_MOVE_BAD_KO:
276 case DEFEND_MOVE_GOOD_KO:
277 case DEFEND_MOVE_BAD_KO:
278 return what;
279
280 case SEMEAI_MOVE:
281 case SEMEAI_THREAT:
282 case STRATEGIC_ATTACK_MOVE:
283 case STRATEGIC_DEFEND_MOVE:
284 case OWL_ATTACK_MOVE:
285 case OWL_DEFEND_MOVE:
286 case OWL_ATTACK_THREAT:
287 case OWL_DEFEND_THREAT:
288 case OWL_PREVENT_THREAT:
289 case UNCERTAIN_OWL_ATTACK:
290 case UNCERTAIN_OWL_DEFENSE:
291 case OWL_ATTACK_MOVE_GOOD_KO:
292 case OWL_ATTACK_MOVE_BAD_KO:
293 case OWL_DEFEND_MOVE_GOOD_KO:
294 case OWL_DEFEND_MOVE_BAD_KO:
295 return what;
296
297 case EITHER_MOVE:
298 /* FIXME: What should we return here? */
299 return either_data[what].what1;
300
301 case ALL_MOVE:
302 /* FIXME: What should we return here? */
303 return all_data[what].what1;
304
305 case CONNECT_MOVE:
306 case CUT_MOVE:
307 return conn_worm1[what];
308
309 case ANTISUJI_MOVE:
310 case EXPAND_TERRITORY_MOVE:
311 case EXPAND_MOYO_MOVE:
312 case INVASION_MOVE:
313 case MY_ATARI_ATARI_MOVE:
314 case YOUR_ATARI_ATARI_MOVE:
315 return NO_MOVE;
316
317 case OWL_ATTACK_MOVE_GAIN:
318 case OWL_DEFEND_MOVE_LOSS:
319 /* FIXME: What should we return here? */
320 return either_data[what].what1;
321
322 default:
323 /* We should never get here: */
324 gg_assert(0);
325 return 0; /* To keep gcc happy. */
326 }
327}
328
329/*
330 * See if a lunch is already in the list of lunches, otherwise add a new
331 * entry. A lunch is in this context a pair of eater (a dragon) and food
332 * (a worm).
333 */
334void
335add_lunch(int eater, int food)
336{
337 int k;
338 int dragon1 = dragon[eater].origin;
339 int worm1 = worm[food].origin;
340 ASSERT_ON_BOARD1(eater);
341 ASSERT_ON_BOARD1(food);
342
343 for (k = 0; k < next_lunch; k++)
344 if ((lunch_dragon[k] == dragon1) && (lunch_worm[k] == worm1))
345 return;
346
347 /* Add a new entry. */
348 gg_assert(next_lunch < MAX_LUNCHES);
349 lunch_dragon[next_lunch] = dragon1;
350 lunch_worm[next_lunch] = worm1;
351 next_lunch++;
352 return;
353}
354
355/* ---------------------------------------------------------------- */
356
357
358/*
359 * Add a move reason for (pos) if it's not already there or the
360 * table is full.
361 */
362static void
363add_move_reason(int pos, int type, int what)
364{
365 int k;
366
367 ASSERT_ON_BOARD1(pos);
368 if (stackp == 0) {
369 ASSERT1(board[pos] == EMPTY, pos);
370 }
371
372 for (k = 0; k < MAX_REASONS; k++) {
373 int r = move[pos].reason[k];
374 if (r < 0)
375 break;
376 if (move_reasons[r].type == type
377 && move_reasons[r].what == what)
378 return; /* Reason already listed. */
379 }
380
381 /* Reason not found, add it if there is place left in both lists.
382 * Otherwise drop it.
383 */
384 if (k >= MAX_REASONS) {
385 DEBUG(DEBUG_MOVE_REASONS,
386 "Move reason at %1m (type=%d, what=%d) dropped because list full.\n",
387 pos, type, what);
388 return;
389 }
390
391 if (next_reason >= MAX_MOVE_REASONS) {
392 DEBUG(DEBUG_MOVE_REASONS,
393 "Move reason at %1m (type=%d, what=%d) dropped because global list full.\n",
394 pos, type, what);
395 return;
396 }
397
398 /* Add a new entry. */
399 move[pos].reason[k] = next_reason;
400 move_reasons[next_reason].type = type;
401 move_reasons[next_reason].what = what;
402 move_reasons[next_reason].status = ACTIVE;
403 next_reason++;
404}
405
406/*
407 * Remove a move reason for (pos). Ignore silently if the reason
408 * wasn't there.
409 */
410static void
411remove_move_reason(int pos, int type, int what)
412{
413 int k;
414 int n = -1; /* Position of the move reason to be deleted. */
415
416 ASSERT_ON_BOARD1(pos);
417 for (k = 0; k < MAX_REASONS; k++) {
418 int r = move[pos].reason[k];
419 if (r < 0)
420 break;
421 if (move_reasons[r].type == type
422 && move_reasons[r].what == what)
423 n = k;
424 }
425
426 if (n == -1)
427 return; /* Move reason wasn't there. */
428
429 /* Now move the last move reason to position n, thereby removing the
430 * one we were looking for.
431 */
432 k--;
433 move[pos].reason[n] = move[pos].reason[k];
434 move[pos].reason[k] = -1;
435}
436
437
438/*
439 * Check whether a move reason already is recorded for a move.
440 * A negative value for 'what' means only match 'type'.
441 */
442int
443move_reason_known(int pos, int type, int what)
444{
445 int k;
446 int r;
447
448 ASSERT_ON_BOARD1(pos);
449 for (k = 0; k < MAX_REASONS; k++) {
450 r = move[pos].reason[k];
451 if (r < 0)
452 break;
453 if (move_reasons[r].type == type
454 && (what < 0
455 || move_reasons[r].what == what))
456 return 1;
457 }
458 return 0;
459}
460
461/* ---------------------------------------------------------------- */
462
463/* Functions used in discard_rules follow below. */
464
465/*
466 * Check whether an attack move reason already is recorded for a move.
467 * A negative value for 'what' means only match 'type'.
468 */
469int
470attack_move_reason_known(int pos, int what)
471{
472 ASSERT1(what < 0 || IS_STONE(board[what]), what);
473 what = worm[what].origin;
474 if (move_reason_known(pos, ATTACK_MOVE, what))
475 return WIN;
476 if (move_reason_known(pos, ATTACK_MOVE_GOOD_KO, what))
477 return KO_A;
478 if (move_reason_known(pos, ATTACK_MOVE_BAD_KO, what))
479 return KO_B;
480 return 0;
481}
482
483/*
484 * Check whether a defense move reason already is recorded for a move.
485 * A negative value for 'what' means only match 'type'.
486 */
487int
488defense_move_reason_known(int pos, int what)
489{
490 ASSERT1(what < 0 || IS_STONE(board[what]), what);
491 what = worm[what].origin;
492 if (move_reason_known(pos, DEFEND_MOVE, what))
493 return WIN;
494 if (move_reason_known(pos, DEFEND_MOVE_GOOD_KO, what))
495 return KO_A;
496 if (move_reason_known(pos, DEFEND_MOVE_BAD_KO, what))
497 return KO_B;
498 return 0;
499}
500
501/* Check whether a dragon consists of only one worm. If so, check
502 * whether we know of a tactical attack or defense move.
503 */
504static int
505tactical_move_vs_whole_dragon_known(int pos, int what)
506{
507 return ((worm[what].size == dragon[what].size)
508 && (attack_move_reason_known(pos, what)
509 || defense_move_reason_known(pos, what)));
510}
511
512/*
513 * Check whether an owl attack move reason already is recorded for a move.
514 * A negative value for 'what' means only match 'type'.
515 */
516int
517owl_attack_move_reason_known(int pos, int what)
518{
519 if (move_reason_known(pos, OWL_ATTACK_MOVE, what))
520 return WIN;
521 if (move_reason_known(pos, OWL_ATTACK_MOVE_GOOD_KO, what))
522 return KO_A;
523 if (move_reason_known(pos, OWL_ATTACK_MOVE_BAD_KO, what))
524 return KO_B;
525 return 0;
526}
527
528/*
529 * Check whether an owl defense move reason already is recorded for a move.
530 * A negative value for 'what' means only match 'type'.
531 */
532int
533owl_defense_move_reason_known(int pos, int what)
534{
535 if (move_reason_known(pos, OWL_DEFEND_MOVE, what))
536 return WIN;
537 if (move_reason_known(pos, OWL_DEFEND_MOVE_GOOD_KO, what))
538 return KO_A;
539 if (move_reason_known(pos, OWL_DEFEND_MOVE_BAD_KO, what))
540 return KO_B;
541 return 0;
542}
543
544/*
545 * Check whether an owl attack/defense move reason is recorded for a move.
546 * A negative value for 'what' means only match 'type'.
547 */
548int
549owl_move_reason_known(int pos, int what)
550{
551 return (owl_attack_move_reason_known(pos, what)
552 || owl_defense_move_reason_known(pos, what));
553}
554
555/*
556 * Check whether we have an owl attack/defense reason for a move that
557 * involves a specific worm.
558 */
559static int
560owl_move_vs_worm_known(int pos, int what)
561{
562 return owl_move_reason_known(pos, dragon[what].origin);
563}
564
565int
566semeai_move_reason_known(int pos, int what)
567{
568 return move_reason_known(pos, SEMEAI_MOVE, what);
569}
570
571/* Check whether a worm is inessential */
572static int
573concerns_inessential_worm(int pos, int what)
574{
575 UNUSED(pos);
576 return DRAGON2(what).safety == INESSENTIAL
577 || worm[what].inessential;
578}
579
580/* Check whether a dragon is inessential */
581static int
582concerns_inessential_dragon(int pos, int what)
583{
584 UNUSED(pos);
585 return DRAGON2(what).safety == INESSENTIAL;
586}
587
588static int
589move_is_marked_unsafe(int pos, int what)
590{
591 UNUSED(what);
592 return (!move[pos].move_safety
593 && !adjacent_to_nondead_stone(pos, current_color));
594}
595
596/* Check whether a dragon is non-critical. */
597static int
598concerns_noncritical_dragon(int pos, int what)
599{
600 UNUSED(pos);
601 return (dragon[what].status != CRITICAL
602 && worm[what].attack_codes[0] == 0);
603}
604
605
606/* (what) points to two worms listed in either_data. Returns true if
607 * this is a "attack either" move reason, and one of the worms attackable.
608 * FIXME: Ko?
609 */
610static int
611either_worm_attackable(int pos, int what)
612{
613 UNUSED(pos);
614 return (either_data[what].reason1 == ATTACK_STRING
615 && either_data[what].reason2 == ATTACK_STRING
616 && (worm[either_data[what].what1].attack_codes[0] != 0
617 || worm[either_data[what].what2].attack_codes[0] != 0));
618}
619
620/* (what) points to two worms via all_data. Returns true if this is
621 * a "defend both" move reason, and one of the worms is attackable.
622 * FIXME: Ko?
623 */
624static int
625one_of_both_attackable(int pos, int what)
626{
627 UNUSED(pos);
628 return (all_data[what].reason1 == DEFEND_STRING
629 && all_data[what].reason2 == DEFEND_STRING
630 && (worm[all_data[what].what1].attack_codes[0] != 0
631 || worm[all_data[what].what2].attack_codes[0] != 0));
632}
633
634
635/* ---------------------------------------------------------------- */
636
637
638/*
639 * Add to the reasons for the move at (pos) that it attacks the worm
640 * at (ww).
641 */
642void
643add_attack_move(int pos, int ww, int code)
644{
645 ASSERT_ON_BOARD1(ww);
646 ww = worm[ww].origin;
647
648 if (code == WIN)
649 add_move_reason(pos, ATTACK_MOVE, ww);
650 else if (code == KO_A)
651 add_move_reason(pos, ATTACK_MOVE_GOOD_KO, ww);
652 else if (code == KO_B)
653 add_move_reason(pos, ATTACK_MOVE_BAD_KO, ww);
654}
655
656/*
657 * Add to the reasons for the move at (pos) that it defends the worm
658 * at (ww).
659 */
660void
661add_defense_move(int pos, int ww, int code)
662{
663 ASSERT_ON_BOARD1(ww);
664 ww = worm[ww].origin;
665
666 if (code == WIN)
667 add_move_reason(pos, DEFEND_MOVE, ww);
668 else if (code == KO_A)
669 add_move_reason(pos, DEFEND_MOVE_GOOD_KO, ww);
670 else if (code == KO_B)
671 add_move_reason(pos, DEFEND_MOVE_BAD_KO, ww);
672}
673
674/*
675 * Add to the reasons for the move at (pos) that it threatens to
676 * attack the worm at (ww).
677 */
678void
679add_attack_threat_move(int pos, int ww, int code)
680{
681 UNUSED(code);
682
683 ASSERT_ON_BOARD1(ww);
684 add_move_reason(pos, ATTACK_THREAT, worm[ww].origin);
685}
686
687/* Remove an attack threat move reason. */
688
689void
690remove_attack_threat_move(int pos, int ww)
691{
692 ASSERT_ON_BOARD1(ww);
693 remove_move_reason(pos, ATTACK_THREAT, worm[ww].origin);
694}
695
696/*
697 * Add to the reasons for the move at (pos) that it defends the worm
698 * at (ww).
699 */
700void
701add_defense_threat_move(int pos, int ww, int code)
702{
703 UNUSED(code);
704
705 ASSERT_ON_BOARD1(ww);
706 add_move_reason(pos, DEFEND_THREAT, worm[ww].origin);
707}
708
709
710/* Report all, or up to max_strings, strings that are threatened
711 * at (pos).
712 */
713int
714get_attack_threats(int pos, int max_strings, int strings[])
715{
716 int k;
717 int num_strings;
718
719 num_strings = 0;
720 for (k = 0; k < MAX_REASONS; k++) {
721 int r = move[pos].reason[k];
722 if (r < 0)
723 break;
724
725 if (move_reasons[r].type == ATTACK_THREAT)
726 strings[num_strings++] = move_reasons[r].what;
727
728 if (num_strings == max_strings)
729 break;
730 }
731
732 return num_strings;
733}
734
735/* Report all, or up to max_strings, strings that might be defended
736 * at (pos).
737 */
738int
739get_defense_threats(int pos, int max_strings, int strings[])
740{
741 int k;
742 int num_strings;
743
744 num_strings = 0;
745 for (k = 0; k < MAX_REASONS; k++) {
746 int r = move[pos].reason[k];
747 if (r < 0)
748 break;
749
750 if (move_reasons[r].type == DEFEND_THREAT)
751 strings[num_strings++] = move_reasons[r].what;
752
753 if (num_strings == max_strings)
754 break;
755 }
756
757 return num_strings;
758}
759
760/* Report the biggest dragon that is owl-affected (possibily with ko)
761 * by a move at (pos).
762 */
763int
764get_biggest_owl_target(int pos)
765{
766 int k;
767 int biggest_target = -1;
768 float target_size = 0.0;
769 for (k = 0; k < MAX_REASONS; k++) {
770 int r = move[pos].reason[k];
771 if (r < 0)
772 break;
773
774 switch (move_reasons[r].type) {
775 case OWL_ATTACK_MOVE:
776 case OWL_ATTACK_MOVE_GOOD_KO:
777 case OWL_ATTACK_MOVE_BAD_KO:
778 case OWL_ATTACK_THREAT:
779 case OWL_DEFEND_MOVE:
780 case OWL_DEFEND_MOVE_GOOD_KO:
781 case OWL_DEFEND_MOVE_BAD_KO:
782 case OWL_DEFEND_THREAT:
783 case OWL_PREVENT_THREAT:
784 if (dragon[move_reasons[r].what].effective_size > target_size) {
785 biggest_target = move_reasons[r].what;
786 target_size = dragon[move_reasons[r].what].effective_size;
787 }
788 break;
789 }
790 }
791 return biggest_target;
792}
793
794/*
795 * Add to the reasons for the move at (pos) that it connects the
796 * dragons at (dr1) and (dr2). Require that the dragons are
797 * distinct.
798 */
799void
800add_connection_move(int pos, int w1, int w2)
801{
802 int connection;
803
804 ASSERT_ON_BOARD1(w1);
805 ASSERT_ON_BOARD1(w2);
806 ASSERT1(worm[w1].color == worm[w2].color, w1);
807 if (worm[w1].origin == worm[w2].origin)
808 return;
809
810 connection = find_connection(worm[w1].origin, worm[w2].origin);
811 add_move_reason(pos, CONNECT_MOVE, connection);
812}
813
814/*
815 * Add to the reasons for the move at (pos) that it cuts the
816 * dragons at (dr1) and (dr2). Require that the dragons are
817 * distinct.
818 */
819void
820add_cut_move(int pos, int w1, int w2)
821{
822 int connection;
823
824 ASSERT_ON_BOARD1(w1);
825 ASSERT_ON_BOARD1(w2);
826 ASSERT1(worm[w1].color == worm[w2].color, w1);
827 if (worm[w1].origin == worm[w2].origin)
828 return;
829 connection = find_connection(worm[w1].origin, worm[w2].origin);
830
831 /*
832 * Ignore the cut or connection if either (w1) or (w2)
833 * points to a tactically captured worm.
834 */
835 if ((worm[w1].attack_codes[0] != 0 && worm[w1].defense_codes[0] == 0)
836 || (worm[w2].attack_codes[0] != 0 && worm[w2].defense_codes[0] == 0))
837 return;
838
839 add_move_reason(pos, CUT_MOVE, connection);
840
841}
842
843/*
844 * Add to the reasons for the move at (pos) that it is an anti-suji.
845 * This means that it's a locally inferior move or for some other reason
846 * must *not* be played.
847 */
848void
849add_antisuji_move(int pos)
850{
851 add_move_reason(pos, ANTISUJI_MOVE, 0);
852}
853
854/*
855 * Add to the reasons for the move at (pos) that it wins the
856 * dragon (friendly or not) at (dr) in semeai. Since it is
857 * possible that in some semeai one player can kill but the
858 * other can only make seki, it is possible that one dragon
859 * is already alive in seki. Therefore separate move reasons
860 * must be added for the two dragons.
861 */
862void
863add_semeai_move(int pos, int dr)
864{
865 ASSERT_ON_BOARD1(dr);
866 add_move_reason(pos, SEMEAI_MOVE, dragon[dr].origin);
867}
868
869/*
870 * Add to the reasons for the move at (pos) that it might
871 * kill/save the dragon at (dr1) in the semeai against (dr2).
872 */
873static void
874add_potential_semeai_move(int pos, int type, int dr1, int dr2)
875{
876 ASSERT1(ON_BOARD(dr1), pos);
877 ASSERT1(ON_BOARD(dr2), pos);
878 if (next_semeai >= MAX_POTENTIAL_SEMEAI)
879 DEBUG(DEBUG_MOVE_REASONS,
880 "Potential semeai move at %1m dropped as list was full\n", pos);
881 else {
882 semeai_target1[next_semeai] = dr1;
883 semeai_target2[next_semeai] = dr2;
884 add_move_reason(pos, type, next_semeai);
885 next_semeai++;
886 }
887}
888
889/*
890 * Add to the reasons for the move at (pos) that it might
891 * kill the dragon at (dr1) in the semeai against (dr2).
892 */
893void
894add_potential_semeai_attack(int pos, int dr1, int dr2)
895{
896 add_potential_semeai_move(pos, POTENTIAL_SEMEAI_ATTACK, dr1, dr2);
897}
898
899/*
900 * Add to the reasons for the move at (pos) that it might
901 * save the dragon at (dr1) in the semeai against (dr2).
902 */
903void
904add_potential_semeai_defense(int pos, int dr1, int dr2)
905{
906 add_potential_semeai_move(pos, POTENTIAL_SEMEAI_DEFENSE, dr1, dr2);
907}
908
909/*
910 * Add to the reasons for the move at (pos) that given two
911 * moves in a row a move here can win the dragon (friendly or
912 * not) at (dr) in semeai. Such a move can be used as a
913 * ko threat, and it is also given some value due to uncertainty
914 * in the counting of liberties.
915 */
916void
917add_semeai_threat(int pos, int dr)
918{
919 ASSERT_ON_BOARD1(dr);
920 add_move_reason(pos, SEMEAI_THREAT, dragon[dr].origin);
921}
922
923/*
924 * Add to the reasons for the move at (pos) that it will accomplish
925 * one of two things: either (reason1) on (target1) or (reason2) on
926 * (target2).
927 *
928 * At this time, (reason) can only be ATTACK_STRING.
929 * However, more reasons will be implemented in the future.
930 *
931 * FIXME: Implement at least ATTACK_MOVE_GOOD_KO, ATTACK_MOVE_BAD_KO,
932 * DEFEND_MOVE and associates, CONNECT_MOVE, OWL_ATTACK_MOVE,
933 * OWL_DEFEND_MOVE, and possibly more.
934 *
935 * FIXME: Generalize to more than 2 parameters.
936 * When that is done, this will be a good way to add
937 * atari_atari moves.
938 */
939void
940add_either_move(int pos, int reason1, int target1, int reason2, int target2)
941{
942 int what1 = 0;
943 int what2 = 0;
944 int index;
945
946 ASSERT_ON_BOARD1(target1);
947 ASSERT_ON_BOARD1(target2);
948 if (reason1 == reason2 && target1 == target2)
949 return;
950
951 /* For now. */
952 gg_assert(reason1 == ATTACK_STRING);
953 gg_assert(reason2 == ATTACK_STRING);
954
955 switch (reason1) {
956 case ATTACK_STRING:
957 {
958 what1 = worm[target1].origin;
959
960 /* If this string is already attacked, and with no defense, then
961 * there is no additional value of this move reason. */
962 if (worm[target1].attack_codes[0] != 0
963 && worm[target1].defense_codes[0] == 0)
964 return;
965 }
966 break;
967
968 default:
969 break;
970 }
971
972 switch (reason2) {
973 case ATTACK_STRING:
974 {
975 what2 = worm[target2].origin;
976
977 /* If this string is already attacked, and with no defense, then
978 * there is no additional value of this move reason. */
979 if (worm[target2].attack_codes[0] != 0
980 && worm[target2].defense_codes[0] == 0)
981 return;
982 }
983 break;
984
985 default:
986 break;
987 }
988
989 index = find_either_data(reason1, what1, reason2, what2);
990 add_move_reason(pos, EITHER_MOVE, index);
991}
992
993
994/*
995 * Add to the reasons for the move at (pos) that it will accomplish
996 * both of two things: (reason1) on (target1) and (reason2) on
997 * (target2).
998 *
999 * At this time, (reason) can only be DEFEND_STRING.
1000 * However, more reasons will be implemented in the future.
1001 *
1002 * FIXME: Implement at least ATTACK_MOVE_GOOD_KO, ATTACK_MOVE_BAD_KO,
1003 * DEFEND_MOVE and associates, CONNECT_MOVE, OWL_ATTACK_MOVE,
1004 * OWL_DEFEND_MOVE, and possibly more.
1005 *
1006 * FIXME: Generalize to more than 2 parameters.
1007 * When that is done, this will be a good way to add
1008 * atari_atari moves.
1009 */
1010void
1011add_all_move(int pos, int reason1, int target1, int reason2, int target2)
1012{
1013 int what1 = 0;
1014 int what2 = 0;
1015 int index;
1016
1017 ASSERT_ON_BOARD1(target1);
1018 ASSERT_ON_BOARD1(target2);
1019 if (reason1 == reason2 && target1 == target2)
1020 return;
1021
1022 /* For now. */
1023 gg_assert(reason1 == DEFEND_STRING);
1024 gg_assert(reason2 == DEFEND_STRING);
1025
1026 switch (reason1) {
1027 case DEFEND_STRING:
1028 what1 = worm[target1].origin;
1029 break;
1030
1031 default:
1032 break;
1033 }
1034
1035 switch (reason2) {
1036 case DEFEND_STRING:
1037 what2 = worm[target2].origin;
1038 break;
1039
1040 default:
1041 break;
1042 }
1043
1044 index = find_all_data(reason1, what1, reason2, what2);
1045 add_move_reason(pos, ALL_MOVE, index);
1046}
1047
1048
1049void
1050add_loss_move(int pos, int target1, int target2)
1051{
1052 int what1 = dragon[target1].origin;
1053 int what2 = worm[target2].origin;
1054 int index = find_pair_data(what1, what2);
1055 ASSERT1(target2 != NO_MOVE, pos);
1056 add_move_reason(pos, OWL_DEFEND_MOVE_LOSS, index);
1057}
1058
1059/*
1060 * Add to the reasons for the move at (pos) that it expands
1061 * territory.
1062 */
1063void
1064add_expand_territory_move(int pos)
1065{
1066 add_move_reason(pos, EXPAND_TERRITORY_MOVE, 0);
1067}
1068
1069/*
1070 * Add to the reasons for the move at (pos) that it expands
1071 * moyo.
1072 */
1073void
1074add_expand_moyo_move(int pos)
1075{
1076 add_move_reason(pos, EXPAND_MOYO_MOVE, 0);
1077}
1078
1079/*
1080 * Add to the reasons for the move at (pos) that it is an invasion.
1081 */
1082void
1083add_invasion_move(int pos)
1084{
1085 add_move_reason(pos, INVASION_MOVE, 0);
1086}
1087
1088/*
1089 * This function is called when a shape value for the move at (pos)
1090 * is found.
1091 *
1092 * We keep track of the largest positive shape value found, and the
1093 * total number of positive contributions, as well as the largest
1094 * negative shape value found, and the total number of negative
1095 * shape contributions.
1096 */
1097void
1098add_shape_value(int pos, float value)
1099{
1100 ASSERT_ON_BOARD1(pos);
1101 if (value > 0.0) {
1102 if (value > move[pos].maxpos_shape)
1103 move[pos].maxpos_shape = value;
1104 move[pos].numpos_shape += 1;
1105 }
1106 else if (value < 0.0) {
1107 value = -value;
1108 if (value > move[pos].maxneg_shape)
1109 move[pos].maxneg_shape = value;
1110 move[pos].numneg_shape += 1;
1111 }
1112}
1113
1114/*
1115 * Flag that this move is worthwhile to play as a pure threat move.
1116 */
1117void
1118add_worthwhile_threat_move(int pos)
1119{
1120 move[pos].worthwhile_threat = 1;
1121}
1122
1123/*
1124 * Add to the reasons for the move at (pos) that it attacks
1125 * the dragon (dr) on a strategical level.
1126 */
1127void
1128add_strategical_attack_move(int pos, int dr)
1129{
1130 dr = dragon[dr].origin;
1131 ASSERT_ON_BOARD1(dr);
1132 add_move_reason(pos, STRATEGIC_ATTACK_MOVE, dr);
1133}
1134
1135/*
1136 * Add to the reasons for the move at (pos) that it defends
1137 * the dragon (dr) on a strategical level.
1138 */
1139void
1140add_strategical_defense_move(int pos, int dr)
1141{
1142 dr = dragon[dr].origin;
1143 ASSERT_ON_BOARD1(dr);
1144 add_move_reason(pos, STRATEGIC_DEFEND_MOVE, dr);
1145}
1146
1147/*
1148 * Add to the reasons for the move at (pos) that the owl
1149 * code reports an attack on the dragon (dr).
1150 */
1151void
1152add_owl_attack_move(int pos, int dr, int kworm, int code)
1153{
1154 dr = dragon[dr].origin;
1155
1156 ASSERT_ON_BOARD1(dr);
1157 if (code == WIN)
1158 add_move_reason(pos, OWL_ATTACK_MOVE, dr);
1159 else if (code == KO_A)
1160 add_move_reason(pos, OWL_ATTACK_MOVE_GOOD_KO, dr);
1161 else if (code == KO_B)
1162 add_move_reason(pos, OWL_ATTACK_MOVE_BAD_KO, dr);
1163 else if (code == GAIN) {
1164 ASSERT_ON_BOARD1(kworm);
1165 add_move_reason(pos, OWL_ATTACK_MOVE_GAIN, find_pair_data(dr, kworm));
1166 }
1167}
1168
1169/*
1170 * Add to the reasons for the move at (pos) that the owl
1171 * code reports a defense of the dragon (dr).
1172 */
1173void
1174add_owl_defense_move(int pos, int dr, int code)
1175{
1176 dr = dragon[dr].origin;
1177
1178 ASSERT_ON_BOARD1(dr);
1179 if (code == WIN)
1180 add_move_reason(pos, OWL_DEFEND_MOVE, dr);
1181 else if (code == KO_A)
1182 add_move_reason(pos, OWL_DEFEND_MOVE_GOOD_KO, dr);
1183 else if (code == KO_B)
1184 add_move_reason(pos, OWL_DEFEND_MOVE_BAD_KO, dr);
1185}
1186
1187/*
1188 * Add to the reasons for the move at (pos) that the owl
1189 * code reports a move threatening to attack the dragon enemy (dr).
1190 * That is, if the attacker is given two moves in a row, (pos)
1191 * can be the first move.
1192 */
1193void
1194add_owl_attack_threat_move(int pos, int dr, int code)
1195{
1196 UNUSED(code);
1197 dr = dragon[dr].origin;
1198
1199 ASSERT_ON_BOARD1(dr);
1200 add_move_reason(pos, OWL_ATTACK_THREAT, dragon[dr].origin);
1201 add_worthwhile_threat_move(pos);
1202}
1203
1204/* The owl code found the friendly dragon alive, or the unfriendly dragon
1205 * dead, and an extra point of attack or defense was found, so this might be a
1206 * good place to play.
1207 */
1208void
1209add_owl_uncertain_defense_move(int pos, int dr)
1210{
1211 dr = dragon[dr].origin;
1212 ASSERT_ON_BOARD1(dr);
1213 add_move_reason(pos, UNCERTAIN_OWL_DEFENSE, dragon[dr].origin);
1214}
1215
1216/* The owl code found the opponent dragon alive, or the friendly
1217 * dragon dead, but was uncertain, and this move reason propose
1218 * an attack or defense which is expected to fail but might succeed.
1219 */
1220void
1221add_owl_uncertain_attack_move(int pos, int dr)
1222{
1223 dr = dragon[dr].origin;
1224 ASSERT_ON_BOARD1(dr);
1225 add_move_reason(pos, UNCERTAIN_OWL_ATTACK, dragon[dr].origin);
1226}
1227
1228/*
1229 * Add to the reasons for the move at (pos) that the owl
1230 * code reports a move threatening to rescue the dragon (dr).
1231 * That is, if the defender is given two moves in a row, (pos)
1232 * can be the first move.
1233 */
1234void
1235add_owl_defense_threat_move(int pos, int dr, int code)
1236{
1237 UNUSED(code);
1238 dr = dragon[dr].origin;
1239
1240 ASSERT_ON_BOARD1(dr);
1241 add_move_reason(pos, OWL_DEFEND_THREAT, dragon[dr].origin);
1242 add_worthwhile_threat_move(pos);
1243}
1244
1245/* Add to the reasons for the move at (pos) that it captures
1246 * at least one of a set of worms which individually are tactically
1247 * safe (such as a double atari). Only one such move reason is
1248 * permitted per move.
1249 */
1250void
1251add_my_atari_atari_move(int pos, int size)
1252{
1253 add_move_reason(pos, MY_ATARI_ATARI_MOVE, size);
1254}
1255
1256/* Add to the reasons for the move at (pos) that it stops a
1257 * combination attack for the opponent.
1258 */
1259void
1260add_your_atari_atari_move(int pos, int size)
1261{
1262 add_move_reason(pos, YOUR_ATARI_ATARI_MOVE, size);
1263}
1264
1265
1266/*
1267 * Add to the reasons for the move at (pos) that the owl
1268 * code reports a move threatening to defend the dragon enemy (dr),
1269 * and that (pos) is a move which attacks the dragon.
1270 * That is, if the defender is given two moves in a row, (pos)
1271 * can be the first move. Hopefully playing at (pos) makes it harder
1272 * for the dragon to live.
1273 */
1274void
1275add_owl_prevent_threat_move(int pos, int dr)
1276{
1277 ASSERT_ON_BOARD1(dr);
1278 add_move_reason(pos, OWL_PREVENT_THREAT, dragon[dr].origin);
1279}
1280
1281/*
1282 * Add value of followup moves.
1283 */
1284void
1285add_followup_value(int pos, float value)
1286{
1287 ASSERT_ON_BOARD1(pos);
1288 if (value > move[pos].followup_value)
1289 move[pos].followup_value = value;
1290}
1291
1292/*
1293 * Add value of reverse followup moves.
1294 */
1295void
1296add_reverse_followup_value(int pos, float value)
1297{
1298 ASSERT_ON_BOARD1(pos);
1299 if (value > move[pos].reverse_followup_value)
1300 move[pos].reverse_followup_value = value;
1301}
1302
1303/*
1304 * Set a minimum allowed value for the move.
1305 */
1306int
1307set_minimum_move_value(int pos, float value)
1308{
1309 ASSERT_ON_BOARD1(pos);
1310 if (value > move[pos].min_value) {
1311 move[pos].min_value = value;
1312 return 1;
1313 }
1314 return 0;
1315}
1316
1317/*
1318 * Set a maximum allowed value for the move.
1319 */
1320void
1321set_maximum_move_value(int pos, float value)
1322{
1323 ASSERT_ON_BOARD1(pos);
1324 if (value < move[pos].max_value)
1325 move[pos].max_value = value;
1326}
1327
1328/*
1329 * Set a minimum allowed territorial value for the move.
1330 */
1331void
1332set_minimum_territorial_value(int pos, float value)
1333{
1334 ASSERT_ON_BOARD1(pos);
1335 if (value > move[pos].min_territory)
1336 move[pos].min_territory = value;
1337}
1338
1339/*
1340 * Set a maximum allowed territorial value for the move.
1341 */
1342void
1343set_maximum_territorial_value(int pos, float value)
1344{
1345 ASSERT_ON_BOARD1(pos);
1346 if (value < move[pos].max_territory)
1347 move[pos].max_territory = value;
1348}
1349
1350/*
1351 * Add a point redistribution rule, sending the points from (from)
1352 * to (to).
1353 */
1354void
1355add_replacement_move(int from, int to, int color)
1356{
1357 int cc;
1358 int pos;
1359 int dummy;
1360
1361 ASSERT_ON_BOARD1(from);
1362 ASSERT_ON_BOARD1(to);
1363
1364 if (board[from] != EMPTY)
1365 return;
1366 ASSERT1(board[to] == EMPTY, to);
1367
1368 cc = replacement_map[to];
1369 if (unconditionally_meaningless_move(to, color, &dummy)) {
1370 /* Silently ignore replacement patterns which conflict with the
1371 * unconditional analysis since the latter is always correct and
1372 * it's difficult to anticipate such situations for the patterns.
1373 */
1374 return;
1375 }
1376
1377 /* First check for an incompatible redistribution rule. */
1378 if (replacement_map[from] != NO_MOVE) {
1379 int dd = replacement_map[from];
1380 /* Abort if the old rule isn't compatible with the new one.
1381 * (But not in the stable release.)
1382 */
1383 if (0) {
1384 ASSERT1(dd == to || to == replacement_map[dd], from);
1385 }
1386 /* There already is a redistribution in effect so we
1387 * have nothing more to do.
1388 */
1389 return;
1390 }
1391
1392 TRACE("Move at %1m is replaced by %1m.\n", from, to);
1393
1394 /* Verify that we don't introduce a cyclic redistribution. */
1395 if (cc == from) {
1396 gprintf("Cyclic point redistribution detected.\n");
1397 ASSERT1(0, from);
1398 }
1399
1400 /* Update the replacement map. Make sure that all replacements
1401 * always are directed immediately to the final destination.
1402 */
1403 if (cc != NO_MOVE)
1404 replacement_map[from] = cc;
1405 else
1406 replacement_map[from] = to;
1407
1408 for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
1409 if (ON_BOARD(pos) && replacement_map[pos] == from)
1410 replacement_map[pos] = replacement_map[from];
1411 }
1412}
1413
1414
1415/* Find worms rescued by a move at (pos). */
1416void
1417get_saved_worms(int pos, signed char saved[BOARDMAX])
1418{
1419 int k;
1420 memset(saved, 0, sizeof(saved[0]) * BOARDMAX);
1421
1422 for (k = 0; k < MAX_REASONS; k++) {
1423 int r = move[pos].reason[k];
1424 int what;
1425
1426 if (r < 0)
1427 break;
1428
1429 what = move_reasons[r].what;
1430 /* We exclude the ko contingent defenses, to avoid that the
1431 * confirm_safety routines spot an attack with ko and thinks the
1432 * move is unsafe.
1433 */
1434 if (move_reasons[r].type == DEFEND_MOVE)
1435 mark_string(worm[what].origin, saved, 1);
1436 else if (move_reasons[r].type == OWL_DEFEND_MOVE_LOSS) {
1437 int origin = dragon[what].origin;
1438 int kworm = worm[what].origin;
1439 int ii;
1440 for (ii = BOARDMIN; ii < BOARDMAX; ii++)
1441 if (IS_STONE(board[ii]) && dragon[ii].origin == origin
1442 && worm[ii].origin != kworm)
1443 mark_string(worm[ii].origin, saved, 1);
1444 }
1445 }
1446}
1447
1448/* This function marks all stones whose status is changed by an owl move
1449 * reason according to the following rules:
1450 * 1. For an owl attack, all stones belonging to the attacked dragon are
1451 * marked as INFLUENCE_CAPTURED_STONE
1452 * 2. For an owl defense, all stones belonging to the defended dragon are
1453 * markes as INFLUENCE_SAVED_STONE if they are also sufficiently
1454 * tactically stable.
1455 *
1456 * In effective_size, the sum of the effective size of the changed worms
1457 * is returned (unless it is a NULL pointer).
1458 */
1459void
1460mark_changed_dragon(int pos, int color, int affected, int affected2,
1461 int move_reason_type, signed char safe_stones[BOARDMAX],
1462 float strength[BOARDMAX], float *effective_size)
1463{
1464 int ii;
1465 signed char new_status = INFLUENCE_SAVED_STONE;
1466 int result_to_beat = 0;
1467
1468 ASSERT1(board[pos] == EMPTY, pos);
1469 ASSERT1(IS_STONE(board[affected]), pos);
1470
1471 if (effective_size)
1472 *effective_size = 0.0;
1473
1474 /* For attack moves, we immediately can set the effective size.
1475 * For defense moves, it will be calculated in the course of
1476 * updating the worms' status.
1477 */
1478 switch (move_reason_type) {
1479 case OWL_ATTACK_MOVE:
1480 case OWL_ATTACK_MOVE_GOOD_KO:
1481 case OWL_ATTACK_MOVE_BAD_KO:
1482 ASSERT1(board[affected] == OTHER_COLOR(color), pos);
1483 new_status = 0;
1484 if (effective_size)
1485 *effective_size = dragon[affected].effective_size;
1486 break;
1487 case OWL_DEFEND_MOVE:
1488 ASSERT1(board[affected] == color, pos);
1489 result_to_beat = WIN;
1490 break;
1491 case OWL_DEFEND_MOVE_GOOD_KO:
1492 ASSERT1(board[affected] == color, pos);
1493 result_to_beat = KO_A;
1494 break;
1495 case OWL_DEFEND_MOVE_BAD_KO:
1496 ASSERT1(board[affected] == color, pos);
1497 result_to_beat = KO_B;
1498 break;
1499 case OWL_ATTACK_MOVE_GAIN:
1500 ASSERT1(board[affected] == OTHER_COLOR(color), pos);
1501 new_status = 0;
1502 if (effective_size)
1503 *effective_size = worm[affected2].effective_size;
1504 break;
1505 case OWL_DEFEND_MOVE_LOSS:
1506 ASSERT1(board[affected] == color, pos);
1507 if (effective_size)
1508 *effective_size = dragon[affected].effective_size
1509 - worm[affected2].effective_size;
1510 result_to_beat = WIN;
1511 break;
1512 case SEMEAI_MOVE:
1513 ASSERT1(IS_STONE(board[affected]), pos);
1514 if (board[affected] == color)
1515 result_to_beat = WIN;
1516 else {
1517 new_status = 0;
1518 if (effective_size)
1519 *effective_size = dragon[affected].effective_size;
1520 }
1521 break;
1522
1523 default:
1524 /* mark_changed_dragon() called with invalid move reason. */
1525 ASSERT1(0, pos);
1526 }
1527
1528 if (move_reason_type == OWL_ATTACK_MOVE_GAIN)
1529 mark_changed_string(affected2, safe_stones, strength, new_status);
1530 else {
1531 for (ii = first_worm_in_dragon(affected); ii != NO_MOVE;
1532 ii = next_worm_in_dragon(ii))
1533 if (new_status == 0)
1534 mark_changed_string(ii, safe_stones, strength, new_status);
1535 else {
1536 int worm_is_safe = 0;
1537 if (worm[ii].attack_codes[0] == NO_MOVE
1538 || defense_move_reason_known(pos, ii))
1539 worm_is_safe = 1;
1540 else if (trymove(pos, color, "mark-changed-dragon", ii)) {
1541 if (REVERSE_RESULT(attack(ii, NULL)) >= result_to_beat)
1542 worm_is_safe = 1;
1543 popgo();
1544 }
1545 if (worm_is_safe || move_reason_type == SEMEAI_MOVE) {
1546 /* This string can now be considered safe. Hence we mark the
1547 * whole string as such:
1548 */
1549 mark_changed_string(ii, safe_stones, strength, new_status);
1550 if (effective_size)
1551 *effective_size += worm[ii].effective_size;
1552 }
1553 }
1554 if (move_reason_type == OWL_DEFEND_MOVE_LOSS) {
1555 new_status = 0;
1556 mark_changed_string(affected2, safe_stones, strength, new_status);
1557 }
1558 }
1559}
1560
1561/* Marks the string at (affected) with the new status and accordingly
1562 * with the new strength.
1563 */
1564void
1565mark_changed_string(int affected, signed char safe_stones[BOARDMAX],
1566 float strength[BOARDMAX], signed char new_status)
1567{
1568 float new_strength;
1569 int ii;
1570
1571 ASSERT1(IS_STONE(board[affected]), affected);
1572
1573 if (new_status == 0)
1574 new_strength = 0.0;
1575 else {
1576 gg_assert(new_status == INFLUENCE_SAVED_STONE);
1577 new_strength = DEFAULT_STRENGTH;
1578 }
1579 for (ii = BOARDMIN; ii < BOARDMAX; ii++)
1580 if (board[ii] == board[affected]
1581 && same_string(ii, affected)) {
1582 strength[ii] = new_strength;
1583 safe_stones[ii] = new_status;
1584 }
1585}
1586
1587
1588/* Find dragons rescued by a move at (pos). */
1589void
1590get_saved_dragons(int pos, signed char saved[BOARDMAX])
1591{
1592 int k;
1593 memset(saved, 0, sizeof(saved[0]) * BOARDMAX);
1594
1595 for (k = 0; k < MAX_REASONS; k++) {
1596 int r = move[pos].reason[k];
1597 int what;
1598
1599 if (r < 0)
1600 break;
1601
1602 what = move_reasons[r].what;
1603 /* We exclude the ko contingent defenses, to avoid that the
1604 * confirm_safety routines spot an attack with ko and thinks the
1605 * move is unsafe.
1606 */
1607 if (move_reasons[r].type == OWL_DEFEND_MOVE)
1608 mark_dragon(what, saved, 1);
1609 }
1610}
1611
1612
1613/* If a move has saved the dragons in saved_dragons[] and worms in
1614 * saved_worms[], this functions writes the stones now supposedly safe
1615 * in the array safe_stones[].
1616 *
1617 * The safety of the played move itself is set according to
1618 * move[pos].move_safety.
1619 */
1620void
1621mark_safe_stones(int color, int move_pos,
1622 const signed char saved_dragons[BOARDMAX],
1623 const signed char saved_worms[BOARDMAX],
1624 signed char safe_stones[BOARDMAX])
1625{
1626 int pos;
1627
1628 for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
1629 if (board[pos] == OTHER_COLOR(color)) {
1630 if (dragon[pos].status == DEAD
1631 || (worm[pos].attack_codes[0] != 0
1632 && worm[pos].defense_codes[0] == 0))
1633 safe_stones[pos] = 0;
1634 else
1635 safe_stones[pos] = SAFE_STONE;
1636 }
1637 else if (board[pos] == color) {
1638 if ((worm[pos].attack_codes[0] != 0
1639 && (worm[pos].defense_codes[0] == 0 || !saved_worms[pos]))
1640 || dragon[pos].status == DEAD)
1641 safe_stones[pos] = 0;
1642 else if (saved_dragons[pos])
1643 safe_stones[pos] = OWL_SAVED_STONE;
1644 else if (dragon[pos].status == CRITICAL)
1645 safe_stones[pos] = 0;
1646 else
1647 safe_stones[pos] = SAFE_STONE;
1648 }
1649 else
1650 safe_stones[pos] = 0;
1651 }
1652 safe_stones[move_pos]
1653 = move[move_pos].move_safety && safe_move(move_pos, color) == WIN;
1654}
1655
1656
1657/* List the move reasons for (color)'s move at (pos). Return the
1658 * number of move reasons.
1659 */
1660int
1661list_move_reasons(FILE *out, int move_pos)
1662{
1663 int m;
1664 int n;
1665 int pos;
1666 int k;
1667 int reason1;
1668 int reason2;
1669 int aa = NO_MOVE;
1670 int bb = NO_MOVE;
1671 int worm1 = -1;
1672 int worm2 = -1;
1673 int num_move_reasons = 0;
1674
1675 gprintf("\nMove reasons:\n");
1676
1677 for (n = 0; n < board_size; n++)
1678 for (m = board_size-1; m >= 0; m--) {
1679 pos = POS(m, n);
1680
1681 if (move_pos != NO_MOVE && move_pos != pos)
1682 continue;
1683
1684 for (k = 0; k < MAX_REASONS; k++) {
1685 int r = move[pos].reason[k];
1686
1687 if (r < 0)
1688 break;
1689
1690 num_move_reasons++;
1691
1692 switch (move_reasons[r].type) {
1693 case ATTACK_MOVE:
1694 aa = move_reasons[r].what;
1695 gfprintf(out, "Move at %1m attacks %1m%s\n", pos, aa,
1696 (worm[aa].defense_codes[0] == 0) ? " (defenseless)" : "");
1697 break;
1698 case ATTACK_MOVE_GOOD_KO:
1699 aa = move_reasons[r].what;
1700 gfprintf(out, "Move at %1m attacks %1m%s with good ko\n", pos, aa,
1701 (worm[aa].defense_codes[0] == 0) ? " (defenseless)" : "");
1702 break;
1703 case ATTACK_MOVE_BAD_KO:
1704 aa = move_reasons[r].what;
1705 gfprintf(out, "Move at %1m attacks %1m%s with bad ko\n", pos, aa,
1706 (worm[aa].defense_codes[0] == 0) ? " (defenseless)" : "");
1707 break;
1708
1709 case DEFEND_MOVE:
1710 aa = move_reasons[r].what;
1711 gfprintf(out, "Move at %1m defends %1m\n", pos, aa);
1712 break;
1713 case DEFEND_MOVE_GOOD_KO:
1714 aa = move_reasons[r].what;
1715 gfprintf(out, "Move at %1m defends %1m with good ko\n", pos, aa);
1716 break;
1717 case DEFEND_MOVE_BAD_KO:
1718 aa = move_reasons[r].what;
1719 gfprintf(out, "Move at %1m defends %1m with bad ko\n", pos, aa);
1720 break;
1721
1722 case ATTACK_THREAT:
1723 case DEFEND_THREAT:
1724 aa = move_reasons[r].what;
1725
1726 if (move_reasons[r].type == ATTACK_THREAT)
1727 gfprintf(out, "Move at %1m threatens to attack %1m\n", pos, aa);
1728 else if (move_reasons[r].type == DEFEND_THREAT)
1729 gfprintf(out, "Move at %1m threatens to defend %1m\n", pos, aa);
1730 break;
1731
1732 case UNCERTAIN_OWL_DEFENSE:
1733 aa = move_reasons[r].what;
1734 if (board[aa] == current_color)
1735 gfprintf(out, "%1m found alive but not certainly, %1m defends it again\n",
1736 aa, pos);
1737 else
1738 gfprintf(out, "%1m found dead but not certainly, %1m attacks it again\n",
1739 aa, pos);
1740 break;
1741
1742 case CONNECT_MOVE:
1743 case CUT_MOVE:
1744 worm1 = conn_worm1[move_reasons[r].what];
1745 worm2 = conn_worm2[move_reasons[r].what];
1746 if (move_reasons[r].type == CONNECT_MOVE)
1747 gfprintf(out, "Move at %1m connects %1m and %1m\n",
1748 pos, worm1, worm2);
1749 else
1750 gfprintf(out, "Move at %1m cuts %1m and %1m\n", pos, worm1, worm2);
1751 break;
1752
1753 case ANTISUJI_MOVE:
1754 gfprintf(out, "Move at %1m is an antisuji\n", pos);
1755 break;
1756
1757 case SEMEAI_MOVE:
1758 aa = move_reasons[r].what;
1759 gfprintf(out, "Move at %1m wins semeai for %1m\n", pos, aa);
1760 break;
1761
1762 case SEMEAI_THREAT:
1763 aa = move_reasons[r].what;
1764 gfprintf(out, "Move at %1m threatens to win semeai for %1m\n",
1765 pos, aa);
1766 break;
1767
1768 case EITHER_MOVE:
1769 reason1 = either_data[move_reasons[r].what].reason1;
1770 reason2 = either_data[move_reasons[r].what].reason2;
1771 worm1 = either_data[move_reasons[r].what].what1;
1772 worm2 = either_data[move_reasons[r].what].what2;
1773 gfprintf(out, "Move at %1m either %s %1m or %s %1m\n", pos,
1774 reason1 == ATTACK_STRING ? "attacks" : "defends", worm1,
1775 reason2 == ATTACK_STRING ? "attacks" : "defends", worm2);
1776 break;
1777
1778 case ALL_MOVE:
1779 reason1 = all_data[move_reasons[r].what].reason1;
1780 reason2 = all_data[move_reasons[r].what].reason2;
1781 worm1 = all_data[move_reasons[r].what].what1;
1782 worm2 = all_data[move_reasons[r].what].what2;
1783 gfprintf(out, "Move at %1m both %s %1m and %s %1m\n", pos,
1784 reason1 == ATTACK_STRING ? "attacks" : "defends", worm1,
1785 reason2 == ATTACK_STRING ? "attacks" : "defends", worm2);
1786 break;
1787
1788 case OWL_ATTACK_MOVE:
1789 aa = move_reasons[r].what;
1790 gfprintf(out, "Move at %1m owl-attacks %1m\n", pos, aa);
1791 break;
1792 case OWL_ATTACK_MOVE_GOOD_KO:
1793 aa = move_reasons[r].what;
1794 gfprintf(out, "Move at %1m owl-attacks %1m with good ko\n", pos, aa);
1795 break;
1796 case OWL_ATTACK_MOVE_BAD_KO:
1797 aa = move_reasons[r].what;
1798 gfprintf(out, "Move at %1m owl-attacks %1m with bad ko\n", pos, aa);
1799 break;
1800 case OWL_ATTACK_MOVE_GAIN:
1801 aa = either_data[move_reasons[r].what].what1;
1802 bb = either_data[move_reasons[r].what].what2;
1803 gfprintf(out, "Move at %1m owl-attacks %1m (captures %1m)\n",
1804 pos, aa, bb);
1805 break;
1806
1807 case OWL_DEFEND_MOVE:
1808 aa = move_reasons[r].what;
1809 gfprintf(out, "Move at %1m owl-defends %1m\n", pos, aa);
1810 break;
1811 case OWL_DEFEND_MOVE_GOOD_KO:
1812 aa = move_reasons[r].what;
1813 gfprintf(out, "Move at %1m owl-defends %1m with good ko\n", pos, aa);
1814 break;
1815 case OWL_DEFEND_MOVE_BAD_KO:
1816 aa = move_reasons[r].what;
1817 gfprintf(out, "Move at %1m owl-defends %1m with bad ko\n", pos, aa);
1818 break;
1819 case OWL_DEFEND_MOVE_LOSS:
1820 aa = either_data[move_reasons[r].what].what1;
1821 bb = either_data[move_reasons[r].what].what2;
1822 gfprintf(out, "Move at %1m owl-defends %1m (loses %1m)\n",
1823 pos, aa, bb);
1824 break;
1825
1826 case OWL_ATTACK_THREAT:
1827 aa = move_reasons[r].what;
1828 gfprintf(out, "Move at %1m owl-threatens to attack %1m\n", pos, aa);
1829 break;
1830
1831 case OWL_DEFEND_THREAT:
1832 aa = move_reasons[r].what;
1833 gfprintf(out, "Move at %1m owl-threatens to defend %1m\n", pos, aa);
1834 break;
1835
1836 case OWL_PREVENT_THREAT:
1837 aa = move_reasons[r].what;
1838 gfprintf(out, "Move at %1m owl-prevents a threat to attack or defend %1m\n",
1839 pos, aa);
1840 break;
1841
1842 case EXPAND_TERRITORY_MOVE:
1843 gfprintf(out, "Move at %1m expands territory\n", pos);
1844 break;
1845
1846 case EXPAND_MOYO_MOVE:
1847 gfprintf(out, "Move at %1m expands moyo\n", pos);
1848 break;
1849
1850 case INVASION_MOVE:
1851 gfprintf(out, "Move at %1m is an invasion\n", pos);
1852 break;
1853
1854 case STRATEGIC_ATTACK_MOVE:
1855 case STRATEGIC_DEFEND_MOVE:
1856 aa = move_reasons[r].what;
1857
1858 if (move_reasons[r].type == STRATEGIC_ATTACK_MOVE)
1859 gfprintf(out, "Move at %1m strategically attacks %1m\n", pos, aa);
1860 else
1861 gfprintf(out, "Move at %1m strategically defends %1m\n", pos, aa);
1862 break;
1863
1864 case MY_ATARI_ATARI_MOVE:
1865 gfprintf(out, "Move at %1m captures something\n", pos);
1866
1867 case YOUR_ATARI_ATARI_MOVE:
1868 gfprintf(out, "Move at %1m defends against combination attack\n",
1869 pos);
1870 }
1871 }
1872 if (k > 0 && move[pos].move_safety == 0)
1873 gfprintf(out, "Move at %1m strategically or tactically unsafe\n", pos);
1874 }
1875
1876 return num_move_reasons;
1877}
1878
1879
1880
1881
1882/* This array lists rules according to which we set the status
1883 * flags of a move reasons.
1884 * The format is:
1885 * { List of reasons to which the rule applies, condition of the rule,
1886 * flags to be set, trace message }
1887 * The condition must be of type discard_condition_fn_ptr, that is a pointer
1888 * to a function with parameters (pos, what).
1889 *
1890 * FIXME: Add handling of ALL and EITHER moves for inessential worms.
1891 */
1892
1893static struct discard_rule discard_rules[] =
1894{
1895 { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO,
1896 ATTACK_MOVE_BAD_KO, ATTACK_THREAT,
1897 DEFEND_MOVE, DEFEND_MOVE_GOOD_KO,
1898 DEFEND_MOVE_BAD_KO, DEFEND_THREAT, -1 },
1899 owl_move_vs_worm_known, TERRITORY_REDUNDANT,
1900 " %1m: 0.0 - (threat of) attack/defense of %1m (owl attack/defense as well)\n" },
1901 { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
1902 owl_move_reason_known, REDUNDANT,
1903 " %1m: 0.0 - (threat to) win semeai involving %1m (owl move as well)\n"},
1904 { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
1905 tactical_move_vs_whole_dragon_known, REDUNDANT,
1906 " %1m: 0.0 - (threat to) win semeai involving %1m (tactical move as well)\n"},
1907 { { EITHER_MOVE, -1 },
1908 either_worm_attackable, REDUNDANT,
1909 " %1m: 0.0 - 'attack either' is redundant at %1m (direct att./def. as well)\n"},
1910 { { ALL_MOVE, -1 },
1911 one_of_both_attackable, REDUNDANT,
1912 " %1m: 0.0 - 'defend both' is redundant at %1m (direct att./def. as well)\n"},
1913 { { ATTACK_THREAT, DEFEND_THREAT, -1 },
1914 concerns_inessential_worm, TERRITORY_REDUNDANT,
1915 " %1m: 0.0 - attack/defense threat of %1m (inessential)\n"},
1916 { { OWL_ATTACK_THREAT, UNCERTAIN_OWL_DEFENSE, -1 },
1917 concerns_inessential_dragon, REDUNDANT,
1918 " %1m: 0.0 - (uncertain) owl attack/defense of %1m (inessential)\n"},
1919 { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO, ATTACK_MOVE_BAD_KO,
1920 DEFEND_MOVE, DEFEND_MOVE_GOOD_KO, DEFEND_MOVE_BAD_KO, -1},
1921 move_is_marked_unsafe, REDUNDANT,
1922 " %1m: 0.0 - tactical move vs %1m (unsafe move)\n"},
1923 { { OWL_ATTACK_MOVE, OWL_ATTACK_MOVE_GOOD_KO, OWL_ATTACK_MOVE_BAD_KO,
1924 OWL_DEFEND_MOVE, OWL_DEFEND_MOVE_GOOD_KO, OWL_DEFEND_MOVE_BAD_KO, -1},
1925 concerns_noncritical_dragon, REDUNDANT,
1926 " %1m: 0.0 - owl move vs %1m (non-critical)\n"},
1927 { { -1 }, NULL, 0, ""} /* Keep this entry at end of the list. */
1928};
1929
1930/* This function checks the list of move reasons for redundant move
1931 * reasons and marks them accordingly in their status field.
1932 */
1933void
1934discard_redundant_move_reasons(int pos)
1935{
1936 int k1, k2;
1937 int l;
1938 for (k1 = 0; !(discard_rules[k1].reason_type[0] == -1); k1++) {
1939 for (k2 = 0; !(discard_rules[k1].reason_type[k2] == -1); k2++) {
1940 for (l = 0; l < MAX_REASONS; l++) {
1941
1942 int r = move[pos].reason[l];
1943 if (r < 0)
1944 break;
1945 if ((move_reasons[r].type == discard_rules[k1].reason_type[k2])
1946 && (discard_rules[k1].condition(pos, move_reasons[r].what))) {
1947 DEBUG(DEBUG_MOVE_REASONS, discard_rules[k1].trace_message,
1948 pos, get_pos(move_reasons[r].type, move_reasons[r].what));
1949 move_reasons[r].status |= discard_rules[k1].flags;
1950 }
1951 }
1952 }
1953 }
1954}
1955
1956
1957/* Look through the move reasons to see whether (pos) is an antisuji move. */
1958int
1959is_antisuji_move(int pos)
1960{
1961 int k;
1962 for (k = 0; k < MAX_REASONS; k++) {
1963 int r = move[pos].reason[k];
1964 if (r < 0)
1965 break;
1966 if (move_reasons[r].type == ANTISUJI_MOVE)
1967 return 1; /* This move must not be played. End of story. */
1968 }
1969
1970 return 0;
1971}
1972
1973/* Increase the randomness scaling factor.
1974 * This causes the move value to be more random.
1975 */
1976
1977void
1978scale_randomness(int pos, float scaling)
1979{
1980 if (scaling > move[pos].randomness_scaling)
1981 move[pos].randomness_scaling = scaling;
1982}
1983
1984
1985/* Register the given `move' as a good attack threat against `target'. By
1986 * "good" we mean a threat which is effectively a sente for the player.
1987 * E.g. in this position the threat is good, because it results in four
1988 * sente moves locally (trevord:950):
1989 *
1990 * ..OX..
1991 * .O.*..
1992 * .OXX..
1993 * .OOX..
1994 * ------
1995 *
1996 * We use this list of good threats for performance reasons so that
1997 * estimate_territorial_value() in valuemoves.c doesn't have to read
1998 * through all the moves. Such threats are found with patterns.
1999 */
2000void
2001register_good_attack_threat(int move, int target)
2002{
2003 int k;
2004 ASSERT_ON_BOARD1(move);
2005 ASSERT_ON_BOARD1(target);
2006 ASSERT1(IS_STONE(worm[target].color), move);
2007
2008 target = worm[target].origin;
2009 for (k = 0; k < MAX_ATTACK_THREATS; k++) {
2010 if (known_good_attack_threats[move][k] == target)
2011 break;
2012 if (known_good_attack_threats[move][k] == NO_MOVE) {
2013 known_good_attack_threats[move][k] = target;
2014 break;
2015 }
2016 }
2017}
2018
2019
2020/* Determine if an attack threat is registered as good (see above). */
2021int
2022is_known_good_attack_threat(int move, int target)
2023{
2024 int k;
2025 ASSERT_ON_BOARD1(move);
2026 ASSERT_ON_BOARD1(target);
2027 ASSERT1(IS_STONE(worm[target].color), move);
2028
2029 target = worm[target].origin;
2030 for (k = 0; k < MAX_ATTACK_THREATS; k++) {
2031 if (known_good_attack_threats[move][k] == target)
2032 return 1;
2033 if (known_good_attack_threats[move][k] == NO_MOVE)
2034 break;
2035 }
2036
2037 return 0;
2038}
2039
2040/* Like documented in endgame:980, there are also moves which aren't
2041 * safe by themselves, but attempting to capture these stones would
2042 * result in a loss for the opponent (typically, by damezumari).
2043 * Simple examples include snapbacks, but more complicated ones do
2044 * exist. Following functions are helpers for the valuation processing
2045 * which deal with such special cases.
2046 */
2047void
2048register_known_safe_move(int move)
2049{
2050 ASSERT_ON_BOARD1(move);
2051
2052 known_safe_moves[move] = 1;
2053}
2054
2055
2056int
2057is_known_safe_move(int move)
2058{
2059 ASSERT_ON_BOARD1(move);
2060
2061 return known_safe_moves[move];
2062}
2063
2064
2065/*
2066 * Local Variables:
2067 * tab-width: 8
2068 * c-basic-offset: 2
2069 * End:
2070 */