Playout choose: Take also playout_setup * parameter
[pachi/ann.git] / playout / moggy.c
blobe35395a49410eecec7b06b15315646f8a976dd58
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 "YO."
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 ".Q."
84 "Y.."
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 "?XQ"
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 "?OY"
126 "Y.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 playout_setup *s, 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 playout_setup *s, 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 void
510 playout_moggy_assess_group(struct playout_policy *p, struct prior_map *map, group_t g, int games)
512 struct moggy_policy *pp = p->data;
513 struct board *b = map->b;
514 struct move_queue q; q.moves = 0;
516 if (board_group_info(b, g).libs > 2)
517 return;
519 if (PLDEBUGL(5)) {
520 fprintf(stderr, "ASSESS of group %s:\n", coord2sstr(g, b));
521 board_print(b, stderr);
524 if (board_group_info(b, g).libs == 2) {
525 if (!pp->atarirate)
526 return;
527 group_2lib_check(b, g, map->to_play, &q, 0);
528 while (q.moves--) {
529 coord_t coord = q.move[q.moves];
530 if (PLDEBUGL(5))
531 fprintf(stderr, "1.0: 2lib %s\n", coord2sstr(coord, b));
532 int assess = games / 2;
533 add_prior_value(map, coord, 1, assess);
535 return;
538 /* This group, sir, is in atari! */
540 coord_t ladder = pass;
541 group_atari_check(pp->alwaysccaprate, b, g, map->to_play, &q, &ladder, 0);
542 while (q.moves--) {
543 coord_t coord = q.move[q.moves];
545 /* _Never_ play here if this move plays out
546 * a caught ladder. */
547 if (coord == ladder && !board_playing_ko_threat(b)) {
548 /* Note that the opposite is not guarded against;
549 * we do not advise against capturing a laddered
550 * group (but we don't encourage it either). Such
551 * a move can simplify tactical situations if we
552 * can afford it. */
553 if (map->to_play != board_at(b, g))
554 continue;
555 /* FIXME: We give the malus even if this move
556 * captures another group. */
557 if (PLDEBUGL(5))
558 fprintf(stderr, "0.0: ladder %s\n", coord2sstr(coord, b));
559 add_prior_value(map, coord, 0, games);
560 continue;
563 if (!pp->capturerate && !pp->lcapturerate)
564 continue;
566 if (PLDEBUGL(5))
567 fprintf(stderr, "1.0: atari %s\n", coord2sstr(coord, b));
568 int assess = games * 2;
569 add_prior_value(map, coord, 1, assess);
573 void
574 playout_moggy_assess_one(struct playout_policy *p, struct prior_map *map, coord_t coord, int games)
576 struct moggy_policy *pp = p->data;
577 struct board *b = map->b;
579 if (PLDEBUGL(5)) {
580 fprintf(stderr, "ASSESS of move %s:\n", coord2sstr(coord, b));
581 board_print(b, stderr);
584 /* Is this move a self-atari? */
585 if (pp->selfatarirate) {
586 if (!board_playing_ko_threat(b) && is_bad_selfatari(b, map->to_play, coord)) {
587 if (PLDEBUGL(5))
588 fprintf(stderr, "0.0: self-atari\n");
589 add_prior_value(map, coord, 0, games);
590 if (!pp->selfatari_other)
591 return;
592 /* If we can play on the other liberty of the
593 * endangered group, do! */
594 coord = selfatari_cousin(b, map->to_play, coord);
595 if (is_pass(coord))
596 return;
597 if (PLDEBUGL(5))
598 fprintf(stderr, "1.0: self-atari redirect %s\n", coord2sstr(coord, b));
599 add_prior_value(map, coord, 1.0, games);
600 return;
604 /* Pattern check */
605 if (pp->patternrate) {
606 struct move m = { .color = map->to_play, .coord = coord };
607 if (test_pattern3_here(p, b, &m)) {
608 if (PLDEBUGL(5))
609 fprintf(stderr, "1.0: pattern\n");
610 add_prior_value(map, coord, 1, games);
614 return;
617 void
618 playout_moggy_assess(struct playout_policy *p, struct prior_map *map, int games)
620 struct moggy_policy *pp = p->data;
622 /* First, go through all endangered groups. */
623 for (group_t g = 1; g < board_size2(map->b); g++)
624 if (group_at(map->b, g) == g)
625 playout_moggy_assess_group(p, map, g, games);
627 /* Then, assess individual moves. */
628 if (!pp->patternrate && !pp->selfatarirate)
629 return;
630 foreach_free_point(map->b) {
631 if (map->consider[c])
632 playout_moggy_assess_one(p, map, c, games);
633 } foreach_free_point_end;
636 bool
637 playout_moggy_permit(struct playout_policy *p, struct board *b, struct move *m)
639 struct moggy_policy *pp = p->data;
641 /* The idea is simple for now - never allow self-atari moves.
642 * They suck in general, but this also permits us to actually
643 * handle seki in the playout stage. */
645 if (fast_random(100) >= pp->selfatarirate) {
646 if (PLDEBUGL(5))
647 fprintf(stderr, "skipping sar test\n");
648 return true;
650 bool selfatari = is_bad_selfatari(b, m->color, m->coord);
651 if (selfatari) {
652 if (PLDEBUGL(5))
653 fprintf(stderr, "__ Prohibiting self-atari %s %s\n",
654 stone2str(m->color), coord2sstr(m->coord, b));
655 if (pp->selfatari_other) {
656 /* Ok, try the other liberty of the atari'd group. */
657 coord_t c = selfatari_cousin(b, m->color, m->coord);
658 if (is_pass(c)) return false;
659 if (PLDEBUGL(5))
660 fprintf(stderr, "___ Redirecting to other lib %s\n",
661 coord2sstr(c, b));
662 m->coord = c;
663 return true;
665 return false;
667 return true;
671 struct playout_policy *
672 playout_moggy_init(char *arg, struct board *b, struct joseki_dict *jdict)
674 struct playout_policy *p = calloc2(1, sizeof(*p));
675 struct moggy_policy *pp = calloc2(1, sizeof(*pp));
676 p->data = pp;
677 p->choose = playout_moggy_partchoose;
678 p->assess = playout_moggy_assess;
679 p->permit = playout_moggy_permit;
681 pp->jdict = jdict;
683 int rate = 90;
685 pp->lcapturerate = pp->atarirate = pp->capturerate = pp->patternrate
686 = pp->selfatarirate = pp->josekirate = -1U;
687 pp->korate = 20; pp->koage = 4;
688 pp->alwaysccaprate = 20;
689 pp->selfatari_other = true;
691 /* C is stupid. */
692 double mq_prob_default[MQ_MAX] = {
693 [MQ_KO] = 6.0,
694 [MQ_LATARI] = 5.0,
695 [MQ_L2LIB] = 4.0,
696 [MQ_PAT3] = 3.0,
697 [MQ_GATARI] = 2.0,
698 [MQ_JOSEKI] = 1.0,
700 memcpy(pp->mq_prob, mq_prob_default, sizeof(pp->mq_prob));
702 if (arg) {
703 char *optspec, *next = arg;
704 while (*next) {
705 optspec = next;
706 next += strcspn(next, ":");
707 if (*next) { *next++ = 0; } else { *next = 0; }
709 char *optname = optspec;
710 char *optval = strchr(optspec, '=');
711 if (optval) *optval++ = 0;
713 if (!strcasecmp(optname, "debug") && optval) {
714 p->debug_level = atoi(optval);
715 } else if (!strcasecmp(optname, "lcapturerate") && optval) {
716 pp->lcapturerate = atoi(optval);
717 } else if (!strcasecmp(optname, "atarirate") && optval) {
718 pp->atarirate = atoi(optval);
719 } else if (!strcasecmp(optname, "capturerate") && optval) {
720 pp->capturerate = atoi(optval);
721 } else if (!strcasecmp(optname, "patternrate") && optval) {
722 pp->patternrate = atoi(optval);
723 } else if (!strcasecmp(optname, "selfatarirate") && optval) {
724 pp->selfatarirate = atoi(optval);
725 } else if (!strcasecmp(optname, "korate") && optval) {
726 pp->korate = atoi(optval);
727 } else if (!strcasecmp(optname, "josekirate") && optval) {
728 pp->josekirate = atoi(optval);
729 } else if (!strcasecmp(optname, "alwaysccaprate") && optval) {
730 pp->alwaysccaprate = atoi(optval);
731 } else if (!strcasecmp(optname, "rate") && optval) {
732 rate = atoi(optval);
733 } else if (!strcasecmp(optname, "fillboardtries")) {
734 pp->fillboardtries = atoi(optval);
735 } else if (!strcasecmp(optname, "koage") && optval) {
736 pp->koage = atoi(optval);
737 } else if (!strcasecmp(optname, "pattern2")) {
738 pp->pattern2 = optval && *optval == '0' ? false : true;
739 } else if (!strcasecmp(optname, "selfatari_other")) {
740 pp->selfatari_other = optval && *optval == '0' ? false : true;
741 } else if (!strcasecmp(optname, "capcheckall")) {
742 pp->capcheckall = optval && *optval == '0' ? false : true;
743 } else if (!strcasecmp(optname, "fullchoose")) {
744 p->choose = optval && *optval == '0' ? playout_moggy_partchoose : playout_moggy_fullchoose;
745 } else if (!strcasecmp(optname, "mqprob") && optval) {
746 /* KO%LATARI%L2LIB%PAT3%GATARI%JOSEKI */
747 for (int i = 1; *optval && i < MQ_MAX; i++, optval += strcspn(optval, "%")) {
748 optval++;
749 pp->mq_prob[i] = atof(optval);
751 } else if (!strcasecmp(optname, "tenukiprob") && optval) {
752 pp->tenuki_prob = atof(optval);
753 } else {
754 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
755 exit(1);
759 if (pp->lcapturerate == -1U) pp->lcapturerate = rate;
760 if (pp->atarirate == -1U) pp->atarirate = rate;
761 if (pp->capturerate == -1U) pp->capturerate = rate;
762 if (pp->patternrate == -1U) pp->patternrate = rate;
763 if (pp->selfatarirate == -1U) pp->selfatarirate = rate;
764 if (pp->korate == -1U) pp->korate = rate;
765 if (pp->josekirate == -1U) pp->josekirate = rate;
766 if (pp->alwaysccaprate == -1U) pp->alwaysccaprate = rate;
768 pattern3s_init(&pp->patterns, moggy_patterns_src, moggy_patterns_src_n);
770 return p;