Permission policy for random plays is available now
[pachi.git] / playout / moggy.c
blob326e6c7af5f7ff8aae1f1f2d40d16cc236018422
1 #include <assert.h>
2 #include <math.h>
3 #include <stdio.h>
4 #include <stdlib.h>
6 #define DEBUG
7 #include "board.h"
8 #include "debug.h"
9 #include "playout.h"
10 #include "playout/moggy.h"
11 #include "random.h"
13 #define PLDEBUGL(n) DEBUGL_(p->debug_level, n)
16 struct moggy_policy {
17 bool ladders, ladderassess, borderladders;
18 int lcapturerate, capturerate, patternrate;
21 #define MQL 64
22 struct move_queue {
23 int moves;
24 coord_t move[MQL];
27 static void
28 mq_nodup(struct move_queue *q)
30 if ((q->moves > 1 && q->move[q->moves - 2] == q->move[q->moves - 1])
31 || (q->moves > 2 && q->move[q->moves - 3] == q->move[q->moves - 1])
32 || (q->moves > 3 && q->move[q->moves - 4] == q->move[q->moves - 1]))
33 q->moves--;
37 /* Pattern encoding:
38 * X: black; O: white; .: empty; #: edge
39 * x: !black; o: !white; ?: any
41 * extra X: pattern valid only for one side;
42 * middle point ignored. */
44 static char moggy_patterns_src[][11] = {
45 /* hane pattern - enclosing hane */
46 "XOX"
47 "..."
48 "???",
49 /* hane pattern - non-cutting hane */
50 "XO."
51 "..."
52 "?.?",
53 /* hane pattern - magari */
54 "XO?"
55 "X.."
56 "x.?",
57 /* hane pattern - thin hane */
58 "XOO"
59 "..."
60 "?.?" "X",
61 /* generic pattern - katatsuke or diagonal attachment; similar to magari */
62 ".O."
63 "X.."
64 "...",
65 /* cut1 pattern (kiri) - unprotected cut */
66 "XO?"
67 "O.o"
68 "?o?",
69 /* cut1 pattern (kiri) - peeped cut */
70 "XO?"
71 "O.X"
72 "???",
73 /* cut2 pattern (de) */
74 "?X?"
75 "O.O"
76 "ooo",
77 /* cut keima (not in Mogo) */
78 "OX?"
79 "o.O"
80 "o??",
81 /* side pattern - chase */
82 "X.?"
83 "O.?"
84 "##?",
85 /* side pattern - weirdness (SUSPICIOUS) */
86 "?X?"
87 "X.O"
88 "###",
89 /* side pattern - sagari (SUSPICIOUS) */
90 "?XO"
91 "x.?" /* "?.x" ? */
92 "###" "X",
93 /* side pattern - weirdcut (SUSPICIOUS) */
94 #if 0
95 "?OX"
96 "?.O"
97 "?##" "X",
98 #endif
99 /* side pattern - cut (SUSPICIOUS) */
100 "?OX"
101 "X.O"
102 "###" /* Mogo has "X" */,
104 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
106 /* Hashtable: 2*8 bits (ignore middle point, 2 bits per intersection) */
107 /* Value: 0: no pattern, 1: black pattern, 2: white pattern, 3: both patterns */
108 static char moggy_patterns[65536];
110 static void
111 _record_pattern(char *table, char *str, int pat, int fixed_color)
113 //fprintf(stderr, "[%s] %04x\n", str, pat);
115 /* Original color assignment */
116 table[pat] = fixed_color ? fixed_color : 3;
118 /* Reverse color assignment - achieved by swapping odd and even bits */
119 pat = ((pat >> 1) & 0x5555) | ((pat & 0x5555) << 1);
120 table[pat] = fixed_color ? 2 - (fixed_color == 2) : 3;
123 static int
124 _pat_vmirror(int pat)
126 /* V mirror pattern; reverse order of 3-2-3 chunks */
127 return ((pat & 0xfc00) >> 10) | (pat & 0x03c0) | ((pat & 0x003f) << 10);
130 static int
131 _pat_hmirror(int pat)
133 /* H mirror pattern; reverse order of 2-bit values within the chunks */
134 #define rev3(p) ((p >> 4) | (p & 0xc) | ((p & 0x3) << 4))
135 #define rev2(p) ((p >> 2) | ((p & 0x3) << 2))
136 return (rev3((pat & 0xfc00) >> 10) << 10)
137 | (rev2((pat & 0x03c0) >> 6) << 6)
138 | rev3((pat & 0x003f));
139 #undef rev3
140 #undef rev2
143 static int
144 _pat_90rot(int pat)
146 /* Rotate by 90 degrees:
147 * 5 6 7 7 4 2
148 * 3 4 -> 6 1
149 * 0 1 2 5 3 0 */
150 /* I'm too lazy to optimize this :) */
151 int vals[8];
152 for (int i = 0; i < 8; i++)
153 vals[i] = (pat >> (i * 2)) & 0x3;
154 int vals2[8];
155 vals2[0] = vals[5]; vals2[1] = vals[3]; vals2[2] = vals[0];
156 vals2[3] = vals[6]; vals2[4] = vals[1];
157 vals2[5] = vals[7]; vals2[6] = vals[4]; vals2[7] = vals[2];
158 int p2 = 0;
159 for (int i = 0; i < 8; i++)
160 p2 |= vals2[i] << (i * 2);
161 return p2;
164 static void
165 _gen_pattern(char *table, int pat, char *src, int srclen, int fixed_color)
167 for (; srclen > 0; src++, srclen--) {
168 if (srclen == 5)
169 continue;
170 int patofs = (srclen > 5 ? srclen - 1 : srclen) - 1;
171 switch (*src) {
172 case '?':
173 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
174 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
175 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
176 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
177 *src = '?'; // for future recursions
178 return;
179 case 'x':
180 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
181 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
182 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
183 *src = 'x'; // for future recursions
184 return;
185 case 'o':
186 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
187 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
188 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
189 *src = 'o'; // for future recursions
190 return;
191 case '.': /* 0 */ break;
192 case 'X': pat |= S_BLACK << (patofs * 2); break;
193 case 'O': pat |= S_WHITE << (patofs * 2); break;
194 case '#': pat |= S_OFFBOARD << (patofs * 2); break;
198 /* Original pattern, all transpositions and rotations */
199 _record_pattern(table, src - 9, pat, fixed_color);
200 _record_pattern(table, src - 9, _pat_vmirror(pat), fixed_color);
201 _record_pattern(table, src - 9, _pat_hmirror(pat), fixed_color);
202 _record_pattern(table, src - 9, _pat_vmirror(_pat_hmirror(pat)), fixed_color);
203 _record_pattern(table, src - 9, _pat_90rot(pat), fixed_color);
204 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(pat)), fixed_color);
205 _record_pattern(table, src - 9, _pat_90rot(_pat_hmirror(pat)), fixed_color);
206 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(_pat_hmirror(pat))), fixed_color);
209 static void __attribute__((constructor))
210 _init_patterns(void)
212 for (int i = 0; i < moggy_patterns_src_n; i++) {
213 int fixed_color = 0;
214 switch (moggy_patterns_src[i][9]) {
215 case 'X': fixed_color = S_BLACK; break;
216 case 'O': fixed_color = S_WHITE; break;
218 _gen_pattern(moggy_patterns, 0, moggy_patterns_src[i], 9, fixed_color);
223 /* Check if we match any pattern centered on given move. */
224 static bool
225 test_pattern_here(struct playout_policy *p, char *hashtable,
226 struct board *b, struct move *m)
228 int pat = 0;
229 int x = coord_x(m->coord, b), y = coord_y(m->coord, b);
230 pat |= (board_atxy(b, x - 1, y - 1) << 14)
231 | (board_atxy(b, x, y - 1) << 12)
232 | (board_atxy(b, x + 1, y - 1) << 10);
233 pat |= (board_atxy(b, x - 1, y) << 8)
234 | (board_atxy(b, x + 1, y) << 6);
235 pat |= (board_atxy(b, x - 1, y + 1) << 4)
236 | (board_atxy(b, x, y + 1) << 2)
237 | (board_atxy(b, x + 1, y + 1));
238 //fprintf(stderr, "(%d,%d) hashtable[%04x] = %d\n", x, y, pat, hashtable[pat]);
239 return (hashtable[pat] & m->color) && !is_selfatari(b, m->color, m->coord);
242 static void
243 apply_pattern_here(struct playout_policy *p, char *hashtable,
244 struct board *b, struct move *m, struct move_queue *q)
246 if (test_pattern_here(p, hashtable, b, m))
247 q->move[q->moves++] = m->coord;
250 /* Check if we match any pattern around given move (with the other color to play). */
251 static coord_t
252 apply_pattern(struct playout_policy *p, struct board *b, struct move *m)
254 //struct moggy_policy *pp = p->data;
255 struct move_queue q;
256 q.moves = 0;
258 /* Suicides do not make any patterns and confuse us. */
259 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
260 return pass;
262 foreach_neighbor(b, m->coord, {
263 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
264 if (board_at(b, c) == S_NONE)
265 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
267 foreach_diag_neighbor(b, m->coord) {
268 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
269 if (board_at(b, c) == S_NONE)
270 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
271 } foreach_diag_neighbor_end;
273 if (PLDEBUGL(5)) {
274 fprintf(stderr, "Pattern candidate moves: ");
275 for (int i = 0; i < q.moves; i++) {
276 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
278 fprintf(stderr, "\n");
281 int i = fast_random(q.moves);
282 return q.moves ? q.move[i] : pass;
287 /* Is this ladder breaker friendly for the one who catches ladder. */
288 static bool
289 ladder_catcher(struct board *b, int x, int y, enum stone laddered)
291 enum stone breaker = board_atxy(b, x, y);
292 return breaker == stone_other(laddered) || breaker == S_OFFBOARD;
295 static bool
296 ladder_catches(struct playout_policy *p, struct board *b, coord_t coord, group_t laddered)
298 struct moggy_policy *pp = p->data;
300 /* This is very trivial and gets a lot of corner cases wrong.
301 * We need this to be just very fast. One important point is
302 * that we sometimes might not notice a ladder but if we do,
303 * it should always work; thus we can use this for strong
304 * negative hinting safely. */
305 //fprintf(stderr, "ladder check\n");
307 enum stone lcolor = board_at(b, group_base(laddered));
308 int x = coord_x(coord, b), y = coord_y(coord, b);
310 /* First, special-case first-line "ladders". This is a huge chunk
311 * of ladders we actually meet and want to play. */
312 if (pp->borderladders
313 && neighbor_count_at(b, coord, S_OFFBOARD) == 1
314 && neighbor_count_at(b, coord, lcolor) == 1) {
315 if (PLDEBUGL(5))
316 fprintf(stderr, "border ladder\n");
317 /* Direction along border; xd is horiz. border, yd vertical. */
318 int xd = 0, yd = 0;
319 if (board_atxy(b, x + 1, y) == S_OFFBOARD || board_atxy(b, x - 1, y) == S_OFFBOARD)
320 yd = 1;
321 else
322 xd = 1;
323 /* Direction from the border; -1 is above/left, 1 is below/right. */
324 int dd = (board_atxy(b, x + yd, y + xd) == S_OFFBOARD) ? 1 : -1;
325 if (PLDEBUGL(6))
326 fprintf(stderr, "xd %d yd %d dd %d\n", xd, yd, dd);
327 /* | ? ?
328 * | . O #
329 * | c X #
330 * | . O #
331 * | ? ? */
332 /* This is normally caught, unless we have friends both above
333 * and below... */
334 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor
335 && board_atxy(b, x - xd * 2, y - yd * 2) == lcolor)
336 return false;
337 /* ...or can't block where we need because of shortage
338 * of liberties. */
339 int libs1 = board_group_info(b, group_atxy(b, x + xd - yd * dd, y + yd - xd * dd)).libs;
340 int libs2 = board_group_info(b, group_atxy(b, x - xd - yd * dd, y - yd - xd * dd)).libs;
341 if (PLDEBUGL(6))
342 fprintf(stderr, "libs1 %d libs2 %d\n", libs1, libs2);
343 if (libs1 < 2 && libs2 < 2)
344 return false;
345 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor && libs1 < 3)
346 return false;
347 if (board_atxy(b, x - xd * 2, y - yd * 2) == lcolor && libs2 < 3)
348 return false;
349 return true;
352 /* Figure out the ladder direction */
353 int xd, yd;
354 xd = board_atxy(b, x + 1, y) == S_NONE ? 1 : board_atxy(b, x - 1, y) == S_NONE ? -1 : 0;
355 yd = board_atxy(b, x, y + 1) == S_NONE ? 1 : board_atxy(b, x, y - 1) == S_NONE ? -1 : 0;
357 /* We do only tight ladders, not loose ladders. Furthermore,
358 * the ladders need to be simple:
359 * . X . . . X
360 * c O X supported . c O unsupported
361 * X # # X O #
364 /* For given (xd,yd), we have two possibilities where to move
365 * next. Consider (-1,1):
366 * n X . n c X
367 * c O X X O #
368 * X # # . X #
370 if (!xd || !yd || !(ladder_catcher(b, x - xd, y, lcolor) ^ ladder_catcher(b, x, y - yd, lcolor))) {
371 /* Silly situation, probably non-simple ladder or suicide. */
372 /* TODO: In case of basic non-simple ladder, play out both variants. */
373 if (PLDEBUGL(5))
374 fprintf(stderr, "non-simple ladder\n");
375 return false;
378 #define ladder_check(xd1_, yd1_, xd2_, yd2_) \
379 if (board_atxy(b, x, y) != S_NONE) { \
380 /* Did we hit a stone when playing out ladder? */ \
381 if (ladder_catcher(b, x, y, lcolor)) \
382 return true; /* ladder works */ \
383 if (board_group_info(b, group_atxy(b, x, y)).lib[0] > 0) \
384 return false; /* friend that's not in atari himself */ \
385 } else { \
386 /* No. So we are at new position. \
387 * We need to check indirect ladder breakers. */ \
388 /* . 2 x . . \
389 * . x o O 1 <- only at O we can check for o at 2 \
390 * x o o x . otherwise x at O would be still deadly \
391 * o o x . . \
392 * We check for o and x at 1, these are vital. \
393 * We check only for o at 2; x at 2 would mean we \
394 * need to fork (one step earlier). */ \
395 enum stone s1 = board_atxy(b, x + (xd1_), y + (yd1_)); \
396 if (s1 == lcolor) return false; \
397 if (s1 == stone_other(lcolor)) return true; \
398 enum stone s2 = board_atxy(b, x + (xd2_), y + (yd2_)); \
399 if (s2 == lcolor) return false; \
401 #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)
402 #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)
404 if (ladder_catcher(b, x - xd, y, lcolor))
405 ladder_horiz;
406 do {
407 ladder_vert;
408 ladder_horiz;
409 } while (1);
413 static void
414 group_atari_check(struct playout_policy *p, struct board *b, group_t group, struct move_queue *q)
416 struct moggy_policy *pp = p->data;
418 /* Do not bother with kos. */
419 if (group_is_onestone(b, group))
420 return;
422 enum stone color = board_at(b, group_base(group));
423 coord_t lib = board_group_info(b, group).lib[0];
425 assert(color != S_OFFBOARD && color != S_NONE);
426 if (PLDEBUGL(5))
427 fprintf(stderr, "atariiiiiiiii %s of color %d\n", coord2sstr(lib, b), color);
428 assert(board_at(b, lib) == S_NONE);
430 /* Can we capture some neighbor? */
431 foreach_in_group(b, group) {
432 foreach_neighbor(b, c, {
433 if (board_at(b, c) != stone_other(color)
434 || board_group_info(b, group_at(b, c)).libs > 1)
435 continue;
436 if (PLDEBUGL(6))
437 fprintf(stderr, "can capture group %d\n", group_at(b, c));
438 /* If we are saving our group, capture! */
439 if (b->last_move.color == stone_other(color))
440 q->move[q->moves++] = board_group_info(b, group_at(b, c)).lib[0];
441 else /* If we chase the group, capture it now! */
442 q->move[q->moves++] = lib;
443 /* Make sure capturing the group will actually
444 * expand our liberties if we are filling our
445 * last liberty. */
446 if (q->move[q->moves - 1] == lib && is_selfatari(b, color, lib))
447 q->moves--;
448 else
449 mq_nodup(q);
451 } foreach_in_group_end;
453 /* Do not suicide... */
454 if (is_selfatari(b, color, lib))
455 return;
456 if (PLDEBUGL(6))
457 fprintf(stderr, "...escape route valid\n");
459 /* ...or play out ladders. */
460 if (pp->ladders && ladder_catches(p, b, lib, group)) {
461 return;
463 if (PLDEBUGL(6))
464 fprintf(stderr, "...no ladder\n");
466 q->move[q->moves++] = lib;
467 mq_nodup(q);
470 static coord_t
471 global_atari_check(struct playout_policy *p, struct board *b)
473 struct move_queue q;
474 q.moves = 0;
476 if (b->clen == 0)
477 return pass;
479 int g_base = fast_random(b->clen);
480 for (int g = g_base; g < b->clen; g++) {
481 group_atari_check(p, b, group_at(b, group_base(b->c[g])), &q);
482 if (q.moves > 0)
483 return q.move[fast_random(q.moves)];
485 for (int g = 0; g < g_base; g++) {
486 group_atari_check(p, b, group_at(b, group_base(b->c[g])), &q);
487 if (q.moves > 0)
488 return q.move[fast_random(q.moves)];
490 return pass;
493 static coord_t
494 local_atari_check(struct playout_policy *p, struct board *b, struct move *m)
496 struct move_queue q;
497 q.moves = 0;
499 /* Did the opponent play a self-atari? */
500 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
501 group_atari_check(p, b, group_at(b, m->coord), &q);
504 foreach_neighbor(b, m->coord, {
505 group_t g = group_at(b, c);
506 if (!g || board_group_info(b, g).libs != 1)
507 continue;
508 group_atari_check(p, b, g, &q);
511 if (PLDEBUGL(5)) {
512 fprintf(stderr, "Local atari candidate moves: ");
513 for (int i = 0; i < q.moves; i++) {
514 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
516 fprintf(stderr, "\n");
519 int i = fast_random(q.moves);
520 return q.moves ? q.move[i] : pass;
523 coord_t
524 playout_moggy_choose(struct playout_policy *p, struct board *b, enum stone to_play)
526 struct moggy_policy *pp = p->data;
527 coord_t c;
529 if (PLDEBUGL(5))
530 board_print(b, stderr);
532 /* Local checks */
533 if (!is_pass(b->last_move.coord)) {
534 /* Local group in atari? */
535 if (pp->lcapturerate > fast_random(100)) {
536 c = local_atari_check(p, b, &b->last_move);
537 if (!is_pass(c))
538 return c;
541 /* Check for patterns we know */
542 if (pp->patternrate > fast_random(100)) {
543 c = apply_pattern(p, b, &b->last_move);
544 if (!is_pass(c))
545 return c;
549 /* Global checks */
551 /* Any groups in atari? */
552 if (pp->capturerate > fast_random(100)) {
553 c = global_atari_check(p, b);
554 if (!is_pass(c))
555 return c;
558 return pass;
561 float
562 playout_moggy_assess(struct playout_policy *p, struct board *b, struct move *m)
564 struct moggy_policy *pp = p->data;
566 if (is_pass(m->coord))
567 return NAN;
569 if (PLDEBUGL(5)) {
570 fprintf(stderr, "ASSESS of %s:\n", coord2sstr(m->coord, b));
571 board_print(b, stderr);
574 /* Are we dealing with atari? */
575 if (pp->lcapturerate || pp->capturerate) {
576 bool ladder = false;
578 foreach_neighbor(b, m->coord, {
579 group_t g = group_at(b, c);
580 if (!g || board_group_info(b, g).libs != 1)
581 continue;
583 struct move_queue q; q.moves = 0;
584 group_atari_check(p, b, g, &q);
585 while (q.moves--)
586 if (q.move[q.moves] == m->coord) {
587 if (PLDEBUGL(5))
588 fprintf(stderr, "1.0: atari\n");
589 return 1.0;
592 /* _Never_ play here if this move plays out
593 * a caught ladder. (Unless it captures another
594 * group. :-) */
595 if (pp->ladderassess && ladder_catches(p, b, m->coord, g))
596 ladder = true;
599 if (ladder) {
600 if (PLDEBUGL(5))
601 fprintf(stderr, "0.0: ladder\n");
602 return 0.0;
606 /* Pattern check */
607 if (pp->patternrate) {
608 if (test_pattern_here(p, moggy_patterns, b, m)) {
609 if (PLDEBUGL(5))
610 fprintf(stderr, "1.0: pattern\n");
611 return 1.0;
615 return NAN;
619 struct playout_policy *
620 playout_moggy_init(char *arg)
622 struct playout_policy *p = calloc(1, sizeof(*p));
623 struct moggy_policy *pp = calloc(1, sizeof(*pp));
624 p->data = pp;
625 p->choose = playout_moggy_choose;
626 p->assess = playout_moggy_assess;
628 pp->lcapturerate = 90;
629 pp->capturerate = 90;
630 pp->patternrate = 90;
631 pp->ladders = pp->borderladders = true;
632 pp->ladderassess = true;
634 if (arg) {
635 char *optspec, *next = arg;
636 while (*next) {
637 optspec = next;
638 next += strcspn(next, ":");
639 if (*next) { *next++ = 0; } else { *next = 0; }
641 char *optname = optspec;
642 char *optval = strchr(optspec, '=');
643 if (optval) *optval++ = 0;
645 if (!strcasecmp(optname, "lcapturerate") && optval) {
646 pp->lcapturerate = atoi(optval);
647 } else if (!strcasecmp(optname, "capturerate") && optval) {
648 pp->capturerate = atoi(optval);
649 } else if (!strcasecmp(optname, "patternrate") && optval) {
650 pp->patternrate = atoi(optval);
651 } else if (!strcasecmp(optname, "ladders")) {
652 pp->ladders = optval && *optval == '0' ? false : true;
653 } else if (!strcasecmp(optname, "borderladders")) {
654 pp->borderladders = optval && *optval == '0' ? false : true;
655 } else if (!strcasecmp(optname, "ladderassess")) {
656 pp->ladderassess = optval && *optval == '0' ? false : true;
657 } else {
658 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
663 return p;