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
39 struct move_data move[BOARDMAX];
40 struct move_reason move_reasons[MAX_MOVE_REASONS];
41 int next_reason;
42
43 /* Connections */
44 int conn_worm1[MAX_CONNECTIONS];
45 int conn_worm2[MAX_CONNECTIONS];
46 int next_connection;
47
48 /* Potential semeai moves. */
49 int semeai_target1[MAX_POTENTIAL_SEMEAI];
50 int semeai_target2[MAX_POTENTIAL_SEMEAI];
51 static int next_semeai;
52
53 /* Unordered sets (currently pairs) of move reasons / targets */
54 Reason_set either_data[MAX_EITHER];
55 int next_either;
56 Reason_set all_data[MAX_ALL];
57 int next_all;
58
59 /* Eye shapes */
60 int eyes[MAX_EYES];
61 int eyecolor[MAX_EYES];
62 int next_eye;
63
64 /* Lunches */
65 int lunch_dragon[MAX_LUNCHES]; /* eater */
66 int lunch_worm[MAX_LUNCHES]; /* food */
67 int next_lunch;
68
69 /* Point redistribution */
70 int replacement_map[BOARDMAX];
71
72 /* The color for which we are evaluating moves. */
73 int current_color;
74
75 /* Attack threats that are known to be sente locally. */
76 static 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 */
81 static int known_safe_moves[BOARDMAX];
82
83 /* Helper functions to check conditions in discard rules. */
84 typedef int (*discard_condition_fn_ptr)(int pos, int what);
85
86 struct 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. */
95 void
clear_move_reasons(void)96 clear_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 */
157 int
find_connection(int worm1,int worm2)158 find_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
182 static int
find_either_data(int reason1,int what1,int reason2,int what2)183 find_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
211 static int
find_all_data(int reason1,int what1,int reason2,int what2)212 find_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
240 static int
find_pair_data(int what1,int what2)241 find_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 */
266 static int
get_pos(int reason,int what)267 get_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 */
334 void
add_lunch(int eater,int food)335 add_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 */
362 static void
add_move_reason(int pos,int type,int what)363 add_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 */
410 static void
remove_move_reason(int pos,int type,int what)411 remove_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 */
442 int
move_reason_known(int pos,int type,int what)443 move_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 */
469 int
attack_move_reason_known(int pos,int what)470 attack_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 */
487 int
defense_move_reason_known(int pos,int what)488 defense_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 */
504 static int
tactical_move_vs_whole_dragon_known(int pos,int what)505 tactical_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 */
516 int
owl_attack_move_reason_known(int pos,int what)517 owl_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 */
532 int
owl_defense_move_reason_known(int pos,int what)533 owl_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 */
548 int
owl_move_reason_known(int pos,int what)549 owl_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 */
559 static int
owl_move_vs_worm_known(int pos,int what)560 owl_move_vs_worm_known(int pos, int what)
561 {
562 return owl_move_reason_known(pos, dragon[what].origin);
563 }
564
565 int
semeai_move_reason_known(int pos,int what)566 semeai_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 */
572 static int
concerns_inessential_worm(int pos,int what)573 concerns_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 */
581 static int
concerns_inessential_dragon(int pos,int what)582 concerns_inessential_dragon(int pos, int what)
583 {
584 UNUSED(pos);
585 return DRAGON2(what).safety == INESSENTIAL;
586 }
587
588 static int
move_is_marked_unsafe(int pos,int what)589 move_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. */
597 static int
concerns_noncritical_dragon(int pos,int what)598 concerns_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 */
610 static int
either_worm_attackable(int pos,int what)611 either_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 */
624 static int
one_of_both_attackable(int pos,int what)625 one_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 */
642 void
add_attack_move(int pos,int ww,int code)643 add_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 */
660 void
add_defense_move(int pos,int ww,int code)661 add_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 */
678 void
add_attack_threat_move(int pos,int ww,int code)679 add_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
689 void
remove_attack_threat_move(int pos,int ww)690 remove_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 */
700 void
add_defense_threat_move(int pos,int ww,int code)701 add_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 */
713 int
get_attack_threats(int pos,int max_strings,int strings[])714 get_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 */
738 int
get_defense_threats(int pos,int max_strings,int strings[])739 get_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 */
763 int
get_biggest_owl_target(int pos)764 get_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 */
799 void
add_connection_move(int pos,int w1,int w2)800 add_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 */
819 void
add_cut_move(int pos,int w1,int w2)820 add_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 */
848 void
add_antisuji_move(int pos)849 add_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 */
862 void
add_semeai_move(int pos,int dr)863 add_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 */
873 static void
add_potential_semeai_move(int pos,int type,int dr1,int dr2)874 add_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 */
893 void
add_potential_semeai_attack(int pos,int dr1,int dr2)894 add_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 */
903 void
add_potential_semeai_defense(int pos,int dr1,int dr2)904 add_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 */
916 void
add_semeai_threat(int pos,int dr)917 add_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 */
939 void
add_either_move(int pos,int reason1,int target1,int reason2,int target2)940 add_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 */
1010 void
add_all_move(int pos,int reason1,int target1,int reason2,int target2)1011 add_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
1049 void
add_loss_move(int pos,int target1,int target2)1050 add_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 */
1063 void
add_expand_territory_move(int pos)1064 add_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 */
1073 void
add_expand_moyo_move(int pos)1074 add_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 */
1082 void
add_invasion_move(int pos)1083 add_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 */
1097 void
add_shape_value(int pos,float value)1098 add_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 */
1117 void
add_worthwhile_threat_move(int pos)1118 add_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 */
1127 void
add_strategical_attack_move(int pos,int dr)1128 add_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 */
1139 void
add_strategical_defense_move(int pos,int dr)1140 add_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 */
1151 void
add_owl_attack_move(int pos,int dr,int kworm,int code)1152 add_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 */
1173 void
add_owl_defense_move(int pos,int dr,int code)1174 add_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 */
1193 void
add_owl_attack_threat_move(int pos,int dr,int code)1194 add_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 */
1208 void
add_owl_uncertain_defense_move(int pos,int dr)1209 add_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 */
1220 void
add_owl_uncertain_attack_move(int pos,int dr)1221 add_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 */
1234 void
add_owl_defense_threat_move(int pos,int dr,int code)1235 add_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 */
1250 void
add_my_atari_atari_move(int pos,int size)1251 add_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 */
1259 void
add_your_atari_atari_move(int pos,int size)1260 add_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 */
1274 void
add_owl_prevent_threat_move(int pos,int dr)1275 add_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 */
1284 void
add_followup_value(int pos,float value)1285 add_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 */
1295 void
add_reverse_followup_value(int pos,float value)1296 add_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 */
1306 int
set_minimum_move_value(int pos,float value)1307 set_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 */
1320 void
set_maximum_move_value(int pos,float value)1321 set_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 */
1331 void
set_minimum_territorial_value(int pos,float value)1332 set_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 */
1342 void
set_maximum_territorial_value(int pos,float value)1343 set_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 */
1354 void
add_replacement_move(int from,int to,int color)1355 add_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). */
1416 void
get_saved_worms(int pos,signed char saved[BOARDMAX])1417 get_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 */
1459 void
mark_changed_dragon(int pos,int color,int affected,int affected2,int move_reason_type,signed char safe_stones[BOARDMAX],float strength[BOARDMAX],float * effective_size)1460 mark_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 */
1564 void
mark_changed_string(int affected,signed char safe_stones[BOARDMAX],float strength[BOARDMAX],signed char new_status)1565 mark_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). */
1589 void
get_saved_dragons(int pos,signed char saved[BOARDMAX])1590 get_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 */
1620 void
mark_safe_stones(int color,int move_pos,const signed char saved_dragons[BOARDMAX],const signed char saved_worms[BOARDMAX],signed char safe_stones[BOARDMAX])1621 mark_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 */
1660 int
list_move_reasons(FILE * out,int move_pos)1661 list_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
1893 static 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 */
1933 void
discard_redundant_move_reasons(int pos)1934 discard_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. */
1958 int
is_antisuji_move(int pos)1959 is_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
1977 void
scale_randomness(int pos,float scaling)1978 scale_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 */
2000 void
register_good_attack_threat(int move,int target)2001 register_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). */
2021 int
is_known_good_attack_threat(int move,int target)2022 is_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 */
2047 void
register_known_safe_move(int move)2048 register_known_safe_move(int move)
2049 {
2050 ASSERT_ON_BOARD1(move);
2051
2052 known_safe_moves[move] = 1;
2053 }
2054
2055
2056 int
is_known_safe_move(int move)2057 is_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 */
2071