Move group_2lib_check() et al. from Moggy to tactics/2lib.c
[pachi/derm.git] / playout / moggy.c
blob333638e02537f0cb3b74a568ec974447ee0c575f
1 /* Playout policy by stochastically applying a fixed set of decision
2 * rules in given order - modelled after the intelligent playouts
3 * in the Mogo engine. */
5 #include <assert.h>
6 #include <math.h>
7 #include <stdio.h>
8 #include <stdlib.h>
10 #define DEBUG
11 #include "board.h"
12 #include "debug.h"
13 #include "joseki/base.h"
14 #include "mq.h"
15 #include "pattern3.h"
16 #include "playout.h"
17 #include "playout/moggy.h"
18 #include "random.h"
19 #include "tactics/1lib.h"
20 #include "tactics/2lib.h"
21 #include "tactics/ladder.h"
22 #include "tactics/selfatari.h"
23 #include "uct/prior.h"
25 #define PLDEBUGL(n) DEBUGL_(p->debug_level, n)
28 /* Move queue tags: */
29 enum mq_tag {
30 MQ_KO = 1,
31 MQ_LATARI,
32 MQ_L2LIB,
33 MQ_PAT3,
34 MQ_GATARI,
35 MQ_JOSEKI,
36 MQ_MAX
40 /* Note that the context can be shared by multiple threads! */
42 struct moggy_policy {
43 unsigned int lcapturerate, atarirate, capturerate, patternrate, korate, josekirate;
44 unsigned int selfatarirate, alwaysccaprate;
45 unsigned int fillboardtries;
46 int koage;
47 /* Whether to look for patterns around second-to-last move. */
48 bool pattern2;
49 /* Whether, when self-atari attempt is detected, to play the other
50 * group's liberty if that is non-self-atari. */
51 bool selfatari_other;
52 /* Whether to always pick from moves capturing all groups in
53 * global_atari_check(). */
54 bool capcheckall;
56 struct joseki_dict *jdict;
57 struct pattern3s patterns;
59 /* Gamma values for queue tags - correspond to probabilities. */
60 /* XXX: Tune. */
61 double mq_prob[MQ_MAX], tenuki_prob;
65 static char moggy_patterns_src[][11] = {
66 /* hane pattern - enclosing hane */
67 "XOX"
68 "..."
69 "???",
70 /* hane pattern - non-cutting hane */
71 "XO."
72 "..."
73 "?.?",
74 /* hane pattern - magari */
75 "XO?"
76 "X.."
77 "x.?",
78 /* hane pattern - thin hane */
79 "XOO"
80 "..."
81 "?.?" "X",
82 /* generic pattern - katatsuke or diagonal attachment; similar to magari */
83 ".O."
84 "X.."
85 "...",
86 /* cut1 pattern (kiri) - unprotected cut */
87 "XO?"
88 "O.o"
89 "?o?",
90 /* cut1 pattern (kiri) - peeped cut */
91 "XO?"
92 "O.X"
93 "???",
94 /* cut2 pattern (de) */
95 "?X?"
96 "O.O"
97 "ooo",
98 /* cut keima (not in Mogo) */
99 "OX?"
100 "o.O"
101 "???", /* o?? has some pathological tsumego cases */
102 /* side pattern - chase */
103 "X.?"
104 "O.?"
105 "##?",
106 /* side pattern - block side cut */
107 "OX?"
108 "X.O"
109 "###",
110 /* side pattern - block side connection */
111 "?X?"
112 "x.O"
113 "###",
114 /* side pattern - sagari (SUSPICIOUS) */
115 "?XO"
116 "x.x" /* Mogo has "x.?" */
117 "###" /* Mogo has "X" */,
118 /* side pattern - throw-in (SUSPICIOUS) */
119 #if 0
120 "?OX"
121 "o.O"
122 "?##" "X",
123 #endif
124 /* side pattern - cut (SUSPICIOUS) */
125 "?OX"
126 "X.O"
127 "###" /* Mogo has "X" */,
129 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
131 static inline bool
132 test_pattern3_here(struct playout_policy *p, struct board *b, struct move *m)
134 struct moggy_policy *pp = p->data;
135 /* Check if 3x3 pattern is matched by given move... */
136 if (!pattern3_move_here(&pp->patterns, b, m))
137 return false;
138 /* ...and the move is not obviously stupid. */
139 if (is_bad_selfatari(b, m->color, m->coord))
140 return false;
141 /* Ladder moves are stupid. */
142 group_t atari_neighbor = board_get_atari_neighbor(b, m->coord, m->color);
143 if (atari_neighbor && is_ladder(b, m->coord, atari_neighbor))
144 return false;
145 return true;
148 static void
149 apply_pattern_here(struct playout_policy *p, struct board *b, coord_t c, enum stone color, struct move_queue *q)
151 struct move m2 = { .coord = c, .color = color };
152 if (board_is_valid_move(b, &m2) && test_pattern3_here(p, b, &m2))
153 mq_add(q, c, 1<<MQ_PAT3);
156 /* Check if we match any pattern around given move (with the other color to play). */
157 static void
158 apply_pattern(struct playout_policy *p, struct board *b, struct move *m, struct move *mm, struct move_queue *q)
160 /* Suicides do not make any patterns and confuse us. */
161 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
162 return;
164 foreach_8neighbor(b, m->coord) {
165 apply_pattern_here(p, b, c, stone_other(m->color), q);
166 } foreach_8neighbor_end;
168 if (mm) { /* Second move for pattern searching */
169 foreach_8neighbor(b, mm->coord) {
170 if (coord_is_8adjecent(m->coord, c, b))
171 continue;
172 apply_pattern_here(p, b, c, stone_other(m->color), q);
173 } foreach_8neighbor_end;
176 if (PLDEBUGL(5))
177 mq_print(q, b, "Pattern");
181 static void
182 joseki_check(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q)
184 struct moggy_policy *pp = p->data;
185 if (!pp->jdict)
186 return;
188 for (int i = 0; i < 4; i++) {
189 hash_t h = b->qhash[i] & joseki_hash_mask;
190 coord_t *cc = pp->jdict->patterns[h].moves[to_play];
191 if (!cc) continue;
192 for (; !is_pass(*cc); cc++) {
193 if (coord_quadrant(*cc, b) != i)
194 continue;
195 mq_add(q, *cc, 1<<MQ_JOSEKI);
199 if (q->moves > 0 && PLDEBUGL(5))
200 mq_print(q, b, "Joseki");
203 static void
204 global_atari_check(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q)
206 if (b->clen == 0)
207 return;
209 struct moggy_policy *pp = p->data;
210 if (pp->capcheckall) {
211 for (int g = 0; g < b->clen; g++)
212 group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, 1<<MQ_GATARI);
213 if (PLDEBUGL(5))
214 mq_print(q, b, "Global atari");
215 return;
218 int g_base = fast_random(b->clen);
219 for (int g = g_base; g < b->clen; g++) {
220 group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, 1<<MQ_GATARI);
221 if (q->moves > 0) {
222 /* XXX: Try carrying on. */
223 if (PLDEBUGL(5))
224 mq_print(q, b, "Global atari");
225 return;
228 for (int g = 0; g < g_base; g++) {
229 group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, 1<<MQ_GATARI);
230 if (q->moves > 0) {
231 /* XXX: Try carrying on. */
232 if (PLDEBUGL(5))
233 mq_print(q, b, "Global atari");
234 return;
237 return;
240 static void
241 local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q)
243 struct moggy_policy *pp = p->data;
245 /* Did the opponent play a self-atari? */
246 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
247 group_atari_check(pp->alwaysccaprate, b, group_at(b, m->coord), stone_other(m->color), q, NULL, 1<<MQ_LATARI);
250 foreach_neighbor(b, m->coord, {
251 group_t g = group_at(b, c);
252 if (!g || board_group_info(b, g).libs != 1)
253 continue;
254 group_atari_check(pp->alwaysccaprate, b, g, stone_other(m->color), q, NULL, 1<<MQ_LATARI);
257 if (PLDEBUGL(5))
258 mq_print(q, b, "Local atari");
262 static void
263 local_2lib_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q)
265 /* Does the opponent have just two liberties? */
266 if (board_group_info(b, group_at(b, m->coord)).libs == 2) {
267 group_2lib_check(b, group_at(b, m->coord), stone_other(m->color), q, 1<<MQ_L2LIB);
268 #if 0
269 /* We always prefer to take off an enemy chain liberty
270 * before pulling out ourselves. */
271 /* XXX: We aren't guaranteed to return to that group
272 * later. */
273 if (q->moves)
274 return q->move[fast_random(q->moves)];
275 #endif
278 /* Then he took a third liberty from neighboring chain? */
279 foreach_neighbor(b, m->coord, {
280 group_t g = group_at(b, c);
281 if (!g || board_group_info(b, g).libs != 2)
282 continue;
283 group_2lib_check(b, g, stone_other(m->color), q, 1<<MQ_L2LIB);
286 if (PLDEBUGL(5))
287 mq_print(q, b, "Local 2lib");
290 coord_t
291 fillboard_check(struct playout_policy *p, struct board *b)
293 struct moggy_policy *pp = p->data;
294 unsigned int fbtries = b->flen / 8;
295 if (pp->fillboardtries < fbtries)
296 fbtries = pp->fillboardtries;
298 for (unsigned int i = 0; i < fbtries; i++) {
299 coord_t coord = b->f[fast_random(b->flen)];
300 if (immediate_liberty_count(b, coord) != 4)
301 continue;
302 foreach_diag_neighbor(b, coord) {
303 if (board_at(b, c) != S_NONE)
304 goto next_try;
305 } foreach_diag_neighbor_end;
306 return coord;
307 next_try:;
309 return pass;
312 coord_t
313 playout_moggy_partchoose(struct playout_policy *p, struct board *b, enum stone to_play)
315 struct moggy_policy *pp = p->data;
317 if (PLDEBUGL(5))
318 board_print(b, stderr);
320 /* Ko fight check */
321 if (!is_pass(b->last_ko.coord) && is_pass(b->ko.coord)
322 && b->moves - b->last_ko_age < pp->koage
323 && pp->korate > fast_random(100)) {
324 if (board_is_valid_play(b, to_play, b->last_ko.coord)
325 && !is_bad_selfatari(b, to_play, b->last_ko.coord))
326 return b->last_ko.coord;
329 /* Local checks */
330 if (!is_pass(b->last_move.coord)) {
331 /* Local group in atari? */
332 if (pp->lcapturerate > fast_random(100)) {
333 struct move_queue q; q.moves = 0;
334 local_atari_check(p, b, &b->last_move, &q);
335 if (q.moves > 0)
336 return mq_pick(&q);
339 /* Local group can be PUT in atari? */
340 if (pp->atarirate > fast_random(100)) {
341 struct move_queue q; q.moves = 0;
342 local_2lib_check(p, b, &b->last_move, &q);
343 if (q.moves > 0)
344 return mq_pick(&q);
347 /* Check for patterns we know */
348 if (pp->patternrate > fast_random(100)) {
349 struct move_queue q; q.moves = 0;
350 apply_pattern(p, b, &b->last_move,
351 pp->pattern2 && b->last_move2.coord >= 0 ? &b->last_move2 : NULL,
352 &q);
353 if (q.moves > 0)
354 return mq_pick(&q);
358 /* Global checks */
360 /* Any groups in atari? */
361 if (pp->capturerate > fast_random(100)) {
362 struct move_queue q; q.moves = 0;
363 global_atari_check(p, b, to_play, &q);
364 if (q.moves > 0)
365 return mq_pick(&q);
368 /* Joseki moves? */
369 if (pp->josekirate > fast_random(100)) {
370 struct move_queue q; q.moves = 0;
371 joseki_check(p, b, to_play, &q);
372 if (q.moves > 0)
373 return mq_pick(&q);
376 /* Fill board */
377 if (pp->fillboardtries > 0) {
378 coord_t c = fillboard_check(p, b);
379 if (!is_pass(c))
380 return c;
383 return pass;
386 /* Pick a move from queue q, giving different likelihoods to moves
387 * based on their tags. */
388 coord_t
389 mq_tagged_choose(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q)
391 struct moggy_policy *pp = p->data;
393 /* First, merge all entries for a move. */
394 /* We use a naive O(N^2) since the average length of the queue
395 * is about 1.4. */
396 for (unsigned int i = 0; i < q->moves; i++) {
397 for (unsigned int j = i + 1; j < q->moves; j++) {
398 if (q->move[i] != q->move[j])
399 continue;
400 q->tag[i] |= q->tag[j];
401 q->moves--;
402 q->tag[j] = q->tag[q->moves];
403 q->move[j] = q->move[q->moves];
407 /* Now, construct a probdist. */
408 fixp_t total = 0;
409 fixp_t pd[q->moves];
410 for (unsigned int i = 0; i < q->moves; i++) {
411 double val = 1.0;
412 assert(q->tag[i] != 0);
413 for (int j = 1; j < MQ_MAX; j++)
414 if (q->tag[i] & (1<<j)) {
415 //fprintf(stderr, "%s(%x) %d %f *= %f\n", coord2sstr(q->move[i], b), q->tag[i], j, val, pp->mq_prob[j]);
416 val *= pp->mq_prob[j];
418 pd[i] = double_to_fixp(val);
419 total += pd[i];
421 total += double_to_fixp(pp->tenuki_prob);
423 /* Finally, pick a move! */
424 fixp_t stab = fast_irandom(total);
425 for (unsigned int i = 0; i < q->moves; i++) {
426 //fprintf(stderr, "%s(%x) %f (%f/%f)\n", coord2sstr(q->move[i], b), q->tag[i], fixp_to_double(stab), fixp_to_double(pd[i]), fixp_to_double(total));
427 if (stab < pd[i])
428 return q->move[i];
429 stab -= pd[i];
432 /* Tenuki. */
433 assert(stab < double_to_fixp(pp->tenuki_prob));
434 return pass;
437 coord_t
438 playout_moggy_fullchoose(struct playout_policy *p, struct board *b, enum stone to_play)
440 struct moggy_policy *pp = p->data;
441 struct move_queue q; q.moves = 0;
443 if (PLDEBUGL(5))
444 board_print(b, stderr);
446 /* Ko fight check */
447 if (!is_pass(b->last_ko.coord) && is_pass(b->ko.coord)
448 && b->moves - b->last_ko_age < pp->koage
449 && pp->korate > fast_random(100)) {
450 if (board_is_valid_play(b, to_play, b->last_ko.coord)
451 && !is_bad_selfatari(b, to_play, b->last_ko.coord))
452 mq_add(&q, b->last_ko.coord, 1<<MQ_KO);
455 /* Local checks */
456 if (!is_pass(b->last_move.coord)) {
457 /* Local group in atari? */
458 if (pp->lcapturerate > fast_random(100)) {
459 local_atari_check(p, b, &b->last_move, &q);
462 /* Local group can be PUT in atari? */
463 if (pp->atarirate > fast_random(100)) {
464 local_2lib_check(p, b, &b->last_move, &q);
467 /* Check for patterns we know */
468 if (pp->patternrate > fast_random(100)) {
469 apply_pattern(p, b, &b->last_move,
470 pp->pattern2 && b->last_move2.coord >= 0 ? &b->last_move2 : NULL,
471 &q);
475 /* Global checks */
477 /* Any groups in atari? */
478 if (pp->capturerate > fast_random(100)) {
479 global_atari_check(p, b, to_play, &q);
482 /* Joseki moves? */
483 if (pp->josekirate > fast_random(100)) {
484 joseki_check(p, b, to_play, &q);
487 #if 0
488 /* Average length of the queue is 1.4 move. */
489 printf("MQL %d ", q.moves);
490 for (unsigned int i = 0; i < q.moves; i++)
491 printf("%s ", coord2sstr(q.move[i], b));
492 printf("\n");
493 #endif
495 if (q.moves > 0)
496 return mq_tagged_choose(p, b, to_play, &q);
498 /* Fill board */
499 if (pp->fillboardtries > 0) {
500 coord_t c = fillboard_check(p, b);
501 if (!is_pass(c))
502 return c;
505 return pass;
509 static coord_t
510 selfatari_cousin(struct board *b, enum stone color, coord_t coord)
512 group_t groups[4]; int groups_n = 0;
513 foreach_neighbor(b, coord, {
514 enum stone s = board_at(b, c);
515 if (s != color) continue;
516 group_t g = group_at(b, c);
517 if (board_group_info(b, g).libs == 2)
518 groups[groups_n++] = g;
521 if (!groups_n)
522 return pass;
523 group_t group = groups[fast_random(groups_n)];
525 coord_t lib2 = board_group_other_lib(b, group, coord);
526 if (is_bad_selfatari(b, color, lib2))
527 return pass;
528 return lib2;
531 void
532 playout_moggy_assess_group(struct playout_policy *p, struct prior_map *map, group_t g, int games)
534 struct moggy_policy *pp = p->data;
535 struct board *b = map->b;
536 struct move_queue q; q.moves = 0;
538 if (board_group_info(b, g).libs > 2)
539 return;
541 if (PLDEBUGL(5)) {
542 fprintf(stderr, "ASSESS of group %s:\n", coord2sstr(g, b));
543 board_print(b, stderr);
546 if (board_group_info(b, g).libs == 2) {
547 if (!pp->atarirate)
548 return;
549 group_2lib_check(b, g, map->to_play, &q, 0);
550 while (q.moves--) {
551 coord_t coord = q.move[q.moves];
552 if (PLDEBUGL(5))
553 fprintf(stderr, "1.0: 2lib %s\n", coord2sstr(coord, b));
554 int assess = games / 2;
555 add_prior_value(map, coord, 1, assess);
557 return;
560 /* This group, sir, is in atari! */
562 coord_t ladder = pass;
563 group_atari_check(pp->alwaysccaprate, b, g, map->to_play, &q, &ladder, 0);
564 while (q.moves--) {
565 coord_t coord = q.move[q.moves];
567 /* _Never_ play here if this move plays out
568 * a caught ladder. */
569 if (coord == ladder && !board_playing_ko_threat(b)) {
570 /* Note that the opposite is not guarded against;
571 * we do not advise against capturing a laddered
572 * group (but we don't encourage it either). Such
573 * a move can simplify tactical situations if we
574 * can afford it. */
575 if (map->to_play != board_at(b, g))
576 continue;
577 /* FIXME: We give the malus even if this move
578 * captures another group. */
579 if (PLDEBUGL(5))
580 fprintf(stderr, "0.0: ladder %s\n", coord2sstr(coord, b));
581 add_prior_value(map, coord, 0, games);
582 continue;
585 if (!pp->capturerate && !pp->lcapturerate)
586 continue;
588 if (PLDEBUGL(5))
589 fprintf(stderr, "1.0: atari %s\n", coord2sstr(coord, b));
590 int assess = games * 2;
591 add_prior_value(map, coord, 1, assess);
595 void
596 playout_moggy_assess_one(struct playout_policy *p, struct prior_map *map, coord_t coord, int games)
598 struct moggy_policy *pp = p->data;
599 struct board *b = map->b;
601 if (PLDEBUGL(5)) {
602 fprintf(stderr, "ASSESS of move %s:\n", coord2sstr(coord, b));
603 board_print(b, stderr);
606 /* Is this move a self-atari? */
607 if (pp->selfatarirate) {
608 if (!board_playing_ko_threat(b) && is_bad_selfatari(b, map->to_play, coord)) {
609 if (PLDEBUGL(5))
610 fprintf(stderr, "0.0: self-atari\n");
611 add_prior_value(map, coord, 0, games);
612 if (!pp->selfatari_other)
613 return;
614 /* If we can play on the other liberty of the
615 * endangered group, do! */
616 coord = selfatari_cousin(b, map->to_play, coord);
617 if (is_pass(coord))
618 return;
619 if (PLDEBUGL(5))
620 fprintf(stderr, "1.0: self-atari redirect %s\n", coord2sstr(coord, b));
621 add_prior_value(map, coord, 1.0, games);
622 return;
626 /* Pattern check */
627 if (pp->patternrate) {
628 struct move m = { .color = map->to_play, .coord = coord };
629 if (test_pattern3_here(p, b, &m)) {
630 if (PLDEBUGL(5))
631 fprintf(stderr, "1.0: pattern\n");
632 add_prior_value(map, coord, 1, games);
636 return;
639 void
640 playout_moggy_assess(struct playout_policy *p, struct prior_map *map, int games)
642 struct moggy_policy *pp = p->data;
644 /* First, go through all endangered groups. */
645 for (group_t g = 1; g < board_size2(map->b); g++)
646 if (group_at(map->b, g) == g)
647 playout_moggy_assess_group(p, map, g, games);
649 /* Then, assess individual moves. */
650 if (!pp->patternrate && !pp->selfatarirate)
651 return;
652 foreach_free_point(map->b) {
653 if (map->consider[c])
654 playout_moggy_assess_one(p, map, c, games);
655 } foreach_free_point_end;
658 bool
659 playout_moggy_permit(struct playout_policy *p, struct board *b, struct move *m)
661 struct moggy_policy *pp = p->data;
663 /* The idea is simple for now - never allow self-atari moves.
664 * They suck in general, but this also permits us to actually
665 * handle seki in the playout stage. */
667 if (fast_random(100) >= pp->selfatarirate) {
668 if (PLDEBUGL(5))
669 fprintf(stderr, "skipping sar test\n");
670 return true;
672 bool selfatari = is_bad_selfatari(b, m->color, m->coord);
673 if (selfatari) {
674 if (PLDEBUGL(5))
675 fprintf(stderr, "__ Prohibiting self-atari %s %s\n",
676 stone2str(m->color), coord2sstr(m->coord, b));
677 if (pp->selfatari_other) {
678 /* Ok, try the other liberty of the atari'd group. */
679 coord_t c = selfatari_cousin(b, m->color, m->coord);
680 if (is_pass(c)) return false;
681 if (PLDEBUGL(5))
682 fprintf(stderr, "___ Redirecting to other lib %s\n",
683 coord2sstr(c, b));
684 m->coord = c;
685 return true;
687 return false;
689 return true;
693 struct playout_policy *
694 playout_moggy_init(char *arg, struct board *b, struct joseki_dict *jdict)
696 struct playout_policy *p = calloc2(1, sizeof(*p));
697 struct moggy_policy *pp = calloc2(1, sizeof(*pp));
698 p->data = pp;
699 p->choose = playout_moggy_partchoose;
700 p->assess = playout_moggy_assess;
701 p->permit = playout_moggy_permit;
703 pp->jdict = jdict;
705 int rate = 90;
707 pp->lcapturerate = pp->atarirate = pp->capturerate = pp->patternrate
708 = pp->selfatarirate = pp->josekirate = -1U;
709 pp->korate = 0; pp->koage = 4;
710 pp->alwaysccaprate = 0;
711 pp->selfatari_other = true;
713 /* C is stupid. */
714 double mq_prob_default[MQ_MAX] = {
715 [MQ_KO] = 6.0,
716 [MQ_LATARI] = 5.0,
717 [MQ_L2LIB] = 4.0,
718 [MQ_PAT3] = 3.0,
719 [MQ_GATARI] = 2.0,
720 [MQ_JOSEKI] = 1.0,
722 memcpy(pp->mq_prob, mq_prob_default, sizeof(pp->mq_prob));
724 if (arg) {
725 char *optspec, *next = arg;
726 while (*next) {
727 optspec = next;
728 next += strcspn(next, ":");
729 if (*next) { *next++ = 0; } else { *next = 0; }
731 char *optname = optspec;
732 char *optval = strchr(optspec, '=');
733 if (optval) *optval++ = 0;
735 if (!strcasecmp(optname, "debug") && optval) {
736 p->debug_level = atoi(optval);
737 } else if (!strcasecmp(optname, "lcapturerate") && optval) {
738 pp->lcapturerate = atoi(optval);
739 } else if (!strcasecmp(optname, "atarirate") && optval) {
740 pp->atarirate = atoi(optval);
741 } else if (!strcasecmp(optname, "capturerate") && optval) {
742 pp->capturerate = atoi(optval);
743 } else if (!strcasecmp(optname, "patternrate") && optval) {
744 pp->patternrate = atoi(optval);
745 } else if (!strcasecmp(optname, "selfatarirate") && optval) {
746 pp->selfatarirate = atoi(optval);
747 } else if (!strcasecmp(optname, "korate") && optval) {
748 pp->korate = atoi(optval);
749 } else if (!strcasecmp(optname, "josekirate") && optval) {
750 pp->josekirate = atoi(optval);
751 } else if (!strcasecmp(optname, "alwaysccaprate") && optval) {
752 pp->alwaysccaprate = atoi(optval);
753 } else if (!strcasecmp(optname, "rate") && optval) {
754 rate = atoi(optval);
755 } else if (!strcasecmp(optname, "fillboardtries")) {
756 pp->fillboardtries = atoi(optval);
757 } else if (!strcasecmp(optname, "koage") && optval) {
758 pp->koage = atoi(optval);
759 } else if (!strcasecmp(optname, "pattern2")) {
760 pp->pattern2 = optval && *optval == '0' ? false : true;
761 } else if (!strcasecmp(optname, "selfatari_other")) {
762 pp->selfatari_other = optval && *optval == '0' ? false : true;
763 } else if (!strcasecmp(optname, "capcheckall")) {
764 pp->capcheckall = optval && *optval == '0' ? false : true;
765 } else if (!strcasecmp(optname, "fullchoose")) {
766 p->choose = optval && *optval == '0' ? playout_moggy_partchoose : playout_moggy_fullchoose;
767 } else if (!strcasecmp(optname, "mqprob") && optval) {
768 /* KO%LATARI%L2LIB%PAT3%GATARI%JOSEKI */
769 for (int i = 1; *optval && i < MQ_MAX; i++, optval += strcspn(optval, "%")) {
770 optval++;
771 pp->mq_prob[i] = atof(optval);
773 } else if (!strcasecmp(optname, "tenukiprob") && optval) {
774 pp->tenuki_prob = atof(optval);
775 } else {
776 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
777 exit(1);
781 if (pp->lcapturerate == -1U) pp->lcapturerate = rate;
782 if (pp->atarirate == -1U) pp->atarirate = rate;
783 if (pp->capturerate == -1U) pp->capturerate = rate;
784 if (pp->patternrate == -1U) pp->patternrate = rate;
785 if (pp->selfatarirate == -1U) pp->selfatarirate = rate;
786 if (pp->korate == -1U) pp->korate = rate;
787 if (pp->josekirate == -1U) pp->josekirate = rate;
788 if (pp->alwaysccaprate == -1U) pp->alwaysccaprate = rate;
790 pattern3s_init(&pp->patterns, moggy_patterns_src, moggy_patterns_src_n);
792 return p;