/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * 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. * \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include "gnugo.h" #include #include #include #include "liberty.h" #include "gg_utils.h" #include "patterns.h" #include "dfa.h" /**************************************************************************/ /* Pattern profiling functions: */ /**************************************************************************/ #if PROFILE_PATTERNS /* Initialize pattern profiling fields in one pattern struct array. */ static void clear_profile(struct pattern *pattern) { for (; pattern->patn; ++pattern) { pattern->hits = 0; pattern->reading_nodes = 0; pattern->dfa_hits = 0; } } #endif #if PROFILE_PATTERNS /* Print profiling information for one pattern struct array. */ static void print_profile(struct pattern *pattern, int *total_hits, int *total_nodes, int *total_dfa_hits) { for (; pattern->patn; ++pattern) if (pattern->hits > 0) { *total_hits += pattern->hits; *total_nodes += pattern->reading_nodes; *total_dfa_hits += pattern->dfa_hits; fprintf(stderr, "%6d %6d %9d %8.1f %s\n", pattern->dfa_hits, pattern->hits, pattern->reading_nodes, pattern->reading_nodes / (float) pattern->hits, pattern->name); } } #endif /* PROFILE_PATTERNS */ /* Initialize pattern profiling fields in pattern struct arrays. */ void prepare_pattern_profiling() { #if PROFILE_PATTERNS clear_profile(pat_db.patterns); clear_profile(attpat_db.patterns); clear_profile(defpat_db.patterns); clear_profile(endpat_db.patterns); clear_profile(conn_db.patterns); clear_profile(influencepat_db.patterns); clear_profile(barrierspat_db.patterns); clear_profile(aa_attackpat_db.patterns); clear_profile(owl_attackpat_db.patterns); clear_profile(owl_vital_apat_db.patterns); clear_profile(owl_defendpat_db.patterns); clear_profile(fusekipat_db.patterns); clear_profile(oracle_db.patterns); #else fprintf(stderr, "Warning, no support for pattern profiling in this binary.\n"); #endif } /* Report result of pattern profiling. Only patterns with at least one * match are listed. */ void report_pattern_profiling() { #if PROFILE_PATTERNS int hits = 0; int dfa_hits = 0; int nodes = 0; print_profile(pat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(attpat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(defpat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(endpat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(conn_db.patterns, &hits, &nodes, &dfa_hits); print_profile(influencepat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(barrierspat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(aa_attackpat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(owl_attackpat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(owl_vital_apat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(owl_defendpat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(fusekipat_db.patterns, &hits, &nodes, &dfa_hits); print_profile(oracle_db.patterns, &hits, &nodes, &dfa_hits); fprintf(stderr, "------ ---------\n"); fprintf(stderr, "%6d, %6d %9d\n", dfa_hits, hits, nodes); #endif } /**************************************************************************/ /* Standard matcher: */ /**************************************************************************/ /* Forward declarations. */ static void fixup_patterns_for_board_size(struct pattern *pattern); static void prepare_for_match(int color); static void do_matchpat(int anchor, matchpat_callback_fn_ptr callback, int color, struct pattern *database, void *callback_data, signed char goal[BOARDMAX]); static void matchpat_loop(matchpat_callback_fn_ptr callback, int color, int anchor, struct pattern_db *pdb, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal); /* Precomputed tables to allow rapid checks on the piece at * the board. This table relies on the fact that color is * 1 or 2. * * For pattern element i, require (board[pos] & andmask[i]) == valmask[i] * * .XO) For i=0,1,2, board[pos] & 3 is a no-op, so we check board[pos] * == valmask * x) For i=3, we are checking that board[pos] is not color, so AND * color and we get 0 for either empty or OTHER_COLOR, but color * if it contains color * o) Works the other way round for checking it is not X. * * * gcc allows the entries to be computed at run-time, but that is not ANSI. */ static const int and_mask[2][8] = { /* . X O x o , a ! color */ { 3, 3, 3, WHITE, BLACK, 3, 3, 3 }, /* BLACK */ { 3, 3, 3, BLACK, WHITE, 3, 3, 3 } /* WHITE */ }; static const int val_mask[2][8] = { { EMPTY, BLACK, WHITE, 0, 0, EMPTY, EMPTY, EMPTY}, /* BLACK */ { EMPTY, WHITE, BLACK, 0, 0, EMPTY, EMPTY, EMPTY} /* WHITE */ }; /* and a table for checking classes quickly * class_mask[status][color] contains the mask to look for in class. * ie. if pat[r].class & class_mask[dragon[pos].status][board[pos]] * is not zero then we reject it * Most elements if class_mask[] are zero - it is a sparse * matrix containing * CLASS_O in [DEAD][color] * CLASS_O in [CRITICAL][color] * CLASS_o in [ALIVE][color] * CLASS_X in [DEAD][other] * CLASS_x in [ALIVE][other] * * so eg. if we have a dead white dragon, and we * are checking a pattern for black, then * class_mask[DEAD][other] will contain CLASS_X * Then we reject any patterns which have CLASS_X * set in the class bits. * * Making it static guarantees that all fields are * initially set to 0, and we overwrite the ones * we care about each time. */ static unsigned int class_mask[NUM_DRAGON_STATUS][3]; /* In the current implementation, the edge constraints depend on * the board size, because we pad width or height out to the * board size. (This is because it is easy to find the corners * of the rotated pattern, but it is harder to transform the * bitmask of edge constraints.) * * But since version 1.103, board size is variable. Thus we * make a first pass through the table once we know the board * size. * * This should be called once for each pattern database. */ static void fixup_patterns_for_board_size(struct pattern *pattern) { for (; pattern->patn; ++pattern) if (pattern->edge_constraints != 0) { /* If the patterns have been fixed up for a different board size * earlier, we need to undo the modifications that were done * below before we do them anew. The first time this function is * called, this step is effectively a no-op. */ if (pattern->edge_constraints & NORTH_EDGE) pattern->maxi = pattern->mini + pattern->height; if (pattern->edge_constraints & SOUTH_EDGE) pattern->mini = pattern->maxi - pattern->height; if (pattern->edge_constraints & WEST_EDGE) pattern->maxj = pattern->minj + pattern->width; if (pattern->edge_constraints & EAST_EDGE) pattern->minj = pattern->maxj - pattern->width; /* we extend the pattern in the direction opposite the constraint, * such that maxi (+ve) - mini (-ve) = board_size-1 * Note : the pattern may be wider than the board, so * we need to be a bit careful ! */ if (pattern->edge_constraints & NORTH_EDGE) if (pattern->maxi < (board_size-1) + pattern->mini) pattern->maxi = (board_size-1) + pattern->mini; if (pattern->edge_constraints & SOUTH_EDGE) if (pattern->mini > pattern->maxi - (board_size-1)) pattern->mini = pattern->maxi - (board_size-1); if (pattern->edge_constraints & WEST_EDGE) if (pattern->maxj < (board_size-1) + pattern->minj) pattern->maxj = (board_size-1) + pattern->minj; if (pattern->edge_constraints & EAST_EDGE) if (pattern->minj > pattern->maxj - (board_size-1)) pattern->minj = pattern->maxj - (board_size-1); } } /* * prepare a pattern matching for color point of view */ static void prepare_for_match(int color) { int other = OTHER_COLOR(color); /* Basic sanity checks. */ gg_assert(color != EMPTY); /* If we set one of class_mask[XXX][color] and * class_mask[XXX][other], we have to explicitly set or reset the * other as well, since 'color' may change between calls. */ class_mask[DEAD][color] = CLASS_O; class_mask[DEAD][other] = CLASS_X; class_mask[CRITICAL][color] = CLASS_O; class_mask[CRITICAL][other] = 0; /* Need to reset this. */ class_mask[ALIVE][color] = CLASS_o; class_mask[ALIVE][other] = CLASS_x; } /* * Try all the patterns in the given array at (anchor). Invoke the * callback for any that matches. Classes X,O,x,o are checked here. It * is up to the callback to process the other classes, and any helper * or autohelper functions. * * If the support of goal[BOARDMAX] is a subset of the board, patterns * are rejected which do not involve this dragon. If goal is a null * pointer, this parameter is ignored. */ static void do_matchpat(int anchor, matchpat_callback_fn_ptr callback, int color, struct pattern *pattern, void *callback_data, signed char goal[BOARDMAX]) { const int anchor_test = board[anchor] ^ color; /* see below */ int m = I(anchor); int n = J(anchor); int merged_val; /* Basic sanity checks. */ ASSERT_ON_BOARD1(anchor); /* calculate the merged value around [m][n] for the grid opt */ { /* FIXME: Convert this to 2D (using delta[]) but be aware that you'll * also need to make corresponding changes in mkpat.c! */ int i, j; int shift = 30; merged_val = 0; for (i = m-1; i <= m+2; ++i) for (j = n-1; j <= n+2; shift -= 2, ++j) { unsigned int this; if (!ON_BOARD2(i, j)) this = 3; else if ((this = BOARD(i, j)) == 0) continue; else if (color == 2) this = OTHER_COLOR(this); merged_val |= (this << shift); } } /* Try each pattern - NULL pattern marks end of list. Assume at least 1 */ gg_assert(pattern->patn); do { /* * These days we always match all patterns. */ { int end_transformation; int ll; /* Iterate over transformations (rotations or reflections) */ int k; /* Iterate over elements of pattern */ int found_goal; /* We can check the color of the anchor stone now. * Roughly half the patterns are anchored at each * color, and since the anchor stone is invariant under * rotation, we can reject all rotations of a wrongly-anchored * pattern in one go. * * Patterns are always drawn from O perspective in .db, * so board[pos] is 'color' if the pattern is anchored * at O, or 'other' for X. * Since we require that this flag contains 3 for * anchored_at_X, we can check that * board[pos] == (color ^ anchored_at_X) * which is equivalent to * (board[pos] ^ color) == anchored_at_X) * and the LHS is something we precomputed. */ if (anchor_test != pattern->anchored_at_X) continue; /* does not match the anchor */ ll = 0; /* first transformation number */ end_transformation = pattern->trfno; /* Ugly trick for dealing with 'O' symmetry. */ if (pattern->trfno == 5) { ll = 2; end_transformation = 6; } /* try each orientation transformation. Assume at least 1 */ do { #if PROFILE_PATTERNS int nodes_before; #endif #if GRID_OPT == 1 /* We first perform the grid check : this checks up to 16 * elements in one go, and allows us to rapidly reject * patterns which do not match. While this check invokes a * necessary condition, it is not a sufficient test, so more * careful checks are still required, but this allows rapid * rejection. merged_val should contain a combination of * 16 board positions around m, n. The colours have been fixed * up so that stones which are 'O' in the pattern are * bit-pattern %01. */ if ((merged_val & pattern->and_mask[ll]) != pattern->val_mask[ll]) continue; /* large-scale match failed */ #endif /* GRID_OPT == 1 */ /* Next, we do the range check. This applies the edge * constraints implicitly. */ { int mi, mj, xi, xj; TRANSFORM2(pattern->mini, pattern->minj, &mi, &mj, ll); TRANSFORM2(pattern->maxi, pattern->maxj, &xi, &xj, ll); /* {min,max}{i,j} are the appropriate corners of the original * pattern, Once we transform, {m,x}{i,j} are still corners, * but we don't know *which* corners. * We could sort them, but it turns out to be cheaper * to just test enough cases to be safe. */ DEBUG(DEBUG_MATCHER, "---\nconsidering pattern '%s', rotation %d at %1m. Range %d,%d -> %d,%d\n", pattern->name, ll, anchor, mi, mj, xi, xj); /* now do the range-check */ if (!ON_BOARD2(m + mi, n + mj) || !ON_BOARD2(m + xi, n + xj)) continue; /* out of range */ } /* Now iterate over the elements of the pattern. */ found_goal = 0; for (k = 0; k < pattern->patlen; ++k) { /* match each point */ int pos; /* absolute coords of (transformed) pattern element */ int att = pattern->patn[k].att; /* what we are looking for */ /* Work out the position on the board of this pattern element. */ /* transform pattern real coordinate... */ pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor); ASSERT_ON_BOARD1(pos); /* ...and check that board[pos] matches (see above). */ if ((board[pos] & and_mask[color-1][att]) != val_mask[color-1][att]) goto match_failed; if (goal != NULL && board[pos] != EMPTY && goal[pos]) found_goal = 1; /* Check out the class_X, class_O, class_x, class_o * attributes - see patterns.db and above. */ if ((pattern->class & class_mask[dragon[pos].status][board[pos]]) != 0) goto match_failed; } /* loop over elements */ #if GRID_OPT == 2 /* Make sure the grid optimisation wouldn't have rejected this pattern */ ASSERT2((merged_val & pattern->and_mask[ll]) == pattern->val_mask[ll], m, n); #endif /* we don't trust the grid optimisation */ /* Make it here ==> We have matched all the elements to the board. */ if ((goal != NULL) && !found_goal) goto match_failed; #if PROFILE_PATTERNS pattern->hits++; nodes_before = stats.nodes; #endif /* A match! - Call back to the invoker to let it know. */ callback(anchor, color, pattern, ll, callback_data); #if PROFILE_PATTERNS pattern->reading_nodes += stats.nodes - nodes_before; #endif /* We jump to here as soon as we discover a pattern has failed. */ match_failed: DEBUG(DEBUG_MATCHER, "end of pattern '%s', rotation %d at %1m\n---\n", pattern->name, ll, anchor); } while (++ll < end_transformation); /* ll loop over symmetries */ } /* if not rejected by maxwt */ } while ((++pattern)->patn); /* loop over patterns */ } /* * Scan the board to get patterns anchored by anchor from color * point of view. * the board must be prepared by dfa_prepare_for_match(color) ! */ static void matchpat_loop(matchpat_callback_fn_ptr callback, int color, int anchor, struct pattern_db *pdb, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal) { int pos; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] == anchor && (!anchor_in_goal || goal[pos] != 0)) do_matchpat(pos, callback, color, pdb->patterns, callback_data, goal); } } /**************************************************************************/ /* DFA matcher: */ /**************************************************************************/ /* Set this to show the dfa board in action */ /* #define DFA_TRACE 1 */ /* Data. */ static int dfa_board_size = -1; static int dfa_p[DFA_BASE * DFA_BASE]; /* This is used by the EXPECTED_COLOR macro. */ static const int convert[3][4] = { {-1, -1, -1, -1}, /* not used */ {EMPTY, WHITE, BLACK, OUT_BOARD}, /* WHITE */ {EMPTY, BLACK, WHITE, OUT_BOARD} /* BLACK */ }; #define EXPECTED_COLOR(player_c, position_c) \ (convert[player_c][position_c]) /* Forward declarations. */ static void dfa_prepare_for_match(int color); static int scan_for_patterns(dfa_rt_t *pdfa, int l, int *dfa_pos, int *pat_list); static void do_dfa_matchpat(dfa_rt_t *pdfa, int anchor, matchpat_callback_fn_ptr callback, int color, struct pattern *database, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal); static void check_pattern_light(int anchor, matchpat_callback_fn_ptr callback, int color, struct pattern *pattern, int ll, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal); static void dfa_matchpat_loop(matchpat_callback_fn_ptr callback, int color, int anchor, struct pattern_db *pdb, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal); /***********************************************************************/ /* prepare the dfa board (gnugo initialization) */ void dfa_match_init(void) { build_spiral_order(); if (owl_vital_apat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "owl_vital_apat --> using dfa\n"); if (owl_attackpat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "owl_attackpat --> using dfa\n"); if (owl_defendpat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "owl_defendpat --> using dfa\n"); if (pat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "pat --> using dfa\n"); if (attpat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "attpat --> using dfa\n"); if (defpat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "defpat --> using dfa\n"); if (endpat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "endpat --> using dfa\n"); if (conn_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "conn --> using dfa\n"); if (influencepat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "influencepat --> using dfa\n"); if (barrierspat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "barrierspat --> using dfa\n"); if (fusekipat_db.pdfa != NULL) DEBUG(DEBUG_MATCHER, "barrierspat --> using dfa\n"); /* force out_board initialization */ dfa_board_size = -1; } /* * copy the board on a private board with adapted colors * and adapted size */ static void dfa_prepare_for_match(int color) { int i, j; int pos; if (dfa_board_size != board_size) { dfa_board_size = board_size; /* clean up the board */ for (pos = 0; pos < DFA_BASE * DFA_BASE; pos++) dfa_p[pos] = OUT_BOARD; } /* copy the board */ for (i = 0; i < dfa_board_size; i++) for (j = 0; j < dfa_board_size; j++) dfa_p[DFA_POS(i, j)] = EXPECTED_COLOR(color, BOARD(i, j)); prepare_for_match(color); } #if 0 /* Debug function. */ static void dump_dfa_board(int m, int n) { int i, j; for (i = 0; i < dfa_board_size; i++) { for (j = 0; j < dfa_board_size; j++) { if (i != m || j != n) fprintf(stderr, "%1d", dfa_p[DFA_POS(i, j)]); else fprintf(stderr, "*"); } fprintf(stderr, "\n"); } } #endif /* * Scan the board with a DFA to get all patterns matching at * `dfa_pos' with transformation l. Store patterns indexes * `pat_list'. Return the number of patterns found. */ static int scan_for_patterns(dfa_rt_t *pdfa, int l, int *dfa_pos, int *pat_list) { int delta; int state = 1; /* initial state */ int row = 0; /* initial row */ int id = 0; /* position in id_list */ do { /* collect patterns indexes */ int att = pdfa->states[state].att; while (att != 0) { pat_list[id] = pdfa->indexes[att].val; id++; att = pdfa->indexes[att].next; } /* go to next state */ delta = pdfa->states[state].next[dfa_pos[spiral[row][l]]]; state += delta; row++; } while (delta != 0); /* while not on error state */ return id; } /* Perform pattern matching with DFA filtering. */ static void do_dfa_matchpat(dfa_rt_t *pdfa, int anchor, matchpat_callback_fn_ptr callback, int color, struct pattern *database, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal) { int k; int ll; /* Iterate over transformations (rotations or reflections) */ int patterns[DFA_MAX_MATCHED + 8]; int num_matched = 0; int *dfa_pos = dfa_p + DFA_POS(I(anchor), J(anchor)); /* Basic sanity checks. */ ASSERT_ON_BOARD1(anchor); /* One scan by transformation */ for (ll = 0; ll < 8; ll++) { num_matched += scan_for_patterns(pdfa, ll, dfa_pos, patterns + num_matched); patterns[num_matched++] = -1; } ASSERT1(num_matched <= DFA_MAX_MATCHED + 8, anchor); /* Constraints and other tests. */ for (ll = 0, k = 0; ll < 8; k++) { int matched; if (patterns[k] == -1) { ll++; continue; } matched = patterns[k]; #if PROFILE_PATTERNS database[matched].dfa_hits++; #endif check_pattern_light(anchor, callback, color, database + matched, ll, callback_data, goal, anchor_in_goal); } } /* * Do the pattern matching for a given pattern and a given * transformation ll. * (does not recompute what dfa filtering has already done) */ static void check_pattern_light(int anchor, matchpat_callback_fn_ptr callback, int color, struct pattern *pattern, int ll, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal) { int k; /* Iterate over elements of pattern */ int found_goal = 0; #if PROFILE_PATTERNS int nodes_before; #endif if (0) gprintf("check_pattern_light @ %1m rot:%d pattern: %s\n", anchor, ll, pattern->name); /* Throw out duplicating orientations of symmetric patterns. */ if (pattern->trfno == 5) { if (ll < 2 || ll >= 6) return; } else { if (ll >= pattern->trfno) return; } /* Now iterate over the elements of the pattern. */ for (k = 0; k < pattern->patlen; k++) { /* match each point */ int pos; /* absolute (board) co-ords of (transformed) pattern element */ /* transform pattern real coordinate... */ pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor); ASSERT_ON_BOARD1(pos); if (!anchor_in_goal) { /* goal check */ if (goal != NULL && board[pos] != EMPTY && goal[pos]) found_goal = 1; } /* class check */ ASSERT1(dragon[pos].status < 4, anchor); if ((pattern->class & class_mask[dragon[pos].status][board[pos]]) != 0) goto match_failed; } /* loop over elements */ /* Make it here ==> We have matched all the elements to the board. */ if (!anchor_in_goal) { if (goal != NULL && !found_goal) goto match_failed; } #if PROFILE_PATTERNS pattern->hits++; nodes_before = stats.nodes; #endif /* A match! - Call back to the invoker to let it know. */ callback(anchor, color, pattern, ll, callback_data); #if PROFILE_PATTERNS pattern->reading_nodes += stats.nodes - nodes_before; #endif /* We jump to here as soon as we discover a pattern has failed. */ match_failed: DEBUG(DEBUG_MATCHER, "end of pattern '%s', rotation %d at %1m\n---\n", pattern->name, ll, anchor); } /* check_pattern_light */ /* * Scan the board to get patterns anchored by anchor from color * point of view. * the board must be prepared by dfa_prepare_for_match(color) ! */ static void dfa_matchpat_loop(matchpat_callback_fn_ptr callback, int color, int anchor, struct pattern_db *pdb, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal) { int pos; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] == anchor && (!anchor_in_goal || goal[pos] != 0)) do_dfa_matchpat(pdb->pdfa, pos, callback, color, pdb->patterns, callback_data, goal, anchor_in_goal); } } /**************************************************************************/ /* Main functions: */ /**************************************************************************/ typedef void (*loop_fn_ptr_t)(matchpat_callback_fn_ptr callback, int color, int anchor, struct pattern_db *pdb, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal); typedef void (*prepare_fn_ptr_t)(int color); /* same as the old matchpat but for all the board with * preparation. * * 4 possible values for color argument: * WHITE or BLACK: matchpat is called from this color point of view. * ANCHOR_COLOR : matchpat is called from the anchor's point of view. * ANCHOR_OTHER : matchpat is called from the opposite color of the * anchor's point of view. */ void matchpat(matchpat_callback_fn_ptr callback, int color, struct pattern_db *pdb, void *callback_data, signed char goal[BOARDMAX]) { matchpat_goal_anchor(callback, color, pdb, callback_data, goal, pdb->fixed_anchor); } void matchpat_goal_anchor(matchpat_callback_fn_ptr callback, int color, struct pattern_db *pdb, void *callback_data, signed char goal[BOARDMAX], int anchor_in_goal) { loop_fn_ptr_t loop = matchpat_loop; prepare_fn_ptr_t prepare = prepare_for_match; /* check board size */ if (pdb->fixed_for_size != board_size) { fixup_patterns_for_board_size(pdb->patterns); pdb->fixed_for_size = board_size; } /* select pattern matching strategy */ if (pdb->pdfa != NULL) { loop = dfa_matchpat_loop; prepare = dfa_prepare_for_match; } /* select strategy */ switch (color) { case ANCHOR_COLOR: { /* match pattern for the color of their anchor */ prepare(WHITE); loop(callback, WHITE, WHITE, pdb, callback_data, goal, anchor_in_goal); prepare(BLACK); loop(callback, BLACK, BLACK, pdb, callback_data, goal, anchor_in_goal); } break; case ANCHOR_OTHER: { /* match pattern for the opposite color of their anchor */ prepare(WHITE); loop(callback, WHITE, BLACK, pdb, callback_data, goal, anchor_in_goal); prepare(BLACK); loop(callback, BLACK, WHITE, pdb, callback_data, goal, anchor_in_goal); } break; default: { /* match all patterns for color */ prepare(color); loop(callback, color, WHITE, pdb, callback_data, goal, anchor_in_goal); loop(callback, color, BLACK, pdb, callback_data, goal, anchor_in_goal); } } } static int fullboard_transform(int pos, int trans) { int dx = I(pos) - (board_size-1)/2; int dy = J(pos) - (board_size-1)/2; int x, y; gg_assert(POS((board_size-1)/2, (board_size-1)/2) + DELTA(dx, dy) == pos); TRANSFORM2(dx, dy, &x, &y, trans); return POS(x + (board_size-1)/2, y + (board_size-1)/2); } /* A dedicated matcher which can only do fullboard matching on * odd-sized boards, optimized for fuseki patterns. */ void fullboard_matchpat(fullboard_matchpat_callback_fn_ptr callback, int color, struct fullboard_pattern *pattern) { int ll; /* Iterate over transformations (rotations or reflections) */ /* We transform around the center point. */ int number_of_stones_on_board = stones_on_board(BLACK | WHITE); static int color_map[gg_max(WHITE, BLACK) + 1]; /* One hash value for each rotation/reflection: */ Hash_data current_board_hash[8]; /* Basic sanity check. */ gg_assert(color != EMPTY); gg_assert(board_size % 2 == 1); color_map[EMPTY] = EMPTY; if (color == WHITE) { color_map[WHITE] = WHITE; color_map[BLACK] = BLACK; } else { color_map[WHITE] = BLACK; color_map[BLACK] = WHITE; } /* Get hash data of all rotations/reflections of current board position. */ for (ll = 0; ll < 8; ll++) { Intersection p[BOARDSIZE]; int pos; for (pos = 0; pos < BOARDSIZE; pos++) if (ON_BOARD(pos)) p[pos] = color_map[board[fullboard_transform(pos, ll)]]; else p[pos] = GRAY; if (ON_BOARD(board_ko_pos)) hashdata_recalc(¤t_board_hash[ll], p, fullboard_transform(board_ko_pos, ll)); else hashdata_recalc(¤t_board_hash[ll], p, NO_MOVE); } /* Try each pattern - NULL pattern name marks end of list. */ for (; pattern->name; pattern++) { if (pattern->number_of_stones != number_of_stones_on_board) continue; /* Try each orientation transformation. */ for (ll = 0; ll < 8; ll++) if (hashdata_is_equal(current_board_hash[ll], pattern->fullboard_hash)) { /* A match! - Call back to the invoker to let it know. */ int pos = AFFINE_TRANSFORM(pattern->move_offset, ll, POS((board_size-1)/2, (board_size-1)/2)); callback(pos, pattern, ll); } } } /**************************************************************************/ /* Corner matcher */ /**************************************************************************/ /* These arrays specify anchor corner for each transformation. They _must_ * be in line with transformation2[][] array in patterns/transform.c. */ static const int corner_x[8] = {0, 0, 1, 1, 1, 1, 0, 0}; static const int corner_y[8] = {0, 1, 1, 0, 1, 0, 0, 1}; /* The number of stones in "corner area" for each board position. For example, * corner area for position E3 when anchoring at A1 corner, looks like this: * * |........ In general, NUM_STONES(pos) is the number of stones * |........ which are closer to the corner (stone at pos, if any, * 3 |#####... counts too) than pos. Note, that say G2 is not closer * |#####... to the corner than E3, the reverse isn't true either. * 1 |#####... Their distances are "incomparable" since E < G but * +-------- 3 > 2. * A E * * Note that we need these values in at most MAX_BOARD x MAX_BOARD array. * However, it may be anchored at any corner of the board, so if the board is * small, we may calculate NUM_STONES() at negative coordinates. */ static int num_stones[2*BOARDMAX]; #define NUM_STONES(pos) num_stones[(pos) + BOARDMAX] /* Stone locations are stored in this array. They might be needed by callback * function. */ static int pattern_stones[BOARDMAX]; /* Recursively performs corner matching. This function checks whether * `num_variation' variations pointed by `variation' parameter match. * If any of them does, the function calls itself recursively. If any * pattern corresponding to those variations matches, it notifies * callback function. */ static void do_corner_matchpat(int num_variations, struct corner_variation *variation, int match_color, corner_matchpat_callback_fn_ptr callback, int callback_color, int trans, int anchor, int stones) { for (; --num_variations >= 0; variation++) { int move = AFFINE_TRANSFORM(variation->move_offset, trans, anchor); int color_check = match_color ^ variation->xor_att; struct corner_pattern *pattern = variation->pattern; if (pattern && color_check == callback_color) { int second_corner = AFFINE_TRANSFORM(pattern->second_corner_offset, trans, anchor); if (NUM_STONES(second_corner) == stones && (!pattern->symmetric || trans < 4)) { /* We have found a matching pattern. */ ASSERT1(board[move] == EMPTY, move); callback(move, callback_color, pattern, trans, pattern_stones, stones); continue; } } if (variation->num_variations && NUM_STONES(move) == variation->num_stones && board[move] == color_check) { /* A matching variation. */ pattern_stones[stones] = move; do_corner_matchpat(variation->num_variations, variation->variations, match_color, callback, callback_color, trans, anchor, stones + 1); } } } /* Perform corner matching at all four corners and both possible * transformations at each corner. `callback' is notified if any * matching pattern is found. */ void corner_matchpat(corner_matchpat_callback_fn_ptr callback, int color, struct corner_db *database) { int k; for (k = 0; k < 8; k++) { int anchor = POS(corner_x[k] * (board_size - 1), corner_y[k] * (board_size - 1)); int i; int j; int dx = TRANSFORM(OFFSET(1, 0), k); int dy = TRANSFORM(OFFSET(0, 1), k); int pos; struct corner_variation *variation = database->top_variations; /* Fill in the NUM_STONES() array. We use `max_width' and `max_height' * fields of database structure to stop working as early as possible. */ NUM_STONES(anchor) = IS_STONE(board[anchor]); pos = anchor; for (i = 1; i < database->max_height; i++) { pos += dx; if (!ON_BOARD(pos)) { do { NUM_STONES(pos) = BOARDMAX; pos += dx; } while (++i < database->max_height); break; } NUM_STONES(pos) = NUM_STONES(pos - dx) + IS_STONE(board[pos]); } pos = anchor; for (j = 1; j < database->max_width; j++) { pos += dy; if (!ON_BOARD(pos)) { do { NUM_STONES(pos) = BOARDMAX; pos += dy; } while (++j < database->max_width); break; } NUM_STONES(pos) = NUM_STONES(pos - dy) + IS_STONE(board[pos]); } for (i = 1; i < database->max_height; i++) { pos = anchor + i * dy; for (j = 1; j < database->max_width; j++) { pos += dx; NUM_STONES(pos) = NUM_STONES(pos - dx) + NUM_STONES(pos - dy) - NUM_STONES(pos - dx - dy); if (ON_BOARD1(pos) && IS_STONE(board[pos])) NUM_STONES(pos)++; } } /* Try to match top variations. If any of them matches, we call * do_corner_matchpat() to recurse that variation's tree. */ for (i = 0; i < database->num_top_variations; i++) { int move = AFFINE_TRANSFORM(variation->move_offset, k, anchor); if (NUM_STONES(move) == 1 && IS_STONE(board[move])) { pattern_stones[0] = move; do_corner_matchpat(variation->num_variations, variation->variations, board[move], callback, color, k, anchor, 1); } variation++; } } } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */