Moggy stone weighing: Fix case when cap_stone_denom == 0
[pachi/pachi-r6144.git] / playout / moggy.c
blob9873fa228003b2b9918c848dc710d7e96f937ee4
1 /* Heuristical playout (and tree prior) policy modelled primarily after
2 * the description of the Mogo engine. */
4 #include <assert.h>
5 #include <math.h>
6 #include <stdio.h>
7 #include <stdlib.h>
9 #define DEBUG
10 #include "board.h"
11 #include "debug.h"
12 #include "joseki/base.h"
13 #include "mq.h"
14 #include "pattern3.h"
15 #include "playout.h"
16 #include "playout/moggy.h"
17 #include "random.h"
18 #include "tactics/1lib.h"
19 #include "tactics/2lib.h"
20 #include "tactics/nlib.h"
21 #include "tactics/ladder.h"
22 #include "tactics/nakade.h"
23 #include "tactics/selfatari.h"
24 #include "uct/prior.h"
26 #define PLDEBUGL(n) DEBUGL_(p->debug_level, n)
29 /* In case "seqchoose" move picker is enabled (i.e. no "fullchoose"
30 * parameter passed), we stochastically apply fixed set of decision
31 * rules in given order.
33 * In "fullchoose" mode, we instead build a move queue of variously
34 * tagged candidates, then consider a probability distribution over
35 * them and pick a move from that. */
37 /* Move queue tags. Some may be even undesirable - these moves then
38 * receive a penalty; penalty tags should be used only when it is
39 * certain the move would be considered anyway. */
40 enum mq_tag {
41 MQ_KO = 0,
42 MQ_LATARI,
43 MQ_L2LIB,
44 MQ_LNLIB,
45 MQ_PAT3,
46 MQ_GATARI,
47 MQ_JOSEKI,
48 MQ_NAKADE,
49 MQ_MAX
53 /* Note that the context can be shared by multiple threads! */
55 struct moggy_policy {
56 unsigned int lcapturerate, atarirate, nlibrate, capturerate, patternrate, korate, josekirate, nakaderate;
57 unsigned int selfatarirate, alwaysccaprate;
58 unsigned int fillboardtries;
59 int koage;
60 /* Whether to look for patterns around second-to-last move. */
61 bool pattern2;
62 /* Whether, when self-atari attempt is detected, to play the other
63 * group's liberty if that is non-self-atari. */
64 bool selfatari_other;
66 /* 1lib settings: */
67 /* Whether to always pick from moves capturing all groups in
68 * global_atari_check(). */
69 bool capcheckall;
70 /* Prior stone weighting. Weight of each stone between
71 * cap_stone_min and cap_stone_max is (assess*100)/cap_stone_denom. */
72 int cap_stone_min, cap_stone_max;
73 int cap_stone_denom;
75 /* 2lib settings: */
76 bool atari_def_no_hopeless;
77 bool atari_miaisafe;
79 /* nlib settings: */
80 int nlib_count;
82 struct joseki_dict *jdict;
83 struct pattern3s patterns;
85 /* Gamma values for queue tags - correspond to probabilities. */
86 /* XXX: Tune. */
87 double mq_prob[MQ_MAX], tenuki_prob;
91 static char moggy_patterns_src[][11] = {
92 /* hane pattern - enclosing hane */
93 "XOX"
94 "..."
95 "???",
96 /* hane pattern - non-cutting hane */
97 "YO."
98 "..."
99 "?.?",
100 /* hane pattern - magari */
101 "XO?"
102 "X.."
103 "x.?",
104 /* hane pattern - thin hane */
105 "XOO"
106 "..."
107 "?.?" "X",
108 /* generic pattern - katatsuke or diagonal attachment; similar to magari */
109 ".Q."
110 "Y.."
111 "...",
112 /* cut1 pattern (kiri) - unprotected cut */
113 "XO?"
114 "O.o"
115 "?o?",
116 /* cut1 pattern (kiri) - peeped cut */
117 "XO?"
118 "O.X"
119 "???",
120 /* cut2 pattern (de) */
121 "?X?"
122 "O.O"
123 "ooo",
124 /* cut keima (not in Mogo) */
125 "OX?"
126 "o.O"
127 "???", /* o?? has some pathological tsumego cases */
128 /* side pattern - chase */
129 "X.?"
130 "O.?"
131 "##?",
132 /* side pattern - block side cut */
133 "OX?"
134 "X.O"
135 "###",
136 /* side pattern - block side connection */
137 "?X?"
138 "x.O"
139 "###",
140 /* side pattern - sagari (SUSPICIOUS) */
141 "?XQ"
142 "x.x" /* Mogo has "x.?" */
143 "###" /* Mogo has "X" */,
144 /* side pattern - throw-in (SUSPICIOUS) */
145 #if 0
146 "?OX"
147 "o.O"
148 "?##" "X",
149 #endif
150 /* side pattern - cut (SUSPICIOUS) */
151 "?OY"
152 "Y.O"
153 "###" /* Mogo has "X" */,
154 /* side pattern - eye piercing:
155 * # O O O .
156 * # O . O .
157 * # . . . .
158 * # # # # # */
159 #if 0
160 "Oxx"
161 "..."
162 "###",
163 #endif
165 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
167 static inline bool
168 test_pattern3_here(struct playout_policy *p, struct board *b, struct move *m)
170 struct moggy_policy *pp = p->data;
171 /* Check if 3x3 pattern is matched by given move... */
172 if (!pattern3_move_here(&pp->patterns, b, m))
173 return false;
174 /* ...and the move is not obviously stupid. */
175 if (is_bad_selfatari(b, m->color, m->coord))
176 return false;
177 /* Ladder moves are stupid. */
178 group_t atari_neighbor = board_get_atari_neighbor(b, m->coord, m->color);
179 if (atari_neighbor && is_ladder(b, m->coord, atari_neighbor)
180 && !can_countercapture(b, board_at(b, group_base(atari_neighbor)),
181 atari_neighbor, m->color, NULL, 0))
182 return false;
183 return true;
186 static void
187 apply_pattern_here(struct playout_policy *p, struct board *b, coord_t c, enum stone color, struct move_queue *q)
189 struct move m2 = { .coord = c, .color = color };
190 if (board_is_valid_move(b, &m2) && test_pattern3_here(p, b, &m2))
191 mq_add(q, c, 1<<MQ_PAT3);
194 /* Check if we match any pattern around given move (with the other color to play). */
195 static void
196 apply_pattern(struct playout_policy *p, struct board *b, struct move *m, struct move *mm, struct move_queue *q)
198 /* Suicides do not make any patterns and confuse us. */
199 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
200 return;
202 foreach_8neighbor(b, m->coord) {
203 apply_pattern_here(p, b, c, stone_other(m->color), q);
204 } foreach_8neighbor_end;
206 if (mm) { /* Second move for pattern searching */
207 foreach_8neighbor(b, mm->coord) {
208 if (coord_is_8adjecent(m->coord, c, b))
209 continue;
210 apply_pattern_here(p, b, c, stone_other(m->color), q);
211 } foreach_8neighbor_end;
214 if (PLDEBUGL(5))
215 mq_print(q, b, "Pattern");
219 static void
220 joseki_check(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q)
222 struct moggy_policy *pp = p->data;
223 if (!pp->jdict)
224 return;
226 for (int i = 0; i < 4; i++) {
227 hash_t h = b->qhash[i] & joseki_hash_mask;
228 coord_t *cc = pp->jdict->patterns[h].moves[to_play];
229 if (!cc) continue;
230 for (; !is_pass(*cc); cc++) {
231 if (coord_quadrant(*cc, b) != i)
232 continue;
233 mq_add(q, *cc, 1<<MQ_JOSEKI);
237 if (q->moves > 0 && PLDEBUGL(5))
238 mq_print(q, b, "Joseki");
241 static void
242 global_atari_check(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q)
244 if (b->clen == 0)
245 return;
247 struct moggy_policy *pp = p->data;
248 if (pp->capcheckall) {
249 for (int g = 0; g < b->clen; g++)
250 group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, 1<<MQ_GATARI);
251 if (PLDEBUGL(5))
252 mq_print(q, b, "Global atari");
253 return;
256 int g_base = fast_random(b->clen);
257 for (int g = g_base; g < b->clen; g++) {
258 group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, 1<<MQ_GATARI);
259 if (q->moves > 0) {
260 /* XXX: Try carrying on. */
261 if (PLDEBUGL(5))
262 mq_print(q, b, "Global atari");
263 return;
266 for (int g = 0; g < g_base; g++) {
267 group_atari_check(pp->alwaysccaprate, b, group_at(b, group_base(b->c[g])), to_play, q, NULL, 1<<MQ_GATARI);
268 if (q->moves > 0) {
269 /* XXX: Try carrying on. */
270 if (PLDEBUGL(5))
271 mq_print(q, b, "Global atari");
272 return;
275 return;
278 static void
279 local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q)
281 struct moggy_policy *pp = p->data;
283 /* Did the opponent play a self-atari? */
284 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
285 group_atari_check(pp->alwaysccaprate, b, group_at(b, m->coord), stone_other(m->color), q, NULL, 1<<MQ_LATARI);
288 foreach_neighbor(b, m->coord, {
289 group_t g = group_at(b, c);
290 if (!g || board_group_info(b, g).libs != 1)
291 continue;
292 group_atari_check(pp->alwaysccaprate, b, g, stone_other(m->color), q, NULL, 1<<MQ_LATARI);
295 if (PLDEBUGL(5))
296 mq_print(q, b, "Local atari");
300 static void
301 local_2lib_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q)
303 struct moggy_policy *pp = p->data;
304 group_t group = group_at(b, m->coord), group2 = 0;
306 /* Does the opponent have just two liberties? */
307 if (board_group_info(b, group).libs == 2) {
308 group_2lib_check(b, group, stone_other(m->color), q, 1<<MQ_L2LIB, pp->atari_miaisafe, pp->atari_def_no_hopeless);
309 #if 0
310 /* We always prefer to take off an enemy chain liberty
311 * before pulling out ourselves. */
312 /* XXX: We aren't guaranteed to return to that group
313 * later. */
314 if (q->moves)
315 return q->move[fast_random(q->moves)];
316 #endif
319 /* Then he took a third liberty from neighboring chain? */
320 foreach_neighbor(b, m->coord, {
321 group_t g = group_at(b, c);
322 if (!g || g == group || g == group2 || board_group_info(b, g).libs != 2)
323 continue;
324 group_2lib_check(b, g, stone_other(m->color), q, 1<<MQ_L2LIB, pp->atari_miaisafe, pp->atari_def_no_hopeless);
325 group2 = g; // prevent trivial repeated checks
328 if (PLDEBUGL(5))
329 mq_print(q, b, "Local 2lib");
332 static void
333 local_nlib_check(struct playout_policy *p, struct board *b, struct move *m, struct move_queue *q)
335 struct moggy_policy *pp = p->data;
336 enum stone color = stone_other(m->color);
338 /* Attacking N-liberty groups in general is probably
339 * not feasible. What we are primarily concerned about is
340 * counter-attacking groups that have two physical liberties,
341 * but three effective liberties:
343 * . O . . . . #
344 * O O X X X X #
345 * . X O O X . #
346 * . X O . O X #
347 * . X O O . X #
348 * # # # # # # #
350 * The time for this to come is when the opponent took a liberty
351 * of ours, making a few-liberty group. Therefore, we focus
352 * purely on defense.
354 * There is a tradeoff - down to how many liberties we need to
355 * be to start looking? nlib_count=3 will work for the left black
356 * group (2lib-solver will suggest connecting the false eye), but
357 * not for top black group (it is too late to start playing 3-3
358 * capturing race). Also, we cannot prevent stupidly taking an
359 * outside liberty ourselves; the higher nlib_count, the higher
360 * the chance we withstand this.
362 * However, higher nlib_count means that we will waste more time
363 * checking non-urgent or alive groups, and we will play silly
364 * or wasted moves around alive groups. */
366 group_t group2 = 0;
367 foreach_8neighbor(b, m->coord) {
368 group_t g = group_at(b, c);
369 if (!g || group2 == g || board_at(b, c) != color)
370 continue;
371 if (board_group_info(b, g).libs < 3 || board_group_info(b, g).libs > pp->nlib_count)
372 continue;
373 group_nlib_defense_check(b, g, color, q, 1<<MQ_LNLIB);
374 group2 = g; // prevent trivial repeated checks
375 } foreach_8neighbor_end;
377 if (PLDEBUGL(5))
378 mq_print(q, b, "Local nlib");
381 static coord_t
382 nakade_check(struct playout_policy *p, struct board *b, struct move *m, enum stone to_play)
384 coord_t empty = pass;
385 foreach_neighbor(b, m->coord, {
386 if (board_at(b, c) != S_NONE)
387 continue;
388 if (is_pass(empty)) {
389 empty = c;
390 continue;
392 if (!coord_is_8adjecent(c, empty, b)) {
393 /* Seems like impossible nakade
394 * shape! */
395 return pass;
398 assert(!is_pass(empty));
400 coord_t nakade = nakade_point(b, empty, stone_other(to_play));
401 if (PLDEBUGL(5) && !is_pass(nakade))
402 fprintf(stderr, "Nakade: %s\n", coord2sstr(nakade, b));
403 return nakade;
406 coord_t
407 fillboard_check(struct playout_policy *p, struct board *b)
409 struct moggy_policy *pp = p->data;
410 unsigned int fbtries = b->flen / 8;
411 if (pp->fillboardtries < fbtries)
412 fbtries = pp->fillboardtries;
414 for (unsigned int i = 0; i < fbtries; i++) {
415 coord_t coord = b->f[fast_random(b->flen)];
416 if (immediate_liberty_count(b, coord) != 4)
417 continue;
418 foreach_diag_neighbor(b, coord) {
419 if (board_at(b, c) != S_NONE)
420 goto next_try;
421 } foreach_diag_neighbor_end;
422 return coord;
423 next_try:
426 return pass;
429 coord_t
430 playout_moggy_seqchoose(struct playout_policy *p, struct playout_setup *s, struct board *b, enum stone to_play)
432 struct moggy_policy *pp = p->data;
434 if (PLDEBUGL(5))
435 board_print(b, stderr);
437 /* Ko fight check */
438 if (!is_pass(b->last_ko.coord) && is_pass(b->ko.coord)
439 && b->moves - b->last_ko_age < pp->koage
440 && pp->korate > fast_random(100)) {
441 if (board_is_valid_play(b, to_play, b->last_ko.coord)
442 && !is_bad_selfatari(b, to_play, b->last_ko.coord))
443 return b->last_ko.coord;
446 /* Local checks */
447 if (!is_pass(b->last_move.coord)) {
448 /* Nakade check */
449 if (pp->nakaderate > fast_random(100)
450 && immediate_liberty_count(b, b->last_move.coord) > 0) {
451 coord_t nakade = nakade_check(p, b, &b->last_move, to_play);
452 if (!is_pass(nakade))
453 return nakade;
456 /* Local group in atari? */
457 if (pp->lcapturerate > fast_random(100)) {
458 struct move_queue q; q.moves = 0;
459 local_atari_check(p, b, &b->last_move, &q);
460 if (q.moves > 0)
461 return mq_pick(&q);
464 /* Local group can be PUT in atari? */
465 if (pp->atarirate > fast_random(100)) {
466 struct move_queue q; q.moves = 0;
467 local_2lib_check(p, b, &b->last_move, &q);
468 if (q.moves > 0)
469 return mq_pick(&q);
472 /* Local group reduced some of our groups to 3 libs? */
473 if (pp->nlibrate > fast_random(100)) {
474 struct move_queue q; q.moves = 0;
475 local_nlib_check(p, b, &b->last_move, &q);
476 if (q.moves > 0)
477 return mq_pick(&q);
480 /* Check for patterns we know */
481 if (pp->patternrate > fast_random(100)) {
482 struct move_queue q; q.moves = 0;
483 apply_pattern(p, b, &b->last_move,
484 pp->pattern2 && b->last_move2.coord >= 0 ? &b->last_move2 : NULL,
485 &q);
486 if (q.moves > 0)
487 return mq_pick(&q);
491 /* Global checks */
493 /* Any groups in atari? */
494 if (pp->capturerate > fast_random(100)) {
495 struct move_queue q; q.moves = 0;
496 global_atari_check(p, b, to_play, &q);
497 if (q.moves > 0)
498 return mq_pick(&q);
501 /* Joseki moves? */
502 if (pp->josekirate > fast_random(100)) {
503 struct move_queue q; q.moves = 0;
504 joseki_check(p, b, to_play, &q);
505 if (q.moves > 0)
506 return mq_pick(&q);
509 /* Fill board */
510 if (pp->fillboardtries > 0) {
511 coord_t c = fillboard_check(p, b);
512 if (!is_pass(c))
513 return c;
516 return pass;
519 /* Pick a move from queue q, giving different likelihoods to moves
520 * based on their tags. */
521 coord_t
522 mq_tagged_choose(struct playout_policy *p, struct board *b, enum stone to_play, struct move_queue *q)
524 struct moggy_policy *pp = p->data;
526 /* First, merge all entries for a move. */
527 /* We use a naive O(N^2) since the average length of the queue
528 * is about 1.4. */
529 for (unsigned int i = 0; i < q->moves; i++) {
530 for (unsigned int j = i + 1; j < q->moves; j++) {
531 if (q->move[i] != q->move[j])
532 continue;
533 q->tag[i] |= q->tag[j];
534 q->moves--;
535 q->tag[j] = q->tag[q->moves];
536 q->move[j] = q->move[q->moves];
540 /* Now, construct a probdist. */
541 fixp_t total = 0;
542 fixp_t pd[q->moves];
543 for (unsigned int i = 0; i < q->moves; i++) {
544 double val = 1.0;
545 assert(q->tag[i] != 0);
546 for (int j = 0; j < MQ_MAX; j++)
547 if (q->tag[i] & (1<<j)) {
548 //fprintf(stderr, "%s(%x) %d %f *= %f\n", coord2sstr(q->move[i], b), q->tag[i], j, val, pp->mq_prob[j]);
549 val *= pp->mq_prob[j];
551 pd[i] = double_to_fixp(val);
552 total += pd[i];
554 total += double_to_fixp(pp->tenuki_prob);
556 /* Finally, pick a move! */
557 fixp_t stab = fast_irandom(total);
558 for (unsigned int i = 0; i < q->moves; i++) {
559 //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));
560 if (stab < pd[i])
561 return q->move[i];
562 stab -= pd[i];
565 /* Tenuki. */
566 assert(stab < double_to_fixp(pp->tenuki_prob));
567 return pass;
570 coord_t
571 playout_moggy_fullchoose(struct playout_policy *p, struct playout_setup *s, struct board *b, enum stone to_play)
573 struct moggy_policy *pp = p->data;
574 struct move_queue q; q.moves = 0;
576 if (PLDEBUGL(5))
577 board_print(b, stderr);
579 /* Ko fight check */
580 if (!is_pass(b->last_ko.coord) && is_pass(b->ko.coord)
581 && b->moves - b->last_ko_age < pp->koage) {
582 if (board_is_valid_play(b, to_play, b->last_ko.coord)
583 && !is_bad_selfatari(b, to_play, b->last_ko.coord))
584 mq_add(&q, b->last_ko.coord, 1<<MQ_KO);
587 /* Local checks */
588 if (!is_pass(b->last_move.coord)) {
589 /* Nakade check */
590 if (immediate_liberty_count(b, b->last_move.coord) > 0) {
591 coord_t nakade = nakade_check(p, b, &b->last_move, to_play);
592 if (!is_pass(nakade))
593 mq_add(&q, nakade, 1<<MQ_NAKADE);
596 /* Local group in atari? */
597 local_atari_check(p, b, &b->last_move, &q);
599 /* Local group can be PUT in atari? */
600 local_2lib_check(p, b, &b->last_move, &q);
602 /* Local group reduced some of our groups to 3 libs? */
603 local_nlib_check(p, b, &b->last_move, &q);
605 /* Check for patterns we know */
606 apply_pattern(p, b, &b->last_move,
607 pp->pattern2 && b->last_move2.coord >= 0 ? &b->last_move2 : NULL,
608 &q);
611 /* Global checks */
613 /* Any groups in atari? */
614 global_atari_check(p, b, to_play, &q);
616 /* Joseki moves? */
617 joseki_check(p, b, to_play, &q);
619 #if 0
620 /* Average length of the queue is 1.4 move. */
621 printf("MQL %d ", q.moves);
622 for (unsigned int i = 0; i < q.moves; i++)
623 printf("%s ", coord2sstr(q.move[i], b));
624 printf("\n");
625 #endif
627 if (q.moves > 0)
628 return mq_tagged_choose(p, b, to_play, &q);
630 /* Fill board */
631 if (pp->fillboardtries > 0) {
632 coord_t c = fillboard_check(p, b);
633 if (!is_pass(c))
634 return c;
637 return pass;
641 void
642 playout_moggy_assess_group(struct playout_policy *p, struct prior_map *map, group_t g, int games)
644 struct moggy_policy *pp = p->data;
645 struct board *b = map->b;
646 struct move_queue q; q.moves = 0;
648 if (board_group_info(b, g).libs > pp->nlib_count)
649 return;
651 if (PLDEBUGL(5)) {
652 fprintf(stderr, "ASSESS of group %s:\n", coord2sstr(g, b));
653 board_print(b, stderr);
656 if (board_group_info(b, g).libs > 2) {
657 if (!pp->nlibrate)
658 return;
659 if (board_at(b, g) != map->to_play)
660 return; // we do only defense
661 group_nlib_defense_check(b, g, map->to_play, &q, 0);
662 while (q.moves--) {
663 coord_t coord = q.move[q.moves];
664 if (PLDEBUGL(5))
665 fprintf(stderr, "1.0: nlib %s\n", coord2sstr(coord, b));
666 int assess = games / 2;
667 add_prior_value(map, coord, 1, assess);
669 return;
672 if (board_group_info(b, g).libs == 2) {
673 if (!pp->atarirate)
674 return;
675 group_2lib_check(b, g, map->to_play, &q, 0, pp->atari_miaisafe, pp->atari_def_no_hopeless);
676 while (q.moves--) {
677 coord_t coord = q.move[q.moves];
678 if (PLDEBUGL(5))
679 fprintf(stderr, "1.0: 2lib %s\n", coord2sstr(coord, b));
680 int assess = games / 2;
681 add_prior_value(map, coord, 1, assess);
683 return;
686 /* This group, sir, is in atari! */
688 coord_t ladder = pass;
689 group_atari_check(pp->alwaysccaprate, b, g, map->to_play, &q, &ladder, 0);
690 while (q.moves--) {
691 coord_t coord = q.move[q.moves];
693 /* _Never_ play here if this move plays out
694 * a caught ladder. */
695 if (coord == ladder && !board_playing_ko_threat(b)) {
696 /* Note that the opposite is not guarded against;
697 * we do not advise against capturing a laddered
698 * group (but we don't encourage it either). Such
699 * a move can simplify tactical situations if we
700 * can afford it. */
701 if (map->to_play != board_at(b, g))
702 continue;
703 /* FIXME: We give the malus even if this move
704 * captures another group. */
705 if (PLDEBUGL(5))
706 fprintf(stderr, "0.0: ladder %s\n", coord2sstr(coord, b));
707 add_prior_value(map, coord, 0, games);
708 continue;
711 if (!pp->capturerate && !pp->lcapturerate)
712 continue;
714 int assess = games * 2;
715 if (pp->cap_stone_denom > 0) {
716 int stones = group_stone_count(b, g, pp->cap_stone_max) - (pp->cap_stone_min-1);
717 assess += (stones > 0 ? stones : 0) * games * 100 / pp->cap_stone_denom;
719 if (PLDEBUGL(5))
720 fprintf(stderr, "1.0 (%d): atari %s\n", assess, coord2sstr(coord, b));
721 add_prior_value(map, coord, 1, assess);
725 void
726 playout_moggy_assess_one(struct playout_policy *p, struct prior_map *map, coord_t coord, int games)
728 struct moggy_policy *pp = p->data;
729 struct board *b = map->b;
731 if (PLDEBUGL(5)) {
732 fprintf(stderr, "ASSESS of move %s:\n", coord2sstr(coord, b));
733 board_print(b, stderr);
736 /* Is this move a self-atari? */
737 if (pp->selfatarirate) {
738 if (!board_playing_ko_threat(b) && is_bad_selfatari(b, map->to_play, coord)) {
739 if (PLDEBUGL(5))
740 fprintf(stderr, "0.0: self-atari\n");
741 add_prior_value(map, coord, 0, games);
742 if (!pp->selfatari_other)
743 return;
744 /* If we can play on the other liberty of the
745 * endangered group, do! */
746 coord = selfatari_cousin(b, map->to_play, coord);
747 if (is_pass(coord))
748 return;
749 if (PLDEBUGL(5))
750 fprintf(stderr, "1.0: self-atari redirect %s\n", coord2sstr(coord, b));
751 add_prior_value(map, coord, 1.0, games);
752 return;
756 /* Pattern check */
757 if (pp->patternrate) {
758 struct move m = { .color = map->to_play, .coord = coord };
759 if (test_pattern3_here(p, b, &m)) {
760 if (PLDEBUGL(5))
761 fprintf(stderr, "1.0: pattern\n");
762 add_prior_value(map, coord, 1, games);
766 return;
769 void
770 playout_moggy_assess(struct playout_policy *p, struct prior_map *map, int games)
772 struct moggy_policy *pp = p->data;
774 /* First, go through all endangered groups. */
775 for (group_t g = 1; g < board_size2(map->b); g++)
776 if (group_at(map->b, g) == g)
777 playout_moggy_assess_group(p, map, g, games);
779 /* Then, assess individual moves. */
780 if (!pp->patternrate && !pp->selfatarirate)
781 return;
782 foreach_free_point(map->b) {
783 if (map->consider[c])
784 playout_moggy_assess_one(p, map, c, games);
785 } foreach_free_point_end;
788 bool
789 playout_moggy_permit(struct playout_policy *p, struct board *b, struct move *m)
791 struct moggy_policy *pp = p->data;
793 /* The idea is simple for now - never allow self-atari moves.
794 * They suck in general, but this also permits us to actually
795 * handle seki in the playout stage. */
797 if (fast_random(100) >= pp->selfatarirate) {
798 if (PLDEBUGL(5))
799 fprintf(stderr, "skipping sar test\n");
800 return true;
802 bool selfatari = is_bad_selfatari(b, m->color, m->coord);
803 if (selfatari) {
804 if (PLDEBUGL(5))
805 fprintf(stderr, "__ Prohibiting self-atari %s %s\n",
806 stone2str(m->color), coord2sstr(m->coord, b));
807 if (pp->selfatari_other) {
808 /* Ok, try the other liberty of the atari'd group. */
809 coord_t c = selfatari_cousin(b, m->color, m->coord);
810 if (is_pass(c)) return false;
811 if (PLDEBUGL(5))
812 fprintf(stderr, "___ Redirecting to other lib %s\n",
813 coord2sstr(c, b));
814 m->coord = c;
815 return true;
817 return false;
819 return true;
823 struct playout_policy *
824 playout_moggy_init(char *arg, struct board *b, struct joseki_dict *jdict)
826 struct playout_policy *p = calloc2(1, sizeof(*p));
827 struct moggy_policy *pp = calloc2(1, sizeof(*pp));
828 p->data = pp;
829 p->choose = playout_moggy_seqchoose;
830 p->assess = playout_moggy_assess;
831 p->permit = playout_moggy_permit;
833 pp->jdict = jdict;
835 /* These settings are tuned for 19x19 play with several threads
836 * on reasonable time limits (i.e., rather large number of playouts).
837 * XXX: no 9x9 tuning has been done recently. */
838 int rate = board_large(b) ? 80 : 90;
840 pp->lcapturerate = pp->atarirate = pp->nlibrate = pp->patternrate
841 = pp->selfatarirate = pp->josekirate = -1U;
842 if (board_large(b)) {
843 pp->lcapturerate = 90;
844 pp->patternrate = 100;
845 pp->nlibrate = 20;
846 pp->pattern2 = true;
848 pp->korate = 20; pp->koage = 4;
849 pp->alwaysccaprate = 20;
850 pp->selfatari_other = true;
852 pp->cap_stone_min = 2;
853 pp->cap_stone_max = 10;
854 /* By default, stone weighing is turned off. Try values like 300
855 * or 200 for each stone weighing games/3 to games/2. */
856 pp->cap_stone_denom = 0;
858 pp->atari_def_no_hopeless = !board_large(b);
859 pp->atari_miaisafe = true;
860 pp->nlib_count = 4;
862 /* C is stupid. */
863 double mq_prob_default[MQ_MAX] = {
864 [MQ_KO] = 6.0,
865 [MQ_NAKADE] = 5.5,
866 [MQ_LATARI] = 5.0,
867 [MQ_L2LIB] = 4.0,
868 [MQ_LNLIB] = 3.5,
869 [MQ_PAT3] = 3.0,
870 [MQ_GATARI] = 2.0,
871 [MQ_JOSEKI] = 1.0,
873 memcpy(pp->mq_prob, mq_prob_default, sizeof(pp->mq_prob));
875 if (arg) {
876 char *optspec, *next = arg;
877 while (*next) {
878 optspec = next;
879 next += strcspn(next, ":");
880 if (*next) { *next++ = 0; } else { *next = 0; }
882 char *optname = optspec;
883 char *optval = strchr(optspec, '=');
884 if (optval) *optval++ = 0;
886 if (!strcasecmp(optname, "debug") && optval) {
887 p->debug_level = atoi(optval);
888 } else if (!strcasecmp(optname, "lcapturerate") && optval) {
889 pp->lcapturerate = atoi(optval);
890 } else if (!strcasecmp(optname, "atarirate") && optval) {
891 pp->atarirate = atoi(optval);
892 } else if (!strcasecmp(optname, "nlibrate") && optval) {
893 pp->nlibrate = atoi(optval);
894 } else if (!strcasecmp(optname, "capturerate") && optval) {
895 pp->capturerate = atoi(optval);
896 } else if (!strcasecmp(optname, "patternrate") && optval) {
897 pp->patternrate = atoi(optval);
898 } else if (!strcasecmp(optname, "selfatarirate") && optval) {
899 pp->selfatarirate = atoi(optval);
900 } else if (!strcasecmp(optname, "korate") && optval) {
901 pp->korate = atoi(optval);
902 } else if (!strcasecmp(optname, "josekirate") && optval) {
903 pp->josekirate = atoi(optval);
904 } else if (!strcasecmp(optname, "nakaderate") && optval) {
905 pp->nakaderate = atoi(optval);
906 } else if (!strcasecmp(optname, "alwaysccaprate") && optval) {
907 pp->alwaysccaprate = atoi(optval);
908 } else if (!strcasecmp(optname, "rate") && optval) {
909 rate = atoi(optval);
910 } else if (!strcasecmp(optname, "fillboardtries")) {
911 pp->fillboardtries = atoi(optval);
912 } else if (!strcasecmp(optname, "koage") && optval) {
913 pp->koage = atoi(optval);
914 } else if (!strcasecmp(optname, "pattern2")) {
915 pp->pattern2 = optval && *optval == '0' ? false : true;
916 } else if (!strcasecmp(optname, "selfatari_other")) {
917 pp->selfatari_other = optval && *optval == '0' ? false : true;
918 } else if (!strcasecmp(optname, "capcheckall")) {
919 pp->capcheckall = optval && *optval == '0' ? false : true;
920 } else if (!strcasecmp(optname, "cap_stone_min") && optval) {
921 pp->cap_stone_min = atoi(optval);
922 } else if (!strcasecmp(optname, "cap_stone_max") && optval) {
923 pp->cap_stone_max = atoi(optval);
924 } else if (!strcasecmp(optname, "cap_stone_denom") && optval) {
925 pp->cap_stone_denom = atoi(optval);
926 } else if (!strcasecmp(optname, "atari_miaisafe")) {
927 pp->atari_miaisafe = optval && *optval == '0' ? false : true;
928 } else if (!strcasecmp(optname, "atari_def_no_hopeless")) {
929 pp->atari_def_no_hopeless = optval && *optval == '0' ? false : true;
930 } else if (!strcasecmp(optname, "nlib_count") && optval) {
931 pp->nlib_count = atoi(optval);
932 } else if (!strcasecmp(optname, "fullchoose")) {
933 p->choose = optval && *optval == '0' ? playout_moggy_seqchoose : playout_moggy_fullchoose;
934 } else if (!strcasecmp(optname, "mqprob") && optval) {
935 /* KO%LATARI%L2LIB%LNLIB%PAT3%GATARI%JOSEKI%NAKADE */
936 for (int i = 0; *optval && i < MQ_MAX; i++, optval += strcspn(optval, "%")) {
937 optval++;
938 pp->mq_prob[i] = atof(optval);
940 } else if (!strcasecmp(optname, "tenukiprob") && optval) {
941 pp->tenuki_prob = atof(optval);
942 } else {
943 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
944 exit(1);
948 if (pp->lcapturerate == -1U) pp->lcapturerate = rate;
949 if (pp->atarirate == -1U) pp->atarirate = rate;
950 if (pp->capturerate == -1U) pp->capturerate = rate;
951 if (pp->patternrate == -1U) pp->patternrate = rate;
952 if (pp->selfatarirate == -1U) pp->selfatarirate = rate;
953 if (pp->korate == -1U) pp->korate = rate;
954 if (pp->josekirate == -1U) pp->josekirate = rate;
955 if (pp->nakaderate == -1U) pp->nakaderate = rate;
956 if (pp->alwaysccaprate == -1U) pp->alwaysccaprate = rate;
958 pattern3s_init(&pp->patterns, moggy_patterns_src, moggy_patterns_src_n);
960 return p;