Tactics: Split out selfatari.c and ladder.c
[pachi/derm.git] / tactics / selfatari.c
blob0bc254050a267c4eb36381ebece4b6bac39ef6e2
1 #include <assert.h>
2 #include <stdio.h>
3 #include <stdlib.h>
5 #define DEBUG
6 #include "board.h"
7 #include "debug.h"
8 #include "tactics/selfatari.h"
11 struct selfatari_state {
12 int groupcts[S_MAX];
13 group_t groupids[S_MAX][4];
15 /* This is set if this move puts a group out of _all_
16 * liberties; we need to watch out for snapback then. */
17 bool friend_has_no_libs;
18 /* We may have one liberty, but be looking for one more.
19 * In that case, @needs_more_lib is id of group
20 * already providing one, don't consider it again. */
21 group_t needs_more_lib;
22 /* ID of the first liberty, providing it again is not
23 * interesting. */
24 coord_t needs_more_lib_except;
27 static int
28 examine_friendly_groups(struct board *b, enum stone color, coord_t to, struct selfatari_state *s)
30 for (int i = 0; i < s->groupcts[color]; i++) {
31 /* We can escape by connecting to this group if it's
32 * not in atari. */
33 group_t g = s->groupids[color][i];
35 if (board_group_info(b, g).libs == 1) {
36 if (!s->needs_more_lib)
37 s->friend_has_no_libs = true;
38 // or we already have a friend with 1 lib
39 continue;
42 /* Could we self-atari the group here? */
43 if (board_group_info(b, g).libs > 2)
44 return false;
46 /* We need to have another liberty, and
47 * it must not be the other liberty of
48 * the group. */
49 int lib2 = board_group_other_lib(b, g, to);
50 /* Maybe we already looked at another
51 * group providing one liberty? */
52 if (s->needs_more_lib && s->needs_more_lib != g
53 && s->needs_more_lib_except != lib2)
54 return false;
56 /* Can we get the liberty locally? */
57 /* Yes if we are route to more liberties... */
58 if (s->groupcts[S_NONE] > 1)
59 return false;
60 /* ...or one liberty, but not lib2. */
61 if (s->groupcts[S_NONE] > 0
62 && !coord_is_adjecent(lib2, to, b))
63 return false;
65 /* ...ok, then we can still contribute a liberty
66 * later by capturing something. */
67 s->needs_more_lib = g;
68 s->needs_more_lib_except = lib2;
69 s->friend_has_no_libs = false;
72 return -1;
75 static int
76 examine_enemy_groups(struct board *b, enum stone color, coord_t to, struct selfatari_state *s)
78 /* We may be able to gain a liberty by capturing this group. */
79 group_t can_capture = 0;
81 /* Examine enemy groups: */
82 for (int i = 0; i < s->groupcts[stone_other(color)]; i++) {
83 /* We can escape by capturing this group if it's in atari. */
84 group_t g = s->groupids[stone_other(color)][i];
85 if (board_group_info(b, g).libs > 1)
86 continue;
88 /* But we need to get to at least two liberties by this;
89 * we already have one outside liberty, or the group is
90 * more than 1 stone (in that case, capturing is always
91 * nice!). */
92 if (s->groupcts[S_NONE] > 0 || !group_is_onestone(b, g))
93 return false;
94 /* ...or, it's a ko stone, */
95 if (neighbor_count_at(b, g, color) + neighbor_count_at(b, g, S_OFFBOARD) == 3) {
96 /* and we don't have a group to save: then, just taking
97 * single stone means snapback! */
98 if (!s->friend_has_no_libs)
99 return false;
101 /* ...or, we already have one indirect liberty provided
102 * by another group. */
103 if (s->needs_more_lib || (can_capture && can_capture != g))
104 return false;
105 can_capture = g;
109 if (DEBUGL(6))
110 fprintf(stderr, "no cap group\n");
112 if (!s->needs_more_lib && !can_capture && !s->groupcts[S_NONE]) {
113 /* We have no hope for more fancy tactics - this move is simply
114 * a suicide, not even a self-atari. */
115 if (DEBUGL(6))
116 fprintf(stderr, "suicide\n");
117 return true;
119 /* XXX: I wonder if it makes sense to continue if we actually
120 * just !s->needs_more_lib. */
122 return -1;
125 static int
126 setup_nakade_or_snapback(struct board *b, enum stone color, coord_t to, struct selfatari_state *s)
128 /* There is another possibility - we can self-atari if it is
129 * a nakade: we put an enemy group in atari from the inside. */
130 /* This branch also allows eyes falsification:
131 * O O O . . (This is different from throw-in to false eye
132 * X X O O . checked below in that there is no X stone at the
133 * X . X O . right of the star point in this diagram.)
134 * X X X O O
135 * X O * . . */
136 /* TODO: Allow to only nakade if the created shape is dead
137 * (http://senseis.xmp.net/?Nakade). */
139 /* This branch also covers snapback, which is kind of special
140 * nakade case. ;-) */
141 for (int i = 0; i < s->groupcts[stone_other(color)]; i++) {
142 group_t g = s->groupids[stone_other(color)][i];
143 if (board_group_info(b, g).libs != 2)
144 goto next_group;
145 /* Simple check not to re-examine the same group. */
146 if (i > 0 && s->groupids[stone_other(color)][i] == s->groupids[stone_other(color)][i - 1])
147 continue;
149 /* We must make sure the other liberty of that group:
150 * (i) is an internal liberty
151 * (ii) filling it to capture our group will not gain
152 * safety */
154 /* Let's look at neighbors of the other liberty: */
155 int lib2 = board_group_other_lib(b, g, to);
156 foreach_neighbor(b, lib2, {
157 /* This neighbor of course does not contribute
158 * anything to the enemy. */
159 if (board_at(b, c) == S_OFFBOARD)
160 continue;
162 /* If the other liberty has empty neighbor,
163 * it must be the original liberty; otherwise,
164 * since the whole group has only 2 liberties,
165 * the other liberty may not be internal and
166 * we are nakade'ing eyeless group from outside,
167 * which is stupid. */
168 if (board_at(b, c) == S_NONE) {
169 if (c == to)
170 continue;
171 else
172 goto next_group;
175 int g2 = group_at(b, c);
176 /* If the neighbor is of our color, it must
177 * be also a 2-lib group. If it is more,
178 * we CERTAINLY want that liberty to be played
179 * first, what if it is an alive group? If it
180 * is in atari, we want to extend from it to
181 * prevent eye-making capture. However, if it
182 * is 2-lib, it is selfatari connecting two
183 * nakade'ing groups! */
184 /* X X X X We will not allow play on 'a',
185 * X X a X because 'b' would capture two
186 * X O b X different groups, forming two
187 * X X X X eyes. */
188 if (board_at(b, c) == color) {
189 if (board_group_info(b, group_at(b, c)).libs == 2)
190 continue;
191 goto next_group;
194 /* The neighbor is enemy color. It's ok if
195 * it's still the same group or this is its
196 * only liberty. */
197 if (g == g2 || board_group_info(b, g2).libs == 1)
198 continue;
199 /* Otherwise, it must have the exact same
200 * liberties as the original enemy group. */
201 if (board_group_info(b, g2).libs == 2
202 && (board_group_info(b, g2).lib[0] == to
203 || board_group_info(b, g2).lib[1] == to))
204 continue;
206 goto next_group;
209 /* Now, we must distinguish between nakade and eye
210 * falsification; we must not falsify an eye by more
211 * than two stones. */
212 if (s->groupcts[color] < 1)
213 return false; // simple throw-in
214 if (s->groupcts[color] == 1 && group_is_onestone(b, s->groupids[color][0])) {
215 /* More complex throw-in - we are in one of
216 * three situations:
217 * a O O O O X b O O O X c O O O X
218 * O . X . O O X . . O . X .
219 * # # # # # # # # # # # # #
220 * b is desirable here (since maybe O has no
221 * backup two eyes); a may be desirable, but
222 * is tested next in check_throwin(). c is
223 * never desirable. */
224 group_t g2 = s->groupids[color][0];
225 assert(board_group_info(b, g2).libs <= 2);
226 if (board_group_info(b, g2).libs == 1)
227 return false; // b
228 goto next_group; // a or c
231 /* We would create more than 2-stone group; in that
232 * case, the liberty of our result must be lib2,
233 * indicating this really is a nakade. */
234 for (int j = 0; j < s->groupcts[color]; j++) {
235 group_t g2 = s->groupids[color][j];
236 assert(board_group_info(b, g2).libs <= 2);
237 if (board_group_info(b, g2).libs == 2) {
238 if (board_group_info(b, g2).lib[0] != lib2
239 && board_group_info(b, g2).lib[1] != lib2)
240 goto next_group;
241 } else {
242 assert(board_group_info(b, g2).lib[0] == to);
246 return false;
247 next_group:
248 /* Unless we are dealing with snapback setup, we don't need to look
249 * further. */
250 if (s->groupcts[color])
251 return -1;
254 return -1;
257 static int
258 check_throwin(struct board *b, enum stone color, coord_t to, struct selfatari_state *s)
260 /* We can be throwing-in to false eye:
261 * X X X O X X X O X X X X X
262 * X . * X * O . X * O O . X
263 * # # # # # # # # # # # # # */
264 /* We cannot sensibly throw-in into a corner. */
265 if (neighbor_count_at(b, to, S_OFFBOARD) < 2
266 && neighbor_count_at(b, to, stone_other(color))
267 + neighbor_count_at(b, to, S_OFFBOARD) == 3
268 && board_is_false_eyelike(b, to, stone_other(color))) {
269 assert(s->groupcts[color] <= 1);
270 /* Single-stone throw-in may be ok... */
271 if (s->groupcts[color] == 0) {
272 /* O X . There is one problem - when it's
273 * . * X actually not a throw-in!
274 * # # # */
275 foreach_neighbor(b, to, {
276 if (board_at(b, c) == S_NONE) {
277 /* Is the empty neighbor an escape path? */
278 /* (Note that one S_NONE neighbor is already @to.) */
279 if (neighbor_count_at(b, c, stone_other(color))
280 + neighbor_count_at(b, c, S_OFFBOARD) < 2)
281 return -1;
284 return false;
287 /* Multi-stone throwin...? */
288 assert(s->groupcts[color] == 1);
289 group_t g = s->groupids[color][0];
291 assert(board_group_info(b, g).libs <= 2);
292 /* Suicide is definitely NOT ok, no matter what else
293 * we could test. */
294 if (board_group_info(b, g).libs == 1)
295 return true;
297 /* In that case, we must be connected to at most one stone,
298 * or throwin will not destroy any eyes. */
299 if (group_is_onestone(b, g))
300 return false;
302 return -1;
305 bool
306 is_bad_selfatari_slow(struct board *b, enum stone color, coord_t to)
308 if (DEBUGL(5))
309 fprintf(stderr, "sar check %s %s\n", stone2str(color), coord2sstr(to, b));
310 /* Assess if we actually gain any liberties by this escape route.
311 * Note that this is not 100% as we cannot check whether we are
312 * connecting out or just to ourselves. */
314 struct selfatari_state s;
315 memset(&s, 0, sizeof(s));
316 int d;
318 foreach_neighbor(b, to, {
319 enum stone color = board_at(b, c);
320 s.groupids[color][s.groupcts[color]++] = group_at(b, c);
323 /* We have shortage of liberties; that's the point. */
324 assert(s.groupcts[S_NONE] <= 1);
326 d = examine_friendly_groups(b, color, to, &s);
327 if (d >= 0)
328 return d;
330 if (DEBUGL(6))
331 fprintf(stderr, "no friendly group\n");
333 d = examine_enemy_groups(b, color, to, &s);
334 if (d >= 0)
335 return d;
337 if (DEBUGL(6))
338 fprintf(stderr, "no escape\n");
340 d = setup_nakade_or_snapback(b, color, to, &s);
341 if (d >= 0)
342 return d;
344 if (DEBUGL(6))
345 fprintf(stderr, "no nakade group\n");
347 d = check_throwin(b, color, to, &s);
348 if (d >= 0)
349 return d;
351 if (DEBUGL(6))
352 fprintf(stderr, "no throw-in group\n");
354 /* No way to pull out, no way to connect out. This really
355 * is a bad self-atari! */
356 return true;