/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *
* http://www.gnu.org/software/gnugo/ for more information. *
* Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
* 2008 and 2009 by the Free Software Foundation. *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation - version 3 or *
* (at your option) any later version. *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License in file COPYING for more details. *
* You should have received a copy of the GNU General Public *
* License along with this program; if not, write to the Free *
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
* Boston, MA 02111, USA. *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Compile one of the pattern databases. This takes a database file,
* e.g. patterns.db, and produces a C code file, in this case
/* See also patterns.h, and the *.db files.
/* Differences when compiling connections patterns (-c) :
* '*' means cutting point
* '!' is allowed (inhibit connection there), matches as '.'.
* '!' will always be written as the first elements
/* FIXME: This file is a horrible mess, especially after pattern
* matching went 1D. Cleaning it will make future work
* with pattern mathing easier.
/* As in the rest of GNU Go, co-ordinate convention (i,j) is 'i' down from
* the top, then 'j' across from the left
Usage : mkpat [options] <prefix>\n\
-i = one or more input files (typically *.db)\n\
-o = output file (typically *.c)\n\
-t = DFA transformations file (typically *.dtr)\n\
-V <level> = DFA verbiage level\n\
-p = compile general pattern database (the default)\n\
-c = compile connections database\n\
-C = compile a corner pattern database\n\
-D = compile a DFA database (allows fast matching)\n\
-d <iterations> = don't generate database, but optimize a DFA\n\
transformation file instead\n\
Pattern generation options:\n\
-O = allow only O to be anchor (the default)\n\
-X = allow only X to be anchor\n\
-b = allow both colors to be anchor\n\
-m = try to place the anchor in the center of the pattern\n\
(works best with DFA databases)\n\
-a = require anchor in all patterns. Sets fixed_anchor flag in db\n\
If no input files specified, reads from stdin.\n\
If output file is not specified, writes to stdout.\n\
#define DB_GENERAL ((int) 'p')
#define DB_CONNECTIONS ((int) 'c')
#define DB_CORNER ((int) 'C')
#define DB_DFA ((int) 'D')
#define OPTIMIZE_DFA ((int) 'd')
/* code assumes that ATT_O and ATT_X are 1 and 2 (in either order)
* An attribute is a candidate for anchor if (att & anchor) != 0
#define ANCHOR_BOTH (ATT_O | ATT_X)
#define MAXCONSTRAINT 10000
#define MAX_INPUT_FILE_NAMES 10
/* Avoid compiler warnings with unused parameters */
#define UNUSED(x) (void)x
/* valid characters that can appear in a pattern
* position in string is att value to store
static const char VALID_PATTERN_CHARS
[] = ".XOxo,a!*?QY";
static const char VALID_EDGE_CHARS
[] = "+-|";
static const char VALID_CONSTRAINT_LABELS
[] = "abcdefghijklmnpqrstuvwyzABCDEFGHIJKLMNPRSTUVWZ";
/* the offsets into the list are the ATT_* defined in patterns.h
* The following defns are for internal use only, and are not
* written out to the compiled pattern database
/* These arrays control discarding of unnecessary patval elements.
* Modify them using `goal_elements ...' and `callback_data ..'
* commands in a database. By default, we don't drop any elements.
static int nongoal
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
static int callback_unneeded
[8] = {0, 0, 0, 0, 0, 0, 0, 0};
/* stuff used in reading/parsing pattern rows */
static int maxi
, maxj
; /* (i,j) offsets of largest element */
static int mini
, minj
; /* offset of top-left element
(0,0) unless there are edge constraints */
static unsigned int where
; /* NORTH_EDGE | WEST_EDGE, etc */
static int el
; /* next element number in current pattern */
static struct patval_b elements
[MAX_BOARD
*MAX_BOARD
]; /* elements of current pattern */
static int ci
= -1, cj
= -1; /* position of origin (first piece element)
static int patno
; /* current pattern */
static int discard_pattern
= 0; /* Set to nonzero to discard a pattern (if e.g.
* it is too large or duplicated). */
static int pats_with_constraints
= 0; /* just out of interest */
static int label_coords
[256][2]; /* Coordinates for labeled stones in the
static int current_c_i
; /* Counter for the line number of a
static char constraint
[MAXCONSTRAINT
]; /* Store constraint lines. */
static char action
[MAXCONSTRAINT
]; /* Store action lines. */
static char diagram
[MAX_BOARD
+2][MAX_BOARD
+3];
/* store pattern diagram*/
static char constraint_diagram
[MAX_BOARD
+2][MAX_BOARD
+3];
/* store pattern constraint diagram */
/* stuff to maintain info about patterns while reading */
static struct pattern pattern
[MAXPATNO
]; /* accumulate the patterns into here */
static char pattern_names
[MAXPATNO
][MAXNAME
]; /* with optional names here, */
static int num_attributes
;
static struct pattern_attribute attributes
[MAXPATNO
* NUM_ATTRIBUTES
];
static char helper_fn_names
[MAXPATNO
][MAXNAME
]; /* helper fn names here */
static char autohelper_code
[MAXPATNO
*300]; /* code for automatically generated */
/* helper functions here */
static char *code_pos
; /* current position in code buffer */
int type
; /* 0 - just copy the parameters,
* 1 - add parameter count,
* 2 - add address of the current pattern.
* current_* are useful for debugging broken patterns.
static const char *current_file
= NULL
;
static int current_line_number
= 0;
struct attribute_description
{
const char *input_name
; /* The name used in `.db' files. */
enum attribute_type type
;
static const char *attribute_name
[NUM_ATTRIBUTES
+ 1] = {
/* Owl-style value stored in pattern itself. */
#define IN_PATTERN_VALUE NUM_ATTRIBUTES
static struct attribute_description general_attribute_map
[] = {
{ "minvalue", MIN_VALUE
},
{ "maxvalue", MAX_VALUE
},
{ "terri", MIN_TERRITORY
},
{ "minterri", MIN_TERRITORY
},
{ "maxterri", MAX_TERRITORY
},
{ "followup", FOLLOWUP
},
{ "followup_value", FOLLOWUP
},
{ "reverse_followup", REVERSE_FOLLOWUP
},
static struct attribute_description value_only_attribute_map
[] = {
{ "value", IN_PATTERN_VALUE
},
static struct attribute_description owl_attack_attribute_map
[] = {
{ "value", IN_PATTERN_VALUE
},
{ "threatens_to_capture", THREATENS_TO_CAPTURE
},
{ "threatens_eye", THREATENS_EYE
},
{ "reverse_sente", REVERSE_SENTE
},
static struct attribute_description owl_defense_attribute_map
[] = {
{ "value", IN_PATTERN_VALUE
},
{ "threatens_to_capture", THREATENS_TO_CAPTURE
},
{ "threatens_eye", THREATENS_EYE
},
{ "reverse_sente", REVERSE_SENTE
},
static struct attribute_description
*attribute_map
= NULL
;
static int attributes_needed
= 0;
/* ================================================================ */
/* Autohelper function definitions */
/* ================================================================ */
* If one function has a name which is a prefix of another name, the
* shorter name must come later in the list. E.g. "lib" must be preceded
* by "lib2", "lib3", and "lib4".
static struct autohelper_func autohelper_functions
[] = {
{"lib2", 1, 0, 0.01, "worm[%s].liberties2"},
{"lib3", 1, 0, 0.01, "worm[%s].liberties3"},
{"lib4", 1, 0, 0.01, "worm[%s].liberties4"},
{"lib", 1, 0, 0.01, "countlib(%s)"},
"(dragon[%s].status == ALIVE)"},
"(dragon[%s].status == UNKNOWN)"},
"(dragon[%s].status == CRITICAL)"},
{"dead", 1, 0, 0.01, "(dragon[%s].status == DEAD)"},
{"status", 1, 0, 0.01, "dragon[%s].status"},
{"ko", 1, 0, 0.01, "is_ko_point(%s)"},
{"xdefend_against", 2, 0, 1.00,
"defend_against(%s, OTHER_COLOR(color), %s)"},
{"odefend_against", 2, 0, 1.00, "defend_against(%s, color, %s)"},
{"defend_against_atari", 1, 0, 1.00,
"defend_against_atari_helper(move, %s)"},
{"does_defend", 2, 0, 1.00, "does_defend(%s, %s)"},
{"does_attack", 2, 0, 1.00, "does_attack(%s, %s)"},
{"attack", 1, 0, 1.00, "ATTACK_MACRO(%s)"},
{"defend", 1, 0, 1.00, "DEFEND_MACRO(%s)"},
{"weakness", 1, 0, 0.01, "dragon_weakness(%s, 0)"},
{"weak", 1, 0, 0.01, "dragon_weak(%s)"},
{"safe_xmove", 1, 0, 1.00, "safe_move(%s, OTHER_COLOR(color))"},
{"safe_omove", 1, 0, 1.00, "safe_move(%s, color)"},
{"legal_xmove", 1, 0, 0.05, "is_legal(%s, OTHER_COLOR(color))"},
{"legal_omove", 1, 0, 0.05, "is_legal(%s, color)"},
{"x_suicide", 1, 0, 0.05, "is_suicide(%s, OTHER_COLOR(color))"},
{"o_suicide", 1, 0, 0.05, "is_suicide(%s, color)"},
{"x_alive_somewhere", 0, 1, 0.01,
"somewhere(OTHER_COLOR(color), 1, %d"},
{"o_alive_somewhere", 0, 1, 0.01, "somewhere(color, 1, %d"},
{"x_somewhere", 0, 1, 0.01,
"somewhere(OTHER_COLOR(color), 0, %d"},
{"o_somewhere", 0, 1, 0.01, "somewhere(color, 0, %d"},
{"xmoyo_opposite", 1, 0, 0.01,
"(whose_moyo(INITIAL_INFLUENCE(color), %s) == OTHER_COLOR(color))"},
{"omoyo_opposite", 1, 0, 0.01,
"(whose_moyo(INITIAL_INFLUENCE(color), %s) == color)"},
"(whose_moyo(OPPOSITE_INFLUENCE(color), %s) == OTHER_COLOR(color))"},
"(whose_moyo(OPPOSITE_INFLUENCE(color), %s) == color)"},
"(whose_area(OPPOSITE_INFLUENCE(color), %s) == OTHER_COLOR(color))"},
"(whose_area(OPPOSITE_INFLUENCE(color), %s) == color)"},
"(whose_territory(OPPOSITE_INFLUENCE(color), %s) == OTHER_COLOR(color))"},
"(whose_territory(OPPOSITE_INFLUENCE(color), %s) == color)"},
{"genus", 1, 0, 0.01, "dragon[%s].genus"},
{"approx_xlib", 1, 0, 0.03,
"approxlib(%s, OTHER_COLOR(color), MAX_LIBERTIES, NULL)"},
{"approx_olib", 1, 0, 0.03,
"approxlib(%s, color, MAX_LIBERTIES, NULL)"},
"accuratelib(%s, OTHER_COLOR(color), MAX_LIBERTIES, NULL)"},
"accuratelib(%s, color, MAX_LIBERTIES, NULL)"},
"cut_possible(%s, OTHER_COLOR(color))"},
{"ocut", 1, 0, 0.05, "cut_possible(%s, color)"},
{"edge_double_sente", 4, 1, 3.00,
"edge_double_sente_helper(%s, %s, %s, %s)"},
{"xplay_defend_both", 2, 1, 3.00,
"play_attack_defend2_n(OTHER_COLOR(color), 0, %d"},
{"oplay_defend_both", 2, 1, 3.00, "play_attack_defend2_n(color, 0, %d"},
{"xplay_attack_either", 2, 1, 3.00,
"play_attack_defend2_n(OTHER_COLOR(color), 1, %d"},
{"oplay_attack_either", 2, 1, 3.00, "play_attack_defend2_n(color, 1, %d"},
{"xplay_defend", 1, 1, 1.00,
"play_attack_defend_n(OTHER_COLOR(color), 0, %d"},
{"oplay_defend", 1, 1, 1.00, "play_attack_defend_n(color, 0, %d"},
{"xplay_attack", 1, 1, 1.00,
"play_attack_defend_n(OTHER_COLOR(color), 1, %d"},
{"oplay_attack", 1, 1, 1.00, "play_attack_defend_n(color, 1, %d"},
{"xplay_break_through", 3, 1, 5.00,
"play_break_through_n(OTHER_COLOR(color), %d"},
{"oplay_break_through", 3, 1, 5.00, "play_break_through_n(color, %d"},
{"oplay_connect", 2, 1, 10.00, "play_connect_n(color, 1, %d"},
{"xplay_connect", 2, 1, 10.00,
"play_connect_n(OTHER_COLOR(color), 1, %d"},
{"oplay_disconnect", 2, 1, 10.00, "play_connect_n(color, 0, %d"},
{"xplay_disconnect", 2, 1, 10.00,
"play_connect_n(OTHER_COLOR(color), 0, %d"},
{"oplay_lib", 1, 1, 0.06, "play_lib_n(color, %d"},
{"xplay_lib", 1, 1, 0.06,
"play_lib_n(OTHER_COLOR(color), %d"},
{"seki_helper", 1, 0, 0.0, "seki_helper(%s)"},
{"threaten_to_save", 1, 0, 0.0, "threaten_to_save_helper(move,%s)"},
{"threaten_to_capture", 1, 0, 0.0,
"threaten_to_capture_helper(move,%s)"},
{"prevent_attack_threat", 1, 0, 0.0,
"prevent_attack_threat_helper(move, %s)"},
{"eye", 1, 0, 0.01, "is_eye_space(%s)"},
{"proper_eye", 1, 0, 0.01, "is_proper_eye_space(%s)"},
{"marginal_eye", 1, 0, 0.01, "is_marginal_eye_space(%s)"},
{"halfeye", 1, 0, 0.01, "is_halfeye(half_eye,%s)"},
{"max_eye_value", 1, 0, 0.01, "max_eye_value(%s)"},
{"owl_topological_eye", 2, 0, 0.01, "owl_topological_eye(%s, board[%s])"},
{"obvious_false_oeye", 1, 0, 0.01, "obvious_false_eye(%s, color)"},
{"obvious_false_xeye", 1, 0, 0.01,
"obvious_false_eye(%s, OTHER_COLOR(color))"},
{"antisuji", 1, 0, 0.0, "add_antisuji_move(%s)"},
{"add_connect_move", 2, 0, 0.0, "add_connection_move(move,%s, %s)"},
{"add_cut_move", 2, 0, 0.0, "add_cut_move(move, %s, %s)"},
{"test_attack_either_move", 2, 0, 0.0,
"test_attack_either_move(move, color, %s, %s)"},
{"add_defend_both_move", 2, 0, 0.0,
"add_all_move(move, DEFEND_STRING, %s, DEFEND_STRING, %s)"},
{"same_dragon", 2, 0, 0.01, "is_same_dragon(%s, %s)"},
{"same_string", 2, 0, 0.01, "same_string(%s, %s)"},
{"dragonsize", 1, 0, 0.01, "dragon[%s].size"},
{"wormsize", 1, 0, 0.01, "countstones(%s)"},
{"effective_size", 1, 0, 0.01, "dragon[%s].effective_size"},
{"vital_chain", 1, 0, 0.05, "vital_chain(%s)"},
{"potential_cutstone", 1, 0, 0.01, "worm[%s].cutstone2 > 1"},
{"amalgamate_most_valuable_helper", 3, 0, 0.0,
"amalgamate_most_valuable_helper(%s, %s, %s)"},
{"amalgamate", 2, 0, 0.0, "join_dragons(%s, %s)"},
{"owl_escape_value", 1, 0, 0.01, "owl_escape_value(%s)"},
{"owl_goal_dragon", 1, 0, 0.01, "owl_goal_dragon(%s)"},
{"owl_eyespace", 1, 0, 0.01, "owl_eyespace(%s)"},
{"owl_big_eyespace", 1, 0, 0.01, "owl_big_eyespace(%s)"},
{"owl_mineye", 1, 0, 0.01, "owl_mineye(%s)"},
{"owl_maxeye", 1, 0, 0.01, "owl_maxeye(%s)"},
{"owl_proper_eye", 1, 0, 0.01, "owl_proper_eye(%s)"},
{"owl_eye_size", 1, 0, 0.01, "owl_eye_size(%s)"},
{"owl_lunch", 1, 0, 0.01, "owl_lunch(%s)"},
{"owl_strong_dragon", 1, 0, 0.01, "owl_strong_dragon(%s)"},
"(dragon[%s].owl_threat_status == CAN_THREATEN_DEFENSE)"},
{"finish_ko_helper", 1, 0, 0.05, "finish_ko_helper(%s)"},
{"squeeze_ko_helper", 1, 0, 0.03, "squeeze_ko_helper(%s)"},
{"backfill_helper", 3, 0, 1.50, "backfill_helper(%s, %s, %s)"},
{"connect_and_cut_helper2", 3, 0, 3.00,
"connect_and_cut_helper2(%s, %s, %s, color)"},
{"connect_and_cut_helper", 3, 0, 3.00, "connect_and_cut_helper(%s, %s, %s)"},
{"owl_threatens", 2, 0, 0.01, "owl_threatens_attack(%s, %s)"},
{"replace", 2, 0, 0.0, "add_replacement_move(%s, %s, color)"},
{"backfill_replace", 2, 0, 0.0, "backfill_replace(%s, %s)"},
{"non_oterritory", 1, 0, 0.0,
"influence_mark_non_territory(%s, color)"},
{"non_xterritory", 1, 0, 0.0,
"influence_mark_non_territory(%s, OTHER_COLOR(color))"},
{"remaining_handicap_stones", 0, 0, 0.0, "free_handicap_remaining_stones()"},
{"total_handicap_stones", 0, 0, 0.0, "free_handicap_total_stones()"},
{"o_captures_something", 1, 0, 0.02, "does_capture_something(%s, color)"},
{"x_captures_something", 1, 0, 0.02,
"does_capture_something(%s, OTHER_COLOR(color))"},
{"false_eye_territory", 1, 0, 0.0, "false_eye_territory[%s]"},
{"false_eye", 1, 0, 0.01, "is_false_eye(half_eye, %s)"},
{"o_visible_along_edge", 2, 0, 0.05, "visible_along_edge(color,%s,%s)"},
{"x_visible_along_edge", 2, 0, 0.05,
"visible_along_edge(OTHER_COLOR(color),%s,%s)"},
{"is_surrounded", 1, 0, 0.01, "is_surrounded(%s)"},
{"does_surround", 2, 0, 1.00, "does_surround(%s, %s)"},
{"surround_map", 2, 0, 0.01, "surround_map(%s, %s)"},
{"oracle_threatens", 2, 0, 0.01, "oracle_threatens(%s, %s)"},
{"value", 0, 2, 0.0, "(%s->value)"},
{"adjacent_to_stone_in_atari", 1, 0, 1.0,
"adjacent_to_stone_in_atari(%s)"},
{"adjacent_to_defendable_stone_in_atari", 1, 0, 1.0,
"adjacent_to_defendable_stone_in_atari(%s)"},
{"good_attack_threat", 2, 0, 0.01, "register_good_attack_threat(%s, %s)"},
{"known_safe_move", 1, 0, 0.01, "register_known_safe_move(%s)"},
{"break_mirror_helper", 1, 0, 0.01, "break_mirror_helper(%s, color)"}
/* To get a valid function pointer different from NULL. */
dummyhelper(int transformation
, int move
, int color
, int action
)
UNUSED(transformation
); UNUSED(move
); UNUSED(color
);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n\
* This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *\n\
* http://www.gnu.org/software/gnugo/ for more information. *\n\
* Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *\n\
* 2008 and 2009 by the Free Software Foundation. *\n\
* This program is free software; you can redistribute it and/or *\n\
* modify it under the terms of the GNU General Public License as *\n\
* published by the Free Software Foundation - version 3 or *\n\
* (at your option) any later version. *\n\
* This program is distributed in the hope that it will be useful, *\n\
* but WITHOUT ANY WARRANTY; without even the implied warranty of *\n\
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *\n\
* GNU General Public License in file COPYING for more details. *\n\
* You should have received a copy of the GNU General Public *\n\
* License along with this program; if not, write to the Free *\n\
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *\n\
* Boston, MA 02111, USA. *\n\
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n\n\
#include <stdio.h> /* for NULL */\n\
#include \"liberty.h\"\n\
#include \"patterns.h\"\n\n\
static int fatal_errors
= 0;
int verbose
= 0; /* -v */
static int database_type
= 0; /* -p (default), -c, -f, -C, -D or -T */
static int anchor
= 0; /* Whether both O and/or X may be anchors.
* -b for both. -X for only X.
static int choose_best_anchor
= 0; /* -m */
/* FIXME: `fixed anchor' option doesn't work properly yet.
* Probably the first thing to implement is to add
* checks for anchor validity.
static int fixed_anchor
= 0; /* -a */
static dfa_patterns dfa_pats
;
static int transformation_hint
;
static int labels_transformation
= 0;
static struct hint_data
*first_hint
= NULL
;
parse_transformations_file(FILE *file
)
struct hint_data
**link
= &first_hint
;
struct hint_data
*hint
= malloc(sizeof(*hint
));
n
= fscanf(file
, "%s %d", hint
->name
, &hint
->transformation_hint
);
find_transformation_hint(const char *pattern_name
)
if (database_type
== DB_DFA
|| database_type
== OPTIMIZE_DFA
) {
for (hint
= first_hint
; hint
; hint
= hint
->next
) {
if (!strcmp(hint
->name
, pattern_name
))
return hint
->transformation_hint
;
return database_type
== OPTIMIZE_DFA
? -1 : 0;
/**************************
* stuff to check the constraint diagram
**************************/
#define CHECK_CHARS "xXoO"
check_constraint_diagram(void)
int i
, j
, ino
= 0, iso
= 0, jwo
= 0;
int have_constraint
= (pattern
[patno
].autohelper_flag
& HAVE_CONSTRAINT
);
fprintf(stderr
, "patno: %d\n", patno
);
for (i
= ino
; i
<= maxi
+ino
+iso
; i
++)
fprintf(stderr
, "%02d %s\n", i
, diagram
[i
]);
for (i
= ino
; i
<= maxi
+ino
+iso
; i
++)
fprintf(stderr
, "%02d %s\n", i
, constraint_diagram
[i
]);
fprintf(stderr
, "have_constraint: %d\n", have_constraint
);
if (have_constraint
&& el
) {
for (i
= ino
; i
<= maxi
+ino
; i
++)
for (j
= jwo
; j
<= maxj
+jwo
; j
++) {
fprintf(stderr
, "%2d %2d %c %c\n", i
, j
, constraint_diagram
[i
][j
],
if (strchr(CHECK_CHARS
, constraint_diagram
[i
][j
])
&& constraint_diagram
[i
][j
] != diagram
[i
][j
]) {
fprintf(stderr
, "%s(%d) : Error : "
"xXoO not matched in constraint diagram of pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
/**************************
* stuff to parse the input
**************************/
/* reset state of all pattern variables */
strcpy(helper_fn_names
[patno
], "NULL");
for (i
= 0; i
< 256; i
++)
for (i
= 0; i
< MAX_BOARD
+2; i
++) {
for (j
= 0; j
< MAX_BOARD
+3; j
++) {
constraint_diagram
[i
][j
] = '\0';
memset(&pattern
[patno
], 0, sizeof(struct pattern
));
/* This is called to compute the extents of the pattern, applying
* edge constraints as necessary.
/* When this is called, elements go from (mini,minj) inclusive to
* (maxi-1, maxj-1) [ie exclusive]. Make them inclusive.
* Ie maximum element lies on (maxi,maxj).
/* apply edge constraints to the size of the pattern */
if (where
& (NORTH_EDGE
| SOUTH_EDGE
| EAST_EDGE
| WEST_EDGE
))
fprintf(stderr
, "Pattern %s has constraints 0x%x\n",
pattern_names
[patno
], where
);
pattern
[patno
].edge_constraints
= where
;
/* At this point, (mini,minj) -> (maxi,maxj) contain
* the extent of the pattern, relative to top-left
* of pattern, rather than (ci,cj).
* But we store them in the output file relative
* to (ci,cj), so that we can transform the corners
* of the pattern like any other relative co-ord.
pattern
[patno
].mini
= mini
- ci
;
pattern
[patno
].minj
= minj
- cj
;
pattern
[patno
].maxi
= maxi
- ci
;
pattern
[patno
].maxj
= maxj
- cj
;
char str
[DFA_MAX_ORDER
+ 1];
char strrot
[DFA_MAX_ORDER
+ 1];
assert(ci
!= -1 && cj
!= -1);
pattern
[index
].patn
= elements
; /* a little modification : keep in touch! */
pattern
[index
].name
= &(pattern_names
[index
][0]);
/* First we create the string from the actual pattern. */
pattern_2_string(pattern
+ index
, elements
, str
, ci
, cj
);
fprintf(stderr
, "Add :%s\n", pattern
[index
].name
);
if (database_type
== DB_DFA
) {
dfa_rotate_string(strrot
, str
, transformation_hint
);
ratio
= ((dfa_add_string(&dfa
, strrot
, index
, transformation_hint
) - 1.0)
/* Complain when there is more than 10% of increase */
if (dfa_size(&dfa
) > 100 && ratio
> 10.0) {
fprintf(stderr
, "Pattern %s => %3.1f%% increase: ",
pattern
[index
].name
, ratio
);
fprintf(stderr
, "another orientation may save memory.\n");
labels_transformation
= transformation_hint
;
int rot_stop
= pattern
[index
].trfno
;
assert(database_type
== OPTIMIZE_DFA
);
for (ll
= rot_start
; ll
< rot_stop
; ll
++) {
dfa_rotate_string(strrot
, str
, ll
);
dfa_patterns_add_pattern(&dfa_pats
, strrot
, index
);
if (transformation_hint
== -1)
dfa_patterns_select_shortest_variation(&dfa_pats
);
dfa_patterns_set_last_pattern_variation(&dfa_pats
, (transformation_hint
/* For good performance, we want to reject patterns as quickly as
* possible. For each pattern, this combines 16 positions around
* the anchor stone into a 32-bit mask and value. In the matcher,
* the same 4x4 grid is precomputed, and then we can quickly
* test 16 board positions with one test.
* See matchpat.c for details of how this works - basically, if
* we AND what is on the board with the and_mask, and get the
* value in the val_mask, we have a match. This test can be
* applied in parallel : 2 bits per posn x 16 posns = 32 bits.
* "Don't care" has and_mask = val_mask = 0, which is handy !
/* element: . X O x o , a ! */
static const unsigned int and_mask
[] = { 3, 3, 3, 1, 2, 3, 3, 3 };
static const unsigned int val_mask
[] = { 0, 2, 1, 0, 0, 0, 0, 0 };
int ll
; /* iterate over rotations */
int k
; /* iterate over elements */
for (ll
= 0; ll
< 8; ++ll
) {
for (k
= 0; k
< el
; ++k
) {
TRANSFORM2(elements
[k
].x
- ci
, elements
[k
].y
- cj
, &ti
, &tj
,
TRANSFORM2(ti
, tj
, &di
, &dj
, ll
);
if (di
>= 0 && di
< 4 && dj
>= 0 && dj
< 4) {
pattern
[patno
].and_mask
[ll
]
|= and_mask
[elements
[k
].att
] << (30 - di
* 8 - dj
* 2);
pattern
[patno
].val_mask
[ll
]
|= val_mask
[elements
[k
].att
] << (30 - di
* 8 - dj
* 2);
/* We've just read a line that looks like a pattern line. Now process it.
* If the pattern becomes larger than maximal supported board, the function
* returns zero, so that the pattern can be discarded.
read_pattern_line(char *p
)
/* something wrong here : pattern line after a SOUTH_EDGE constraint */
if (*p
== '+' || *p
== '-') {
/* must be a north/south constraint */
if (maxi
> 0 && !(where
& WEST_EDGE
))
/* if this is end of pattern, must already know about west */
/* count the number of -'s */
for (width
= 0; *p
== '-'; ++p
, ++width
)
if (maxi
> 0 && !(where
& EAST_EDGE
))
/* if this is end of pattern, must already know about west */
if (maxi
> 0 && width
!= maxj
)
/* get here => its a real pattern entry,
* rather than a north/south constraint
/* we have a pattern line - add it into the current pattern */
/* if this is not the first line, or if there is a north
* constraint, we should already know about it
if (!(where
& WEST_EDGE
) && ((where
& NORTH_EDGE
) || maxi
> 0))
/* we should already know about this constraint */
else if (where
& WEST_EDGE
)
/* need a | if we are already constrained to west */
(char_offset
= strchr(VALID_PATTERN_CHARS
, *p
)) != NULL
;
/* char_offset is a pointer within the VALID_PATTERN_CHARS string.
* so (char-VALID_PATTERN_CHARS) is the att (0 to 11) to write to the
/* one of ATT_* - see above */
int off
= char_offset
- VALID_PATTERN_CHARS
;
continue; /* boring - pad character */
if (off
== ATT_a
) /* this were used by halfeye patterns */
off
= ATT_dot
; /* add a '.' to the pattern instead */
pattern
[patno
].anchored_at_X
= (off
== ATT_X
) ? 3 : 0;
/*FIXME: Make sure O is valid anchor*/
pattern
[patno
].anchored_at_X
= (off
== ATT_X
) ? 3 : 0;
/*FIXME: Make sure X is valid anchor*/
if ((ci
== -1) && (off
< 3) && ((off
& anchor
) != 0)
/* Use this position as the pattern origin. */
pattern
[patno
].anchored_at_X
= (off
== ATT_X
) ? 3 : 0;
if (el
>= (int) (sizeof(elements
) / sizeof(elements
[0])))
elements
[el
].att
= off
; /* '*' mapped to '.' and 'Q' to 'O' above */
/* if this is not the first line, or if there is a north
* constraint, we should already know about it
if (!(where
& EAST_EDGE
) && ((where
& NORTH_EDGE
) || maxi
> 0))
goto fatal
; /* we should already know about this constraint */
else if (where
& EAST_EDGE
)
goto fatal
; /* need a | if we are already constrained to east */
if (maxi
> 0 && j
!= maxj
)
strncpy(diagram
[maxi
], pcopy
, maxj
+ jwo
+ jeo
);
return maxi
<= MAX_BOARD
&& maxj
<= MAX_BOARD
;
fprintf(stderr
, "%s(%d) : error : Illegal pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
fprintf(stderr
, "%s(%d) : error : Pattern %s not rectangular\n",
current_file
, current_line_number
, pattern_names
[patno
]);
* We've just read a line that looks like a constraint pattern line.
read_constraint_diagram_line(char *p
)
/* North or south boundary, no letter to be found. */
if (*p
== '+' || *p
== '-')
/* Skip west boundary. */
strchr(VALID_PATTERN_CHARS
, *p
) || strchr(VALID_CONSTRAINT_LABELS
, *p
);
if (strchr(VALID_CONSTRAINT_LABELS
, *p
)
&& label_coords
[(int)*p
][0] == -1) {
/* New constraint letter */
label_coords
[(int)*p
][0] = current_c_i
;
label_coords
[(int)*p
][1] = j
;
/* Now j holds the width of this constraint diagram line. Require
* this to match the main diagram width stored in maxj. However,
* maxj was modified by find_extents() so we have to compensate for
if (j
!= maxj
+ 1 && !discard_pattern
) {
fprintf(stderr
, "%s(%d) : error : Mismatching width of constraint line in pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
strncpy(constraint_diagram
[current_c_i
], pcopy
, maxj
+jwo
+jeo
+1);
/* Check that the constraint diagram had the same number of rows as
check_constraint_diagram_size(void)
if (current_c_i
!= maxi
+ 1 && !discard_pattern
) {
fprintf(stderr
, "%s(%d) : error : Mismatching height of constraint diagram in pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
convert_attribute_labels_to_offsets(void)
struct pattern_attribute
*attribute
;
if (patno
< 0 || !pattern
[patno
].attributes
)
for (attribute
= pattern
[patno
].attributes
;
attribute
->type
!= LAST_ATTRIBUTE
;
if (attribute
->type
>= FIRST_OFFSET_ATTRIBUTE
) {
int label
= attribute
->offset
;
if (label_coords
[label
][0] == -1) {
"%s(%d) : error : Pattern attribute uses label '%c' that wasn't specified in the diagram\n",
current_file
, current_line_number
, label
);
TRANSFORM2(label_coords
[label
][0] - ci
- movei
,
label_coords
[label
][1] - cj
- movej
, &x
, &y
,
attribute
->offset
= OFFSET(x
, y
);
/* On reading a line starting ':', finish up and write
* out the current pattern
finish_pattern(char *line
)
/* end of pattern layout */
char symmetry
; /* the symmetry character */
mini
= minj
= 0; /* initially : can change with edge-constraints */
if (num_stars
> 1 || (database_type
!= DB_CONNECTIONS
&& num_stars
== 0)) {
fprintf(stderr
, "%s(%d) : error : No or too many *'s in pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
if (database_type
== DB_CORNER
) {
else if (choose_best_anchor
&& !discard_pattern
) {
/* Try to find a better anchor if
/* We seek the element of suitable value minimizing
* the distance to the middle.
mj
= (maxj
- 1) * 50 - 1;
for (k
= 0; k
!= el
; k
++)
if (elements
[k
].att
< 3 && (elements
[k
].att
& anchor
) != 0) {
d
= gg_abs(100 * elements
[k
].x
- mi
)
+ gg_abs(100 * elements
[k
].y
- mj
);
pattern
[patno
].anchored_at_X
= (elements
[min_k
].att
== ATT_X
) ? 3 : 0;
else if ((ci
== -1 || cj
== -1) && !discard_pattern
) {
fprintf(stderr
, "%s(%d) : No origin for pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
/* translate posn of * (or Q) to relative co-ords
else if (num_stars
== 0) {
TRANSFORM2(movei
, movej
, &x
, &y
, transformation_hint
);
pattern
[patno
].move_offset
= OFFSET(x
, y
);
pattern
[patno
].patlen
= el
;
/* Now parse the line. Only the symmetry character and the class
* field are mandatory. The compiler guarantees that all the fields
* are already initialized to 0.
class[0] = 0; /* in case sscanf doesn't get that far */
s
= sscanf(p
, ":%c,%[^,]%n", &symmetry
, class, &n
);
fprintf(stderr
, ": line must contain symmetry character and class\n");
pattern
[patno
].attributes
= NULL
;
while (sscanf(p
, "%*[, ]%[^,]%n", entry
, &n
) > 0) {
paren
= strchr(entry
, '(');
struct attribute_description
*description
= NULL
;
for (description
= attribute_map
; description
->input_name
;
if (strncmp(entry
, description
->input_name
, paren
- entry
) == 0) {
if (description
->type
!= IN_PATTERN_VALUE
) {
if (!pattern
[patno
].attributes
)
pattern
[patno
].attributes
= attributes
+ num_attributes
;
attributes
[num_attributes
].type
= description
->type
;
if (description
->type
>= FIRST_OFFSET_ATTRIBUTE
) {
/* We store the label for now, since we don't know
* its offset without having seen the constraint
&& !strchr(VALID_CONSTRAINT_LABELS
, *(paren
+ 1))) {
fprintf(stderr
, "%s(%d) : error : '%c' is not a valid label.\n",
current_file
, current_line_number
, *(paren
+ 1));
attributes
[num_attributes
].offset
= *(paren
+ 1);
sscanf(paren
+ 1, "%f", &attributes
[num_attributes
].value
);
sscanf(paren
+ 1, "%f", &pattern
[patno
].value
);
if (!strchr(paren
+ 1, ')')) {
fprintf(stderr
, "%s(%d) : error : ')' missed\n",
current_file
, current_line_number
);
if (attribute_map
== NULL
|| description
->input_name
== NULL
) {
fprintf(stderr
, "%s(%d) : error : Unknown value field: %s\n",
current_file
, current_line_number
, entry
);
strncpy(helper_fn_names
[patno
], entry
, 79);
if (pattern
[patno
].attributes
!= NULL
) {
attributes
[num_attributes
].type
= LAST_ATTRIBUTE
;
attributes
[num_attributes
].value
= 0.0;
for (p2
= class; *p2
; p2
++) {
case 's': pattern
[patno
].class |= CLASS_s
; break;
case 'O': pattern
[patno
].class |= CLASS_O
; break;
case 'o': pattern
[patno
].class |= CLASS_o
; break;
case 'X': pattern
[patno
].class |= CLASS_X
; break;
case 'x': pattern
[patno
].class |= CLASS_x
; break;
case 'D': pattern
[patno
].class |= CLASS_D
; break;
case 'C': pattern
[patno
].class |= CLASS_C
; break;
case 'c': pattern
[patno
].class |= CLASS_c
; break;
case 'n': pattern
[patno
].class |= CLASS_n
; break;
case 'B': pattern
[patno
].class |= CLASS_B
; break;
case 'A': pattern
[patno
].class |= CLASS_A
; break;
case 'b': pattern
[patno
].class |= CLASS_b
; break;
case 'e': pattern
[patno
].class |= CLASS_e
; break;
case 'E': pattern
[patno
].class |= CLASS_E
; break;
case 'a': pattern
[patno
].class |= CLASS_a
; break;
case 'd': pattern
[patno
].class |= CLASS_d
; break;
case 'I': pattern
[patno
].class |= CLASS_I
; break;
case 'J': pattern
[patno
].class |= CLASS_J
; break;
case 'j': pattern
[patno
].class |= CLASS_j
; break;
case 't': pattern
[patno
].class |= CLASS_t
; break;
case 'T': pattern
[patno
].class |= CLASS_T
; break;
case 'U': pattern
[patno
].class |= CLASS_U
; break;
case 'W': pattern
[patno
].class |= CLASS_W
; break;
case 'F': pattern
[patno
].class |= CLASS_F
; break;
case 'N': pattern
[patno
].class |= CLASS_N
; break;
case 'Y': pattern
[patno
].class |= CLASS_Y
; break;
"%s(%d) : error : Unknown classification letter %c. (pattern %s).\n",
current_file
, current_line_number
, *p2
,
/* Now get the symmetry. There are extra checks we can make to do with
* square-ness and edges. We do this before we work out the edge constraints,
* since that mangles the size info.
if (where
& (NORTH_EDGE
| EAST_EDGE
| SOUTH_EDGE
| WEST_EDGE
))
"%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
pattern
[patno
].trfno
= 2;
if (where
& (NORTH_EDGE
| EAST_EDGE
| SOUTH_EDGE
| WEST_EDGE
))
"%s(%d) : Warning : X symmetry inconsistent with edge constraints (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
"%s(%d) : Warning : X symmetry requires a square pattern (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
pattern
[patno
].trfno
= 2;
if (where
& (NORTH_EDGE
| SOUTH_EDGE
))
"%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
pattern
[patno
].trfno
= 4;
if (where
& (EAST_EDGE
| WEST_EDGE
))
"%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
pattern
[patno
].trfno
= 4;
/* FIXME: Can't be bothered putting in the edge tests.
"%s(%d) : Warning : \\ or / symmetry requires a square pattern (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
pattern
[patno
].trfno
= 4;
if (where
& (NORTH_EDGE
| EAST_EDGE
| SOUTH_EDGE
| WEST_EDGE
))
"%s(%d) : Warning : symmetry inconsistent with edge constraints (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
pattern
[patno
].trfno
= 5; /* Ugly special convention. */
"%s(%d) : Warning : symmetry character '%c' not implemented - using '8' (pattern %s)\n",
current_file
, current_line_number
, symmetry
, pattern_names
[patno
]);
pattern
[patno
].trfno
= 8;
read_constraint_line(char *line
)
/* Avoid buffer overrun. */
assert(strlen(constraint
) + strlen(line
) < MAXCONSTRAINT
);
/* Append the new line. */
strcat(constraint
, line
);
pattern
[patno
].autohelper_flag
|= HAVE_CONSTRAINT
;
read_action_line(char *line
)
/* Avoid buffer overrun. */
assert(strlen(action
) + strlen(line
) < MAXACTION
);
/* Append the new line. */
pattern
[patno
].autohelper_flag
|= HAVE_ACTION
;
generate_autohelper_code(int funcno
, int number_of_params
, int *labels
)
char varnames
[MAXPARAMS
][8];
char pattern_id
[MAXLINE
];
for (i
= 0; i
< number_of_params
; i
++) {
if (labels
[i
] == (int) '*')
sprintf(varnames
[i
], "move");
/* The special label '?' represents a tenuki. We replace this
else if (labels
[i
] == (int) '?')
sprintf(varnames
[i
], "NO_MOVE");
sprintf(varnames
[i
], "%c", labels
[i
]);
switch (autohelper_functions
[funcno
].type
) {
/* A common case. Just use the labels as parameters. */
switch (number_of_params
) {
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
);
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
varnames
[0], varnames
[1]);
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
varnames
[0], varnames
[1], varnames
[2]);
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
varnames
[0], varnames
[1], varnames
[2],
fprintf(stderr
, "%s(%d) : error : unknown number of parameters (pattern %s)",
current_file
, current_line_number
, pattern_names
[patno
]);
/* This is a very special case where there is assumed to be a
* variable number of parameters and these constitute a series of
* moves to make followed by a final attack or defense test.
if (number_of_params
< autohelper_functions
[funcno
].params
) {
fprintf(stderr
, "%s(%d) : error : too few parameters (pattern %s)",
current_file
, current_line_number
, pattern_names
[patno
]);
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
number_of_params
- autohelper_functions
[funcno
].params
);
for (i
= 0; i
< number_of_params
; i
++)
code_pos
+= sprintf(code_pos
, ", %s", varnames
[i
]);
*code_pos
++ = ')'; /* Close parenthesis. */
/* A very special case. We add the address of the current pattern
* before the actual parameters. So far, used only by `value'.
sprintf(pattern_id
, "(%s + %d)", prefix
, patno
);
switch (number_of_params
) {
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
pattern_id
, varnames
[0]);
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
pattern_id
, varnames
[0], varnames
[1]);
code_pos
+= sprintf(code_pos
, autohelper_functions
[funcno
].code
,
pattern_id
, varnames
[0], varnames
[1], varnames
[2]);
fprintf(stderr
, "%s(%d) : error : unknown number of parameters (pattern %s)",
current_file
, current_line_number
, pattern_names
[patno
]);
/* Parse the constraint and generate the corresponding helper code.
* We use a small state machine.
parse_constraint_or_action(char *line
, float *cost
)
int N
= sizeof(autohelper_functions
)/sizeof(struct autohelper_func
);
int number_of_params
= 0;
case 0: /* Looking for a token, echoing other characters. */
for (n
= 0; n
< N
; n
++) {
if (strncmp(p
, autohelper_functions
[n
].name
,
strlen(autohelper_functions
[n
].name
)) == 0) {
p
+= strlen(autohelper_functions
[n
].name
)-1;
*cost
+= autohelper_functions
[n
].cost
* cost_factor
;
if (state
== 0 && *p
!= '\n')
case 1: /* Token found, now expect a '('. */
if (autohelper_functions
[n
].params
== 0) {
/* We allow to omit parenthesis when using functions which
* have no parameters. In any case, you may still place them,
* but having to write `value() = 50' is disgusting.
generate_autohelper_code(n
, 0, NULL
);
"%s(%d) : error : Syntax error in constraint or action, '(' expected (pattern %s, autohelper %s).\n",
current_file
, current_line_number
, pattern_names
[patno
],
autohelper_functions
[n
].name
);
assert(autohelper_functions
[n
].params
<= MAXPARAMS
);
if (autohelper_functions
[n
].params
!= 0
|| autohelper_functions
[n
].type
== 1)
case 2: /* Time for a label. */
if ((*p
!= '*') && (*p
!= '?') && !strchr(VALID_CONSTRAINT_LABELS
, *p
)) {
"%s(%d) : error : '%c' is not allowed as a constraint label.\n",
current_file
, current_line_number
, *p
);
"%s(%d) : error : Syntax error in constraint or action, label expected, found '%c'.\n",
current_file
, current_line_number
, *p
);
if ((*p
== '?') && (number_of_params
== 0)) {
"mkpat: tenuki (?) cannot be the first label (pattern %s)\n", pattern_names
[patno
]);
/* The special label '?' represents a tenuki. */
if (*p
!= '*' && *p
!= '?' && label_coords
[label
][0] == -1) {
"%s(%d) : error : The constraint or action uses a label (%c) that wasn't specified in the diagram (pattern %s).\n",
current_file
, current_line_number
, label
, pattern_names
[patno
]);
labels
[number_of_params
] = label
;
case 3: /* A ',' or a ')'. */
if (autohelper_functions
[n
].type
!= 1
&& number_of_params
== autohelper_functions
[n
].params
) {
"%s(%d) : error : Syntax error in constraint or action, ')' expected (pattern %s).\n",
current_file
, current_line_number
, pattern_names
[patno
]);
if (number_of_params
== MAXPARAMS
) {
"Error in constraint or action, too many parameters. (pattern %s).\n",
"%s(%d) : error : Syntax error in constraint or action, ',' or ')' expected (pattern %s).\n",
current_file
, current_line_number
, pattern_names
[patno
]);
else { /* a closing parenthesis */
if ((autohelper_functions
[n
].type
!= 1)
&& (number_of_params
< autohelper_functions
[n
].params
)) {
"%s(%d) : error : Syntax error in constraint or action, %s() requires %d parameters.\n",
current_file
, current_line_number
, autohelper_functions
[n
].name
, autohelper_functions
[n
].params
);
generate_autohelper_code(n
, number_of_params
, labels
);
"%s(%d) : error : Internal error in parse_constraint_or_action(), unknown state.\n",
current_file
, current_line_number
);
/* Finish up a constraint and/or action and generate the automatic
* helper code. The constraint text is in the global variable
finish_constraint_and_action(void)
int have_constraint
= (pattern
[patno
].autohelper_flag
& HAVE_CONSTRAINT
);
int have_action
= (pattern
[patno
].autohelper_flag
& HAVE_ACTION
);
/* Mark that this pattern has an autohelper. */
pattern
[patno
].autohelper
= dummyhelper
;
/* Generate autohelper function declaration. */
code_pos
+= sprintf(code_pos
,
"static int\nautohelper%s%d(int trans, int move, int color, int action)\n{\n int",
/* Generate variable declarations. */
for (i
= 0; i
< sizeof(VALID_CONSTRAINT_LABELS
); i
++) {
int c
= (int) VALID_CONSTRAINT_LABELS
[i
];
if (label_coords
[c
][0] != -1)
code_pos
+= sprintf(code_pos
, " %c,", c
);
/* Replace the last ',' with ';' */
if (*(code_pos
-1) == ',')
code_pos
-= 3; /* no variable, erase "int" */
code_pos
+= sprintf(code_pos
, "UNUSED(trans);");
/* Include UNUSED statements for two parameters */
code_pos
+= sprintf(code_pos
, "\n UNUSED(color);\n");
if (!have_constraint
|| !have_action
)
code_pos
+= sprintf(code_pos
, " UNUSED(action);\n");
/* Generate coordinate transformations. */
for (i
= 0; i
< sizeof(VALID_CONSTRAINT_LABELS
); i
++) {
int c
= (int) VALID_CONSTRAINT_LABELS
[i
];
if (label_coords
[c
][0] != -1) {
TRANSFORM2(label_coords
[c
][0] - ci
- movei
,
label_coords
[c
][1] - cj
- movej
, &x
, &y
,
code_pos
+= sprintf(code_pos
,
"\n %c = AFFINE_TRANSFORM(%d, trans, move);",
/* move might be unused. Add an UNUSED statement to avoid warnings. */
code_pos
+= sprintf(code_pos
, "\n UNUSED(move);");
code_pos
+= sprintf(code_pos
, "\n\n");
if (have_constraint
&& have_action
)
code_pos
+= sprintf(code_pos
, " if (!action)\n ");
code_pos
+= sprintf(code_pos
, " return ");
parse_constraint_or_action(constraint
, &cost
);
pattern
[patno
].constraint_cost
= cost
;
code_pos
+= sprintf(code_pos
, ";\n");
code_pos
+= sprintf(code_pos
, " ");
parse_constraint_or_action(action
, &cost
);
code_pos
+= sprintf(code_pos
, ";\n");
code_pos
+= sprintf(code_pos
, "\n return 0;\n");
code_pos
+= sprintf(code_pos
, "}\n\n");
/* Check that we have not overrun our buffer. That would be really bad. */
assert(code_pos
<= autohelper_code
+ sizeof(autohelper_code
));
/* ================================================================ */
/* stuff to write the elements of a pattern */
/* ================================================================ */
/* callback function used to sort the elements in a pattern.
* We want to sort the patterns by attribute.
* so that cheaper / more specific tests are done early on
* For connections mode, the inhibition points (7)
compare_elements(const void *a
, const void *b
)
/* score for each attribute */
static int order
[] = {7, 2, 3, 5, 6, 0, 4, 1};
return order
[((const struct patval_b
*)a
)->att
]
- order
[((const struct patval_b
*)b
)->att
];
struct element_node
*next
;
/* flush out the pattern stored in elements[]. Don't forget
* that elements[].{x,y} and min/max{i,j} are still relative
* to the top-left corner of the original ascii pattern, and
* not relative to the anchor stone ci,cj
write_elements(FILE *outfile
)
assert(ci
!= -1 && cj
!= -1);
assert(database_type
== DB_DFA
|| transformation_hint
== 0);
/* sort the elements so that least-likely elements are tested first. */
gg_sort(elements
, el
, sizeof(struct patval_b
), compare_elements
);
fprintf(outfile
, "static struct patval %s%d[] = {", prefix
, patno
);
for (node
= 0; node
< el
; node
++) {
int x
= elements
[node
].x
;
int y
= elements
[node
].y
;
int att
= elements
[node
].att
;
assert(x
>= mini
&& y
>= minj
);
if (!(x
<= maxi
&& y
<= maxj
)) {
"%s(%d) : error : Maximum number of elements exceeded in %s.\n",
current_file
, current_line_number
, prefix
);
/* Check if this element is not needed for goal checking and by
* callback function. Also, check that pattern class doesn't
* require dragon status checking on it.
if ((fixed_anchor
|| nongoal
[att
]) && callback_unneeded
[att
]
&& (((pattern
[patno
].class & (CLASS_X
| CLASS_x
)) == 0)
|| (att
!= ATT_X
&& att
!= ATT_x
))
&& (((pattern
[patno
].class & (CLASS_O
| CLASS_o
)) == 0)
|| (att
!= ATT_O
&& att
!= ATT_o
))) {
/* Now check that pattern matcher won't need the element for
/* If we do grid optimization, we can avoid matching 9 pattern elements
* around its anchor (the 9 elements are the intersection of 16 grid
* elements for all possible transformations).
if ((database_type
== DB_GENERAL
|| database_type
== DB_CONNECTIONS
)
&& ci
-1 <= x
&& x
<= ci
+1 && cj
-1 <= y
&& y
<= cj
+1)
#endif /* GRID_OPT == 1 */
/* DFA pattern matcher doesn't itself need these elements at all. But
* they might be needed for goal checking or by callback function, so
* we check it before discarding an element.
if (database_type
== DB_DFA
)
} /* If the element is discardable. */
fprintf(outfile
, used_nodes
% 4 ? "\t" : "\n ");
TRANSFORM2(x
- ci
, y
- cj
, &dx
, &dy
, transformation_hint
);
fprintf(outfile
, "{%d,%d}", OFFSET(dx
, dy
), att
);
/* This may happen if we have discarded all
* the elements as unneeded by the matcher.
fprintf(outfile
, "{0,-1}}; /* Dummy element, not used. */\n\n");
fprintf(outfile
, "\n};\n\n");
pattern
[patno
].patlen
= used_nodes
;
/* ================================================================ */
/* Corner database creation stuff */
/* ================================================================ */
#define CORNER_DB_SIZE(patterns, variations)\
((int) ((patterns * sizeof(struct corner_pattern)\
+ variations * sizeof(struct corner_variation)) / 1024))
static struct corner_variation_b corner_root
;
static int second_corner_offset
[MAXPATNO
];
static int total_variations
= 0;
static int variations_written
= 0;
static int corner_max_width
= 0;
static int corner_max_height
= 0;
/* This structure is used by corner_add_pattern() */
/* Initialize corner variation tree. */
corner_root
.num_variations
= 0;
corner_root
.child
= NULL
;
/* corner_best_element() chooses the best element from supplied. The best
* element is always selected from those which don't have other elements
* +------ E.g. elements A and B are candidates to become the best
* |...... element, while C and D are not (A is closer to the corner
* |..A... than both C and D). Note that A and B are at "incomparable"
* |..C.D. distances from the corner, since their coordinates are in
* |.B.... opposite relations.
* If there are existing variations among candidates, all other candidates
* are automatically rejected. Once we have a set of candidates, we select
* the best candidate as the one which has the best parameters (in order
* of decreasing importance):
* 1) maximal "corner area" (see function code);
* 2) minimal distance to the corner bisector;
* 3) those which x coordinate is smaller than y one
* The purpose of this function is to reduce size of variation tree (by
* selecting similar variations) and allow rejecting variations as
* quickly as possible (based on num_stones field value). The latter
* can still be improved if a need arises.
corner_best_element(struct corner_element
*el
, int n
,
struct corner_variation_b
*variations
, int color
)
int candidate
[MAX_BOARD
* MAX_BOARD
];
int existing_variation
[MAX_BOARD
* MAX_BOARD
];
int have_existing_variation
= 0;
/* Find candidates and mark those which correspond to existing variations. */
for (k
= 0; k
< n
; k
++) {
for (i
= 0; i
< n
; i
++) {
if (el
[k
].x
>= el
[i
].x
&& el
[k
].y
>= el
[i
].y
)
struct corner_variation_b
*v
;
int move_offset
= OFFSET(el
[k
].x
, el
[k
].y
);
int xor_att
= (el
[k
].color
== color
? ATT_O
^ ATT_O
: ATT_O
^ ATT_X
);
candidate
[candidates
] = k
;
existing_variation
[candidates
] = 0;
for (v
= variations
; v
!= NULL
; v
= v
->next
) {
if (v
->move_offset
== move_offset
&& (v
->xor_att
== xor_att
|| color
== 0)) {
existing_variation
[candidates
] = 1;
have_existing_variation
= 1;
/* Select the best move. */
for (k
= 0; k
< candidates
; k
++) {
int value
= 2 * MAX_BOARD
* (el
[m
].x
+ 1) * (el
[m
].y
+ 1)
- 2 * gg_abs(el
[m
].x
- el
[m
].y
) + (el
[m
].x
< el
[m
].y
? 1 : 0);
if (existing_variation
[k
] == have_existing_variation
/* Dynamically allocates a new variation structure. */
static struct corner_variation_b
*
corner_variation_new(int move_offset
, signed char xor_att
,
unsigned char num_stones
)
struct corner_variation_b
*variation
;
variation
= malloc(sizeof(*variation
));
variation
->move_offset
= move_offset
;
variation
->xor_att
= xor_att
;
variation
->num_stones
= num_stones
;
variation
->num_variations
= 0;
variation
->child_num
= -1;
variation
->pattern_num
= -1;
/* Follow a variation. If such a variation already exists, returns
* a pointer to it. Otherwise, creates and initializes a new one.
static struct corner_variation_b
*
corner_follow_variation(struct corner_variation_b
*variation
,
int offset
, int color
, unsigned char num_stones
)
signed char xor_att
= color
? ATT_O
^ ATT_O
: ATT_O
^ ATT_X
;
struct corner_variation_b
*subvariation
= variation
->child
;
struct corner_variation_b
**link
= &(variation
->child
);
if (subvariation
->move_offset
== offset
&& subvariation
->xor_att
== xor_att
) {
assert(subvariation
->num_stones
== num_stones
);
link
= &(subvariation
->next
);
subvariation
= subvariation
->next
;
variation
->num_variations
++;
*link
= corner_variation_new(offset
, xor_att
, num_stones
);
/* Adds a pattern to corner database. */
struct corner_element stone
[MAX_BOARD
* MAX_BOARD
];
unsigned char num_stones
;
struct corner_variation_b
*variation
= &corner_root
;
/* Check if we have a corner pattern and select appropriate transformation. */
case SOUTH_EDGE
| WEST_EDGE
: trans
= 5; corner_x
= maxi
; break;
case WEST_EDGE
| NORTH_EDGE
: trans
= 0; break;
case NORTH_EDGE
| EAST_EDGE
: trans
= 7; corner_y
= maxj
; break;
case EAST_EDGE
| SOUTH_EDGE
:
trans
= 2; corner_x
= maxi
; corner_y
= maxj
; break;
fprintf(stderr
, "%s(%d) : error : Illegal edge constraint in pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
move_pos
= AFFINE_TRANSFORM(pattern
[patno
].move_offset
- OFFSET_DELTA(corner_x
, corner_y
), trans
, POS(0, 0));
/* We need to transform labels as well. */
labels_transformation
= trans
;
/* Find all pattern elements. */
for (k
= 0; k
< el
; k
++) {
if (elements
[k
].att
== ATT_X
|| elements
[k
].att
== ATT_O
) {
TRANSFORM2(elements
[k
].x
, elements
[k
].y
,
&stone
[stones
].x
, &stone
[stones
].y
, trans
);
stone
[stones
].x
+= corner_x
;
stone
[stones
].y
+= corner_y
;
stone
[stones
].color
= elements
[k
].att
;
else if (elements
[k
].att
!= ATT_dot
) {
fprintf(stderr
, "%s(%d) : error : Illegal element in pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
for (k
= 0; k
< stones
; k
++) {
struct corner_element stone_t
;
best
= k
+ corner_best_element(stone
+ k
, stones
- k
, variation
->child
,
best
= corner_best_element(stone
, stones
, variation
->child
, color
);
if (stone
[0].x
> stone
[0].y
) {
/* To reduce number of variations, swap coordinates of all elements
* unless there is one with mirrored coordinates already.
for (i
= 1; i
< k
; i
++) {
if (stone
[i
].x
== stone
[0].y
&& stone
[i
].y
== stone
[0].x
)
for (i
= 0; i
< stones
; i
++) {
for (i
= 0; i
< k
; i
++) {
if (stone
[i
].x
<= stone
[k
].x
&& stone
[i
].y
<= stone
[k
].y
)
variation
= corner_follow_variation(variation
,
OFFSET(stone
[k
].x
, stone
[k
].y
), stone
[k
].color
== color
,
/* Finally, follow the pattern move as a variation. */
for (k
= 0; k
< stones
; k
++) {
if (stone
[k
].x
<= move_x
&& stone
[k
].y
<= move_y
)
variation
= corner_follow_variation(variation
, OFFSET(move_x
, move_y
),
ATT_O
== color
, num_stones
);
if (variation
->pattern_num
== -1) {
variation
->pattern_num
= patno
;
second_corner_offset
[patno
] = OFFSET(maxi
, maxj
);
if (maxi
> corner_max_height
)
corner_max_height
= maxi
;
if (maxj
> corner_max_width
)
fprintf(stderr
, "%s(%d) : warning : duplicated patterns encountered (%s and %s)\n",
current_file
, current_line_number
,
pattern_names
[variation
->pattern_num
], pattern_names
[patno
]);
/* Enumerates all variations so that we know addresses of subvariations
* when it is time to write them into a .c file.
corner_pack_variations(struct corner_variation_b
*variation
, int counter
)
counter
= corner_pack_variations(variation
->next
, counter
);
variation
->child_num
= counter
;
counter
= corner_pack_variations(variation
->child
, counter
);
/* Write variations recursively into an array. */
corner_write_variations(struct corner_variation_b
*variation
, FILE *outfile
)
fprintf(outfile
, " {%d,%d,%d,%d,", variation
->move_offset
,
variation
->xor_att
, variation
->num_stones
,
variation
->num_variations
);
fprintf(outfile
, "&%s_variations[%d],", prefix
, variation
->child_num
);
fprintf(outfile
, "NULL,");
if (variation
->pattern_num
!= -1)
fprintf(outfile
, "&%s[%d]", prefix
, variation
->pattern_num
);
fprintf(outfile
, "NULL");
if (variations_written
!= total_variations
)
fprintf(outfile
, "},\n");
fprintf(outfile
, "}\n};\n\n\n");
corner_write_variations(variation
->next
, outfile
);
corner_write_variations(variation
->child
, outfile
);
write_attributes(FILE *outfile
)
fprintf(outfile
, "static struct pattern_attribute attributes[] = {\n");
fprintf(outfile
, "#ifdef HAVE_TRANSPARENT_UNIONS\n");
for (k
= 0; k
< num_attributes
; k
++) {
fprintf(outfile
, " {%s,", attribute_name
[attributes
[k
].type
]);
if (attributes
[k
].type
>= FIRST_OFFSET_ATTRIBUTE
)
fprintf(outfile
, "{.offset=%d}}", attributes
[k
].offset
);
fprintf(outfile
, "{.value=%f}}", attributes
[k
].value
);
if (k
!= num_attributes
- 1)
fprintf(outfile
, "\n#else\n");
for (k
= 0; k
< num_attributes
; k
++) {
fprintf(outfile
, " {%s,", attribute_name
[attributes
[k
].type
]);
if (attributes
[k
].type
>= FIRST_OFFSET_ATTRIBUTE
)
fprintf(outfile
, "0.0,%d}", attributes
[k
].offset
);
fprintf(outfile
, "%f,0}", attributes
[k
].value
);
if (k
!= num_attributes
- 1)
fprintf(outfile
, "\n#endif\n};\n\n");
/* Sort and write out the patterns. */
write_patterns(FILE *outfile
)
/* Write out the patterns. */
if (database_type
== DB_CORNER
)
fprintf(outfile
, "static struct corner_pattern %s[] = {\n", prefix
);
fprintf(outfile
, "static struct pattern %s[] = {\n", prefix
);
for (j
= 0; j
< patno
; ++j
) {
struct pattern
*p
= pattern
+ j
;
if (database_type
== DB_CORNER
) {
fprintf(outfile
, " {%d,%d,0x%x,\"%s\",",
second_corner_offset
[j
], (p
->trfno
== 4),
p
->class, pattern_names
[j
]);
fprintf(outfile
, "attributes+%d,",
(int) (p
->attributes
? p
->attributes
- attributes
: 0));
fprintf(outfile
, "NULL,");
fprintf(outfile
, "%d,", p
->autohelper_flag
);
fprintf(outfile
, "autohelper%s%d}", prefix
, j
);
fprintf(outfile
, "NULL}");
fprintf(outfile
, "\n};\n\n\n");
/* p->min{i,j} and p->max{i,j} are the maximum extent of the elements,
* including any rows of '?' which do not feature in the elements list.
* ie they are the positions of the topleft and bottomright corners of
* the pattern, relative to the pattern origin. These just transform same
fprintf(outfile
, " {%s%d,%d,%d, \"%s\",%d,%d,%d,%d,%d,%d,0x%x,%d",
p
->maxi
- p
->mini
, /* height */
p
->maxj
- p
->minj
, /* width */
fprintf(outfile
, ",\n {");
for (ll
= 0; ll
< 8; ++ll
)
fprintf(outfile
, " 0x%08x%s", p
->and_mask
[ll
], ll
< 7 ? "," : "");
fprintf(outfile
, "},\n {");
for (ll
= 0; ll
< 8; ++ll
)
fprintf(outfile
, " 0x%08x%s", p
->val_mask
[ll
], ll
< 7 ? "," : "");
fprintf(outfile
, "}\n ");
fprintf(outfile
, ", 0x%x,%f,", p
->class, p
->value
);
fprintf(outfile
, "attributes+%d,",
(int) (p
->attributes
? p
->attributes
- attributes
: 0));
fprintf(outfile
, "NULL,");
fprintf(outfile
, "%d,%s,", p
->autohelper_flag
, helper_fn_names
[j
]);
fprintf(outfile
, "autohelper%s%d", prefix
, j
);
fprintf(outfile
, "NULL");
fprintf(outfile
, ",%d", p
->anchored_at_X
);
fprintf(outfile
, ",%f", p
->constraint_cost
);
fprintf(outfile
, ",0,0");
fprintf(outfile
, "},\n");
if (database_type
== DB_CORNER
)
/* Add a final empty entry. */
fprintf(outfile
, " {NULL, 0,0,NULL,0,0,0,0,0,0,0,0");
fprintf(outfile
, ",{0,0,0,0,0,0,0,0},{0,0,0,0,0,0,0,0}");
fprintf(outfile
, ",0,0.0,NULL,0,NULL,NULL,0,0.0");
fprintf(outfile
, ",0,0,0");
fprintf(outfile
, "}\n};\n");
/* Write out the pattern db */
write_pattern_db(FILE *outfile
)
if (database_type
== DB_CORNER
) {
fprintf(outfile
, "struct corner_db %s_db = {\n", prefix
);
fprintf(outfile
, " %d,\n", corner_max_width
+ 1);
fprintf(outfile
, " %d,\n", corner_max_height
+ 1);
fprintf(outfile
, " %d,\n", corner_root
.num_variations
);
fprintf(outfile
, " %s_variations\n", prefix
);
fprintf(outfile
, "};\n");
/* Write out the pattern database. */
fprintf(outfile
, "struct pattern_db %s_db = {\n", prefix
);
fprintf(outfile
, " -1,\n"); /* fixed_for_size */
fprintf(outfile
, " %d,\n", fixed_anchor
);
fprintf(outfile
, " %s\n", prefix
);
if (database_type
== DB_DFA
)
fprintf(outfile
, " ,& dfa_%s\n", prefix
); /* pointer to the wired dfa */
fprintf(outfile
, " , NULL\n"); /* pointer to a possible dfa */
fprintf(outfile
, "};\n");
main(int argc
, char *argv
[])
static char stdin_name
[] = "<stdin>";
char *input_file_names
[MAX_INPUT_FILE_NAMES
];
char *output_file_name
= NULL
;
char *transformations_file_name
= NULL
;
FILE *input_FILE
= stdin
;
FILE *output_FILE
= stdout
;
FILE *transformations_FILE
= NULL
;
char *save_code_pos
= autohelper_code
;
int multiple_anchor_options
= 0;
/* Parse command-line options */
while ((i
= gg_getopt(argc
, argv
, "i:o:t:vV:pcfCDd:A:OXbma")) != EOF
) {
if (input_files
== MAX_INPUT_FILE_NAMES
) {
fprintf(stderr
, "Error : Too many input files (maximum %d supported)\n",
input_file_names
[input_files
++] = gg_optarg
;
case 'o': output_file_name
= gg_optarg
; break;
case 't': transformations_file_name
= gg_optarg
; break;
case 'v': verbose
= 1; break;
case 'V': dfa_verbose
= strtol(gg_optarg
, NULL
, 10); break;
fprintf(stderr
, "Error : More than one database type specified (-%c and -%c)\n",
iterations
= strtol(gg_optarg
, NULL
, 10);
fprintf(stderr
, "Error : Expected a non-negative number of iterations\n");
multiple_anchor_options
= 1;
multiple_anchor_options
= 1;
multiple_anchor_options
= 1;
fprintf(stderr
, "Warning : -m and -a are mutually exclusive.\n");
fprintf(stderr
, "Warning : -m and -a are mutually exclusive.\n");
fprintf(stderr
, "\b ; ignored\n");
database_type
= DB_GENERAL
;
input_file_names
[input_files
++] = stdin_name
;
if (output_file_name
&& database_type
!= OPTIMIZE_DFA
) {
output_FILE
= fopen(output_file_name
, "wb");
if (output_FILE
== NULL
) {
fprintf(stderr
, "Error : Cannot write to file %s\n", output_file_name
);
if (transformations_file_name
&& (database_type
== DB_DFA
|| database_type
== OPTIMIZE_DFA
)) {
transformations_FILE
= fopen(transformations_file_name
, "r");
if (transformations_FILE
) {
parse_transformations_file(transformations_FILE
);
fclose(transformations_FILE
);
else if (database_type
== DB_DFA
) {
fprintf(stderr
, "Error : Cannot read file %s\n",
transformations_file_name
);
if (multiple_anchor_options
)
fprintf(stderr
, "Warning : Multiple anchor options encountered. The last took precedence\n");
prefix
= argv
[gg_optind
];
if (database_type
== DB_DFA
) {
new_dfa(&dfa
, "mkpat's dfa");
else if (database_type
== DB_CORNER
)
if (database_type
== OPTIMIZE_DFA
) {
if (transformations_file_name
== NULL
) {
fprintf(stderr
, "error : transformation file required (use -t option)\n");
dfa_patterns_reset(&dfa_pats
);
fprintf(output_FILE
, PREAMBLE
);
/* Initialize pattern number and buffer for automatically generated
code_pos
= autohelper_code
;
attributes
[0].type
= LAST_ATTRIBUTE
;
/* Parse the input file, output pattern elements as as we find them,
* and store pattern entries for later output.
* We do this parsing too with the help of a state machine.
* 0 Waiting for a Pattern line.
* 1 Pattern line found, waiting for a position diagram.
* 2 Reading position diagram.
* 3 Waiting for entry line (":" line).
* 4 Waiting for optional constraint diagram.
* 5 Reading constraint diagram.
* 6 Waiting for constraint line (";" line).
* FIXME: This state machine should be possible to simplify.
for (ifc
= 0; ifc
< input_files
&& !fatal_errors
; ifc
++) {
char line
[MAXLINE
]; /* current line from file */
if (input_file_names
[ifc
] != stdin_name
) {
input_FILE
= fopen(input_file_names
[ifc
], "r");
if (input_FILE
== NULL
) {
fprintf(stderr
, "Error: Cannot open file %s\n", input_file_names
[ifc
]);
current_file
= input_file_names
[ifc
];
while (fgets(line
, MAXLINE
, input_FILE
)) {
char command_data
[MAXLINE
];
if (line
[strlen(line
)-1] != '\n') {
fprintf(stderr
, "%s(%d) : Error : line truncated (longer than %d characters)\n",
current_file
, current_line_number
, MAXLINE
- 1);
/* Remove trailing white space from `line' */
int i
= strlen(line
) - 2; /* Start removing backwards just before newline */
while (i
>= 0 && isspace((int) line
[i
]))
if (sscanf(line
, "Pattern %s", command_data
) == 1)
else if (sscanf(line
, "goal_elements %s", command_data
) == 1)
else if (sscanf(line
, "callback_data %s", command_data
) == 1)
else if (sscanf(line
, "attribute_map %s", command_data
) == 1)
char *p
= strpbrk(command_data
, " \t");
fprintf(stderr
, "%s(%d) : Warning: empty pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
fprintf(stderr
, "%s(%d) : Error : No entry line for pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
struct pattern_attribute
*attribute
= NULL
;
if (pattern
[patno
].attributes
) {
for (attribute
= pattern
[patno
].attributes
;
attribute
->type
!= LAST_ATTRIBUTE
;
if (attribute
->type
>= FIRST_OFFSET_ATTRIBUTE
)
if (attribute
== NULL
|| attribute
->type
== LAST_ATTRIBUTE
) {
"%s(%d) : Warning: constraint diagram but no constraint line or offset attributes for pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
finish_constraint_and_action();
check_constraint_diagram();
if (command
== 1) { /* Pattern `name' */
convert_attribute_labels_to_offsets();
code_pos
= save_code_pos
;
if (strlen(command_data
) > MAXNAME
- 1) {
fprintf(stderr
, "%s(%d) : Warning : pattern name is too long, truncated\n",
current_file
, current_line_number
);
command_data
[MAXNAME
- 1] = 0;
/* Somewhat inefficient, but doesn't take any real time
* given current database sizes.
for (k
= 0; k
< patno
; k
++) {
if (strcmp(pattern_names
[k
], command_data
) == 0) {
fprintf(stderr
, "%s(%d) : Warning : duplicate pattern name `%s'\n",
current_file
, current_line_number
, command_data
);
strcpy(pattern_names
[patno
], command_data
);
transformation_hint
= find_transformation_hint(pattern_names
[patno
]);
else if (command
== 2 || command
== 3) {
int *sort_out
= (command
== 2 ? nongoal
: callback_unneeded
);
if (strcmp(command_data
, "none")) {
for (c
= command_data
; *c
; c
++) {
if (command
== 2) { /* goal_elements */
case 'X': sort_out
[ATT_X
] = 0; break;
case 'O': sort_out
[ATT_O
] = 0; break;
case 'x': sort_out
[ATT_x
] = 0; break;
case 'o': sort_out
[ATT_o
] = 0; break;
fprintf(stderr
, "%s(%d) : Error : illegal character `%c'\n",
current_file
, current_line_number
, *c
);
struct attribute_description
*old_map
= attribute_map
;
if (strcmp(command_data
, "general") == 0) {
attribute_map
= general_attribute_map
;
else if (strcmp(command_data
, "value_only") == 0) {
attribute_map
= value_only_attribute_map
;
else if (strcmp(command_data
, "owl_attack") == 0) {
attribute_map
= owl_attack_attribute_map
;
else if (strcmp(command_data
, "owl_defense") == 0) {
attribute_map
= owl_defense_attribute_map
;
else if (strcmp(command_data
, "none") == 0) {
fprintf(stderr
, "%s(%d) : Error : unknown attribute map `%s'",
current_file
, current_line_number
, command_data
);
if (patno
!= -1 && attribute_map
!= old_map
) {
fprintf(stderr
, "%s(%d) : Error : attribute map can only be set before the first pattern\n",
current_file
, current_line_number
);
else if (line
[0] == '\n' || line
[0] == '#') {
if (state
== 2 || state
== 5) {
check_constraint_diagram_size();
else if (strchr(VALID_PATTERN_CHARS
, line
[0])
|| strchr(VALID_EDGE_CHARS
, line
[0])
|| strchr(VALID_CONSTRAINT_LABELS
, line
[0])) {
"%s(%d) : error : What, another diagram here? (pattern %s)\n",
current_file
, current_line_number
, pattern_names
[patno
]);
state
++; /* fall through */
if (!read_pattern_line(line
)) {
state
++; /* fall through */
read_constraint_diagram_line(line
);
else if (line
[0] == ':') {
if (state
== 2 || state
== 3) {
if (database_type
== DB_DFA
|| database_type
== OPTIMIZE_DFA
)
if (database_type
== DB_CORNER
)
else if (database_type
!= OPTIMIZE_DFA
)
write_elements(output_FILE
);
save_code_pos
= code_pos
;
"%s(%d) : warning : Unexpected entry line in pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
else if (line
[0] == ';') {
check_constraint_diagram_size();
if (state
== 5 || state
== 6 || state
== 7) {
read_constraint_line(line
+1);
fprintf(stderr
, "%s(%d) : Warning: unexpected constraint line in pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
else if (line
[0] == '>') {
if (state
== 4 || state
== 5 || state
== 6
|| state
== 7 || state
== 8) {
check_constraint_diagram_size();
read_action_line(line
+1);
fprintf(stderr
, "Warning: unexpected action line in pattern %s\n",
line
[i
-1] = 0; /* Chop off \n */
fprintf(stderr
, "%s(%d) : error : Malformed line \"%s\" in pattern %s\n",
current_file
, current_line_number
, line
, pattern_names
[patno
]);
line
[i
-1] = c
; /* Put it back - maybe not necessary at this point. */
convert_attribute_labels_to_offsets();
fprintf(stderr
, "Warning: empty pattern %s\n",
fprintf(stderr
, "%s(%d) : Error : no entry line for pattern %s\n",
current_file
, current_line_number
, pattern_names
[patno
]);
"Warning: constraint diagram but no constraint line for pattern %s\n",
finish_constraint_and_action(); /* fall through */
check_constraint_diagram();
code_pos
= save_code_pos
;
fprintf(stderr
, "%d / %d patterns have edge-constraints\n",
pats_with_constraints
, patno
);
if (database_type
!= OPTIMIZE_DFA
) {
/* Forward declaration, which autohelpers might need. */
if (database_type
!= DB_CORNER
)
fprintf(output_FILE
, "static struct pattern %s[%d];\n\n", prefix
, patno
+ 1);
fprintf(output_FILE
, "static struct corner_pattern %s[%d];\n\n", prefix
, patno
+ 1);
/* Write the autohelper code. */
fprintf(output_FILE
, "%s", autohelper_code
);
write_attributes(output_FILE
);
assert(num_attributes
== 1);
write_patterns(output_FILE
);
if (database_type
== DB_DFA
) {
fprintf(stderr
, "---------------------------\n");
fprintf(stderr
, "DFA for %s\n", prefix
);
fprintf(stderr
, "size: %d kB for ", dfa_size(&dfa
));
fprintf(stderr
, "%d patterns", patno
);
fprintf(stderr
, " (%d states)\n", dfa
.last_state
);
strcpy(dfa
.name
, prefix
);
print_c_dfa(output_FILE
, prefix
, &dfa
);
fprintf(stderr
, "---------------------------\n");
if (DFA_MAX_MATCHED
/8 < dfa_calculate_max_matched_patterns(&dfa
))
fprintf(stderr
, "Warning: Increase DFA_MAX_MATCHED in 'dfa.h'.\n");
if (database_type
== DB_CORNER
) {
fprintf(stderr
, "---------------------------\n");
corner_pack_variations(corner_root
.child
, 0);
fprintf(output_FILE
, "static struct corner_variation %s_variations[] = {\n",
corner_write_variations(corner_root
.child
, output_FILE
);
fprintf(stderr
, "corner database for %s\n", prefix
);
fprintf(stderr
, "size: %d kB for %d patterns (%d variations)\n",
CORNER_DB_SIZE(patno
, total_variations
), patno
, total_variations
);
fprintf(stderr
, "---------------------------\n");
write_pattern_db(output_FILE
);
fprintf(output_FILE
, "\n#error: One or more fatal errors compiling %s\n",
else { /* database_type == OPTIMIZE_DFA */
int *optimized_variations
;
transformations_FILE
= fopen(transformations_file_name
, "wb");
if (transformations_FILE
== NULL
) {
fprintf(stderr
, "error : cannot write to file %s\n",
transformations_file_name
);
optimized_variations
= dfa_patterns_optimize_variations(&dfa_pats
,
for (k
= 0; k
< patno
; k
++) {
fprintf(transformations_FILE
, "%s\t%d\n", pattern_names
[k
],
optimized_variations
[k
]);
free(optimized_variations
);
return fatal_errors
? 1 : 0;