8 #include "patternscan/patternscan.h"
10 #include "patternsp.h"
13 /* Internal engine state. */
17 struct pattern_config pc
;
21 bool no_pattern_match
;
23 /* Minimal number of occurences for spatial to be saved. */
25 /* Number of loaded spatials; checkpoint for saving new sids
26 * in case gen_spat_dict is enabled. */
29 /* Book-keeping of spatial occurence count. */
36 process_pattern(struct patternscan
*ps
, struct board
*b
, struct move
*m
, char **str
)
38 /* First, store the spatial configuration in dictionary
40 if (ps
->gen_spat_dict
&& !is_pass(m
->coord
)) {
42 spatial_from_board(&ps
->pc
, &s
, b
, m
);
44 for (int d
= ps
->pc
.spat_min
; d
<= dmax
; d
++) {
46 int sid
= spatial_dict_put(ps
->pc
.spat_dict
, &s
, spatial_hash(0, &s
));
48 /* Allocate space in 1024 blocks. */
49 #define SCOUNTS_ALLOC 1024
50 if (sid
>= ps
->nscounts
) {
51 int newnsc
= (sid
/ SCOUNTS_ALLOC
+ 1) * SCOUNTS_ALLOC
;
52 ps
->scounts
= realloc(ps
->scounts
, newnsc
* sizeof(*ps
->scounts
));
53 memset(&ps
->scounts
[ps
->nscounts
], 0, (newnsc
- ps
->nscounts
) * sizeof(*ps
->scounts
));
54 ps
->nscounts
= newnsc
;
60 /* Now, match the pattern. */
61 if (!ps
->no_pattern_match
) {
63 pattern_match(&ps
->pc
, ps
->ps
, &p
, b
, m
);
64 *str
= pattern2str(*str
, &p
);
69 patternscan_play(struct engine
*e
, struct board
*b
, struct move
*m
)
71 struct patternscan
*ps
= e
->data
;
73 if (is_resign(m
->coord
))
76 static char str
[1048576]; // XXX
80 /* Scan for supported features. */
81 /* For specifiation of features and their payloads,
82 * please refer to pattern.h. */
83 process_pattern(ps
, b
, m
, &strp
);
85 if (ps
->competition
) {
86 /* Look at other possible moves as well. */
87 for (int f
= 0; f
< b
->flen
; f
++) {
88 struct move mo
= { .coord
= b
->f
[f
], .color
= m
->color
};
89 if (mo
.coord
== m
->coord
)
91 if (!board_is_valid_move(b
, &mo
))
94 process_pattern(ps
, b
, &mo
, &strp
);
98 return ps
->no_pattern_match
? NULL
: str
;
102 patternscan_genmove(struct engine
*e
, struct board
*b
, enum stone color
)
104 fprintf(stderr
, "genmove command not available during patternscan!\n");
109 patternscan_finish(struct engine
*e
)
111 struct patternscan
*ps
= e
->data
;
112 if (!ps
->gen_spat_dict
)
115 /* Save newly found patterns. */
118 FILE *f
= fopen(spatial_dict_filename
, "r");
119 if (f
) { fclose(f
); newfile
= false; }
120 f
= fopen(spatial_dict_filename
, "a");
122 spatial_dict_writeinfo(ps
->pc
.spat_dict
, f
);
124 for (int i
= ps
->loaded_spatials
; i
< ps
->pc
.spat_dict
->nspatials
; i
++) {
125 /* By default, threshold is 0 and condition is always true. */
126 assert(i
< ps
->nscounts
&& ps
->scounts
[i
] > 0);
127 if (ps
->scounts
[i
] >= ps
->spat_threshold
)
128 spatial_write(&ps
->pc
.spat_dict
->spatials
[i
], i
, f
);
135 patternscan_state_init(char *arg
)
137 struct patternscan
*ps
= calloc(1, sizeof(struct patternscan
));
141 ps
->pc
= DEFAULT_PATTERN_CONFIG
;
142 memcpy(&ps
->ps
, PATTERN_SPEC_MATCHALL
, sizeof(pattern_spec
));
145 char *optspec
, *next
= arg
;
148 next
+= strcspn(next
, ",");
149 if (*next
) { *next
++ = 0; } else { *next
= 0; }
151 char *optname
= optspec
;
152 char *optval
= strchr(optspec
, '=');
153 if (optval
) *optval
++ = 0;
155 if (!strcasecmp(optname
, "debug")) {
157 ps
->debug_level
= atoi(optval
);
161 } else if (!strcasecmp(optname
, "gen_spat_dict")) {
162 /* If set, re-generate the spatial patterns
163 * dictionary; you need to have a dictionary
164 * of spatial stone configurations in order
165 * to match any spatial features. */
166 ps
->gen_spat_dict
= !optval
|| atoi(optval
);
168 } else if (!strcasecmp(optname
, "no_pattern_match")) {
169 /* If set, do not actually match patterns.
170 * Useful only together with gen_spat_dict
171 * when just building spatial dictionary. */
172 ps
->no_pattern_match
= !optval
|| atoi(optval
);
174 } else if (!strcasecmp(optname
, "spat_threshold") && optval
) {
175 /* Minimal number of times new spatial
176 * feature must occur in this run (!) to
177 * be included in the dictionary. Note that
178 * this will produce discontinuous dictionary
179 * that you should renumber. */
180 ps
->spat_threshold
= atoi(optval
);
182 } else if (!strcasecmp(optname
, "competition")) {
183 /* In competition mode, first the played
184 * pattern is printed, then all other patterns
185 * that could be played but weren't. */
186 ps
->competition
= !optval
|| atoi(optval
);
188 } else if (!strcasecmp(optname
, "matchfast")) {
189 /* Limit the matched features only to the
190 * set used in MC simulations. */
191 ps
->pc
= FAST_PATTERN_CONFIG
;
192 memcpy(&ps
->ps
, PATTERN_SPEC_MATCHFAST
, sizeof(pattern_spec
));
194 } else if (!strcasecmp(optname
, "xspat") && optval
) {
195 /* xspat==0: don't match spatial features
196 * xspat==1: match *only* spatial features */
197 xspat
= atoi(optval
);
199 /* See pattern.h:pattern_config for description and
200 * pattern.c:DEFAULT_PATTERN_CONFIG for default values
201 * of the following options. */
202 } else if (!strcasecmp(optname
, "spat_min") && optval
) {
203 ps
->pc
.spat_min
= atoi(optval
);
204 } else if (!strcasecmp(optname
, "spat_max") && optval
) {
205 ps
->pc
.spat_max
= atoi(optval
);
206 } else if (!strcasecmp(optname
, "bdist_max") && optval
) {
207 ps
->pc
.bdist_max
= atoi(optval
);
208 } else if (!strcasecmp(optname
, "ldist_min") && optval
) {
209 ps
->pc
.ldist_min
= atoi(optval
);
210 } else if (!strcasecmp(optname
, "ldist_max") && optval
) {
211 ps
->pc
.ldist_max
= atoi(optval
);
212 } else if (!strcasecmp(optname
, "mcsims") && optval
) {
213 ps
->pc
.mcsims
= atoi(optval
);
216 fprintf(stderr
, "patternscan: Invalid engine argument %s or missing value\n", optname
);
221 for (int i
= 0; i
< FEAT_MAX
; i
++) if ((xspat
== 0 && i
== FEAT_SPATIAL
) || (xspat
== 1 && i
!= FEAT_SPATIAL
)) ps
->ps
[i
] = 0;
222 ps
->pc
.spat_dict
= spatial_dict_init(ps
->gen_spat_dict
);
223 ps
->loaded_spatials
= ps
->pc
.spat_dict
->nspatials
;
229 engine_patternscan_init(char *arg
)
231 struct patternscan
*ps
= patternscan_state_init(arg
);
232 struct engine
*e
= calloc(1, sizeof(struct engine
));
233 e
->name
= "PatternScan Engine";
234 e
->comment
= "You cannot play Pachi with this engine, it is intended for special development use - scanning of games fed to it as GTP streams for various pattern features.";
235 e
->genmove
= patternscan_genmove
;
236 e
->notify_play
= patternscan_play
;
237 e
->finish
= patternscan_finish
;