| 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 | */ |