Moggy 3x3: Have two color variants instead of color adjustment during matching
[pachi/peepo.git] / playout / moggy.c
blob4c4e474e357fcb12f657e498e4438844bb25be65
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, localassess, ladderassess, borderladders;
18 int lcapturerate, capturerate, patternrate;
19 /* These are relative to patternrate. */
20 int hanerate, cut1rate, cut2rate;
23 #define MQL 64
24 struct move_queue {
25 int moves;
26 coord_t move[MQL];
30 /* Pattern encoding:
31 * X: black; O: white; .: empty; #: edge
32 * x: !black; o: !white; ?: any
34 * X in the middle: pattern valid only for one side;
35 * otherwise, middle ignored. */
37 static char mogo_patterns_src[][10] = {
38 /* hane pattern - enclosing hane */
39 "XOX"
40 "..."
41 "???",
42 /* hane pattern - non-cutting hane */
43 "XO."
44 "..."
45 "?.?",
46 /* hane pattern - magari */
47 "XO?"
48 "X.."
49 "?.?",
50 /* hane pattern - thin hane (SUSPICIOUS) */
51 "XOO"
52 ".X."
53 "?.?",
54 /* cut1 pattern (kiri) - unprotected cut */
55 "XO?"
56 "O.o"
57 "?o?",
58 /* cut1 pattern (kiri) - peeped cut */
59 "XO?"
60 "O.X"
61 "???",
62 /* cut2 pattern (de) */
63 "?X?"
64 "O.O"
65 "ooo",
66 /* side pattern - chase */
67 "X.?"
68 "O.?"
69 "##?",
70 /* side pattern - weirdness (SUSPICIOUS) */
71 "?X?"
72 "X.O"
73 "###",
74 /* side pattern - sagari (SUSPICIOUS) */
75 "?XO"
76 "?X?" /* "?Xx" ? */
77 "###",
78 /* side pattern - weirdcut (SUSPICIOUS) */
79 "?XO"
80 "?OX"
81 "?##",
82 /* side pattern - cut (SUSPICIOUS) */
83 "?XO"
84 "OOX" /* "O.X" ? */
85 "###",
87 #define mogo_patterns_src_n sizeof(mogo_patterns_src) / sizeof(mogo_patterns_src[0])
89 static char mogo_patterns[mogo_patterns_src_n * 8][10];
90 #define mogo_patterns_n sizeof(mogo_patterns) / sizeof(mogo_patterns[0])
92 static void __attribute__((constructor))
93 _init_patterns(void)
95 for (int i = 0; i < mogo_patterns_src_n; i++)
97 int j;
98 strcpy(mogo_patterns[i], mogo_patterns_src[i]);
100 /* Transpositions: */
102 /* reverse */
103 for (j = 8; j >= 0; j--) {
104 mogo_patterns[i + mogo_patterns_src_n][8 - j] = mogo_patterns[i][j];
105 //printf("%s - %s\n", mogo_patterns[i], mogo_patterns[i + mogo_patterns_src_n]);
107 mogo_patterns[i + mogo_patterns_src_n][9] = 0;
109 /* reverse triplets */
110 for (j = 2; j >= 0; j--)
111 memcpy(&mogo_patterns[i + mogo_patterns_src_n * 2][6 - j * 3], &mogo_patterns[i][j * 3], 3);
112 mogo_patterns[i + mogo_patterns_src_n * 2][9] = 0;
114 /* reverse reverse triplets */
115 for (j = 2; j >= 0; j--)
116 memcpy(&mogo_patterns[i + mogo_patterns_src_n * 3][6 - j * 3], &mogo_patterns[i + mogo_patterns_src_n][j * 3], 3);
117 mogo_patterns[i + mogo_patterns_src_n * 3][9] = 0;
120 /* Now, swap colors: */
121 for (int i = 0; i < mogo_patterns_src_n * 4; i++)
123 for (int j = 0; j < 10; j++) {
124 switch (mogo_patterns[i][j]) {
125 case 'X': mogo_patterns[mogo_patterns_src_n * 4 + i][j] = 'O'; break;
126 case 'x': mogo_patterns[mogo_patterns_src_n * 4 + i][j] = 'o'; break;
127 case 'O': mogo_patterns[mogo_patterns_src_n * 4 + i][j] = 'X'; break;
128 case 'o': mogo_patterns[mogo_patterns_src_n * 4 + i][j] = 'x'; break;
129 default: mogo_patterns[mogo_patterns_src_n * 4 + i][j] = mogo_patterns[i][j]; break;
134 #if 0
135 for (int i = 0; i < mogo_patterns_n; i++) {
136 printf("%s\n", mogo_patterns[i]);
138 #endif
142 #if 0
144 static bool
145 cut1_test_cut(struct playout_policy *p, struct board *b, coord_t base, coord_t cut)
147 /* X O ?
148 * O(.)? X is base, (.) is cut
149 * ? ? ? */
150 int x = coord_x(base, b), y = coord_y(base, b);
151 enum stone color = board_at(b, base);
152 int xc = coord_x(cut, b), yc = coord_y(cut, b);
154 if (PLDEBUGL(5))
155 fprintf(stderr, "Probing CUT1 %s -> %s\n", coord2sstr(base, b), coord2sstr(cut, b));
157 /* Kosumi available. Is it cutting? */
158 if (board_atxy(b, x, yc) != stone_other(color)
159 || board_atxy(b, xc, y) != stone_other(color))
160 return false;
162 /* It is! Is the cut protected? */
163 enum stone fix1 = board_atxy(b, xc, yc - (y - yc));
164 enum stone fix2 = board_atxy(b, xc - (x - xc), yc);
165 if (PLDEBUGL(6))
166 fprintf(stderr, "Protection check: %d,%d\n", fix1, fix2);
167 if ((fix1 == stone_other(color) || fix1 == S_OFFBOARD) && fix2 != color)
168 return false;
169 if ((fix2 == stone_other(color) || fix2 == S_OFFBOARD) && fix1 != color)
170 return false;
172 /* Unprotected cut. Feast! */
173 if (PLDEBUGL(6))
174 fprintf(stderr, "Passed.\n");
175 return true;
178 static void
179 cut1_test(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q)
181 coord_t coord = m->coord;
182 int x = coord_x(coord, b), y = coord_y(coord, b);
183 enum stone color = board_at(b, coord);
185 /* Either last move was cutting threat... */
186 foreach_diag_neighbor(b, coord) {
187 if (board_at(b, c) != S_NONE)
188 continue;
189 /* X O ?
190 * O(.)? X is coord, (.) is c
191 * ? ? ? */
192 if (cut1_test_cut(p, b, coord, c))
193 q->move[q->moves++] = c;
194 } foreach_diag_neighbor_end;
196 /* ...or a cuttable hane. */
197 #define cutbase_test(xb_, yb_) \
198 base = coord_xy_otf(xb_, yb_, b); \
199 if (board_at(b, base) == stone_other(color)) \
200 if (cut1_test_cut(p, b, base, c)) \
201 q->move[q->moves++] = c;
202 foreach_neighbor(b, coord, {
203 if (board_at(b, c) != S_NONE)
204 continue;
205 /* X O ?
206 * O(.)? O is coord, (.) is c
207 * ? ? ? */
208 /* Check for X on both sides of O. */
209 int xc = coord_x(c, b);
210 int yc = coord_y(c, b);
211 coord_t base;
212 /* Either x == xc or y == yc. */
213 cutbase_test(x - (yc - y), y - (xc - x));
214 cutbase_test(x + (yc - y), y + (xc - x));
216 #undef cutbase_test
219 static bool
220 cut2_test_cut(struct playout_policy *p, struct board *b, coord_t base, coord_t cut)
222 /* ? X ?
223 * O(.)O X is base, (.) is cut
224 * ? ? ? */
225 int x = coord_x(base, b), y = coord_y(base, b);
226 enum stone color = board_at(b, base);
227 int xc = coord_x(cut, b), yc = coord_y(cut, b);
229 if (PLDEBUGL(5))
230 fprintf(stderr, "Probing CUT2 %s -> %s\n", coord2sstr(base, b), coord2sstr(cut, b));
232 /* Nobi available. Is it cutting? */
233 if (board_atxy(b, xc - (yc - y), yc - (xc - x)) != stone_other(color)
234 || board_atxy(b, xc + (yc - y), yc + (xc - x)) != stone_other(color))
235 return false;
237 /* It is! Is the cut protected? */
238 enum stone ocolor = stone_other(color);
239 if (x == xc) {
240 if (PLDEBUGL(6))
241 fprintf(stderr, "Protection check - row [%d,%d].\n", xc, yc + (yc - y));
242 if (board_atxy(b, xc - 1, yc + (yc - y)) == ocolor
243 || board_atxy(b, xc, yc + (yc - y)) == ocolor
244 || board_atxy(b, xc + 1, yc + (yc - y)) == ocolor)
245 return false;
246 } else {
247 if (PLDEBUGL(6))
248 fprintf(stderr, "Protection check - column [%d,%d].\n", xc + (xc - x), yc);
249 if (board_atxy(b, xc + (xc - x), yc - 1) == ocolor
250 || board_atxy(b, xc + (xc - x), yc) == ocolor
251 || board_atxy(b, xc + (xc - x), yc + 1) == ocolor)
252 return false;
255 /* Unprotected cut. Feast! */
256 if (PLDEBUGL(6))
257 fprintf(stderr, "Passed.\n");
258 return true;
261 static void
262 cut2_test(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q)
264 coord_t coord = m->coord;
265 int x = coord_x(coord, b), y = coord_y(coord, b);
266 enum stone color = board_at(b, coord);
268 /* Either last move was cutting threat... */
269 foreach_neighbor(b, coord, {
270 if (board_at(b, c) != S_NONE)
271 continue;
272 /* ? X ?
273 * O(.)O X is coord, (.) is c
274 * ? ? ? */
275 if (cut2_test_cut(p, b, coord, c))
276 q->move[q->moves++] = c;
279 /* ...or a cuttable ikken tobi. */
280 #define cutbase_test(xb_, yb_) \
281 base = coord_xy_otf(xb_, yb_, b); \
282 if (board_at(b, base) == stone_other(color)) \
283 if (cut2_test_cut(p, b, base, c)) \
284 q->move[q->moves++] = c;
285 foreach_neighbor(b, coord, {
286 if (board_at(b, c) != S_NONE)
287 continue;
288 /* ? X ?
289 * O(.)O O is coord, (.) is c
290 * ? ? ? */
291 /* Check for X on both sides of (.). */
292 int xc = coord_x(c, b);
293 int yc = coord_y(c, b);
294 coord_t base;
295 /* Either x == xc or y == yc. */
296 cutbase_test(xc - (yc - y), yc - (xc - x));
297 cutbase_test(xc + (yc - y), yc + (xc - x));
299 #undef cutbase_test
301 #endif
304 /*** OPTIMIZE ME ***/
306 /* Check if we match a certain pattern transposition centered on given move. */
307 static void
308 apply_one_pattern_here(struct playout_policy *p, char pattern[10],
309 struct board *b, struct move *m, struct move_queue *q)
311 switch (pattern[4]) {
312 case 'X': case 'o': if (m->color != S_BLACK) return; break;
313 case 'O': case 'x': if (m->color != S_WHITE) return; break;
315 for (int y = 0; y < 3; y++) {
316 for (int x = 0; x < 3; x++) {
317 if (x == 1 && y == 1)
318 continue;
319 enum stone color = board_at(b, m->coord + x - 1 + (y - 1) * board_size(b));
320 switch (pattern[x + 3 * y]) {
321 case '?': continue;
322 case '.': if (color != S_NONE) return; break;
323 case '#': if (color != S_OFFBOARD) return; break;
324 case 'X': if (color != S_BLACK) return; break;
325 case 'O': if (color != S_WHITE) return; break;
326 case 'x': if (color == S_BLACK) return; break;
327 case 'o': if (color == S_WHITE) return; break;
331 q->move[q->moves++] = m->coord;
334 /* Check if we match any pattern centered on given move. */
335 static void
336 apply_pattern_here(struct playout_policy *p, char patternset[][10], int patterns,
337 struct board *b, struct move *m, struct move_queue *q)
339 for (int i = 0; i < patterns; i++)
340 apply_one_pattern_here(p, patternset[i], b, m, q);
343 /* Check if we match any pattern around given move (with the other color to play). */
344 static coord_t
345 apply_pattern(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
347 //struct moggy_policy *pp = p->data;
348 struct move_queue q;
349 q.moves = 0;
351 /* Suicides do not make any patterns and confuse us. */
352 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
353 return pass;
355 #if 0
356 if (pp->hanerate > fast_random(100)) {
357 /* TODO */
360 if (pp->cut1rate > fast_random(100)) {
361 cut1_test(p, b, m, &q);
364 if (pp->cut2rate > fast_random(100)) {
365 cut2_test(p, b, m, &q);
367 #endif
368 // FIXME: Fix assess callers
369 foreach_neighbor(b, m->coord, {
370 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
371 if (board_at(b, c) == S_NONE)
372 apply_pattern_here(p, mogo_patterns, mogo_patterns_n, b, &m2, &q);
374 foreach_diag_neighbor(b, m->coord) {
375 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
376 if (board_at(b, c) == S_NONE)
377 apply_pattern_here(p, mogo_patterns, mogo_patterns_n, b, &m2, &q);
378 } foreach_diag_neighbor_end;
380 if (0){//PLDEBUGL(5)) {
381 fprintf(stderr, "Pattern candidate moves: ");
382 for (int i = 0; i < q.moves; i++) {
383 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
385 fprintf(stderr, "\n");
388 if (testmove) {
389 while (q.moves--)
390 if (q.move[q.moves] == testmove->coord) {
391 if (PLDEBUGL(5))
392 fprintf(stderr, "Found queried move.\n");
393 return testmove->coord;
395 return pass;
398 int i = fast_random(q.moves);
399 return q.moves ? q.move[i] : pass;
404 /* Is this ladder breaker friendly for the one who catches ladder. */
405 static bool
406 ladder_catcher(struct board *b, int x, int y, enum stone laddered)
408 enum stone breaker = board_atxy(b, x, y);
409 return breaker == stone_other(laddered) || breaker == S_OFFBOARD;
412 static bool
413 ladder_catches(struct playout_policy *p, struct board *b, coord_t coord, group_t laddered)
415 struct moggy_policy *pp = p->data;
417 /* This is very trivial and gets a lot of corner cases wrong.
418 * We need this to be just very fast. One important point is
419 * that we sometimes might not notice a ladder but if we do,
420 * it should always work; thus we can use this for strong
421 * negative hinting safely. */
422 //fprintf(stderr, "ladder check\n");
424 enum stone lcolor = board_at(b, group_base(laddered));
425 int x = coord_x(coord, b), y = coord_y(coord, b);
427 /* First, special-case first-line "ladders". This is a huge chunk
428 * of ladders we actually meet and want to play. */
429 if (pp->borderladders
430 && neighbor_count_at(b, coord, S_OFFBOARD) == 1
431 && neighbor_count_at(b, coord, lcolor) == 1) {
432 if (PLDEBUGL(5))
433 fprintf(stderr, "border ladder\n");
434 /* Direction along border; xd is horiz. border, yd vertical. */
435 int xd = 0, yd = 0;
436 if (board_atxy(b, x + 1, y) == S_OFFBOARD || board_atxy(b, x - 1, y) == S_OFFBOARD)
437 yd = 1;
438 else
439 xd = 1;
440 /* Direction from the border; -1 is above/left, 1 is below/right. */
441 int dd = (board_atxy(b, x + yd, y + xd) == S_OFFBOARD) ? 1 : -1;
442 if (PLDEBUGL(6))
443 fprintf(stderr, "xd %d yd %d dd %d\n", xd, yd, dd);
444 /* | ? ?
445 * | . O #
446 * | c X #
447 * | . O #
448 * | ? ? */
449 /* This is normally caught, unless we have friends both above
450 * and below... */
451 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor
452 && board_atxy(b, x - xd * 2, y - yd * 2) == lcolor)
453 return false;
454 /* ...or can't block where we need because of shortage
455 * of liberties. */
456 int libs1 = board_group_info(b, group_atxy(b, x + xd - yd * dd, y + yd - xd * dd)).libs;
457 int libs2 = board_group_info(b, group_atxy(b, x - xd - yd * dd, y - yd - xd * dd)).libs;
458 if (PLDEBUGL(6))
459 fprintf(stderr, "libs1 %d libs2 %d\n", libs1, libs2);
460 if (libs1 < 2 && libs2 < 2)
461 return false;
462 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor && libs1 < 3)
463 return false;
464 if (board_atxy(b, x - xd * 2, y - yd * 2) == lcolor && libs2 < 3)
465 return false;
466 return true;
469 /* Figure out the ladder direction */
470 int xd, yd;
471 xd = board_atxy(b, x + 1, y) == S_NONE ? 1 : board_atxy(b, x - 1, y) == S_NONE ? -1 : 0;
472 yd = board_atxy(b, x, y + 1) == S_NONE ? 1 : board_atxy(b, x, y - 1) == S_NONE ? -1 : 0;
474 /* We do only tight ladders, not loose ladders. Furthermore,
475 * the ladders need to be simple:
476 * . X . . . X
477 * c O X supported . c O unsupported
478 * X # # X O #
481 /* For given (xd,yd), we have two possibilities where to move
482 * next. Consider (-1,1):
483 * n X . n c X
484 * c O X X O #
485 * X # # . X #
487 if (!xd || !yd || !(ladder_catcher(b, x - xd, y, lcolor) ^ ladder_catcher(b, x, y - yd, lcolor))) {
488 /* Silly situation, probably non-simple ladder or suicide. */
489 /* TODO: In case of basic non-simple ladder, play out both variants. */
490 if (PLDEBUGL(5))
491 fprintf(stderr, "non-simple ladder\n");
492 return false;
495 #define ladder_check(xd1_, yd1_, xd2_, yd2_) \
496 if (board_atxy(b, x, y) != S_NONE) { \
497 /* Did we hit a stone when playing out ladder? */ \
498 if (ladder_catcher(b, x, y, lcolor)) \
499 return true; /* ladder works */ \
500 if (board_group_info(b, group_atxy(b, x, y)).lib[0] > 0) \
501 return false; /* friend that's not in atari himself */ \
502 } else { \
503 /* No. So we are at new position. \
504 * We need to check indirect ladder breakers. */ \
505 /* . 2 x . . \
506 * . x o O 1 <- only at O we can check for o at 2 \
507 * x o o x . otherwise x at O would be still deadly \
508 * o o x . . \
509 * We check for o and x at 1, these are vital. \
510 * We check only for o at 2; x at 2 would mean we \
511 * need to fork (one step earlier). */ \
512 enum stone s1 = board_atxy(b, x + (xd1_), y + (yd1_)); \
513 if (s1 == lcolor) return false; \
514 if (s1 == stone_other(lcolor)) return true; \
515 enum stone s2 = board_atxy(b, x + (xd2_), y + (yd2_)); \
516 if (s2 == lcolor) return false; \
518 #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)
519 #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)
521 if (ladder_catcher(b, x - xd, y, lcolor))
522 ladder_horiz;
523 do {
524 ladder_vert;
525 ladder_horiz;
526 } while (1);
530 static coord_t
531 group_atari_check(struct playout_policy *p, struct board *b, group_t group)
533 struct moggy_policy *pp = p->data;
534 enum stone color = board_at(b, group_base(group));
535 coord_t lib = board_group_info(b, group).lib[0];
537 assert(color != S_OFFBOARD && color != S_NONE);
538 if (PLDEBUGL(5))
539 fprintf(stderr, "atariiiiiiiii %s of color %d\n", coord2sstr(lib, b), color);
540 assert(board_at(b, lib) == S_NONE);
542 /* Do not suicide... */
543 if (!valid_escape_route(b, color, lib))
544 goto caught;
545 if (PLDEBUGL(6))
546 fprintf(stderr, "...escape route valid\n");
548 /* ...or play out ladders. */
549 if (pp->ladders && ladder_catches(p, b, lib, group)) {
550 goto caught;
552 if (PLDEBUGL(6))
553 fprintf(stderr, "...no ladder\n");
555 return lib;
557 caught:
558 /* There is still hope - can't we capture some neighbor? */
559 foreach_in_group(b, group) {
560 foreach_neighbor(b, c, {
561 if (board_at(b, c) != stone_other(color)
562 || board_group_info(b, group_at(b, c)).libs > 1)
563 continue;
564 if (PLDEBUGL(6))
565 fprintf(stderr, "can capture group %d\n", group_at(b, c));
566 /* If we are saving our group, capture! */
567 if (b->last_move.color == stone_other(color))
568 return board_group_info(b, group_at(b, c)).lib[0];
569 /* If we chase the group, capture it now! */
570 return lib;
572 } foreach_in_group_end;
573 return pass;
576 static coord_t
577 global_atari_check(struct playout_policy *p, struct board *b)
579 if (b->clen == 0)
580 return pass;
582 int g_base = fast_random(b->clen);
583 for (int g = g_base; g < b->clen; g++) {
584 coord_t c = group_atari_check(p, b, group_at(b, group_base(b->c[g])));
585 if (!is_pass(c))
586 return c;
588 for (int g = 0; g < g_base; g++) {
589 coord_t c = group_atari_check(p, b, group_at(b, group_base(b->c[g])));
590 if (!is_pass(c))
591 return c;
593 return pass;
596 static coord_t
597 local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct move *testmove)
599 struct move_queue q;
600 q.moves = 0;
602 /* Did the opponent play a self-atari? */
603 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
604 coord_t l = group_atari_check(p, b, group_at(b, m->coord));
605 if (!is_pass(l))
606 q.move[q.moves++] = l;
609 foreach_neighbor(b, m->coord, {
610 group_t g = group_at(b, c);
611 if (!g || board_group_info(b, g).libs != 1)
612 continue;
613 coord_t l = group_atari_check(p, b, g);
614 if (!is_pass(l))
615 q.move[q.moves++] = l;
618 if (PLDEBUGL(5)) {
619 fprintf(stderr, "Local atari candidate moves: ");
620 for (int i = 0; i < q.moves; i++) {
621 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
623 fprintf(stderr, "\n");
626 if (testmove) {
627 while (q.moves--)
628 if (q.move[q.moves] == testmove->coord) {
629 if (PLDEBUGL(5))
630 fprintf(stderr, "Found queried move.\n");
631 return testmove->coord;
633 return pass;
636 int i = fast_random(q.moves);
637 return q.moves ? q.move[i] : pass;
640 coord_t
641 playout_moggy_choose(struct playout_policy *p, struct board *b, enum stone our_real_color)
643 struct moggy_policy *pp = p->data;
644 coord_t c;
646 if (PLDEBUGL(5))
647 board_print(b, stderr);
649 /* Local checks */
650 if (!is_pass(b->last_move.coord)) {
651 /* Local group in atari? */
652 if (pp->lcapturerate > fast_random(100)) {
653 c = local_atari_check(p, b, &b->last_move, NULL);
654 if (!is_pass(c))
655 return c;
658 /* Check for patterns we know */
659 if (pp->patternrate > fast_random(100)) {
660 c = apply_pattern(p, b, &b->last_move, NULL);
661 if (!is_pass(c))
662 return c;
666 /* Global checks */
668 /* Any groups in atari? */
669 if (pp->capturerate > fast_random(100)) {
670 c = global_atari_check(p, b);
671 if (!is_pass(c))
672 return c;
675 return pass;
678 float
679 playout_moggy_assess(struct playout_policy *p, struct board *b, struct move *m)
681 struct moggy_policy *pp = p->data;
683 if (is_pass(m->coord))
684 return NAN;
686 if (PLDEBUGL(5)) {
687 fprintf(stderr, "ASSESS of %s:\n", coord2sstr(m->coord, b));
688 board_print(b, stderr);
691 /* Are we dealing with atari? */
692 if (pp->lcapturerate > fast_random(100)) {
693 if (pp->localassess && !is_pass(b->last_move.coord)) {
694 if (local_atari_check(p, b, &b->last_move, m) == m->coord)
695 return 1.0;
696 } else {
697 foreach_neighbor(b, m->coord, {
698 struct move m2;
699 m2.coord = c; m2.color = stone_other(m->color);
700 if (local_atari_check(p, b, &m2, m) == m->coord)
701 return 1.0;
705 /* Assess ladders anywhere, local or not. */
706 /* In case we don't localassess, local_atari_check() will
707 * alaready do the job. */
708 if (!pp->localassess && pp->ladderassess) {
709 //fprintf(stderr, "ASSESS %s\n", coord2sstr(m->coord, b));
710 foreach_neighbor(b, m->coord, {
711 if (board_at(b, c) == S_NONE || board_at(b, c) == S_OFFBOARD)
712 continue;
713 group_t g = group_at(b, c);
714 if (board_group_info(b, g).libs != 1)
715 continue;
716 if (ladder_catches(p, b, m->coord, g))
717 return 0.0;
722 /* Pattern check */
723 if (pp->patternrate > fast_random(100)) {
724 if (pp->localassess && !is_pass(b->last_move.coord)) {
725 if (apply_pattern(p, b, &b->last_move, m) == m->coord)
726 return 1.0;
727 } else {
728 foreach_neighbor(b, m->coord, {
729 struct move m2;
730 m2.coord = c; m2.color = stone_other(m->color);
731 if (apply_pattern(p, b, &m2, m) == m->coord)
732 return 1.0;
734 foreach_diag_neighbor(b, m->coord) {
735 struct move m2;
736 m2.coord = c; m2.color = stone_other(m->color);
737 if (apply_pattern(p, b, &m2, m) == m->coord)
738 return 1.0;
739 } foreach_diag_neighbor_end;
743 return NAN;
747 struct playout_policy *
748 playout_moggy_init(char *arg)
750 struct playout_policy *p = calloc(1, sizeof(*p));
751 struct moggy_policy *pp = calloc(1, sizeof(*pp));
752 p->data = pp;
753 p->choose = playout_moggy_choose;
754 p->assess = playout_moggy_assess;
756 pp->lcapturerate = 75;
757 pp->capturerate = 75;
758 pp->patternrate = 75;
759 pp->hanerate = pp->cut1rate = pp->cut2rate = 75;
760 pp->ladders = pp->borderladders = true;
761 pp->ladderassess = true;
763 if (arg) {
764 char *optspec, *next = arg;
765 while (*next) {
766 optspec = next;
767 next += strcspn(next, ":");
768 if (*next) { *next++ = 0; } else { *next = 0; }
770 char *optname = optspec;
771 char *optval = strchr(optspec, '=');
772 if (optval) *optval++ = 0;
774 if (!strcasecmp(optname, "lcapturerate") && optval) {
775 pp->lcapturerate = atoi(optval);
776 } else if (!strcasecmp(optname, "capturerate") && optval) {
777 pp->capturerate = atoi(optval);
778 } else if (!strcasecmp(optname, "patternrate") && optval) {
779 pp->patternrate = atoi(optval);
780 } else if (!strcasecmp(optname, "hanerate") && optval) {
781 pp->hanerate = atoi(optval);
782 } else if (!strcasecmp(optname, "cut1rate") && optval) {
783 pp->cut1rate = atoi(optval);
784 } else if (!strcasecmp(optname, "cut2rate") && optval) {
785 pp->cut2rate = atoi(optval);
786 } else if (!strcasecmp(optname, "ladders")) {
787 pp->ladders = optval && *optval == '0' ? false : true;
788 } else if (!strcasecmp(optname, "borderladders")) {
789 pp->borderladders = optval && *optval == '0' ? false : true;
790 } else if (!strcasecmp(optname, "localassess")) {
791 pp->localassess = optval && *optval == '0' ? false : true;
792 } else if (!strcasecmp(optname, "ladderassess")) {
793 pp->ladderassess = optval && *optval == '0' ? false : true;
794 } else {
795 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
800 return p;