struct moggy_policy: Add thread-safety note
[pachi/json.git] / playout / moggy.c
blobe4fd79fad8f6a0b7d31a40f6e6fb54c115146c4e
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 /* Note that the context can be shared by multiple threads! */
18 struct moggy_policy {
19 bool ladders, ladderassess, borderladders;
20 int lcapturerate, capturerate, patternrate;
21 int selfatarirate;
23 /* Hashtable: 2*8 bits (ignore middle point, 2 bits per intersection) */
24 /* Value: 0: no pattern, 1: black pattern,
25 * 2: white pattern, 3: both patterns */
26 char patterns[65536];
29 #define MQL 64
30 struct move_queue {
31 int moves;
32 coord_t move[MQL];
35 static void
36 mq_nodup(struct move_queue *q)
38 if ((q->moves > 1 && q->move[q->moves - 2] == q->move[q->moves - 1])
39 || (q->moves > 2 && q->move[q->moves - 3] == q->move[q->moves - 1])
40 || (q->moves > 3 && q->move[q->moves - 4] == q->move[q->moves - 1]))
41 q->moves--;
45 /* Pattern encoding:
46 * X: black; O: white; .: empty; #: edge
47 * x: !black; o: !white; ?: any
49 * extra X: pattern valid only for one side;
50 * middle point ignored. */
52 static char moggy_patterns_src[][11] = {
53 /* hane pattern - enclosing hane */
54 "XOX"
55 "..."
56 "???",
57 /* hane pattern - non-cutting hane */
58 "XO."
59 "..."
60 "?.?",
61 /* hane pattern - magari */
62 "XO?"
63 "X.."
64 "x.?",
65 /* hane pattern - thin hane */
66 "XOO"
67 "..."
68 "?.?" "X",
69 /* generic pattern - katatsuke or diagonal attachment; similar to magari */
70 ".O."
71 "X.."
72 "...",
73 /* cut1 pattern (kiri) - unprotected cut */
74 "XO?"
75 "O.o"
76 "?o?",
77 /* cut1 pattern (kiri) - peeped cut */
78 "XO?"
79 "O.X"
80 "???",
81 /* cut2 pattern (de) */
82 "?X?"
83 "O.O"
84 "ooo",
85 /* cut keima (not in Mogo) */
86 "OX?"
87 "o.O"
88 "o??",
89 /* side pattern - chase */
90 "X.?"
91 "O.?"
92 "##?",
93 /* side pattern - weirdness (SUSPICIOUS) */
94 "?X?"
95 "X.O"
96 "###",
97 /* side pattern - sagari (SUSPICIOUS) */
98 "?XO"
99 "x.x" /* Mogo has "x.?" */
100 "###" /* Mogo has "X" */,
101 /* side pattern - weirdcut (SUSPICIOUS) */
102 #if 0
103 "?OX"
104 "?.O"
105 "?##" "X",
106 #endif
107 /* side pattern - cut (SUSPICIOUS) */
108 "?OX"
109 "X.O"
110 "###" /* Mogo has "X" */,
112 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
114 static void
115 pattern_record(char *table, char *str, int pat, int fixed_color)
117 /* Original color assignment */
118 table[pat] = fixed_color ? fixed_color : 3;
119 //fprintf(stderr, "[%s] %04x %d\n", str, pat, fixed_color);
121 /* Reverse color assignment - achieved by swapping odd and even bits */
122 pat = ((pat >> 1) & 0x5555) | ((pat & 0x5555) << 1);
123 table[pat] = fixed_color ? 2 - (fixed_color == 2) : 3;
124 //fprintf(stderr, "[%s] %04x %d\n", str, pat, fixed_color);
127 static int
128 pat_vmirror(int pat)
130 /* V mirror pattern; reverse order of 3-2-3 chunks */
131 return ((pat & 0xfc00) >> 10) | (pat & 0x03c0) | ((pat & 0x003f) << 10);
134 static int
135 pat_hmirror(int pat)
137 /* H mirror pattern; reverse order of 2-bit values within the chunks */
138 #define rev3(p) ((p >> 4) | (p & 0xc) | ((p & 0x3) << 4))
139 #define rev2(p) ((p >> 2) | ((p & 0x3) << 2))
140 return (rev3((pat & 0xfc00) >> 10) << 10)
141 | (rev2((pat & 0x03c0) >> 6) << 6)
142 | rev3((pat & 0x003f));
143 #undef rev3
144 #undef rev2
147 static int
148 pat_90rot(int pat)
150 /* Rotate by 90 degrees:
151 * 5 6 7 7 4 2
152 * 3 4 -> 6 1
153 * 0 1 2 5 3 0 */
154 /* I'm too lazy to optimize this :) */
155 int vals[8];
156 for (int i = 0; i < 8; i++)
157 vals[i] = (pat >> (i * 2)) & 0x3;
158 int vals2[8];
159 vals2[0] = vals[5]; vals2[1] = vals[3]; vals2[2] = vals[0];
160 vals2[3] = vals[6]; vals2[4] = vals[1];
161 vals2[5] = vals[7]; vals2[6] = vals[4]; vals2[7] = vals[2];
162 int p2 = 0;
163 for (int i = 0; i < 8; i++)
164 p2 |= vals2[i] << (i * 2);
165 return p2;
168 static void
169 pattern_gen(char *table, int pat, char *src, int srclen, int fixed_color)
171 for (; srclen > 0; src++, srclen--) {
172 if (srclen == 5)
173 continue;
174 int patofs = (srclen > 5 ? srclen - 1 : srclen) - 1;
175 switch (*src) {
176 case '?':
177 *src = '.'; pattern_gen(table, pat, src, srclen, fixed_color);
178 *src = 'X'; pattern_gen(table, pat, src, srclen, fixed_color);
179 *src = 'O'; pattern_gen(table, pat, src, srclen, fixed_color);
180 *src = '#'; pattern_gen(table, pat, src, srclen, fixed_color);
181 *src = '?'; // for future recursions
182 return;
183 case 'x':
184 *src = '.'; pattern_gen(table, pat, src, srclen, fixed_color);
185 *src = 'O'; pattern_gen(table, pat, src, srclen, fixed_color);
186 *src = '#'; pattern_gen(table, pat, src, srclen, fixed_color);
187 *src = 'x'; // for future recursions
188 return;
189 case 'o':
190 *src = '.'; pattern_gen(table, pat, src, srclen, fixed_color);
191 *src = 'X'; pattern_gen(table, pat, src, srclen, fixed_color);
192 *src = '#'; pattern_gen(table, pat, src, srclen, fixed_color);
193 *src = 'o'; // for future recursions
194 return;
195 case '.': /* 0 */ break;
196 case 'X': pat |= S_BLACK << (patofs * 2); break;
197 case 'O': pat |= S_WHITE << (patofs * 2); break;
198 case '#': pat |= S_OFFBOARD << (patofs * 2); break;
202 /* Original pattern, all transpositions and rotations */
203 pattern_record(table, src - 9, pat, fixed_color);
204 pattern_record(table, src - 9, pat_vmirror(pat), fixed_color);
205 pattern_record(table, src - 9, pat_hmirror(pat), fixed_color);
206 pattern_record(table, src - 9, pat_vmirror(pat_hmirror(pat)), fixed_color);
207 pattern_record(table, src - 9, pat_90rot(pat), fixed_color);
208 pattern_record(table, src - 9, pat_90rot(pat_vmirror(pat)), fixed_color);
209 pattern_record(table, src - 9, pat_90rot(pat_hmirror(pat)), fixed_color);
210 pattern_record(table, src - 9, pat_90rot(pat_vmirror(pat_hmirror(pat))), fixed_color);
213 #warning gcc is stupid; ignore following out-of-bounds warnings
215 static void
216 pattern_genall(struct playout_policy *p, char src[][11], int src_n)
218 struct moggy_policy *pp = p->data;
220 for (int i = 0; i < src_n; i++) {
221 //printf("<%s>\n", src[i]);
222 int fixed_color = 0;
223 switch (src[i][9]) {
224 case 'X': fixed_color = S_BLACK; break;
225 case 'O': fixed_color = S_WHITE; break;
227 //fprintf(stderr, "** %s **\n", src[i]);
228 pattern_gen(pp->patterns, 0, src[i], 9, fixed_color);
232 static bool
233 load_patterns(char src[][11], int src_n, char *filename)
235 FILE *f = fopen("moggy.patterns", "r");
236 if (!f) return false;
238 int i;
239 for (i = 0; i < moggy_patterns_src_n; i++) {
240 char line[32];
241 if (!fgets(line, sizeof(line), f))
242 goto error;
243 int l = strlen(line);
244 if (l != 10 + (line[l - 1] == '\n'))
245 goto error;
246 memcpy(src[i], line, 10);
248 fprintf(stderr, "moggy.patterns: %d patterns loaded\n", i);
249 fclose(f);
250 return true;
251 error:
252 fprintf(stderr, "Error loading moggy.patterns.\n");
253 fclose(f);
254 return false;
257 static void
258 init_patterns(struct playout_policy *p)
260 /* Replaces default patterns if the file is found, no-op otherwise. */
261 load_patterns(moggy_patterns_src, moggy_patterns_src_n, "moggy.patterns");
263 pattern_genall(p, moggy_patterns_src, moggy_patterns_src_n);
267 /* Check if we match any pattern centered on given move. */
268 static bool
269 test_pattern_here(struct playout_policy *p, char *hashtable,
270 struct board *b, struct move *m)
272 int pat = 0;
273 int x = coord_x(m->coord, b), y = coord_y(m->coord, b);
274 pat |= (board_atxy(b, x - 1, y - 1) << 14)
275 | (board_atxy(b, x, y - 1) << 12)
276 | (board_atxy(b, x + 1, y - 1) << 10);
277 pat |= (board_atxy(b, x - 1, y) << 8)
278 | (board_atxy(b, x + 1, y) << 6);
279 pat |= (board_atxy(b, x - 1, y + 1) << 4)
280 | (board_atxy(b, x, y + 1) << 2)
281 | (board_atxy(b, x + 1, y + 1));
282 //fprintf(stderr, "(%d,%d) hashtable[%04x] = %d\n", x, y, pat, hashtable[pat]);
283 return (hashtable[pat] & m->color) && !is_selfatari(b, m->color, m->coord);
286 static void
287 apply_pattern_here(struct playout_policy *p, char *hashtable,
288 struct board *b, struct move *m, struct move_queue *q)
290 if (test_pattern_here(p, hashtable, b, m))
291 q->move[q->moves++] = m->coord;
294 /* Check if we match any pattern around given move (with the other color to play). */
295 static coord_t
296 apply_pattern(struct playout_policy *p, struct board *b, struct move *m)
298 struct moggy_policy *pp = p->data;
299 struct move_queue q;
300 q.moves = 0;
302 /* Suicides do not make any patterns and confuse us. */
303 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
304 return pass;
306 foreach_neighbor(b, m->coord, {
307 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
308 if (board_at(b, c) == S_NONE)
309 apply_pattern_here(p, pp->patterns, b, &m2, &q);
311 foreach_diag_neighbor(b, m->coord) {
312 struct move m2; m2.coord = c; m2.color = stone_other(m->color);
313 if (board_at(b, c) == S_NONE)
314 apply_pattern_here(p, pp->patterns, b, &m2, &q);
315 } foreach_diag_neighbor_end;
317 if (PLDEBUGL(5)) {
318 fprintf(stderr, "Pattern candidate moves: ");
319 for (int i = 0; i < q.moves; i++) {
320 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
322 fprintf(stderr, "\n");
325 int i = fast_random(q.moves);
326 return q.moves ? q.move[i] : pass;
331 /* Is this ladder breaker friendly for the one who catches ladder. */
332 static bool
333 ladder_catcher(struct board *b, int x, int y, enum stone laddered)
335 enum stone breaker = board_atxy(b, x, y);
336 return breaker == stone_other(laddered) || breaker == S_OFFBOARD;
339 static bool
340 ladder_catches(struct playout_policy *p, struct board *b, coord_t coord, group_t laddered)
342 struct moggy_policy *pp = p->data;
344 /* This is very trivial and gets a lot of corner cases wrong.
345 * We need this to be just very fast. One important point is
346 * that we sometimes might not notice a ladder but if we do,
347 * it should always work; thus we can use this for strong
348 * negative hinting safely. */
349 //fprintf(stderr, "ladder check\n");
351 enum stone lcolor = board_at(b, group_base(laddered));
352 int x = coord_x(coord, b), y = coord_y(coord, b);
354 /* First, special-case first-line "ladders". This is a huge chunk
355 * of ladders we actually meet and want to play. */
356 if (pp->borderladders
357 && neighbor_count_at(b, coord, S_OFFBOARD) == 1
358 && neighbor_count_at(b, coord, lcolor) == 1) {
359 if (PLDEBUGL(5))
360 fprintf(stderr, "border ladder\n");
361 /* Direction along border; xd is horiz. border, yd vertical. */
362 int xd = 0, yd = 0;
363 if (board_atxy(b, x + 1, y) == S_OFFBOARD || board_atxy(b, x - 1, y) == S_OFFBOARD)
364 yd = 1;
365 else
366 xd = 1;
367 /* Direction from the border; -1 is above/left, 1 is below/right. */
368 int dd = (board_atxy(b, x + yd, y + xd) == S_OFFBOARD) ? 1 : -1;
369 if (PLDEBUGL(6))
370 fprintf(stderr, "xd %d yd %d dd %d\n", xd, yd, dd);
371 /* | ? ?
372 * | . O #
373 * | c X #
374 * | . O #
375 * | ? ? */
376 /* This is normally caught, unless we have friends both above
377 * and below... */
378 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor
379 && board_atxy(b, x - xd * 2, y - yd * 2) == lcolor)
380 return false;
381 /* ...or can't block where we need because of shortage
382 * of liberties. */
383 int libs1 = board_group_info(b, group_atxy(b, x + xd - yd * dd, y + yd - xd * dd)).libs;
384 int libs2 = board_group_info(b, group_atxy(b, x - xd - yd * dd, y - yd - xd * dd)).libs;
385 if (PLDEBUGL(6))
386 fprintf(stderr, "libs1 %d libs2 %d\n", libs1, libs2);
387 if (libs1 < 2 && libs2 < 2)
388 return false;
389 if (board_atxy(b, x + xd * 2, y + yd * 2) == lcolor && libs1 < 3)
390 return false;
391 if (board_atxy(b, x - xd * 2, y - yd * 2) == lcolor && libs2 < 3)
392 return false;
393 return true;
396 /* Figure out the ladder direction */
397 int xd, yd;
398 xd = board_atxy(b, x + 1, y) == S_NONE ? 1 : board_atxy(b, x - 1, y) == S_NONE ? -1 : 0;
399 yd = board_atxy(b, x, y + 1) == S_NONE ? 1 : board_atxy(b, x, y - 1) == S_NONE ? -1 : 0;
401 /* We do only tight ladders, not loose ladders. Furthermore,
402 * the ladders need to be simple:
403 * . X . . . X
404 * c O X supported . c O unsupported
405 * X # # X O #
408 /* For given (xd,yd), we have two possibilities where to move
409 * next. Consider (-1,1):
410 * n X . n c X
411 * c O X X O #
412 * X # # . X #
414 if (!xd || !yd || !(ladder_catcher(b, x - xd, y, lcolor) ^ ladder_catcher(b, x, y - yd, lcolor))) {
415 /* Silly situation, probably non-simple ladder or suicide. */
416 /* TODO: In case of basic non-simple ladder, play out both variants. */
417 if (PLDEBUGL(5))
418 fprintf(stderr, "non-simple ladder\n");
419 return false;
422 #define ladder_check(xd1_, yd1_, xd2_, yd2_) \
423 if (board_atxy(b, x, y) != S_NONE) { \
424 /* Did we hit a stone when playing out ladder? */ \
425 if (ladder_catcher(b, x, y, lcolor)) \
426 return true; /* ladder works */ \
427 if (board_group_info(b, group_atxy(b, x, y)).lib[0] > 0) \
428 return false; /* friend that's not in atari himself */ \
429 } else { \
430 /* No. So we are at new position. \
431 * We need to check indirect ladder breakers. */ \
432 /* . 2 x . . \
433 * . x o O 1 <- only at O we can check for o at 2 \
434 * x o o x . otherwise x at O would be still deadly \
435 * o o x . . \
436 * We check for o and x at 1, these are vital. \
437 * We check only for o at 2; x at 2 would mean we \
438 * need to fork (one step earlier). */ \
439 enum stone s1 = board_atxy(b, x + (xd1_), y + (yd1_)); \
440 if (s1 == lcolor) return false; \
441 if (s1 == stone_other(lcolor)) return true; \
442 enum stone s2 = board_atxy(b, x + (xd2_), y + (yd2_)); \
443 if (s2 == lcolor) return false; \
445 #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)
446 #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)
448 if (ladder_catcher(b, x - xd, y, lcolor))
449 ladder_horiz;
450 do {
451 ladder_vert;
452 ladder_horiz;
453 } while (1);
457 static void
458 group_atari_check(struct playout_policy *p, struct board *b, group_t group, struct move_queue *q)
460 struct moggy_policy *pp = p->data;
462 /* Do not bother with kos. */
463 if (group_is_onestone(b, group))
464 return;
466 enum stone color = board_at(b, group_base(group));
467 coord_t lib = board_group_info(b, group).lib[0];
469 assert(color != S_OFFBOARD && color != S_NONE);
470 if (PLDEBUGL(5))
471 fprintf(stderr, "atariiiiiiiii %s of color %d\n", coord2sstr(lib, b), color);
472 assert(board_at(b, lib) == S_NONE);
474 /* Can we capture some neighbor? */
475 foreach_in_group(b, group) {
476 foreach_neighbor(b, c, {
477 if (board_at(b, c) != stone_other(color)
478 || board_group_info(b, group_at(b, c)).libs > 1)
479 continue;
480 if (PLDEBUGL(6))
481 fprintf(stderr, "can capture group %d\n", group_at(b, c));
482 /* If we are saving our group, capture! */
483 if (b->last_move.color == stone_other(color))
484 q->move[q->moves++] = board_group_info(b, group_at(b, c)).lib[0];
485 else /* If we chase the group, capture it now! */
486 q->move[q->moves++] = lib;
487 /* Make sure capturing the group will actually
488 * expand our liberties if we are filling our
489 * last liberty. */
490 if (q->move[q->moves - 1] == lib && is_selfatari(b, color, lib))
491 q->moves--;
492 else
493 mq_nodup(q);
495 } foreach_in_group_end;
497 /* Do not suicide... */
498 if (is_selfatari(b, color, lib))
499 return;
500 if (PLDEBUGL(6))
501 fprintf(stderr, "...escape route valid\n");
503 /* ...or play out ladders. */
504 if (pp->ladders && ladder_catches(p, b, lib, group)) {
505 return;
507 if (PLDEBUGL(6))
508 fprintf(stderr, "...no ladder\n");
510 q->move[q->moves++] = lib;
511 mq_nodup(q);
514 static coord_t
515 global_atari_check(struct playout_policy *p, struct board *b)
517 struct move_queue q;
518 q.moves = 0;
520 if (b->clen == 0)
521 return pass;
523 int g_base = fast_random(b->clen);
524 for (int g = g_base; g < b->clen; g++) {
525 group_atari_check(p, b, group_at(b, group_base(b->c[g])), &q);
526 if (q.moves > 0)
527 return q.move[fast_random(q.moves)];
529 for (int g = 0; g < g_base; g++) {
530 group_atari_check(p, b, group_at(b, group_base(b->c[g])), &q);
531 if (q.moves > 0)
532 return q.move[fast_random(q.moves)];
534 return pass;
537 static coord_t
538 local_atari_check(struct playout_policy *p, struct board *b, struct move *m)
540 struct move_queue q;
541 q.moves = 0;
543 /* Did the opponent play a self-atari? */
544 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
545 group_atari_check(p, b, group_at(b, m->coord), &q);
548 foreach_neighbor(b, m->coord, {
549 group_t g = group_at(b, c);
550 if (!g || board_group_info(b, g).libs != 1)
551 continue;
552 group_atari_check(p, b, g, &q);
555 if (PLDEBUGL(5)) {
556 fprintf(stderr, "Local atari candidate moves: ");
557 for (int i = 0; i < q.moves; i++) {
558 fprintf(stderr, "%s ", coord2sstr(q.move[i], b));
560 fprintf(stderr, "\n");
563 int i = fast_random(q.moves);
564 return q.moves ? q.move[i] : pass;
567 coord_t
568 playout_moggy_choose(struct playout_policy *p, struct board *b, enum stone to_play)
570 struct moggy_policy *pp = p->data;
571 coord_t c;
573 if (PLDEBUGL(5))
574 board_print(b, stderr);
576 /* Local checks */
577 if (!is_pass(b->last_move.coord)) {
578 /* Local group in atari? */
579 if (pp->lcapturerate > fast_random(100)) {
580 c = local_atari_check(p, b, &b->last_move);
581 if (!is_pass(c))
582 return c;
585 /* Check for patterns we know */
586 if (pp->patternrate > fast_random(100)) {
587 c = apply_pattern(p, b, &b->last_move);
588 if (!is_pass(c))
589 return c;
593 /* Global checks */
595 /* Any groups in atari? */
596 if (pp->capturerate > fast_random(100)) {
597 c = global_atari_check(p, b);
598 if (!is_pass(c))
599 return c;
602 return pass;
605 float
606 playout_moggy_assess(struct playout_policy *p, struct board *b, struct move *m)
608 struct moggy_policy *pp = p->data;
610 if (is_pass(m->coord))
611 return NAN;
613 if (PLDEBUGL(5)) {
614 fprintf(stderr, "ASSESS of %s:\n", coord2sstr(m->coord, b));
615 board_print(b, stderr);
618 /* Are we dealing with atari? */
619 if (pp->lcapturerate || pp->capturerate) {
620 bool ladder = false;
622 foreach_neighbor(b, m->coord, {
623 group_t g = group_at(b, c);
624 if (!g || board_group_info(b, g).libs != 1)
625 continue;
627 /* _Never_ play here if this move plays out
628 * a caught ladder. (Unless it captures another
629 * group. :-) */
630 if (pp->ladderassess && ladder_catches(p, b, m->coord, g)) {
631 /* Note that the opposite is not guarded against;
632 * we do not advise against capturing a laddered
633 * group (but we don't encourage it either). Such
634 * a move can simplify tactical situations if we
635 * can afford it. */
636 if (m->color == board_at(b, c))
637 ladder = true;
638 continue;
641 struct move_queue q; q.moves = 0;
642 group_atari_check(p, b, g, &q);
643 while (q.moves--)
644 if (q.move[q.moves] == m->coord) {
645 if (PLDEBUGL(5))
646 fprintf(stderr, "1.0: atari\n");
647 return 1.0;
651 if (ladder) {
652 if (PLDEBUGL(5))
653 fprintf(stderr, "0.0: ladder\n");
654 return 0.0;
658 /* Pattern check */
659 if (pp->patternrate) {
660 if (test_pattern_here(p, pp->patterns, b, m)) {
661 if (PLDEBUGL(5))
662 fprintf(stderr, "1.0: pattern\n");
663 return 1.0;
667 return NAN;
670 bool
671 playout_moggy_permit(struct playout_policy *p, struct board *b, struct move *m)
673 struct moggy_policy *pp = p->data;
675 /* The idea is simple for now - never allow self-atari moves.
676 * They suck in general, but this also permits us to actually
677 * handle seki in the playout stage. */
678 /* FIXME: We must allow self-atari in some basic nakade shapes. */
679 #if 0
680 if (is_selfatari(b, m->color, m->coord))
681 fprintf(stderr, "__ Prohibiting self-atari %s %s\n", stone2str(m->color), coord2sstr(m->coord, b));
682 #endif
683 return fast_random(100) >= pp->selfatarirate || !is_selfatari(b, m->color, m->coord);
687 struct playout_policy *
688 playout_moggy_init(char *arg)
690 struct playout_policy *p = calloc(1, sizeof(*p));
691 struct moggy_policy *pp = calloc(1, sizeof(*pp));
692 p->data = pp;
693 p->choose = playout_moggy_choose;
694 p->assess = playout_moggy_assess;
695 p->permit = playout_moggy_permit;
697 pp->lcapturerate = 90;
698 pp->capturerate = 90;
699 pp->patternrate = 90;
700 pp->selfatarirate = 90;
701 pp->ladders = pp->borderladders = true;
702 pp->ladderassess = true;
704 if (arg) {
705 char *optspec, *next = arg;
706 while (*next) {
707 optspec = next;
708 next += strcspn(next, ":");
709 if (*next) { *next++ = 0; } else { *next = 0; }
711 char *optname = optspec;
712 char *optval = strchr(optspec, '=');
713 if (optval) *optval++ = 0;
715 if (!strcasecmp(optname, "lcapturerate") && optval) {
716 pp->lcapturerate = atoi(optval);
717 } else if (!strcasecmp(optname, "capturerate") && optval) {
718 pp->capturerate = atoi(optval);
719 } else if (!strcasecmp(optname, "patternrate") && optval) {
720 pp->patternrate = atoi(optval);
721 } else if (!strcasecmp(optname, "selfatarirate") && optval) {
722 pp->selfatarirate = atoi(optval);
723 } else if (!strcasecmp(optname, "ladders")) {
724 pp->ladders = optval && *optval == '0' ? false : true;
725 } else if (!strcasecmp(optname, "borderladders")) {
726 pp->borderladders = optval && *optval == '0' ? false : true;
727 } else if (!strcasecmp(optname, "ladderassess")) {
728 pp->ladderassess = optval && *optval == '0' ? false : true;
729 } else {
730 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
735 init_patterns(p);
737 return p;