9 #include "tactics/2lib.h"
10 #include "tactics/selfatari.h"
11 #include "tactics/goals.h"
14 /* Whether to avoid capturing/atariing doomed groups (this is big
15 * performance hit and may reduce playouts balance; it does increase
16 * the strength, but not quite proportionally to the performance). */
17 //#define NO_DOOMED_GROUPS
21 miai_2lib(struct board
*b
, group_t group
, enum stone color
)
23 bool can_connect
= false, can_pull_out
= false;
24 /* We have miai if we can either connect on both libs,
25 * or connect on one lib and escape on another. (Just
26 * having two escape routes can be risky.) We must make
27 * sure that we don't consider following as miai:
30 * O O X O - left dot would be pull-out, right dot connect */
31 foreach_neighbor(b
, board_group_info(b
, group
).lib
[0], {
32 enum stone cc
= board_at(b
, c
);
33 if (cc
== S_NONE
&& cc
!= board_at(b
, board_group_info(b
, group
).lib
[1])) {
35 } else if (cc
!= color
) {
39 group_t cg
= group_at(b
, c
);
40 if (cg
&& cg
!= group
&& board_group_info(b
, cg
).libs
> 1)
43 foreach_neighbor(b
, board_group_info(b
, group
).lib
[1], {
44 enum stone cc
= board_at(b
, c
);
45 if (c
== board_group_info(b
, group
).lib
[0])
47 if (cc
== S_NONE
&& can_connect
) {
49 } else if (cc
!= color
) {
53 group_t cg
= group_at(b
, c
);
54 if (cg
&& cg
!= group
&& board_group_info(b
, cg
).libs
> 1)
55 return (can_connect
|| can_pull_out
);
61 defense_is_hopeless(struct board
*b
, group_t group
, enum stone owner
,
62 enum stone to_play
, coord_t lib
, coord_t otherlib
,
65 /* If we are the defender not connecting out, do not
66 * escape with moves that do not gain liberties anyway
67 * - either the new extension has just single extra
68 * liberty, or the "gained" liberties are shared. */
69 /* XXX: We do not check connecting to a short-on-liberty
70 * group (e.g. ourselves). */
72 fprintf(stderr
, "\tif_check %d and defending %d and uscount %d ilcount %d\n",
73 use
, to_play
== owner
,
74 neighbor_count_at(b
, lib
, owner
),
75 immediate_liberty_count(b
, lib
));
78 if (to_play
== owner
&& neighbor_count_at(b
, lib
, owner
) == 1) {
79 if (immediate_liberty_count(b
, lib
) == 1)
81 if (immediate_liberty_count(b
, lib
) == 2
82 && coord_is_adjecent(lib
, otherlib
, b
))
89 can_atari_group(struct board
*b
, group_t group
, enum stone owner
,
90 enum stone to_play
, struct libmap_mq
*q
,
91 int tag
, struct libmap_group lmg
, hash_t ca_hash
,
92 bool use_def_no_hopeless
)
94 bool have
[2] = { false, false };
95 bool preference
[2] = { true, true };
96 for (int i
= 0; i
< 2; i
++) {
97 coord_t lib
= board_group_info(b
, group
).lib
[i
];
98 assert(board_at(b
, lib
) == S_NONE
);
99 if (!board_is_valid_play(b
, to_play
, lib
))
103 fprintf(stderr
, "- checking liberty %s of %s %s, filled by %s\n",
105 stone2str(owner
), coord2sstr(group
, b
),
108 /* Don't play at the spot if it is extremely short
110 /* XXX: This looks harmful, could significantly
111 * prefer atari to throwin:
117 if (neighbor_count_at(b
, lib
, stone_other(owner
)) + immediate_liberty_count(b
, lib
) < 2)
121 /* Prevent hopeless escape attempts. */
122 if (defense_is_hopeless(b
, group
, owner
, to_play
, lib
,
123 board_group_info(b
, group
).lib
[1 - i
],
124 use_def_no_hopeless
))
127 #ifdef NO_DOOMED_GROUPS
128 /* If the owner can't play at the spot, we don't want
129 * to bother either. */
130 if (is_bad_selfatari(b
, owner
, lib
))
134 /* Of course we don't want to play bad selfatari
135 * ourselves, if we are the attacker... */
137 #ifdef NO_DOOMED_GROUPS
140 is_bad_selfatari(b
, to_play
, lib
)) {
142 fprintf(stderr
, "\tliberty is selfatari\n");
143 coord_t coord
= pass
;
145 if (to_play
!= owner
) {
146 /* Okay! We are attacker; maybe we just need
147 * to connect a false eye before atari - this
148 * is very common in the corner. */
149 coord
= selfatari_cousin(b
, to_play
, lib
, &bygroup
);
153 /* Ok, connect, but prefer not to. */
154 enum stone byowner
= board_at(b
, bygroup
);
156 fprintf(stderr
, "\treluctantly switching to cousin %s (group %s %s)\n",
157 coord2sstr(coord
, b
), coord2sstr(bygroup
, b
), stone2str(byowner
));
158 /* One more thing - is the cousin sensible defense
159 * for the other group? */
160 if (defense_is_hopeless(b
, bygroup
, byowner
, to_play
,
162 use_def_no_hopeless
))
165 preference
[i
] = false;
167 /* By now, we must be decided we add the move to the
168 * queue! [comment intentionally misindented] */
174 /* If the move is too "lumpy", prefer the alternative:
177 * ..O.X.X <- always play the left one!
179 if (neighbor_count_at(b
, lib
, to_play
) + neighbor_count_at(b
, lib
, S_OFFBOARD
) >= 3) {
181 fprintf(stderr
, "\tlumpy: mine %d + edge %d\n",
182 neighbor_count_at(b
, lib
, to_play
),
183 neighbor_count_at(b
, lib
, S_OFFBOARD
));
184 preference
[i
] = false;
188 fprintf(stderr
, "+ liberty %s ready with preference %d\n", coord2sstr(lib
, b
), preference
[i
]);
190 /* If we prefer only one of the moves, pick that one. */
191 if (i
== 1 && have
[0] && preference
[0] != preference
[1]) {
192 if (!preference
[0]) {
193 if (q
->mq
.move
[q
->mq
.moves
- 1] == board_group_info(b
, group
).lib
[0])
195 /* ...else{ may happen, since we call
196 * mq_nodup() and the move might have
197 * been there earlier. */
199 assert(!preference
[1]);
204 /* Tasty! Crispy! Good! */
205 struct move m
= { .coord
= lib
, .color
= to_play
};
206 if (libmap_config
.counterattack
& LMC_DEFENSE
) {
207 libmap_mq_add(q
, m
, tag
, lmg
);
210 if (libmap_config
.counterattack
& LMC_ATTACK
&& ca_hash
) {
211 struct libmap_group lmgx
= lmg
; lmgx
.hash
= ca_hash
;
212 libmap_mq_add(q
, m
, tag
, lmgx
);
215 if (libmap_config
.counterattack
& LMC_DEFENSE_ATTACK
&& ca_hash
) {
216 struct libmap_group lmgx
= lmg
; lmgx
.hash
^= ca_hash
;
217 libmap_mq_add(q
, m
, tag
, lmgx
);
224 snprintf(label
, 256, "= final %s %s liberties to play by %s",
225 stone2str(owner
), coord2sstr(group
, b
),
227 libmap_mq_print(q
, b
, label
);
232 group_2lib_check(struct board
*b
, group_t group
, enum stone to_play
, struct libmap_mq
*q
, int tag
, bool use_miaisafe
, bool use_def_no_hopeless
)
234 enum stone color
= board_at(b
, group_base(group
));
235 assert(color
!= S_OFFBOARD
&& color
!= S_NONE
);
238 fprintf(stderr
, "[%s] 2lib check of color %d\n",
239 coord2sstr(group
, b
), color
);
241 /* Do not try to atari groups that cannot be harmed. */
242 if (use_miaisafe
&& miai_2lib(b
, group
, color
))
245 hash_t libhash
= group_to_libmap(b
, group
);
246 struct libmap_group lmg
= { .group
= group
, .hash
= libhash
, .goal
= to_play
};
247 can_atari_group(b
, group
, color
, to_play
, q
, tag
, lmg
, 0, use_def_no_hopeless
);
249 /* Can we counter-atari another group, if we are the defender? */
250 if (to_play
!= color
)
252 foreach_in_group(b
, group
) {
253 foreach_neighbor(b
, c
, {
254 if (board_at(b
, c
) != stone_other(color
))
256 group_t g2
= group_at(b
, c
);
257 if (board_group_info(b
, g2
).libs
== 1) {
258 /* We can capture a neighbor. */
259 struct move m
; m
.coord
= board_group_info(b
, g2
).lib
[0]; m
.color
= to_play
;
260 struct libmap_group lmg
; lmg
.group
= group
; lmg
.hash
= libhash
; lmg
.goal
= to_play
;
261 libmap_mq_add(q
, m
, tag
, lmg
);
265 if (board_group_info(b
, g2
).libs
!= 2)
267 /* libhash: Liberty info for both original and
268 * counter-atari group. */
269 can_atari_group(b
, g2
, stone_other(color
), to_play
, q
, tag
, lmg
, group_to_libmap(b
, g2
), use_def_no_hopeless
);
271 } foreach_in_group_end
;