Updated README: Equal sign not required with `--mode` flag.
[sgk-go] / patterns / helpers.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 <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
51int
52jump_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
72int
73jump_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
85int
86high_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
100int
101reinforce_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
137int
138throw_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
188int
189seki_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
213int
214cutstone2_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
260int
261edge_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
283void
284threaten_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 */
296void
297prevent_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
332void
333threaten_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
392void
393defend_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
429void
430amalgamate_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
447int
448finish_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
472int
473squeeze_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
499int
500backfill_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
523int
524owl_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
549int
550connect_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
608int
609connect_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
657void
658test_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 */
698int
699adjacent_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 */
717int
718adjacent_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
732void
733backfill_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 */
759int
760thrash_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 */
795int
796break_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 */
832int 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 */