Commit | Line | Data |
---|---|---|
7eeb782e AT |
1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ |
2 | * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * | |
3 | * http://www.gnu.org/software/gnugo/ for more information. * | |
4 | * * | |
5 | * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * | |
6 | * 2008 and 2009 by the Free Software Foundation. * | |
7 | * * | |
8 | * This program is free software; you can redistribute it and/or * | |
9 | * modify it under the terms of the GNU General Public License as * | |
10 | * published by the Free Software Foundation - version 3 or * | |
11 | * (at your option) any later version. * | |
12 | * * | |
13 | * This program is distributed in the hope that it will be useful, * | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
16 | * GNU General Public License in file COPYING for more details. * | |
17 | * * | |
18 | * You should have received a copy of the GNU General Public * | |
19 | * License along with this program; if not, write to the Free * | |
20 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * | |
21 | * Boston, MA 02111, USA. * | |
22 | \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ | |
23 | ||
24 | /* Compile one of the pattern databases. This takes a database file, | |
25 | * e.g. patterns.db, and produces a C code file, in this case | |
26 | * patterns.c. | |
27 | */ | |
28 | ||
29 | /* See also patterns.h, and the *.db files. | |
30 | */ | |
31 | ||
32 | /* Differences when compiling connections patterns (-c) : | |
33 | * '*' means cutting point | |
34 | * '!' is allowed (inhibit connection there), matches as '.'. | |
35 | * '!' will always be written as the first elements | |
36 | */ | |
37 | ||
38 | /* FIXME: This file is a horrible mess, especially after pattern | |
39 | * matching went 1D. Cleaning it will make future work | |
40 | * with pattern mathing easier. | |
41 | */ | |
42 | ||
43 | /* As in the rest of GNU Go, co-ordinate convention (i,j) is 'i' down from | |
44 | * the top, then 'j' across from the left | |
45 | */ | |
46 | ||
47 | #define USAGE "\ | |
48 | Usage : mkpat [options] <prefix>\n\ | |
49 | General options:\n\ | |
50 | -i = one or more input files (typically *.db)\n\ | |
51 | -o = output file (typically *.c)\n\ | |
52 | -t = DFA transformations file (typically *.dtr)\n\ | |
53 | -v = verbose\n\ | |
54 | -V <level> = DFA verbiage level\n\ | |
55 | Database type:\n\ | |
56 | -p = compile general pattern database (the default)\n\ | |
57 | -c = compile connections database\n\ | |
58 | -C = compile a corner pattern database\n\ | |
59 | -D = compile a DFA database (allows fast matching)\n\ | |
60 | -d <iterations> = don't generate database, but optimize a DFA\n\ | |
61 | transformation file instead\n\ | |
62 | Pattern generation options:\n\ | |
63 | -O = allow only O to be anchor (the default)\n\ | |
64 | -X = allow only X to be anchor\n\ | |
65 | -b = allow both colors to be anchor\n\ | |
66 | -m = try to place the anchor in the center of the pattern\n\ | |
67 | (works best with DFA databases)\n\ | |
68 | -a = require anchor in all patterns. Sets fixed_anchor flag in db\n\ | |
69 | If no input files specified, reads from stdin.\n\ | |
70 | If output file is not specified, writes to stdout.\n\ | |
71 | " | |
72 | ||
73 | ||
74 | #ifdef HAVE_CONFIG_H | |
75 | #include <config.h> | |
76 | #endif | |
77 | ||
78 | #include <stdio.h> | |
79 | #include <stdlib.h> | |
80 | #include <string.h> | |
81 | #include <ctype.h> | |
82 | #include <assert.h> | |
83 | ||
84 | #include "liberty.h" | |
85 | #include "patterns.h" | |
86 | #include "gg-getopt.h" | |
87 | #include "gg_utils.h" | |
88 | ||
89 | #include "dfa-mkpat.h" | |
90 | ||
91 | ||
92 | #define DB_GENERAL ((int) 'p') | |
93 | #define DB_CONNECTIONS ((int) 'c') | |
94 | #define DB_CORNER ((int) 'C') | |
95 | #define DB_DFA ((int) 'D') | |
96 | #define OPTIMIZE_DFA ((int) 'd') | |
97 | ||
98 | /* code assumes that ATT_O and ATT_X are 1 and 2 (in either order) | |
99 | * An attribute is a candidate for anchor if (att & anchor) != 0 | |
100 | */ | |
101 | #define ANCHOR_O ATT_O | |
102 | #define ANCHOR_X ATT_X | |
103 | #define ANCHOR_BOTH (ATT_O | ATT_X) | |
104 | ||
105 | #define MAXLINE 500 | |
106 | #define MAXCONSTRAINT 10000 | |
107 | #define MAXACTION 10000 | |
108 | #define MAXPATNO 5000 | |
109 | #define MAXLABELS 20 | |
110 | #define MAXPARAMS 20 | |
111 | #define MAX_INPUT_FILE_NAMES 10 | |
112 | #define MAXNAME 80 | |
113 | ||
114 | /* Avoid compiler warnings with unused parameters */ | |
115 | #define UNUSED(x) (void)x | |
116 | ||
117 | /* valid characters that can appear in a pattern | |
118 | * position in string is att value to store | |
119 | */ | |
120 | static const char VALID_PATTERN_CHARS[] = ".XOxo,a!*?QY"; | |
121 | static const char VALID_EDGE_CHARS[] = "+-|"; | |
122 | static const char VALID_CONSTRAINT_LABELS[] = "abcdefghijklmnpqrstuvwyzABCDEFGHIJKLMNPRSTUVWZ"; | |
123 | ||
124 | ||
125 | /* the offsets into the list are the ATT_* defined in patterns.h | |
126 | * The following defns are for internal use only, and are not | |
127 | * written out to the compiled pattern database | |
128 | */ | |
129 | ||
130 | #define ATT_star 8 | |
131 | #define ATT_wild 9 | |
132 | #define ATT_Q 10 | |
133 | #define ATT_Y 11 | |
134 | ||
135 | /* These arrays control discarding of unnecessary patval elements. | |
136 | * Modify them using `goal_elements ...' and `callback_data ..' | |
137 | * commands in a database. By default, we don't drop any elements. | |
138 | */ | |
139 | static int nongoal[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | |
140 | static int callback_unneeded[8] = {0, 0, 0, 0, 0, 0, 0, 0}; | |
141 | ||
142 | /* stuff used in reading/parsing pattern rows */ | |
143 | static int maxi, maxj; /* (i,j) offsets of largest element */ | |
144 | static int mini, minj; /* offset of top-left element | |
145 | (0,0) unless there are edge constraints */ | |
146 | static int movei, movej; | |
147 | static unsigned int where; /* NORTH_EDGE | WEST_EDGE, etc */ | |
148 | static int el; /* next element number in current pattern */ | |
149 | static struct patval_b elements[MAX_BOARD*MAX_BOARD]; /* elements of current pattern */ | |
150 | static int num_stars; | |
151 | ||
152 | static int ci = -1, cj = -1; /* position of origin (first piece element) | |
153 | relative to top-left */ | |
154 | static int patno; /* current pattern */ | |
155 | static int discard_pattern = 0; /* Set to nonzero to discard a pattern (if e.g. | |
156 | * it is too large or duplicated). */ | |
157 | static int pats_with_constraints = 0; /* just out of interest */ | |
158 | static int label_coords[256][2]; /* Coordinates for labeled stones in the | |
159 | autohelper patterns. */ | |
160 | static int current_c_i; /* Counter for the line number of a | |
161 | constraint diagram. */ | |
162 | static char constraint[MAXCONSTRAINT]; /* Store constraint lines. */ | |
163 | static char action[MAXCONSTRAINT]; /* Store action lines. */ | |
164 | static char diagram[MAX_BOARD+2][MAX_BOARD+3]; | |
165 | /* store pattern diagram*/ | |
166 | static char constraint_diagram[MAX_BOARD+2][MAX_BOARD+3]; | |
167 | /* store pattern constraint diagram */ | |
168 | ||
169 | /* stuff to maintain info about patterns while reading */ | |
170 | static char *prefix; | |
171 | static struct pattern pattern[MAXPATNO]; /* accumulate the patterns into here */ | |
172 | static char pattern_names[MAXPATNO][MAXNAME]; /* with optional names here, */ | |
173 | static int num_attributes; | |
174 | static struct pattern_attribute attributes[MAXPATNO * NUM_ATTRIBUTES]; | |
175 | static char helper_fn_names[MAXPATNO][MAXNAME]; /* helper fn names here */ | |
176 | static char autohelper_code[MAXPATNO*300]; /* code for automatically generated */ | |
177 | /* helper functions here */ | |
178 | static char *code_pos; /* current position in code buffer */ | |
179 | struct autohelper_func { | |
180 | const char *name; | |
181 | int params; | |
182 | int type; /* 0 - just copy the parameters, | |
183 | * 1 - add parameter count, | |
184 | * 2 - add address of the current pattern. | |
185 | */ | |
186 | float cost; | |
187 | const char *code; | |
188 | }; | |
189 | ||
190 | ||
191 | /* | |
192 | * current_* are useful for debugging broken patterns. | |
193 | */ | |
194 | static const char *current_file = NULL; | |
195 | static int current_line_number = 0; | |
196 | ||
197 | ||
198 | struct attribute_description { | |
199 | const char *input_name; /* The name used in `.db' files. */ | |
200 | enum attribute_type type; | |
201 | }; | |
202 | ||
203 | ||
204 | static const char *attribute_name[NUM_ATTRIBUTES + 1] = { | |
205 | "MIN_VALUE", | |
206 | "MAX_VALUE", | |
207 | "MIN_TERRITORY", | |
208 | "MAX_TERRITORY", | |
209 | "SHAPE", | |
210 | "FOLLOWUP", | |
211 | "REVERSE_FOLLOWUP", | |
212 | "THREATENS_TO_CAPTURE", | |
213 | "THREATENS_EYE", | |
214 | "REVERSE_SENTE", | |
215 | "LAST_ATTRIBUTE" | |
216 | }; | |
217 | ||
218 | ||
219 | /* Owl-style value stored in pattern itself. */ | |
220 | #define IN_PATTERN_VALUE NUM_ATTRIBUTES | |
221 | ||
222 | static struct attribute_description general_attribute_map[] = { | |
223 | { "value", MIN_VALUE }, | |
224 | { "minvalue", MIN_VALUE }, | |
225 | { "maxvalue", MAX_VALUE }, | |
226 | { "terri", MIN_TERRITORY }, | |
227 | { "minterri", MIN_TERRITORY }, | |
228 | { "maxterri", MAX_TERRITORY }, | |
229 | { "shape", SHAPE }, | |
230 | { "followup", FOLLOWUP }, | |
231 | { "followup_value", FOLLOWUP }, | |
232 | { "reverse_followup", REVERSE_FOLLOWUP }, | |
233 | { NULL, LAST_ATTRIBUTE } | |
234 | }; | |
235 | ||
236 | static struct attribute_description value_only_attribute_map[] = { | |
237 | { "value", IN_PATTERN_VALUE }, | |
238 | { NULL, LAST_ATTRIBUTE } | |
239 | }; | |
240 | ||
241 | static struct attribute_description owl_attack_attribute_map[] = { | |
242 | { "value", IN_PATTERN_VALUE }, | |
243 | { "threatens_to_capture", THREATENS_TO_CAPTURE }, | |
244 | { "threatens_eye", THREATENS_EYE }, | |
245 | { "reverse_sente", REVERSE_SENTE }, | |
246 | { NULL, LAST_ATTRIBUTE } | |
247 | }; | |
248 | ||
249 | static struct attribute_description owl_defense_attribute_map[] = { | |
250 | { "value", IN_PATTERN_VALUE }, | |
251 | { "threatens_to_capture", THREATENS_TO_CAPTURE }, | |
252 | { "threatens_eye", THREATENS_EYE }, | |
253 | { "reverse_sente", REVERSE_SENTE }, | |
254 | { NULL, LAST_ATTRIBUTE } | |
255 | }; | |
256 | ||
257 | static struct attribute_description *attribute_map = NULL; | |
258 | static int attributes_needed = 0; | |
259 | ||
260 | ||
261 | /* ================================================================ */ | |
262 | /* */ | |
263 | /* Autohelper function definitions */ | |
264 | /* */ | |
265 | /* ================================================================ */ | |
266 | ||
267 | /* Important notice: | |
268 | * If one function has a name which is a prefix of another name, the | |
269 | * shorter name must come later in the list. E.g. "lib" must be preceded | |
270 | * by "lib2", "lib3", and "lib4". | |
271 | */ | |
272 | static struct autohelper_func autohelper_functions[] = { | |
273 | {"lib2", 1, 0, 0.01, "worm[%s].liberties2"}, | |
274 | {"lib3", 1, 0, 0.01, "worm[%s].liberties3"}, | |
275 | {"lib4", 1, 0, 0.01, "worm[%s].liberties4"}, | |
276 | {"lib", 1, 0, 0.01, "countlib(%s)"}, | |
277 | {"alive", 1, 0, 0.01, | |
278 | "(dragon[%s].status == ALIVE)"}, | |
279 | {"unknown", 1, 0, 0.01, | |
280 | "(dragon[%s].status == UNKNOWN)"}, | |
281 | {"critical", 1, 0, 0.01, | |
282 | "(dragon[%s].status == CRITICAL)"}, | |
283 | {"dead", 1, 0, 0.01, "(dragon[%s].status == DEAD)"}, | |
284 | {"status", 1, 0, 0.01, "dragon[%s].status"}, | |
285 | {"ko", 1, 0, 0.01, "is_ko_point(%s)"}, | |
286 | {"xdefend_against", 2, 0, 1.00, | |
287 | "defend_against(%s, OTHER_COLOR(color), %s)"}, | |
288 | {"odefend_against", 2, 0, 1.00, "defend_against(%s, color, %s)"}, | |
289 | {"defend_against_atari", 1, 0, 1.00, | |
290 | "defend_against_atari_helper(move, %s)"}, | |
291 | {"does_defend", 2, 0, 1.00, "does_defend(%s, %s)"}, | |
292 | {"does_attack", 2, 0, 1.00, "does_attack(%s, %s)"}, | |
293 | {"attack", 1, 0, 1.00, "ATTACK_MACRO(%s)"}, | |
294 | {"defend", 1, 0, 1.00, "DEFEND_MACRO(%s)"}, | |
295 | {"weakness", 1, 0, 0.01, "dragon_weakness(%s, 0)"}, | |
296 | {"weak", 1, 0, 0.01, "dragon_weak(%s)"}, | |
297 | {"safe_xmove", 1, 0, 1.00, "safe_move(%s, OTHER_COLOR(color))"}, | |
298 | {"safe_omove", 1, 0, 1.00, "safe_move(%s, color)"}, | |
299 | {"legal_xmove", 1, 0, 0.05, "is_legal(%s, OTHER_COLOR(color))"}, | |
300 | {"legal_omove", 1, 0, 0.05, "is_legal(%s, color)"}, | |
301 | {"x_suicide", 1, 0, 0.05, "is_suicide(%s, OTHER_COLOR(color))"}, | |
302 | {"o_suicide", 1, 0, 0.05, "is_suicide(%s, color)"}, | |
303 | {"x_alive_somewhere", 0, 1, 0.01, | |
304 | "somewhere(OTHER_COLOR(color), 1, %d"}, | |
305 | {"o_alive_somewhere", 0, 1, 0.01, "somewhere(color, 1, %d"}, | |
306 | {"x_somewhere", 0, 1, 0.01, | |
307 | "somewhere(OTHER_COLOR(color), 0, %d"}, | |
308 | {"o_somewhere", 0, 1, 0.01, "somewhere(color, 0, %d"}, | |
309 | {"xmoyo_opposite", 1, 0, 0.01, | |
310 | "(whose_moyo(INITIAL_INFLUENCE(color), %s) == OTHER_COLOR(color))"}, | |
311 | {"omoyo_opposite", 1, 0, 0.01, | |
312 | "(whose_moyo(INITIAL_INFLUENCE(color), %s) == color)"}, | |
313 | {"xmoyo", 1, 0, 0.01, | |
314 | "(whose_moyo(OPPOSITE_INFLUENCE(color), %s) == OTHER_COLOR(color))"}, | |
315 | {"omoyo", 1, 0, 0.01, | |
316 | "(whose_moyo(OPPOSITE_INFLUENCE(color), %s) == color)"}, | |
317 | {"xarea", 1, 0, 0.01, | |
318 | "(whose_area(OPPOSITE_INFLUENCE(color), %s) == OTHER_COLOR(color))"}, | |
319 | {"oarea", 1, 0, 0.01, | |
320 | "(whose_area(OPPOSITE_INFLUENCE(color), %s) == color)"}, | |
321 | {"xterri", 1, 0, 0.01, | |
322 | "(whose_territory(OPPOSITE_INFLUENCE(color), %s) == OTHER_COLOR(color))"}, | |
323 | {"oterri", 1, 0, 0.01, | |
324 | "(whose_territory(OPPOSITE_INFLUENCE(color), %s) == color)"}, | |
325 | {"genus", 1, 0, 0.01, "dragon[%s].genus"}, | |
326 | {"approx_xlib", 1, 0, 0.03, | |
327 | "approxlib(%s, OTHER_COLOR(color), MAX_LIBERTIES, NULL)"}, | |
328 | {"approx_olib", 1, 0, 0.03, | |
329 | "approxlib(%s, color, MAX_LIBERTIES, NULL)"}, | |
330 | {"xlib", 1, 0, 0.05, | |
331 | "accuratelib(%s, OTHER_COLOR(color), MAX_LIBERTIES, NULL)"}, | |
332 | {"olib", 1, 0, 0.05, | |
333 | "accuratelib(%s, color, MAX_LIBERTIES, NULL)"}, | |
334 | {"xcut", 1, 0, 0.01, | |
335 | "cut_possible(%s, OTHER_COLOR(color))"}, | |
336 | {"ocut", 1, 0, 0.05, "cut_possible(%s, color)"}, | |
337 | {"edge_double_sente", 4, 1, 3.00, | |
338 | "edge_double_sente_helper(%s, %s, %s, %s)"}, | |
339 | {"xplay_defend_both", 2, 1, 3.00, | |
340 | "play_attack_defend2_n(OTHER_COLOR(color), 0, %d"}, | |
341 | {"oplay_defend_both", 2, 1, 3.00, "play_attack_defend2_n(color, 0, %d"}, | |
342 | {"xplay_attack_either", 2, 1, 3.00, | |
343 | "play_attack_defend2_n(OTHER_COLOR(color), 1, %d"}, | |
344 | {"oplay_attack_either", 2, 1, 3.00, "play_attack_defend2_n(color, 1, %d"}, | |
345 | {"xplay_defend", 1, 1, 1.00, | |
346 | "play_attack_defend_n(OTHER_COLOR(color), 0, %d"}, | |
347 | {"oplay_defend", 1, 1, 1.00, "play_attack_defend_n(color, 0, %d"}, | |
348 | {"xplay_attack", 1, 1, 1.00, | |
349 | "play_attack_defend_n(OTHER_COLOR(color), 1, %d"}, | |
350 | {"oplay_attack", 1, 1, 1.00, "play_attack_defend_n(color, 1, %d"}, | |
351 | {"xplay_break_through", 3, 1, 5.00, | |
352 | "play_break_through_n(OTHER_COLOR(color), %d"}, | |
353 | {"oplay_break_through", 3, 1, 5.00, "play_break_through_n(color, %d"}, | |
354 | {"oplay_connect", 2, 1, 10.00, "play_connect_n(color, 1, %d"}, | |
355 | {"xplay_connect", 2, 1, 10.00, | |
356 | "play_connect_n(OTHER_COLOR(color), 1, %d"}, | |
357 | {"oplay_disconnect", 2, 1, 10.00, "play_connect_n(color, 0, %d"}, | |
358 | {"xplay_disconnect", 2, 1, 10.00, | |
359 | "play_connect_n(OTHER_COLOR(color), 0, %d"}, | |
360 | {"oplay_lib", 1, 1, 0.06, "play_lib_n(color, %d"}, | |
361 | {"xplay_lib", 1, 1, 0.06, | |
362 | "play_lib_n(OTHER_COLOR(color), %d"}, | |
363 | {"seki_helper", 1, 0, 0.0, "seki_helper(%s)"}, | |
364 | {"threaten_to_save", 1, 0, 0.0, "threaten_to_save_helper(move,%s)"}, | |
365 | {"threaten_to_capture", 1, 0, 0.0, | |
366 | "threaten_to_capture_helper(move,%s)"}, | |
367 | {"prevent_attack_threat", 1, 0, 0.0, | |
368 | "prevent_attack_threat_helper(move, %s)"}, | |
369 | {"eye", 1, 0, 0.01, "is_eye_space(%s)"}, | |
370 | {"proper_eye", 1, 0, 0.01, "is_proper_eye_space(%s)"}, | |
371 | {"marginal_eye", 1, 0, 0.01, "is_marginal_eye_space(%s)"}, | |
372 | {"halfeye", 1, 0, 0.01, "is_halfeye(half_eye,%s)"}, | |
373 | {"max_eye_value", 1, 0, 0.01, "max_eye_value(%s)"}, | |
374 | {"owl_topological_eye", 2, 0, 0.01, "owl_topological_eye(%s, board[%s])"}, | |
375 | {"obvious_false_oeye", 1, 0, 0.01, "obvious_false_eye(%s, color)"}, | |
376 | {"obvious_false_xeye", 1, 0, 0.01, | |
377 | "obvious_false_eye(%s, OTHER_COLOR(color))"}, | |
378 | {"antisuji", 1, 0, 0.0, "add_antisuji_move(%s)"}, | |
379 | {"add_connect_move", 2, 0, 0.0, "add_connection_move(move,%s, %s)"}, | |
380 | {"add_cut_move", 2, 0, 0.0, "add_cut_move(move, %s, %s)"}, | |
381 | {"test_attack_either_move", 2, 0, 0.0, | |
382 | "test_attack_either_move(move, color, %s, %s)"}, | |
383 | {"add_defend_both_move", 2, 0, 0.0, | |
384 | "add_all_move(move, DEFEND_STRING, %s, DEFEND_STRING, %s)"}, | |
385 | {"same_dragon", 2, 0, 0.01, "is_same_dragon(%s, %s)"}, | |
386 | {"same_string", 2, 0, 0.01, "same_string(%s, %s)"}, | |
387 | {"dragonsize", 1, 0, 0.01, "dragon[%s].size"}, | |
388 | {"wormsize", 1, 0, 0.01, "countstones(%s)"}, | |
389 | {"effective_size", 1, 0, 0.01, "dragon[%s].effective_size"}, | |
390 | {"vital_chain", 1, 0, 0.05, "vital_chain(%s)"}, | |
391 | {"potential_cutstone", 1, 0, 0.01, "worm[%s].cutstone2 > 1"}, | |
392 | {"amalgamate_most_valuable_helper", 3, 0, 0.0, | |
393 | "amalgamate_most_valuable_helper(%s, %s, %s)"}, | |
394 | {"amalgamate", 2, 0, 0.0, "join_dragons(%s, %s)"}, | |
395 | {"owl_escape_value", 1, 0, 0.01, "owl_escape_value(%s)"}, | |
396 | {"owl_goal_dragon", 1, 0, 0.01, "owl_goal_dragon(%s)"}, | |
397 | {"owl_eyespace", 1, 0, 0.01, "owl_eyespace(%s)"}, | |
398 | {"owl_big_eyespace", 1, 0, 0.01, "owl_big_eyespace(%s)"}, | |
399 | {"owl_mineye", 1, 0, 0.01, "owl_mineye(%s)"}, | |
400 | {"owl_maxeye", 1, 0, 0.01, "owl_maxeye(%s)"}, | |
401 | {"owl_proper_eye", 1, 0, 0.01, "owl_proper_eye(%s)"}, | |
402 | {"owl_eye_size", 1, 0, 0.01, "owl_eye_size(%s)"}, | |
403 | {"owl_lunch", 1, 0, 0.01, "owl_lunch(%s)"}, | |
404 | {"owl_strong_dragon", 1, 0, 0.01, "owl_strong_dragon(%s)"}, | |
405 | {"has_aji", 1, 0, 0.01, | |
406 | "(dragon[%s].owl_threat_status == CAN_THREATEN_DEFENSE)"}, | |
407 | {"finish_ko_helper", 1, 0, 0.05, "finish_ko_helper(%s)"}, | |
408 | {"squeeze_ko_helper", 1, 0, 0.03, "squeeze_ko_helper(%s)"}, | |
409 | {"backfill_helper", 3, 0, 1.50, "backfill_helper(%s, %s, %s)"}, | |
410 | {"connect_and_cut_helper2", 3, 0, 3.00, | |
411 | "connect_and_cut_helper2(%s, %s, %s, color)"}, | |
412 | {"connect_and_cut_helper", 3, 0, 3.00, "connect_and_cut_helper(%s, %s, %s)"}, | |
413 | {"owl_threatens", 2, 0, 0.01, "owl_threatens_attack(%s, %s)"}, | |
414 | {"replace", 2, 0, 0.0, "add_replacement_move(%s, %s, color)"}, | |
415 | {"backfill_replace", 2, 0, 0.0, "backfill_replace(%s, %s)"}, | |
416 | {"non_oterritory", 1, 0, 0.0, | |
417 | "influence_mark_non_territory(%s, color)"}, | |
418 | {"non_xterritory", 1, 0, 0.0, | |
419 | "influence_mark_non_territory(%s, OTHER_COLOR(color))"}, | |
420 | {"remaining_handicap_stones", 0, 0, 0.0, "free_handicap_remaining_stones()"}, | |
421 | {"total_handicap_stones", 0, 0, 0.0, "free_handicap_total_stones()"}, | |
422 | {"o_captures_something", 1, 0, 0.02, "does_capture_something(%s, color)"}, | |
423 | {"x_captures_something", 1, 0, 0.02, | |
424 | "does_capture_something(%s, OTHER_COLOR(color))"}, | |
425 | {"false_eye_territory", 1, 0, 0.0, "false_eye_territory[%s]"}, | |
426 | {"false_eye", 1, 0, 0.01, "is_false_eye(half_eye, %s)"}, | |
427 | {"o_visible_along_edge", 2, 0, 0.05, "visible_along_edge(color,%s,%s)"}, | |
428 | {"x_visible_along_edge", 2, 0, 0.05, | |
429 | "visible_along_edge(OTHER_COLOR(color),%s,%s)"}, | |
430 | {"is_surrounded", 1, 0, 0.01, "is_surrounded(%s)"}, | |
431 | {"does_surround", 2, 0, 1.00, "does_surround(%s, %s)"}, | |
432 | {"surround_map", 2, 0, 0.01, "surround_map(%s, %s)"}, | |
433 | {"oracle_threatens", 2, 0, 0.01, "oracle_threatens(%s, %s)"}, | |
434 | {"value", 0, 2, 0.0, "(%s->value)"}, | |
435 | {"adjacent_to_stone_in_atari", 1, 0, 1.0, | |
436 | "adjacent_to_stone_in_atari(%s)"}, | |
437 | {"adjacent_to_defendable_stone_in_atari", 1, 0, 1.0, | |
438 | "adjacent_to_defendable_stone_in_atari(%s)"}, | |
439 | {"good_attack_threat", 2, 0, 0.01, "register_good_attack_threat(%s, %s)"}, | |
440 | {"known_safe_move", 1, 0, 0.01, "register_known_safe_move(%s)"}, | |
441 | {"break_mirror_helper", 1, 0, 0.01, "break_mirror_helper(%s, color)"} | |
442 | }; | |
443 | ||
444 | ||
445 | /* To get a valid function pointer different from NULL. */ | |
446 | static int | |
447 | dummyhelper(int transformation, int move, int color, int action) | |
448 | { | |
449 | UNUSED(transformation); UNUSED(move); UNUSED(color); | |
450 | UNUSED(action); | |
451 | return 0; | |
452 | } | |
453 | ||
454 | ||
455 | #define PREAMBLE "\ | |
456 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\ | |
457 | * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *\n\ | |
458 | * http://www.gnu.org/software/gnugo/ for more information. *\n\ | |
459 | * *\n\ | |
460 | * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *\n\ | |
461 | * 2008 and 2009 by the Free Software Foundation. *\n\ | |
462 | * *\n\ | |
463 | * This program is free software; you can redistribute it and/or *\n\ | |
464 | * modify it under the terms of the GNU General Public License as *\n\ | |
465 | * published by the Free Software Foundation - version 3 or *\n\ | |
466 | * (at your option) any later version. *\n\ | |
467 | * *\n\ | |
468 | * This program is distributed in the hope that it will be useful, *\n\ | |
469 | * but WITHOUT ANY WARRANTY; without even the implied warranty of *\n\ | |
470 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *\n\ | |
471 | * GNU General Public License in file COPYING for more details. *\n\ | |
472 | * *\n\ | |
473 | * You should have received a copy of the GNU General Public *\n\ | |
474 | * License along with this program; if not, write to the Free *\n\ | |
475 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *\n\ | |
476 | * Boston, MA 02111, USA. *\n\ | |
477 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\n\ | |
478 | #include <stdio.h> /* for NULL */\n\ | |
479 | #include \"liberty.h\"\n\ | |
480 | #include \"patterns.h\"\n\n\ | |
481 | " | |
482 | ||
483 | static int fatal_errors = 0; | |
484 | ||
485 | /* options */ | |
486 | int verbose = 0; /* -v */ | |
487 | static int database_type = 0; /* -p (default), -c, -f, -C, -D or -T */ | |
488 | static int anchor = 0; /* Whether both O and/or X may be anchors. | |
489 | * -b for both. -X for only X. | |
490 | */ | |
491 | ||
492 | static int choose_best_anchor = 0; /* -m */ | |
493 | /* FIXME: `fixed anchor' option doesn't work properly yet. | |
494 | * Probably the first thing to implement is to add | |
495 | * checks for anchor validity. | |
496 | */ | |
497 | static int fixed_anchor = 0; /* -a */ | |
498 | ||
499 | static dfa_t dfa; | |
500 | static dfa_patterns dfa_pats; | |
501 | ||
502 | static int transformation_hint; | |
503 | static int labels_transformation = 0; | |
504 | ||
505 | ||
506 | struct hint_data { | |
507 | char name[MAXNAME]; | |
508 | int transformation_hint; | |
509 | struct hint_data *next; | |
510 | }; | |
511 | ||
512 | static struct hint_data *first_hint = NULL; | |
513 | ||
514 | ||
515 | static void | |
516 | parse_transformations_file(FILE *file) | |
517 | { | |
518 | struct hint_data **link = &first_hint; | |
519 | ||
520 | while (!feof(file)) { | |
521 | int n; | |
522 | struct hint_data *hint = malloc(sizeof(*hint)); | |
523 | ||
524 | n = fscanf(file, "%s %d", hint->name, &hint->transformation_hint); | |
525 | if (n == 2) { | |
526 | hint->next = NULL; | |
527 | *link = hint; | |
528 | link = &hint->next; | |
529 | } | |
530 | else | |
531 | free(hint); | |
532 | } | |
533 | } | |
534 | ||
535 | ||
536 | static int | |
537 | find_transformation_hint(const char *pattern_name) | |
538 | { | |
539 | struct hint_data *hint; | |
540 | ||
541 | if (database_type == DB_DFA || database_type == OPTIMIZE_DFA) { | |
542 | for (hint = first_hint; hint; hint = hint->next) { | |
543 | if (!strcmp(hint->name, pattern_name)) | |
544 | return hint->transformation_hint; | |
545 | } | |
546 | } | |
547 | ||
548 | return database_type == OPTIMIZE_DFA ? -1 : 0; | |
549 | } | |
550 | ||
551 | ||
552 | /************************** | |
553 | * | |
554 | * stuff to check the constraint diagram | |
555 | * | |
556 | **************************/ | |
557 | ||
558 | #define CHECK_CHARS "xXoO" | |
559 | static void | |
560 | check_constraint_diagram(void) | |
561 | { | |
562 | int i, j, ino = 0, iso = 0, jwo = 0; | |
563 | ||
564 | int have_constraint = (pattern[patno].autohelper_flag & HAVE_CONSTRAINT); | |
565 | if (0) | |
566 | fprintf(stderr, "patno: %d\n", patno); | |
567 | ||
568 | if (where & NORTH_EDGE) | |
569 | ino = 1; | |
570 | if (where & SOUTH_EDGE) | |
571 | iso = 1; | |
572 | if (where & WEST_EDGE) | |
573 | jwo = 1; | |
574 | ||
575 | if (verbose) { | |
576 | for (i = ino; i <= maxi+ino+iso; i++) | |
577 | fprintf(stderr, "%02d %s\n", i, diagram[i]); | |
578 | for (i = ino; i <= maxi+ino+iso; i++) | |
579 | fprintf(stderr, "%02d %s\n", i, constraint_diagram[i]); | |
580 | } | |
581 | ||
582 | if (0) | |
583 | fprintf(stderr, "have_constraint: %d\n", have_constraint); | |
584 | if (have_constraint && el) { | |
585 | for (i = ino; i <= maxi+ino; i++) | |
586 | for (j = jwo; j <= maxj+jwo; j++) { | |
587 | if (0) | |
588 | fprintf(stderr, "%2d %2d %c %c\n", i, j, constraint_diagram[i][j], | |
589 | diagram[i][j]); | |
590 | if (strchr(CHECK_CHARS, constraint_diagram[i][j]) | |
591 | && constraint_diagram[i][j] != diagram[i][j]) { | |
592 | fprintf(stderr, "%s(%d) : Error : " | |
593 | "xXoO not matched in constraint diagram of pattern %s\n", | |
594 | current_file, current_line_number, pattern_names[patno]); | |
595 | fatal_errors++; | |
596 | } | |
597 | } | |
598 | } | |
599 | } | |
600 | ||
601 | /************************** | |
602 | * | |
603 | * stuff to parse the input | |
604 | * | |
605 | **************************/ | |
606 | ||
607 | /* reset state of all pattern variables */ | |
608 | static void | |
609 | reset_pattern(void) | |
610 | { | |
611 | int i, j; | |
612 | ||
613 | maxi = 0; | |
614 | maxj = 0; | |
615 | ci = -1; | |
616 | cj = -1; | |
617 | where = 0; | |
618 | el = 0; | |
619 | num_stars = 0; | |
620 | strcpy(helper_fn_names[patno], "NULL"); | |
621 | for (i = 0; i < 256; i++) | |
622 | label_coords[i][0] = -1; | |
623 | current_c_i = 0; | |
624 | constraint[0] = 0; | |
625 | action[0] = 0; | |
626 | for (i = 0; i < MAX_BOARD+2; i++) { | |
627 | for (j = 0; j < MAX_BOARD+3; j++) { | |
628 | diagram[i][j] = '\0'; | |
629 | constraint_diagram[i][j] = '\0'; | |
630 | } | |
631 | } | |
632 | memset(&pattern[patno], 0, sizeof(struct pattern)); | |
633 | } | |
634 | ||
635 | ||
636 | /* This is called to compute the extents of the pattern, applying | |
637 | * edge constraints as necessary. | |
638 | */ | |
639 | static void | |
640 | find_extents(void) | |
641 | { | |
642 | /* When this is called, elements go from (mini,minj) inclusive to | |
643 | * (maxi-1, maxj-1) [ie exclusive]. Make them inclusive. | |
644 | * Ie maximum element lies on (maxi,maxj). | |
645 | */ | |
646 | ||
647 | --maxi; | |
648 | --maxj; | |
649 | ||
650 | /* apply edge constraints to the size of the pattern */ | |
651 | ||
652 | if (where & (NORTH_EDGE | SOUTH_EDGE | EAST_EDGE | WEST_EDGE)) | |
653 | ++pats_with_constraints; | |
654 | ||
655 | if (verbose) | |
656 | fprintf(stderr, "Pattern %s has constraints 0x%x\n", | |
657 | pattern_names[patno], where); | |
658 | ||
659 | pattern[patno].edge_constraints = where; | |
660 | ||
661 | ||
662 | /* At this point, (mini,minj) -> (maxi,maxj) contain | |
663 | * the extent of the pattern, relative to top-left | |
664 | * of pattern, rather than (ci,cj). | |
665 | * | |
666 | * But we store them in the output file relative | |
667 | * to (ci,cj), so that we can transform the corners | |
668 | * of the pattern like any other relative co-ord. | |
669 | */ | |
670 | ||
671 | pattern[patno].mini = mini - ci; | |
672 | pattern[patno].minj = minj - cj; | |
673 | pattern[patno].maxi = maxi - ci; | |
674 | pattern[patno].maxj = maxj - cj; | |
675 | } | |
676 | ||
677 | ||
678 | /* | |
679 | * Here we build the dfa. | |
680 | */ | |
681 | ||
682 | static void | |
683 | write_to_dfa(int index) | |
684 | { | |
685 | char str[DFA_MAX_ORDER + 1]; | |
686 | char strrot[DFA_MAX_ORDER + 1]; | |
687 | ||
688 | assert(ci != -1 && cj != -1); | |
689 | #if 0 | |
690 | pattern[index].patn = elements; /* a little modification : keep in touch! */ | |
691 | #endif | |
692 | pattern[index].name = &(pattern_names[index][0]); | |
693 | ||
694 | /* First we create the string from the actual pattern. */ | |
695 | pattern_2_string(pattern + index, elements, str, ci, cj); | |
696 | ||
697 | if (verbose) | |
698 | fprintf(stderr, "Add :%s\n", pattern[index].name); | |
699 | ||
700 | if (database_type == DB_DFA) { | |
701 | float ratio; | |
702 | ||
703 | dfa_rotate_string(strrot, str, transformation_hint); | |
704 | ratio = ((dfa_add_string(&dfa, strrot, index, transformation_hint) - 1.0) | |
705 | * 100); | |
706 | ||
707 | /* Complain when there is more than 10% of increase */ | |
708 | if (dfa_size(&dfa) > 100 && ratio > 10.0) { | |
709 | fprintf(stderr, "Pattern %s => %3.1f%% increase: ", | |
710 | pattern[index].name, ratio); | |
711 | fprintf(stderr, "another orientation may save memory.\n"); | |
712 | } | |
713 | if (dfa_verbose > 2) | |
714 | dump_dfa(stderr, &dfa); | |
715 | ||
716 | labels_transformation = transformation_hint; | |
717 | } | |
718 | else { | |
719 | int ll; | |
720 | int rot_start = 0; | |
721 | int rot_stop = pattern[index].trfno; | |
722 | ||
723 | assert(database_type == OPTIMIZE_DFA); | |
724 | ||
725 | if (rot_stop == 5) { | |
726 | rot_start = 2; | |
727 | rot_stop = 6; | |
728 | } | |
729 | ||
730 | for (ll = rot_start; ll < rot_stop; ll++) { | |
731 | dfa_rotate_string(strrot, str, ll); | |
732 | dfa_patterns_add_pattern(&dfa_pats, strrot, index); | |
733 | } | |
734 | ||
735 | if (transformation_hint == -1) | |
736 | dfa_patterns_select_shortest_variation(&dfa_pats); | |
737 | else { | |
738 | dfa_patterns_set_last_pattern_variation(&dfa_pats, (transformation_hint | |
739 | - rot_start)); | |
740 | } | |
741 | } | |
742 | } | |
743 | ||
744 | ||
745 | /* For good performance, we want to reject patterns as quickly as | |
746 | * possible. For each pattern, this combines 16 positions around | |
747 | * the anchor stone into a 32-bit mask and value. In the matcher, | |
748 | * the same 4x4 grid is precomputed, and then we can quickly | |
749 | * test 16 board positions with one test. | |
750 | * See matchpat.c for details of how this works - basically, if | |
751 | * we AND what is on the board with the and_mask, and get the | |
752 | * value in the val_mask, we have a match. This test can be | |
753 | * applied in parallel : 2 bits per posn x 16 posns = 32 bits. | |
754 | * "Don't care" has and_mask = val_mask = 0, which is handy ! | |
755 | */ | |
756 | ||
757 | static void | |
758 | compute_grids(void) | |
759 | { | |
760 | #if GRID_OPT | |
761 | /* element: . X O x o , a ! */ | |
762 | static const unsigned int and_mask[] = { 3, 3, 3, 1, 2, 3, 3, 3 }; | |
763 | static const unsigned int val_mask[] = { 0, 2, 1, 0, 0, 0, 0, 0 }; | |
764 | ||
765 | int ll; /* iterate over rotations */ | |
766 | int k; /* iterate over elements */ | |
767 | ||
768 | for (ll = 0; ll < 8; ++ll) { | |
769 | for (k = 0; k < el; ++k) { | |
770 | int ti, tj; | |
771 | int di, dj; | |
772 | ||
773 | TRANSFORM2(elements[k].x - ci, elements[k].y - cj, &ti, &tj, | |
774 | transformation_hint); | |
775 | TRANSFORM2(ti, tj, &di, &dj, ll); | |
776 | ||
777 | ++di; | |
778 | ++dj; | |
779 | if (di >= 0 && di < 4 && dj >= 0 && dj < 4) { | |
780 | pattern[patno].and_mask[ll] | |
781 | |= and_mask[elements[k].att] << (30 - di * 8 - dj * 2); | |
782 | pattern[patno].val_mask[ll] | |
783 | |= val_mask[elements[k].att] << (30 - di * 8 - dj * 2); | |
784 | } | |
785 | } | |
786 | } | |
787 | #endif | |
788 | } | |
789 | ||
790 | ||
791 | /* We've just read a line that looks like a pattern line. Now process it. | |
792 | * If the pattern becomes larger than maximal supported board, the function | |
793 | * returns zero, so that the pattern can be discarded. | |
794 | */ | |
795 | static int | |
796 | read_pattern_line(char *p) | |
797 | { | |
798 | const char *char_offset; | |
799 | char *pcopy = p; | |
800 | int j; | |
801 | int width; | |
802 | int jwo = 0, jeo = 0; | |
803 | ||
804 | if (where & SOUTH_EDGE) | |
805 | /* something wrong here : pattern line after a SOUTH_EDGE constraint */ | |
806 | goto fatal; | |
807 | ||
808 | ||
809 | if (*p == '+' || *p == '-') { | |
810 | /* must be a north/south constraint */ | |
811 | ||
812 | if (maxi == 0) | |
813 | where |= NORTH_EDGE; | |
814 | else | |
815 | where |= SOUTH_EDGE; | |
816 | ||
817 | if (*p == '+') { | |
818 | if (maxi > 0 && !(where & WEST_EDGE)) | |
819 | /* if this is end of pattern, must already know about west */ | |
820 | goto fatal; | |
821 | ||
822 | where |= WEST_EDGE; | |
823 | ++p; | |
824 | } | |
825 | ||
826 | /* count the number of -'s */ | |
827 | for (width = 0; *p == '-'; ++p, ++width) | |
828 | ; | |
829 | ||
830 | if (width == 0) | |
831 | goto fatal; | |
832 | ||
833 | if (*p == '+') { | |
834 | if (maxi > 0 && !(where & EAST_EDGE)) | |
835 | /* if this is end of pattern, must already know about west */ | |
836 | goto fatal; | |
837 | where |= EAST_EDGE; | |
838 | } | |
839 | ||
840 | if (maxi > 0 && width != maxj) | |
841 | goto notrectangle; | |
842 | ||
843 | return 1; | |
844 | } | |
845 | ||
846 | /* get here => its a real pattern entry, | |
847 | * rather than a north/south constraint | |
848 | */ | |
849 | ||
850 | /* we have a pattern line - add it into the current pattern */ | |
851 | if (*p == '|') { | |
852 | /* if this is not the first line, or if there is a north | |
853 | * constraint, we should already know about it | |
854 | */ | |
855 | if (!(where & WEST_EDGE) && ((where & NORTH_EDGE) || maxi > 0)) | |
856 | /* we should already know about this constraint */ | |
857 | goto fatal; | |
858 | ||
859 | where |= WEST_EDGE; | |
860 | ++p; | |
861 | } | |
862 | else if (where & WEST_EDGE) | |
863 | /* need a | if we are already constrained to west */ | |
864 | goto fatal; | |
865 | ||
866 | ||
867 | for (j = 0; | |
868 | (char_offset = strchr(VALID_PATTERN_CHARS, *p)) != NULL; | |
869 | ++j, ++p) { | |
870 | ||
871 | /* char_offset is a pointer within the VALID_PATTERN_CHARS string. | |
872 | * so (char-VALID_PATTERN_CHARS) is the att (0 to 11) to write to the | |
873 | * pattern element | |
874 | */ | |
875 | ||
876 | /* one of ATT_* - see above */ | |
877 | int off = char_offset - VALID_PATTERN_CHARS; | |
878 | ||
879 | if (off == ATT_wild) | |
880 | continue; /* boring - pad character */ | |
881 | ||
882 | if (off == ATT_a) /* this were used by halfeye patterns */ | |
883 | goto fatal; | |
884 | ||
885 | if (off == ATT_star) { | |
886 | /* '*' */ | |
887 | movei = maxi; | |
888 | movej = j; | |
889 | ++num_stars; | |
890 | off = ATT_dot; /* add a '.' to the pattern instead */ | |
891 | } | |
892 | ||
893 | if (off == ATT_Q) { | |
894 | off = ATT_O; | |
895 | ci = maxi; | |
896 | cj = j; | |
897 | pattern[patno].anchored_at_X = (off == ATT_X) ? 3 : 0; | |
898 | /*FIXME: Make sure O is valid anchor*/ | |
899 | } | |
900 | ||
901 | if (off == ATT_Y) { | |
902 | off = ATT_X; | |
903 | ci = maxi; | |
904 | cj = j; | |
905 | pattern[patno].anchored_at_X = (off == ATT_X) ? 3 : 0; | |
906 | /*FIXME: Make sure X is valid anchor*/ | |
907 | } | |
908 | ||
909 | assert(off <= ATT_not); | |
910 | ||
911 | ||
912 | if ((ci == -1) && (off < 3) && ((off & anchor) != 0) | |
913 | && !fixed_anchor) { | |
914 | /* Use this position as the pattern origin. */ | |
915 | ci = maxi; | |
916 | cj = j; | |
917 | pattern[patno].anchored_at_X = (off == ATT_X) ? 3 : 0; | |
918 | } | |
919 | ||
920 | /* Range checking. */ | |
921 | if (el >= (int) (sizeof(elements) / sizeof(elements[0]))) | |
922 | return 0; | |
923 | ||
924 | elements[el].x = maxi; | |
925 | elements[el].y = j; | |
926 | elements[el].att = off; /* '*' mapped to '.' and 'Q' to 'O' above */ | |
927 | ||
928 | ++el; | |
929 | } | |
930 | ||
931 | if (*p == '|') { | |
932 | ||
933 | /* if this is not the first line, or if there is a north | |
934 | * constraint, we should already know about it | |
935 | */ | |
936 | if (!(where & EAST_EDGE) && ((where & NORTH_EDGE) || maxi > 0)) | |
937 | goto fatal; /* we should already know about this constraint */ | |
938 | ||
939 | where |= EAST_EDGE; | |
940 | ||
941 | } | |
942 | else if (where & EAST_EDGE) | |
943 | goto fatal; /* need a | if we are already constrained to east */ | |
944 | ||
945 | ||
946 | if (maxi > 0 && j != maxj) | |
947 | goto notrectangle; | |
948 | ||
949 | if (j > maxj) | |
950 | maxj = j; | |
951 | ||
952 | ||
953 | if (where & WEST_EDGE) | |
954 | jwo = 1; | |
955 | if (where & EAST_EDGE) | |
956 | jeo = 1; | |
957 | if (maxi <= MAX_BOARD) | |
958 | strncpy(diagram[maxi], pcopy, maxj + jwo + jeo); | |
959 | maxi++; | |
960 | ||
961 | return maxi <= MAX_BOARD && maxj <= MAX_BOARD; | |
962 | ||
963 | fatal: | |
964 | fprintf(stderr, "%s(%d) : error : Illegal pattern %s\n", | |
965 | current_file, current_line_number, pattern_names[patno]); | |
966 | fatal_errors = 1; | |
967 | return 0; | |
968 | ||
969 | notrectangle: | |
970 | fprintf(stderr, "%s(%d) : error : Pattern %s not rectangular\n", | |
971 | current_file, current_line_number, pattern_names[patno]); | |
972 | fatal_errors++; | |
973 | return 0; | |
974 | } | |
975 | ||
976 | ||
977 | /* | |
978 | * We've just read a line that looks like a constraint pattern line. | |
979 | * Now process it. | |
980 | */ | |
981 | ||
982 | static void | |
983 | read_constraint_diagram_line(char *p) | |
984 | { | |
985 | int j; | |
986 | int jwo = 0, jeo = 0; | |
987 | const char *pcopy = p; | |
988 | ||
989 | /* North or south boundary, no letter to be found. */ | |
990 | if (*p == '+' || *p == '-') | |
991 | return; | |
992 | ||
993 | /* Skip west boundary. */ | |
994 | if (*p == '|') | |
995 | p++; | |
996 | ||
997 | for (j = 0; | |
998 | strchr(VALID_PATTERN_CHARS, *p) || strchr(VALID_CONSTRAINT_LABELS, *p); | |
999 | ++j, ++p) { | |
1000 | if (strchr(VALID_CONSTRAINT_LABELS, *p) | |
1001 | && label_coords[(int)*p][0] == -1) { | |
1002 | ||
1003 | /* New constraint letter */ | |
1004 | label_coords[(int)*p][0] = current_c_i; | |
1005 | label_coords[(int)*p][1] = j; | |
1006 | } | |
1007 | } | |
1008 | ||
1009 | /* Now j holds the width of this constraint diagram line. Require | |
1010 | * this to match the main diagram width stored in maxj. However, | |
1011 | * maxj was modified by find_extents() so we have to compensate for | |
1012 | * this. | |
1013 | */ | |
1014 | if (j != maxj + 1 && !discard_pattern) { | |
1015 | fprintf(stderr, "%s(%d) : error : Mismatching width of constraint line in pattern %s\n", | |
1016 | current_file, current_line_number, pattern_names[patno]); | |
1017 | fatal_errors++; | |
1018 | return; | |
1019 | } | |
1020 | ||
1021 | if (where & WEST_EDGE) | |
1022 | jwo = 1; | |
1023 | if (where & EAST_EDGE) | |
1024 | jeo = 1; | |
1025 | if (el) | |
1026 | strncpy(constraint_diagram[current_c_i], pcopy, maxj+jwo+jeo+1); | |
1027 | current_c_i++; | |
1028 | ||
1029 | return; | |
1030 | } | |
1031 | ||
1032 | /* Check that the constraint diagram had the same number of rows as | |
1033 | * the main diagram. | |
1034 | */ | |
1035 | static void | |
1036 | check_constraint_diagram_size(void) | |
1037 | { | |
1038 | if (current_c_i != maxi + 1 && !discard_pattern) { | |
1039 | fprintf(stderr, "%s(%d) : error : Mismatching height of constraint diagram in pattern %s\n", | |
1040 | current_file, current_line_number, pattern_names[patno]); | |
1041 | fatal_errors++; | |
1042 | } | |
1043 | } | |
1044 | ||
1045 | ||
1046 | static void | |
1047 | convert_attribute_labels_to_offsets(void) | |
1048 | { | |
1049 | struct pattern_attribute *attribute; | |
1050 | ||
1051 | if (patno < 0 || !pattern[patno].attributes) | |
1052 | return; | |
1053 | ||
1054 | for (attribute = pattern[patno].attributes; | |
1055 | attribute->type != LAST_ATTRIBUTE; | |
1056 | attribute++) { | |
1057 | if (attribute->type >= FIRST_OFFSET_ATTRIBUTE) { | |
1058 | int label = attribute->offset; | |
1059 | int x; | |
1060 | int y; | |
1061 | ||
1062 | if (label_coords[label][0] == -1) { | |
1063 | fprintf(stderr, | |
1064 | "%s(%d) : error : Pattern attribute uses label '%c' that wasn't specified in the diagram\n", | |
1065 | current_file, current_line_number, label); | |
1066 | fatal_errors++; | |
1067 | return; | |
1068 | } | |
1069 | ||
1070 | TRANSFORM2(label_coords[label][0] - ci - movei, | |
1071 | label_coords[label][1] - cj - movej, &x, &y, | |
1072 | labels_transformation); | |
1073 | attribute->offset = OFFSET(x, y); | |
1074 | } | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | ||
1079 | /* On reading a line starting ':', finish up and write | |
1080 | * out the current pattern | |
1081 | */ | |
1082 | static void | |
1083 | finish_pattern(char *line) | |
1084 | { | |
1085 | int x; | |
1086 | int y; | |
1087 | ||
1088 | /* end of pattern layout */ | |
1089 | char symmetry; /* the symmetry character */ | |
1090 | ||
1091 | mini = minj = 0; /* initially : can change with edge-constraints */ | |
1092 | ||
1093 | if (num_stars > 1 || (database_type != DB_CONNECTIONS && num_stars == 0)) { | |
1094 | fprintf(stderr, "%s(%d) : error : No or too many *'s in pattern %s\n", | |
1095 | current_file, current_line_number, pattern_names[patno]); | |
1096 | fatal_errors = 1; | |
1097 | } | |
1098 | ||
1099 | if (database_type == DB_CORNER) { | |
1100 | ci = 0; | |
1101 | cj = 0; | |
1102 | } | |
1103 | else if (choose_best_anchor && !discard_pattern) { | |
1104 | ||
1105 | /* Try to find a better anchor if | |
1106 | * the -m option is set. | |
1107 | */ | |
1108 | int mi, mj; /* middle */ | |
1109 | int d, min_d = 36100; | |
1110 | int k, min_k = -1; | |
1111 | ||
1112 | /* We seek the element of suitable value minimizing | |
1113 | * the distance to the middle. | |
1114 | */ | |
1115 | mi = (maxi - 1) * 50; | |
1116 | mj = (maxj - 1) * 50 - 1; | |
1117 | for (k = 0; k != el; k++) | |
1118 | if (elements[k].att < 3 && (elements[k].att & anchor) != 0) { | |
1119 | d = gg_abs(100 * elements[k].x - mi) | |
1120 | + gg_abs(100 * elements[k].y - mj); | |
1121 | if (d < min_d) { | |
1122 | min_k = k; | |
1123 | min_d = d; | |
1124 | } | |
1125 | } | |
1126 | assert(min_k != -1); | |
1127 | ci = elements[min_k].x; | |
1128 | cj = elements[min_k].y; | |
1129 | pattern[patno].anchored_at_X = (elements[min_k].att == ATT_X) ? 3 : 0; | |
1130 | ||
1131 | } | |
1132 | else if ((ci == -1 || cj == -1) && !discard_pattern) { | |
1133 | fprintf(stderr, "%s(%d) : No origin for pattern %s\n", | |
1134 | current_file, current_line_number, pattern_names[patno]); | |
1135 | fatal_errors = 1; | |
1136 | ci = 0; | |
1137 | cj = 0; | |
1138 | } | |
1139 | ||
1140 | /* translate posn of * (or Q) to relative co-ords | |
1141 | */ | |
1142 | ||
1143 | if (num_stars == 1) { | |
1144 | movei -= ci; | |
1145 | movej -= cj; | |
1146 | } | |
1147 | else if (num_stars == 0) { | |
1148 | movei = ci; | |
1149 | movej = cj; | |
1150 | } | |
1151 | ||
1152 | TRANSFORM2(movei, movej, &x, &y, transformation_hint); | |
1153 | pattern[patno].move_offset = OFFSET(x, y); | |
1154 | ||
1155 | find_extents(); | |
1156 | ||
1157 | compute_grids(); | |
1158 | ||
1159 | pattern[patno].patlen = el; | |
1160 | ||
1161 | /* Now parse the line. Only the symmetry character and the class | |
1162 | * field are mandatory. The compiler guarantees that all the fields | |
1163 | * are already initialized to 0. | |
1164 | */ | |
1165 | ||
1166 | { | |
1167 | int s; | |
1168 | char class[80]; | |
1169 | char entry[80]; | |
1170 | char *p = line; | |
1171 | char *p2; | |
1172 | int n; | |
1173 | ||
1174 | class[0] = 0; /* in case sscanf doesn't get that far */ | |
1175 | s = sscanf(p, ":%c,%[^,]%n", &symmetry, class, &n); | |
1176 | p += n; | |
1177 | ||
1178 | if (s < 2) { | |
1179 | fprintf(stderr, ": line must contain symmetry character and class\n"); | |
1180 | fatal_errors++; | |
1181 | } | |
1182 | ||
1183 | pattern[patno].attributes = NULL; | |
1184 | while (sscanf(p, "%*[, ]%[^,]%n", entry, &n) > 0) { | |
1185 | const char *paren; | |
1186 | p += n; | |
1187 | ||
1188 | paren = strchr(entry, '('); | |
1189 | if (paren) { | |
1190 | struct attribute_description *description = NULL; | |
1191 | ||
1192 | if (attribute_map) { | |
1193 | for (description = attribute_map; description->input_name; | |
1194 | description++) { | |
1195 | if (strncmp(entry, description->input_name, paren - entry) == 0) { | |
1196 | if (description->type != IN_PATTERN_VALUE) { | |
1197 | if (!pattern[patno].attributes) | |
1198 | pattern[patno].attributes = attributes + num_attributes; | |
1199 | ||
1200 | attributes[num_attributes].type = description->type; | |
1201 | if (description->type >= FIRST_OFFSET_ATTRIBUTE) { | |
1202 | /* We store the label for now, since we don't know | |
1203 | * its offset without having seen the constraint | |
1204 | * diagram. | |
1205 | */ | |
1206 | if (*(paren + 1) != '*' | |
1207 | && !strchr(VALID_CONSTRAINT_LABELS, *(paren + 1))) { | |
1208 | fprintf(stderr, "%s(%d) : error : '%c' is not a valid label.\n", | |
1209 | current_file, current_line_number, *(paren + 1)); | |
1210 | fatal_errors++; | |
1211 | continue; | |
1212 | } | |
1213 | ||
1214 | attributes[num_attributes].offset = *(paren + 1); | |
1215 | } | |
1216 | else | |
1217 | sscanf(paren + 1, "%f", &attributes[num_attributes].value); | |
1218 | ||
1219 | num_attributes++; | |
1220 | } | |
1221 | else | |
1222 | sscanf(paren + 1, "%f", &pattern[patno].value); | |
1223 | ||
1224 | if (!strchr(paren + 1, ')')) { | |
1225 | fprintf(stderr, "%s(%d) : error : ')' missed\n", | |
1226 | current_file, current_line_number); | |
1227 | fatal_errors++; | |
1228 | } | |
1229 | ||
1230 | break; | |
1231 | } | |
1232 | } | |
1233 | } | |
1234 | ||
1235 | if (attribute_map == NULL || description->input_name == NULL) { | |
1236 | fprintf(stderr, "%s(%d) : error : Unknown value field: %s\n", | |
1237 | current_file, current_line_number, entry); | |
1238 | fatal_errors++; | |
1239 | break; | |
1240 | } | |
1241 | } | |
1242 | else { | |
1243 | strncpy(helper_fn_names[patno], entry, 79); | |
1244 | break; | |
1245 | } | |
1246 | } | |
1247 | ||
1248 | if (pattern[patno].attributes != NULL) { | |
1249 | attributes[num_attributes].type = LAST_ATTRIBUTE; | |
1250 | attributes[num_attributes].value = 0.0; | |
1251 | num_attributes++; | |
1252 | } | |
1253 | ||
1254 | for (p2 = class; *p2; p2++) { | |
1255 | switch (*p2) { | |
1256 | case 's': pattern[patno].class |= CLASS_s; break; | |
1257 | case 'O': pattern[patno].class |= CLASS_O; break; | |
1258 | case 'o': pattern[patno].class |= CLASS_o; break; | |
1259 | case 'X': pattern[patno].class |= CLASS_X; break; | |
1260 | case 'x': pattern[patno].class |= CLASS_x; break; | |
1261 | case 'D': pattern[patno].class |= CLASS_D; break; | |
1262 | case 'C': pattern[patno].class |= CLASS_C; break; | |
1263 | case 'c': pattern[patno].class |= CLASS_c; break; | |
1264 | case 'n': pattern[patno].class |= CLASS_n; break; | |
1265 | case 'B': pattern[patno].class |= CLASS_B; break; | |
1266 | case 'A': pattern[patno].class |= CLASS_A; break; | |
1267 | case 'b': pattern[patno].class |= CLASS_b; break; | |
1268 | case 'e': pattern[patno].class |= CLASS_e; break; | |
1269 | case 'E': pattern[patno].class |= CLASS_E; break; | |
1270 | case 'a': pattern[patno].class |= CLASS_a; break; | |
1271 | case 'd': pattern[patno].class |= CLASS_d; break; | |
1272 | case 'I': pattern[patno].class |= CLASS_I; break; | |
1273 | case 'J': pattern[patno].class |= CLASS_J; break; | |
1274 | case 'j': pattern[patno].class |= CLASS_j; break; | |
1275 | case 't': pattern[patno].class |= CLASS_t; break; | |
1276 | case 'T': pattern[patno].class |= CLASS_T; break; | |
1277 | case 'U': pattern[patno].class |= CLASS_U; break; | |
1278 | case 'W': pattern[patno].class |= CLASS_W; break; | |
1279 | case 'F': pattern[patno].class |= CLASS_F; break; | |
1280 | case 'N': pattern[patno].class |= CLASS_N; break; | |
1281 | case 'Y': pattern[patno].class |= CLASS_Y; break; | |
1282 | case '-': break; | |
1283 | default: | |
1284 | if (!isgraph((int) *p2)) | |
1285 | break; | |
1286 | fprintf(stderr, | |
1287 | "%s(%d) : error : Unknown classification letter %c. (pattern %s).\n", | |
1288 | current_file, current_line_number, *p2, | |
1289 | pattern_names[patno]); | |
1290 | fatal_errors++; | |
1291 | break; | |
1292 | } | |
1293 | } | |
1294 | } | |
1295 | ||
1296 | ||
1297 | /* Now get the symmetry. There are extra checks we can make to do with | |
1298 | * square-ness and edges. We do this before we work out the edge constraints, | |
1299 | * since that mangles the size info. | |
1300 | */ | |
1301 | ||
1302 | switch (symmetry) { | |
1303 | case '+' : | |
1304 | if (where & (NORTH_EDGE | EAST_EDGE | SOUTH_EDGE | WEST_EDGE)) | |
1305 | fprintf(stderr, | |
1306 | "%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n", | |
1307 | current_file, current_line_number, pattern_names[patno]); | |
1308 | pattern[patno].trfno = 2; | |
1309 | break; | |
1310 | ||
1311 | case 'X' : | |
1312 | if (where & (NORTH_EDGE | EAST_EDGE | SOUTH_EDGE | WEST_EDGE)) | |
1313 | fprintf(stderr, | |
1314 | "%s(%d) : Warning : X symmetry inconsistent with edge constraints (pattern %s)\n", | |
1315 | current_file, current_line_number, pattern_names[patno]); | |
1316 | if (maxi != maxj) | |
1317 | fprintf(stderr, | |
1318 | "%s(%d) : Warning : X symmetry requires a square pattern (pattern %s)\n", | |
1319 | current_file, current_line_number, pattern_names[patno]); | |
1320 | pattern[patno].trfno = 2; | |
1321 | break; | |
1322 | ||
1323 | case '-' : | |
1324 | if (where & (NORTH_EDGE | SOUTH_EDGE)) | |
1325 | fprintf(stderr, | |
1326 | "%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n", | |
1327 | current_file, current_line_number, pattern_names[patno]); | |
1328 | pattern[patno].trfno = 4; | |
1329 | break; | |
1330 | ||
1331 | case '|' : | |
1332 | if (where & (EAST_EDGE | WEST_EDGE)) | |
1333 | fprintf(stderr, | |
1334 | "%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n", | |
1335 | current_file, current_line_number, pattern_names[patno]); | |
1336 | pattern[patno].trfno = 4; | |
1337 | break; | |
1338 | ||
1339 | case '\\' : | |
1340 | case '/' : | |
1341 | /* FIXME: Can't be bothered putting in the edge tests. | |
1342 | * (What does this mean?) | |
1343 | */ | |
1344 | if (maxi != maxj) | |
1345 | fprintf(stderr, | |
1346 | "%s(%d) : Warning : \\ or / symmetry requires a square pattern (pattern %s)\n", | |
1347 | current_file, current_line_number, pattern_names[patno]); | |
1348 | ||
1349 | pattern[patno].trfno = 4; | |
1350 | break; | |
1351 | ||
1352 | case 'O' : | |
1353 | if (where & (NORTH_EDGE | EAST_EDGE | SOUTH_EDGE | WEST_EDGE)) | |
1354 | fprintf(stderr, | |
1355 | "%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n", | |
1356 | current_file, current_line_number, pattern_names[patno]); | |
1357 | pattern[patno].trfno = 5; /* Ugly special convention. */ | |
1358 | break; | |
1359 | ||
1360 | default: | |
1361 | fprintf(stderr, | |
1362 | "%s(%d) : Warning : symmetry character '%c' not implemented - using '8' (pattern %s)\n", | |
1363 | current_file, current_line_number, symmetry, pattern_names[patno]); | |
1364 | /* FALLTHROUGH */ | |
1365 | case '8' : | |
1366 | pattern[patno].trfno = 8; | |
1367 | break; | |
1368 | } | |
1369 | } | |
1370 | ||
1371 | ||
1372 | static void | |
1373 | read_constraint_line(char *line) | |
1374 | { | |
1375 | /* Avoid buffer overrun. */ | |
1376 | assert(strlen(constraint) + strlen(line) < MAXCONSTRAINT); | |
1377 | ||
1378 | /* Append the new line. */ | |
1379 | strcat(constraint, line); | |
1380 | ||
1381 | pattern[patno].autohelper_flag |= HAVE_CONSTRAINT; | |
1382 | } | |
1383 | ||
1384 | ||
1385 | static void | |
1386 | read_action_line(char *line) | |
1387 | { | |
1388 | /* Avoid buffer overrun. */ | |
1389 | assert(strlen(action) + strlen(line) < MAXACTION); | |
1390 | ||
1391 | /* Append the new line. */ | |
1392 | strcat(action, line); | |
1393 | ||
1394 | pattern[patno].autohelper_flag |= HAVE_ACTION; | |
1395 | } | |
1396 | ||
1397 | ||
1398 | static void | |
1399 | generate_autohelper_code(int funcno, int number_of_params, int *labels) | |
1400 | { | |
1401 | int i; | |
1402 | char varnames[MAXPARAMS][8]; | |
1403 | char pattern_id[MAXLINE]; | |
1404 | ||
1405 | for (i = 0; i < number_of_params; i++) { | |
1406 | if (labels[i] == (int) '*') | |
1407 | sprintf(varnames[i], "move"); | |
1408 | /* The special label '?' represents a tenuki. We replace this | |
1409 | * with NO_MOVE value. | |
1410 | */ | |
1411 | else if (labels[i] == (int) '?') | |
1412 | sprintf(varnames[i], "NO_MOVE"); | |
1413 | else | |
1414 | sprintf(varnames[i], "%c", labels[i]); | |
1415 | } | |
1416 | ||
1417 | switch (autohelper_functions[funcno].type) { | |
1418 | case 0: | |
1419 | /* A common case. Just use the labels as parameters. */ | |
1420 | switch (number_of_params) { | |
1421 | case 0: | |
1422 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code); | |
1423 | break; | |
1424 | case 1: | |
1425 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1426 | varnames[0]); | |
1427 | break; | |
1428 | case 2: | |
1429 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1430 | varnames[0], varnames[1]); | |
1431 | break; | |
1432 | case 3: | |
1433 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1434 | varnames[0], varnames[1], varnames[2]); | |
1435 | break; | |
1436 | case 4: | |
1437 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1438 | varnames[0], varnames[1], varnames[2], | |
1439 | varnames[3]); | |
1440 | break; | |
1441 | default: | |
1442 | fprintf(stderr, "%s(%d) : error : unknown number of parameters (pattern %s)", | |
1443 | current_file, current_line_number, pattern_names[patno]); | |
1444 | fatal_errors++; | |
1445 | } | |
1446 | break; | |
1447 | ||
1448 | case 1: | |
1449 | /* This is a very special case where there is assumed to be a | |
1450 | * variable number of parameters and these constitute a series of | |
1451 | * moves to make followed by a final attack or defense test. | |
1452 | */ | |
1453 | if (number_of_params < autohelper_functions[funcno].params) { | |
1454 | fprintf(stderr, "%s(%d) : error : too few parameters (pattern %s)", | |
1455 | current_file, current_line_number, pattern_names[patno]); | |
1456 | fatal_errors++; | |
1457 | return; | |
1458 | } | |
1459 | ||
1460 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1461 | number_of_params - autohelper_functions[funcno].params); | |
1462 | ||
1463 | for (i = 0; i < number_of_params; i++) | |
1464 | code_pos += sprintf(code_pos, ", %s", varnames[i]); | |
1465 | ||
1466 | *code_pos++ = ')'; /* Close parenthesis. */ | |
1467 | break; | |
1468 | ||
1469 | default: /* 2 */ | |
1470 | /* A very special case. We add the address of the current pattern | |
1471 | * before the actual parameters. So far, used only by `value'. | |
1472 | */ | |
1473 | sprintf(pattern_id, "(%s + %d)", prefix, patno); | |
1474 | ||
1475 | switch (number_of_params) { | |
1476 | case 0: | |
1477 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1478 | pattern_id); | |
1479 | break; | |
1480 | case 1: | |
1481 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1482 | pattern_id, varnames[0]); | |
1483 | break; | |
1484 | case 2: | |
1485 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1486 | pattern_id, varnames[0], varnames[1]); | |
1487 | break; | |
1488 | case 3: | |
1489 | code_pos += sprintf(code_pos, autohelper_functions[funcno].code, | |
1490 | pattern_id, varnames[0], varnames[1], varnames[2]); | |
1491 | break; | |
1492 | default: | |
1493 | fprintf(stderr, "%s(%d) : error : unknown number of parameters (pattern %s)", | |
1494 | current_file, current_line_number, pattern_names[patno]); | |
1495 | fatal_errors++; | |
1496 | } | |
1497 | } | |
1498 | } | |
1499 | ||
1500 | ||
1501 | /* Parse the constraint and generate the corresponding helper code. | |
1502 | * We use a small state machine. | |
1503 | */ | |
1504 | static void | |
1505 | parse_constraint_or_action(char *line, float *cost) | |
1506 | { | |
1507 | int state = 0; | |
1508 | char *p; | |
1509 | int n = 0; | |
1510 | int label = 0; | |
1511 | int labels[MAXLABELS]; | |
1512 | int N = sizeof(autohelper_functions)/sizeof(struct autohelper_func); | |
1513 | int number_of_params = 0; | |
1514 | float cost_factor = 1.0; | |
1515 | ||
1516 | *cost = 0.0; | |
1517 | for (p = line; *p; p++) | |
1518 | { | |
1519 | switch (state) { | |
1520 | case 0: /* Looking for a token, echoing other characters. */ | |
1521 | for (n = 0; n < N; n++) { | |
1522 | if (strncmp(p, autohelper_functions[n].name, | |
1523 | strlen(autohelper_functions[n].name)) == 0) { | |
1524 | state = 1; | |
1525 | p += strlen(autohelper_functions[n].name)-1; | |
1526 | *cost += autohelper_functions[n].cost * cost_factor; | |
1527 | cost_factor *= 0.6; | |
1528 | break; | |
1529 | } | |
1530 | } | |
1531 | if (state == 0 && *p != '\n') | |
1532 | *(code_pos++) = *p; | |
1533 | break; | |
1534 | ||
1535 | case 1: /* Token found, now expect a '('. */ | |
1536 | if (*p != '(') { | |
1537 | if (autohelper_functions[n].params == 0) { | |
1538 | /* We allow to omit parenthesis when using functions which | |
1539 | * have no parameters. In any case, you may still place them, | |
1540 | * but having to write `value() = 50' is disgusting. | |
1541 | */ | |
1542 | generate_autohelper_code(n, 0, NULL); | |
1543 | p--; | |
1544 | state = 0; | |
1545 | break; | |
1546 | } | |
1547 | fprintf(stderr, | |
1548 | "%s(%d) : error : Syntax error in constraint or action, '(' expected (pattern %s, autohelper %s).\n", | |
1549 | current_file, current_line_number, pattern_names[patno], | |
1550 | autohelper_functions[n].name); | |
1551 | fatal_errors++; | |
1552 | return; | |
1553 | } | |
1554 | else { | |
1555 | assert(autohelper_functions[n].params <= MAXPARAMS); | |
1556 | number_of_params = 0; | |
1557 | if (autohelper_functions[n].params != 0 | |
1558 | || autohelper_functions[n].type == 1) | |
1559 | state = 2; | |
1560 | else | |
1561 | state = 3; | |
1562 | } | |
1563 | break; | |
1564 | ||
1565 | case 2: /* Time for a label. */ | |
1566 | if ((*p != '*') && (*p != '?') && !strchr(VALID_CONSTRAINT_LABELS, *p)) { | |
1567 | if (strchr("XxOo", *p)) | |
1568 | fprintf(stderr, | |
1569 | "%s(%d) : error : '%c' is not allowed as a constraint label.\n", | |
1570 | current_file, current_line_number, *p); | |
1571 | else | |
1572 | fprintf(stderr, | |
1573 | "%s(%d) : error : Syntax error in constraint or action, label expected, found '%c'.\n", | |
1574 | current_file, current_line_number, *p); | |
1575 | fatal_errors++; | |
1576 | return; | |
1577 | } | |
1578 | else { | |
1579 | if ((*p == '?') && (number_of_params == 0)) { | |
1580 | fprintf(stderr, | |
1581 | "mkpat: tenuki (?) cannot be the first label (pattern %s)\n", pattern_names[patno]); | |
1582 | return; | |
1583 | } | |
1584 | ||
1585 | label = (int) *p; | |
1586 | /* The special label '?' represents a tenuki. */ | |
1587 | if (*p != '*' && *p != '?' && label_coords[label][0] == -1) { | |
1588 | fprintf(stderr, | |
1589 | "%s(%d) : error : The constraint or action uses a label (%c) that wasn't specified in the diagram (pattern %s).\n", | |
1590 | current_file, current_line_number, label, pattern_names[patno]); | |
1591 | fatal_errors++; | |
1592 | return; | |
1593 | } | |
1594 | labels[number_of_params] = label; | |
1595 | number_of_params++; | |
1596 | state = 3; | |
1597 | } | |
1598 | break; | |
1599 | ||
1600 | case 3: /* A ',' or a ')'. */ | |
1601 | if (*p == ',') { | |
1602 | if (autohelper_functions[n].type != 1 | |
1603 | && number_of_params == autohelper_functions[n].params) { | |
1604 | fprintf(stderr, | |
1605 | "%s(%d) : error : Syntax error in constraint or action, ')' expected (pattern %s).\n", | |
1606 | current_file, current_line_number, pattern_names[patno]); | |
1607 | return; | |
1608 | } | |
1609 | if (number_of_params == MAXPARAMS) { | |
1610 | fprintf(stderr, | |
1611 | "Error in constraint or action, too many parameters. (pattern %s).\n", | |
1612 | pattern_names[patno]); | |
1613 | return; | |
1614 | } | |
1615 | state = 2; | |
1616 | break; | |
1617 | } | |
1618 | else if (*p != ')') { | |
1619 | fprintf(stderr, | |
1620 | "%s(%d) : error : Syntax error in constraint or action, ',' or ')' expected (pattern %s).\n", | |
1621 | current_file, current_line_number, pattern_names[patno]); | |
1622 | return; | |
1623 | } | |
1624 | else { /* a closing parenthesis */ | |
1625 | if ((autohelper_functions[n].type != 1) | |
1626 | && (number_of_params < autohelper_functions[n].params)) { | |
1627 | fprintf(stderr, | |
1628 | "%s(%d) : error : Syntax error in constraint or action, %s() requires %d parameters.\n", | |
1629 | current_file, current_line_number, autohelper_functions[n].name, autohelper_functions[n].params); | |
1630 | fatal_errors++; | |
1631 | return; | |
1632 | } | |
1633 | generate_autohelper_code(n, number_of_params, labels); | |
1634 | state = 0; | |
1635 | } | |
1636 | break; | |
1637 | ||
1638 | default: | |
1639 | fprintf(stderr, | |
1640 | "%s(%d) : error : Internal error in parse_constraint_or_action(), unknown state.\n", | |
1641 | current_file, current_line_number); | |
1642 | fatal_errors++; | |
1643 | return; | |
1644 | ||
1645 | } | |
1646 | } | |
1647 | } | |
1648 | ||
1649 | /* Finish up a constraint and/or action and generate the automatic | |
1650 | * helper code. The constraint text is in the global variable | |
1651 | * constraint. */ | |
1652 | ||
1653 | static void | |
1654 | finish_constraint_and_action(void) | |
1655 | { | |
1656 | unsigned int i; | |
1657 | float cost; | |
1658 | int have_constraint = (pattern[patno].autohelper_flag & HAVE_CONSTRAINT); | |
1659 | int have_action = (pattern[patno].autohelper_flag & HAVE_ACTION); | |
1660 | int no_labels = 1; | |
1661 | ||
1662 | /* Mark that this pattern has an autohelper. */ | |
1663 | pattern[patno].autohelper = dummyhelper; | |
1664 | ||
1665 | /* Generate autohelper function declaration. */ | |
1666 | code_pos += sprintf(code_pos, | |
1667 | "static int\nautohelper%s%d(int trans, int move, int color, int action)\n{\n int", | |
1668 | prefix, patno); | |
1669 | ||
1670 | /* Generate variable declarations. */ | |
1671 | for (i = 0; i < sizeof(VALID_CONSTRAINT_LABELS); i++) { | |
1672 | int c = (int) VALID_CONSTRAINT_LABELS[i]; | |
1673 | ||
1674 | if (label_coords[c][0] != -1) | |
1675 | code_pos += sprintf(code_pos, " %c,", c); | |
1676 | } | |
1677 | ||
1678 | /* Replace the last ',' with ';' */ | |
1679 | if (*(code_pos-1) == ',') | |
1680 | *(code_pos-1) = ';'; | |
1681 | else { | |
1682 | code_pos -= 3; /* no variable, erase "int" */ | |
1683 | code_pos += sprintf(code_pos, "UNUSED(trans);"); | |
1684 | } | |
1685 | ||
1686 | /* Include UNUSED statements for two parameters */ | |
1687 | code_pos += sprintf(code_pos, "\n UNUSED(color);\n"); | |
1688 | if (!have_constraint || !have_action) | |
1689 | code_pos += sprintf(code_pos, " UNUSED(action);\n"); | |
1690 | ||
1691 | /* Generate coordinate transformations. */ | |
1692 | for (i = 0; i < sizeof(VALID_CONSTRAINT_LABELS); i++) { | |
1693 | int c = (int) VALID_CONSTRAINT_LABELS[i]; | |
1694 | ||
1695 | if (label_coords[c][0] != -1) { | |
1696 | int x; | |
1697 | int y; | |
1698 | ||
1699 | TRANSFORM2(label_coords[c][0] - ci - movei, | |
1700 | label_coords[c][1] - cj - movej, &x, &y, | |
1701 | labels_transformation); | |
1702 | code_pos += sprintf(code_pos, | |
1703 | "\n %c = AFFINE_TRANSFORM(%d, trans, move);", | |
1704 | c, OFFSET(x, y)); | |
1705 | no_labels = 0; | |
1706 | } | |
1707 | } | |
1708 | ||
1709 | /* move might be unused. Add an UNUSED statement to avoid warnings. */ | |
1710 | if (no_labels) | |
1711 | code_pos += sprintf(code_pos, "\n UNUSED(move);"); | |
1712 | ||
1713 | code_pos += sprintf(code_pos, "\n\n"); | |
1714 | if (have_constraint && have_action) | |
1715 | code_pos += sprintf(code_pos, " if (!action)\n "); | |
1716 | if (have_constraint) { | |
1717 | code_pos += sprintf(code_pos, " return "); | |
1718 | parse_constraint_or_action(constraint, &cost); | |
1719 | pattern[patno].constraint_cost = cost; | |
1720 | code_pos += sprintf(code_pos, ";\n"); | |
1721 | } | |
1722 | if (have_action) { | |
1723 | code_pos += sprintf(code_pos, " "); | |
1724 | parse_constraint_or_action(action, &cost); | |
1725 | code_pos += sprintf(code_pos, ";\n"); | |
1726 | code_pos += sprintf(code_pos, "\n return 0;\n"); | |
1727 | } | |
1728 | code_pos += sprintf(code_pos, "}\n\n"); | |
1729 | ||
1730 | /* Check that we have not overrun our buffer. That would be really bad. */ | |
1731 | assert(code_pos <= autohelper_code + sizeof(autohelper_code)); | |
1732 | } | |
1733 | ||
1734 | ||
1735 | ||
1736 | /* ================================================================ */ | |
1737 | /* stuff to write the elements of a pattern */ | |
1738 | /* ================================================================ */ | |
1739 | ||
1740 | /* callback function used to sort the elements in a pattern. | |
1741 | * We want to sort the patterns by attribute. | |
1742 | * | |
1743 | * RANK 01234567 | |
1744 | * ATT 57126340 | |
1745 | * ,!XOaxo. | |
1746 | * | |
1747 | * so that cheaper / more specific tests are done early on | |
1748 | * For connections mode, the inhibition points (7) | |
1749 | * must be first. | |
1750 | */ | |
1751 | ||
1752 | static int | |
1753 | compare_elements(const void *a, const void *b) | |
1754 | { | |
1755 | /* score for each attribute */ | |
1756 | static int order[] = {7, 2, 3, 5, 6, 0, 4, 1}; | |
1757 | ||
1758 | return order[((const struct patval_b *)a)->att] | |
1759 | - order[((const struct patval_b *)b)->att]; | |
1760 | } | |
1761 | ||
1762 | struct element_node { | |
1763 | struct patval_b e; | |
1764 | struct element_node *next; | |
1765 | }; | |
1766 | ||
1767 | /* flush out the pattern stored in elements[]. Don't forget | |
1768 | * that elements[].{x,y} and min/max{i,j} are still relative | |
1769 | * to the top-left corner of the original ascii pattern, and | |
1770 | * not relative to the anchor stone ci,cj | |
1771 | */ | |
1772 | ||
1773 | static void | |
1774 | write_elements(FILE *outfile) | |
1775 | { | |
1776 | int node; | |
1777 | int used_nodes = 0; | |
1778 | ||
1779 | assert(ci != -1 && cj != -1); | |
1780 | assert(database_type == DB_DFA || transformation_hint == 0); | |
1781 | ||
1782 | /* sort the elements so that least-likely elements are tested first. */ | |
1783 | gg_sort(elements, el, sizeof(struct patval_b), compare_elements); | |
1784 | ||
1785 | fprintf(outfile, "static struct patval %s%d[] = {", prefix, patno); | |
1786 | ||
1787 | for (node = 0; node < el; node++) { | |
1788 | int x = elements[node].x; | |
1789 | int y = elements[node].y; | |
1790 | int att = elements[node].att; | |
1791 | int dx; | |
1792 | int dy; | |
1793 | ||
1794 | assert(x >= mini && y >= minj); | |
1795 | if (!(x <= maxi && y <= maxj)) { | |
1796 | fprintf(stderr, | |
1797 | "%s(%d) : error : Maximum number of elements exceeded in %s.\n", | |
1798 | current_file, current_line_number, prefix); | |
1799 | fatal_errors++; | |
1800 | } | |
1801 | ||
1802 | /* Check if this element is not needed for goal checking and by | |
1803 | * callback function. Also, check that pattern class doesn't | |
1804 | * require dragon status checking on it. | |
1805 | */ | |
1806 | if ((fixed_anchor || nongoal[att]) && callback_unneeded[att] | |
1807 | && (((pattern[patno].class & (CLASS_X | CLASS_x)) == 0) | |
1808 | || (att != ATT_X && att != ATT_x)) | |
1809 | && (((pattern[patno].class & (CLASS_O | CLASS_o)) == 0) | |
1810 | || (att != ATT_O && att != ATT_o))) { | |
1811 | /* Now check that pattern matcher won't need the element for | |
1812 | * matching itself. | |
1813 | */ | |
1814 | ||
1815 | #if GRID_OPT == 1 | |
1816 | /* If we do grid optimization, we can avoid matching 9 pattern elements | |
1817 | * around its anchor (the 9 elements are the intersection of 16 grid | |
1818 | * elements for all possible transformations). | |
1819 | */ | |
1820 | if ((database_type == DB_GENERAL || database_type == DB_CONNECTIONS) | |
1821 | && ci-1 <= x && x <= ci+1 && cj-1 <= y && y <= cj+1) | |
1822 | continue; | |
1823 | #endif /* GRID_OPT == 1 */ | |
1824 | ||
1825 | /* DFA pattern matcher doesn't itself need these elements at all. But | |
1826 | * they might be needed for goal checking or by callback function, so | |
1827 | * we check it before discarding an element. | |
1828 | */ | |
1829 | if (database_type == DB_DFA) | |
1830 | continue; | |
1831 | } /* If the element is discardable. */ | |
1832 | ||
1833 | if (used_nodes) | |
1834 | fprintf(outfile, ","); | |
1835 | fprintf(outfile, used_nodes % 4 ? "\t" : "\n "); | |
1836 | used_nodes++; | |
1837 | ||
1838 | TRANSFORM2(x - ci, y - cj, &dx, &dy, transformation_hint); | |
1839 | fprintf(outfile, "{%d,%d}", OFFSET(dx, dy), att); | |
1840 | } | |
1841 | ||
1842 | /* This may happen if we have discarded all | |
1843 | * the elements as unneeded by the matcher. | |
1844 | */ | |
1845 | if (!used_nodes) | |
1846 | fprintf(outfile, "{0,-1}}; /* Dummy element, not used. */\n\n"); | |
1847 | else | |
1848 | fprintf(outfile, "\n};\n\n"); | |
1849 | ||
1850 | pattern[patno].patlen = used_nodes; | |
1851 | } | |
1852 | ||
1853 | ||
1854 | /* ================================================================ */ | |
1855 | /* Corner database creation stuff */ | |
1856 | /* ================================================================ */ | |
1857 | ||
1858 | #define CORNER_DB_SIZE(patterns, variations)\ | |
1859 | ((int) ((patterns * sizeof(struct corner_pattern)\ | |
1860 | + variations * sizeof(struct corner_variation)) / 1024)) | |
1861 | ||
1862 | static struct corner_variation_b corner_root; | |
1863 | static int second_corner_offset[MAXPATNO]; | |
1864 | ||
1865 | static int total_variations = 0; | |
1866 | static int variations_written = 0; | |
1867 | ||
1868 | static int corner_max_width = 0; | |
1869 | static int corner_max_height = 0; | |
1870 | ||
1871 | /* This structure is used by corner_add_pattern() */ | |
1872 | struct corner_element { | |
1873 | int x; | |
1874 | int y; | |
1875 | int color; | |
1876 | }; | |
1877 | ||
1878 | ||
1879 | /* Initialize corner variation tree. */ | |
1880 | static void | |
1881 | corner_init(void) | |
1882 | { | |
1883 | corner_root.num_variations = 0; | |
1884 | corner_root.child = NULL; | |
1885 | } | |
1886 | ||
1887 | ||
1888 | /* corner_best_element() chooses the best element from supplied. The best | |
1889 | * element is always selected from those which don't have other elements | |
1890 | * closer to the corner | |
1891 | * | |
1892 | * +------ E.g. elements A and B are candidates to become the best | |
1893 | * |...... element, while C and D are not (A is closer to the corner | |
1894 | * |..A... than both C and D). Note that A and B are at "incomparable" | |
1895 | * |..C.D. distances from the corner, since their coordinates are in | |
1896 | * |.B.... opposite relations. | |
1897 | * |...... | |
1898 | * | |
1899 | * If there are existing variations among candidates, all other candidates | |
1900 | * are automatically rejected. Once we have a set of candidates, we select | |
1901 | * the best candidate as the one which has the best parameters (in order | |
1902 | * of decreasing importance): | |
1903 | * 1) maximal "corner area" (see function code); | |
1904 | * 2) minimal distance to the corner bisector; | |
1905 | * 3) those which x coordinate is smaller than y one | |
1906 | * | |
1907 | * The purpose of this function is to reduce size of variation tree (by | |
1908 | * selecting similar variations) and allow rejecting variations as | |
1909 | * quickly as possible (based on num_stones field value). The latter | |
1910 | * can still be improved if a need arises. | |
1911 | */ | |
1912 | static int | |
1913 | corner_best_element(struct corner_element *el, int n, | |
1914 | struct corner_variation_b *variations, int color) | |
1915 | { | |
1916 | int k; | |
1917 | int i; | |
1918 | int best = 0; | |
1919 | int best_value = 0; | |
1920 | ||
1921 | int candidate[MAX_BOARD * MAX_BOARD]; | |
1922 | int candidates = 0; | |
1923 | int existing_variation[MAX_BOARD * MAX_BOARD]; | |
1924 | int have_existing_variation = 0; | |
1925 | ||
1926 | /* Find candidates and mark those which correspond to existing variations. */ | |
1927 | for (k = 0; k < n; k++) { | |
1928 | for (i = 0; i < n; i++) { | |
1929 | if (i == k) | |
1930 | continue; | |
1931 | ||
1932 | if (el[k].x >= el[i].x && el[k].y >= el[i].y) | |
1933 | break; | |
1934 | } | |
1935 | ||
1936 | if (i == n) { | |
1937 | struct corner_variation_b *v; | |
1938 | int move_offset = OFFSET(el[k].x, el[k].y); | |
1939 | int xor_att = (el[k].color == color ? ATT_O ^ ATT_O : ATT_O ^ ATT_X); | |
1940 | ||
1941 | candidate[candidates] = k; | |
1942 | existing_variation[candidates] = 0; | |
1943 | ||
1944 | for (v = variations; v != NULL; v = v->next) { | |
1945 | if (v->move_offset == move_offset | |
1946 | && (v->xor_att == xor_att || color == 0)) { | |
1947 | existing_variation[candidates] = 1; | |
1948 | have_existing_variation = 1; | |
1949 | break; | |
1950 | } | |
1951 | } | |
1952 | ||
1953 | candidates++; | |
1954 | } | |
1955 | } | |
1956 | ||
1957 | /* Select the best move. */ | |
1958 | for (k = 0; k < candidates; k++) { | |
1959 | int m = candidate[k]; | |
1960 | int value = 2 * MAX_BOARD * (el[m].x + 1) * (el[m].y + 1) | |
1961 | - 2 * gg_abs(el[m].x - el[m].y) + (el[m].x < el[m].y ? 1 : 0); | |
1962 | ||
1963 | if (existing_variation[k] == have_existing_variation | |
1964 | && value > best_value) { | |
1965 | best = k; | |
1966 | best_value = value; | |
1967 | } | |
1968 | } | |
1969 | ||
1970 | return candidate[best]; | |
1971 | } | |
1972 | ||
1973 | ||
1974 | /* Dynamically allocates a new variation structure. */ | |
1975 | static struct corner_variation_b * | |
1976 | corner_variation_new(int move_offset, signed char xor_att, | |
1977 | unsigned char num_stones) | |
1978 | { | |
1979 | struct corner_variation_b *variation; | |
1980 | ||
1981 | variation = malloc(sizeof(*variation)); | |
1982 | ||
1983 | variation->move_offset = move_offset; | |
1984 | variation->xor_att = xor_att; | |
1985 | variation->num_stones = num_stones; | |
1986 | variation->num_variations = 0; | |
1987 | variation->next = NULL; | |
1988 | variation->child = NULL; | |
1989 | variation->child_num = -1; | |
1990 | variation->pattern_num = -1; | |
1991 | ||
1992 | total_variations++; | |
1993 | ||
1994 | return variation; | |
1995 | } | |
1996 | ||
1997 | ||
1998 | /* Follow a variation. If such a variation already exists, returns | |
1999 | * a pointer to it. Otherwise, creates and initializes a new one. | |
2000 | */ | |
2001 | static struct corner_variation_b * | |
2002 | corner_follow_variation(struct corner_variation_b *variation, | |
2003 | int offset, int color, unsigned char num_stones) | |
2004 | { | |
2005 | signed char xor_att = color ? ATT_O ^ ATT_O : ATT_O ^ ATT_X; | |
2006 | struct corner_variation_b *subvariation = variation->child; | |
2007 | struct corner_variation_b **link = &(variation->child); | |
2008 | ||
2009 | while (subvariation) { | |
2010 | if (subvariation->move_offset == offset | |
2011 | && subvariation->xor_att == xor_att) { | |
2012 | assert(subvariation->num_stones == num_stones); | |
2013 | return subvariation; | |
2014 | } | |
2015 | ||
2016 | link = &(subvariation->next); | |
2017 | subvariation = subvariation->next; | |
2018 | } | |
2019 | ||
2020 | variation->num_variations++; | |
2021 | *link = corner_variation_new(offset, xor_att, num_stones); | |
2022 | ||
2023 | return *link; | |
2024 | } | |
2025 | ||
2026 | ||
2027 | /* Adds a pattern to corner database. */ | |
2028 | static void | |
2029 | corner_add_pattern(void) | |
2030 | { | |
2031 | int k; | |
2032 | struct corner_element stone[MAX_BOARD * MAX_BOARD]; | |
2033 | int stones = 0; | |
2034 | int trans; | |
2035 | int corner_x = 0; | |
2036 | int corner_y = 0; | |
2037 | int color = 0; | |
2038 | int move_pos; | |
2039 | int move_x; | |
2040 | int move_y; | |
2041 | unsigned char num_stones; | |
2042 | struct corner_variation_b *variation = &corner_root; | |
2043 | ||
2044 | /* Check if we have a corner pattern and select appropriate transformation. */ | |
2045 | switch (where) { | |
2046 | case SOUTH_EDGE | WEST_EDGE: trans = 5; corner_x = maxi; break; | |
2047 | case WEST_EDGE | NORTH_EDGE: trans = 0; break; | |
2048 | case NORTH_EDGE | EAST_EDGE: trans = 7; corner_y = maxj; break; | |
2049 | case EAST_EDGE | SOUTH_EDGE: | |
2050 | trans = 2; corner_x = maxi; corner_y = maxj; break; | |
2051 | ||
2052 | default: | |
2053 | fprintf(stderr, "%s(%d) : error : Illegal edge constraint in pattern %s\n", | |
2054 | current_file, current_line_number, pattern_names[patno]); | |
2055 | return; | |
2056 | } | |
2057 | ||
2058 | move_pos = AFFINE_TRANSFORM(pattern[patno].move_offset | |
2059 | - OFFSET_DELTA(corner_x, corner_y), trans, POS(0, 0)); | |
2060 | move_x = I(move_pos); | |
2061 | move_y = J(move_pos); | |
2062 | ||
2063 | /* We need to transform labels as well. */ | |
2064 | labels_transformation = trans; | |
2065 | ||
2066 | /* Find all pattern elements. */ | |
2067 | for (k = 0; k < el; k++) { | |
2068 | if (elements[k].att == ATT_X || elements[k].att == ATT_O) { | |
2069 | TRANSFORM2(elements[k].x, elements[k].y, | |
2070 | &stone[stones].x, &stone[stones].y, trans); | |
2071 | stone[stones].x += corner_x; | |
2072 | stone[stones].y += corner_y; | |
2073 | stone[stones].color = elements[k].att; | |
2074 | stones++; | |
2075 | } | |
2076 | else if (elements[k].att != ATT_dot) { | |
2077 | fprintf(stderr, "%s(%d) : error : Illegal element in pattern %s\n", | |
2078 | current_file, current_line_number, pattern_names[patno]); | |
2079 | return; | |
2080 | } | |
2081 | } | |
2082 | ||
2083 | /* Follow variations. */ | |
2084 | for (k = 0; k < stones; k++) { | |
2085 | int i; | |
2086 | int best; | |
2087 | struct corner_element stone_t; | |
2088 | ||
2089 | if (k > 0) { | |
2090 | best = k + corner_best_element(stone + k, stones - k, variation->child, | |
2091 | color); | |
2092 | stone_t = stone[k]; | |
2093 | stone[k] = stone[best]; | |
2094 | stone[best] = stone_t; | |
2095 | } | |
2096 | else { | |
2097 | best = corner_best_element(stone, stones, variation->child, color); | |
2098 | stone_t = stone[0]; | |
2099 | stone[0] = stone[best]; | |
2100 | stone[best] = stone_t; | |
2101 | color = stone[0].color; | |
2102 | ||
2103 | if (stone[0].x > stone[0].y) { | |
2104 | /* To reduce number of variations, swap coordinates of all elements | |
2105 | * unless there is one with mirrored coordinates already. | |
2106 | */ | |
2107 | int t; | |
2108 | ||
2109 | for (i = 1; i < k; i++) { | |
2110 | if (stone[i].x == stone[0].y && stone[i].y == stone[0].x) | |
2111 | break; | |
2112 | } | |
2113 | ||
2114 | if (i == k) { | |
2115 | t = maxi; | |
2116 | maxi = maxj; | |
2117 | maxj = t; | |
2118 | ||
2119 | t = move_x; | |
2120 | move_x = move_y; | |
2121 | move_y = t; | |
2122 | ||
2123 | for (i = 0; i < stones; i++) { | |
2124 | t = stone[i].x; | |
2125 | stone[i].x = stone[i].y; | |
2126 | stone[i].y = t; | |
2127 | } | |
2128 | } | |
2129 | } | |
2130 | } | |
2131 | ||
2132 | num_stones = 1; | |
2133 | for (i = 0; i < k; i++) { | |
2134 | if (stone[i].x <= stone[k].x && stone[i].y <= stone[k].y) | |
2135 | num_stones++; | |
2136 | } | |
2137 | ||
2138 | variation = corner_follow_variation(variation, | |
2139 | OFFSET(stone[k].x, stone[k].y), stone[k].color == color, | |
2140 | num_stones); | |
2141 | } | |
2142 | ||
2143 | /* Finally, follow the pattern move as a variation. */ | |
2144 | num_stones = 1; | |
2145 | for (k = 0; k < stones; k++) { | |
2146 | if (stone[k].x <= move_x && stone[k].y <= move_y) | |
2147 | num_stones++; | |
2148 | } | |
2149 | ||
2150 | variation = corner_follow_variation(variation, OFFSET(move_x, move_y), | |
2151 | ATT_O == color, num_stones); | |
2152 | if (variation->pattern_num == -1) { | |
2153 | variation->pattern_num = patno; | |
2154 | second_corner_offset[patno] = OFFSET(maxi, maxj); | |
2155 | if (maxi > corner_max_height) | |
2156 | corner_max_height = maxi; | |
2157 | if (maxj > corner_max_width) | |
2158 | corner_max_width = maxj; | |
2159 | } | |
2160 | else { | |
2161 | fprintf(stderr, "%s(%d) : warning : duplicated patterns encountered (%s and %s)\n", | |
2162 | current_file, current_line_number, | |
2163 | pattern_names[variation->pattern_num], pattern_names[patno]); | |
2164 | discard_pattern = 1; | |
2165 | } | |
2166 | } | |
2167 | ||
2168 | ||
2169 | /* Enumerates all variations so that we know addresses of subvariations | |
2170 | * when it is time to write them into a .c file. | |
2171 | */ | |
2172 | static int | |
2173 | corner_pack_variations(struct corner_variation_b *variation, int counter) | |
2174 | { | |
2175 | counter++; | |
2176 | if (variation->next) | |
2177 | counter = corner_pack_variations(variation->next, counter); | |
2178 | if (variation->child) { | |
2179 | variation->child_num = counter; | |
2180 | counter = corner_pack_variations(variation->child, counter); | |
2181 | } | |
2182 | ||
2183 | return counter; | |
2184 | } | |
2185 | ||
2186 | ||
2187 | /* Write variations recursively into an array. */ | |
2188 | static void | |
2189 | corner_write_variations(struct corner_variation_b *variation, FILE *outfile) | |
2190 | { | |
2191 | fprintf(outfile, " {%d,%d,%d,%d,", variation->move_offset, | |
2192 | variation->xor_att, variation->num_stones, | |
2193 | variation->num_variations); | |
2194 | if (variation->child) | |
2195 | fprintf(outfile, "&%s_variations[%d],", prefix, variation->child_num); | |
2196 | else | |
2197 | fprintf(outfile, "NULL,"); | |
2198 | if (variation->pattern_num != -1) | |
2199 | fprintf(outfile, "&%s[%d]", prefix, variation->pattern_num); | |
2200 | else | |
2201 | fprintf(outfile, "NULL"); | |
2202 | ||
2203 | variations_written++; | |
2204 | if (variations_written != total_variations) | |
2205 | fprintf(outfile, "},\n"); | |
2206 | else | |
2207 | fprintf(outfile, "}\n};\n\n\n"); | |
2208 | ||
2209 | if (variation->next) | |
2210 | corner_write_variations(variation->next, outfile); | |
2211 | if (variation->child) | |
2212 | corner_write_variations(variation->child, outfile); | |
2213 | } | |
2214 | ||
2215 | ||
2216 | ||
2217 | static void | |
2218 | write_attributes(FILE *outfile) | |
2219 | { | |
2220 | int k; | |
2221 | ||
2222 | fprintf(outfile, "static struct pattern_attribute attributes[] = {\n"); | |
2223 | fprintf(outfile, "#ifdef HAVE_TRANSPARENT_UNIONS\n"); | |
2224 | ||
2225 | for (k = 0; k < num_attributes; k++) { | |
2226 | fprintf(outfile, " {%s,", attribute_name[attributes[k].type]); | |
2227 | if (attributes[k].type >= FIRST_OFFSET_ATTRIBUTE) | |
2228 | fprintf(outfile, "{.offset=%d}}", attributes[k].offset); | |
2229 | else | |
2230 | fprintf(outfile, "{.value=%f}}", attributes[k].value); | |
2231 | ||
2232 | if (k != num_attributes - 1) | |
2233 | fprintf(outfile, ",\n"); | |
2234 | } | |
2235 | ||
2236 | fprintf(outfile, "\n#else\n"); | |
2237 | ||
2238 | for (k = 0; k < num_attributes; k++) { | |
2239 | fprintf(outfile, " {%s,", attribute_name[attributes[k].type]); | |
2240 | if (attributes[k].type >= FIRST_OFFSET_ATTRIBUTE) | |
2241 | fprintf(outfile, "0.0,%d}", attributes[k].offset); | |
2242 | else | |
2243 | fprintf(outfile, "%f,0}", attributes[k].value); | |
2244 | ||
2245 | if (k != num_attributes - 1) | |
2246 | fprintf(outfile, ",\n"); | |
2247 | } | |
2248 | ||
2249 | fprintf(outfile, "\n#endif\n};\n\n"); | |
2250 | } | |
2251 | ||
2252 | ||
2253 | /* Sort and write out the patterns. */ | |
2254 | static void | |
2255 | write_patterns(FILE *outfile) | |
2256 | { | |
2257 | int j; | |
2258 | ||
2259 | /* Write out the patterns. */ | |
2260 | if (database_type == DB_CORNER) | |
2261 | fprintf(outfile, "static struct corner_pattern %s[] = {\n", prefix); | |
2262 | else | |
2263 | fprintf(outfile, "static struct pattern %s[] = {\n", prefix); | |
2264 | ||
2265 | for (j = 0; j < patno; ++j) { | |
2266 | struct pattern *p = pattern + j; | |
2267 | ||
2268 | if (database_type == DB_CORNER) { | |
2269 | fprintf(outfile, " {%d,%d,0x%x,\"%s\",", | |
2270 | second_corner_offset[j], (p->trfno == 4), | |
2271 | p->class, pattern_names[j]); | |
2272 | ||
2273 | if (attributes_needed) { | |
2274 | fprintf(outfile, "attributes+%d,", | |
2275 | (int) (p->attributes ? p->attributes - attributes : 0)); | |
2276 | } | |
2277 | else | |
2278 | fprintf(outfile, "NULL,"); | |
2279 | ||
2280 | fprintf(outfile, "%d,", p->autohelper_flag); | |
2281 | ||
2282 | if (p->autohelper) | |
2283 | fprintf(outfile, "autohelper%s%d}", prefix, j); | |
2284 | else | |
2285 | fprintf(outfile, "NULL}"); | |
2286 | ||
2287 | if (j != patno - 1) | |
2288 | fprintf(outfile, ",\n"); | |
2289 | else | |
2290 | fprintf(outfile, "\n};\n\n\n"); | |
2291 | ||
2292 | continue; | |
2293 | } | |
2294 | ||
2295 | /* p->min{i,j} and p->max{i,j} are the maximum extent of the elements, | |
2296 | * including any rows of '?' which do not feature in the elements list. | |
2297 | * ie they are the positions of the topleft and bottomright corners of | |
2298 | * the pattern, relative to the pattern origin. These just transform same | |
2299 | * as the elements. | |
2300 | */ | |
2301 | fprintf(outfile, " {%s%d,%d,%d, \"%s\",%d,%d,%d,%d,%d,%d,0x%x,%d", | |
2302 | prefix, j, | |
2303 | p->patlen, | |
2304 | p->trfno, | |
2305 | pattern_names[j], | |
2306 | p->mini, p->minj, | |
2307 | p->maxi, p->maxj, | |
2308 | p->maxi - p->mini, /* height */ | |
2309 | p->maxj - p->minj, /* width */ | |
2310 | p->edge_constraints, | |
2311 | p->move_offset); | |
2312 | ||
2313 | ||
2314 | #if GRID_OPT | |
2315 | fprintf(outfile, ",\n {"); | |
2316 | { | |
2317 | int ll; | |
2318 | ||
2319 | for (ll = 0; ll < 8; ++ll) | |
2320 | fprintf(outfile, " 0x%08x%s", p->and_mask[ll], ll < 7 ? "," : ""); | |
2321 | fprintf(outfile, "},\n {"); | |
2322 | for (ll = 0; ll < 8; ++ll) | |
2323 | fprintf(outfile, " 0x%08x%s", p->val_mask[ll], ll < 7 ? "," : ""); | |
2324 | } | |
2325 | fprintf(outfile, "}\n "); | |
2326 | #endif | |
2327 | ||
2328 | fprintf(outfile, ", 0x%x,%f,", p->class, p->value); | |
2329 | ||
2330 | if (attributes_needed) { | |
2331 | fprintf(outfile, "attributes+%d,", | |
2332 | (int) (p->attributes ? p->attributes - attributes : 0)); | |
2333 | } | |
2334 | else | |
2335 | fprintf(outfile, "NULL,"); | |
2336 | ||
2337 | fprintf(outfile, "%d,%s,", p->autohelper_flag, helper_fn_names[j]); | |
2338 | ||
2339 | if (p->autohelper) | |
2340 | fprintf(outfile, "autohelper%s%d", prefix, j); | |
2341 | else | |
2342 | fprintf(outfile, "NULL"); | |
2343 | fprintf(outfile, ",%d", p->anchored_at_X); | |
2344 | fprintf(outfile, ",%f", p->constraint_cost); | |
2345 | #if PROFILE_PATTERNS | |
2346 | fprintf(outfile, ",0,0"); | |
2347 | fprintf(outfile, ",0"); | |
2348 | #endif | |
2349 | ||
2350 | fprintf(outfile, "},\n"); | |
2351 | } | |
2352 | ||
2353 | if (database_type == DB_CORNER) | |
2354 | return; | |
2355 | ||
2356 | /* Add a final empty entry. */ | |
2357 | fprintf(outfile, " {NULL, 0,0,NULL,0,0,0,0,0,0,0,0"); | |
2358 | #if GRID_OPT | |
2359 | fprintf(outfile, ",{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}"); | |
2360 | #endif | |
2361 | fprintf(outfile, ",0,0.0,NULL,0,NULL,NULL,0,0.0"); | |
2362 | #if PROFILE_PATTERNS | |
2363 | fprintf(outfile, ",0,0,0"); | |
2364 | #endif | |
2365 | fprintf(outfile, "}\n};\n"); | |
2366 | } | |
2367 | ||
2368 | /* Write out the pattern db */ | |
2369 | static void | |
2370 | write_pattern_db(FILE *outfile) | |
2371 | { | |
2372 | if (database_type == DB_CORNER) { | |
2373 | fprintf(outfile, "struct corner_db %s_db = {\n", prefix); | |
2374 | fprintf(outfile, " %d,\n", corner_max_width + 1); | |
2375 | fprintf(outfile, " %d,\n", corner_max_height + 1); | |
2376 | fprintf(outfile, " %d,\n", corner_root.num_variations); | |
2377 | fprintf(outfile, " %s_variations\n", prefix); | |
2378 | fprintf(outfile, "};\n"); | |
2379 | ||
2380 | return; | |
2381 | } | |
2382 | ||
2383 | /* Write out the pattern database. */ | |
2384 | fprintf(outfile, "\n"); | |
2385 | fprintf(outfile, "struct pattern_db %s_db = {\n", prefix); | |
2386 | fprintf(outfile, " -1,\n"); /* fixed_for_size */ | |
2387 | fprintf(outfile, " %d,\n", fixed_anchor); | |
2388 | fprintf(outfile, " %s\n", prefix); | |
2389 | if (database_type == DB_DFA) | |
2390 | fprintf(outfile, " ,& dfa_%s\n", prefix); /* pointer to the wired dfa */ | |
2391 | else | |
2392 | fprintf(outfile, " , NULL\n"); /* pointer to a possible dfa */ | |
2393 | ||
2394 | fprintf(outfile, "};\n"); | |
2395 | } | |
2396 | ||
2397 | ||
2398 | int | |
2399 | main(int argc, char *argv[]) | |
2400 | { | |
2401 | static char stdin_name[] = "<stdin>"; | |
2402 | int input_files = 0; | |
2403 | int ifc; | |
2404 | char *input_file_names[MAX_INPUT_FILE_NAMES]; | |
2405 | char *output_file_name = NULL; | |
2406 | char *transformations_file_name = NULL; | |
2407 | FILE *input_FILE = stdin; | |
2408 | FILE *output_FILE = stdout; | |
2409 | FILE *transformations_FILE = NULL; | |
2410 | int state = 0; | |
2411 | char *save_code_pos = autohelper_code; | |
2412 | int iterations = 0; | |
2413 | ||
2414 | transformation_init(); | |
2415 | ||
2416 | { | |
2417 | int i; | |
2418 | int multiple_anchor_options = 0; | |
2419 | ||
2420 | /* Parse command-line options */ | |
2421 | while ((i = gg_getopt(argc, argv, "i:o:t:vV:pcfCDd:A:OXbma")) != EOF) { | |
2422 | switch (i) { | |
2423 | case 'i': | |
2424 | if (input_files == MAX_INPUT_FILE_NAMES) { | |
2425 | fprintf(stderr, "Error : Too many input files (maximum %d supported)\n", | |
2426 | MAX_INPUT_FILE_NAMES); | |
2427 | return EXIT_FAILURE; | |
2428 | } | |
2429 | input_file_names[input_files++] = gg_optarg; | |
2430 | break; | |
2431 | ||
2432 | case 'o': output_file_name = gg_optarg; break; | |
2433 | case 't': transformations_file_name = gg_optarg; break; | |
2434 | case 'v': verbose = 1; break; | |
2435 | case 'V': dfa_verbose = strtol(gg_optarg, NULL, 10); break; | |
2436 | ||
2437 | case 'p': | |
2438 | case 'c': | |
2439 | case 'f': | |
2440 | case 'C': | |
2441 | case 'D': | |
2442 | case 'd': | |
2443 | if (database_type) { | |
2444 | fprintf(stderr, "Error : More than one database type specified (-%c and -%c)\n", | |
2445 | database_type, i); | |
2446 | return 1; | |
2447 | } | |
2448 | database_type = i; | |
2449 | if (i == 'd') { | |
2450 | iterations = strtol(gg_optarg, NULL, 10); | |
2451 | if (iterations < 0) { | |
2452 | fprintf(stderr, "Error : Expected a non-negative number of iterations\n"); | |
2453 | return 1; | |
2454 | } | |
2455 | } | |
2456 | break; | |
2457 | ||
2458 | case 'O': | |
2459 | if (anchor) | |
2460 | multiple_anchor_options = 1; | |
2461 | anchor = ANCHOR_O; | |
2462 | break; | |
2463 | case 'X': | |
2464 | if (anchor) | |
2465 | multiple_anchor_options = 1; | |
2466 | anchor = ANCHOR_X; | |
2467 | break; | |
2468 | case 'b': | |
2469 | if (anchor) | |
2470 | multiple_anchor_options = 1; | |
2471 | anchor = ANCHOR_BOTH; | |
2472 | break; | |
2473 | ||
2474 | case 'm': | |
2475 | choose_best_anchor = 1; | |
2476 | if (fixed_anchor) | |
2477 | fprintf(stderr, "Warning : -m and -a are mutually exclusive.\n"); | |
2478 | break; | |
2479 | case 'a': | |
2480 | fixed_anchor = 1; | |
2481 | if (choose_best_anchor) | |
2482 | fprintf(stderr, "Warning : -m and -a are mutually exclusive.\n"); | |
2483 | break; | |
2484 | ||
2485 | default: | |
2486 | fprintf(stderr, "\b ; ignored\n"); | |
2487 | } | |
2488 | } | |
2489 | ||
2490 | if (!database_type) | |
2491 | database_type = DB_GENERAL; | |
2492 | if (!anchor) | |
2493 | anchor = ANCHOR_O; | |
2494 | ||
2495 | if (!input_files) | |
2496 | input_file_names[input_files++] = stdin_name; | |
2497 | if (output_file_name && database_type != OPTIMIZE_DFA) { | |
2498 | output_FILE = fopen(output_file_name, "wb"); | |
2499 | if (output_FILE == NULL) { | |
2500 | fprintf(stderr, "Error : Cannot write to file %s\n", output_file_name); | |
2501 | return 1; | |
2502 | } | |
2503 | } | |
2504 | if (transformations_file_name | |
2505 | && (database_type == DB_DFA || database_type == OPTIMIZE_DFA)) { | |
2506 | transformations_FILE = fopen(transformations_file_name, "r"); | |
2507 | if (transformations_FILE) { | |
2508 | parse_transformations_file(transformations_FILE); | |
2509 | fclose(transformations_FILE); | |
2510 | } | |
2511 | else if (database_type == DB_DFA) { | |
2512 | fprintf(stderr, "Error : Cannot read file %s\n", | |
2513 | transformations_file_name); | |
2514 | return 1; | |
2515 | } | |
2516 | } | |
2517 | ||
2518 | if (multiple_anchor_options) | |
2519 | fprintf(stderr, "Warning : Multiple anchor options encountered. The last took precedence\n"); | |
2520 | } | |
2521 | ||
2522 | if (gg_optind >= argc) { | |
2523 | fputs(USAGE, stderr); | |
2524 | exit(EXIT_FAILURE); | |
2525 | } | |
2526 | ||
2527 | prefix = argv[gg_optind]; | |
2528 | ||
2529 | if (database_type == DB_DFA) { | |
2530 | dfa_init(); | |
2531 | new_dfa(&dfa, "mkpat's dfa"); | |
2532 | } | |
2533 | else if (database_type == DB_CORNER) | |
2534 | corner_init(); | |
2535 | ||
2536 | if (database_type == OPTIMIZE_DFA) { | |
2537 | if (transformations_file_name == NULL) { | |
2538 | fprintf(stderr, "error : transformation file required (use -t option)\n"); | |
2539 | return 1; | |
2540 | } | |
2541 | dfa_patterns_reset(&dfa_pats); | |
2542 | dfa_init(); | |
2543 | } | |
2544 | else | |
2545 | fprintf(output_FILE, PREAMBLE); | |
2546 | ||
2547 | /* Initialize pattern number and buffer for automatically generated | |
2548 | * helper code. | |
2549 | */ | |
2550 | patno = -1; | |
2551 | autohelper_code[0] = 0; | |
2552 | code_pos = autohelper_code; | |
2553 | ||
2554 | num_attributes = 1; | |
2555 | attributes[0].type = LAST_ATTRIBUTE; | |
2556 | ||
2557 | /* Parse the input file, output pattern elements as as we find them, | |
2558 | * and store pattern entries for later output. | |
2559 | * | |
2560 | * We do this parsing too with the help of a state machine. | |
2561 | * state | |
2562 | * 0 Waiting for a Pattern line. | |
2563 | * 1 Pattern line found, waiting for a position diagram. | |
2564 | * 2 Reading position diagram. | |
2565 | * 3 Waiting for entry line (":" line). | |
2566 | * 4 Waiting for optional constraint diagram. | |
2567 | * 5 Reading constraint diagram. | |
2568 | * 6 Waiting for constraint line (";" line). | |
2569 | * 7 Reading a constraint | |
2570 | * 8 Reading an action | |
2571 | * | |
2572 | * FIXME: This state machine should be possible to simplify. | |
2573 | * | |
2574 | */ | |
2575 | ||
2576 | for (ifc = 0; ifc < input_files && !fatal_errors; ifc++) { | |
2577 | char line[MAXLINE]; /* current line from file */ | |
2578 | ||
2579 | if (input_file_names[ifc] != stdin_name) { | |
2580 | input_FILE = fopen(input_file_names[ifc], "r"); | |
2581 | if (input_FILE == NULL) { | |
2582 | fprintf(stderr, "Error: Cannot open file %s\n", input_file_names[ifc]); | |
2583 | return 1; | |
2584 | } | |
2585 | } | |
2586 | ||
2587 | current_file = input_file_names[ifc]; | |
2588 | current_line_number = 0; | |
2589 | ||
2590 | while (fgets(line, MAXLINE, input_FILE)) { | |
2591 | int command = 0; | |
2592 | char command_data[MAXLINE]; | |
2593 | ||
2594 | current_line_number++; | |
2595 | if (line[strlen(line)-1] != '\n') { | |
2596 | fprintf(stderr, "%s(%d) : Error : line truncated (longer than %d characters)\n", | |
2597 | current_file, current_line_number, MAXLINE - 1); | |
2598 | ||
2599 | fatal_errors++; | |
2600 | } | |
2601 | ||
2602 | /* Remove trailing white space from `line' */ | |
2603 | { | |
2604 | int i = strlen(line) - 2; /* Start removing backwards just before newline */ | |
2605 | while (i >= 0 && isspace((int) line[i])) | |
2606 | i--; | |
2607 | line[i+1] = '\n'; | |
2608 | line[i+2] = '\0'; | |
2609 | } | |
2610 | ||
2611 | if (sscanf(line, "Pattern %s", command_data) == 1) | |
2612 | command = 1; | |
2613 | else if (sscanf(line, "goal_elements %s", command_data) == 1) | |
2614 | command = 2; | |
2615 | else if (sscanf(line, "callback_data %s", command_data) == 1) | |
2616 | command = 3; | |
2617 | else if (sscanf(line, "attribute_map %s", command_data) == 1) | |
2618 | command = 4; | |
2619 | ||
2620 | if (command) { | |
2621 | char *p = strpbrk(command_data, " \t"); | |
2622 | ||
2623 | if (p) | |
2624 | *p = 0; | |
2625 | ||
2626 | if (patno >= 0) { | |
2627 | switch (state) { | |
2628 | case 1: | |
2629 | fprintf(stderr, "%s(%d) : Warning: empty pattern %s\n", | |
2630 | current_file, current_line_number, pattern_names[patno]); | |
2631 | break; | |
2632 | case 2: | |
2633 | case 3: | |
2634 | fprintf(stderr, "%s(%d) : Error : No entry line for pattern %s\n", | |
2635 | current_file, current_line_number, pattern_names[patno]); | |
2636 | fatal_errors++; | |
2637 | break; | |
2638 | case 5: | |
2639 | case 6: | |
2640 | { | |
2641 | struct pattern_attribute *attribute = NULL; | |
2642 | ||
2643 | if (pattern[patno].attributes) { | |
2644 | for (attribute = pattern[patno].attributes; | |
2645 | attribute->type != LAST_ATTRIBUTE; | |
2646 | attribute++) { | |
2647 | if (attribute->type >= FIRST_OFFSET_ATTRIBUTE) | |
2648 | break; | |
2649 | } | |
2650 | } | |
2651 | ||
2652 | if (attribute == NULL || attribute->type == LAST_ATTRIBUTE) { | |
2653 | fprintf(stderr, | |
2654 | "%s(%d) : Warning: constraint diagram but no constraint line or offset attributes for pattern %s\n", | |
2655 | current_file, current_line_number, pattern_names[patno]); | |
2656 | } | |
2657 | } | |
2658 | ||
2659 | break; | |
2660 | case 7: | |
2661 | case 8: | |
2662 | finish_constraint_and_action(); | |
2663 | /* fall through */ | |
2664 | case 0: | |
2665 | case 4: | |
2666 | check_constraint_diagram(); | |
2667 | } | |
2668 | } | |
2669 | ||
2670 | state = 0; | |
2671 | if (command == 1) { /* Pattern `name' */ | |
2672 | int k; | |
2673 | ||
2674 | if (!discard_pattern) { | |
2675 | convert_attribute_labels_to_offsets(); | |
2676 | patno++; | |
2677 | } | |
2678 | else | |
2679 | code_pos = save_code_pos; | |
2680 | reset_pattern(); | |
2681 | ||
2682 | if (strlen(command_data) > MAXNAME - 1) { | |
2683 | fprintf(stderr, "%s(%d) : Warning : pattern name is too long, truncated\n", | |
2684 | current_file, current_line_number); | |
2685 | command_data[MAXNAME - 1] = 0; | |
2686 | } | |
2687 | ||
2688 | /* Somewhat inefficient, but doesn't take any real time | |
2689 | * given current database sizes. | |
2690 | */ | |
2691 | for (k = 0; k < patno; k++) { | |
2692 | if (strcmp(pattern_names[k], command_data) == 0) { | |
2693 | fprintf(stderr, "%s(%d) : Warning : duplicate pattern name `%s'\n", | |
2694 | current_file, current_line_number, command_data); | |
2695 | break; | |
2696 | } | |
2697 | } | |
2698 | ||
2699 | strcpy(pattern_names[patno], command_data); | |
2700 | ||
2701 | transformation_hint = find_transformation_hint(pattern_names[patno]); | |
2702 | ||
2703 | state = 1; | |
2704 | discard_pattern = 0; | |
2705 | } | |
2706 | else if (command == 2 || command == 3) { | |
2707 | int *sort_out = (command == 2 ? nongoal : callback_unneeded); | |
2708 | int k; | |
2709 | ||
2710 | for (k = 0; k < 8; k++) | |
2711 | sort_out[k] = 1; | |
2712 | ||
2713 | if (strcmp(command_data, "none")) { | |
2714 | char *c; | |
2715 | ||
2716 | for (c = command_data; *c; c++) { | |
2717 | switch (*c) { | |
2718 | case '.': | |
2719 | sort_out[ATT_dot] = 0; | |
2720 | if (command == 2) { /* goal_elements */ | |
2721 | sort_out[ATT_comma] = 0; | |
2722 | sort_out[ATT_not] = 0; | |
2723 | } | |
2724 | break; | |
2725 | case 'X': sort_out[ATT_X] = 0; break; | |
2726 | case 'O': sort_out[ATT_O] = 0; break; | |
2727 | case 'x': sort_out[ATT_x] = 0; break; | |
2728 | case 'o': sort_out[ATT_o] = 0; break; | |
2729 | case ',': | |
2730 | sort_out[ATT_comma] = 0; | |
2731 | if (command != 2) | |
2732 | break; | |
2733 | case '!': | |
2734 | sort_out[ATT_not] = 0; | |
2735 | if (command != 2) | |
2736 | break; | |
2737 | default: | |
2738 | fprintf(stderr, "%s(%d) : Error : illegal character `%c'\n", | |
2739 | current_file, current_line_number, *c); | |
2740 | fatal_errors++; | |
2741 | } | |
2742 | } | |
2743 | } | |
2744 | } | |
2745 | else { | |
2746 | struct attribute_description *old_map = attribute_map; | |
2747 | ||
2748 | if (strcmp(command_data, "general") == 0) { | |
2749 | attribute_map = general_attribute_map; | |
2750 | attributes_needed = 1; | |
2751 | } | |
2752 | else if (strcmp(command_data, "value_only") == 0) { | |
2753 | attribute_map = value_only_attribute_map; | |
2754 | attributes_needed = 0; | |
2755 | } | |
2756 | else if (strcmp(command_data, "owl_attack") == 0) { | |
2757 | attribute_map = owl_attack_attribute_map; | |
2758 | attributes_needed = 1; | |
2759 | } | |
2760 | else if (strcmp(command_data, "owl_defense") == 0) { | |
2761 | attribute_map = owl_defense_attribute_map; | |
2762 | attributes_needed = 1; | |
2763 | } | |
2764 | else if (strcmp(command_data, "none") == 0) { | |
2765 | attribute_map = NULL; | |
2766 | attributes_needed = 0; | |
2767 | } | |
2768 | else { | |
2769 | fprintf(stderr, "%s(%d) : Error : unknown attribute map `%s'", | |
2770 | current_file, current_line_number, command_data); | |
2771 | fatal_errors++; | |
2772 | } | |
2773 | ||
2774 | if (patno != -1 && attribute_map != old_map) { | |
2775 | fprintf(stderr, "%s(%d) : Error : attribute map can only be set before the first pattern\n", | |
2776 | current_file, current_line_number); | |
2777 | fatal_errors++; | |
2778 | } | |
2779 | } | |
2780 | } | |
2781 | else if (line[0] == '\n' || line[0] == '#') { | |
2782 | /* nothing */ | |
2783 | if (state == 2 || state == 5) { | |
2784 | if (state == 5) | |
2785 | check_constraint_diagram_size(); | |
2786 | state++; | |
2787 | } | |
2788 | } | |
2789 | else if (strchr(VALID_PATTERN_CHARS, line[0]) | |
2790 | || strchr(VALID_EDGE_CHARS, line[0]) | |
2791 | || strchr(VALID_CONSTRAINT_LABELS, line[0])) { | |
2792 | /* diagram line */ | |
2793 | switch (state) { | |
2794 | case 0: | |
2795 | case 3: | |
2796 | case 6: | |
2797 | case 7: | |
2798 | case 8: | |
2799 | fprintf(stderr, | |
2800 | "%s(%d) : error : What, another diagram here? (pattern %s)\n", | |
2801 | current_file, current_line_number, pattern_names[patno]); | |
2802 | fatal_errors++; | |
2803 | break; | |
2804 | case 1: | |
2805 | state++; /* fall through */ | |
2806 | case 2: | |
2807 | if (!read_pattern_line(line)) { | |
2808 | discard_pattern = 1; | |
2809 | el = 0; | |
2810 | } | |
2811 | break; | |
2812 | case 4: | |
2813 | state++; /* fall through */ | |
2814 | case 5: | |
2815 | read_constraint_diagram_line(line); | |
2816 | break; | |
2817 | } | |
2818 | } | |
2819 | else if (line[0] == ':') { | |
2820 | if (state == 2 || state == 3) { | |
2821 | finish_pattern(line); | |
2822 | ||
2823 | if (!discard_pattern) { | |
2824 | if (database_type == DB_DFA || database_type == OPTIMIZE_DFA) | |
2825 | write_to_dfa(patno); | |
2826 | if (database_type == DB_CORNER) | |
2827 | corner_add_pattern(); | |
2828 | else if (database_type != OPTIMIZE_DFA) | |
2829 | write_elements(output_FILE); | |
2830 | } | |
2831 | ||
2832 | state = 4; | |
2833 | save_code_pos = code_pos; | |
2834 | } | |
2835 | else { | |
2836 | fprintf(stderr, | |
2837 | "%s(%d) : warning : Unexpected entry line in pattern %s\n", | |
2838 | current_file, current_line_number, pattern_names[patno]); | |
2839 | } | |
2840 | } | |
2841 | else if (line[0] == ';') { | |
2842 | if (state == 5) | |
2843 | check_constraint_diagram_size(); | |
2844 | ||
2845 | if (state == 5 || state == 6 || state == 7) { | |
2846 | read_constraint_line(line+1); | |
2847 | state = 7; | |
2848 | } | |
2849 | else { | |
2850 | fprintf(stderr, "%s(%d) : Warning: unexpected constraint line in pattern %s\n", | |
2851 | current_file, current_line_number, pattern_names[patno]); | |
2852 | } | |
2853 | } | |
2854 | else if (line[0] == '>') { | |
2855 | if (state == 4 || state == 5 || state == 6 | |
2856 | || state == 7 || state == 8) { | |
2857 | if (state == 5) | |
2858 | check_constraint_diagram_size(); | |
2859 | read_action_line(line+1); | |
2860 | state = 8; | |
2861 | } | |
2862 | else { | |
2863 | fprintf(stderr, "Warning: unexpected action line in pattern %s\n", | |
2864 | pattern_names[patno]); | |
2865 | } | |
2866 | } | |
2867 | else { | |
2868 | int i = strlen(line); | |
2869 | char c = line[i-1]; | |
2870 | line[i-1] = 0; /* Chop off \n */ | |
2871 | fprintf(stderr, "%s(%d) : error : Malformed line \"%s\" in pattern %s\n", | |
2872 | current_file, current_line_number, line, pattern_names[patno]); | |
2873 | line[i-1] = c; /* Put it back - maybe not necessary at this point. */ | |
2874 | fatal_errors++; | |
2875 | } | |
2876 | } /* while not EOF */ | |
2877 | } /* for each file */ | |
2878 | ||
2879 | convert_attribute_labels_to_offsets(); | |
2880 | ||
2881 | if (patno >= 0) { | |
2882 | switch (state) { | |
2883 | case 1: | |
2884 | fprintf(stderr, "Warning: empty pattern %s\n", | |
2885 | pattern_names[patno]); | |
2886 | break; | |
2887 | case 2: | |
2888 | case 3: | |
2889 | fprintf(stderr, "%s(%d) : Error : no entry line for pattern %s\n", | |
2890 | current_file, current_line_number, pattern_names[patno]); | |
2891 | fatal_errors++; | |
2892 | break; | |
2893 | case 5: | |
2894 | case 6: | |
2895 | fprintf(stderr, | |
2896 | "Warning: constraint diagram but no constraint line for pattern %s\n", | |
2897 | pattern_names[patno]); | |
2898 | break; | |
2899 | case 7: | |
2900 | case 8: | |
2901 | finish_constraint_and_action(); /* fall through */ | |
2902 | case 0: | |
2903 | case 4: | |
2904 | check_constraint_diagram(); | |
2905 | patno++; | |
2906 | reset_pattern(); | |
2907 | } | |
2908 | } | |
2909 | ||
2910 | if (discard_pattern) { | |
2911 | patno--; | |
2912 | code_pos = save_code_pos; | |
2913 | } | |
2914 | ||
2915 | *code_pos = 0; | |
2916 | ||
2917 | if (verbose) | |
2918 | fprintf(stderr, "%d / %d patterns have edge-constraints\n", | |
2919 | pats_with_constraints, patno); | |
2920 | ||
2921 | if (database_type != OPTIMIZE_DFA) { | |
2922 | /* Forward declaration, which autohelpers might need. */ | |
2923 | if (database_type != DB_CORNER) | |
2924 | fprintf(output_FILE, "static struct pattern %s[%d];\n\n", prefix, patno + 1); | |
2925 | else | |
2926 | fprintf(output_FILE, "static struct corner_pattern %s[%d];\n\n", prefix, patno + 1); | |
2927 | ||
2928 | /* Write the autohelper code. */ | |
2929 | fprintf(output_FILE, "%s", autohelper_code); | |
2930 | ||
2931 | if (attributes_needed) | |
2932 | write_attributes(output_FILE); | |
2933 | else | |
2934 | assert(num_attributes == 1); | |
2935 | ||
2936 | write_patterns(output_FILE); | |
2937 | ||
2938 | if (database_type == DB_DFA) { | |
2939 | fprintf(stderr, "---------------------------\n"); | |
2940 | ||
2941 | dfa_finalize(&dfa); | |
2942 | dfa_shuffle(&dfa); | |
2943 | ||
2944 | fprintf(stderr, "DFA for %s\n", prefix); | |
2945 | fprintf(stderr, "size: %d kB for ", dfa_size(&dfa)); | |
2946 | fprintf(stderr, "%d patterns", patno); | |
2947 | fprintf(stderr, " (%d states)\n", dfa.last_state); | |
2948 | ||
2949 | strcpy(dfa.name, prefix); | |
2950 | print_c_dfa(output_FILE, prefix, &dfa); | |
2951 | fprintf(stderr, "---------------------------\n"); | |
2952 | ||
2953 | if (DFA_MAX_MATCHED/8 < dfa_calculate_max_matched_patterns(&dfa)) | |
2954 | fprintf(stderr, "Warning: Increase DFA_MAX_MATCHED in 'dfa.h'.\n"); | |
2955 | ||
2956 | kill_dfa(&dfa); | |
2957 | dfa_end(); | |
2958 | } | |
2959 | ||
2960 | if (database_type == DB_CORNER) { | |
2961 | fprintf(stderr, "---------------------------\n"); | |
2962 | ||
2963 | corner_pack_variations(corner_root.child, 0); | |
2964 | fprintf(output_FILE, "static struct corner_variation %s_variations[] = {\n", | |
2965 | prefix); | |
2966 | corner_write_variations(corner_root.child, output_FILE); | |
2967 | ||
2968 | fprintf(stderr, "corner database for %s\n", prefix); | |
2969 | fprintf(stderr, "size: %d kB for %d patterns (%d variations)\n", | |
2970 | CORNER_DB_SIZE(patno, total_variations), patno, total_variations); | |
2971 | fprintf(stderr, "---------------------------\n"); | |
2972 | } | |
2973 | ||
2974 | write_pattern_db(output_FILE); | |
2975 | ||
2976 | if (fatal_errors) { | |
2977 | fprintf(output_FILE, "\n#error: One or more fatal errors compiling %s\n", | |
2978 | current_file); | |
2979 | } | |
2980 | } | |
2981 | else { /* database_type == OPTIMIZE_DFA */ | |
2982 | int k; | |
2983 | int *optimized_variations; | |
2984 | ||
2985 | transformations_FILE = fopen(transformations_file_name, "wb"); | |
2986 | if (transformations_FILE == NULL) { | |
2987 | fprintf(stderr, "error : cannot write to file %s\n", | |
2988 | transformations_file_name); | |
2989 | } | |
2990 | ||
2991 | optimized_variations = dfa_patterns_optimize_variations(&dfa_pats, | |
2992 | iterations); | |
2993 | for (k = 0; k < patno; k++) { | |
2994 | fprintf(transformations_FILE, "%s\t%d\n", pattern_names[k], | |
2995 | optimized_variations[k]); | |
2996 | } | |
2997 | ||
2998 | free(optimized_variations); | |
2999 | dfa_end(); | |
3000 | } | |
3001 | ||
3002 | return fatal_errors ? 1 : 0; | |
3003 | } | |
3004 | ||
3005 | ||
3006 | /* | |
3007 | * Local Variables: | |
3008 | * tab-width: 8 | |
3009 | * c-basic-offset: 2 | |
3010 | * End: | |
3011 | */ |