Moggy 3x3: Don't use self-atari patterns
[pachi.git] / playout / moggy.c
blob6935a7839b94f103514850a53017544d8c6c0929
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 > 4 &&
31 (q->move[q->moves - 2] == q->move[q->moves - 1]
32 || q->move[q->moves - 3] == q->move[q->moves - 1]
33 || q->move[q->moves - 4] == q->move[q->moves - 1]))
34 q->moves--;
38 /* Pattern encoding:
39 * X: black; O: white; .: empty; #: edge
40 * x: !black; o: !white; ?: any
42 * extra X: pattern valid only for one side;
43 * middle point ignored. */
45 static char moggy_patterns_src[][11] = {
46 /* hane pattern - enclosing hane */
47 "XOX"
48 "..."
49 "???",
50 /* hane pattern - non-cutting hane */
51 "XO."
52 "..."
53 "?.?",
54 /* hane pattern - magari */
55 "XO?"
56 "X.."
57 "?.?",
58 /* hane pattern - thin hane */
59 "XOO"
60 "..."
61 "?.?" "X",
62 /* cut1 pattern (kiri) - unprotected cut */
63 "XO?"
64 "O.o"
65 "?o?",
66 /* cut1 pattern (kiri) - peeped cut */
67 "XO?"
68 "O.X"
69 "???",
70 /* cut2 pattern (de) */
71 "?X?"
72 "O.O"
73 "ooo",
74 /* cut keima (not in Mogo) */
75 "OX?"
76 "o.O"
77 "o??",
78 /* side pattern - chase */
79 "X.?"
80 "O.?"
81 "##?",
82 /* side pattern - weirdness (SUSPICIOUS) */
83 "?X?"
84 "X.O"
85 "###",
86 /* side pattern - sagari (SUSPICIOUS) */
87 "?XO"
88 "?.?" /* "?.x" ? */
89 "###" "X",
90 /* side pattern - weirdcut (SUSPICIOUS) */
91 /* "?OX"
92 "?.O"
93 "?##" "X", */
94 /* side pattern - cut (SUSPICIOUS) */
95 "?OX"
96 "X.O"
97 "###" /* Mogo has "X" */,
99 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
101 /* Hashtable: 2*8 bits (ignore middle point, 2 bits per intersection) */
102 /* Value: 0: no pattern, 1: black pattern, 2: white pattern, 3: both patterns */
103 static char moggy_patterns[65536];
105 static void
106 _record_pattern(char *table, char *str, int pat, int fixed_color)
108 //fprintf(stderr, "[%s] %04x\n", str, pat);
110 /* Original color assignment */
111 table[pat] = fixed_color ? fixed_color : 3;
113 /* Reverse color assignment - achieved by swapping odd and even bits */
114 pat = ((pat >> 1) & 0x5555) | ((pat & 0x5555) << 1);
115 table[pat] = fixed_color ? 2 - (fixed_color == 2) : 3;
118 static int
119 _pat_vmirror(int pat)
121 /* V mirror pattern; reverse order of 3-2-3 chunks */
122 return ((pat & 0xfc00) >> 10) | (pat & 0x03c0) | ((pat & 0x003f) << 10);
125 static int
126 _pat_hmirror(int pat)
128 /* H mirror pattern; reverse order of 2-bit values within the chunks */
129 #define rev3(p) ((p >> 4) | (p & 0xc) | ((p & 0x3) << 4))
130 #define rev2(p) ((p >> 2) | ((p & 0x3) << 2))
131 return (rev3((pat & 0xfc00) >> 10) << 10)
132 | (rev2((pat & 0x03c0) >> 6) << 6)
133 | rev3((pat & 0x003f));
134 #undef rev3
135 #undef rev2
138 static int
139 _pat_90rot(int pat)
141 /* Rotate by 90 degrees:
142 * 5 6 7 7 4 2
143 * 3 4 -> 6 1
144 * 0 1 2 5 3 0 */
145 /* I'm too lazy to optimize this :) */
146 int vals[8];
147 for (int i = 0; i < 8; i++)
148 vals[i] = (pat >> (i * 2)) & 0x3;
149 int vals2[8];
150 vals2[0] = vals[5]; vals2[1] = vals[3]; vals2[2] = vals[0];
151 vals2[3] = vals[6]; vals2[4] = vals[1];
152 vals2[5] = vals[7]; vals2[6] = vals[4]; vals2[7] = vals[2];
153 int p2 = 0;
154 for (int i = 0; i < 8; i++)
155 p2 |= vals2[i] << (i * 2);
156 return p2;
159 static void
160 _gen_pattern(char *table, int pat, char *src, int srclen, int fixed_color)
162 for (; srclen > 0; src++, srclen--) {
163 if (srclen == 5)
164 continue;
165 int patofs = (srclen > 5 ? srclen - 1 : srclen) - 1;
166 switch (*src) {
167 case '?':
168 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
169 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
170 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
171 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
172 *src = '?'; // for future recursions
173 return;
174 case 'x':
175 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
176 *src = 'O'; _gen_pattern(table, pat, src, srclen, fixed_color);
177 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
178 *src = 'x'; // for future recursions
179 return;
180 case 'o':
181 *src = '.'; _gen_pattern(table, pat, src, srclen, fixed_color);
182 *src = 'X'; _gen_pattern(table, pat, src, srclen, fixed_color);
183 *src = '#'; _gen_pattern(table, pat, src, srclen, fixed_color);
184 *src = 'o'; // for future recursions
185 return;
186 case '.': /* 0 */ break;
187 case 'X': pat |= S_BLACK << (patofs * 2); break;
188 case 'O': pat |= S_WHITE << (patofs * 2); break;
189 case '#': pat |= S_OFFBOARD << (patofs * 2); break;
193 /* Original pattern, all transpositions and rotations */
194 _record_pattern(table, src - 9, pat, fixed_color);
195 _record_pattern(table, src - 9, _pat_vmirror(pat), fixed_color);
196 _record_pattern(table, src - 9, _pat_hmirror(pat), fixed_color);
197 _record_pattern(table, src - 9, _pat_vmirror(_pat_hmirror(pat)), fixed_color);
198 _record_pattern(table, src - 9, _pat_90rot(pat), fixed_color);
199 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(pat)), fixed_color);
200 _record_pattern(table, src - 9, _pat_90rot(_pat_hmirror(pat)), fixed_color);
201 _record_pattern(table, src - 9, _pat_90rot(_pat_vmirror(_pat_hmirror(pat))), fixed_color);
204 static void __attribute__((constructor))
205 _init_patterns(void)
207 for (int i = 0; i < moggy_patterns_src_n; i++) {
208 int fixed_color = 0;
209 switch (moggy_patterns_src[i][9]) {
210 case 'X': fixed_color = S_BLACK; break;
211 case 'O': fixed_color = S_WHITE; break;
213 _gen_pattern(moggy_patterns, 0, moggy_patterns_src[i], 9, fixed_color);
218 /* Check if we match any pattern centered on given move. */
219 static bool
220 test_pattern_here(struct playout_policy *p, char *hashtable,
221 struct board *b, struct move *m)
223 int pat = 0;
224 int x = coord_x(m->coord, b), y = coord_y(m->coord, b);
225 pat |= (board_atxy(b, x - 1, y - 1) << 14)
226 | (board_atxy(b, x, y - 1) << 12)
227 | (board_atxy(b, x + 1, y - 1) << 10);
228 pat |= (board_atxy(b, x - 1, y) << 8)
229 | (board_atxy(b, x + 1, y) << 6);
230 pat |= (board_atxy(b, x - 1, y + 1) << 4)
231 | (board_atxy(b, x, y + 1) << 2)
232 | (board_atxy(b, x + 1, y + 1));
233 //fprintf(stderr, "(%d,%d) hashtable[%04x] = %d\n", x, y, pat, hashtable[pat]);
234 return (hashtable[pat] & m->color);
237 static void
238 apply_pattern_here(struct playout_policy *p, char *hashtable,
239 struct board *b, struct move *m, struct move_queue *q)
241 if (test_pattern_here(p, hashtable, b, m)
242 && !is_selfatari(b, m->color, m->coord))
243 q->move[q->moves++] = m->coord;
246 /* Check if we match any pattern around given move (with the other color to play). */
247 static coord_t
248 apply_pattern(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
250 //struct moggy_policy *pp = p->data;
251 struct move_queue q;
252 q.moves = 0;
254 /* Suicides do not make any patterns and confuse us. */
255 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
256 return pass;
258 // FIXME: Fix assess callers
259 foreach_neighbor(b, m->coord, {
260 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
261 if (board_at(b, c) == S_NONE)
262 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
264 foreach_diag_neighbor(b, m->coord) {
265 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
266 if (board_at(b, c) == S_NONE)
267 apply_pattern_here(p, moggy_patterns, b, &m2, &q);
268 } foreach_diag_neighbor_end;
270 if (PLDEBUGL(5)) {
271 fprintf(stderr, "Pattern candidate moves: ");
272 for (int i = 0; i < q.moves; i++) {
273 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
275 fprintf(stderr, "\n");
278 if (testmove) {
279 while (q.moves--)
280 if (q.move[q.moves] == testmove->coord) {
281 if (PLDEBUGL(5))
282 fprintf(stderr, "Found queried move.\n");
283 return testmove->coord;
285 return pass;
288 int i = fast_random(q.moves);
289 return q.moves ? q.move[i] : pass;
294 /* Is this ladder breaker friendly for the one who catches ladder. */
295 static bool
296 ladder_catcher(struct board *b, int x, int y, enum stone laddered)
298 enum stone breaker = board_atxy(b, x, y);
299 return breaker == stone_other(laddered) || breaker == S_OFFBOARD;
302 static bool
303 ladder_catches(struct playout_policy *p, struct board *b, coord_t coord, group_t laddered)
305 struct moggy_policy *pp = p->data;
307 /* This is very trivial and gets a lot of corner cases wrong.
308 * We need this to be just very fast. One important point is
309 * that we sometimes might not notice a ladder but if we do,
310 * it should always work; thus we can use this for strong
311 * negative hinting safely. */
312 //fprintf(stderr, "ladder check\n");
314 enum stone lcolor = board_at(b, group_base(laddered));
315 int x = coord_x(coord, b), y = coord_y(coord, b);
317 /* First, special-case first-line "ladders". This is a huge chunk
318 * of ladders we actually meet and want to play. */
319 if (pp->borderladders
320 && neighbor_count_at(b, coord, S_OFFBOARD) == 1
321 && neighbor_count_at(b, coord, lcolor) == 1) {
322 if (PLDEBUGL(5))
323 fprintf(stderr, "border ladder\n");
324 /* Direction along border; xd is horiz. border, yd vertical. */
325 int xd = 0, yd = 0;
326 if (board_atxy(b, x + 1, y) == S_OFFBOARD || board_atxy(b, x - 1, y) == S_OFFBOARD)
327 yd = 1;
328 else
329 xd = 1;
330 /* Direction from the border; -1 is above/left, 1 is below/right. */
331 int dd = (board_atxy(b, x + yd, y + xd) == S_OFFBOARD) ? 1 : -1;
332 if (PLDEBUGL(6))
333 fprintf(stderr, "xd %d yd %d dd %d\n", xd, yd, dd);
334 /* | ? ?
335 * | . O #
336 * | c X #
337 * | . O #
338 * | ? ? */
339 /* This is normally caught, unless we have friends both above
340 * and below... */
341 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor
342 && board_atxy(b, x - xd * 2, y - yd * 2) == lcolor)
343 return false;
344 /* ...or can't block where we need because of shortage
345 * of liberties. */
346 int libs1 = board_group_info(b, group_atxy(b, x + xd - yd * dd, y + yd - xd * dd)).libs;
347 int libs2 = board_group_info(b, group_atxy(b, x - xd - yd * dd, y - yd - xd * dd)).libs;
348 if (PLDEBUGL(6))
349 fprintf(stderr, "libs1 %d libs2 %d\n", libs1, libs2);
350 if (libs1 < 2 && libs2 < 2)
351 return false;
352 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor && libs1 < 3)
353 return false;
354 if (board_atxy(b, x - xd * 2, y - yd * 2) == lcolor && libs2 < 3)
355 return false;
356 return true;
359 /* Figure out the ladder direction */
360 int xd, yd;
361 xd = board_atxy(b, x + 1, y) == S_NONE ? 1 : board_atxy(b, x - 1, y) == S_NONE ? -1 : 0;
362 yd = board_atxy(b, x, y + 1) == S_NONE ? 1 : board_atxy(b, x, y - 1) == S_NONE ? -1 : 0;
364 /* We do only tight ladders, not loose ladders. Furthermore,
365 * the ladders need to be simple:
366 * . X . . . X
367 * c O X supported . c O unsupported
368 * X # # X O #
371 /* For given (xd,yd), we have two possibilities where to move
372 * next. Consider (-1,1):
373 * n X . n c X
374 * c O X X O #
375 * X # # . X #
377 if (!xd || !yd || !(ladder_catcher(b, x - xd, y, lcolor) ^ ladder_catcher(b, x, y - yd, lcolor))) {
378 /* Silly situation, probably non-simple ladder or suicide. */
379 /* TODO: In case of basic non-simple ladder, play out both variants. */
380 if (PLDEBUGL(5))
381 fprintf(stderr, "non-simple ladder\n");
382 return false;
385 #define ladder_check(xd1_, yd1_, xd2_, yd2_) \
386 if (board_atxy(b, x, y) != S_NONE) { \
387 /* Did we hit a stone when playing out ladder? */ \
388 if (ladder_catcher(b, x, y, lcolor)) \
389 return true; /* ladder works */ \
390 if (board_group_info(b, group_atxy(b, x, y)).lib[0] > 0) \
391 return false; /* friend that's not in atari himself */ \
392 } else { \
393 /* No. So we are at new position. \
394 * We need to check indirect ladder breakers. */ \
395 /* . 2 x . . \
396 * . x o O 1 <- only at O we can check for o at 2 \
397 * x o o x . otherwise x at O would be still deadly \
398 * o o x . . \
399 * We check for o and x at 1, these are vital. \
400 * We check only for o at 2; x at 2 would mean we \
401 * need to fork (one step earlier). */ \
402 enum stone s1 = board_atxy(b, x + (xd1_), y + (yd1_)); \
403 if (s1 == lcolor) return false; \
404 if (s1 == stone_other(lcolor)) return true; \
405 enum stone s2 = board_atxy(b, x + (xd2_), y + (yd2_)); \
406 if (s2 == lcolor) return false; \
408 #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)
409 #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)
411 if (ladder_catcher(b, x - xd, y, lcolor))
412 ladder_horiz;
413 do {
414 ladder_vert;
415 ladder_horiz;
416 } while (1);
420 static void
421 group_atari_check(struct playout_policy *p, struct board *b, group_t group, struct move_queue *q)
423 struct moggy_policy *pp = p->data;
424 enum stone color = board_at(b, group_base(group));
425 coord_t lib = board_group_info(b, group).lib[0];
427 assert(color != S_OFFBOARD && color != S_NONE);
428 if (PLDEBUGL(5))
429 fprintf(stderr, "atariiiiiiiii %s of color %d\n", coord2sstr(lib, b), color);
430 assert(board_at(b, lib) == S_NONE);
432 /* Can we capture some neighbor? */
433 foreach_in_group(b, group) {
434 foreach_neighbor(b, c, {
435 if (board_at(b, c) != stone_other(color)
436 || board_group_info(b, group_at(b, c)).libs > 1)
437 continue;
438 if (PLDEBUGL(6))
439 fprintf(stderr, "can capture group %d\n", group_at(b, c));
440 /* If we are saving our group, capture! */
441 if (b->last_move.color == stone_other(color))
442 q->move[q->moves++] = board_group_info(b, group_at(b, c)).lib[0];
443 else /* If we chase the group, capture it now! */
444 q->move[q->moves++] = lib;
445 /* Make sure capturing the group will actually
446 * expand our liberties if we are filling our
447 * last liberty. */
448 if (q->move[q->moves - 1] == lib && is_selfatari(b, color, lib))
449 q->moves--;
450 else
451 mq_nodup(q);
453 } foreach_in_group_end;
455 /* Do not suicide... */
456 if (!is_selfatari(b, color, lib))
457 return;
458 if (PLDEBUGL(6))
459 fprintf(stderr, "...escape route valid\n");
461 /* ...or play out ladders. */
462 if (pp->ladders && ladder_catches(p, b, lib, group)) {
463 return;
465 if (PLDEBUGL(6))
466 fprintf(stderr, "...no ladder\n");
468 q->move[q->moves++] = lib;
469 mq_nodup(q);
472 static coord_t
473 global_atari_check(struct playout_policy *p, struct board *b)
475 struct move_queue q;
476 q.moves = 0;
478 if (b->clen == 0)
479 return pass;
481 int g_base = fast_random(b->clen);
482 for (int g = g_base; g < b->clen; g++) {
483 group_atari_check(p, b, group_at(b, group_base(b->c[g])), &q);
484 if (q.moves > 0)
485 return q.move[fast_random(q.moves)];
487 for (int g = 0; g < g_base; g++) {
488 group_atari_check(p, b, group_at(b, group_base(b->c[g])), &q);
489 if (q.moves > 0)
490 return q.move[fast_random(q.moves)];
492 return pass;
495 static coord_t
496 local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
498 struct move_queue q;
499 q.moves = 0;
501 /* Did the opponent play a self-atari? */
502 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
503 group_atari_check(p, b, group_at(b, m->coord), &q);
506 foreach_neighbor(b, m->coord, {
507 group_t g = group_at(b, c);
508 if (!g || board_group_info(b, g).libs != 1)
509 continue;
510 group_atari_check(p, b, g, &q);
513 if (PLDEBUGL(5)) {
514 fprintf(stderr, "Local atari candidate moves: ");
515 for (int i = 0; i < q.moves; i++) {
516 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
518 fprintf(stderr, "\n");
521 if (testmove) {
522 while (q.moves--)
523 if (q.move[q.moves] == testmove->coord) {
524 if (PLDEBUGL(5))
525 fprintf(stderr, "Found queried move.\n");
526 return testmove->coord;
528 return pass;
531 int i = fast_random(q.moves);
532 return q.moves ? q.move[i] : pass;
535 coord_t
536 playout_moggy_choose(struct playout_policy *p, struct board *b, enum stone our_real_color)
538 struct moggy_policy *pp = p->data;
539 coord_t c;
541 if (PLDEBUGL(5))
542 board_print(b, stderr);
544 /* Local checks */
545 if (!is_pass(b->last_move.coord)) {
546 /* Local group in atari? */
547 if (pp->lcapturerate > fast_random(100)) {
548 c = local_atari_check(p, b, &b->last_move, NULL);
549 if (!is_pass(c))
550 return c;
553 /* Check for patterns we know */
554 if (pp->patternrate > fast_random(100)) {
555 c = apply_pattern(p, b, &b->last_move, NULL);
556 if (!is_pass(c))
557 return c;
561 /* Global checks */
563 /* Any groups in atari? */
564 if (pp->capturerate > fast_random(100)) {
565 c = global_atari_check(p, b);
566 if (!is_pass(c))
567 return c;
570 return pass;
573 float
574 playout_moggy_assess(struct playout_policy *p, struct board *b, struct move *m)
576 struct moggy_policy *pp = p->data;
578 if (is_pass(m->coord))
579 return NAN;
581 if (PLDEBUGL(5)) {
582 fprintf(stderr, "ASSESS of %s:\n", coord2sstr(m->coord, b));
583 board_print(b, stderr);
586 /* Are we dealing with atari? */
587 if (pp->lcapturerate > fast_random(100)) {
588 foreach_neighbor(b, m->coord, {
589 if (board_at(b, c) == S_NONE || board_at(b, c) == S_OFFBOARD)
590 continue;
591 struct move m2;
592 m2.coord = c; m2.color = stone_other(m->color);
593 if (local_atari_check(p, b, &m2, m) == m->coord)
594 return 1.0;
597 /* Assess ladders anywhere, local or not. */
598 if (pp->ladderassess) {
599 //fprintf(stderr, "ASSESS %s\n", coord2sstr(m->coord, b));
600 foreach_neighbor(b, m->coord, {
601 if (board_at(b, c) == S_NONE || board_at(b, c) == S_OFFBOARD)
602 continue;
603 group_t g = group_at(b, c);
604 if (board_group_info(b, g).libs != 1)
605 continue;
606 if (ladder_catches(p, b, m->coord, g))
607 return 0.0;
612 /* Pattern check */
613 if (pp->patternrate > fast_random(100)) {
614 if (test_pattern_here(p, moggy_patterns, b, m))
615 return 1.0;
618 return NAN;
622 struct playout_policy *
623 playout_moggy_init(char *arg)
625 struct playout_policy *p = calloc(1, sizeof(*p));
626 struct moggy_policy *pp = calloc(1, sizeof(*pp));
627 p->data = pp;
628 p->choose = playout_moggy_choose;
629 p->assess = playout_moggy_assess;
631 pp->lcapturerate = 90;
632 pp->capturerate = 90;
633 pp->patternrate = 90;
634 pp->ladders = pp->borderladders = true;
635 pp->ladderassess = true;
637 if (arg) {
638 char *optspec, *next = arg;
639 while (*next) {
640 optspec = next;
641 next += strcspn(next, ":");
642 if (*next) { *next++ = 0; } else { *next = 0; }
644 char *optname = optspec;
645 char *optval = strchr(optspec, '=');
646 if (optval) *optval++ = 0;
648 if (!strcasecmp(optname, "lcapturerate") && optval) {
649 pp->lcapturerate = atoi(optval);
650 } else if (!strcasecmp(optname, "capturerate") && optval) {
651 pp->capturerate = atoi(optval);
652 } else if (!strcasecmp(optname, "patternrate") && optval) {
653 pp->patternrate = atoi(optval);
654 } else if (!strcasecmp(optname, "ladders")) {
655 pp->ladders = optval && *optval == '0' ? false : true;
656 } else if (!strcasecmp(optname, "borderladders")) {
657 pp->borderladders = optval && *optval == '0' ? false : true;
658 } else if (!strcasecmp(optname, "ladderassess")) {
659 pp->ladderassess = optval && *optval == '0' ? false : true;
660 } else {
661 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
666 return p;