can_countercapture(): check snapback / can_countercapture_any(): no checks
[pachi.git] / tactics / 1lib.c
blob8ffc2be4690680d1918618838f99b786d15ed6c7
1 #include <assert.h>
2 #include <stdio.h>
3 #include <stdlib.h>
5 #define QUICK_BOARD_CODE
7 #define DEBUG
8 #include "board.h"
9 #include "debug.h"
10 #include "mq.h"
11 #include "tactics/1lib.h"
12 #include "tactics/ladder.h"
13 #include "tactics/selfatari.h"
16 static inline bool
17 capturing_group_is_snapback(struct board *b, group_t group)
19 coord_t lib = board_group_info(b, group).lib[0];
21 if (immediate_liberty_count(b, lib) > 0 ||
22 group_stone_count(b, group, 2) > 1)
23 return false;
25 enum stone to_play = stone_other(board_at(b, group));
26 enum stone other = stone_other(to_play);
27 if (board_is_eyelike(b, lib, other))
28 return false;
30 foreach_neighbor(b, lib, {
31 group_t g = group_at(b, c);
32 if (board_at(b, c) == S_OFFBOARD || g == group)
33 continue;
35 if (board_at(b, c) == other &&
36 board_group_info(b, g).libs == 1) // capture more than one group
37 return false;
38 if (board_at(b, c) == to_play &&
39 board_group_info(b, g).libs > 1)
40 return false;
41 });
42 return true;
45 /* Whether to avoid capturing/atariing doomed groups (this is big
46 * performance hit and may reduce playouts balance; it does increase
47 * the strength, but not quite proportionally to the performance). */
48 //#define NO_DOOMED_GROUPS
51 static inline bool
52 can_capture(struct board *b, group_t g, enum stone to_play)
54 coord_t capture = board_group_info(b, g).lib[0];
55 if (DEBUGL(6))
56 fprintf(stderr, "can capture group %d (%s)?\n",
57 g, coord2sstr(capture, b));
58 /* Does playing on the liberty usefully capture the group? */
59 if (board_is_valid_play(b, to_play, capture)
60 && !capturing_group_is_snapback(b, g))
61 return true;
63 return false;
66 static inline bool
67 can_play_on_lib(struct board *b, group_t g, enum stone to_play)
69 coord_t capture = board_group_info(b, g).lib[0];
70 if (DEBUGL(6))
71 fprintf(stderr, "can capture group %d (%s)?\n",
72 g, coord2sstr(capture, b));
73 /* Does playing on the liberty usefully capture the group? */
74 if (board_is_valid_play(b, to_play, capture)
75 && !is_bad_selfatari(b, to_play, capture))
76 return true;
78 return false;
81 /* Check snapbacks */
82 static inline __attribute__((always_inline)) bool
83 capturable_group(struct board *b, enum stone capturer, coord_t c,
84 enum stone to_play)
86 group_t g = group_at(b, c);
87 if (likely(board_at(b, c) != stone_other(capturer)
88 || board_group_info(b, g).libs > 1))
89 return false;
91 return can_capture(b, g, to_play);
94 bool
95 can_countercapture(struct board *b, enum stone owner, group_t g,
96 enum stone to_play, struct move_queue *q, int tag)
98 //if (!b->clen)
99 // return false;
101 unsigned int qmoves_prev = q ? q->moves : 0;
103 foreach_in_group(b, g) {
104 foreach_neighbor(b, c, {
105 if (!capturable_group(b, owner, c, to_play))
106 continue;
108 if (!q) {
109 return true;
111 mq_add(q, board_group_info(b, group_at(b, c)).lib[0], tag);
112 mq_nodup(q);
114 } foreach_in_group_end;
116 bool can = q ? q->moves > qmoves_prev : false;
117 return can;
120 static inline __attribute__((always_inline)) bool
121 capturable_group_fast(struct board *b, enum stone capturer, coord_t c,
122 enum stone to_play)
124 group_t g = group_at(b, c);
125 if (likely(board_at(b, c) != stone_other(capturer)
126 || board_group_info(b, g).libs > 1))
127 return false;
129 coord_t lib = board_group_info(b, g).lib[0];
130 return board_is_valid_play(b, to_play, lib);
133 bool
134 can_countercapture_any(struct board *b, enum stone owner, group_t g,
135 enum stone to_play, struct move_queue *q, int tag)
137 //if (b->clen < 2)
138 // return false;
140 unsigned int qmoves_prev = q ? q->moves : 0;
142 foreach_in_group(b, g) {
143 foreach_neighbor(b, c, {
144 if (!capturable_group_fast(b, owner, c, to_play))
145 continue;
147 if (!q) {
148 return true;
150 mq_add(q, board_group_info(b, group_at(b, c)).lib[0], tag);
151 mq_nodup(q);
153 } foreach_in_group_end;
155 bool can = q ? q->moves > qmoves_prev : false;
156 return can;
159 #if 0
160 bool
161 can_countercapture_(struct board *b, enum stone owner, group_t g,
162 enum stone to_play, struct move_queue *q, int tag)
164 if (!g ||
165 board_at(b, g) != owner ||
166 owner != to_play) {
167 board_print(b, stderr);
168 fprintf(stderr, "can_countercap(%s %s %s): \n",
169 stone2str(owner), coord2sstr(g, b), stone2str(to_play));
171 /* Sanity checks */
172 assert(g);
173 assert(board_at(b, g) == owner);
174 assert(owner == to_play);
176 #if 0
177 bool r1 = my_can_countercapture(b, owner, g, to_play, NULL, 0);
178 bool r2 = orig_can_countercapture(b, owner, g, to_play, NULL, 0);
179 if (r1 != r2) {
180 fprintf(stderr, "---------------------------------------------------------------\n");
181 board_print(b, stderr);
182 fprintf(stderr, "can_countercap(%s %s %s) diff ! my:%i org:%i\n",
183 stone2str(owner), coord2sstr(g, b), stone2str(to_play), r1, r2);
185 #endif
186 return orig_can_countercapture(b, owner, g, to_play, q, tag);
188 #endif
190 #ifdef NO_DOOMED_GROUPS
191 static bool
192 can_be_rescued(struct board *b, group_t group, enum stone color, int tag)
194 /* Does playing on the liberty rescue the group? */
195 if (can_play_on_lib(b, group, color))
196 return true;
198 /* Then, maybe we can capture one of our neighbors? */
199 return can_countercapture(b, color, group, color, NULL, tag);
201 #endif
203 void
204 group_atari_check(unsigned int alwaysccaprate, struct board *b, group_t group, enum stone to_play,
205 struct move_queue *q, coord_t *ladder, bool middle_ladder, int tag)
207 enum stone color = board_at(b, group_base(group));
208 coord_t lib = board_group_info(b, group).lib[0];
210 assert(color != S_OFFBOARD && color != S_NONE);
211 if (DEBUGL(5))
212 fprintf(stderr, "[%s] atariiiiiiiii %s of color %d\n",
213 coord2sstr(group, b), coord2sstr(lib, b), color);
214 assert(board_at(b, lib) == S_NONE);
216 if (to_play != color) {
217 /* We are the attacker! In that case, do not try defending
218 * our group, since we can capture the culprit. */
219 #ifdef NO_DOOMED_GROUPS
220 /* Do not remove group that cannot be saved by the opponent. */
221 if (!can_be_rescued(b, group, color, tag))
222 return;
223 #endif
224 if (can_play_on_lib(b, group, to_play)) {
225 mq_add(q, lib, tag);
226 mq_nodup(q);
228 return;
231 /* Can we capture some neighbor? */
232 /* XXX Attempts at using new can_countercapture() here failed so far.
233 * Could be because of a bug / under the stones situations
234 * (maybe not so uncommon in moggy ?) / it upsets moggy's balance somehow
235 * (there's always a chance opponent doesn't capture after taking snapback) */
236 bool ccap = can_countercapture_any(b, color, group, to_play, q, tag);
237 if (ccap && !ladder && alwaysccaprate > fast_random(100))
238 return;
240 /* Otherwise, do not save kos. */
241 if (group_is_onestone(b, group)
242 && neighbor_count_at(b, lib, color) + neighbor_count_at(b, lib, S_OFFBOARD) == 4) {
243 /* Except when the ko is for an eye! */
244 bool eyeconnect = false;
245 foreach_diag_neighbor(b, lib) {
246 if (board_at(b, c) == S_NONE && neighbor_count_at(b, c, color) + neighbor_count_at(b, c, S_OFFBOARD) == 4) {
247 eyeconnect = true;
248 break;
250 } foreach_diag_neighbor_end;
251 if (!eyeconnect)
252 return;
255 /* Do not suicide... */
256 if (!can_play_on_lib(b, group, to_play))
257 return;
258 if (DEBUGL(6))
259 fprintf(stderr, "...escape route valid\n");
261 /* ...or play out ladders (unless we can counter-capture anytime). */
262 if (!ccap) {
263 if (is_ladder(b, lib, group, middle_ladder)) {
264 /* Sometimes we want to keep the ladder move in the
265 * queue in order to discourage it. */
266 if (!ladder)
267 return;
268 else
269 *ladder = lib;
270 } else if (DEBUGL(6))
271 fprintf(stderr, "...no ladder\n");
274 mq_add(q, lib, tag);
275 mq_nodup(q);