Commit | Line | Data |
---|---|---|
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 <stdio.h> | |
25 | #include "liberty.h" | |
26 | #include "patterns.h" | |
27 | ||
28 | ||
29 | #define TRYMOVE(pos, color) trymove(pos, color, "helper", NO_MOVE) | |
30 | #define OFFSET_BY(x, y) AFFINE_TRANSFORM(OFFSET(x, y), trans, move) | |
31 | #define ARGS struct pattern *pattern, int trans, int move, int color | |
32 | ||
33 | ||
34 | /* This file contains helper functions which assist the pattern matcher. | |
35 | * They are invoked with (move) = the position on the board marked with '*'. | |
36 | * They are invoked with color = WHITE or BLACK: any pieces on the | |
37 | * board marked with 'O' in the pattern will always contain color, | |
38 | * and 'X's contain OTHER_COLOR(color) | |
39 | * | |
40 | * The helper must return 0 if the pattern is rejected and 1 otherwise. | |
41 | */ | |
42 | ||
43 | ||
44 | ||
45 | ||
46 | /* Jump out into nothingness. To avoid jumping into our own territory, | |
47 | * we use the "area" measure. Also we never ever jump into our own | |
48 | * established eyespace. | |
49 | */ | |
50 | ||
51 | int | |
52 | jump_out_helper(ARGS) | |
53 | { | |
54 | int own_eyespace; | |
55 | ||
56 | UNUSED(trans); UNUSED(pattern); | |
57 | ||
58 | own_eyespace = (white_eye[move].color == color); | |
59 | ||
60 | if (whose_area(OPPOSITE_INFLUENCE(color), move) != color && !own_eyespace) | |
61 | return 1; | |
62 | else | |
63 | return 0; | |
64 | } | |
65 | ||
66 | ||
67 | /* Make a long jump into nothingness. Since these jumps are not | |
68 | * securely connected we don't use them to jump into the opponent's | |
69 | * zone of control. | |
70 | */ | |
71 | ||
72 | int | |
73 | jump_out_far_helper(ARGS) | |
74 | { | |
75 | if (whose_area(OPPOSITE_INFLUENCE(color), move) != OTHER_COLOR(color)) | |
76 | return jump_out_helper(pattern, trans, move, color); | |
77 | else | |
78 | return 0; | |
79 | } | |
80 | ||
81 | ||
82 | /* Active until the opponent has played his first stone. | |
83 | */ | |
84 | ||
85 | int | |
86 | high_handicap_helper(ARGS) | |
87 | { | |
88 | UNUSED(trans); UNUSED(pattern); UNUSED(move); | |
89 | ||
90 | return !doing_scoring && stones_on_board(OTHER_COLOR(color)) == 0; | |
91 | } | |
92 | ||
93 | ||
94 | /* Active when the opponent is thought to be everywhere dead. This | |
95 | * typically happens early in high handicap games on small boards. | |
96 | * This helper is used by patterns intended to reinforce possible | |
97 | * weaknesses in the position. | |
98 | */ | |
99 | ||
100 | int | |
101 | reinforce_helper(ARGS) | |
102 | { | |
103 | UNUSED(trans); UNUSED(pattern); | |
104 | ||
105 | return (!doing_scoring | |
106 | && !lively_dragon_exists(OTHER_COLOR(color)) | |
107 | && safe_move(move, color)); | |
108 | } | |
109 | ||
110 | ||
111 | /* | |
112 | * | |
113 | * XXO XXc decrease eye space in sente (unless it kills) | |
114 | * .*X e*a | |
115 | * --- --- | |
116 | * | |
117 | * or | |
118 | * | |
119 | * XXO XXc decrease eye space in sente (unless it kills) | |
120 | * .*X e*a | |
121 | * XXO XXd | |
122 | * | |
123 | * or | |
124 | * | |
125 | * |XXO |XXc decrease eye space in sente (unless it kills) | |
126 | * |.*X |e*a | |
127 | * |XXO |XXd | |
128 | * | |
129 | * or | |
130 | * | |
131 | * |XXO |XXc decrease eye space in sente (unless it kills) | |
132 | * |.*X |e*a | |
133 | * +--- +--- | |
134 | * | |
135 | */ | |
136 | ||
137 | int | |
138 | throw_in_atari_helper(ARGS) | |
139 | { | |
140 | int apos, bpos, cpos, dpos; | |
141 | int success = 0; | |
142 | int other = OTHER_COLOR(color); | |
143 | int libs[2]; | |
144 | UNUSED(pattern); | |
145 | ||
146 | apos = OFFSET_BY(0, 1); | |
147 | cpos = OFFSET_BY(-1, 1); | |
148 | dpos = OFFSET_BY(1, 1); | |
149 | ||
150 | /* Find second liberty of the stone a. */ | |
151 | findlib(apos, 2, libs); | |
152 | if (libs[0] != move) | |
153 | bpos = libs[0]; | |
154 | else | |
155 | bpos = libs[1]; | |
156 | ||
157 | if (TRYMOVE(move, color)) { | |
158 | if (!attack(cpos, NULL) && !(ON_BOARD(dpos) && attack(dpos, NULL))) { | |
159 | if (TRYMOVE(bpos, other)) { | |
160 | if (attack(apos, NULL)) | |
161 | success = 1; | |
162 | popgo(); | |
163 | } | |
164 | else { | |
165 | success = 1; /* X move at (bpos) would have been suicide */ | |
166 | } | |
167 | } | |
168 | popgo(); | |
169 | } | |
170 | ||
171 | /* The followup is to capture the "a" string. Estimate the value to | |
172 | * twice the size. | |
173 | */ | |
174 | add_followup_value(move, 2 * worm[apos].effective_size); | |
175 | TRACE("...followup value %f\n", 2 * worm[apos].effective_size); | |
176 | ||
177 | return success; | |
178 | } | |
179 | ||
180 | ||
181 | /* This is intended for use in autohelpers. */ | |
182 | ||
183 | /* Check whether the string at (str) can attack any surrounding | |
184 | * string. If so, return false as the move to create a seki (probably) | |
185 | * wouldn't work. | |
186 | */ | |
187 | ||
188 | int | |
189 | seki_helper(int str) | |
190 | { | |
191 | int r; | |
192 | int adj; | |
193 | int adjs[MAXCHAIN]; | |
194 | ||
195 | adj = chainlinks(str, adjs); | |
196 | for (r = 0; r < adj; r++) | |
197 | if (worm[adjs[r]].attack_codes[0] != 0) | |
198 | return 0; | |
199 | ||
200 | return 1; | |
201 | } | |
202 | ||
203 | ||
204 | /* | |
205 | * XO aO | |
206 | * O* O* | |
207 | * | |
208 | * Increase the cutstone2 field if * is a potential cutting point, | |
209 | * i.e. if it does work as a cutting point once 'a' has been | |
210 | * defended. This helper is expected to always return 0. | |
211 | */ | |
212 | ||
213 | int | |
214 | cutstone2_helper(ARGS) | |
215 | { | |
216 | int apos; | |
217 | int bpos; | |
218 | int cpos; | |
219 | int dpos; | |
220 | UNUSED(pattern); | |
221 | UNUSED(color); | |
222 | ||
223 | if (stackp > 0) | |
224 | return 0; | |
225 | ||
226 | apos = OFFSET_BY(-1, -1); | |
227 | bpos = OFFSET_BY(-1, 0); | |
228 | cpos = OFFSET_BY( 0, -1); | |
229 | ||
230 | if (worm[apos].defense_codes[0] == 0) | |
231 | return 0; | |
232 | ||
233 | dpos = worm[apos].defense_points[0]; | |
234 | ||
235 | if (TRYMOVE(dpos, board[apos])) { | |
236 | if (!board[bpos] || attack(bpos, NULL) | |
237 | || !board[cpos] || attack(cpos, NULL) | |
238 | || safe_move(move, board[apos]) != 0) { | |
239 | popgo(); | |
240 | worm[worm[apos].origin].cutstone2++; | |
241 | propagate_worm(worm[apos].origin); | |
242 | return 0; | |
243 | } | |
244 | popgo(); | |
245 | } | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
250 | /* | |
251 | * ?x?? ?x?? | |
252 | * ?X.. ?Xb. | |
253 | * O*.. c*a. | |
254 | * | |
255 | * Is the push at * sente? c must have exactly two liberties. This is | |
256 | * called edge_double_sente_helper because it mainly called for edge | |
257 | * hanes, but it can be used anywhere on the board. | |
258 | */ | |
259 | ||
260 | int | |
261 | edge_double_sente_helper(int move, int apos, int bpos, int cpos) | |
262 | { | |
263 | int color = board[cpos]; | |
264 | int success = 0; | |
265 | ASSERT1((color == BLACK || color == WHITE), move); | |
266 | ||
267 | if (TRYMOVE(move, color)) { | |
268 | ASSERT1(countlib(move) == 2, move); | |
269 | success = connect_and_cut_helper(move, apos, bpos); | |
270 | popgo(); | |
271 | } | |
272 | ||
273 | return success; | |
274 | } | |
275 | ||
276 | /* | |
277 | * This is intended for use in autohelpers. | |
278 | * | |
279 | * Give a conservative estimate of the value of saving the string (str) | |
280 | * by capturing one opponent stone. | |
281 | */ | |
282 | ||
283 | void | |
284 | threaten_to_save_helper(int move, int str) | |
285 | { | |
286 | add_followup_value(move, 2.0 + 2.0 * worm[str].effective_size); | |
287 | TRACE("...followup value %f\n", 2.0 + 2.0 * worm[str].effective_size); | |
288 | } | |
289 | ||
290 | ||
291 | /* For use in autohelpers. | |
292 | * | |
293 | * Adds a reverse followup value if the opponent's move here would threaten | |
294 | * to capture (str). | |
295 | */ | |
296 | void | |
297 | prevent_attack_threat_helper(int move, int str) | |
298 | { | |
299 | add_reverse_followup_value(move, 2.0 * worm[str].effective_size); | |
300 | TRACE("...reverse followup value %f\n", 2.0 * worm[str].effective_size); | |
301 | } | |
302 | ||
303 | ||
304 | /* This function is obsolete. Code in `value_moves.c' is more general. */ | |
305 | #if 0 | |
306 | ||
307 | /* | |
308 | * This is intended for use in autohelpers. | |
309 | * | |
310 | * Estimate the value of capturing the string (str) and add this as | |
311 | * a followup value. We don't do this for too stupid looking threats, | |
312 | * however, e.g. in a position like | |
313 | * | |
314 | * OOOO.. | |
315 | * XXX.*. | |
316 | * XOOOX. | |
317 | * XXXXO. | |
318 | * | |
319 | * where X can get out of atari with profit by capturing three O stones. | |
320 | * | |
321 | * Another case where we don't award the followup value is when the | |
322 | * opponent can defend with a threat against our move, e.g. in this | |
323 | * position: | |
324 | * | |
325 | * .OOOXX. | |
326 | * .OXXO.X | |
327 | * ..*.X.. | |
328 | * ..XX... | |
329 | * | |
330 | */ | |
331 | ||
332 | void | |
333 | threaten_to_capture_helper(int move, int str) | |
334 | { | |
335 | int adj, adjs[MAXCHAIN]; | |
336 | int defense_move; | |
337 | int k; | |
338 | ||
339 | adj = chainlinks2(str, adjs, 1); | |
340 | for (k = 0; k < adj; k++) | |
341 | if (worm[adjs[k]].defense_codes[0] != 0 | |
342 | && !does_defend(move, adjs[k])) | |
343 | return; | |
344 | ||
345 | if (!TRYMOVE(move, OTHER_COLOR(board[str]))) | |
346 | return; | |
347 | if (find_defense(str, &defense_move) != 0 | |
348 | && defense_move != NO_MOVE | |
349 | && TRYMOVE(defense_move, board[str])) { | |
350 | if (board[move] == EMPTY || attack(move, NULL) != 0) { | |
351 | popgo(); | |
352 | popgo(); | |
353 | return; | |
354 | } | |
355 | popgo(); | |
356 | } | |
357 | ||
358 | /* In addition to the move found by find_defense(), also try all | |
359 | * chain breaking moves in the same way. | |
360 | */ | |
361 | adj = chainlinks2(str, adjs, 1); | |
362 | for (k = 0; k < adj; k++) { | |
363 | int lib; | |
364 | findlib(adjs[k], 1, &lib); | |
365 | if (TRYMOVE(lib, board[str])) { | |
366 | if (!attack(str, NULL) | |
367 | && (board[move] == EMPTY || attack(move, NULL) != 0)) { | |
368 | popgo(); | |
369 | popgo(); | |
370 | return; | |
371 | } | |
372 | popgo(); | |
373 | } | |
374 | } | |
375 | ||
376 | popgo(); | |
377 | ||
378 | add_followup_value(move, 2.0 * worm[str].effective_size); | |
379 | TRACE("...followup value %f\n", 2.0 * worm[str].effective_size); | |
380 | } | |
381 | ||
382 | #endif | |
383 | ||
384 | ||
385 | /* | |
386 | * This is intended for use in autohelpers. | |
387 | * | |
388 | * Estimate the value of defending a string which can be put into | |
389 | * atari and add this as a reverse followup value. | |
390 | */ | |
391 | ||
392 | void | |
393 | defend_against_atari_helper(int move, int str) | |
394 | { | |
395 | int adj, adjs[MAXCHAIN]; | |
396 | int libs[2]; | |
397 | int k; | |
398 | ||
399 | ASSERT1(countlib(str) == 2, str); | |
400 | ||
401 | /* No value if the string can capture out of atari. */ | |
402 | adj = chainlinks2(str, adjs, 1); | |
403 | for (k = 0; k < adj; k++) | |
404 | if (worm[adjs[k]].defense_codes[0] != 0 | |
405 | && !does_defend(move, adjs[k])) | |
406 | return; | |
407 | ||
408 | /* No value if opponent has no safe atari. */ | |
409 | findlib(str, 2, libs); | |
410 | if (!safe_move(libs[0], OTHER_COLOR(board[str])) | |
411 | && !safe_move(libs[1], OTHER_COLOR(board[str]))) | |
412 | return; | |
413 | ||
414 | TRACE("...reverse followup value %f\n", 2.0 * worm[str].effective_size); | |
415 | add_reverse_followup_value(move, 2.0 * worm[str].effective_size); | |
416 | } | |
417 | ||
418 | ||
419 | /* | |
420 | * This is intended for use in conn.db autohelpers. | |
421 | * | |
422 | * Amalgamate either a with b or c with b, depending on which of the | |
423 | * two dragons a and c is largest. | |
424 | * | |
425 | * If either of these pairs already have been amalgamated somehow, | |
426 | * do nothing. | |
427 | */ | |
428 | ||
429 | void | |
430 | amalgamate_most_valuable_helper(int apos, int bpos, int cpos) | |
431 | { | |
432 | if (!is_same_dragon(apos, bpos) && !is_same_dragon(bpos, cpos)) { | |
433 | if (dragon[apos].effective_size >= dragon[cpos].effective_size) | |
434 | join_dragons(apos, bpos); | |
435 | else | |
436 | join_dragons(bpos, cpos); | |
437 | } | |
438 | } | |
439 | ||
440 | ||
441 | /* | |
442 | * This is intended for use in autohelpers. | |
443 | * | |
444 | * Returns 1 if (pos) is adjacent to a stone which can be captured by ko. | |
445 | */ | |
446 | ||
447 | int | |
448 | finish_ko_helper(int pos) | |
449 | { | |
450 | int adj, adjs[MAXCHAIN]; | |
451 | int lib; | |
452 | int k; | |
453 | ||
454 | adj = chainlinks2(pos, adjs, 1); | |
455 | for (k = 0; k < adj; k++) { | |
456 | if (countstones(adjs[k]) == 1) { | |
457 | findlib(adjs[k], 1, &lib); | |
458 | if (is_ko(lib, board[pos], NULL)) | |
459 | return 1; | |
460 | } | |
461 | } | |
462 | return 0; | |
463 | } | |
464 | ||
465 | ||
466 | /* | |
467 | * This is intended for use in autohelpers. | |
468 | * | |
469 | * Returns 1 if (ai, aj) is next to a ko point. | |
470 | */ | |
471 | ||
472 | int | |
473 | squeeze_ko_helper(int pos) | |
474 | { | |
475 | int libs[2]; | |
476 | int liberties; | |
477 | int k; | |
478 | ||
479 | liberties = findlib(pos, 2, libs); | |
480 | ASSERT1(liberties == 2, pos); | |
481 | ||
482 | for (k = 0; k < liberties; k++) { | |
483 | int aa = libs[k]; | |
484 | if (is_ko(aa, OTHER_COLOR(board[pos]), NULL)) | |
485 | return 1; | |
486 | } | |
487 | ||
488 | return 0; | |
489 | } | |
490 | ||
491 | /* | |
492 | * This is intended for use in autohelpers. | |
493 | * | |
494 | * If after playing a and b, the string at c can be attacked, this | |
495 | * function adds a small fixed move value for a move which defends | |
496 | * c. | |
497 | */ | |
498 | ||
499 | int | |
500 | backfill_helper(int apos, int bpos, int cpos) | |
501 | { | |
502 | int color = board[cpos]; | |
503 | int other = OTHER_COLOR(color); | |
504 | int dpos = NO_MOVE; | |
505 | ||
506 | if (TRYMOVE(apos, color)) { | |
507 | if (TRYMOVE(bpos, other)) { | |
508 | if (attack(cpos, NULL) && find_defense(cpos, &dpos)) { | |
509 | set_minimum_move_value(dpos, 0.1); | |
510 | TRACE("%o...setting min move value of %1m to 0.1\n", dpos); | |
511 | } | |
512 | popgo(); | |
513 | } | |
514 | popgo(); | |
515 | } | |
516 | ||
517 | return 0; | |
518 | } | |
519 | ||
520 | ||
521 | /* Returns true if (apos) kills or threatens to kill (bpos). */ | |
522 | ||
523 | int | |
524 | owl_threatens_attack(int apos, int bpos) | |
525 | { | |
526 | if (DRAGON2(bpos).owl_status == CRITICAL | |
527 | && DRAGON2(bpos).owl_attack_point == apos) | |
528 | return 1; | |
529 | ||
530 | if (DRAGON2(bpos).owl_threat_status == CAN_THREATEN_ATTACK) | |
531 | if (DRAGON2(bpos).owl_attack_point == apos | |
532 | || DRAGON2(bpos).owl_second_attack_point == apos) | |
533 | return 1; | |
534 | ||
535 | return 0; | |
536 | } | |
537 | ||
538 | ||
539 | /* Returns true if O needs to connect at c in the position below after | |
540 | * O at b and X at d, because X can cut at c. In general d is the | |
541 | * second liberty of A, which must have exactly two liberties. | |
542 | * | |
543 | * |.X |dX | |
544 | * |XO |AO | |
545 | * |XO |Ae | |
546 | * |.. |bc | |
547 | */ | |
548 | ||
549 | int | |
550 | connect_and_cut_helper(int Apos, int bpos, int cpos) | |
551 | { | |
552 | int dpos; | |
553 | int epos = NO_MOVE; | |
554 | int other = board[Apos]; | |
555 | int color = OTHER_COLOR(other); | |
556 | int libs[2]; | |
557 | int liberties = findlib(Apos, 2, libs); | |
558 | int result = 0; | |
559 | int k; | |
560 | ||
561 | gg_assert(IS_STONE(color)); | |
562 | gg_assert(liberties == 2); | |
563 | ||
564 | if (libs[0] == bpos) | |
565 | dpos = libs[1]; | |
566 | else | |
567 | dpos = libs[0]; | |
568 | ||
569 | for (k = 0; k < 4; k++) | |
570 | if (board[cpos + delta[k]] == color | |
571 | && neighbor_of_string(cpos + delta[k], Apos)) { | |
572 | epos = cpos + delta[k]; | |
573 | break; | |
574 | } | |
575 | ||
576 | gg_assert(epos != NO_MOVE); | |
577 | ||
578 | if (TRYMOVE(bpos, color)) { | |
579 | if (TRYMOVE(dpos, other)) { | |
580 | if (TRYMOVE(cpos, other)) { | |
581 | if (board[bpos] == EMPTY | |
582 | || board[epos] == EMPTY | |
583 | || !defend_both(bpos, epos)) | |
584 | result = 1; | |
585 | popgo(); | |
586 | } | |
587 | popgo(); | |
588 | } | |
589 | popgo(); | |
590 | } | |
591 | ||
592 | return result; | |
593 | } | |
594 | ||
595 | ||
596 | ||
597 | /* | |
598 | * This is similar to connect_and_cut_helper(), except it starts with | |
599 | * a move at A and that d is found as a general defense point for A. A | |
600 | * is no longer restricted to two liberties. | |
601 | * | |
602 | * |.X |dX | |
603 | * |XO |XO | |
604 | * |.O |Ae | |
605 | * |.. |bc | |
606 | */ | |
607 | ||
608 | int | |
609 | connect_and_cut_helper2(int Apos, int bpos, int cpos, int color) | |
610 | { | |
611 | int dpos; | |
612 | int epos = NO_MOVE; | |
613 | int other = OTHER_COLOR(color); | |
614 | int result = 0; | |
615 | int k; | |
616 | ||
617 | gg_assert(IS_STONE(color)); | |
618 | ||
619 | ||
620 | if (TRYMOVE(Apos, color)) { | |
621 | for (k = 0; k < 4; k++) | |
622 | if (board[cpos + delta[k]] == other | |
623 | && neighbor_of_string(cpos + delta[k], Apos)) { | |
624 | epos = cpos + delta[k]; | |
625 | break; | |
626 | } | |
627 | ||
628 | gg_assert(epos != NO_MOVE); | |
629 | ||
630 | if (TRYMOVE(bpos, other)) { | |
631 | if (!find_defense(Apos, &dpos) || dpos == NO_MOVE) { | |
632 | popgo(); | |
633 | popgo(); | |
634 | return 0; | |
635 | } | |
636 | ||
637 | if (TRYMOVE(dpos, color)) { | |
638 | if (TRYMOVE(cpos, color)) { | |
639 | if (board[bpos] == EMPTY | |
640 | || board[epos] == EMPTY | |
641 | || !defend_both(bpos, epos)) | |
642 | result = 1; | |
643 | popgo(); | |
644 | } | |
645 | popgo(); | |
646 | } | |
647 | popgo(); | |
648 | } | |
649 | popgo(); | |
650 | } | |
651 | ||
652 | return result; | |
653 | } | |
654 | ||
655 | ||
656 | ||
657 | void | |
658 | test_attack_either_move(int move, int color, int worma, int wormb) | |
659 | { | |
660 | ASSERT_ON_BOARD1(move); | |
661 | ASSERT1(board[move] == EMPTY, move); | |
662 | ASSERT1(board[worma] == OTHER_COLOR(color) | |
663 | && board[wormb] == OTHER_COLOR(color), move); | |
664 | ||
665 | if (!defend_both(worma, wormb)) { | |
666 | if (0) | |
667 | gprintf("%1m: Reject attack_either_move for %1m, %1m (can't defend both)\n", | |
668 | move, worma, wormb); | |
669 | return; | |
670 | } | |
671 | if (trymove(move, color, "test_attack_either_move", worma)) { | |
672 | if (board[worma] == OTHER_COLOR(color) | |
673 | && board[wormb] == OTHER_COLOR(color)) { | |
674 | if (!find_defense(worma, NULL) || !find_defense(wormb, NULL)) { | |
675 | if (0) | |
676 | gprintf("%1m: Rej. attack_either_move for %1m & %1m (regular attack)\n", | |
677 | move, worma, wormb); | |
678 | } | |
679 | else if (!defend_both(worma, wormb)) | |
680 | add_either_move(move, ATTACK_STRING, worma, ATTACK_STRING, wormb); | |
681 | else { | |
682 | if (0) | |
683 | gprintf("%1m: Rej. attack_either_move for %1m & %1m (doesn't work)\n", | |
684 | move, worma, wormb); | |
685 | } | |
686 | } | |
687 | else | |
688 | if (0) | |
689 | gprintf("%1m: Rej. attack_either_move for %1m & %1m (captured directly)\n", | |
690 | move, worma, wormb); | |
691 | popgo(); | |
692 | } | |
693 | } | |
694 | ||
695 | /* True if str is adjacent to a stone in atari, which is tactically | |
696 | * attackable (to exclude pointless captures of snapback stones). | |
697 | */ | |
698 | int | |
699 | adjacent_to_stone_in_atari(int str) | |
700 | { | |
701 | int adj; | |
702 | int adjs[MAXCHAIN]; | |
703 | int k; | |
704 | ||
705 | adj = chainlinks2(str, adjs, 1); | |
706 | for (k = 0; k < adj; k++) | |
707 | if (attack(adjs[k], NULL)) | |
708 | return 1; | |
709 | ||
710 | return 0; | |
711 | } | |
712 | ||
713 | ||
714 | /* True if str is adjacent to a stone in atari, which is tactically | |
715 | * defendable. | |
716 | */ | |
717 | int | |
718 | adjacent_to_defendable_stone_in_atari(int str) | |
719 | { | |
720 | int adj; | |
721 | int adjs[MAXCHAIN]; | |
722 | int k; | |
723 | ||
724 | adj = chainlinks2(str, adjs, 1); | |
725 | for (k = 0; k < adj; k++) | |
726 | if (attack_and_defend(adjs[k], NULL, NULL, NULL, NULL)) | |
727 | return 1; | |
728 | ||
729 | return 0; | |
730 | } | |
731 | ||
732 | void | |
733 | backfill_replace(int move, int str) | |
734 | { | |
735 | int defense_move = NO_MOVE; | |
736 | ||
737 | if (TRYMOVE(move, OTHER_COLOR(board[str]))) { | |
738 | if (attack_and_defend(str, NULL, NULL, NULL, &defense_move)) { | |
739 | /* Must undo the trymove before adding the replacement move. */ | |
740 | popgo(); | |
741 | add_replacement_move(move, defense_move, board[str]); | |
742 | } | |
743 | else | |
744 | popgo(); | |
745 | } | |
746 | } | |
747 | ||
748 | ||
749 | /* True if | |
750 | * 1. White to move. | |
751 | * 2. All white stones look dead. | |
752 | * 3. Less than 40% of the board is filled or less than 20% of the | |
753 | * board is filled with white stones. | |
754 | * | |
755 | * This is intended for patterns forcing white to thrash around early | |
756 | * in high handicap games, instead of passing because it looks like no | |
757 | * stones can live. | |
758 | */ | |
759 | int | |
760 | thrash_around_helper(ARGS) | |
761 | { | |
762 | UNUSED(pattern); | |
763 | UNUSED(trans); | |
764 | UNUSED(move); | |
765 | ||
766 | /* Do not generate these moves when doing scoring or if fuseki move | |
767 | * generation is disabled (typically used when solving life and | |
768 | * death problems embedded on a big board). | |
769 | */ | |
770 | if (doing_scoring | |
771 | || disable_fuseki | |
772 | || (stones_on_board(BLACK | WHITE) > board_size * board_size * 2 / 5 | |
773 | && stones_on_board(WHITE) > board_size * board_size / 5) | |
774 | || color == BLACK | |
775 | || lively_dragon_exists(WHITE)) | |
776 | return 0; | |
777 | ||
778 | return 1; | |
779 | } | |
780 | ||
781 | ||
782 | /* Returns true if | |
783 | * | |
784 | * 1. The board size is odd. | |
785 | * 2. A white move is being generated. | |
786 | * 3. The komi is less than or equal to zero. | |
787 | * 4. str is placed at tengen. | |
788 | * 5. At least 10 moves have been played. | |
789 | * 6. The board is currently mirror symmetric. | |
790 | * | |
791 | * This is intended for patterns to break mirror go when black starts at | |
792 | * tengen and then mirrors white. We only care about breaking the mirroring | |
793 | * if komi is non-positive, otherwise the mirroring is to our advantage. | |
794 | */ | |
795 | int | |
796 | break_mirror_helper(int str, int color) | |
797 | { | |
798 | if (board_size % 2 == 1 | |
799 | && color == WHITE | |
800 | && komi <= 0.0 | |
801 | && I(str) == (board_size - 1) / 2 | |
802 | && J(str) == (board_size - 1) / 2 | |
803 | && stones_on_board(BLACK | WHITE) > 10 | |
804 | && test_symmetry_after_move(PASS_MOVE, EMPTY, 1)) | |
805 | return 1; | |
806 | ||
807 | return 0; | |
808 | } | |
809 | ||
810 | ||
811 | /* This helper is intended to detect semeai kind of positions where | |
812 | * the tactical reading can't be trusted enough to allow amalgamation | |
813 | * over presumably tactically dead strings. | |
814 | * | |
815 | * It has turned out to be best not to trust tactical reading of three | |
816 | * and four liberty strings at all. Not trusting two liberty strings | |
817 | * leads to an underamalgamation and unnecessarily many dragons on the | |
818 | * board. Therefore we try to detect two liberty strings with an | |
819 | * enclosed nakade, which after capturing leads to an unreliable | |
820 | * reading at three or four liberties. | |
821 | * | |
822 | * More specifically we check whether the string has a neighbor with | |
823 | * the following properties: | |
824 | * 1. At least three stones in size. | |
825 | * 2. All its liberties are common liberties with the string. | |
826 | * 3. It has no second order liberties. | |
827 | * 4. Its liberties are adjacent to no other strings than itself and | |
828 | * the primary string. | |
829 | * | |
830 | * If we find such a neighbor 1 is returned, otherwise 0. | |
831 | */ | |
832 | int distrust_tactics_helper(int str) | |
833 | { | |
834 | int color = board[str]; | |
835 | int adj; | |
836 | int adjs[MAXCHAIN]; | |
837 | int k; | |
838 | int r; | |
839 | int s; | |
840 | int lib = countlib(str); | |
841 | ||
842 | ASSERT1(IS_STONE(board[str]), str); | |
843 | ||
844 | if (lib > 2) | |
845 | return 1; | |
846 | else if (lib == 1) | |
847 | return 0; | |
848 | ||
849 | adj = chainlinks3(str, adjs, lib); | |
850 | for (r = 0; r < adj; r++) { | |
851 | int nakade = 1; | |
852 | int adjlib; | |
853 | int adjlibs[3]; | |
854 | if (countstones(adjs[r]) < 3) | |
855 | continue; | |
856 | adjlib = findlib(adjs[r], 3, adjlibs); | |
857 | for (s = 0; s < adjlib; s++) { | |
858 | int str_found = 0; | |
859 | for (k = 0; k < 4; k++) { | |
860 | int pos = adjlibs[s] + delta[k]; | |
861 | if (board[pos] == EMPTY | |
862 | && !liberty_of_string(pos, adjs[r])) | |
863 | nakade = 0; | |
864 | else if (board[pos] == color) { | |
865 | if (same_string(pos, str)) | |
866 | str_found = 1; | |
867 | else | |
868 | nakade = 0; | |
869 | } | |
870 | else if (board[pos] == OTHER_COLOR(color) | |
871 | && !same_string(pos, adjs[r])) | |
872 | nakade = 0; | |
873 | } | |
874 | if (!str_found) | |
875 | nakade = 0; | |
876 | } | |
877 | if (nakade) | |
878 | return 1; | |
879 | } | |
880 | ||
881 | return 0; | |
882 | } | |
883 | ||
884 | /* | |
885 | * LOCAL Variables: | |
886 | * tab-width: 8 | |
887 | * c-basic-offset: 2 | |
888 | * End: | |
889 | */ |