UCT: Track criticality statistics
[pachi/json.git] / tactics / 2lib.c
blobef1fb7302b18dfe1d756007f11c252d895c84367
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 "mq.h"
9 #include "tactics/2lib.h"
10 #include "tactics/selfatari.h"
13 /* Whether to avoid capturing/atariing doomed groups (this is big
14 * performance hit and may reduce playouts balance; it does increase
15 * the strength, but not quite proportionally to the performance). */
16 //#define NO_DOOMED_GROUPS
19 static bool
20 miai_2lib(struct board *b, group_t group, enum stone color)
22 bool can_connect = false, can_pull_out = false;
23 /* We have miai if we can either connect on both libs,
24 * or connect on one lib and escape on another. (Just
25 * having two escape routes can be risky.) We must make
26 * sure that we don't consider following as miai:
27 * X X X O
28 * X . . O
29 * O O X O - left dot would be pull-out, right dot connect */
30 foreach_neighbor(b, board_group_info(b, group).lib[0], {
31 enum stone cc = board_at(b, c);
32 if (cc == S_NONE && cc != board_at(b, board_group_info(b, group).lib[1])) {
33 can_pull_out = true;
34 } else if (cc != color) {
35 continue;
38 group_t cg = group_at(b, c);
39 if (cg && cg != group && board_group_info(b, cg).libs > 1)
40 can_connect = true;
41 });
42 foreach_neighbor(b, board_group_info(b, group).lib[1], {
43 enum stone cc = board_at(b, c);
44 if (c == board_group_info(b, group).lib[0])
45 continue;
46 if (cc == S_NONE && can_connect) {
47 return true;
48 } else if (cc != color) {
49 continue;
52 group_t cg = group_at(b, c);
53 if (cg && cg != group && board_group_info(b, cg).libs > 1)
54 return (can_connect || can_pull_out);
55 });
56 return false;
59 void
60 can_atari_group(struct board *b, group_t group, enum stone owner,
61 enum stone to_play, struct move_queue *q,
62 int tag, bool use_def_no_hopeless)
64 bool have[2] = { false, false };
65 bool preference[2] = { true, true };
66 for (int i = 0; i < 2; i++) {
67 coord_t lib = board_group_info(b, group).lib[i];
68 assert(board_at(b, lib) == S_NONE);
69 if (!board_is_valid_play(b, to_play, lib))
70 continue;
72 if (DEBUGL(6))
73 fprintf(stderr, "checking liberty %s of %s %s, filled by %s\n",
74 coord2sstr(lib, b),
75 stone2str(owner), coord2sstr(group, b),
76 stone2str(to_play));
78 /* Don't play at the spot if it is extremely short
79 * of liberties... */
80 /* XXX: This looks harmful, could significantly
81 * prefer atari to throwin:
83 * XXXOOOOOXX
84 * .OO.....OX
85 * XXXOOOOOOX */
86 #if 0
87 if (neighbor_count_at(b, lib, stone_other(owner)) + immediate_liberty_count(b, lib) < 2)
88 continue;
89 #endif
91 /* If we are the defender not connecting out, do not
92 * escape with moves that do not gain liberties anyway
93 * - either the new extension has just single extra
94 * liberty, or the "gained" liberties are shared. */
95 /* XXX: We do not check connecting to a short-on-liberty
96 * group (e.g. ourselves). */
97 if (use_def_no_hopeless && to_play == owner && neighbor_count_at(b, lib, owner) == 1) {
98 if (immediate_liberty_count(b, lib) == 1)
99 continue;
100 if (immediate_liberty_count(b, lib) == 2
101 && coord_is_adjecent(lib, board_group_info(b, group).lib[1 - i], b))
102 continue;
105 #ifdef NO_DOOMED_GROUPS
106 /* If the owner can't play at the spot, we don't want
107 * to bother either. */
108 if (is_bad_selfatari(b, owner, lib))
109 continue;
110 #endif
112 /* Of course we don't want to play bad selfatari
113 * ourselves, if we are the attacker... */
114 if (
115 #ifdef NO_DOOMED_GROUPS
116 to_play != owner &&
117 #endif
118 is_bad_selfatari(b, to_play, lib)) {
119 /* Okay! But maybe we just need to connect a false
120 * eye before atari - this is very common in the
121 * corner. */
122 coord_t coord = selfatari_cousin(b, to_play, lib);
123 if (is_pass(coord))
124 continue;
125 /* Ok, connect, but prefer not to. */
126 lib = coord;
127 preference[i] = false;
129 /* By now, we must be decided we add the move to the
130 * queue! [comment intentionally misindented] */
134 have[i] = true;
136 /* If the move is too "lumpy", prefer the alternative:
138 * #######
139 * ..O.X.X <- always play the left one!
140 * OXXXXXX */
141 if (neighbor_count_at(b, lib, to_play) + neighbor_count_at(b, lib, S_OFFBOARD) >= 3) {
142 /* However, the counter-example is connecting to
143 * a different group [along the edge]. */
144 bool connection = false;
145 foreach_neighbor(b, lib, {
146 if (board_at(b, c) == to_play
147 && group_at(b, c) != group)
148 connection = true;
150 if (!connection)
151 preference[i] = false;
154 if (DEBUGL(6))
155 fprintf(stderr, "liberty %s ready with preference %d\n", coord2sstr(lib, b), preference[i]);
157 /* If we prefer only one of the moves, pick that one. */
158 if (i == 1 && have[0] && preference[0] != preference[1]) {
159 if (!preference[0]) {
160 if (q->move[q->moves - 1] == board_group_info(b, group).lib[0])
161 q->moves--;
162 /* ...else{ may happen, since we call
163 * mq_nodup() and the move might have
164 * been there earlier. */
165 } else {
166 assert(!preference[1]);
167 continue;
171 /* Tasty! Crispy! Good! */
172 mq_add(q, lib, tag);
173 mq_nodup(q);
177 void
178 group_2lib_check(struct board *b, group_t group, enum stone to_play, struct move_queue *q, int tag, bool use_miaisafe, bool use_def_no_hopeless)
180 enum stone color = board_at(b, group_base(group));
181 assert(color != S_OFFBOARD && color != S_NONE);
183 if (DEBUGL(5))
184 fprintf(stderr, "[%s] 2lib check of color %d\n",
185 coord2sstr(group, b), color);
187 /* Do not try to atari groups that cannot be harmed. */
188 if (use_miaisafe && miai_2lib(b, group, color))
189 return;
191 can_atari_group(b, group, color, to_play, q, tag, use_def_no_hopeless);
193 /* Can we counter-atari another group, if we are the defender? */
194 if (to_play != color)
195 return;
196 foreach_in_group(b, group) {
197 foreach_neighbor(b, c, {
198 if (board_at(b, c) != stone_other(color))
199 continue;
200 group_t g2 = group_at(b, c);
201 if (board_group_info(b, g2).libs == 1) {
202 /* We can capture a neighbor. */
203 mq_add(q, board_group_info(b, g2).lib[0], tag);
204 mq_nodup(q);
205 continue;
207 if (board_group_info(b, g2).libs != 2)
208 continue;
209 can_atari_group(b, g2, stone_other(color), to_play, q, tag, use_def_no_hopeless);
211 } foreach_in_group_end;