Moggy 3x3: Cut keima pattern, adjust side patterns
[pachi.git] / playout / moggy.c
blobe04b388401c4e65fbc8fddfd98160256ba235cd7
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];
28 /* Pattern encoding:
29 * X: black; O: white; .: empty; #: edge
30 * x: !black; o: !white; ?: any
32 * extra X: pattern valid only for one side;
33 * middle point ignored. */
35 static char moggy_patterns_src[][11] = {
36 /* hane pattern - enclosing hane */
37 "XOX"
38 "..."
39 "???",
40 /* hane pattern - non-cutting hane */
41 "XO."
42 "..."
43 "?.?",
44 /* hane pattern - magari */
45 "XO?"
46 "X.."
47 "?.?",
48 /* hane pattern - thin hane */
49 "XOO"
50 "..."
51 "?.?" "X",
52 /* cut1 pattern (kiri) - unprotected cut */
53 "XO?"
54 "O.o"
55 "?o?",
56 /* cut1 pattern (kiri) - peeped cut */
57 "XO?"
58 "O.X"
59 "???",
60 /* cut2 pattern (de) */
61 "?X?"
62 "O.O"
63 "ooo",
64 /* cut keima (not in Mogo) */
65 "OX?",
66 "o.O",
67 "o??",
68 /* side pattern - chase */
69 "X.?"
70 "O.?"
71 "##?",
72 /* side pattern - weirdness (SUSPICIOUS) */
73 "?X?"
74 "X.O"
75 "###",
76 /* side pattern - sagari (SUSPICIOUS) */
77 "?XO"
78 "?.?" /* "?.x" ? */
79 "###" "X",
80 /* side pattern - weirdcut (SUSPICIOUS) */
81 /* "?OX"
82 "?.O"
83 "?##" "X", */
84 /* side pattern - cut (SUSPICIOUS) */
85 "?OX"
86 "X.O"
87 "###" /* Mogo has "X" */,
89 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
91 /* Hashtable: 2*8 bits (ignore middle point, 2 bits per intersection) */
92 /* Value: 0: no pattern, 1: black pattern, 2: white pattern, 3: both patterns */
93 static char moggy_patterns[65536];
95 static void
96 _record_pattern(char *table, char *str, int pat, int fixed_color)
98 //fprintf(stderr, "[%s] %04x\n", str, pat);
100 /* Original color assignment */
101 table[pat] = fixed_color ? fixed_color : 3;
103 /* Reverse color assignment - achieved by swapping odd and even bits */
104 pat = ((pat >> 1) & 0x5555) | ((pat & 0x5555) << 1);
105 table[pat] = fixed_color ? 2 - (fixed_color == 2) : 3;
108 static int
109 _pat_vmirror(int pat)
111 /* V mirror pattern; reverse order of 3-2-3 chunks */
112 return ((pat & 0xfc00) >> 10) | (pat & 0x03c0) | ((pat & 0x003f) << 10);
115 static int
116 _pat_hmirror(int pat)
118 /* H mirror pattern; reverse order of 2-bit values within the chunks */
119 #define rev3(p) ((p >> 4) | (p & 0xc) | ((p & 0x3) << 4))
120 #define rev2(p) ((p >> 2) | ((p & 0x3) << 2))
121 return (rev3((pat & 0xfc00) >> 10) << 10)
122 | (rev2((pat & 0x03c0) >> 6) << 6)
123 | rev3((pat & 0x003f));
124 #undef rev3
125 #undef rev2
128 static int
129 _pat_90rot(int pat)
131 /* Rotate by 90 degrees:
132 * 5 6 7 7 4 2
133 * 3 4 -> 6 1
134 * 0 1 2 5 3 0 */
135 /* I'm too lazy to optimize this :) */
136 int vals[8];
137 for (int i = 0; i < 8; i++)
138 vals[i] = (pat >> (i * 2)) & 0x3;
139 int vals2[8];
140 vals2[0] = vals[5]; vals2[1] = vals[3]; vals2[2] = vals[0];
141 vals2[3] = vals[6]; vals2[4] = vals[1];
142 vals2[5] = vals[7]; vals2[6] = vals[4]; vals2[7] = vals[2];
143 int p2 = 0;
144 for (int i = 0; i < 8; i++)
145 p2 |= vals2[i] << (i * 2);
146 return p2;
149 static void
150 _gen_pattern(char *table, int pat, char *src, int srclen, int fixed_color)
152 for (; srclen > 0; src++, srclen--) {
153 if (srclen == 5)
154 continue;
155 int patofs = (srclen > 5 ? srclen - 1 : srclen) - 1;
156 switch (*src) {
157 case '?':
158 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
159 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
160 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
161 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
162 *src = '?'; // for future recursions
163 return;
164 case 'x':
165 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
166 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
167 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
168 *src = 'x'; // for future recursions
169 return;
170 case 'o':
171 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
172 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
173 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
174 *src = 'o'; // for future recursions
175 return;
176 case '.': /* 0 */ break;
177 case 'X': pat |= S_BLACK << (patofs * 2); break;
178 case 'O': pat |= S_WHITE << (patofs * 2); break;
179 case '#': pat |= S_OFFBOARD << (patofs * 2); break;
183 /* Original pattern, all transpositions and rotations */
184 _record_pattern(table, src - 9, pat, fixed_color);
185 _record_pattern(table, src - 9, _pat_vmirror(pat), fixed_color);
186 _record_pattern(table, src - 9, _pat_hmirror(pat), fixed_color);
187 _record_pattern(table, src - 9, _pat_vmirror(_pat_hmirror(pat)), fixed_color);
188 _record_pattern(table, src - 9, _pat_90rot(pat), fixed_color);
189 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(pat)), fixed_color);
190 _record_pattern(table, src - 9, _pat_90rot(_pat_hmirror(pat)), fixed_color);
191 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(_pat_hmirror(pat))), fixed_color);
194 static void __attribute__((constructor))
195 _init_patterns(void)
197 for (int i = 0; i < moggy_patterns_src_n; i++) {
198 int fixed_color = 0;
199 switch (moggy_patterns_src[i][9]) {
200 case 'X': fixed_color = S_BLACK; break;
201 case 'O': fixed_color = S_WHITE; break;
203 _gen_pattern(moggy_patterns, 0, moggy_patterns_src[i], 9, fixed_color);
209 /* Check if we match any pattern centered on given move. */
210 static bool
211 test_pattern_here(struct playout_policy *p, char *hashtable,
212 struct board *b, struct move *m)
214 int pat = 0;
215 int x = coord_x(m->coord, b), y = coord_y(m->coord, b);
216 pat |= (board_atxy(b, x - 1, y - 1) << 14)
217 | (board_atxy(b, x, y - 1) << 12)
218 | (board_atxy(b, x + 1, y - 1) << 10);
219 pat |= (board_atxy(b, x - 1, y) << 8)
220 | (board_atxy(b, x + 1, y) << 6);
221 pat |= (board_atxy(b, x - 1, y + 1) << 4)
222 | (board_atxy(b, x, y + 1) << 2)
223 | (board_atxy(b, x + 1, y + 1));
224 //fprintf(stderr, "(%d,%d) hashtable[%04x] = %d\n", x, y, pat, hashtable[pat]);
225 return (hashtable[pat] & m->color);
228 static void
229 apply_pattern_here(struct playout_policy *p, char *hashtable,
230 struct board *b, struct move *m, struct move_queue *q)
232 if (test_pattern_here(p, hashtable, b, m))
233 q->move[q->moves++] = m->coord;
236 /* Check if we match any pattern around given move (with the other color to play). */
237 static coord_t
238 apply_pattern(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
240 //struct moggy_policy *pp = p->data;
241 struct move_queue q;
242 q.moves = 0;
244 /* Suicides do not make any patterns and confuse us. */
245 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
246 return pass;
248 // FIXME: Fix assess callers
249 foreach_neighbor(b, m->coord, {
250 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
251 if (board_at(b, c) == S_NONE && !board_is_eyelike(b, &c, m->color))
252 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
254 foreach_diag_neighbor(b, m->coord) {
255 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
256 if (board_at(b, c) == S_NONE && !board_is_eyelike(b, &c, m->color))
257 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
258 } foreach_diag_neighbor_end;
260 if (PLDEBUGL(5)) {
261 fprintf(stderr, "Pattern candidate moves: ");
262 for (int i = 0; i < q.moves; i++) {
263 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
265 fprintf(stderr, "\n");
268 if (testmove) {
269 while (q.moves--)
270 if (q.move[q.moves] == testmove->coord) {
271 if (PLDEBUGL(5))
272 fprintf(stderr, "Found queried move.\n");
273 return testmove->coord;
275 return pass;
278 int i = fast_random(q.moves);
279 return q.moves ? q.move[i] : pass;
284 /* Is this ladder breaker friendly for the one who catches ladder. */
285 static bool
286 ladder_catcher(struct board *b, int x, int y, enum stone laddered)
288 enum stone breaker = board_atxy(b, x, y);
289 return breaker == stone_other(laddered) || breaker == S_OFFBOARD;
292 static bool
293 ladder_catches(struct playout_policy *p, struct board *b, coord_t coord, group_t laddered)
295 struct moggy_policy *pp = p->data;
297 /* This is very trivial and gets a lot of corner cases wrong.
298 * We need this to be just very fast. One important point is
299 * that we sometimes might not notice a ladder but if we do,
300 * it should always work; thus we can use this for strong
301 * negative hinting safely. */
302 //fprintf(stderr, "ladder check\n");
304 enum stone lcolor = board_at(b, group_base(laddered));
305 int x = coord_x(coord, b), y = coord_y(coord, b);
307 /* First, special-case first-line "ladders". This is a huge chunk
308 * of ladders we actually meet and want to play. */
309 if (pp->borderladders
310 && neighbor_count_at(b, coord, S_OFFBOARD) == 1
311 && neighbor_count_at(b, coord, lcolor) == 1) {
312 if (PLDEBUGL(5))
313 fprintf(stderr, "border ladder\n");
314 /* Direction along border; xd is horiz. border, yd vertical. */
315 int xd = 0, yd = 0;
316 if (board_atxy(b, x + 1, y) == S_OFFBOARD || board_atxy(b, x - 1, y) == S_OFFBOARD)
317 yd = 1;
318 else
319 xd = 1;
320 /* Direction from the border; -1 is above/left, 1 is below/right. */
321 int dd = (board_atxy(b, x + yd, y + xd) == S_OFFBOARD) ? 1 : -1;
322 if (PLDEBUGL(6))
323 fprintf(stderr, "xd %d yd %d dd %d\n", xd, yd, dd);
324 /* | ? ?
325 * | . O #
326 * | c X #
327 * | . O #
328 * | ? ? */
329 /* This is normally caught, unless we have friends both above
330 * and below... */
331 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor
332 && board_atxy(b, x - xd * 2, y - yd * 2) == lcolor)
333 return false;
334 /* ...or can't block where we need because of shortage
335 * of liberties. */
336 int libs1 = board_group_info(b, group_atxy(b, x + xd - yd * dd, y + yd - xd * dd)).libs;
337 int libs2 = board_group_info(b, group_atxy(b, x - xd - yd * dd, y - yd - xd * dd)).libs;
338 if (PLDEBUGL(6))
339 fprintf(stderr, "libs1 %d libs2 %d\n", libs1, libs2);
340 if (libs1 < 2 && libs2 < 2)
341 return false;
342 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor && libs1 < 3)
343 return false;
344 if (board_atxy(b, x - xd * 2, y - yd * 2) == lcolor && libs2 < 3)
345 return false;
346 return true;
349 /* Figure out the ladder direction */
350 int xd, yd;
351 xd = board_atxy(b, x + 1, y) == S_NONE ? 1 : board_atxy(b, x - 1, y) == S_NONE ? -1 : 0;
352 yd = board_atxy(b, x, y + 1) == S_NONE ? 1 : board_atxy(b, x, y - 1) == S_NONE ? -1 : 0;
354 /* We do only tight ladders, not loose ladders. Furthermore,
355 * the ladders need to be simple:
356 * . X . . . X
357 * c O X supported . c O unsupported
358 * X # # X O #
361 /* For given (xd,yd), we have two possibilities where to move
362 * next. Consider (-1,1):
363 * n X . n c X
364 * c O X X O #
365 * X # # . X #
367 if (!xd || !yd || !(ladder_catcher(b, x - xd, y, lcolor) ^ ladder_catcher(b, x, y - yd, lcolor))) {
368 /* Silly situation, probably non-simple ladder or suicide. */
369 /* TODO: In case of basic non-simple ladder, play out both variants. */
370 if (PLDEBUGL(5))
371 fprintf(stderr, "non-simple ladder\n");
372 return false;
375 #define ladder_check(xd1_, yd1_, xd2_, yd2_) \
376 if (board_atxy(b, x, y) != S_NONE) { \
377 /* Did we hit a stone when playing out ladder? */ \
378 if (ladder_catcher(b, x, y, lcolor)) \
379 return true; /* ladder works */ \
380 if (board_group_info(b, group_atxy(b, x, y)).lib[0] > 0) \
381 return false; /* friend that's not in atari himself */ \
382 } else { \
383 /* No. So we are at new position. \
384 * We need to check indirect ladder breakers. */ \
385 /* . 2 x . . \
386 * . x o O 1 <- only at O we can check for o at 2 \
387 * x o o x . otherwise x at O would be still deadly \
388 * o o x . . \
389 * We check for o and x at 1, these are vital. \
390 * We check only for o at 2; x at 2 would mean we \
391 * need to fork (one step earlier). */ \
392 enum stone s1 = board_atxy(b, x + (xd1_), y + (yd1_)); \
393 if (s1 == lcolor) return false; \
394 if (s1 == stone_other(lcolor)) return true; \
395 enum stone s2 = board_atxy(b, x + (xd2_), y + (yd2_)); \
396 if (s2 == lcolor) return false; \
398 #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)
399 #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)
401 if (ladder_catcher(b, x - xd, y, lcolor))
402 ladder_horiz;
403 do {
404 ladder_vert;
405 ladder_horiz;
406 } while (1);
410 static coord_t
411 group_atari_check(struct playout_policy *p, struct board *b, group_t group)
413 struct moggy_policy *pp = p->data;
414 enum stone color = board_at(b, group_base(group));
415 coord_t lib = board_group_info(b, group).lib[0];
417 assert(color != S_OFFBOARD && color != S_NONE);
418 if (PLDEBUGL(5))
419 fprintf(stderr, "atariiiiiiiii %s of color %d\n", coord2sstr(lib, b), color);
420 assert(board_at(b, lib) == S_NONE);
422 /* Do not suicide... */
423 if (!valid_escape_route(b, color, lib))
424 goto caught;
425 if (PLDEBUGL(6))
426 fprintf(stderr, "...escape route valid\n");
428 /* ...or play out ladders. */
429 if (pp->ladders && ladder_catches(p, b, lib, group)) {
430 goto caught;
432 if (PLDEBUGL(6))
433 fprintf(stderr, "...no ladder\n");
435 return lib;
437 caught:
438 /* There is still hope - can't we capture some neighbor? */
439 foreach_in_group(b, group) {
440 foreach_neighbor(b, c, {
441 if (board_at(b, c) != stone_other(color)
442 || board_group_info(b, group_at(b, c)).libs > 1)
443 continue;
444 if (PLDEBUGL(6))
445 fprintf(stderr, "can capture group %d\n", group_at(b, c));
446 /* If we are saving our group, capture! */
447 if (b->last_move.color == stone_other(color))
448 return board_group_info(b, group_at(b, c)).lib[0];
449 /* If we chase the group, capture it now! */
450 return lib;
452 } foreach_in_group_end;
453 return pass;
456 static coord_t
457 global_atari_check(struct playout_policy *p, struct board *b)
459 if (b->clen == 0)
460 return pass;
462 int g_base = fast_random(b->clen);
463 for (int g = g_base; g < b->clen; g++) {
464 coord_t c = group_atari_check(p, b, group_at(b, group_base(b->c[g])));
465 if (!is_pass(c))
466 return c;
468 for (int g = 0; g < g_base; g++) {
469 coord_t c = group_atari_check(p, b, group_at(b, group_base(b->c[g])));
470 if (!is_pass(c))
471 return c;
473 return pass;
476 static coord_t
477 local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
479 struct move_queue q;
480 q.moves = 0;
482 /* Did the opponent play a self-atari? */
483 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
484 coord_t l = group_atari_check(p, b, group_at(b, m->coord));
485 if (!is_pass(l))
486 q.move[q.moves++] = l;
489 foreach_neighbor(b, m->coord, {
490 group_t g = group_at(b, c);
491 if (!g || board_group_info(b, g).libs != 1)
492 continue;
493 coord_t l = group_atari_check(p, b, g);
494 if (!is_pass(l))
495 q.move[q.moves++] = l;
498 if (PLDEBUGL(5)) {
499 fprintf(stderr, "Local atari candidate moves: ");
500 for (int i = 0; i < q.moves; i++) {
501 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
503 fprintf(stderr, "\n");
506 if (testmove) {
507 while (q.moves--)
508 if (q.move[q.moves] == testmove->coord) {
509 if (PLDEBUGL(5))
510 fprintf(stderr, "Found queried move.\n");
511 return testmove->coord;
513 return pass;
516 int i = fast_random(q.moves);
517 return q.moves ? q.move[i] : pass;
520 coord_t
521 playout_moggy_choose(struct playout_policy *p, struct board *b, enum stone our_real_color)
523 struct moggy_policy *pp = p->data;
524 coord_t c;
526 if (PLDEBUGL(5))
527 board_print(b, stderr);
529 /* Local checks */
530 if (!is_pass(b->last_move.coord)) {
531 /* Local group in atari? */
532 if (pp->lcapturerate > fast_random(100)) {
533 c = local_atari_check(p, b, &b->last_move, NULL);
534 if (!is_pass(c))
535 return c;
538 /* Check for patterns we know */
539 if (pp->patternrate > fast_random(100)) {
540 c = apply_pattern(p, b, &b->last_move, NULL);
541 if (!is_pass(c))
542 return c;
546 /* Global checks */
548 /* Any groups in atari? */
549 if (pp->capturerate > fast_random(100)) {
550 c = global_atari_check(p, b);
551 if (!is_pass(c))
552 return c;
555 return pass;
558 float
559 playout_moggy_assess(struct playout_policy *p, struct board *b, struct move *m)
561 struct moggy_policy *pp = p->data;
563 if (is_pass(m->coord))
564 return NAN;
566 if (PLDEBUGL(5)) {
567 fprintf(stderr, "ASSESS of %s:\n", coord2sstr(m->coord, b));
568 board_print(b, stderr);
571 /* Are we dealing with atari? */
572 if (pp->lcapturerate > fast_random(100)) {
573 foreach_neighbor(b, m->coord, {
574 if (board_at(b, c) == S_NONE || board_at(b, c) == S_OFFBOARD)
575 continue;
576 struct move m2;
577 m2.coord = c; m2.color = stone_other(m->color);
578 if (local_atari_check(p, b, &m2, m) == m->coord)
579 return 1.0;
582 /* Assess ladders anywhere, local or not. */
583 if (pp->ladderassess) {
584 //fprintf(stderr, "ASSESS %s\n", coord2sstr(m->coord, b));
585 foreach_neighbor(b, m->coord, {
586 if (board_at(b, c) == S_NONE || board_at(b, c) == S_OFFBOARD)
587 continue;
588 group_t g = group_at(b, c);
589 if (board_group_info(b, g).libs != 1)
590 continue;
591 if (ladder_catches(p, b, m->coord, g))
592 return 0.0;
597 /* Pattern check */
598 if (pp->patternrate > fast_random(100)) {
599 if (test_pattern_here(p, moggy_patterns, b, m))
600 return 1.0;
603 return NAN;
607 struct playout_policy *
608 playout_moggy_init(char *arg)
610 struct playout_policy *p = calloc(1, sizeof(*p));
611 struct moggy_policy *pp = calloc(1, sizeof(*pp));
612 p->data = pp;
613 p->choose = playout_moggy_choose;
614 p->assess = playout_moggy_assess;
616 pp->lcapturerate = 90;
617 pp->capturerate = 90;
618 pp->patternrate = 90;
619 pp->ladders = pp->borderladders = true;
620 pp->ladderassess = true;
622 if (arg) {
623 char *optspec, *next = arg;
624 while (*next) {
625 optspec = next;
626 next += strcspn(next, ":");
627 if (*next) { *next++ = 0; } else { *next = 0; }
629 char *optname = optspec;
630 char *optval = strchr(optspec, '=');
631 if (optval) *optval++ = 0;
633 if (!strcasecmp(optname, "lcapturerate") && optval) {
634 pp->lcapturerate = atoi(optval);
635 } else if (!strcasecmp(optname, "capturerate") && optval) {
636 pp->capturerate = atoi(optval);
637 } else if (!strcasecmp(optname, "patternrate") && optval) {
638 pp->patternrate = atoi(optval);
639 } else if (!strcasecmp(optname, "ladders")) {
640 pp->ladders = optval && *optval == '0' ? false : true;
641 } else if (!strcasecmp(optname, "borderladders")) {
642 pp->borderladders = optval && *optval == '0' ? false : true;
643 } else if (!strcasecmp(optname, "ladderassess")) {
644 pp->ladderassess = optval && *optval == '0' ? false : true;
645 } else {
646 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
651 return p;