Moggy 1lib prior: Add support for stone weighing, turned off by default
[pachi/json.git] / playout / moggy.c
blob234d749470717163b7f24c3737a0e5bcd184e487
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 stones = group_stone_count(b, g, pp->cap_stone_max) - (pp->cap_stone_min-1);
715 int assess = games * 2 + (stones > 0 ? stones : 0) * games * 100 / pp->cap_stone_denom;
716 if (PLDEBUGL(5))
717 fprintf(stderr, "1.0 (%d): atari %s\n", assess, coord2sstr(coord, b));
718 add_prior_value(map, coord, 1, assess);
722 void
723 playout_moggy_assess_one(struct playout_policy *p, struct prior_map *map, coord_t coord, int games)
725 struct moggy_policy *pp = p->data;
726 struct board *b = map->b;
728 if (PLDEBUGL(5)) {
729 fprintf(stderr, "ASSESS of move %s:\n", coord2sstr(coord, b));
730 board_print(b, stderr);
733 /* Is this move a self-atari? */
734 if (pp->selfatarirate) {
735 if (!board_playing_ko_threat(b) && is_bad_selfatari(b, map->to_play, coord)) {
736 if (PLDEBUGL(5))
737 fprintf(stderr, "0.0: self-atari\n");
738 add_prior_value(map, coord, 0, games);
739 if (!pp->selfatari_other)
740 return;
741 /* If we can play on the other liberty of the
742 * endangered group, do! */
743 coord = selfatari_cousin(b, map->to_play, coord);
744 if (is_pass(coord))
745 return;
746 if (PLDEBUGL(5))
747 fprintf(stderr, "1.0: self-atari redirect %s\n", coord2sstr(coord, b));
748 add_prior_value(map, coord, 1.0, games);
749 return;
753 /* Pattern check */
754 if (pp->patternrate) {
755 struct move m = { .color = map->to_play, .coord = coord };
756 if (test_pattern3_here(p, b, &m)) {
757 if (PLDEBUGL(5))
758 fprintf(stderr, "1.0: pattern\n");
759 add_prior_value(map, coord, 1, games);
763 return;
766 void
767 playout_moggy_assess(struct playout_policy *p, struct prior_map *map, int games)
769 struct moggy_policy *pp = p->data;
771 /* First, go through all endangered groups. */
772 for (group_t g = 1; g < board_size2(map->b); g++)
773 if (group_at(map->b, g) == g)
774 playout_moggy_assess_group(p, map, g, games);
776 /* Then, assess individual moves. */
777 if (!pp->patternrate && !pp->selfatarirate)
778 return;
779 foreach_free_point(map->b) {
780 if (map->consider[c])
781 playout_moggy_assess_one(p, map, c, games);
782 } foreach_free_point_end;
785 bool
786 playout_moggy_permit(struct playout_policy *p, struct board *b, struct move *m)
788 struct moggy_policy *pp = p->data;
790 /* The idea is simple for now - never allow self-atari moves.
791 * They suck in general, but this also permits us to actually
792 * handle seki in the playout stage. */
794 if (fast_random(100) >= pp->selfatarirate) {
795 if (PLDEBUGL(5))
796 fprintf(stderr, "skipping sar test\n");
797 return true;
799 bool selfatari = is_bad_selfatari(b, m->color, m->coord);
800 if (selfatari) {
801 if (PLDEBUGL(5))
802 fprintf(stderr, "__ Prohibiting self-atari %s %s\n",
803 stone2str(m->color), coord2sstr(m->coord, b));
804 if (pp->selfatari_other) {
805 /* Ok, try the other liberty of the atari'd group. */
806 coord_t c = selfatari_cousin(b, m->color, m->coord);
807 if (is_pass(c)) return false;
808 if (PLDEBUGL(5))
809 fprintf(stderr, "___ Redirecting to other lib %s\n",
810 coord2sstr(c, b));
811 m->coord = c;
812 return true;
814 return false;
816 return true;
820 struct playout_policy *
821 playout_moggy_init(char *arg, struct board *b, struct joseki_dict *jdict)
823 struct playout_policy *p = calloc2(1, sizeof(*p));
824 struct moggy_policy *pp = calloc2(1, sizeof(*pp));
825 p->data = pp;
826 p->choose = playout_moggy_seqchoose;
827 p->assess = playout_moggy_assess;
828 p->permit = playout_moggy_permit;
830 pp->jdict = jdict;
832 /* These settings are tuned for 19x19 play with several threads
833 * on reasonable time limits (i.e., rather large number of playouts).
834 * XXX: no 9x9 tuning has been done recently. */
835 int rate = board_large(b) ? 80 : 90;
837 pp->lcapturerate = pp->atarirate = pp->nlibrate = pp->patternrate
838 = pp->selfatarirate = pp->josekirate = -1U;
839 if (board_large(b)) {
840 pp->lcapturerate = 90;
841 pp->patternrate = 100;
842 pp->nlibrate = 20;
843 pp->pattern2 = true;
845 pp->korate = 20; pp->koage = 4;
846 pp->alwaysccaprate = 20;
847 pp->selfatari_other = true;
849 pp->cap_stone_min = 2;
850 pp->cap_stone_max = 10;
851 /* By default, stone weighing is turned off. Try values like 300
852 * or 200 for each stone weighing games/3 to games/2. */
853 pp->cap_stone_denom = 0;
855 pp->atari_def_no_hopeless = !board_large(b);
856 pp->atari_miaisafe = true;
857 pp->nlib_count = 4;
859 /* C is stupid. */
860 double mq_prob_default[MQ_MAX] = {
861 [MQ_KO] = 6.0,
862 [MQ_NAKADE] = 5.5,
863 [MQ_LATARI] = 5.0,
864 [MQ_L2LIB] = 4.0,
865 [MQ_LNLIB] = 3.5,
866 [MQ_PAT3] = 3.0,
867 [MQ_GATARI] = 2.0,
868 [MQ_JOSEKI] = 1.0,
870 memcpy(pp->mq_prob, mq_prob_default, sizeof(pp->mq_prob));
872 if (arg) {
873 char *optspec, *next = arg;
874 while (*next) {
875 optspec = next;
876 next += strcspn(next, ":");
877 if (*next) { *next++ = 0; } else { *next = 0; }
879 char *optname = optspec;
880 char *optval = strchr(optspec, '=');
881 if (optval) *optval++ = 0;
883 if (!strcasecmp(optname, "debug") && optval) {
884 p->debug_level = atoi(optval);
885 } else if (!strcasecmp(optname, "lcapturerate") && optval) {
886 pp->lcapturerate = atoi(optval);
887 } else if (!strcasecmp(optname, "atarirate") && optval) {
888 pp->atarirate = atoi(optval);
889 } else if (!strcasecmp(optname, "nlibrate") && optval) {
890 pp->nlibrate = atoi(optval);
891 } else if (!strcasecmp(optname, "capturerate") && optval) {
892 pp->capturerate = atoi(optval);
893 } else if (!strcasecmp(optname, "patternrate") && optval) {
894 pp->patternrate = atoi(optval);
895 } else if (!strcasecmp(optname, "selfatarirate") && optval) {
896 pp->selfatarirate = atoi(optval);
897 } else if (!strcasecmp(optname, "korate") && optval) {
898 pp->korate = atoi(optval);
899 } else if (!strcasecmp(optname, "josekirate") && optval) {
900 pp->josekirate = atoi(optval);
901 } else if (!strcasecmp(optname, "nakaderate") && optval) {
902 pp->nakaderate = atoi(optval);
903 } else if (!strcasecmp(optname, "alwaysccaprate") && optval) {
904 pp->alwaysccaprate = atoi(optval);
905 } else if (!strcasecmp(optname, "rate") && optval) {
906 rate = atoi(optval);
907 } else if (!strcasecmp(optname, "fillboardtries")) {
908 pp->fillboardtries = atoi(optval);
909 } else if (!strcasecmp(optname, "koage") && optval) {
910 pp->koage = atoi(optval);
911 } else if (!strcasecmp(optname, "pattern2")) {
912 pp->pattern2 = optval && *optval == '0' ? false : true;
913 } else if (!strcasecmp(optname, "selfatari_other")) {
914 pp->selfatari_other = optval && *optval == '0' ? false : true;
915 } else if (!strcasecmp(optname, "capcheckall")) {
916 pp->capcheckall = optval && *optval == '0' ? false : true;
917 } else if (!strcasecmp(optname, "cap_stone_min") && optval) {
918 pp->cap_stone_min = atoi(optval);
919 } else if (!strcasecmp(optname, "cap_stone_max") && optval) {
920 pp->cap_stone_max = atoi(optval);
921 } else if (!strcasecmp(optname, "cap_stone_denom") && optval) {
922 pp->cap_stone_denom = atoi(optval);
923 } else if (!strcasecmp(optname, "atari_miaisafe")) {
924 pp->atari_miaisafe = optval && *optval == '0' ? false : true;
925 } else if (!strcasecmp(optname, "atari_def_no_hopeless")) {
926 pp->atari_def_no_hopeless = optval && *optval == '0' ? false : true;
927 } else if (!strcasecmp(optname, "nlib_count") && optval) {
928 pp->nlib_count = atoi(optval);
929 } else if (!strcasecmp(optname, "fullchoose")) {
930 p->choose = optval && *optval == '0' ? playout_moggy_seqchoose : playout_moggy_fullchoose;
931 } else if (!strcasecmp(optname, "mqprob") && optval) {
932 /* KO%LATARI%L2LIB%LNLIB%PAT3%GATARI%JOSEKI%NAKADE */
933 for (int i = 0; *optval && i < MQ_MAX; i++, optval += strcspn(optval, "%")) {
934 optval++;
935 pp->mq_prob[i] = atof(optval);
937 } else if (!strcasecmp(optname, "tenukiprob") && optval) {
938 pp->tenuki_prob = atof(optval);
939 } else {
940 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
941 exit(1);
945 if (pp->lcapturerate == -1U) pp->lcapturerate = rate;
946 if (pp->atarirate == -1U) pp->atarirate = rate;
947 if (pp->capturerate == -1U) pp->capturerate = rate;
948 if (pp->patternrate == -1U) pp->patternrate = rate;
949 if (pp->selfatarirate == -1U) pp->selfatarirate = rate;
950 if (pp->korate == -1U) pp->korate = rate;
951 if (pp->josekirate == -1U) pp->josekirate = rate;
952 if (pp->nakaderate == -1U) pp->nakaderate = rate;
953 if (pp->alwaysccaprate == -1U) pp->alwaysccaprate = rate;
955 pattern3s_init(&pp->patterns, moggy_patterns_src, moggy_patterns_src_n);
957 return p;