6 #include "playout/old.h"
11 /* The following arguments tune domain-specific heuristics.
12 * capture_rate=MC_CAPTURERATE how many of 100 moves should be non-random but
13 * fix local atari, if there is any
14 * atari_rate=MC_ATARIRATE how many of 100 moves should be non-random but
15 * make an atari, if there is any
16 * local_rate=MC_LOCALRATE how many of 100 moves should be contact plays
18 * cut_rate=MC_CUTRATE how many of 100 moves should fix local cuts,
21 #define MC_CAPTURERATE 50
22 #define MC_ATARIRATE 50
24 #define MC_LOCALRATE 30
27 /* *** Domain-specific knowledge comes here (that is, any heuristics that perfer
28 * certain moves, aside of requiring the moves to be according to the rules). */
29 /* NOTE: This heuristics affects ONLY the random playouts! It does not help the
30 * engine directly to pick a move, but it makes it pick the hinted moves in the
31 * random playouts FROM the random initial move. So the engine will not prefer
32 * to fix atari on the current board, but it will fix it as the other player
33 * when the next move on current board failed to deal with it. */
37 int capture_rate
, atari_rate
, cut_rate
, local_rate
;
43 #define PLDEBUGL(n) DEBUGL_(p->debug_level, n)
47 domain_hint_capture(struct playout_policy
*p
, struct board
*b
, coord_t coord
)
49 /* If we or our neighbors are in atari, fix that, (Capture or escape.)
50 * This test costs a lot of performance (the whole playout is about 1/4
51 * slower), but improves the playouts a lot. */
54 fprintf(stderr
, "-- Scanning for %d,%d-capture moves:\n", coord_x(coord
, b
), coord_y(coord
, b
));
55 board_print(b
, stderr
);
58 coord_t captures
[5]; int captures_len
= 0, capture_choice
= 0;
59 memset(captures
, 0, sizeof(captures
));
62 if (unlikely(board_group_in_atari(b
, group_at(b
, coord
), &fix
)) && likely(!is_selfatari(b
, board_at(b
, coord
), fix
))) {
63 /* We can capture the opponent! Don't even think about escaping
64 * our own ataris then. */
65 captures
[captures_len
] = fix
;
66 capture_choice
= captures_len
++;
69 foreach_neighbor(b
, coord
, {
70 /* This can produce duplicate candidates. But we should prefer
71 * bigger groups to smaller ones, so I guess that is kinda ok. */
72 if (likely(group_at(b
, c
)) && unlikely(board_group_in_atari(b
, group_at(b
, c
), &fix
)) && likely(!is_selfatari(b
, board_at(b
, c
), fix
)))
73 captures
[captures_len
++] = fix
;
76 if (unlikely(captures_len
)) {
77 capture_choice
= fast_random(captures_len
);
80 fprintf(stderr
, "capture moves found:");
82 for (i
= 0; i
< captures_len
; i
++)
83 fprintf(stderr
, " %c%d,%d",
84 capture_choice
== i
? '*' : ' ',
85 coord_x(captures
[i
], b
), coord_y(captures
[i
], b
));
86 fprintf(stderr
, "\n");
88 return captures
[capture_choice
];
95 valid_atari_move(struct board
*b
, coord_t coord
)
97 /* Do not avoid atari moves that the opponent actuall can never
98 * play because they are one of our eyes. Without this, the
99 * atari avoidance will actually fill one eye of surrounded
101 return board_get_one_point_eye(b
, &coord
) == S_NONE
;
105 validate_atari_pair(struct board
*b
, coord_t coord
[2])
107 /* DTRT if only one of the atari moves is sensible. */
108 bool v
[2] = { valid_atari_move(b
, coord
[0]), valid_atari_move(b
, coord
[1]) };
109 if (likely(v
[0] && v
[1]))
123 domain_hint_atari(struct playout_policy
*p
, struct board
*b
, coord_t coord
)
125 /* If we can put the last-move group in atari, do that. */
128 fprintf(stderr
, "-- Scanning for %d,%d-atari moves:\n", coord_x(coord
, b
), coord_y(coord
, b
));
129 board_print(b
, stderr
);
132 coord_t ataris
[5][2]; int ataris_len
= 0, atari_choice
= 0;
133 memset(ataris
, 0, sizeof(ataris
));
135 if (unlikely(board_group_can_atari(b
, group_at(b
, coord
), ataris
[ataris_len
]))) {
136 if (likely(validate_atari_pair(b
, ataris
[ataris_len
]))) {
137 /* Atari-ing opponent is always better than preventing
138 * opponent atari-ing us. */
139 atari_choice
= ataris_len
++;
143 foreach_neighbor(b
, coord
, {
144 /* This can produce duplicate candidates. But we should prefer
145 * bigger groups to smaller ones, so I guess that is kinda ok. */
146 if (likely(group_at(b
, c
)) && unlikely(board_group_can_atari(b
, group_at(b
, c
), ataris
[ataris_len
])))
147 if (likely(validate_atari_pair(b
, ataris
[ataris_len
])))
151 if (unlikely(ataris_len
)) {
152 atari_choice
= fast_random(ataris_len
);
155 fprintf(stderr
, "atari moves found:");
157 for (i
= 0; i
< ataris_len
; i
++)
158 fprintf(stderr
, " %c%d,%d;%d,%d",
159 atari_choice
== i
? '*' : ' ',
160 coord_x(ataris
[i
][0], b
), coord_y(ataris
[i
][0], b
),
161 coord_x(ataris
[i
][1], b
), coord_y(ataris
[i
][1], b
));
162 fprintf(stderr
, "\n");
164 return ataris
[atari_choice
][fast_random(2)];
170 domain_hint_cut(struct playout_policy
*p
, struct board
*b
, coord_t coord
)
172 /* Check if this move is cutting kosumi:
177 fprintf(stderr
, "-- Scanning for %d,%d-cut moves:\n", coord_x(coord
, b
), coord_y(coord
, b
));
178 board_print(b
, stderr
);
181 coord_t cuts
[4]; int cuts_len
= 0;
182 memset(cuts
, 0, sizeof(cuts
));
184 enum stone cutting_color
= stone_other(board_at(b
, coord
));
185 foreach_diag_neighbor(b
, coord
) {
186 if (board_at(b
, c
) == S_NONE
) {
187 if (neighbor_count_at(b
, c
, cutting_color
) != 2) {
188 /* Either this isn't a cut or the opponent has
189 * too many friends there. */
193 /* XXX: Some internal board specific magic here... */
194 coord_t cutted
= coord
;
195 if (coord_x(c
, b
) < coord_x(coord
, b
))
199 if (likely(board_at(b
, cutted
) != cutting_color
))
201 coord_raw(cutted
) = coord_raw(c
);
202 if (coord_y(c
, b
) < coord_y(coord
, b
))
203 coord_raw(cutted
) -= board_size(b
);
205 coord_raw(cutted
) += board_size(b
);
206 if (likely(board_at(b
, cutted
) != cutting_color
))
209 cuts
[cuts_len
++] = c
;
211 } foreach_diag_neighbor_end
;
213 if (unlikely(cuts_len
)) {
215 fprintf(stderr
, "Cutting moves found:");
217 for (i
= 0; i
< cuts_len
; i
++)
218 fprintf(stderr
, " %d,%d", coord_x(cuts
[i
], b
), coord_y(cuts
[i
], b
));
219 fprintf(stderr
, "\n");
221 return cuts
[fast_random(cuts_len
)];
227 domain_hint_local(struct playout_policy
*p
, struct board
*b
, coord_t coord
)
229 /* Pick a suitable move that is directly or diagonally adjecent. In the
230 * real game, local moves often tend to be the urgent ones, even if they
234 fprintf(stderr
, "-- Scanning for %d,%d-local moves:\n", coord_x(coord
, b
), coord_y(coord
, b
));
235 board_print(b
, stderr
);
238 coord_t neis
[S_MAX
][8]; int neis_len
[S_MAX
];
239 memset(neis
, 0, sizeof(neis
));
240 memset(neis_len
, 0, sizeof(neis_len
));
242 foreach_neighbor(b
, coord
, {
243 neis
[(enum stone
) board_at(b
, c
)][neis_len
[(enum stone
) board_at(b
, c
)]++] = c
;
246 foreach_diag_neighbor(b
, coord
) {
247 neis
[(enum stone
) board_at(b
, c
)][neis_len
[(enum stone
) board_at(b
, c
)]++] = c
;
248 } foreach_diag_neighbor_end
;
250 if (likely(neis_len
[S_NONE
])) {
252 fprintf(stderr
, "Local moves found:");
254 for (i
= 0; i
< neis_len
[S_NONE
]; i
++)
255 fprintf(stderr
, " %d,%d", coord_x(neis
[S_NONE
][i
], b
), coord_y(neis
[S_NONE
][i
], b
));
256 fprintf(stderr
, "\n");
258 return neis
[S_NONE
][fast_random(neis_len
[S_NONE
])];
264 playout_old_choose(struct playout_policy
*p
, struct board
*b
, enum stone our_real_color
)
266 struct old_policy
*pp
= p
->data
;
268 if (is_pass(b
->last_move
.coord
))
271 /* Now now, if we ignored an urgent move, the opponent will
273 /* Note that we should use this only when the _REAL_ us tenukies
274 * and the _REAL_ opponent comes back. Otherwise we hope in
275 * opponent's tenuki too much and play out ladders. :-) */
276 if (!is_pass(pp
->last_hint
)
277 && unlikely(!coord_eq(b
->last_move
.coord
, pp
->last_hint
))
278 && b
->last_move
.color
== our_real_color
279 && fast_random(100) < pp
->last_hint_value
) {
280 coord_t urgent
= pp
->last_hint
;
281 pp
->last_hint
= pass
;
285 /* In some of the cases, we pick atari response instead of random move.
286 * If there is an atari, capturing tends to be huge. */
287 if (pp
->capture_rate
&& fast_random(100) < pp
->capture_rate
) {
288 pp
->last_hint
= domain_hint_capture(p
, b
, b
->last_move
.coord
);
289 if (!is_pass(pp
->last_hint
)) {
290 pp
->last_hint_value
= pp
->capture_rate
;
291 return pp
->last_hint
;
295 /* Maybe we can _put_ some stones into atari. That's cool. */
296 if (pp
->capture_rate
&& fast_random(100) < pp
->atari_rate
) {
297 pp
->last_hint
= domain_hint_atari(p
, b
, b
->last_move
.coord
);
298 if (!is_pass(pp
->last_hint
)) {
299 pp
->last_hint_value
= pp
->capture_rate
;
300 return pp
->last_hint
;
304 /* Cutting is kinda urgent, too. */
305 if (pp
->cut_rate
&& fast_random(100) < pp
->cut_rate
) {
306 pp
->last_hint
= domain_hint_cut(p
, b
, b
->last_move
.coord
);
307 if (!is_pass(pp
->last_hint
)) {
308 pp
->last_hint_value
= pp
->cut_rate
;
309 return pp
->last_hint
;
313 /* For the non-urgent moves, some of them will be contact play (tsuke
314 * or diagonal). These tend to be likely urgent. */
315 if (pp
->local_rate
&& fast_random(100) < pp
->local_rate
) {
316 pp
->last_hint
= domain_hint_local(p
, b
, b
->last_move
.coord
);
317 if (!is_pass(pp
->last_hint
)) {
318 pp
->last_hint_value
= pp
->local_rate
;
319 return pp
->last_hint
;
323 return ((pp
->last_hint
= pass
));
327 struct playout_policy
*
328 playout_old_init(char *arg
)
330 struct playout_policy
*p
= calloc(1, sizeof(*p
));
331 struct old_policy
*pp
= calloc(1, sizeof(*pp
));
333 p
->choose
= playout_old_choose
;
335 pp
->last_hint
= pass
;
337 pp
->capture_rate
= MC_CAPTURERATE
;
338 pp
->atari_rate
= MC_ATARIRATE
;
339 pp
->local_rate
= MC_LOCALRATE
;
340 pp
->cut_rate
= MC_CUTRATE
;
343 char *optspec
, *next
= arg
;
346 next
+= strcspn(next
, ":");
347 if (*next
) { *next
++ = 0; } else { *next
= 0; }
349 char *optname
= optspec
;
350 char *optval
= strchr(optspec
, '=');
351 if (optval
) *optval
++ = 0;
353 if (!strcasecmp(optname
, "capturerate") && optval
) {
354 pp
->capture_rate
= atoi(optval
);
355 } else if (!strcasecmp(optname
, "atarirate") && optval
) {
356 pp
->atari_rate
= atoi(optval
);
357 } else if (!strcasecmp(optname
, "localrate") && optval
) {
358 pp
->local_rate
= atoi(optval
);
359 } else if (!strcasecmp(optname
, "cutrate") && optval
) {
360 pp
->cut_rate
= atoi(optval
);
362 fprintf(stderr
, "playout-old: Invalid policy argument %s or missing value\n", optname
);