can_countercapture(): foolproof api
[pachi.git] / tactics / 1lib.c
blob6c8459c846b7cb1cfc76a8f2a07d156eef066fd0
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 /* Checks snapbacks */
82 bool
83 can_countercapture(struct board *b, group_t group, struct move_queue *q, int tag)
85 enum stone color = board_at(b, group);
86 enum stone other = stone_other(color);
87 assert(color == S_BLACK || color == S_WHITE);
88 // Not checking b->clen, not maintained by board_quick_play()
90 unsigned int qmoves_prev = q ? q->moves : 0;
92 foreach_in_group(b, group) {
93 foreach_neighbor(b, c, {
94 group_t g = group_at(b, c);
95 if (likely(board_at(b, c) != other
96 || board_group_info(b, g).libs > 1) ||
97 !can_capture(b, g, color))
98 continue;
100 if (!q)
101 return true;
102 mq_add(q, board_group_info(b, group_at(b, c)).lib[0], tag);
103 mq_nodup(q);
105 } foreach_in_group_end;
107 bool can = q ? q->moves > qmoves_prev : false;
108 return can;
111 bool
112 can_countercapture_any(struct board *b, group_t group, struct move_queue *q, int tag)
114 enum stone color = board_at(b, group);
115 enum stone other = stone_other(color);
116 assert(color == S_BLACK || color == S_WHITE);
117 // Not checking b->clen, not maintained by board_quick_play()
119 unsigned int qmoves_prev = q ? q->moves : 0;
121 foreach_in_group(b, group) {
122 foreach_neighbor(b, c, {
123 group_t g = group_at(b, c);
124 if (likely(board_at(b, c) != other
125 || board_group_info(b, g).libs > 1))
126 continue;
127 coord_t lib = board_group_info(b, g).lib[0];
128 if (!board_is_valid_play(b, color, lib))
129 continue;
131 if (!q)
132 return true;
133 mq_add(q, board_group_info(b, group_at(b, c)).lib[0], tag);
134 mq_nodup(q);
136 } foreach_in_group_end;
138 bool can = q ? q->moves > qmoves_prev : false;
139 return can;
143 #ifdef NO_DOOMED_GROUPS
144 static bool
145 can_be_rescued(struct board *b, group_t group, enum stone color, int tag)
147 /* Does playing on the liberty rescue the group? */
148 if (can_play_on_lib(b, group, color))
149 return true;
151 /* Then, maybe we can capture one of our neighbors? */
152 return can_countercapture(b, group, NULL, tag);
154 #endif
156 void
157 group_atari_check(unsigned int alwaysccaprate, struct board *b, group_t group, enum stone to_play,
158 struct move_queue *q, coord_t *ladder, bool middle_ladder, int tag)
160 enum stone color = board_at(b, group_base(group));
161 coord_t lib = board_group_info(b, group).lib[0];
163 assert(color != S_OFFBOARD && color != S_NONE);
164 if (DEBUGL(5))
165 fprintf(stderr, "[%s] atariiiiiiiii %s of color %d\n",
166 coord2sstr(group, b), coord2sstr(lib, b), color);
167 assert(board_at(b, lib) == S_NONE);
169 if (to_play != color) {
170 /* We are the attacker! In that case, do not try defending
171 * our group, since we can capture the culprit. */
172 #ifdef NO_DOOMED_GROUPS
173 /* Do not remove group that cannot be saved by the opponent. */
174 if (!can_be_rescued(b, group, color, tag))
175 return;
176 #endif
177 if (can_play_on_lib(b, group, to_play)) {
178 mq_add(q, lib, tag);
179 mq_nodup(q);
181 return;
184 /* Can we capture some neighbor? */
185 /* XXX Attempts at using new can_countercapture() here failed so far.
186 * Could be because of a bug / under the stones situations
187 * (maybe not so uncommon in moggy ?) / it upsets moggy's balance somehow
188 * (there's always a chance opponent doesn't capture after taking snapback) */
189 bool ccap = can_countercapture_any(b, group, q, tag);
190 if (ccap && !ladder && alwaysccaprate > fast_random(100))
191 return;
193 /* Otherwise, do not save kos. */
194 if (group_is_onestone(b, group)
195 && neighbor_count_at(b, lib, color) + neighbor_count_at(b, lib, S_OFFBOARD) == 4) {
196 /* Except when the ko is for an eye! */
197 bool eyeconnect = false;
198 foreach_diag_neighbor(b, lib) {
199 if (board_at(b, c) == S_NONE && neighbor_count_at(b, c, color) + neighbor_count_at(b, c, S_OFFBOARD) == 4) {
200 eyeconnect = true;
201 break;
203 } foreach_diag_neighbor_end;
204 if (!eyeconnect)
205 return;
208 /* Do not suicide... */
209 if (!can_play_on_lib(b, group, to_play))
210 return;
211 if (DEBUGL(6))
212 fprintf(stderr, "...escape route valid\n");
214 /* ...or play out ladders (unless we can counter-capture anytime). */
215 if (!ccap) {
216 if (is_ladder(b, lib, group, middle_ladder)) {
217 /* Sometimes we want to keep the ladder move in the
218 * queue in order to discourage it. */
219 if (!ladder)
220 return;
221 else
222 *ladder = lib;
223 } else if (DEBUGL(6))
224 fprintf(stderr, "...no ladder\n");
227 mq_add(q, lib, tag);
228 mq_nodup(q);