UCT: bestr_ratio 0.02 by default, best2_ratio 2.5 by default
[pachi/t.git] / playout / moggy.c
blobf0caea37c1230dbe7f2e797ee7ba9f2767c16874
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 "mq.h"
14 #include "pattern3.h"
15 #include "playout.h"
16 #include "playout/moggy.h"
17 #include "random.h"
18 #include "tactics.h"
19 #include "uct/prior.h"
21 #define PLDEBUGL(n) DEBUGL_(p->debug_level, n)
23 /* Whether to avoid capturing/atariing doomed groups (this is big
24 * performance hit and may reduce playouts balance; it does increase
25 * the strength, but not quite proportionally to the performance). */
26 //#define NO_DOOMED_GROUPS
29 /* Note that the context can be shared by multiple threads! */
31 struct moggy_policy {
32 bool ladders, ladderassess, borderladders, assess_local;
33 int lcapturerate, atarirate, capturerate, patternrate;
34 int selfatarirate;
35 int fillboardtries;
36 /* Whether to look for patterns around second-to-last move. */
37 bool pattern2;
39 struct pattern3s patterns;
43 struct group_state {
44 enum {
45 G_ATARI,
46 G_2LIB, /* Unused. */
47 G_SAFE /* Unused. */
48 } status:2;
50 /* Below, we keep track of each trait for each |color_to_play */
51 int capturable_ready:2; // is @capturable meaningful?
52 int capturable:2;
54 int can_countercapture_ready:2;
55 int can_countercapture:2;
58 /* Cache of evaluation of various board features. */
59 struct board_state {
60 int bsize2;
61 hash_t hash;
62 struct group_state *groups; /* [board_size2()], indexed by group_t */
63 unsigned char *groups_known; /* Bitmap of known groups. */
66 /* Using board cache: this turns out to be actually a 10% slowdown,
67 * since we reuse data in the cache only very little within single
68 * move. */
69 // #define CACHE_STATE
70 /* Reusing board cache across moves if they are successive on the
71 * board; only cache entries within cfg distance 2 of the last move
72 * are cleared. */
73 // #define PERSISTENT_STATE
75 #ifdef CACHE_STATE
76 static __thread struct board_state *ss;
78 static bool
79 board_state_reuse(struct board_state *s, struct board *b)
81 /* Decide how much of the board state we can reuse. */
82 /* We do not cache ladder decisions, so we don't have
83 * to worry about this. */
84 coord_t c = b->last_move.coord;
86 if (unlikely(is_pass(c))) {
87 /* Passes don't change anything. */
88 return true;
91 if (unlikely(board_at(b, c) == S_NONE)) {
92 /* Suicide is hopeless. */
93 return false;
96 /* XXX: we can make some moves self-atari. */
98 if (neighbor_count_at(b, c, S_BLACK) + neighbor_count_at(b, c, S_WHITE) == 0) {
99 /* We are not taking off liberties of any other stones. */
100 return true;
103 return false;
106 static inline struct board_state *
107 board_state_init(struct board *b)
109 if (ss) {
110 if (ss->bsize2 != board_size2(b)) {
111 free(ss->groups);
112 free(ss->groups_known);
113 free(ss); ss = NULL;
115 #ifdef PERSISTENT_STATE
116 /* Only one stone added to the board, nothing removed. */
117 else if (ss->hash == (b->hash ^ hash_at(b, b->last_move.coord, b->last_move.color))) {
118 ss->hash = b->hash;
119 if (likely(board_state_reuse(ss, b)))
120 return ss;
122 #endif
124 if (!ss) {
125 ss = malloc(sizeof(*ss));
126 ss->bsize2 = board_size2(b);
127 ss->groups = malloc(board_size2(b) * sizeof(*ss->groups));
128 ss->groups_known = malloc(board_size2(b) / 8 + 1);
130 ss->hash = b->hash;
131 memset(ss->groups_known, 0, board_size2(b) / 8 + 1);
132 return ss;
135 #define group_is_known(s, g) (s->groups_known[g >> 3] & (1 << (g & 7)))
136 #define group_set_known(s, g) (s->groups_known[g >> 3] |= (1 << (g & 7)))
137 #define group_trait_ready(s, g, color, gstat, trait) do { \
138 if (!group_is_known(s, g)) { \
139 memset(&s->groups[g], 0, sizeof(s->groups[g])); \
140 group_set_known(s, g); \
142 s->groups[g].status = gstat; \
143 s->groups[g].trait ## _ready |= color; \
144 } while (0)
145 #define group_trait_is_ready(s, g, color, trait) (s->groups[g].trait ## _ready & color)
146 #define group_trait_set(s, g, color, trait, val) s->groups[g].trait = (s->groups[g].trait & ~color) | (!!val * color)
147 #define group_trait_get(s, g, color, trait) (s->groups[g].trait & color)
149 #else
151 #define board_state_init(b) NULL
152 #define group_is_known(s, g) false
153 #define group_set_known(s, g)
154 #define group_trait_ready(s, g, color, gstat, trait)
155 #define group_trait_is_ready(s, g, color, trait) false
156 #define group_trait_set(s, g, color, trait, val)
157 #define group_trait_get(s, g, color, trait) false
158 #endif
161 static char moggy_patterns_src[][11] = {
162 /* hane pattern - enclosing hane */
163 "XOX"
164 "..."
165 "???",
166 /* hane pattern - non-cutting hane */
167 "XO."
168 "..."
169 "?.?",
170 /* hane pattern - magari */
171 "XO?"
172 "X.."
173 "x.?",
174 /* hane pattern - thin hane */
175 "XOO"
176 "..."
177 "?.?" "X",
178 /* generic pattern - katatsuke or diagonal attachment; similar to magari */
179 ".O."
180 "X.."
181 "...",
182 /* cut1 pattern (kiri) - unprotected cut */
183 "XO?"
184 "O.o"
185 "?o?",
186 /* cut1 pattern (kiri) - peeped cut */
187 "XO?"
188 "O.X"
189 "???",
190 /* cut2 pattern (de) */
191 "?X?"
192 "O.O"
193 "ooo",
194 /* cut keima (not in Mogo) */
195 "OX?"
196 "o.O"
197 "???", /* o?? has some pathological tsumego cases */
198 /* side pattern - chase */
199 "X.?"
200 "O.?"
201 "##?",
202 /* side pattern - weirdness (SUSPICIOUS) */
203 "?X?"
204 "X.O"
205 "###",
206 /* side pattern - sagari (SUSPICIOUS) */
207 "?XO"
208 "x.x" /* Mogo has "x.?" */
209 "###" /* Mogo has "X" */,
210 /* side pattern - throw-in (SUSPICIOUS) */
211 #if 0
212 "?OX"
213 "o.O"
214 "?##" "X",
215 #endif
216 /* side pattern - cut (SUSPICIOUS) */
217 "?OX"
218 "X.O"
219 "###" /* Mogo has "X" */,
221 #define moggy_patterns_src_n sizeof(moggy_patterns_src) / sizeof(moggy_patterns_src[0])
223 static inline bool
224 test_pattern3_here(struct playout_policy *p, struct board *b, struct move *m)
226 struct moggy_policy *pp = p->data;
227 /* Check if 3x3 pattern is matched by given move... */
228 if (!pattern3_move_here(&pp->patterns, b, m))
229 return false;
230 /* ...and the move is not obviously stupid. */
231 if (is_bad_selfatari(b, m->color, m->coord))
232 return false;
233 /* Ladder moves are stupid. */
234 group_t atari_neighbor = board_get_atari_neighbor(b, m->coord, m->color);
235 if (atari_neighbor && is_ladder(b, m->coord, atari_neighbor, pp->borderladders, pp->ladders))
236 return false;
237 return true;
240 static void
241 apply_pattern_here(struct playout_policy *p, struct board *b, coord_t c, enum stone color, struct move_queue *q)
243 struct move m2 = { .coord = c, .color = color };
244 if (board_is_valid_move(b, &m2) && test_pattern3_here(p, b, &m2))
245 mq_add(q, c);
248 /* Check if we match any pattern around given move (with the other color to play). */
249 static coord_t
250 apply_pattern(struct playout_policy *p, struct board *b, struct move *m, struct move *mm)
252 struct move_queue q;
253 q.moves = 0;
255 /* Suicides do not make any patterns and confuse us. */
256 if (board_at(b, m->coord) == S_NONE || board_at(b, m->coord) == S_OFFBOARD)
257 return pass;
259 foreach_8neighbor(b, m->coord) {
260 apply_pattern_here(p, b, c, stone_other(m->color), &q);
261 } foreach_8neighbor_end;
263 if (mm) { /* Second move for pattern searching */
264 foreach_8neighbor(b, mm->coord) {
265 if (coord_is_8adjecent(m->coord, c, b))
266 continue;
267 apply_pattern_here(p, b, c, stone_other(m->color), &q);
268 } foreach_8neighbor_end;
271 if (PLDEBUGL(5))
272 mq_print(&q, b, "Pattern");
274 return mq_pick(&q);
278 static bool
279 can_play_on_lib(struct playout_policy *p, struct board_state *s,
280 struct board *b, group_t g, enum stone to_play)
282 if (group_is_known(s, g) && group_trait_is_ready(s, g, to_play, capturable)) {
283 /* We have already seen this group. */
284 assert(s->groups[g].status == G_ATARI);
285 if (group_trait_get(s, g, to_play, capturable))
286 return true;
287 else
288 return false;
291 /* Cache miss. Set up cache entry, default at capturable = false. */
292 group_trait_ready(s, g, to_play, G_ATARI, capturable);
294 coord_t capture = board_group_info(b, g).lib[0];
295 if (PLDEBUGL(6))
296 fprintf(stderr, "can capture group %d (%s)?\n",
297 g, coord2sstr(capture, b));
298 /* Does playing on the liberty usefully capture the group? */
299 struct move m; m.color = to_play; m.coord = capture;
300 if (board_is_valid_move(b, &m) && !is_bad_selfatari(b, to_play, capture)) {
301 group_trait_set(s, g, to_play, capturable, true);
302 return true;
305 return false;
308 /* For given position @c, decide if this is a group that is in danger from
309 * @capturer and @to_play can do anything about it (play at the last
310 * liberty to either capture or escape). */
311 /* Note that @to_play is important; e.g. consider snapback, it's good
312 * to play at the last liberty by attacker, but not defender. */
313 static __attribute__((always_inline)) bool
314 capturable_group(struct playout_policy *p, struct board_state *s,
315 struct board *b, enum stone capturer, coord_t c,
316 enum stone to_play)
318 group_t g = group_at(b, c);
319 if (likely(board_at(b, c) != stone_other(capturer)
320 || board_group_info(b, g).libs > 1))
321 return false;
323 return can_play_on_lib(p, s, b, g, to_play);
326 /* For given atari group @group owned by @owner, decide if @to_play
327 * can save it / keep it in danger by dealing with one of the
328 * neighboring groups. */
329 static bool
330 can_countercapture(struct playout_policy *p, struct board_state *s,
331 struct board *b, enum stone owner, group_t g,
332 enum stone to_play, struct move_queue *q)
334 if (b->clen < 2)
335 return false;
336 if (group_is_known(s, g) && group_trait_is_ready(s, g, to_play, can_countercapture)) {
337 /* We have already seen this group. */
338 assert(s->groups[g].status == G_ATARI);
339 if (group_trait_get(s, g, to_play, can_countercapture)) {
340 if (q) { /* Scan for countercapture liberties. */
341 goto scan;
343 return true;
344 } else {
345 return false;
349 /* Cache miss. Set up cache entry, default at can_countercapture = true. */
350 group_trait_ready(s, g, to_play, G_ATARI, can_countercapture);
351 group_trait_set(s, g, to_play, can_countercapture, true);
353 scan:;
354 int qmoves_prev = q ? q->moves : 0;
356 foreach_in_group(b, g) {
357 foreach_neighbor(b, c, {
358 if (!capturable_group(p, s, b, owner, c, to_play))
359 continue;
361 if (!q) {
362 return true;
364 mq_add(q, board_group_info(b, group_at(b, c)).lib[0]);
365 mq_nodup(q);
367 } foreach_in_group_end;
369 bool can = q ? q->moves > qmoves_prev : false;
370 group_trait_set(s, g, to_play, can_countercapture, can);
371 return can;
374 #ifdef NO_DOOMED_GROUPS
375 static bool
376 can_be_rescued(struct playout_policy *p, struct board_state *s,
377 struct board *b, group_t group, enum stone color)
379 /* Does playing on the liberty rescue the group? */
380 if (can_play_on_lib(p, s, b, group, color))
381 return true;
383 /* Then, maybe we can capture one of our neighbors? */
384 return can_countercapture(p, s, b, color, group, color, NULL);
386 #endif
388 static void
389 group_atari_check(struct playout_policy *p, struct board *b, group_t group, enum stone to_play,
390 struct move_queue *q, coord_t *ladder, struct board_state *s)
392 struct moggy_policy *pp = p->data;
393 int qmoves_prev = q->moves;
395 /* We don't use @to_play almost anywhere since any moves here are good
396 * for both defender and attacker. */
398 enum stone color = board_at(b, group_base(group));
399 coord_t lib = board_group_info(b, group).lib[0];
401 assert(color != S_OFFBOARD && color != S_NONE);
402 if (PLDEBUGL(5))
403 fprintf(stderr, "[%s] atariiiiiiiii %s of color %d\n",
404 coord2sstr(group, b), coord2sstr(lib, b), color);
405 assert(board_at(b, lib) == S_NONE);
407 /* Do not bother with kos. */
408 if (group_is_onestone(b, group)
409 && neighbor_count_at(b, lib, color) + neighbor_count_at(b, lib, S_OFFBOARD) == 4)
410 return;
412 /* Can we capture some neighbor? */
413 can_countercapture(p, s, b, color, group, to_play, q);
415 /* Do not suicide... */
416 if (!can_play_on_lib(p, s, b, group, to_play))
417 return;
418 #ifdef NO_DOOMED_GROUPS
419 /* Do not remove group that cannot be saved by the opponent. */
420 if (to_play != color && !can_be_rescued(p, s, b, group, color))
421 return;
422 #endif
423 if (PLDEBUGL(6))
424 fprintf(stderr, "...escape route valid\n");
426 /* ...or play out ladders. */
427 if (is_ladder(b, lib, group, pp->borderladders, pp->ladders)) {
428 /* Sometimes we want to keep the ladder move in the
429 * queue in order to discourage it. */
430 if (!ladder)
431 return;
432 else
433 *ladder = lib;
435 if (PLDEBUGL(6))
436 fprintf(stderr, "...no ladder\n");
438 if (to_play != color) {
439 /* We are the attacker! In that case, throw away the moves
440 * that defend our groups, since we can capture the culprit. */
441 q->moves = qmoves_prev;
444 mq_add(q, lib);
445 mq_nodup(q);
448 static coord_t
449 global_atari_check(struct playout_policy *p, struct board *b, enum stone to_play, struct board_state *s)
451 struct move_queue q;
452 q.moves = 0;
454 if (b->clen == 0)
455 return pass;
457 int g_base = fast_random(b->clen);
458 for (int g = g_base; g < b->clen; g++) {
459 group_atari_check(p, b, group_at(b, group_base(b->c[g])), to_play, &q, NULL, s);
460 if (q.moves > 0)
461 return mq_pick(&q);
463 for (int g = 0; g < g_base; g++) {
464 group_atari_check(p, b, group_at(b, group_base(b->c[g])), to_play, &q, NULL, s);
465 if (q.moves > 0)
466 return mq_pick(&q);
468 return pass;
471 static coord_t
472 local_atari_check(struct playout_policy *p, struct board *b, struct move *m, struct board_state *s)
474 struct move_queue q;
475 q.moves = 0;
477 /* Did the opponent play a self-atari? */
478 if (board_group_info(b, group_at(b, m->coord)).libs == 1) {
479 group_atari_check(p, b, group_at(b, m->coord), stone_other(m->color), &q, NULL, s);
482 foreach_neighbor(b, m->coord, {
483 group_t g = group_at(b, c);
484 if (!g || board_group_info(b, g).libs != 1)
485 continue;
486 group_atari_check(p, b, g, stone_other(m->color), &q, NULL, s);
489 if (PLDEBUGL(5))
490 mq_print(&q, b, "Local atari");
492 return mq_pick(&q);
495 static bool
496 miai_2lib(struct board *b, group_t group, enum stone color)
498 bool can_connect = false, can_pull_out = false;
499 /* We have miai if we can either connect on both libs,
500 * or connect on one lib and escape on another. (Just
501 * having two escape routes can be risky.) */
502 foreach_neighbor(b, board_group_info(b, group).lib[0], {
503 enum stone cc = board_at(b, c);
504 if (cc == S_NONE && cc != board_group_info(b, group).lib[1]) {
505 can_pull_out = true;
506 } else if (cc != color) {
507 continue;
510 group_t cg = group_at(b, c);
511 if (cg && cg != group && board_group_info(b, cg).libs > 1)
512 can_connect = true;
514 foreach_neighbor(b, board_group_info(b, group).lib[1], {
515 enum stone cc = board_at(b, c);
516 if (cc == S_NONE && cc != board_group_info(b, group).lib[0] && can_connect) {
517 return true;
518 } else if (cc != color) {
519 continue;
522 group_t cg = group_at(b, c);
523 if (cg && cg != group && board_group_info(b, cg).libs > 1)
524 return (can_connect || can_pull_out);
526 return false;
529 static void
530 check_group_atari(struct board *b, group_t group, enum stone owner,
531 enum stone to_play, struct move_queue *q)
533 for (int i = 0; i < 2; i++) {
534 coord_t lib = board_group_info(b, group).lib[i];
535 assert(board_at(b, lib) == S_NONE);
536 struct move m; m.color = to_play; m.coord = lib;
537 if (!board_is_valid_move(b, &m))
538 continue;
540 /* Don't play at the spot if it is extremely short
541 * of liberties... */
542 /* XXX: This looks harmful, could significantly
543 * prefer atari to throwin:
545 * XXXOOOOOXX
546 * .OO.....OX
547 * XXXOOOOOOX */
548 #if 0
549 if (neighbor_count_at(b, lib, stone_other(owner)) + immediate_liberty_count(b, lib) < 2)
550 continue;
551 #endif
553 #ifdef NO_DOOMED_GROUPS
554 /* If the owner can't play at the spot, we don't want
555 * to bother either. */
556 if (is_bad_selfatari(b, owner, lib))
557 continue;
558 #endif
560 /* Of course we don't want to play bad selfatari
561 * ourselves, if we are the attacker... */
562 if (
563 #ifdef NO_DOOMED_GROUPS
564 to_play != owner &&
565 #endif
566 is_bad_selfatari(b, to_play, lib))
567 continue;
569 /* Tasty! Crispy! Good! */
570 mq_add(q, lib);
571 mq_nodup(q);
575 static void
576 group_2lib_check(struct playout_policy *p, struct board *b, group_t group, enum stone to_play,
577 struct move_queue *q, struct board_state *s)
579 enum stone color = board_at(b, group_base(group));
580 assert(color != S_OFFBOARD && color != S_NONE);
582 if (PLDEBUGL(5))
583 fprintf(stderr, "[%s] 2lib check of color %d\n",
584 coord2sstr(group, b), color);
586 /* Do not try to atari groups that cannot be harmed. */
587 if (miai_2lib(b, group, color))
588 return;
590 check_group_atari(b, group, color, to_play, q);
592 /* Can we counter-atari another group, if we are the defender? */
593 if (to_play != color)
594 return;
595 foreach_in_group(b, group) {
596 foreach_neighbor(b, c, {
597 if (board_at(b, c) != stone_other(color))
598 continue;
599 group_t g2 = group_at(b, c);
600 if (board_group_info(b, g2).libs != 2)
601 continue;
602 check_group_atari(b, g2, color, to_play, q);
604 } foreach_in_group_end;
607 static coord_t
608 local_2lib_check(struct playout_policy *p, struct board *b, struct move *m, struct board_state *s)
610 struct move_queue q;
611 q.moves = 0;
613 /* Does the opponent have just two liberties? */
614 if (board_group_info(b, group_at(b, m->coord)).libs == 2) {
615 group_2lib_check(p, b, group_at(b, m->coord), stone_other(m->color), &q, s);
616 #if 0
617 /* We always prefer to take off an enemy chain liberty
618 * before pulling out ourselves. */
619 /* XXX: We aren't guaranteed to return to that group
620 * later. */
621 if (q.moves)
622 return q.move[fast_random(q.moves)];
623 #endif
626 /* Then he took a third liberty from neighboring chain? */
627 foreach_neighbor(b, m->coord, {
628 group_t g = group_at(b, c);
629 if (!g || board_group_info(b, g).libs != 2)
630 continue;
631 group_2lib_check(p, b, g, stone_other(m->color), &q, s);
634 if (PLDEBUGL(5))
635 mq_print(&q, b, "Local 2lib");
637 return mq_pick(&q);
640 coord_t
641 playout_moggy_choose(struct playout_policy *p, struct board *b, enum stone to_play)
643 struct moggy_policy *pp = p->data;
644 coord_t c;
646 struct board_state *s = board_state_init(b);
648 if (PLDEBUGL(5))
649 board_print(b, stderr);
651 /* Local checks */
652 if (!is_pass(b->last_move.coord)) {
653 /* Local group in atari? */
654 if (pp->lcapturerate > fast_random(100)) {
655 c = local_atari_check(p, b, &b->last_move, s);
656 if (!is_pass(c))
657 return c;
660 /* Local group can be PUT in atari? */
661 if (pp->atarirate > fast_random(100)) {
662 c = local_2lib_check(p, b, &b->last_move, s);
663 if (!is_pass(c))
664 return c;
667 /* Check for patterns we know */
668 if (pp->patternrate > fast_random(100)) {
669 c = apply_pattern(p, b, &b->last_move,
670 pp->pattern2 && b->last_move2.coord >= 0 ? &b->last_move2 : NULL);
671 if (!is_pass(c))
672 return c;
676 /* Global checks */
678 /* Any groups in atari? */
679 if (pp->capturerate > fast_random(100)) {
680 c = global_atari_check(p, b, to_play, s);
681 if (!is_pass(c))
682 return c;
685 /* Fill board */
686 int fbtries = b->flen / 8;
687 for (int i = 0; i < (fbtries < pp->fillboardtries ? fbtries : pp->fillboardtries); i++) {
688 coord_t coord = b->f[fast_random(b->flen)];
689 if (is_pass(coord) || immediate_liberty_count(b, coord) != 4)
690 continue;
691 foreach_diag_neighbor(b, coord) {
692 if (board_at(b, c) != S_NONE)
693 goto next_try;
694 } foreach_diag_neighbor_end;
695 return coord;
696 next_try:;
699 return pass;
703 static int
704 assess_local_bonus(struct playout_policy *p, struct board *board, coord_t a, coord_t b, int games)
706 struct moggy_policy *pp = p->data;
707 if (!pp->assess_local)
708 return games;
710 int dx = abs(coord_x(a, board) - coord_x(b, board));
711 int dy = abs(coord_y(a, board) - coord_y(b, board));
712 /* adjecent move, directly or diagonally? */
713 if (dx + dy <= 1 + (dx && dy))
714 return games;
715 else
716 return games / 2;
719 void
720 playout_moggy_assess_group(struct playout_policy *p, struct prior_map *map, group_t g, int games,
721 struct board_state *s)
723 struct moggy_policy *pp = p->data;
724 struct board *b = map->b;
725 struct move_queue q; q.moves = 0;
727 if (board_group_info(b, g).libs > 2)
728 return;
730 if (PLDEBUGL(5)) {
731 fprintf(stderr, "ASSESS of group %s:\n", coord2sstr(g, b));
732 board_print(b, stderr);
735 if (board_group_info(b, g).libs == 2) {
736 if (!pp->atarirate)
737 return;
738 group_2lib_check(p, b, g, map->to_play, &q, s);
739 while (q.moves--) {
740 coord_t coord = q.move[q.moves];
741 if (PLDEBUGL(5))
742 fprintf(stderr, "1.0: 2lib %s\n", coord2sstr(coord, b));
743 int assess = assess_local_bonus(p, b, b->last_move.coord, coord, games) / 2;
744 add_prior_value(map, coord, 1, assess);
746 return;
749 /* This group, sir, is in atari! */
751 if (!pp->capturerate && !pp->lcapturerate && !pp->ladderassess)
752 return;
754 coord_t ladder = pass;
755 group_atari_check(p, b, g, map->to_play, &q, &ladder, s);
756 while (q.moves--) {
757 coord_t coord = q.move[q.moves];
759 /* _Never_ play here if this move plays out
760 * a caught ladder. */
761 if (coord == ladder && !board_playing_ko_threat(b)) {
762 /* Note that the opposite is not guarded against;
763 * we do not advise against capturing a laddered
764 * group (but we don't encourage it either). Such
765 * a move can simplify tactical situations if we
766 * can afford it. */
767 if (!pp->ladderassess || map->to_play != board_at(b, g))
768 continue;
769 /* FIXME: We give the malus even if this move
770 * captures another group. */
771 if (PLDEBUGL(5))
772 fprintf(stderr, "0.0: ladder %s\n", coord2sstr(coord, b));
773 add_prior_value(map, coord, 0, games);
774 continue;
777 if (!pp->capturerate && !pp->lcapturerate)
778 continue;
780 if (PLDEBUGL(5))
781 fprintf(stderr, "1.0: atari %s\n", coord2sstr(coord, b));
782 int assess = assess_local_bonus(p, b, b->last_move.coord, coord, games) * 2;
783 add_prior_value(map, coord, 1, assess);
787 void
788 playout_moggy_assess_one(struct playout_policy *p, struct prior_map *map, coord_t coord, int games)
790 struct moggy_policy *pp = p->data;
791 struct board *b = map->b;
793 if (PLDEBUGL(5)) {
794 fprintf(stderr, "ASSESS of move %s:\n", coord2sstr(coord, b));
795 board_print(b, stderr);
798 /* Is this move a self-atari? */
799 if (pp->selfatarirate) {
800 if (!board_playing_ko_threat(b) && is_bad_selfatari(b, map->to_play, coord)) {
801 if (PLDEBUGL(5))
802 fprintf(stderr, "0.0: self-atari\n");
803 add_prior_value(map, coord, 0, games);
804 return;
808 /* Pattern check */
809 if (pp->patternrate) {
810 struct move m = { .color = map->to_play, .coord = coord };
811 if (test_pattern3_here(p, b, &m)) {
812 if (PLDEBUGL(5))
813 fprintf(stderr, "1.0: pattern\n");
814 int assess = assess_local_bonus(p, b, b->last_move.coord, coord, games);
815 add_prior_value(map, coord, 1, assess);
819 return;
822 void
823 playout_moggy_assess(struct playout_policy *p, struct prior_map *map, int games)
825 struct moggy_policy *pp = p->data;
827 struct board_state *s = board_state_init(map->b);
829 /* First, go through all endangered groups. */
830 if (pp->lcapturerate || pp->capturerate || pp->atarirate || pp->ladderassess)
831 for (group_t g = 1; g < board_size2(map->b); g++)
832 if (group_at(map->b, g) == g)
833 playout_moggy_assess_group(p, map, g, games, s);
835 /* Then, assess individual moves. */
836 if (!pp->patternrate && !pp->selfatarirate)
837 return;
838 foreach_point(map->b) {
839 if (map->consider[c])
840 playout_moggy_assess_one(p, map, c, games);
841 } foreach_point_end;
844 bool
845 playout_moggy_permit(struct playout_policy *p, struct board *b, struct move *m)
847 struct moggy_policy *pp = p->data;
849 /* The idea is simple for now - never allow self-atari moves.
850 * They suck in general, but this also permits us to actually
851 * handle seki in the playout stage. */
853 if (fast_random(100) >= pp->selfatarirate) {
854 if (PLDEBUGL(5))
855 fprintf(stderr, "skipping sar test\n");
856 return true;
858 bool selfatari = is_bad_selfatari(b, m->color, m->coord);
859 if (PLDEBUGL(5) && selfatari)
860 fprintf(stderr, "__ Prohibiting self-atari %s %s\n",
861 stone2str(m->color), coord2sstr(m->coord, b));
862 return !selfatari;
866 struct playout_policy *
867 playout_moggy_init(char *arg, struct board *b)
869 struct playout_policy *p = calloc(1, sizeof(*p));
870 struct moggy_policy *pp = calloc(1, sizeof(*pp));
871 p->data = pp;
872 p->choose = playout_moggy_choose;
873 p->assess = playout_moggy_assess;
874 p->permit = playout_moggy_permit;
876 int rate = 90;
878 pp->lcapturerate = pp->atarirate = pp->capturerate = pp->patternrate = pp->selfatarirate = -1;
879 pp->ladders = pp->borderladders = true;
880 pp->ladderassess = true;
882 if (arg) {
883 char *optspec, *next = arg;
884 while (*next) {
885 optspec = next;
886 next += strcspn(next, ":");
887 if (*next) { *next++ = 0; } else { *next = 0; }
889 char *optname = optspec;
890 char *optval = strchr(optspec, '=');
891 if (optval) *optval++ = 0;
893 if (!strcasecmp(optname, "lcapturerate") && optval) {
894 pp->lcapturerate = atoi(optval);
895 } else if (!strcasecmp(optname, "atarirate") && optval) {
896 pp->atarirate = atoi(optval);
897 } else if (!strcasecmp(optname, "capturerate") && optval) {
898 pp->capturerate = atoi(optval);
899 } else if (!strcasecmp(optname, "patternrate") && optval) {
900 pp->patternrate = atoi(optval);
901 } else if (!strcasecmp(optname, "selfatarirate") && optval) {
902 pp->selfatarirate = atoi(optval);
903 } else if (!strcasecmp(optname, "rate") && optval) {
904 rate = atoi(optval);
905 } else if (!strcasecmp(optname, "fillboardtries")) {
906 pp->fillboardtries = atoi(optval);
907 } else if (!strcasecmp(optname, "ladders")) {
908 pp->ladders = optval && *optval == '0' ? false : true;
909 } else if (!strcasecmp(optname, "borderladders")) {
910 pp->borderladders = optval && *optval == '0' ? false : true;
911 } else if (!strcasecmp(optname, "ladderassess")) {
912 pp->ladderassess = optval && *optval == '0' ? false : true;
913 } else if (!strcasecmp(optname, "assess_local")) {
914 pp->assess_local = optval && *optval == '0' ? false : true;
915 } else if (!strcasecmp(optname, "pattern2")) {
916 pp->pattern2 = optval && *optval == '0' ? false : true;
917 } else {
918 fprintf(stderr, "playout-moggy: Invalid policy argument %s or missing value\n", optname);
919 exit(1);
923 if (pp->lcapturerate == -1) pp->lcapturerate = rate;
924 if (pp->atarirate == -1) pp->atarirate = rate;
925 if (pp->capturerate == -1) pp->capturerate = rate;
926 if (pp->patternrate == -1) pp->patternrate = rate;
927 if (pp->selfatarirate == -1) pp->selfatarirate = rate;
929 pattern3s_init(&pp->patterns, moggy_patterns_src, moggy_patterns_src_n);
931 return p;