Moggy 3x3: Consider also rotations of patterns
[pachi.git] / playout / moggy.c
blob577c6a6ee25613b2034dc6c9ab64e29c00c87fa7
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 (SUSPICIOUS) */
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 /* side pattern - chase */
65 "X.?"
66 "O.?"
67 "##?",
68 /* side pattern - weirdness (SUSPICIOUS) */
69 "?X?"
70 "X.O"
71 "###",
72 /* side pattern - sagari (SUSPICIOUS) */
73 "?XO"
74 "?.?" /* "?.x" ? */
75 "###" "X",
76 /* side pattern - weirdcut (SUSPICIOUS) */
77 "?OX"
78 "?.O"
79 "?##" "X",
80 /* side pattern - cut (SUSPICIOUS) */
81 "?OX"
82 "X.O"
83 "###" "X" /* no "X"? */,
85 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
87 /* Hashtable: 2*8 bits (ignore middle point, 2 bits per intersection) */
88 /* Value: 0: no pattern, 1: black pattern, 2: white pattern, 3: both patterns */
89 static char moggy_patterns[65536];
91 static void
92 _record_pattern(char *table, char *str, int pat, int fixed_color)
94 //fprintf(stderr, "[%s] %04x\n", str, pat);
96 /* Original color assignment */
97 table[pat] = fixed_color ? fixed_color : 3;
99 /* Reverse color assignment - achieved by swapping odd and even bits */
100 pat = ((pat >> 1) & 0x5555) | ((pat & 0x5555) << 1);
101 table[pat] = fixed_color ? 2 - (fixed_color == 2) : 3;
104 static int
105 _pat_vmirror(int pat)
107 /* V mirror pattern; reverse order of 3-2-3 chunks */
108 return ((pat & 0xfc00) >> 10) | (pat & 0x03c0) | ((pat & 0x003f) << 10);
111 static int
112 _pat_hmirror(int pat)
114 /* H mirror pattern; reverse order of 2-bit values within the chunks */
115 #define rev3(p) ((p >> 4) | (p & 0xc) | ((p & 0x3) << 4))
116 #define rev2(p) ((p >> 2) | ((p & 0x3) << 2))
117 return (rev3((pat & 0xfc00) >> 10) << 10)
118 | (rev2((pat & 0x03c0) >> 6) << 6)
119 | rev3((pat & 0x003f));
120 #undef rev3
121 #undef rev2
124 static int
125 _pat_90rot(int pat)
127 /* Rotate by 90 degrees:
128 * 5 6 7 7 4 2
129 * 3 4 -> 6 1
130 * 0 1 2 5 3 0 */
131 /* I'm too lazy to optimize this :) */
132 int vals[8];
133 for (int i = 0; i < 8; i++)
134 vals[i] = (pat >> (i * 2)) & 0x3;
135 int vals2[8];
136 vals2[0] = vals[5]; vals2[1] = vals[3]; vals2[2] = vals[0];
137 vals2[3] = vals[6]; vals2[4] = vals[1];
138 vals2[5] = vals[7]; vals2[6] = vals[4]; vals2[7] = vals[2];
139 int p2 = 0;
140 for (int i = 0; i < 8; i++)
141 p2 |= vals2[i] << (i * 2);
142 return p2;
145 static void
146 _gen_pattern(char *table, int pat, char *src, int srclen, int fixed_color)
148 for (; srclen > 0; src++, srclen--) {
149 if (srclen == 5)
150 continue;
151 int patofs = (srclen > 5 ? srclen - 1 : srclen) - 1;
152 switch (*src) {
153 case '?':
154 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
155 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
156 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
157 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
158 *src = '?'; // for future recursions
159 return;
160 case 'x':
161 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
162 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
163 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
164 *src = 'x'; // for future recursions
165 return;
166 case 'o':
167 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
168 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
169 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
170 *src = 'o'; // for future recursions
171 return;
172 case '.': /* 0 */ break;
173 case 'X': pat |= S_BLACK << (patofs * 2); break;
174 case 'O': pat |= S_WHITE << (patofs * 2); break;
175 case '#': pat |= S_OFFBOARD << (patofs * 2); break;
179 /* Original pattern, all transpositions and rotations */
180 _record_pattern(table, src - 9, pat, fixed_color);
181 _record_pattern(table, src - 9, _pat_vmirror(pat), fixed_color);
182 _record_pattern(table, src - 9, _pat_hmirror(pat), fixed_color);
183 _record_pattern(table, src - 9, _pat_vmirror(_pat_hmirror(pat)), fixed_color);
184 _record_pattern(table, src - 9, _pat_90rot(pat), fixed_color);
185 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(pat)), fixed_color);
186 _record_pattern(table, src - 9, _pat_90rot(_pat_hmirror(pat)), fixed_color);
187 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(_pat_hmirror(pat))), fixed_color);
190 static void __attribute__((constructor))
191 _init_patterns(void)
193 for (int i = 0; i < moggy_patterns_src_n; i++) {
194 int fixed_color = 0;
195 switch (moggy_patterns_src[i][9]) {
196 case 'X': fixed_color = S_BLACK; break;
197 case 'O': fixed_color = S_WHITE; break;
199 _gen_pattern(moggy_patterns, 0, moggy_patterns_src[i], 9, fixed_color);
205 /* Check if we match any pattern centered on given move. */
206 static bool
207 test_pattern_here(struct playout_policy *p, char *hashtable,
208 struct board *b, struct move *m)
210 int pat = 0;
211 int x = coord_x(m->coord, b), y = coord_y(m->coord, b);
212 pat |= (board_atxy(b, x - 1, y - 1) << 14)
213 | (board_atxy(b, x, y - 1) << 12)
214 | (board_atxy(b, x + 1, y - 1) << 10);
215 pat |= (board_atxy(b, x - 1, y) << 8)
216 | (board_atxy(b, x + 1, y) << 6);
217 pat |= (board_atxy(b, x - 1, y + 1) << 4)
218 | (board_atxy(b, x, y + 1) << 2)
219 | (board_atxy(b, x + 1, y + 1));
220 //fprintf(stderr, "(%d,%d) hashtable[%04x] = %d\n", x, y, pat, hashtable[pat]);
221 return (hashtable[pat] & m->color);
224 static void
225 apply_pattern_here(struct playout_policy *p, char *hashtable,
226 struct board *b, struct move *m, struct move_queue *q)
228 if (test_pattern_here(p, hashtable, b, m))
229 q->move[q->moves++] = m->coord;
232 /* Check if we match any pattern around given move (with the other color to play). */
233 static coord_t
234 apply_pattern(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
236 //struct moggy_policy *pp = p->data;
237 struct move_queue q;
238 q.moves = 0;
240 /* Suicides do not make any patterns and confuse us. */
241 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
242 return pass;
244 // FIXME: Fix assess callers
245 foreach_neighbor(b, m->coord, {
246 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
247 if (board_at(b, c) == S_NONE && board_is_eyelike(b, &c, m->color))
248 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
250 foreach_diag_neighbor(b, m->coord) {
251 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
252 if (board_at(b, c) == S_NONE && board_is_eyelike(b, &c, m->color))
253 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
254 } foreach_diag_neighbor_end;
256 if (PLDEBUGL(5)) {
257 fprintf(stderr, "Pattern candidate moves: ");
258 for (int i = 0; i < q.moves; i++) {
259 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
261 fprintf(stderr, "\n");
264 if (testmove) {
265 while (q.moves--)
266 if (q.move[q.moves] == testmove->coord) {
267 if (PLDEBUGL(5))
268 fprintf(stderr, "Found queried move.\n");
269 return testmove->coord;
271 return pass;
274 int i = fast_random(q.moves);
275 return q.moves ? q.move[i] : pass;
280 /* Is this ladder breaker friendly for the one who catches ladder. */
281 static bool
282 ladder_catcher(struct board *b, int x, int y, enum stone laddered)
284 enum stone breaker = board_atxy(b, x, y);
285 return breaker == stone_other(laddered) || breaker == S_OFFBOARD;
288 static bool
289 ladder_catches(struct playout_policy *p, struct board *b, coord_t coord, group_t laddered)
291 struct moggy_policy *pp = p->data;
293 /* This is very trivial and gets a lot of corner cases wrong.
294 * We need this to be just very fast. One important point is
295 * that we sometimes might not notice a ladder but if we do,
296 * it should always work; thus we can use this for strong
297 * negative hinting safely. */
298 //fprintf(stderr, "ladder check\n");
300 enum stone lcolor = board_at(b, group_base(laddered));
301 int x = coord_x(coord, b), y = coord_y(coord, b);
303 /* First, special-case first-line "ladders". This is a huge chunk
304 * of ladders we actually meet and want to play. */
305 if (pp->borderladders
306 && neighbor_count_at(b, coord, S_OFFBOARD) == 1
307 && neighbor_count_at(b, coord, lcolor) == 1) {
308 if (PLDEBUGL(5))
309 fprintf(stderr, "border ladder\n");
310 /* Direction along border; xd is horiz. border, yd vertical. */
311 int xd = 0, yd = 0;
312 if (board_atxy(b, x + 1, y) == S_OFFBOARD || board_atxy(b, x - 1, y) == S_OFFBOARD)
313 yd = 1;
314 else
315 xd = 1;
316 /* Direction from the border; -1 is above/left, 1 is below/right. */
317 int dd = (board_atxy(b, x + yd, y + xd) == S_OFFBOARD) ? 1 : -1;
318 if (PLDEBUGL(6))
319 fprintf(stderr, "xd %d yd %d dd %d\n", xd, yd, dd);
320 /* | ? ?
321 * | . O #
322 * | c X #
323 * | . O #
324 * | ? ? */
325 /* This is normally caught, unless we have friends both above
326 * and below... */
327 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor
328 && board_atxy(b, x - xd * 2, y - yd * 2) == lcolor)
329 return false;
330 /* ...or can't block where we need because of shortage
331 * of liberties. */
332 int libs1 = board_group_info(b, group_atxy(b, x + xd - yd * dd, y + yd - xd * dd)).libs;
333 int libs2 = board_group_info(b, group_atxy(b, x - xd - yd * dd, y - yd - xd * dd)).libs;
334 if (PLDEBUGL(6))
335 fprintf(stderr, "libs1 %d libs2 %d\n", libs1, libs2);
336 if (libs1 < 2 && libs2 < 2)
337 return false;
338 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor && libs1 < 3)
339 return false;
340 if (board_atxy(b, x - xd * 2, y - yd * 2) == lcolor && libs2 < 3)
341 return false;
342 return true;
345 /* Figure out the ladder direction */
346 int xd, yd;
347 xd = board_atxy(b, x + 1, y) == S_NONE ? 1 : board_atxy(b, x - 1, y) == S_NONE ? -1 : 0;
348 yd = board_atxy(b, x, y + 1) == S_NONE ? 1 : board_atxy(b, x, y - 1) == S_NONE ? -1 : 0;
350 /* We do only tight ladders, not loose ladders. Furthermore,
351 * the ladders need to be simple:
352 * . X . . . X
353 * c O X supported . c O unsupported
354 * X # # X O #
357 /* For given (xd,yd), we have two possibilities where to move
358 * next. Consider (-1,1):
359 * n X . n c X
360 * c O X X O #
361 * X # # . X #
363 if (!xd || !yd || !(ladder_catcher(b, x - xd, y, lcolor) ^ ladder_catcher(b, x, y - yd, lcolor))) {
364 /* Silly situation, probably non-simple ladder or suicide. */
365 /* TODO: In case of basic non-simple ladder, play out both variants. */
366 if (PLDEBUGL(5))
367 fprintf(stderr, "non-simple ladder\n");
368 return false;
371 #define ladder_check(xd1_, yd1_, xd2_, yd2_) \
372 if (board_atxy(b, x, y) != S_NONE) { \
373 /* Did we hit a stone when playing out ladder? */ \
374 if (ladder_catcher(b, x, y, lcolor)) \
375 return true; /* ladder works */ \
376 if (board_group_info(b, group_atxy(b, x, y)).lib[0] > 0) \
377 return false; /* friend that's not in atari himself */ \
378 } else { \
379 /* No. So we are at new position. \
380 * We need to check indirect ladder breakers. */ \
381 /* . 2 x . . \
382 * . x o O 1 <- only at O we can check for o at 2 \
383 * x o o x . otherwise x at O would be still deadly \
384 * o o x . . \
385 * We check for o and x at 1, these are vital. \
386 * We check only for o at 2; x at 2 would mean we \
387 * need to fork (one step earlier). */ \
388 enum stone s1 = board_atxy(b, x + (xd1_), y + (yd1_)); \
389 if (s1 == lcolor) return false; \
390 if (s1 == stone_other(lcolor)) return true; \
391 enum stone s2 = board_atxy(b, x + (xd2_), y + (yd2_)); \
392 if (s2 == lcolor) return false; \
394 #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)
395 #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)
397 if (ladder_catcher(b, x - xd, y, lcolor))
398 ladder_horiz;
399 do {
400 ladder_vert;
401 ladder_horiz;
402 } while (1);
406 static coord_t
407 group_atari_check(struct playout_policy *p, struct board *b, group_t group)
409 struct moggy_policy *pp = p->data;
410 enum stone color = board_at(b, group_base(group));
411 coord_t lib = board_group_info(b, group).lib[0];
413 assert(color != S_OFFBOARD && color != S_NONE);
414 if (PLDEBUGL(5))
415 fprintf(stderr, "atariiiiiiiii %s of color %d\n", coord2sstr(lib, b), color);
416 assert(board_at(b, lib) == S_NONE);
418 /* Do not suicide... */
419 if (!valid_escape_route(b, color, lib))
420 goto caught;
421 if (PLDEBUGL(6))
422 fprintf(stderr, "...escape route valid\n");
424 /* ...or play out ladders. */
425 if (pp->ladders && ladder_catches(p, b, lib, group)) {
426 goto caught;
428 if (PLDEBUGL(6))
429 fprintf(stderr, "...no ladder\n");
431 return lib;
433 caught:
434 /* There is still hope - can't we capture some neighbor? */
435 foreach_in_group(b, group) {
436 foreach_neighbor(b, c, {
437 if (board_at(b, c) != stone_other(color)
438 || board_group_info(b, group_at(b, c)).libs > 1)
439 continue;
440 if (PLDEBUGL(6))
441 fprintf(stderr, "can capture group %d\n", group_at(b, c));
442 /* If we are saving our group, capture! */
443 if (b->last_move.color == stone_other(color))
444 return board_group_info(b, group_at(b, c)).lib[0];
445 /* If we chase the group, capture it now! */
446 return lib;
448 } foreach_in_group_end;
449 return pass;
452 static coord_t
453 global_atari_check(struct playout_policy *p, struct board *b)
455 if (b->clen == 0)
456 return pass;
458 int g_base = fast_random(b->clen);
459 for (int g = g_base; g < b->clen; g++) {
460 coord_t c = group_atari_check(p, b, group_at(b, group_base(b->c[g])));
461 if (!is_pass(c))
462 return c;
464 for (int g = 0; g < g_base; g++) {
465 coord_t c = group_atari_check(p, b, group_at(b, group_base(b->c[g])));
466 if (!is_pass(c))
467 return c;
469 return pass;
472 static coord_t
473 local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
475 struct move_queue q;
476 q.moves = 0;
478 /* Did the opponent play a self-atari? */
479 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
480 coord_t l = group_atari_check(p, b, group_at(b, m->coord));
481 if (!is_pass(l))
482 q.move[q.moves++] = l;
485 foreach_neighbor(b, m->coord, {
486 group_t g = group_at(b, c);
487 if (!g || board_group_info(b, g).libs != 1)
488 continue;
489 coord_t l = group_atari_check(p, b, g);
490 if (!is_pass(l))
491 q.move[q.moves++] = l;
494 if (PLDEBUGL(5)) {
495 fprintf(stderr, "Local atari candidate moves: ");
496 for (int i = 0; i < q.moves; i++) {
497 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
499 fprintf(stderr, "\n");
502 if (testmove) {
503 while (q.moves--)
504 if (q.move[q.moves] == testmove->coord) {
505 if (PLDEBUGL(5))
506 fprintf(stderr, "Found queried move.\n");
507 return testmove->coord;
509 return pass;
512 int i = fast_random(q.moves);
513 return q.moves ? q.move[i] : pass;
516 coord_t
517 playout_moggy_choose(struct playout_policy *p, struct board *b, enum stone our_real_color)
519 struct moggy_policy *pp = p->data;
520 coord_t c;
522 if (PLDEBUGL(5))
523 board_print(b, stderr);
525 /* Local checks */
526 if (!is_pass(b->last_move.coord)) {
527 /* Local group in atari? */
528 if (pp->lcapturerate > fast_random(100)) {
529 c = local_atari_check(p, b, &b->last_move, NULL);
530 if (!is_pass(c))
531 return c;
534 /* Check for patterns we know */
535 if (pp->patternrate > fast_random(100)) {
536 c = apply_pattern(p, b, &b->last_move, NULL);
537 if (!is_pass(c))
538 return c;
542 /* Global checks */
544 /* Any groups in atari? */
545 if (pp->capturerate > fast_random(100)) {
546 c = global_atari_check(p, b);
547 if (!is_pass(c))
548 return c;
551 return pass;
554 float
555 playout_moggy_assess(struct playout_policy *p, struct board *b, struct move *m)
557 struct moggy_policy *pp = p->data;
559 if (is_pass(m->coord))
560 return NAN;
562 if (PLDEBUGL(5)) {
563 fprintf(stderr, "ASSESS of %s:\n", coord2sstr(m->coord, b));
564 board_print(b, stderr);
567 /* Are we dealing with atari? */
568 if (pp->lcapturerate > fast_random(100)) {
569 foreach_neighbor(b, m->coord, {
570 struct move m2;
571 m2.coord = c; m2.color = stone_other(m->color);
572 if (local_atari_check(p, b, &m2, m) == m->coord)
573 return 1.0;
576 /* Assess ladders anywhere, local or not. */
577 if (pp->ladderassess) {
578 //fprintf(stderr, "ASSESS %s\n", coord2sstr(m->coord, b));
579 foreach_neighbor(b, m->coord, {
580 if (board_at(b, c) == S_NONE || board_at(b, c) == S_OFFBOARD)
581 continue;
582 group_t g = group_at(b, c);
583 if (board_group_info(b, g).libs != 1)
584 continue;
585 if (ladder_catches(p, b, m->coord, g))
586 return 0.0;
591 /* Pattern check */
592 if (pp->patternrate > fast_random(100)) {
593 if (test_pattern_here(p, moggy_patterns, b, m))
594 return 1.0;
597 return NAN;
601 struct playout_policy *
602 playout_moggy_init(char *arg)
604 struct playout_policy *p = calloc(1, sizeof(*p));
605 struct moggy_policy *pp = calloc(1, sizeof(*pp));
606 p->data = pp;
607 p->choose = playout_moggy_choose;
608 p->assess = playout_moggy_assess;
610 pp->lcapturerate = 75;
611 pp->capturerate = 75;
612 pp->patternrate = 95;
613 pp->ladders = pp->borderladders = true;
614 pp->ladderassess = true;
616 if (arg) {
617 char *optspec, *next = arg;
618 while (*next) {
619 optspec = next;
620 next += strcspn(next, ":");
621 if (*next) { *next++ = 0; } else { *next = 0; }
623 char *optname = optspec;
624 char *optval = strchr(optspec, '=');
625 if (optval) *optval++ = 0;
627 if (!strcasecmp(optname, "lcapturerate") && optval) {
628 pp->lcapturerate = atoi(optval);
629 } else if (!strcasecmp(optname, "capturerate") && optval) {
630 pp->capturerate = atoi(optval);
631 } else if (!strcasecmp(optname, "patternrate") && optval) {
632 pp->patternrate = atoi(optval);
633 } else if (!strcasecmp(optname, "ladders")) {
634 pp->ladders = optval && *optval == '0' ? false : true;
635 } else if (!strcasecmp(optname, "borderladders")) {
636 pp->borderladders = optval && *optval == '0' ? false : true;
637 } else if (!strcasecmp(optname, "ladderassess")) {
638 pp->ladderassess = optval && *optval == '0' ? false : true;
639 } else {
640 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
645 return p;