10 #include "playout/moggy.h"
13 #define PLDEBUGL(n) DEBUGL_(p->debug_level, n)
17 bool ladders
, localassess
, ladderassess
;
18 int lcapturerate
, capturerate
, patternrate
;
19 /* These are relative to patternrate. */
20 int hanerate
, cut1rate
, cut2rate
;
31 cut1_test_cut(struct playout_policy
*p
, struct board
*b
, coord_t base
, coord_t cut
)
34 * O(.)? X is base, (.) is cut
36 int x
= coord_x(base
, b
), y
= coord_y(base
, b
);
37 enum stone color
= board_at(b
, base
);
38 int xc
= coord_x(cut
, b
), yc
= coord_y(cut
, b
);
41 fprintf(stderr
, "Probing CUT1 %s -> %s\n", coord2sstr(base
, b
), coord2sstr(cut
, b
));
43 /* Kosumi available. Is it cutting? */
44 if (board_atxy(b
, x
, yc
) != stone_other(color
)
45 || board_atxy(b
, xc
, y
) != stone_other(color
))
48 /* It is! Is the cut protected? */
49 enum stone fix1
= board_atxy(b
, xc
, yc
- (y
- yc
));
50 enum stone fix2
= board_atxy(b
, xc
- (x
- xc
), yc
);
52 fprintf(stderr
, "Protection check: %d,%d\n", fix1
, fix2
);
53 if ((fix1
== stone_other(color
) || fix1
== S_OFFBOARD
) && fix2
!= color
)
55 if ((fix2
== stone_other(color
) || fix2
== S_OFFBOARD
) && fix1
!= color
)
58 /* Unprotected cut. Feast! */
60 fprintf(stderr
, "Passed.\n");
65 cut1_test(struct playout_policy
*p
, struct board
*b
, struct move
*m
, struct move_queue
*q
)
67 coord_t coord
= m
->coord
;
68 int x
= coord_x(coord
, b
), y
= coord_y(coord
, b
);
69 enum stone color
= board_at(b
, coord
);
71 /* Either last move was cutting threat... */
72 foreach_diag_neighbor(b
, coord
) {
73 if (board_at(b
, c
) != S_NONE
)
76 * O(.)? X is coord, (.) is c
78 if (cut1_test_cut(p
, b
, coord
, c
))
79 q
->move
[q
->moves
++] = c
;
80 } foreach_diag_neighbor_end
;
82 /* ...or a cuttable hane. */
83 #define cutbase_test(xb_, yb_) \
84 base = coord_xy_otf(xb_, yb_, b); \
85 if (board_at(b, base) == stone_other(color)) \
86 if (cut1_test_cut(p, b, base, c)) \
87 q->move[q->moves++] = c;
88 foreach_neighbor(b
, coord
, {
89 if (board_at(b
, c
) != S_NONE
)
92 * O(.)? O is coord, (.) is c
94 /* Check for X on both sides of O. */
95 int xc
= coord_x(c
, b
);
96 int yc
= coord_y(c
, b
);
98 /* Either x == xc or y == yc. */
99 cutbase_test(x
- (yc
- y
), y
- (xc
- x
));
100 cutbase_test(x
+ (yc
- y
), y
+ (xc
- x
));
106 cut2_test_cut(struct playout_policy
*p
, struct board
*b
, coord_t base
, coord_t cut
)
109 * O(.)O X is base, (.) is cut
111 int x
= coord_x(base
, b
), y
= coord_y(base
, b
);
112 enum stone color
= board_at(b
, base
);
113 int xc
= coord_x(cut
, b
), yc
= coord_y(cut
, b
);
116 fprintf(stderr
, "Probing CUT2 %s -> %s\n", coord2sstr(base
, b
), coord2sstr(cut
, b
));
118 /* Nobi available. Is it cutting? */
119 if (board_atxy(b
, xc
- (yc
- y
), yc
- (xc
- x
)) != stone_other(color
)
120 || board_atxy(b
, xc
+ (yc
- y
), yc
+ (xc
- x
)) != stone_other(color
))
123 /* It is! Is the cut protected? */
124 enum stone ocolor
= stone_other(color
);
127 fprintf(stderr
, "Protection check - row [%d,%d].\n", xc
, yc
+ (yc
- y
));
128 if (board_atxy(b
, xc
- 1, yc
+ (yc
- y
)) == ocolor
129 || board_atxy(b
, xc
, yc
+ (yc
- y
)) == ocolor
130 || board_atxy(b
, xc
+ 1, yc
+ (yc
- y
)) == ocolor
)
134 fprintf(stderr
, "Protection check - column [%d,%d].\n", xc
+ (xc
- x
), yc
);
135 if (board_atxy(b
, xc
+ (xc
- x
), yc
- 1) == ocolor
136 || board_atxy(b
, xc
+ (xc
- x
), yc
) == ocolor
137 || board_atxy(b
, xc
+ (xc
- x
), yc
+ 1) == ocolor
)
141 /* Unprotected cut. Feast! */
143 fprintf(stderr
, "Passed.\n");
148 cut2_test(struct playout_policy
*p
, struct board
*b
, struct move
*m
, struct move_queue
*q
)
150 coord_t coord
= m
->coord
;
151 int x
= coord_x(coord
, b
), y
= coord_y(coord
, b
);
152 enum stone color
= board_at(b
, coord
);
154 /* Either last move was cutting threat... */
155 foreach_neighbor(b
, coord
, {
156 if (board_at(b
, c
) != S_NONE
)
159 * O(.)O X is coord, (.) is c
161 if (cut2_test_cut(p
, b
, coord
, c
))
162 q
->move
[q
->moves
++] = c
;
165 /* ...or a cuttable ikken tobi. */
166 #define cutbase_test(xb_, yb_) \
167 base = coord_xy_otf(xb_, yb_, b); \
168 if (board_at(b, base) == stone_other(color)) \
169 if (cut2_test_cut(p, b, base, c)) \
170 q->move[q->moves++] = c;
171 foreach_neighbor(b
, coord
, {
172 if (board_at(b
, c
) != S_NONE
)
175 * O(.)O O is coord, (.) is c
177 /* Check for X on both sides of (.). */
178 int xc
= coord_x(c
, b
);
179 int yc
= coord_y(c
, b
);
181 /* Either x == xc or y == yc. */
182 cutbase_test(xc
- (yc
- y
), yc
- (xc
- x
));
183 cutbase_test(xc
+ (yc
- y
), yc
+ (xc
- x
));
188 /* Check if we match a certain pattern centered on given move. */
190 apply_pattern(struct playout_policy
*p
, struct board
*b
, struct move
*m
, struct move
*testmove
)
192 struct moggy_policy
*pp
= p
->data
;
196 /* Suicides do not make any patterns and confuse us. */
197 if (board_at(b
, m
->coord
) == S_NONE
|| board_at(b
, m
->coord
) == S_OFFBOARD
)
200 if (pp
->hanerate
> fast_random(100)) {
204 if (pp
->cut1rate
> fast_random(100)) {
205 cut1_test(p
, b
, m
, &q
);
208 if (pp
->cut2rate
> fast_random(100)) {
209 cut2_test(p
, b
, m
, &q
);
213 fprintf(stderr
, "Pattern candidate moves: ");
214 for (int i
= 0; i
< q
.moves
; i
++) {
215 fprintf(stderr
, "%s ", coord2sstr(q
.move
[i
], b
));
217 fprintf(stderr
, "\n");
222 if (q
.move
[q
.moves
] == testmove
->coord
) {
224 fprintf(stderr
, "Found queried move.\n");
225 return testmove
->coord
;
230 int i
= fast_random(q
.moves
);
231 return q
.moves
? q
.move
[i
] : pass
;
236 /* Is this ladder breaker friendly for the one who catches ladder. */
238 ladder_catcher(struct board
*b
, int x
, int y
, enum stone laddered
)
240 enum stone breaker
= board_atxy(b
, x
, y
);
241 return breaker
== stone_other(laddered
) || breaker
== S_OFFBOARD
;
245 ladder_catches(struct playout_policy
*p
, struct board
*b
, coord_t coord
, group_t laddered
)
247 /* This is very trivial and gets a lot of corner cases wrong.
248 * We need this to be just very fast. */
249 //fprintf(stderr, "ladder check\n");
251 enum stone lcolor
= board_at(b
, laddered
);
253 /* Figure out the ladder direction */
254 int x
= coord_x(coord
, b
), y
= coord_y(coord
, b
);
256 xd
= board_atxy(b
, x
+ 1, y
) == S_NONE
? 1 : board_atxy(b
, x
- 1, y
) == S_NONE
? -1 : 0;
257 yd
= board_atxy(b
, x
, y
+ 1) == S_NONE
? 1 : board_atxy(b
, x
, y
- 1) == S_NONE
? -1 : 0;
259 /* We do only tight ladders, not loose ladders. Furthermore,
260 * the ladders need to be simple:
262 * c O X supported . c O unsupported
266 /* For given (xd,yd), we have two possibilities where to move
267 * next. Consider (-1,1):
272 if (!xd
|| !yd
|| !(ladder_catcher(b
, x
- xd
, y
, lcolor
) ^ ladder_catcher(b
, x
, y
- yd
, lcolor
))) {
273 /* Silly situation, probably non-simple ladder or suicide. */
274 /* TODO: In case of basic non-simple ladder, play out both variants. */
276 fprintf(stderr
, "non-simple ladder\n");
280 #define ladder_check(xd1_, yd1_, xd2_, yd2_) \
281 if (board_atxy(b, x, y) != S_NONE) { \
282 /* Did we hit a stone when playing out ladder? */ \
283 if (ladder_catcher(b, x, y, lcolor)) \
284 return true; /* ladder works */ \
285 if (board_group_info(b, group_atxy(b, x, y)).lib[0] > 0) \
286 return false; /* friend that's not in atari himself */ \
288 /* No. So we are at new position. \
289 * We need to check indirect ladder breakers. */ \
291 * . x o O 1 <- only at O we can check for o at 2 \
292 * x o o x . otherwise x at O would be still deadly \
294 * We check for o and x at 1, these are vital. \
295 * We check only for o at 2; x at 2 would mean we \
296 * need to fork (one step earlier). */ \
297 enum stone s1 = board_atxy(b, x + (xd1_), y + (yd1_)); \
298 if (s1 == lcolor) return false; \
299 if (s1 == stone_other(lcolor)) return true; \
300 enum stone s2 = board_atxy(b, x + (xd2_), y + (yd2_)); \
301 if (s2 == lcolor) return false; \
303 #define ladder_horiz do { if (PLDEBUGL(6)) fprintf(stderr, "%d,%d horiz step %d\n", x, y, xd); x += xd; ladder_check(xd, 0, -2 * xd, yd); } while (0)
304 #define ladder_vert do { if (PLDEBUGL(6)) fprintf(stderr, "%d,%d vert step %d\n", x, y, yd); y += yd; ladder_check(0, yd, xd, -2 * yd); } while (0)
306 if (ladder_catcher(b
, x
- xd
, y
, lcolor
))
316 group_atari_check(struct playout_policy
*p
, struct board
*b
, group_t group
, bool *ladder
)
318 struct moggy_policy
*pp
= p
->data
;
319 enum stone color
= board_at(b
, group
);
320 coord_t lib
= board_group_info(b
, group
).lib
[0];
322 if (board_at(b
, group
) == S_OFFBOARD
) {
327 fprintf(stderr
, "atariiiiiiiii %s of color %d\n", coord2sstr(lib
, b
), color
);
328 assert(board_at(b
, lib
) == S_NONE
);
330 /* Do not suicide... */
331 if (!valid_escape_route(b
, color
, lib
))
334 fprintf(stderr
, "...escape route valid\n");
336 /* ...or play out ladders. */
337 if (pp
->ladders
&& ladder_catches(p
, b
, lib
, group
)) {
343 fprintf(stderr
, "...no ladder\n");
349 global_atari_check(struct playout_policy
*p
, struct board
*b
)
354 int g_base
= fast_random(b
->clen
);
355 for (int g
= g_base
; g
< b
->clen
; g
++) {
356 coord_t c
= group_atari_check(p
, b
, b
->c
[g
], NULL
);
360 for (int g
= 0; g
< g_base
; g
++) {
361 coord_t c
= group_atari_check(p
, b
, b
->c
[g
], NULL
);
369 local_atari_check(struct playout_policy
*p
, struct board
*b
, struct move
*m
, struct move
*testmove
, bool *ladder
)
374 memset(ladders
, 0, sizeof(ladders
));
376 /* Did the opponent play a self-atari? */
377 if (board_group_info(b
, group_at(b
, m
->coord
)).libs
== 1) {
378 coord_t l
= group_atari_check(p
, b
, group_at(b
, m
->coord
), &ladders
[q
.moves
]);
379 if (!is_pass(l
) || (ladder
&& ladders
[q
.moves
]))
380 q
.move
[q
.moves
++] = l
;
383 foreach_neighbor(b
, m
->coord
, {
384 if (board_group_info(b
, group_at(b
, c
)).libs
!= 1)
386 coord_t l
= group_atari_check(p
, b
, group_at(b
, c
), &ladders
[q
.moves
]);
387 if (!is_pass(l
) || (ladder
&& ladders
[q
.moves
]))
388 q
.move
[q
.moves
++] = l
;
392 fprintf(stderr
, "Local atari candidate moves: ");
393 for (int i
= 0; i
< q
.moves
; i
++) {
394 fprintf(stderr
, "%s ", coord2sstr(q
.move
[i
], b
));
396 fprintf(stderr
, "\n");
401 if (q
.move
[q
.moves
] == testmove
->coord
) {
403 fprintf(stderr
, "Found queried move.\n");
405 *ladder
= ladders
[q
.moves
];
406 return testmove
->coord
;
411 int i
= fast_random(q
.moves
);
412 return q
.moves
? q
.move
[i
] : pass
;
416 playout_moggy_choose(struct playout_policy
*p
, struct board
*b
, enum stone our_real_color
)
418 struct moggy_policy
*pp
= p
->data
;
422 board_print(b
, stderr
);
425 if (!is_pass(b
->last_move
.coord
)) {
426 /* Local group in atari? */
427 if (pp
->lcapturerate
> fast_random(100)) {
428 c
= local_atari_check(p
, b
, &b
->last_move
, NULL
, NULL
);
433 /* Check for patterns we know */
434 if (pp
->patternrate
> fast_random(100)) {
435 c
= apply_pattern(p
, b
, &b
->last_move
, NULL
);
443 /* Any groups in atari? */
444 if (pp
->capturerate
> fast_random(100)) {
445 c
= global_atari_check(p
, b
);
454 playout_moggy_assess(struct playout_policy
*p
, struct board
*b
, struct move
*m
)
456 struct moggy_policy
*pp
= p
->data
;
458 if (is_pass(m
->coord
))
462 board_print(b
, stderr
);
464 /* Are we dealing with atari? */
465 if (pp
->lcapturerate
> fast_random(100)) {
466 if (pp
->localassess
) {
468 if (local_atari_check(p
, b
, &b
->last_move
, m
, pp
->ladderassess
? &ladder
: NULL
) == m
->coord
)
469 return ladder
? 0.0 : 1.0;
471 foreach_neighbor(b
, m
->coord
, {
474 m2
.coord
= c
; m2
.color
= stone_other(m
->color
);
475 if (local_atari_check(p
, b
, &m2
, m
, pp
->ladderassess
? &ladder
: NULL
) == m
->coord
)
476 return ladder
? 0.0 : 1.0;
482 if (pp
->patternrate
> fast_random(100)) {
483 if (pp
->localassess
) {
484 if (apply_pattern(p
, b
, &b
->last_move
, m
) == m
->coord
)
487 foreach_neighbor(b
, m
->coord
, {
489 m2
.coord
= c
; m2
.color
= stone_other(m
->color
);
490 if (apply_pattern(p
, b
, &m2
, m
) == m
->coord
)
493 foreach_diag_neighbor(b
, m
->coord
) {
495 m2
.coord
= c
; m2
.color
= stone_other(m
->color
);
496 if (apply_pattern(p
, b
, &m2
, m
) == m
->coord
)
498 } foreach_diag_neighbor_end
;
506 struct playout_policy
*
507 playout_moggy_init(char *arg
)
509 struct playout_policy
*p
= calloc(1, sizeof(*p
));
510 struct moggy_policy
*pp
= calloc(1, sizeof(*pp
));
512 p
->choose
= playout_moggy_choose
;
513 p
->assess
= playout_moggy_assess
;
515 pp
->lcapturerate
= 75;
516 pp
->capturerate
= 75;
517 pp
->patternrate
= 75;
518 pp
->hanerate
= pp
->cut1rate
= pp
->cut2rate
= 75;
521 char *optspec
, *next
= arg
;
524 next
+= strcspn(next
, ":");
525 if (*next
) { *next
++ = 0; } else { *next
= 0; }
527 char *optname
= optspec
;
528 char *optval
= strchr(optspec
, '=');
529 if (optval
) *optval
++ = 0;
531 if (!strcasecmp(optname
, "lcapturerate") && optval
) {
532 pp
->lcapturerate
= atoi(optval
);
533 } else if (!strcasecmp(optname
, "capturerate") && optval
) {
534 pp
->capturerate
= atoi(optval
);
535 } else if (!strcasecmp(optname
, "ladders")) {
537 } else if (!strcasecmp(optname
, "patternrate") && optval
) {
538 pp
->patternrate
= atoi(optval
);
539 } else if (!strcasecmp(optname
, "hanerate") && optval
) {
540 pp
->hanerate
= atoi(optval
);
541 } else if (!strcasecmp(optname
, "cut1rate") && optval
) {
542 pp
->cut1rate
= atoi(optval
);
543 } else if (!strcasecmp(optname
, "cut2rate") && optval
) {
544 pp
->cut2rate
= atoi(optval
);
545 } else if (!strcasecmp(optname
, "localassess")) {
546 pp
->localassess
= true;
547 } else if (!strcasecmp(optname
, "ladderassess")) {
548 pp
->ladderassess
= true;
550 fprintf(stderr
, "playout-moggy: Invalid policy argument %s or missing value\n", optname
);