/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * 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 "liberty.h" #include "patterns.h" /* Maximum number of dragons considered by a, B, C, and d class patterns. */ #define MAX_DRAGONS_PER_PATTERN 5 #define MAX_STRINGS_PER_PATTERN 5 /* Values of joseki patterns. */ #define U_VALUE 40.0 #define J_VALUE 35.0 #define j_VALUE 24.0 #define t_VALUE 16.0 /* Take care of joseki patterns. */ static void handle_joseki_patterns(struct pattern_attribute *attributes, unsigned int class, int move, int my_dragons[MAX_DRAGONS_PER_PATTERN], int my_ndragons, int your_dragons[MAX_DRAGONS_PER_PATTERN], int your_ndragons) { struct pattern_attribute *attribute; /* Pattern class J, joseki standard move. Add expand territory and * moyo, and require the value at least J_value. */ if (class & CLASS_J) { TRACE("...joseki standard move\n"); add_expand_territory_move(move); TRACE("...expands territory\n"); add_expand_moyo_move(move); TRACE("...expands moyo\n"); set_minimum_move_value(move, J_VALUE); TRACE("... minimum move value %f\n", J_VALUE); } /* Class `j' and `t' patterns are treated similarly. */ if (class & (CLASS_j | CLASS_t)) { float min_value; float shape_value = 0.0; if (class & CLASS_j) { min_value = j_VALUE; TRACE("...less urgent joseki move\n"); add_expand_territory_move(move); TRACE("...expands territory\n"); add_expand_moyo_move(move); TRACE("...expands moyo\n"); } else { min_value = t_VALUE; TRACE("...minor joseki move\n"); } /* Board size modification. */ min_value *= board_size / 19.0; for (attribute = attributes; attribute->type != LAST_ATTRIBUTE; attribute++) { if (attribute->type == SHAPE) { shape_value = attribute->value; min_value *= (1 + 0.01 * shape_value); break; } } if ((board_size >= 17) && (class & CLASS_F)) { /* Otherwise, `j' and `t' patterns not of CLASS_F would get * preferred in value_move_reasons(). */ min_value *= 1.005; set_maximum_move_value(move, min_value); scale_randomness(move, 5.0); TRACE("...move value %f (shape %f)\n", min_value, shape_value); } else TRACE("...minimum move value %f (shape %f)\n", min_value, shape_value); set_minimum_move_value(move, min_value); } /* Pattern class U, very urgent joseki move. Add strategical defense * and attack, plus a shape bonus of 15 and a minimum value of 40. */ if (class & CLASS_U) { int k; TRACE("...joseki urgent move\n"); for (k = 0; k < my_ndragons; k++) { add_strategical_defense_move(move, my_dragons[k]); TRACE("...strategical defense of %1m\n", my_dragons[k]); } for (k = 0; k < your_ndragons; k++) { add_strategical_attack_move(move, your_dragons[k]); TRACE("...strategical attack on %1m\n", your_dragons[k]); } add_shape_value(move, 15); TRACE("...shape value 15\n"); set_minimum_move_value(move, U_VALUE); TRACE("...(min) move value %f\n", U_VALUE); } /* Pattern class T, joseki trick move. For the moment we never play * these. */ if (class & CLASS_T) { TRACE("...joseki trick move\n"); add_antisuji_move(move); TRACE("...antisuji\n"); } for (attribute = attributes; attribute->type != LAST_ATTRIBUTE; attribute++) { switch (attribute->type) { case MIN_VALUE: set_minimum_move_value(move, attribute->value); TRACE("...(min) move value %f\n", attribute->value); break; case MAX_VALUE: set_maximum_move_value(move, attribute->value); TRACE("...max move value %f\n", attribute->value); break; case MIN_TERRITORY: set_minimum_territorial_value(move, attribute->value); TRACE("...(min) territorial value %f\n", attribute->value); break; case MAX_TERRITORY: set_maximum_territorial_value(move, attribute->value); TRACE("...max territorial value %f\n", attribute->value); break; case SHAPE: /* For class `j' and `t' patterns shape value has been counted * already. */ if (!(class & (CLASS_j | CLASS_t))) { add_shape_value(move, attribute->value); TRACE("...shape value %f\n", attribute->value); } break; case FOLLOWUP: add_followup_value(move, attribute->value); TRACE("...followup value %f\n", attribute->value); break; case REVERSE_FOLLOWUP: add_reverse_followup_value(move, attribute->value); TRACE("...reverse followup value %f\n", attribute->value); break; default: /* Must not happen. */ gg_assert(0); } } } /* * This callback is invoked for each matched pattern. */ static void shapes_callback(int anchor, int color, struct pattern *pattern, int ll, void *data) { int other = OTHER_COLOR(color); int k, l; int move; /* For restricted search, the pattern must intersect the search area */ /* Dragons of our color. */ int my_dragons[MAX_DRAGONS_PER_PATTERN]; int my_ndragons = 0; /* Dragons of other color. */ int your_dragons[MAX_DRAGONS_PER_PATTERN]; int your_ndragons = 0; /* Strings of our color. */ int my_strings[MAX_STRINGS_PER_PATTERN]; int my_nstrings = 0; /* Strings of other color. */ int your_strings[MAX_STRINGS_PER_PATTERN]; int your_nstrings = 0; /* Make a local copy of the classification that we may modify. */ unsigned int class = pattern->class; /* Don't accept fuseki marked patterns while scoring. */ if (doing_scoring && (class & CLASS_F)) return; /* Don't need auxiliary data in this callback. */ UNUSED(data); /* Pick up the location of the move */ move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor); /* For some classes of patterns we need to find all dragons present * in the pattern. */ if ((class & (CLASS_B | CLASS_C | CLASS_c | CLASS_a | CLASS_d | CLASS_O | CLASS_J | CLASS_j | CLASS_U | CLASS_T | CLASS_t)) != 0) { /* Match each point. */ for (k = 0; k < pattern->patlen; ++k) { int pos; /* absolute (board) co-ord of (transformed) pattern element */ int origin; /* dragon origin */ /* all the following stuff (currently) applies only at occupied cells */ if (pattern->patn[k].att == ATT_dot) continue; /* transform pattern real coordinate */ pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor); /* Already, matchpat rejects O patterns containing a friendly stone with * DEAD or CRITICAL matcher_status. If the stone is tactically * CRITICAL it still could have matcher_status ALIVE since it might * be amalgamated into a live dragon. In this case we want to reject the * pattern if (move) does not rescue it. This is most easily tested * here within shapes_callback(), since the value of (move) is not * known by matchpat(). */ if ((class & CLASS_O) && board[pos] == color && worm[pos].attack_points[0] != 0 && !does_defend(move, pos)) return; origin = dragon[pos].origin; if (board[pos] == color && my_ndragons < MAX_DRAGONS_PER_PATTERN) { for (l = 0; l < my_ndragons; l++) { if (my_dragons[l] == origin) break; } if (l == my_ndragons) { /* We found another dragon of our color. Check that it (or * rather the underlying worm) cannot be tactically * captured before adding it to the list of my_dragons. */ if (worm[pos].attack_codes[0] == 0 || does_defend(move, pos)) { /* Ok, add the dragon to the list. */ my_dragons[l] = origin; my_ndragons++; } } } if (board[pos] == other && your_ndragons < MAX_DRAGONS_PER_PATTERN) { for (l = 0; l < your_ndragons; l++) { if (your_dragons[l] == origin) break; } if (l == your_ndragons) { /* We found another opponent dragon, add it to the list. */ your_dragons[l] = origin; your_ndragons++; } } if (pattern->patn[k].att == ATT_O || pattern->patn[k].att == ATT_X) { origin = find_origin(pos); if (board[pos] == color && my_nstrings < MAX_STRINGS_PER_PATTERN) { for (l = 0; l < my_nstrings; l++) { if (my_strings[l] == origin) break; } if (l == my_nstrings) { /* We found another string of our color. Check that it * cannot be tactically captured before adding it to the * list of my_strings. */ if (worm[pos].attack_codes[0] == 0 || does_defend(move, pos)) { /* Ok, add the string to the list. */ my_strings[l] = origin; my_nstrings++; } } } if (board[pos] == other && your_nstrings < MAX_STRINGS_PER_PATTERN) { for (l = 0; l < your_nstrings; l++) { if (your_strings[l] == origin) break; } if (l == your_nstrings) { /* We found another opponent string, add it to the list. */ your_strings[l] = origin; your_nstrings++; } } } } /* loop over elements */ } /* if we need to loop over the elements */ /* Nothing to connect. Remove C class bit. */ if (my_nstrings < 2) class &= ~CLASS_C; /* Nothing to cut. Remove B class bit. */ if (your_nstrings < 2) class &= ~CLASS_B; /* * If this pattern can't produce any effect (e.g. if it was a B or C * pattern with only one dragon of the appropriate color), don't * do any expensive checking but return immediately. * If it only has some move_values, these will be ignored. */ if (!pattern->helper && !allpats && !(pattern->autohelper_flag & HAVE_ACTION) && !(class & CLASS_MOVE_REASONS) && pattern->attributes->type == LAST_ATTRIBUTE) return; /* For sacrifice patterns, the survival of the stone to be played is * not checked (but it still needs to be legal). Otherwise we * discard moves which can be captured. */ if (!(class & CLASS_s)) { /* Don't allow ko unsafety. */ if (safe_move(move, color) != WIN) { if (0) TRACE(" move at %1m wasn't safe, discarded\n", move); return; } } else { /* Allow illegal ko captures at this stage. */ if (!is_ko(move, color, NULL) && !is_legal(move, color)) { if (0) TRACE(" move at %1m wasn't legal, discarded\n", move); return; } } /* For class n patterns, the pattern is contingent on an opponent * move at * not being captured. */ if (class & CLASS_n) { /* Allow ko unsafety. */ if (safe_move(move, other) == 0) { if (0) TRACE(" opponent can't play safely at %1m, move discarded\n", move); return; } } /* If the pattern has a constraint, call the autohelper to see * if the pattern must be rejected. */ if (pattern->autohelper_flag & HAVE_CONSTRAINT) { if (!pattern->autohelper(ll, move, color, 0)) return; } /* Ask helper for acceptance of pattern. */ if (pattern->helper) { /* ask helper function to consider the move */ int accepted; DEBUG(DEBUG_HELPER, " asking helper to consider '%s'+%d at %1m\n", pattern->name, ll, move); accepted = pattern->helper(pattern, ll, move, color); if (accepted) { DEBUG(DEBUG_HELPER, "helper likes pattern '%s' at %1m\n", pattern->name, move); } else { DEBUG(DEBUG_HELPER, " helper does not like pattern '%s' at %1m\n", pattern->name, move); return; /* pattern matcher does not like it */ } } /* If using -a, want to see all matches even if not -v */ if (allpats || verbose) { TRACE("pattern '%s'+%d matched at %1m\n", pattern->name, ll, move); } /* does the pattern have an action? */ if (pattern->autohelper_flag & HAVE_ACTION) pattern->autohelper(ll, move, color, 1); /* Pattern class B, try to cut all combinations of opponent strings. */ if (class & CLASS_B) { for (k = 0; k < your_nstrings; k++) for (l = k+1; l < your_nstrings; l++) { if (string_connect(your_strings[k], your_strings[l], NULL) && !play_connect_n(color, 1, 1, move, your_strings[k], your_strings[l])) { add_cut_move(move, your_strings[k], your_strings[l]); TRACE("...cuts strings %1m, %1m\n", your_strings[k], your_strings[l]); } } } /* Pattern class C, try to connect all combinations of our strings. */ if (class & CLASS_C) { for (k = 0; k < my_nstrings; k++) for (l = k+1; l < my_nstrings; l++) { if (disconnect(my_strings[k], my_strings[l], NULL) && !play_connect_n(color, 0, 1, move, my_strings[k], my_strings[l])) { add_connection_move(move, my_strings[k], my_strings[l]); TRACE("...connects strings %1m, %1m\n", my_strings[k], my_strings[l]); } } } /* Pattern class c, add strategical defense move reason for all our * dragons and a small shape bonus. * * This is a preliminary effect of "weak connection" and may need to * be revised. */ if (class & CLASS_c) { for (k = 0; k < my_ndragons; k++) { add_strategical_defense_move(move, my_dragons[k]); TRACE("...strategical defense (weak connection) of %1m\n", my_dragons[k]); } add_shape_value(move, 1); TRACE("...shape value 1\n"); } /* Pattern class b is obsolete in the pattern databases handled here. */ gg_assert(!(class & CLASS_b)); /* Pattern class e, expand to make territory. */ if (class & CLASS_e) { add_expand_territory_move(move); TRACE("...expands territory\n"); } /* Pattern class E, expand to make moyo. */ if (class & CLASS_E) { add_expand_moyo_move(move); TRACE("...expands moyo\n"); } /* Pattern class i, an invasion. */ if (class & CLASS_I) { add_invasion_move(move); TRACE("...is an invasion\n"); } /* Pattern class a, strategical level attack on all opponent dragons. */ if (class & CLASS_a) { for (k = 0; k < your_ndragons; k++) { add_strategical_attack_move(move, your_dragons[k]); TRACE("...strategical attack on %1m\n", your_dragons[k]); } } /* Pattern class d, strategical level defense of all own dragons. */ if (class & CLASS_d) { for (k = 0; k < my_ndragons; k++) { add_strategical_defense_move(move, my_dragons[k]); TRACE("...strategical defense of %1m\n", my_dragons[k]); } } /* Pattern class W, worthwhile threat move. */ if (class & CLASS_W) { TRACE("...worthwhile threat move\n"); add_worthwhile_threat_move(move); } handle_joseki_patterns(pattern->attributes, class, move, my_dragons, my_ndragons, your_dragons, your_ndragons); } /* This callback is invoked for each matched pattern from joseki * database. This function is just a copy of relevant parts of * shapes_callback(). However, most of the common code resides in * handle_joseki_patterns(). */ static void joseki_callback(int move, int color, struct corner_pattern *pattern, int trans, int *stones, int num_stones) { int k, l; int class = pattern->class; /* Dragons of our color. */ int my_dragons[MAX_DRAGONS_PER_PATTERN]; int my_ndragons = 0; /* Dragons of other color. */ int your_dragons[MAX_DRAGONS_PER_PATTERN]; int your_ndragons = 0; /* For urgent joseki patterns we need to find all dragons present in the * pattern since such patterns are assumed to have strategical effect on * them. */ if (class & CLASS_U) { /* Loop over all stones in the pattern. */ for (k = 0; k < num_stones; k++) { int pos = stones[k]; int origin = dragon[pos].origin; if (board[pos] == color && my_ndragons < MAX_DRAGONS_PER_PATTERN) { for (l = 0; l < my_ndragons; l++) { if (my_dragons[l] == origin) break; } if (l == my_ndragons) { /* We found another dragon of our color. Check that it (or * rather the underlying worm) cannot be tactically * captured before adding it to the list of my_dragons. */ if (worm[pos].attack_codes[0] == 0 || does_defend(move, pos)) { /* Ok, add the dragon to the list. */ my_dragons[l] = origin; my_ndragons++; } } } if (board[pos] != color && your_ndragons < MAX_DRAGONS_PER_PATTERN) { for (l = 0; l < your_ndragons; l++) { if (your_dragons[l] == origin) break; } if (l == your_ndragons) { /* We found another opponent dragon, add it to the list. */ your_dragons[l] = origin; your_ndragons++; } } } } /* For joseki patterns we don't check if the proposed move is safe or legal. */ /* If the pattern has a constraint, call the autohelper to see * if the pattern must be rejected. */ if (pattern->autohelper_flag & HAVE_CONSTRAINT) { if (!pattern->autohelper(trans, move, color, 0)) return; } /* If using -a, want to see all matches even if not -v. */ if (allpats || verbose) TRACE("pattern '%s'+%d matched at %1m\n", pattern->name, trans, move); /* Does the pattern have an action? */ if (pattern->autohelper_flag & HAVE_ACTION) pattern->autohelper(trans, move, color, 1); /* Pattern class N, antisuji move. */ if (class & CLASS_N) { TRACE("...antisuji move\n"); add_antisuji_move(move); } handle_joseki_patterns(pattern->attributes, class, move, my_dragons, my_ndragons, your_dragons, your_ndragons); } /* * Match all patterns in patterns.db and patterns2.db on all positions. * * This function is one of the basic generators of move reasons, called * by genmove(). */ void shapes(int color) { TRACE("\nPattern matcher is looking for move reasons for %s!\n", color_to_string(color)); matchpat(shapes_callback, color, &pat_db, NULL, NULL); /* Don't match joseki patterns while scoring. */ if (josekidb && !doing_scoring) #if 1 corner_matchpat(joseki_callback, color, &joseki_db); #else matchpat(shapes_callback, color, &joseki_db, NULL, NULL); #endif if (!disable_fuseki && !doing_scoring) matchpat(shapes_callback, color, &fusekipat_db, NULL, NULL); } /* * Match all patterns in endgame.db on all positions. */ void endgame_shapes(int color) { TRACE("\nEndgame pattern matcher is looking for move reasons for %s!\n", color_to_string(color)); matchpat(shapes_callback, color, &endpat_db, NULL, NULL); } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */