board_undo: define QUICK_BOARD_CODE to get compiler error with forbidden fields.
[pachi.git] / tactics / 1lib.c
bloba0e5da3ae82413a5f34de35bb0577d2e71325882
1 #include <assert.h>
2 #include <stdio.h>
3 #include <stdlib.h>
5 #define QUICK_BOARD_CODE
7 #define DEBUG
8 #include "board.h"
9 #include "debug.h"
10 #include "mq.h"
11 #include "tactics/1lib.h"
12 #include "tactics/ladder.h"
13 #include "tactics/selfatari.h"
16 /* Whether to avoid capturing/atariing doomed groups (this is big
17 * performance hit and may reduce playouts balance; it does increase
18 * the strength, but not quite proportionally to the performance). */
19 //#define NO_DOOMED_GROUPS
22 static bool
23 can_play_on_lib(struct board *b, group_t g, enum stone to_play)
25 coord_t capture = board_group_info(b, g).lib[0];
26 if (DEBUGL(6))
27 fprintf(stderr, "can capture group %d (%s)?\n",
28 g, coord2sstr(capture, b));
29 /* Does playing on the liberty usefully capture the group? */
30 if (board_is_valid_play(b, to_play, capture)
31 && !is_bad_selfatari(b, to_play, capture))
32 return true;
34 return false;
37 /* For given position @c, decide if this is a group that is in danger from
38 * @capturer and @to_play can do anything about it (play at the last
39 * liberty to either capture or escape). */
40 /* Note that @to_play is important; e.g. consider snapback, it's good
41 * to play at the last liberty by attacker, but not defender. */
42 static inline __attribute__((always_inline)) bool
43 capturable_group(struct board *b, enum stone capturer, coord_t c,
44 enum stone to_play)
46 group_t g = group_at(b, c);
47 if (likely(board_at(b, c) != stone_other(capturer)
48 || board_group_info(b, g).libs > 1))
49 return false;
51 return can_play_on_lib(b, g, to_play);
54 bool
55 can_countercapture(struct board *b, enum stone owner, group_t g,
56 enum stone to_play, struct move_queue *q, int tag)
58 if (b->clen < 2)
59 return false;
61 unsigned int qmoves_prev = q ? q->moves : 0;
63 foreach_in_group(b, g) {
64 foreach_neighbor(b, c, {
65 if (!capturable_group(b, owner, c, to_play))
66 continue;
68 if (!q) {
69 return true;
71 mq_add(q, board_group_info(b, group_at(b, c)).lib[0], tag);
72 mq_nodup(q);
73 });
74 } foreach_in_group_end;
76 bool can = q ? q->moves > qmoves_prev : false;
77 return can;
80 #ifdef NO_DOOMED_GROUPS
81 static bool
82 can_be_rescued(struct board *b, group_t group, enum stone color, int tag)
84 /* Does playing on the liberty rescue the group? */
85 if (can_play_on_lib(b, group, color))
86 return true;
88 /* Then, maybe we can capture one of our neighbors? */
89 return can_countercapture(b, color, group, color, NULL, tag);
91 #endif
93 void
94 group_atari_check(unsigned int alwaysccaprate, struct board *b, group_t group, enum stone to_play,
95 struct move_queue *q, coord_t *ladder, bool middle_ladder, int tag)
97 enum stone color = board_at(b, group_base(group));
98 coord_t lib = board_group_info(b, group).lib[0];
100 assert(color != S_OFFBOARD && color != S_NONE);
101 if (DEBUGL(5))
102 fprintf(stderr, "[%s] atariiiiiiiii %s of color %d\n",
103 coord2sstr(group, b), coord2sstr(lib, b), color);
104 assert(board_at(b, lib) == S_NONE);
106 if (to_play != color) {
107 /* We are the attacker! In that case, do not try defending
108 * our group, since we can capture the culprit. */
109 #ifdef NO_DOOMED_GROUPS
110 /* Do not remove group that cannot be saved by the opponent. */
111 if (!can_be_rescued(b, group, color, tag))
112 return;
113 #endif
114 if (can_play_on_lib(b, group, to_play)) {
115 mq_add(q, lib, tag);
116 mq_nodup(q);
118 return;
121 /* Can we capture some neighbor? */
122 bool ccap = can_countercapture(b, color, group, to_play, q, tag);
123 if (ccap && !ladder && alwaysccaprate > fast_random(100))
124 return;
126 /* Otherwise, do not save kos. */
127 if (group_is_onestone(b, group)
128 && neighbor_count_at(b, lib, color) + neighbor_count_at(b, lib, S_OFFBOARD) == 4) {
129 /* Except when the ko is for an eye! */
130 bool eyeconnect = false;
131 foreach_diag_neighbor(b, lib) {
132 if (board_at(b, c) == S_NONE && neighbor_count_at(b, c, color) + neighbor_count_at(b, c, S_OFFBOARD) == 4) {
133 eyeconnect = true;
134 break;
136 } foreach_diag_neighbor_end;
137 if (!eyeconnect)
138 return;
141 /* Do not suicide... */
142 if (!can_play_on_lib(b, group, to_play))
143 return;
144 if (DEBUGL(6))
145 fprintf(stderr, "...escape route valid\n");
147 /* ...or play out ladders (unless we can counter-capture anytime). */
148 if (!ccap) {
149 if (is_ladder(b, lib, group, middle_ladder)) {
150 /* Sometimes we want to keep the ladder move in the
151 * queue in order to discourage it. */
152 if (!ladder)
153 return;
154 else
155 *ladder = lib;
156 } else if (DEBUGL(6))
157 fprintf(stderr, "...no ladder\n");
160 mq_add(q, lib, tag);
161 mq_nodup(q);