13 static const char *fnames
[] = {
16 [FEAT_CAPTURE
] = "capture",
17 [FEAT_AESCAPE
] = "atariescape",
18 [FEAT_SELFATARI
] = "selfatari",
19 [FEAT_ATARI
] = "atari",
20 [FEAT_BORDER
] = "border",
21 [FEAT_LDIST
] = "ldist",
22 [FEAT_LLDIST
] = "lldist",
23 [FEAT_MCOWNER
] = "mcowner",
27 feature2str(char *str
, struct feature
*f
)
29 return str
+ sprintf(str
+ strlen(str
), "%s:%"PRIx64
, fnames
[f
->id
], f
->payload
);
33 str2feature(char *str
, struct feature
*f
)
35 while (isspace(*str
)) str
++;
37 int flen
= strcspn(str
, ":");
38 for (int i
= 0; i
< sizeof(fnames
)/sizeof(fnames
[0]); i
++)
39 if (strlen(fnames
[i
]) == flen
&& strncmp(fnames
[i
], str
, flen
)) {
43 fprintf(stderr
, "invalid featurespec: %s\n", str
);
48 f
->payload
= strtoull(str
, &str
, 10);
54 pattern_get(struct pattern
*p
, struct board
*b
, struct move
*m
)
57 struct feature
*f
= &p
->f
[0];
59 /* TODO: We should match pretty much all of these features
66 if (is_pass(m
->coord
)) {
67 f
->id
= FEAT_PASS
; f
->payload
= 0;
68 f
->payload
|= (b
->moves
> 0 && is_pass(b
->last_move
.coord
)) << PF_PASS_LASTPASS
;
74 foreach_neighbor(b
, m
->coord
, {
75 if (board_at(b
, c
) != stone_other(m
->color
))
77 group_t g
= group_at(b
, c
);
78 if (!g
|| board_group_info(b
, g
).libs
!= 1)
82 f
->id
= FEAT_CAPTURE
; f
->payload
= 0;
84 f
->payload
|= is_ladder(b
, m
->coord
, g
, true, true) << PF_CAPTURE_LADDER
;
85 /* TODO: is_ladder() is too conservative in some
86 * very obvious situations, look at complete.gtp. */
88 /* TODO: PF_CAPTURE_RECAPTURE */
90 foreach_in_group(b
, g
) {
91 foreach_neighbor(b
, c
, {
92 assert(board_at(b
, c
) != S_NONE
|| c
== m
->coord
);
93 if (board_at(b
, c
) != m
->color
)
95 group_t g
= group_at(b
, c
);
96 if (!g
|| board_group_info(b
, g
).libs
!= 1)
98 /* A neighboring group of ours is in atari. */
99 f
->payload
|= 1 << PF_CAPTURE_ATARIDEF
;
101 } foreach_in_group_end
;
103 if (group_is_onestone(b
, g
)
104 && neighbor_count_at(b
, m
->coord
, stone_other(m
->color
))
105 + neighbor_count_at(b
, m
->coord
, S_OFFBOARD
) == 4)
106 f
->payload
|= 1 << PF_CAPTURE_KO
;
115 foreach_neighbor(b
, m
->coord
, {
116 if (board_at(b
, c
) != m
->color
)
118 group_t g
= group_at(b
, c
);
119 if (!g
|| board_group_info(b
, g
).libs
!= 1)
123 f
->id
= FEAT_AESCAPE
; f
->payload
= 0;
125 f
->payload
|= is_ladder(b
, m
->coord
, g
, true, true) << PF_AESCAPE_LADDER
;
126 /* TODO: is_ladder() is too conservative in some
127 * very obvious situations, look at complete.gtp. */
135 if (is_bad_selfatari(b
, m
->color
, m
->coord
)) {
136 f
->id
= FEAT_SELFATARI
;
137 /* TODO: Dumb selfatari detection. */
138 f
->payload
= 1 << PF_SELFATARI_SMART
;
143 foreach_neighbor(b
, m
->coord
, {
144 if (board_at(b
, c
) != stone_other(m
->color
))
146 group_t g
= group_at(b
, c
);
147 if (!g
|| board_group_info(b
, g
).libs
!= 2)
151 f
->id
= FEAT_ATARI
; f
->payload
= 0;
153 f
->payload
|= is_ladder(b
, m
->coord
, g
, true, true) << PF_ATARI_LADDER
;
154 /* TODO: is_ladder() is too conservative in some
155 * very obvious situations, look at complete.gtp. */
157 if (!is_pass(b
->ko
.coord
))
158 f
->payload
|= 1 << PF_CAPTURE_KO
;
165 int bdist
= coord_edge_distance(m
->coord
, b
);
166 if (bdist
<= 4) { /* TODO: Configurable threshold. */
180 pattern2str(char *str
, struct pattern
*p
)
183 for (int i
= 0; i
< p
->n
; i
++) {
184 if (i
> 0) strcat(str
++, " ");
185 str
= feature2str(str
, &p
->f
[i
]);