/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * 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 "eyes.h" #include "gg_utils.h" #define MAXEYE 20 /* This structure is used in communication between read_eye() and * recognize_eye(). */ struct vital_points { int attacks[4 * MAXEYE]; int defenses[4 * MAXEYE]; int num_attacks; int num_defenses; }; static void compute_primary_domains(int color, int domain[BOARDMAX], int lively[BOARDMAX], int false_margins[BOARDMAX], int first_time); static void count_neighbours(struct eye_data eyedata[BOARDMAX]); static int is_lively(int owl_call, int pos); static int false_margin(int pos, int color, int lively[BOARDMAX]); static void originate_eye(int origin, int pos, int *esize, int *msize, struct eye_data eye[BOARDMAX]); static int read_eye(int pos, int *attack_point, int *defense_point, struct eyevalue *value, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], int add_moves); static int recognize_eye(int pos, int *attack_point, int *defense_point, struct eyevalue *value, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], struct vital_points *vp); static void guess_eye_space(int pos, int effective_eyesize, int margins, int bulk_score, struct eye_data eye[BOARDMAX], struct eyevalue *value, int *pessimistic_min); static void reset_map(int size); static void first_map(int *map_value); static int next_map(int *q, int map[MAXEYE]); static void print_eye(struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], int pos); static void add_false_eye(int pos, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX]); static float topological_eye(int pos, int color, struct eye_data my_eye[BOARDMAX], struct half_eye_data heye[BOARDMAX]); static float evaluate_diagonal_intersection(int m, int n, int color, int *attack_point, int *defense_point, struct eye_data my_eye[BOARDMAX]); /* These are used during the calculations of eye spaces. */ static int black_domain[BOARDMAX]; static int white_domain[BOARDMAX]; /* Used internally by mapping functions. */ static int map_size; static signed char used_index[MAXEYE]; /* * make_domains() is called from make_dragons() and from * owl_determine_life(). It marks the black and white domains * (eyeshape regions) and collects some statistics about each one. */ void make_domains(struct eye_data b_eye[BOARDMAX], struct eye_data w_eye[BOARDMAX], int owl_call) { int k; int pos; int lively[BOARDMAX]; int false_margins[BOARDMAX]; memset(black_domain, 0, sizeof(black_domain)); memset(white_domain, 0, sizeof(white_domain)); memset(false_margins, 0, sizeof(false_margins)); if (b_eye) memset(b_eye, 0, BOARDMAX * sizeof(b_eye[0])); if (w_eye) memset(w_eye, 0, BOARDMAX * sizeof(w_eye[0])); /* Initialize eye data and compute the lively array. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (ON_BOARD(pos)) lively[pos] = is_lively(owl_call, pos); /* Compute the domains of influence of each color. */ compute_primary_domains(BLACK, black_domain, lively, false_margins, 1); compute_primary_domains(WHITE, white_domain, lively, false_margins, 0); /* Now we fill out the arrays b_eye and w_eye with data describing * each eye shape. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos)) continue; if (board[pos] == EMPTY || !lively[pos]) { if (black_domain[pos] == 0 && white_domain[pos] == 0) { if (w_eye) w_eye[pos].color = GRAY; if (b_eye) b_eye[pos].color = GRAY; } else if (black_domain[pos] == 1 && white_domain[pos] == 0 && b_eye) { b_eye[pos].color = BLACK; for (k = 0; k < 4; k++) { int apos = pos + delta[k]; if (ON_BOARD(apos) && white_domain[apos] && !black_domain[apos]) { b_eye[pos].marginal = 1; break; } } } else if (black_domain[pos] == 0 && white_domain[pos] == 1 && w_eye) { w_eye[pos].color = WHITE; for (k = 0; k < 4; k++) { int apos = pos + delta[k]; if (ON_BOARD(apos) && black_domain[apos] && !white_domain[apos]) { w_eye[pos].marginal = 1; break; } } } else if (black_domain[pos] == 1 && white_domain[pos] == 1) { if (b_eye) { for (k = 0; k < 4; k++) { int apos = pos + delta[k]; if (ON_BOARD(apos) && black_domain[apos] && !white_domain[apos]) { b_eye[pos].marginal = 1; b_eye[pos].color = BLACK; break; } } if (k == 4) b_eye[pos].color = GRAY; } if (w_eye) { for (k = 0; k < 4; k++) { int apos = pos + delta[k]; if (ON_BOARD(apos) && white_domain[apos] && !black_domain[apos]) { w_eye[pos].marginal = 1; w_eye[pos].color = WHITE; break; } } if (k == 4) w_eye[pos].color = GRAY; } } } } /* The eye spaces are all found. Now we need to find the origins. */ partition_eyespaces(b_eye, BLACK); partition_eyespaces(w_eye, WHITE); } /* Find connected eyespace components and compute relevant statistics. */ void partition_eyespaces(struct eye_data eye[BOARDMAX], int color) { int pos; if (!eye) return; for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (ON_BOARD(pos)) eye[pos].origin = NO_MOVE; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos)) continue; if (eye[pos].origin == NO_MOVE && eye[pos].color == color) { int esize = 0; int msize = 0; originate_eye(pos, pos, &esize, &msize, eye); eye[pos].esize = esize; eye[pos].msize = msize; } } /* Now we count the number of neighbors and marginal neighbors * of each vertex. */ count_neighbours(eye); } /* Compute the domains of influence of each color, used in determining * eye shapes. NOTE: the term influence as used here is distinct from the * influence in influence.c. * * For this algorithm the strings which are not lively are invisible. Ignoring * these, the algorithm assigns friendly influence to: * * (1) every vertex which is occupied by a (lively) friendly stone, * (2) every empty vertex adjoining a (lively) friendly stone, * (3) every empty vertex for which two adjoining vertices (not * on the first line) in the (usually 8) surrounding ones have friendly * influence, with two CAVEATS explained below. * * Thus in the following diagram, e would be assigned friendly influence * if a and b have friendly influence, or a and d. It is not sufficent * for b and d to have friendly influence, because they are not adjoining. * * uabc * def * ghi * * The constraint that the two adjoining vertices not lie on the first * line prevents influence from leaking under a stone on the third line. * * The first CAVEAT alluded to above is that even if a and b have friendly * influence, this does not cause e to have friendly influence if there * is a lively opponent stone at d. This constraint prevents * influence from leaking past knight's move extensions. * * The second CAVEAT is that even if a and b have friendly influence * this does not cause e to have influence if there are lively opponent * stones at u and at c. This prevents influence from leaking past * nikken tobis (two space jumps). * * The corner vertices are handled slightly different. * * +--- * |ab * |cd * * We get friendly influence at a if we have friendly influence * at b or c and no lively unfriendly stone at b, c or d. * */ #define sufficient_influence(pos, apos, bpos) \ (ON_BOARD(bpos) && influence[bpos] > threshold[pos] - influence[apos]) static void compute_primary_domains(int color, int domain[BOARDMAX], int lively[BOARDMAX], int false_margins[BOARDMAX], int first_time) { int other = OTHER_COLOR(color); int i, j, k; int pos, pos2; int own, enemy; signed char threshold[BOARDMAX]; signed char influence[BOARDMAX]; int list[BOARDMAX]; int size = 0, lastchange = 0; memset(threshold, 0, sizeof(threshold)); memset(influence, 0, sizeof(influence)); /* In the first pass we * 1. Give influence to lively own stones and their neighbors. * (Cases (1) and (2) above.) * 2. Fill influence[] and threshold[] arrays with initial values. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos)) continue; if (lively[pos]) { if (board[pos] == color) { domain[pos] = 1; /* Case (1) above. */ influence[pos] = 1; } else influence[pos] = -1; continue; } own = enemy = 0; for (k = 0; k < 4; k++) { pos2 = pos + delta[k]; if (ON_BOARD(pos2) && lively[pos2]) { if (board[pos2] == color) own = 1; else enemy = 1; } } if (own) { /* To explain the asymmetry between the first time around * this loop and subsequent ones, a false margin is adjacent * to both B and W lively stones, so it's found on the first * pass through the loop. */ if (first_time) { if (board[pos] == EMPTY && (false_margin(pos, color, lively) || false_margin(pos, other, lively))) false_margins[pos] = 1; else { domain[pos] = 1; influence[pos] = 1; } } else if (board[pos] != EMPTY || !false_margins[pos]) { domain[pos] = 1; influence[pos] = 1; } } else list[size++] = pos; if (enemy) { threshold[pos] = 1; influence[pos]--; } else if (is_edge_vertex(pos)) influence[pos]--; } /* Now we loop over the board until no more vertices can be added to * the domain through case (3) above. */ if (size) { k = size; while (1) { if (!k) k = size; pos = list[--k]; /* Case (3) above. */ if (sufficient_influence(pos, SOUTH(pos), SE(pos)) || sufficient_influence(pos, SOUTH(pos), SW(pos)) || sufficient_influence(pos, EAST(pos), SE(pos)) || sufficient_influence(pos, EAST(pos), NE(pos)) || sufficient_influence(pos, WEST(pos), SW(pos)) || sufficient_influence(pos, WEST(pos), NW(pos)) || sufficient_influence(pos, NORTH(pos), NW(pos)) || sufficient_influence(pos, NORTH(pos), NE(pos))) { domain[pos] = 1; influence[pos]++; if (!--size) break; if (k < size) list[k] = list[size]; else k--; lastchange = k; } else if (k == lastchange) break; /* Looped the whole list and found nothing new */ } } if (0 && (debug & DEBUG_EYES)) { start_draw_board(); for (i = 0; i < board_size; i++) for (j = 0; j < board_size; j++) { draw_color_char(i, j, domain[POS(i, j)] ? '1' : '0', GG_COLOR_BLACK); } end_draw_board(); } } static void count_neighbours(struct eye_data eyedata[BOARDMAX]) { int pos; int k; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos) || eyedata[pos].origin == NO_MOVE) continue; eyedata[pos].esize = eyedata[eyedata[pos].origin].esize; eyedata[pos].msize = eyedata[eyedata[pos].origin].msize; eyedata[pos].neighbors = 0; eyedata[pos].marginal_neighbors = 0; for (k = 0; k < 4; k++) { int pos2 = pos + delta[k]; if (ON_BOARD(pos2) && eyedata[pos2].origin == eyedata[pos].origin) { eyedata[pos].neighbors++; if (eyedata[pos2].marginal) eyedata[pos].marginal_neighbors++; } } } } static int is_lively(int owl_call, int pos) { if (board[pos] == EMPTY) return 0; if (owl_call) return owl_lively(pos); else return (!worm[pos].inessential && (worm[pos].attack_codes[0] == 0 || worm[pos].defense_codes[0] != 0)); } /* In the following situation, we do not wish the vertex at 'a' * included in the O eye space: * * OOOOXX * OXaX.. * ------ * * This eyespace should parse as (X), not (X!). Thus the vertex * should not be included in the eyespace if it is adjacent to * an X stone which is alive, yet X cannot play safely at a. * The function returns 1 if this situation is found at * (pos) for color O. * * The condition above is true, curiously enough, also for the * following case: * A group has two eyes, one of size 1 and one which is critical 1/2. * It also has to have less than 4 external liberties, since the * reading has to be able to capture the group tactically. In that * case, the eye of size one will be treated as a false marginal. * Thus we have to exclude this case, which is done by requiring (pos) * to be adjacent to both white and black stones. Since this test is * least expensive, we start with it. * * As a second optimization we require that one of the other colored * neighbors is not lively. This should cut down on the number of * calls to attack() and safe_move(). */ static int false_margin(int pos, int color, int lively[BOARDMAX]) { int other = OTHER_COLOR(color); int neighbors = 0; int k; int all_lively; int potential_false_margin; /* Require neighbors of both colors. */ for (k = 0; k < 4; k++) if (ON_BOARD(pos + delta[k])) neighbors |= board[pos + delta[k]]; if (neighbors != (WHITE | BLACK)) return 0; /* At least one opponent neighbor should be not lively. */ all_lively = 1; for (k = 0; k < 4; k++) if (board[pos + delta[k]] == other && !lively[pos + delta[k]]) all_lively = 0; if (all_lively) return 0; potential_false_margin = 0; for (k = 0; k < 4; k++) { int apos = pos + delta[k]; if (board[apos] != other || !lively[apos]) continue; if (stackp == 0 && worm[apos].attack_codes[0] == 0) potential_false_margin = 1; if (stackp > 0 && !attack(apos, NULL)) potential_false_margin = 1; } if (potential_false_margin && safe_move(pos, other) == 0) { DEBUG(DEBUG_EYES, "False margin for %C at %1m.\n", color, pos); return 1; } return 0; } /* * originate_eye(pos, pos, *esize, *msize, eye) creates an eyeshape * with origin pos. esize and msize return the size and the number of * marginal vertices. The repeated variables (pos) are due to the * recursive definition of the function. */ static void originate_eye(int origin, int pos, int *esize, int *msize, struct eye_data eye[BOARDMAX]) { int k; ASSERT_ON_BOARD1(origin); ASSERT_ON_BOARD1(pos); gg_assert(esize != NULL); gg_assert(msize != NULL); eye[pos].origin = origin; (*esize)++; if (eye[pos].marginal) (*msize)++; for (k = 0; k < 4; k++) { int pos2 = pos + delta[k]; if (ON_BOARD(pos2) && eye[pos2].color == eye[pos].color && eye[pos2].origin == NO_MOVE && (!eye[pos2].marginal || !eye[pos].marginal)) originate_eye(origin, pos2, esize, msize, eye); } } /* * propagate_eye(origin) copies the data at the (origin) to the * rest of the eye (invariant fields only). */ void propagate_eye(int origin, struct eye_data eye[BOARDMAX]) { int pos; for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (ON_BOARD(pos) && eye[pos].origin == origin) { eye[pos].color = eye[origin].color; eye[pos].esize = eye[origin].esize; eye[pos].msize = eye[origin].msize; eye[pos].origin = eye[origin].origin; eye[pos].value = eye[origin].value; } } /* Find the dragon or dragons surrounding an eye space. Up to * max_dragons dragons adjacent to the eye space are added to * the dragon array, and the number of dragons found is returned. */ int find_eye_dragons(int origin, struct eye_data eye[BOARDMAX], int eye_color, int dragons[], int max_dragons) { int mx[BOARDMAX]; int num_dragons = 0; int pos; memset(mx, 0, sizeof(mx)); DEBUG(DEBUG_MISCELLANEOUS, "find_eye_dragons: %1m %C\n", origin, eye_color); for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (board[pos] == eye_color && mx[dragon[pos].origin] == 0 && ((ON_BOARD(SOUTH(pos)) && eye[SOUTH(pos)].origin == origin && !eye[SOUTH(pos)].marginal) || (ON_BOARD(WEST(pos)) && eye[WEST(pos)].origin == origin && !eye[WEST(pos)].marginal) || (ON_BOARD(NORTH(pos)) && eye[NORTH(pos)].origin == origin && !eye[NORTH(pos)].marginal) || (ON_BOARD(EAST(pos)) && eye[EAST(pos)].origin == origin && !eye[EAST(pos)].marginal))) { DEBUG(DEBUG_MISCELLANEOUS, " dragon: %1m %1m\n", pos, dragon[pos].origin); mx[dragon[pos].origin] = 1; if (dragons != NULL && num_dragons < max_dragons) dragons[num_dragons] = dragon[pos].origin; num_dragons++; } } return num_dragons; } /* Print debugging data for the eyeshape at (i,j). Useful with GDB. */ static void print_eye(struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], int pos) { int m, n; int pos2; int mini, maxi; int minj, maxj; int origin = eye[pos].origin; gprintf("Eyespace at %1m: color=%C, esize=%d, msize=%d\n", pos, eye[pos].color, eye[pos].esize, eye[pos].msize); for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { if (!ON_BOARD(pos2)) continue; if (eye[pos2].origin != pos) continue; if (eye[pos2].marginal && IS_STONE(board[pos2])) gprintf("%1m (X!)\n", pos2); else if (is_halfeye(heye, pos2) && IS_STONE(board[pos2])) { if (heye[pos2].value == 3.0) gprintf("%1m (XH)\n", pos2); else gprintf("%1m (XH) (topological eye value = %f)\n", pos2, heye[pos2].value); } else if (!eye[pos2].marginal && IS_STONE(board[pos2])) gprintf("%1m (X)\n", pos2); else if (eye[pos2].marginal && board[pos2] == EMPTY) gprintf("%1m (!)\n", pos2); else if (is_halfeye(heye, pos2) && board[pos2] == EMPTY) { if (heye[pos2].value == 3.0) gprintf("%1m (H)\n", pos2); else gprintf("%1m (H) (topological eye value = %f)\n", pos2, heye[pos2].value); } else gprintf("%1m\n", pos2); } gprintf("\n"); /* Determine the size of the eye. */ mini = board_size; maxi = -1; minj = board_size; maxj = -1; for (m = 0; m < board_size; m++) for (n = 0; n < board_size; n++) { if (eye[POS(m, n)].origin != origin) continue; if (m < mini) mini = m; if (m > maxi) maxi = m; if (n < minj) minj = n; if (n > maxj) maxj = n; } /* Prints the eye shape. A half eye is shown by h, if empty or H, if an * enemy is present. Note that each half eye has a marginal point which is * not printed, so the representation here may have less points than the * matching eye pattern in eyes.db. Printing a marginal for the half eye * would be nice, but difficult to implement. */ for (m = mini; m <= maxi; m++) { gprintf(""); /* Get the indentation right. */ for (n = minj; n <= maxj; n++) { int pos2 = POS(m, n); if (eye[pos2].origin == origin) { if (board[pos2] == EMPTY) { if (eye[pos2].marginal) gprintf("%o!"); else if (is_halfeye(heye, pos2)) gprintf("%oh"); else gprintf("%o."); } else if (is_halfeye(heye, pos2)) gprintf("%oH"); else gprintf("%oX"); } else gprintf("%o "); } gprintf("\n"); } } /* * Given an eyespace with origin (pos), this function computes the * minimum and maximum numbers of eyes the space can yield. If max and * min are different, then vital points of attack and defense are also * generated. * * If add_moves == 1, this function may add a move_reason for (color) at * a vital point which is found by the function. If add_moves == 0, * set color == EMPTY. */ void compute_eyes(int pos, struct eyevalue *value, int *attack_point, int *defense_point, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], int add_moves) { if (attack_point) *attack_point = NO_MOVE; if (defense_point) *defense_point = NO_MOVE; if (debug & DEBUG_EYES) { print_eye(eye, heye, pos); DEBUG(DEBUG_EYES, "\n"); } /* Look up the eye space in the graphs database. */ if (read_eye(pos, attack_point, defense_point, value, eye, heye, add_moves)) return; /* Ideally any eye space that hasn't been matched yet should be two * secure eyes. Until the database becomes more complete we have * some additional heuristics to guess the values of unknown * eyespaces. */ if (eye[pos].esize - 2*eye[pos].msize > 3) set_eyevalue(value, 2, 2, 2, 2); else if (eye[pos].esize - 2*eye[pos].msize > 0) set_eyevalue(value, 1, 1, 1, 1); else set_eyevalue(value, 0, 0, 0, 0); } /* * This function works like compute_eyes(), except that it also gives * a pessimistic view of the chances to make eyes. Since it is intended * to be used from the owl code, the option to add move reasons has * been removed. */ void compute_eyes_pessimistic(int pos, struct eyevalue *value, int *pessimistic_min, int *attack_point, int *defense_point, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX]) { static int bulk_coefficients[5] = {-1, -1, 1, 4, 12}; int pos2; int margins = 0; int halfeyes = 0; int margins_adjacent_to_margin = 0; int effective_eyesize; int bulk_score = 0; signed char chainlinks[BOARDMAX]; /* Stones inside eyespace which do not coincide with a false eye or * a halfeye. */ int interior_stones = 0; memset(chainlinks, 0, BOARDMAX); for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { int k; if (!ON_BOARD(pos2) || eye[pos2].origin != pos) continue; if (eye[pos2].marginal || is_halfeye(heye, pos2)) { margins++; if (eye[pos2].marginal && eye[pos2].marginal_neighbors > 0) margins_adjacent_to_margin++; if (is_halfeye(heye, pos2)) halfeyes++; } else if (IS_STONE(board[pos2])) interior_stones++; bulk_score += bulk_coefficients[(int) eye[pos2].neighbors]; for (k = 0; k < 4; k++) { int neighbor = pos2 + delta[k]; if (board[neighbor] == eye[pos].color) { if (!chainlinks[neighbor]) { bulk_score += 4; mark_string(neighbor, chainlinks, 1); } } else if (!ON_BOARD(neighbor)) bulk_score += 2; } } /* This is a measure based on the simplified assumption that both * players only cares about playing the marginal eye spaces. It is * used later to guess the eye value for unidentified eye shapes. */ effective_eyesize = (eye[pos].esize + halfeyes - 2*margins - margins_adjacent_to_margin); if (attack_point) *attack_point = NO_MOVE; if (defense_point) *defense_point = NO_MOVE; if (debug & DEBUG_EYES) { print_eye(eye, heye, pos); DEBUG(DEBUG_EYES, "\n"); } /* Look up the eye space in the graphs database. */ if (read_eye(pos, attack_point, defense_point, value, eye, heye, 0)) { *pessimistic_min = min_eyes(value) - margins; /* A single point eye which is part of a ko can't be trusted. */ if (eye[pos].esize == 1 && is_ko(pos, OTHER_COLOR(eye[pos].color), NULL)) *pessimistic_min = 0; DEBUG(DEBUG_EYES, " graph matching - %s, pessimistic_min=%d\n", eyevalue_to_string(value), *pessimistic_min); } /* Ideally any eye space that hasn't been matched yet should be two * secure eyes. Until the database becomes more complete we have * some additional heuristics to guess the values of unknown * eyespaces. */ else { guess_eye_space(pos, effective_eyesize, margins, bulk_score, eye, value, pessimistic_min); DEBUG(DEBUG_EYES, " guess_eye - %s, pessimistic_min=%d\n", eyevalue_to_string(value), *pessimistic_min); } if (*pessimistic_min < 0) { *pessimistic_min = 0; DEBUG(DEBUG_EYES, " pessimistic min revised to 0\n"); } /* An eyespace with at least two interior stones is assumed to be * worth at least one eye, regardless of previous considerations. */ if (*pessimistic_min < 1 && interior_stones >= 2) { *pessimistic_min = 1; DEBUG(DEBUG_EYES, " pessimistic min revised to 1 (interior stones)\n"); } if (attack_point && *attack_point == NO_MOVE && max_eyes(value) != *pessimistic_min) { /* Find one marginal vertex and set as attack and defense point. * * We make some effort to find the best marginal vertex by giving * priority to ones with more than one neighbor in the eyespace. * We prefer non-halfeye margins and ones which are not self-atari * for the opponent. Margins not on the edge are also favored. */ int best_attack_point = NO_MOVE; int best_defense_point = NO_MOVE; float score = 0.0; for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { if (ON_BOARD(pos2) && eye[pos2].origin == pos) { float this_score = 0.0; int this_attack_point = NO_MOVE; int this_defense_point = NO_MOVE; if (eye[pos2].marginal && board[pos2] == EMPTY) { this_score = eye[pos2].neighbors; this_attack_point = pos2; this_defense_point = pos2; if (is_self_atari(pos2, OTHER_COLOR(eye[pos].color))) this_score -= 0.5; if (is_edge_vertex(pos2)) this_score -= 0.1; } else if (is_halfeye(heye, pos2)) { this_score = 0.75; this_defense_point = heye[pos2].defense_point[0]; this_attack_point = heye[pos2].attack_point[0]; } else continue; if (gg_normalize_float2int(this_score, 0.01) > gg_normalize_float2int(score, 0.01)) { best_attack_point = this_attack_point; best_defense_point = this_defense_point; score = this_score; } } } if (score > 0.0) { if (defense_point) *defense_point = best_defense_point; if (attack_point) *attack_point = best_attack_point; } } if (defense_point && *defense_point != NO_MOVE) { ASSERT_ON_BOARD1(*defense_point); } if (attack_point && *attack_point != NO_MOVE) { ASSERT_ON_BOARD1(*attack_point); } } static void guess_eye_space(int pos, int effective_eyesize, int margins, int bulk_score, struct eye_data eye[BOARDMAX], struct eyevalue *value, int *pessimistic_min) { if (effective_eyesize > 3) { set_eyevalue(value, 2, 2, 2, 2); *pessimistic_min = 1; if ((margins == 0 && effective_eyesize > 7) || (margins > 0 && effective_eyesize > 9)) { int eyes = 2 + (effective_eyesize - 2 * (margins > 0) - 8) / 2; int threshold = (4 * (eye[pos].esize - 2) + (effective_eyesize - 8) * (effective_eyesize - 9)); DEBUG(DEBUG_EYES, "size: %d(%d), threshold: %d, bulk score: %d\n", eye[pos].esize, effective_eyesize, threshold, bulk_score); if (bulk_score > threshold && effective_eyesize < 15) eyes = gg_max(2, eyes - ((bulk_score - threshold) / eye[pos].esize)); if (bulk_score < threshold + eye[pos].esize || effective_eyesize >= 15) *pessimistic_min = eyes; set_eyevalue(value, eyes, eyes, eyes, eyes); } } else if (effective_eyesize > 0) { set_eyevalue(value, 1, 1, 1, 1); if (margins > 0) *pessimistic_min = 0; else *pessimistic_min = 1; } else { if (eye[pos].esize - margins > 2) set_eyevalue(value, 0, 0, 1, 1); else set_eyevalue(value, 0, 0, 0, 0); *pessimistic_min = 0; } } /* This function does some minor reading to improve the results of * recognize_eye(). Currently, it has two duties. One is to read * positions like this: * * .XXXX| with half eye with proper eye * XXOOO| * XO.O.| . (1 eye) . (2 eyes) * XXOa.| !.. .* * -----+ * * recognize_eye() sees the eyespace of the white dragon as shown * (there's a half eye at a and it is considered the same as '!.' by * the optics code). Normally, that eye shape gives only one secure * eye, and owl thinks that the white dragon is dead unconditionally. * This function tries to turn such ko-dependent half eyes into proper * eyes and chooses the best alternative. Note that we don't have any * attack/defense codes here, since owl will determine them itself. * * Another one is related to some cases when replacing half eyes with * '!.' doesn't work. E.g. consider this eye (optics:328): * * XXXOO eye graph is 310: * X..X. * XOXX. !.! (second '!' is due to the halfeye) * OXO.. * O.O.. * * When this function detects such a half eye that can be attacked * and/or defended inside its eyespace, it tries to turn it into a * proper eye and see what happens. In case it gives an improvement * for attacker and/or defender, the function keeps new result but * only if new vital points are also vital points for the half eye. * The heuristics used here might need improvements since they are * based on a single game position. * * If add_moves != 0, this function may add move reasons for (color) * at the vital points which are found by recognize_eye(). If add_moves * == 0, set color to be EMPTY. */ static int read_eye(int pos, int *attack_point, int *defense_point, struct eyevalue *value, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], int add_moves) { int eye_color; int k; int pos2; int combination_halfeye = NO_MOVE; int combination_attack = NO_MOVE; int combination_defense = NO_MOVE; int num_ko_halfeyes = 0; int ko_halfeye = NO_MOVE; struct vital_points vp; struct vital_points ko_vp; struct vital_points *best_vp = &vp; eye_color = recognize_eye(pos, attack_point, defense_point, value, eye, heye, &vp); if (!eye_color) return 0; /* Find ko half eyes and "combination" half eyes if any. */ for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { if (ON_BOARD(pos2) && eye[pos2].origin == pos && heye[pos2].type == HALF_EYE) { if (combination_halfeye == NO_MOVE) { int apos = NO_MOVE; int dpos = NO_MOVE; for (k = 0; k < heye[pos2].num_attacks; k++) { if (eye[heye[pos2].attack_point[k]].origin == pos) { apos = heye[pos2].attack_point[k]; break; } } for (k = 0; k < heye[pos2].num_defenses; k++) { if (eye[heye[pos2].defense_point[k]].origin == pos) { dpos = heye[pos2].defense_point[k]; break; } } if (apos || dpos) { combination_halfeye = pos2; combination_attack = apos; combination_defense = dpos; } } if (heye[pos2].value < 3.0) { num_ko_halfeyes++; ko_halfeye = pos2; } } } /* In case we have a "combination" half eye, turn it into a proper eye * vertex for a while and see what happens. */ if (combination_halfeye != NO_MOVE) { int result; int apos = NO_MOVE; int dpos = NO_MOVE; struct eyevalue combination_value; struct vital_points combination_vp; heye[combination_halfeye].type = 0; result = recognize_eye(pos, &apos, &dpos, &combination_value, eye, heye, &combination_vp); heye[combination_halfeye].type = HALF_EYE; if (result) { if (combination_attack && min_eyes(value) > min_eyes(&combination_value)) { /* FIXME: I'm not sure we can ever get here. */ for (k = 0; k < combination_vp.num_attacks; k++) { if (combination_vp.attacks[k] == combination_attack) { value->a = combination_value.a; value->b = combination_value.b; *attack_point = apos; best_vp->num_attacks = 1; best_vp->attacks[0] = combination_attack; break; } } } if (combination_defense && max_eyes(value) < max_eyes(&combination_value)) { /* Turning the half eye into a proper eye gives an improvement. * However, we can only accept this result if there is a vital * point that defends both the half eye and the whole eyespace. */ for (k = 0; k < combination_vp.num_defenses; k++) { if (combination_vp.defenses[k] == combination_defense) { value->c = combination_value.c; value->d = combination_value.d; *defense_point = dpos; best_vp->num_defenses = 1; best_vp->defenses[0] = combination_defense; break; } } } if (min_eyes(value) != max_eyes(value)) { ASSERT1(combination_attack || combination_defense, combination_halfeye); if (*attack_point == NO_MOVE) { *attack_point = combination_attack; if (*attack_point == NO_MOVE) *attack_point = combination_defense; } if (*defense_point == NO_MOVE) { *defense_point = combination_defense; if (*defense_point == NO_MOVE) *defense_point = combination_defense; } } } } /* The same with ko half eye (we cannot win two kos at once, therefore we * give up if there is more than one ko half eye). */ if (num_ko_halfeyes == 1) { int result; int apos = NO_MOVE; int dpos = NO_MOVE; struct eyevalue ko_value; heye[ko_halfeye].type = 0; result = recognize_eye(pos, &apos, &dpos, &ko_value, eye, heye, &ko_vp); heye[ko_halfeye].type = HALF_EYE; if (result && max_eyes(value) < max_eyes(&ko_value)) { /* It is worthy to win the ko. */ *value = ko_value; *attack_point = apos; *defense_point = dpos; best_vp = &ko_vp; } } if (add_moves) { struct vital_eye_points *vital; if (eye_color == WHITE) vital = white_vital_points; else vital = black_vital_points; for (k = 0; k < best_vp->num_defenses && k < MAX_EYE_ATTACKS; k++) vital[pos].defense_points[k] = best_vp->defenses[k]; for (k = 0; k < best_vp->num_attacks && k < MAX_EYE_ATTACKS; k++) vital[pos].attack_points[k] = best_vp->attacks[k]; } return 1; } /* recognize_eye(pos, *attack_point, *defense_point, *max, *min, eye_data, * half_eye_data, color, vp), where pos is the origin of an eyespace, returns * owner of eye (his color) if there is a pattern in eyes.db matching the * eyespace, or 0 if no match is found. If there is a key point for attack, * (*attack_point) is set to its location, or NO_MOVE if there is none. * Similarly (*defense_point) is the location of a vital defense point. * *value is set according to the pattern found. Vital attack/defense points * exist if and only if min_eyes(value) != max_eyes(value). */ static int recognize_eye(int pos, int *attack_point, int *defense_point, struct eyevalue *value, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], struct vital_points *vp) { int pos2; int eye_color; int eye_size = 0; int num_marginals = 0; int vpos[MAXEYE]; signed char marginal[MAXEYE], edge[MAXEYE], neighbors[MAXEYE]; int graph; int map[MAXEYE]; int best_score; int r; gg_assert(attack_point != NULL); gg_assert(defense_point != NULL); /* Set `eye_color' to the owner of the eye. */ eye_color = eye[pos].color; if (eye[pos].esize-eye[pos].msize > 8) return 0; if (eye[pos].msize > MAXEYE) return 0; /* Create list of eye vertices */ for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { if (!ON_BOARD(pos2)) continue; if (eye[pos2].origin == pos) { vpos[eye_size] = pos2; marginal[eye_size] = eye[pos2].marginal; if (marginal[eye_size]) num_marginals++; neighbors[eye_size] = eye[pos2].neighbors; if (0) { if (marginal[eye_size]) TRACE("(%1m)", vpos[eye_size]); else TRACE(" %1m ", vpos[eye_size]); TRACE("\n"); } if (is_corner_vertex(pos2)) edge[eye_size] = 2; else if (is_edge_vertex(pos2)) edge[eye_size] = 1; else edge[eye_size] = 0; if (is_halfeye(heye, pos2)) { neighbors[eye_size]++; /* Increase neighbors of half eye. */ eye_size++; /* Use a virtual marginal vertex for mapping purposes. We set it * to be at NO_MOVE so it won't accidentally count as a * neighbor for another vertex. Note that the half eye precedes * the virtual marginal vertex in the list. */ vpos[eye_size] = NO_MOVE; marginal[eye_size] = 1; num_marginals++; edge[eye_size] = 0; neighbors[eye_size] = 1; } eye_size++; } } /* We attempt to construct a map from the graph to the eyespace * preserving the adjacency structure. If this can be done, we've * identified the eyeshape. */ for (graph = 0; graphs[graph].vertex != NULL; graph++) { int q; if (graphs[graph].esize != eye_size || graphs[graph].msize != num_marginals) continue; reset_map(eye_size); q = 0; first_map(&map[0]); while (1) { struct eye_vertex *gv = &graphs[graph].vertex[q]; int mv = map[q]; int ok = 1; if (0) TRACE("q=%d: %d %d %d %d %d %d\n", q, map[0], map[1], map[2], map[3], map[4], map[5]); if (neighbors[mv] != gv->neighbors || marginal[mv] != gv->marginal || edge[mv] < gv->edge) ok = 0; if (ok) { if (IS_STONE(board[vpos[mv]])) { if (!(gv->flags & CAN_CONTAIN_STONE)) ok = 0; } /* Virtual half eye marginals also fall here since they are off * board. */ else if (!(gv->flags & CAN_BE_EMPTY)) ok = 0; } if (ok) { int k; for (k = 0; k < gv->neighbors; k++) { if (gv->n[k] < q) { int mn = map[gv->n[k]]; /* Two eye vertices are neighbours if they are adjacent on the * board or one of them is a half eye and the other is its * virtual marginal vertex (and follows it in vpos[] array). */ if (vpos[mv] != SOUTH(vpos[mn]) && vpos[mv] != WEST(vpos[mn]) && vpos[mv] != NORTH(vpos[mn]) && vpos[mv] != EAST(vpos[mn]) && (mv != mn - 1 || vpos[mv] == NO_MOVE || heye[vpos[mv]].type != HALF_EYE) && (mn != mv - 1 || vpos[mn] == NO_MOVE || heye[vpos[mn]].type != HALF_EYE)) { ok = 0; break; } } } } if (!ok) { if (!next_map(&q, map)) break; if (0) gprintf(" q=%d, esize=%d: %d %d %d %d %d\n", q, eye_size, map[0], map[1], map[2], map[3], map[4]); } else { q++; if (q == eye_size) break; /* A match! */ first_map(&map[q]); } } if (q == eye_size) { /* We have found a match! Now sort out the vital moves. */ *value = graphs[graph].value; vp->num_attacks = 0; vp->num_defenses = 0; if (eye_move_urgency(value) > 0) { /* Collect all attack and defense points in the pattern. */ int k; for (k = 0; k < eye_size; k++) { struct eye_vertex *ev = &graphs[graph].vertex[k]; if (ev->flags & EYE_ATTACK_POINT) { /* Check for a marginal vertex matching a half eye virtual * marginal. This is the case if a half eye preceeds the * current vertex in the list. */ if (ev->marginal && map[k] > 0 && vpos[map[k] - 1] != NO_MOVE && is_halfeye(heye, vpos[map[k] - 1])) { /* Add all diagonals as vital. */ int ix; struct half_eye_data *he = &heye[vpos[map[k] - 1]]; for (ix = 0; ix < he->num_attacks; ix++) vp->attacks[vp->num_attacks++] = he->attack_point[ix]; } else vp->attacks[vp->num_attacks++] = vpos[map[k]]; } if (ev->flags & EYE_DEFENSE_POINT) { /* Check for a half eye virtual marginal vertex. */ if (ev->marginal && map[k] > 0 && vpos[map[k] - 1] != NO_MOVE && is_halfeye(heye, vpos[map[k] - 1])) { /* Add all diagonals as vital. */ int ix; struct half_eye_data *he = &heye[vpos[map[k] - 1]]; for (ix = 0; ix < he->num_defenses; ix++) vp->defenses[vp->num_defenses++] = he->defense_point[ix]; } else vp->defenses[vp->num_defenses++] = vpos[map[k]]; } } gg_assert(vp->num_attacks > 0 && vp->num_defenses > 0); /* We now have all vital attack and defense points listed but * we are also expected to single out of one of each to return * in *attack_point and *defense_point. Since sometimes those * are the only vital points considered, we want to choose the * best ones, in the sense that they minimize the risk for * error in the eye space analysis. * * One example is this position * * |..XXXX * |XXX..X * |..!O.X * |OO.O.X * |.O.!XX * +------ * * where O has an eyespace of the !..! type. The graph * matching finds that both marginal vertices are vital points * but here the one at 3-3 fails to defend. (For attack both * points work but the 3-3 one is still worse since it leaves * a ko threat.) * * In order to differentiate between the marginal points we * count the number of straight and diagonal neighbors within * the eye space. In the example above both have one straight * neighbor each but the edge margin wins because it also has * a diagonal margin. */ best_score = -10; for (k = 0; k < vp->num_attacks; k++) { int apos = vp->attacks[k]; int score = 0; for (r = 0; r < 8; r++) if (ON_BOARD(apos + delta[r]) && eye[apos + delta[r]].color == eye[pos].color && !eye[apos + delta[r]].marginal) { score++; if (r < 4) { score++; if (board[apos + delta[r]] != EMPTY) score++; } } /* If a vital point is not adjacent to any point in the eye * space, it must be a move to capture or defend a string * related to a halfeye, e.g. the move * in this position, * * ......| * .XXXX.| * .X.O..| * .XO.OO| * .*XO..| * ------+ * * Playing this is probably a good idea. */ if (score == 0) score += 2; if (0) gprintf("attack point %1m score %d\n", apos, score); if (score > best_score) { *attack_point = apos; best_score = score; } } best_score = -10; for (k = 0; k < vp->num_defenses; k++) { int dpos = vp->defenses[k]; int score = 0; for (r = 0; r < 8; r++) if (ON_BOARD(dpos + delta[r]) && eye[dpos + delta[r]].color == eye[pos].color && !eye[dpos + delta[r]].marginal) { score++; if (r < 4) { score++; if (board[dpos + delta[r]] != EMPTY) score++; } } /* If possible, choose a non-sacrificial defense point. * Compare white T8 and T6 in lazarus:21. */ if (safe_move(dpos, eye_color) != WIN) score -= 5; /* See comment to the same code for attack points. */ if (score == 0) score += 2; if (0) gprintf("defense point %1m score %d\n", dpos, score); if (score > best_score) { *defense_point = dpos; best_score = score; } } DEBUG(DEBUG_EYES, " vital points: %1m (attack) %1m (defense)\n", *attack_point, *defense_point); DEBUG(DEBUG_EYES, " pattern matched: %d\n", graphs[graph].patnum); } TRACE("eye space at %1m of type %d\n", pos, graphs[graph].patnum); return eye_color; } } return 0; } /* a MAP is a map of the integers 0,1,2, ... ,q into * 0,1, ... , esize-1 where q < esize. This determines a * bijection of the first q+1 elements of the graph into the * eyespace. The following three functions work with maps. */ /* Reset internal data structure used by first_map() and * next_map() functions. */ static void reset_map(int size) { map_size = size; memset(used_index, 0, size * sizeof(used_index[0])); } /* The function first_map finds the smallest valid * value of a map element. */ static void first_map(int *map_value) { int k = 0; while (used_index[k]) k++; used_index[k] = 1; *map_value = k; } /* The function next_map produces the next map in lexicographical * order. If no next map can be found, q is decremented, then we * try again. If the entire map is lexicographically last, the * function returns false. */ static int next_map(int *q, int map[MAXEYE]) { int k; do { used_index[map[*q]] = 0; for (k = map[*q]; ++k < map_size;) { if (!used_index[k]) { used_index[k] = 1; map[*q] = k; return 1; } } (*q)--; } while (*q >= 0); return 0; } /* add_false_eye() turns a proper eyespace into a margin. */ static void add_false_eye(int pos, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX]) { int k; ASSERT1(heye[pos].type == FALSE_EYE, pos); DEBUG(DEBUG_EYES, "false eye found at %1m\n", pos); if (eye[pos].color == GRAY || eye[pos].marginal != 0) return; eye[pos].marginal = 1; eye[eye[pos].origin].msize++; for (k = 0; k < 4; k++) if (ON_BOARD(pos + delta[k]) && eye[pos + delta[k]].origin == eye[pos].origin) eye[pos + delta[k]].marginal_neighbors++; propagate_eye(eye[pos].origin, eye); } /* These functions are used from constraints to identify eye spaces, * primarily for late endgame moves. */ int is_eye_space(int pos) { return (white_eye[pos].color == WHITE || black_eye[pos].color == BLACK); } int is_proper_eye_space(int pos) { return ((white_eye[pos].color == WHITE && !white_eye[pos].marginal) || (black_eye[pos].color == BLACK && !black_eye[pos].marginal)); } /* Return the maximum number of eyes that can be obtained from the * eyespace at (i, j). This is most useful in order to determine * whether the eyespace can be assumed to produce any territory at * all. */ int max_eye_value(int pos) { int max_white = 0; int max_black = 0; if (white_eye[pos].color == WHITE) max_white = max_eyes(&white_eye[pos].value); if (black_eye[pos].color == BLACK) max_black = max_eyes(&black_eye[pos].value); return gg_max(max_white, max_black); } int is_marginal_eye_space(int pos) { return (white_eye[pos].marginal || black_eye[pos].marginal); } int is_halfeye(struct half_eye_data heye[BOARDMAX], int pos) { return heye[pos].type == HALF_EYE; } int is_false_eye(struct half_eye_data heye[BOARDMAX], int pos) { return heye[pos].type == FALSE_EYE; } /* Find topological half eyes and false eyes by analyzing the * diagonal intersections, as described in the Texinfo * documentation (Eyes/Eye Topology). */ void find_half_and_false_eyes(int color, struct eye_data eye[BOARDMAX], struct half_eye_data heye[BOARDMAX], int find_mask[BOARDMAX]) { int eye_color = color; int pos; float sum; for (pos = BOARDMIN; pos < BOARDMAX; pos++) { /* skip eyespaces which owl doesn't want to be searched */ if (!ON_BOARD(pos) || (find_mask && find_mask[eye[pos].origin] <= 1)) continue; /* skip every vertex which can't be a false or half eye */ if (eye[pos].color != eye_color || eye[pos].marginal || eye[pos].neighbors > 1) continue; sum = topological_eye(pos, color, eye, heye); if (sum >= 4.0) { /* false eye */ heye[pos].type = FALSE_EYE; if (eye[pos].esize == 1 || is_legal(pos, OTHER_COLOR(color)) || board[pos] == OTHER_COLOR(color)) add_false_eye(pos, eye, heye); } else if (sum > 2.0) { /* half eye */ heye[pos].type = HALF_EYE; ASSERT1(heye[pos].num_attacks > 0, pos); ASSERT_ON_BOARD1(heye[pos].attack_point[0]); ASSERT1(heye[pos].num_defenses > 0, pos); ASSERT_ON_BOARD1(heye[pos].defense_point[0]); } } } /* See Texinfo documentation (Eyes:Eye Topology). Returns: * - 2 or less if (pos) is a proper eye for (color); * - between 2 and 3 if the eye can be made false only by ko * - 3 if (pos) is a half eye; * - between 3 and 4 if the eye can be made real only by ko * - 4 or more if (pos) is a false eye. * * Attack and defense points for control of the diagonals are stored * in the heye[] array. * * my_eye is the eye space information with respect to (color). */ static float topological_eye(int pos, int color, struct eye_data my_eye[BOARDMAX], struct half_eye_data heye[BOARDMAX]) { float sum = 0.0; float val; int num_attacks = 0; int num_defenses = 0; int attack_values[4]; int defense_values[4]; int k; int r; int attack_point; int defense_point; int attack_value; int defense_value; memset(attack_values, 0, sizeof(attack_values)); memset(defense_values, 0, sizeof(defense_values)); /* Loop over the diagonal directions. */ for (k = 4; k < 8; k++) { int diag = pos + delta[k]; val = evaluate_diagonal_intersection(I(pos) + deltai[k], J(pos) + deltaj[k], color, &attack_point, &defense_point, my_eye); /* * Eyespaces with cutting points are problematic. In this position * * .....XXXXX * XXXXX.OO.X * X.OOOO.O.X * X.O.XXXO.X * ---------- * * the eyespace will be .XXX. which evaluates to two eyes (seki) * unless countermeasures are taken. * * This can be worked around in the topological analysis by * sometimes setting the diagonal value to 2.0 for vertices inside * the eyespace which are occupied by opponent stones. More * precisely all of the following conditions must hold: * * a) The value is not already 2.0. * a) The (potential) eyepoint is empty. * b) The diagonal is occupied by an opponent string, * c) which is also adjacent to the (potential) eye and * d) at least three stones long. * e) The (potential) eye is not on the edge (to steer clear of all the * hairy cases that are handled by eyes.db anyway). * f) At least two own strings are adjacent to the (potential) eye. * g) At least one of the own strings adjacent to the (potential) eye has * only one liberty which is an eye space and not decided false, yet. * * With this revision the eyespace above becomes .XXXh or * equivalently .XXX.! which is almost evaluated correctly, eye * value 0122 instead of the correct 1122. Compared to the * previous value 2222 it's a major improvement. * * FIXME: This approach has a number of shortcomings. * * 1. d) is kind of arbitrary and there may be exceptional * cases. * * 2. This diagonal value modification should not apply to * two diagonals of the same strings inside the eyespace. * E.g. if we have a partial eyespace looking like * * .OOO. * OO.OO * OXXXO * * it doesn't make sense to mark the middle vertex as a * false eye. Possibly this doesn't make any difference * in practice but it's at the very least confusing. * * 3. Actually it doesn't make sense to mark vertices as * false otherwise either due to these revisions (half * eyes make good sense though) as can be seen if a * stone is added to the initial diagram, * * .....XXXXX * XXXXXXOO.X * X.OOOO.O.X * X.O.XXXO.X * ---------- * * Now the eyespace instead becomes .XXX! which has the * eye value 0011 but if X tries to attack the eye O * suddenly gets two solid eyes! * * The correct analysis would be to remove the vertex * from the eyespace rather than turning it into a false * eye. Then we would have the eyespace .XXX which is * correctly evaluated to one eye (eye value 1112). * * The problem with this is that removing eye points is * messy. It can surely be done but currently there is * no support in the code for doing that. It has existed * at an earlier time but was removed because the * implementation was not robust enough and there was no * longer any apparent need for it. To correct this * problem is sufficient reason to reimplement that * functionality. * * 4. The test of condition g) has a result which * potentially depends on the ordering of the eyespaces * and thus presumably on the orientation of the board. * It might make more sense to examine whether the * string neighbors more than one empty vertex in the * same eyespace. */ if (val < 2.0 && board[pos] == EMPTY && board[diag] == OTHER_COLOR(color) && !is_edge_vertex(pos) && neighbor_of_string(pos, diag) && countstones(diag) >= 3) { int strings[3]; int string_count; int s; string_count = 0; for (r = 0; r < 4; r++) { int str; str = pos + delta[r]; if (board[str] != color) continue; ASSERT1(string_count < 3, pos); for (s = 0; s < string_count; s++) if (same_string(str, strings[s])) break; if (s != string_count) continue; strings[string_count++] = str; } if (string_count > 1) { for (s = 0; s < string_count; s++) { int libs[MAX_LIBERTIES]; int adj_eye_count; int lib_count; adj_eye_count = 0; lib_count = findlib(strings[s], MAX_LIBERTIES, libs); if (lib_count > MAX_LIBERTIES) continue; for (r = 0; r < lib_count && adj_eye_count < 2; r++) if (my_eye[libs[r]].color == OTHER_COLOR(color) && !my_eye[libs[r]].marginal) adj_eye_count++; if (adj_eye_count < 2) { val = 2.0; break; } } } } sum += val; if (val > 0.0 && val < 2.0) { /* Diagonals off the edge has value 1.0 but no attack or defense * point. */ if (attack_point != NO_MOVE && defense_point != NO_MOVE) { ASSERT_ON_BOARD1(attack_point); ASSERT_ON_BOARD1(defense_point); /* Store these in sorted (descending) order. We remap val * differently for attack and defense points according to: * * val attack_value defense_value * --- ------------ ------------- * 1.0 3 3 * <1.0 2 1 * >1.0 1 2 * * This means that we primarily want to take control of * diagonals without ko and secondarily of diagonals we can * take unconditionally but not the opponent. */ if (val == 1.0) { attack_value = 3; defense_value = 3; } else if (val < 1.0) { attack_value = 2; defense_value = 1; } else { attack_value = 1; defense_value = 2; } for (r = 0; r < 4; r++) { if (attack_values[r] < attack_value) { int tmp_value = attack_values[r]; int tmp_point; if (tmp_value) tmp_point = heye[pos].attack_point[r]; else tmp_point = 0; attack_values[r] = attack_value; heye[pos].attack_point[r] = attack_point; attack_value = tmp_value; attack_point = tmp_point; } if (defense_values[r] < defense_value) { int tmp_value = defense_values[r]; int tmp_point; if (tmp_value) tmp_point = heye[pos].defense_point[r]; else tmp_point = 0; defense_values[r] = defense_value; heye[pos].defense_point[r] = defense_point; defense_value = tmp_value; defense_point = tmp_point; } } num_attacks++; num_defenses++; } } } /* Remove attacks and defenses with smaller value than the best * ones. (These might be useful to save as well, but not unless we * also store the attack/defense values in the half_eye_data.) */ for (r = 0; r < num_attacks; r++) { if (attack_values[r] < attack_values[0]) { num_attacks = r; break; } } for (r = 0; r < num_defenses; r++) { if (defense_values[r] < defense_values[0]) { num_defenses = r; break; } } heye[pos].num_attacks = num_attacks; heye[pos].num_defenses = num_defenses; heye[pos].value = sum; return sum; } /* Evaluate an intersection (m, n) which is diagonal to an eye space, * as described in the Texinfo documentation (Eyes/Eye Topology). * * Returns: * * 0 if both coordinates are off the board * 1 if one coordinate is off the board * * 0 if (color) has control over the vertex * a if (color) can take control over the vertex unconditionally and * the opponent can take control by winning a ko. * 1 if both (color) and the opponent can take control of the vertex * unconditionally * b if (color) can take control over the vertex by winning a ko and * the opponent can take control unconditionally. * 2 if the opponent has control over the vertex * * The values a and b are discussed in the documentation. We are * currently using a = 0.75 and b = 1.25. * * Notice that it's necessary to pass the coordinates separately * instead of as a 1D coordinate. The reason is that the 1D mapping * can't uniquely identify "off the corner" points. * * my_eye has to be the eye_data with respect to color. */ static float evaluate_diagonal_intersection(int m, int n, int color, int *attack_point, int *defense_point, struct eye_data my_eye[BOARDMAX]) { float value = 0; int other = OTHER_COLOR(color); int pos = POS(m, n); int acode = 0; int apos = NO_MOVE; int dcode = 0; int dpos = NO_MOVE; int off_edge = 0; const float a = 0.75; const float b = 2 - a; *attack_point = NO_MOVE; *defense_point = NO_MOVE; /* Check whether intersection is off the board. We must do this for * each board coordinate separately because points "off the corner" * are special cases. */ if (m < 0 || m >= board_size) off_edge++; if (n < 0 || n >= board_size) off_edge++; /* Must return 0 if both coordinates out of bounds. */ if (off_edge > 0) return (float) (off_edge % 2); /* Discard points within own eyespace, unless marginal or ko point. * * Comment: For some time discardment of points within own eyespace * was contingent on this being the same eyespace as that of the * examined vertex. This caused problems, e.g. in this position, * * |........ * |XXXXX... * |OOOOX... * |aO.OX... * |OXXOX... * |.XXOX... * +-------- * * where the empty vertex at a was evaluated as a false eye and the * whole group as dead (instead of living in seki). * * The reason for the requirement of less than two marginal * neighbors is this position: * * |.XXXX... * |.OOOX... * |O..OX... * |aOO.X... * |O..XX... * |..O.X... * |.X..X... * |..XXX... * * where the empty vertex at a should not count as a solid eye. * (The eyespace diagonally below a looks like this: * .! * ! * so we can clearly see why having two marginal vertices makes a * difference.) */ if (my_eye[pos].color == color && !my_eye[pos].marginal && my_eye[pos].marginal_neighbors < 2 && !(board[pos] == EMPTY && does_capture_something(pos, other))) return 0.0; if (board[pos] == EMPTY) { int your_safety = safe_move(pos, other); apos = pos; dpos = pos; /* We should normally have a safe move, but occasionally it may * happen that it's not safe. There are complications, however, * with a position like this: * * .XXXX| * XXOO.| * XO.O.| * XXO.O| * -----+ * * Therefore we ignore our own safety if opponent's safety depends * on ko. */ if (your_safety == 0) value = 0.0; else if (your_safety != WIN) value = a; else { /* So your_safety == WIN. */ int our_safety = safe_move(pos, color); if (our_safety == 0) { int k; value = 2.0; /* This check is intended to fix a certain special case, but might * be helpful in other situations as well. Consider this position, * happened in owl reading deep enough: * * |XXXXX * |XOOXX * |O.OOX * |.OXX. * +----- * * Without this check, the corner eye is considered false, not half- * eye. Thus, owl thinks that the capture gains at most one eye and * gives up. */ for (k = 4; k < 8; k++) { int diagonal = pos + delta[k]; int lib; if (board[diagonal] == other && findlib(diagonal, 1, &lib) == 1) { if (lib != pos && does_secure(color, lib, pos)) { value = 1.0; apos = lib; break; } } } } else if (our_safety == WIN) value = 1.0; else /* our_safety depends on ko. */ value = b; } } else if (board[pos] == color) { /* This stone had better be safe, otherwise we wouldn't have an * eyespace in the first place. */ value = 0.0; } else if (board[pos] == other) { if (stackp == 0) { acode = worm[pos].attack_codes[0]; apos = worm[pos].attack_points[0]; dcode = worm[pos].defense_codes[0]; dpos = worm[pos].defense_points[0]; } else attack_and_defend(pos, &acode, &apos, &dcode, &dpos); /* Must test acode first since dcode only is reliable if acode is * non-zero. */ if (acode == 0) value = 2.0; else if (dcode == 0) value = 0.0; else if (acode == WIN && dcode == WIN) value = 1.0; else if (acode == WIN && dcode != WIN) value = a; else if (acode != WIN && dcode == WIN) value = b; else if (acode != WIN && dcode != WIN) value = 1.0; /* Both contingent on ko. Probably can't happen. */ } if (value > 0.0 && value < 2.0) { /* FIXME: * Usually there are several attack and defense moves that would * be equally valid. It's not good that we make an arbitrary * choice at this point. */ ASSERT_ON_BOARD1(apos); ASSERT_ON_BOARD1(dpos); /* Notice: * The point to ATTACK the half eye is the point which DEFENDS * the stones on the diagonal intersection and vice versa. Thus * we must switch attack and defense points here. * If the vertex is empty, dpos == apos and it doesn't matter * whether we switch. */ *attack_point = dpos; *defense_point = apos; } return value; } /* Conservative relative of topological_eye(). Essentially the same * algorithm is used, but only tactically safe opponent strings on * diagonals are considered. This may underestimate the false/half eye * status, but it should never be overestimated. */ int obvious_false_eye(int pos, int color) { int i = I(pos); int j = J(pos); int k; int diagonal_sum = 0; for (k = 4; k < 8; k++) { int di = deltai[k]; int dj = deltaj[k]; if (!ON_BOARD2(i+di, j) && !ON_BOARD2(i, j+dj)) diagonal_sum--; if (!ON_BOARD2(i+di, j+dj)) diagonal_sum++; else if (BOARD(i+di, j+dj) == OTHER_COLOR(color) && !attack(POS(i+di, j+dj), NULL)) diagonal_sum += 2; } return diagonal_sum >= 4; } /* Set the parameters into struct eyevalue as follows: a = number of eyes if attacker plays first twice b = number of eyes if attacker plays first c = number of eyes if defender plays first d =number of eyes if defender plays first twice */ void set_eyevalue(struct eyevalue *e, int a, int b, int c, int d) { e->a = a; e->b = b; e->c = c; e->d = d; } /* Number of eyes if attacker plays first twice (the threat of the first * move by attacker). */ int min_eye_threat(struct eyevalue *e) { return e->a; } /* Number of eyes if attacker plays first followed by alternating play. */ int min_eyes(struct eyevalue *e) { return e->b; } /* Number of eyes if defender plays first followed by alternating play. */ int max_eyes(struct eyevalue *e) { return e->c; } /* Number of eyes if defender plays first twice (the threat of the first * move by defender). */ int max_eye_threat(struct eyevalue *e) { return e->d; } /* Add the eyevalues *e1 and *e2, leaving the result in *sum. It is * safe to let sum be the same as e1 or e2. */ void add_eyevalues(struct eyevalue *e1, struct eyevalue *e2, struct eyevalue *sum) { struct eyevalue res; res.a = gg_min(gg_min(e1->a + e2->c, e1->c + e2->a), gg_max(e1->a + e2->b, e1->b + e2->a)); res.b = gg_min(gg_max(e1->b + e2->b, gg_min(e1->a + e2->d, e1->b + e2->c)), gg_max(e1->b + e2->b, gg_min(e1->d + e2->a, e1->c + e2->b))); res.c = gg_max(gg_min(e1->c + e2->c, gg_max(e1->d + e2->a, e1->c + e2->b)), gg_min(e1->c + e2->c, gg_max(e1->a + e2->d, e1->b + e2->c))); res.d = gg_max(gg_max(e1->d + e2->b, e1->b + e2->d), gg_min(e1->d + e2->c, e1->c + e2->d)); /* The rules above give 0011 + 0002 = 0012, which is incorrect. Thus * we need this annoying exception. */ if ((e1->d - e1->c == 2 && e2->c - e2->b == 1) || (e1->c - e1->b == 1 && e2->d - e2->c == 2)) { res.d = gg_max(gg_min(e1->c + e2->d, e1->d + e2->b), gg_min(e1->d + e2->c, e1->b + e2->d)); } /* The temporary storage in res is necessary if sum is the same as * e1 or e2. */ sum->a = res.a; sum->b = res.b; sum->c = res.c; sum->d = res.d; } /* The impact on the number of eyes (counting up to two) if a vital * move is made. The possible values are * 0 - settled eye, no vital move * 2 - 1/2 eye or 3/2 eyes * 3 - 3/4 eyes or 5/4 eyes * 4 - 1* eyes (a chimera) */ int eye_move_urgency(struct eyevalue *e) { int a = gg_min(e->a, 2); int b = gg_min(e->b, 2); int c = gg_min(e->c, 2); int d = gg_min(e->d, 2); if (b == c) return 0; else return d + c - b - a; } /* Produces a string representing the eyevalue. * * Note: the result string is stored in a statically allocated buffer * which will be overwritten the next time this function is called. */ char * eyevalue_to_string(struct eyevalue *e) { static char result[30]; if (e->a < 10 && e->b < 10 && e->c < 10 && e->d < 10) gg_snprintf(result, 29, "%d%d%d%d", e->a, e->b, e->c, e->d); else gg_snprintf(result, 29, "[%d,%d,%d,%d]", e->a, e->b, e->c, e->d); return result; } /* Test whether the optics code evaluates an eyeshape consistently. */ void test_eyeshape(int eyesize, int *eye_vertices) { int k; int n, N; int mx[BOARDMAX]; int pos; int str = NO_MOVE; int attack_code; int attack_point; int defense_code; int defense_point; int save_verbose; struct board_state starting_position; /* Clear the board and initialize the engine properly. */ clear_board(); reset_engine(); /* Mark the eyespace in the mx array. */ memset(mx, 0, sizeof(mx)); for (k = 0; k < eyesize; k++) { ASSERT_ON_BOARD1(eye_vertices[k]); mx[eye_vertices[k]] = 1; } /* Play white stones surrounding the eyespace, including diagonals. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (!ON_BOARD(pos) || mx[pos] == 1) continue; for (k = 0; k < 8; k++) { if (ON_BOARD(pos + delta[k]) && mx[pos + delta[k]] == 1) { play_move(pos, WHITE); str = pos; break; } } } /* Play black stones surrounding the white group, but leaving all * liberties empty. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) { if (mx[pos] == 1 || board[pos] != EMPTY || liberty_of_string(pos, str)) continue; for (k = 0; k < 8; k++) { if (ON_BOARD(pos + delta[k]) && liberty_of_string(pos + delta[k], str)) { play_move(pos, BLACK); break; } } } /* Show the board if verbose is on. Then turn off traces so we don't * get any from make_worms(), make_dragons(), or the owl reading. */ if (verbose) showboard(0); save_verbose = verbose; verbose = 0; /* Store this position so we can come back to it. */ store_board(&starting_position); /* Loop over all configurations of black stones inserted in the * eyeshape. There are N = 2^(eyesize) configurations and we can * straightforwardly use binary representation to enumerate them. */ N = 1 << eyesize; for (n = 0; n < N; n++) { int valid = 1; int internal_stones = 0; restore_board(&starting_position); /* Play the stones for this configuration. */ for (k = 0; k < eyesize; k++) { if (n & (1 << k)) { if (!is_legal(eye_vertices[k], BLACK)) { valid = 0; break; } play_move(eye_vertices[k], BLACK); internal_stones++; } } if (!valid) continue; if (save_verbose > 1) showboard(0); /* Now we are ready to test the consistency. This is most easily * done with help from the owl code. First we must prepare for * this though. */ examine_position(EXAMINE_DRAGONS_WITHOUT_OWL, 0); attack_code = owl_attack(str, &attack_point, NULL, NULL); if (attack_code == 0) { /* The owl code claims there is no attack. We test this by * trying to attack on all empty spaces in the eyeshape. */ for (k = 0; k < eyesize; k++) { if (board[eye_vertices[k]] == EMPTY && is_legal(eye_vertices[k], BLACK) && owl_does_attack(eye_vertices[k], str, NULL)) { gprintf("%1m alive, but %1m attacks:\n", str, eye_vertices[k]); showboard(0); gprintf("\n"); } } /* Furthermore, if the eyespace is almost filled, white should * be able to play on the remaining eyespace point and still be * alive. */ if (internal_stones == eyesize - 1) { for (k = 0; k < eyesize; k++) { if (board[eye_vertices[k]] == EMPTY && !owl_does_defend(eye_vertices[k], str, NULL)) { gprintf("%1m alive, but almost filled with nakade:\n", str); showboard(0); } } } } else { defense_code = owl_defend(str, &defense_point, NULL, NULL); if (defense_code == 0) { /* The owl code claims there is no defense. We test this by * trying to defend on all empty spaces in the eyeshape. */ for (k = 0; k < eyesize; k++) { if (board[eye_vertices[k]] == EMPTY && is_legal(eye_vertices[k], WHITE) && owl_does_defend(eye_vertices[k], str, NULL)) { gprintf("%1m dead, but %1m defends:\n", str, eye_vertices[k]); showboard(0); gprintf("\n"); } } } else { /* The owl code claims the dragon is critical. Verify the * attack and defense points. */ if (board[attack_point] != EMPTY || !is_legal(attack_point, BLACK)) { gprintf("Bad attack point %1m:\n", attack_point); showboard(0); } else if (!owl_does_attack(attack_point, str, NULL)) { gprintf("Attack point %1m failed:\n", attack_point); showboard(0); } if (board[defense_point] != EMPTY || !is_legal(defense_point, WHITE)) { gprintf("Bad defense point %1m:\n", defense_point); showboard(0); } else if (!owl_does_defend(defense_point, str, NULL)) { gprintf("Defense point %1m failed:\n", defense_point); showboard(0); } } } } verbose = save_verbose; } /******************************************************************** * The following static functions are helpers for analyze_eyegraph() * further down. The purpose is to evaluate eye graphs according to * the rules for local games, as described in doc/eyes.texi. * * The technique to do this is to convert the eye evaluation problem * into a tactical style life and death reading problem. Tactical in * the sense of needing to decide whether certain stones can be * captured, but not in the sense of the tactical reading that five * liberties are considered safe. * * We illustrate how this works with an example. Consider the eye shape * * ! * .X * !... * * The basic idea is to embed the eyespace in a perfectly connected * group without additional eyes or eye potential. This is most easily * done by the somewhat brutal trick to fill the entire board with * stones. We let the group consist of white stones (O) and get this * result, disregarding the two marginal eye vertices: * * A B C D E F G H J K L M N O P Q R S T * 19 O O O O O O O O O O O O O O O O O O O 19 * 18 O O O O O O O O O O O O O O O O O O O 18 * 17 O O O O O O O O O O O O O O O O O O O 17 * 16 O O O O O O O O O O O O O O O O O O O 16 * 15 O O O O O O O O O O O O O O O O O O O 15 * 14 O O O O O O O O O O O O O O O O O O O 14 * 13 O O O O O O O O O O O O O O O O O O O 13 * 12 O O O O O O O O . O O O O O O O O O O 12 * 11 O O O O O O O . X O O O O O O O O O O 11 * 10 O O O O O O . . . . O O O O O O O O O 10 * 9 O O O O O O O O O O O O O O O O O O O 9 * 8 O O O O O O O O O O O O O O O O O O O 8 * 7 O O O O O O O O O O O O O O O O O O O 7 * 6 O O O O O O O O O O O O O O O O O O O 6 * 5 O O O O O O O O O O O O O O O O O O O 5 * 4 O O O O O O O O O O O O O O O O O O O 4 * 3 O O O O O O O O O O O O O O O O O O O 3 * 2 O O O O O O O O O O O O O O O O O O O 2 * 1 O O O O O O O O O O O O O O O O O O O 1 * A B C D E F G H J K L M N O P Q R S T * * The question now is whether black can capture all the white stones * under alternating play where only white may pass. However, first we * need to make the top and leftmost eye vertices marginal. This is * done by inserting small invincible black groups in the sea of white * stones, in contact with the marginal vertices. * * A B C D E F G H J K L M N O P Q R S T * 19 . O O O O O O O O O O O O O O O O O O 19 * 18 O O O O O O O O X X X O O O O O O O O 18 * 17 O O O O O O O O X . X O O O O O O O O 17 * 16 O O O O O O O O X X X O O O O O O O O 16 * 15 O O O O O O O O X . X O O O O O O O O 15 * 14 O O O O O O O O X X X O O O O O O O O 14 * 13 O O O O O O O O X O O O O O O O O O O 13 * 12 O O O O O O O O . O O O O O O O O O O 12 * 11 O O O O O O O . X O O O O O O O O O O 11 * 10 O O O O O O . . . . O O O O O O O O O 10 * 9 O O O O O O X O O O O O O O O O O O O 9 * 8 O O O O X X X O O O O O O O O O O O O 8 * 7 O O O O X . X O O O O O O O O O O O O 7 * 6 O O O O X X X O O O O O O O O O O O O 6 * 5 O O O O X . X O O O O O O O O O O O O 5 * 4 . O O O X X X O O O O O O O O O O O O 4 * 3 X X . O O O O O O O O O O O O O O O O 3 * 2 X . X O O O O O O O O O O O O O O O O 2 * 1 . X X O O O O O O O O O O O O O O O O 1 * A B C D E F G H J K L M N O P Q R S T * * In this diagram we have also added an invincible black group in the * lower left corner in order to add two outer liberties (at A4 and * C3) for the white group (this is sometimes needed for the tactical * life and death reading to make sense). Furthermore there is an * extra eye at A19. This is used when we want to distinguish between * 0 and 1 (or 2) eyes since the tactical life and death reading by * itself only cares about two eyes or not. When trying to distinguish * between 1 (or 0) and 2 eyes we first fill in A19 again. * * Depending on the tactical life and death status with or without the * extra eye we can determine the number of eyes. By evaluating * tactical life and death status after having made a move we can also * identify ko threats and critical moves. * * This code is organized as follows: * * analyze_eyegraph() converts the eyegraph into the tactical board * position as demonstrated, then calls evaluate_eyespace() to its eye * value. * * white_area() is a helper to add a small invincible black group on * the board. * * evaluate_eyespace() calls tactical_life() and itself recursively to * determine the eye value and the critical points. * * tactical_life() determines whether the white stones on the board * (assumed to be a single string) can be captured under alternating * play. * * tactical_life_attack() and tactical_life_defend() are two mutually * recursive functions which perform the actual reading for * tactical_life(). * * Worth to mention in this overview is also the cache used for * tactical_life_attack() and tactical_life_defend(). Since we have a * limited number of vertices (eye space points + two outer liberties * + possibly an extra eye) to play on we use a complete cache with a * unique entry for every possible configuration of stones on the * considered vertices. * * For each cache entry four bits are used, two for attack results and * two four defense results. Each of these can take the values 0-3 * with the following interpretations: * 0 - not yet considered * 1 - result is being computed * 2 - result has been computed and was a failure (0) * 3 - result has been computed and was a success (1) */ /* Like trymove() except that it does a superko check. This does, * however, only disallow repetition (besides simple ko) if the move * does not capture any stones. */ static int eyegraph_trymove(int pos, int color, const char *message, int str) { static Hash_data remembered_board_hashes[MAXSTACK]; int k; int does_capture = does_capture_something(pos, color); remembered_board_hashes[stackp] = board_hash; if (!trymove(pos, color, message, str)) return 0; if (does_capture) return 1; for (k = 0; k < stackp; k++) if (hashdata_is_equal(board_hash, remembered_board_hashes[k])) { popgo(); return 0; } return 1; } static int eyegraph_is_margin_or_outer_liberty(int vertex) { int k; int r; int num_libs; int libs[MAXLIBS]; int eyes; for (k = 0; k < 4; k++) { if (board[vertex + delta[k]] == BLACK) { eyes = 0; num_libs = findlib(vertex + delta[k], MAXLIBS, libs); for (r = 0; r < num_libs; r++) if (is_suicide(libs[r], WHITE)) eyes++; if (eyes >= 2) return 1; } } return 0; } static int eyegraph_order_moves(int num_vertices, int *vertices, int color_to_move, int *moves) { int num_moves = 0; int scores[BOARDMAX]; int move; int score; int k; int r; for (k = 0; k < num_vertices; k++) { if (k >= num_vertices - 3) { /* Never useful for white to fill in outer liberties or a second eye. */ if (color_to_move == WHITE) break; /* No use playing the second outer liberty before the first one. */ if (k == num_vertices - 2 && board[vertices[num_vertices - 3]] == EMPTY) continue; } move = vertices[k]; score = 0; if (board[move] != EMPTY) continue; if (eyegraph_is_margin_or_outer_liberty(move)) { if (k < num_vertices - 3) score = 5; /* margin */ else score = -10; /* outer liberty */ } if (accuratelib(move, color_to_move, 2, NULL) == 1) score -= 3; for (r = 0; r < 4; r++) { if (board[move + delta[r]] == EMPTY) score += 2; else if (board[move + delta[r]] == BLACK) score += 3; } moves[num_moves] = move; scores[num_moves] = score; num_moves++; } for (k = 0; k < num_moves; k++) { int maxscore = scores[k]; int max_at = 0; /* Find the move with the biggest score. */ for (r = k + 1; r < num_moves; r++) { if (scores[r] > maxscore) { maxscore = scores[r]; max_at = r; } } /* Now exchange the move at k with the move at max_at. * Don't forget to exchange the scores as well. */ if (max_at != 0) { int temp = moves[max_at]; moves[max_at] = moves[k]; moves[k] = temp; temp = scores[max_at]; scores[max_at] = scores[k]; scores[k] = temp; } } return num_moves; } /* Place a small invincible black group on the board. * It is required that previously there were white stones at all * involved vertices and on the surrounding vertices. * * Returns 1 if a group was placed, 0 otherwise. */ static int white_area(int mx[BOARDMAX], int pos, int up, int right, int marginpos, int distance) { int u, v; int k; int edge = is_edge_vertex(marginpos); for (k = 1; k < distance; k++) if (!ON_BOARD(marginpos + k * up) || mx[marginpos + k * up] != WHITE) return 0; for (u = -1; u <= 4; u++) for (v = -1; v <= 4; v++) { int pos2 = pos + u * up + v * right; if (!ON_BOARD(pos2)) { if (!edge) return 0; else if (u >= 0 && u <= 3 && v >= 0 && v <= 3) return 0; else if (I(pos2) != I(NORTH(marginpos)) && I(pos2) != I(SOUTH(marginpos)) && J(pos2) != J(WEST(marginpos)) && J(pos2) != J(EAST(marginpos))) return 0; } else if (mx[pos2] != WHITE) return 0; } for (u = 0; u <= 3; u++) for (v = 0; v <= 3; v++) { int pos2 = pos + u * up + v * right; mx[pos2] = BLACK; } mx[pos + up + right] = EMPTY; mx[pos + 2 * up + 2 * right] = EMPTY; return 1; } #define EYEGRAPH_RETURN(result, trace) \ do { \ if (sgf_dumptree) \ sgftreeAddComment(sgf_dumptree, (trace)); \ return (result); \ } while (0); static int tactical_life_defend(int str, int num_vertices, int *vertices, unsigned char *results); /* Determine whether black can capture all white stones. */ static int tactical_life_attack(int str, int num_vertices, int *vertices, unsigned char *results) { int k; int hash = 0; int cached_result; int result; int num_moves; int moves[BOARDMAX]; /* Compute hash value to index the result cache with. */ for (k = 0; k < num_vertices; k++) { hash *= 3; hash += board[vertices[k]]; } hash *= 2; hash += (board_ko_pos != NO_MOVE); /* Is the result known from the cache? */ cached_result = results[hash] & 3; if (0) { showboard(0); gprintf("%d %d (%d)\n", hash, cached_result, results[hash]); } if (cached_result == 2) EYEGRAPH_RETURN(0, "tactical_life_attack: 0 (cached)"); if (cached_result == 3) EYEGRAPH_RETURN(1, "tactical_life_attack: win (cached)"); if (cached_result == 1) EYEGRAPH_RETURN(1, "tactical_life_attack: win (open node in cache)"); /* Mark this entry in the cache as currently being computed. */ results[hash] |= 1; /* Try to play on all relevant vertices. */ num_moves = eyegraph_order_moves(num_vertices, vertices, OTHER_COLOR(board[str]), moves); for (k = 0; k < num_moves; k++) { int move = moves[k]; if (eyegraph_trymove(move, OTHER_COLOR(board[str]), "tactical_life_attack", str)) { /* We were successful if the white stones were captured or if no * defense can be found. */ if (board[str] == EMPTY) result = 1; else result = !tactical_life_defend(str, num_vertices, vertices, results); popgo(); if (result == 1) { /* Store the result (success) in the cache. */ results[hash] = (results[hash] & (~3)) | 3; EYEGRAPH_RETURN(1, "tactical_life_attack: win"); } } } /* Store the result (failure) in the cache. */ results[hash] = (results[hash] & (~3)) | 2; EYEGRAPH_RETURN(0, "tactical_life_attack: 0"); } /* Determine whether white can live with all stones. */ static int tactical_life_defend(int str, int num_vertices, int *vertices, unsigned char *results) { int k; int hash = 0; int cached_result; int result; int num_moves; int moves[BOARDMAX]; /* Compute hash value to index the result cache with. */ for (k = 0; k < num_vertices; k++) { hash *= 3; ASSERT1(board[vertices[k]] <= 2, vertices[k]); hash += board[vertices[k]]; } hash *= 2; hash += (board_ko_pos != NO_MOVE); /* Is the result known from the cache? */ cached_result = (results[hash] >> 2) & 3; if (0) { showboard(0); gprintf("%d %d (%d)\n", hash, cached_result, results[hash]); } if (cached_result == 2) EYEGRAPH_RETURN(0, "tactical_life_defend: 0 (cached)"); if (cached_result == 3) EYEGRAPH_RETURN(1, "tactical_life_defend: win (cached)"); if (cached_result == 1) EYEGRAPH_RETURN(1, "tactical_life_defend: win (node open in cache)"); /* Mark this entry in the cache as currently being computed. */ results[hash] |= (1 << 2); /* Try to play on all relevant vertices. */ num_moves = eyegraph_order_moves(num_vertices, vertices, board[str], moves); for (k = 0; k < num_moves; k++) { int move = moves[k]; if ((!is_suicide(move, OTHER_COLOR(board[str])) || does_capture_something(move, board[str])) && eyegraph_trymove(move, board[str], "tactical_life_defend", str)) { /* We were successful if no attack can be found. */ result = !tactical_life_attack(str, num_vertices, vertices, results); popgo(); if (result == 1) { /* Store the result (success) in the cache. */ results[hash] = (results[hash] & (~12)) | (3 << 2); EYEGRAPH_RETURN(1, "tactical_life_defend: win"); } } } /* If no move worked, also try passing. */ if (!tactical_life_attack(str, num_vertices, vertices, results)) { /* Store the result (success) in the cache. */ results[hash] = (results[hash] & (~12)) | (3 << 2); EYEGRAPH_RETURN(1, "tactical_life_defend: win"); } /* Store the result (failure) in the cache. */ results[hash] = (results[hash] & (~12)) | (2 << 2); EYEGRAPH_RETURN(0, "tactical_life_defend: 0"); } /* Determine the tactical life and death status of all white stones. * Also find all attack and defense moves. The parameter have_eye * determines whether the extra eye in the upper left corner should be * used or filled in before starting reading. */ static void tactical_life(int have_eye, int num_vertices, int *vertices, int *attack_code, int *num_attacks, int *attack_points, int *defense_code, int *num_defenses, int *defense_points, unsigned char *results) { int k; int str; int num_moves; int moves[BOARDMAX]; gg_assert(attack_code != NULL && defense_code != NULL); /* We know that the large white group includes A18. This is the * vertex we test to determine whether the white stones have been * captured. */ str = POS(1, 0); if (board[str] == EMPTY) { /* The stones have already been captured, too late to defend. */ *attack_code = WIN; *defense_code = 0; return; } /* Fill in the extra eye if have_eye is 0. If filling in would be * suicide the white stones can be considered dead. */ if (!have_eye) { if (!eyegraph_trymove(POS(0, 0), WHITE, "tactical_life-A", NO_MOVE)) { *attack_code = WIN; *defense_code = 0; return; } } *attack_code = 0; *defense_code = 0; /* Call tactical_life_attack() and tactical_life_defend() to * determine status. */ if (tactical_life_attack(str, num_vertices, vertices, results)) { *attack_code = WIN; if (tactical_life_defend(str, num_vertices, vertices, results)) *defense_code = WIN; } else *defense_code = WIN; /* If the status is critical, try to play at each relevant vertex * and call tactical_life_defend() or tactical_life_attack() to * determine whether the move works as attack or defense. */ if (*attack_code != 0 && *defense_code != 0) { if (num_attacks != NULL && attack_points != NULL) { *num_attacks = 0; num_moves = eyegraph_order_moves(num_vertices, vertices, OTHER_COLOR(board[str]), moves); for (k = 0; k < num_moves; k++) { int move = moves[k]; if (eyegraph_trymove(move, OTHER_COLOR(board[str]), "tactical_life-B", str)) { if (board[str] == EMPTY || !tactical_life_defend(str, num_vertices, vertices, results)) attack_points[(*num_attacks)++] = move; popgo(); } } } if (num_defenses != NULL && defense_points != NULL) { *num_defenses = 0; num_moves = eyegraph_order_moves(num_vertices, vertices, board[str], moves); for (k = 0; k < num_moves; k++) { int move = moves[k]; if (eyegraph_trymove(move, board[str], "tactical_life-C", str)) { if (!tactical_life_attack(str, num_vertices, vertices, results)) defense_points[(*num_defenses)++] = move; popgo(); } } } } /* Unfill the extra eye if we didn't use it. */ if (!have_eye) popgo(); } /* Determine the eye value of the eyespace for the big white group on * the board and vital moves. The possible eye values are documented * in the preamble to eyes.db. By calling tactical_life() multiple * times, with and without using an extra eye, we can compute the eye * values. To determine ko threats and vital moves, tactical_life() is * called again after trying to play on one of the relevant vertices. * In order to find out whether ko threats really are effective and to * distinguish between 0122/1122 and 0012/0011 eye values (see * discussion on pattern 6141 in the preamble of eyes.db), we may also * need to recursively call ourselves after a move has been made. */ static void evaluate_eyespace(struct eyevalue *result, int num_vertices, int *vertices, int *num_vital_attacks, int *vital_attacks, int *num_vital_defenses, int *vital_defenses, unsigned char *tactical_life_results) { int k; int attack_code; int num_attacks; int attack_points[BOARDMAX]; int defense_code; int num_defenses; int defense_points[BOARDMAX]; int attack_code2; int num_attacks2; int attack_points2[BOARDMAX]; int defense_code2; struct eyevalue result2; int num_vital_attacks2; int vital_attacks2[BOARDMAX]; int num_vital_defenses2; int vital_defenses2[BOARDMAX]; int num_moves; int moves[BOARDMAX]; *num_vital_attacks = 0; *num_vital_defenses = 0; /* Determine tactical life without an extra eye. */ tactical_life(0, num_vertices, vertices, &attack_code, &num_attacks, attack_points, &defense_code, &num_defenses, defense_points, tactical_life_results); if (attack_code == 0) { /* Alive without extra eye. * Possible results: 0222, 1222, 2222 * * Determine whether there are ko threats and how serious. */ int a = 2; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Alive without extra eye.\n"); num_moves = eyegraph_order_moves(num_vertices, vertices, BLACK, moves); for (k = 0; k < num_moves; k++) { int acode, dcode; int move = moves[k]; if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-A", NO_MOVE)) { tactical_life(0, num_vertices, vertices, &acode, NULL, NULL, &dcode, NULL, NULL, tactical_life_results); if (acode != 0) { tactical_life(1, num_vertices, vertices, &acode, NULL, NULL, &dcode, NULL, NULL, tactical_life_results); if (acode != 0) { if (a == 1) *num_vital_attacks = 0; a = 0; vital_attacks[(*num_vital_attacks)++] = move; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Ko threat to remove both eyes.\n"); } else { if (a != 0) { vital_attacks[(*num_vital_attacks)++] = move; a = 1; } if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Ko threat to remove one eye.\n"); } } popgo(); } } set_eyevalue(result, a, 2, 2, 2); if (sgf_dumptree) { if (a == 0) sgftreeAddComment(sgf_dumptree, "Eyevalue 0222.\n"); else if (a == 1) sgftreeAddComment(sgf_dumptree, "Eyevalue 1222.\n"); else sgftreeAddComment(sgf_dumptree, "Eyevalue 2222.\n"); } } else if (defense_code != 0) { /* Critical without extra eye. * Possible results: 0022, 0122, 1122 */ if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Critical without extra eye.\n"); tactical_life(1, num_vertices, vertices, &attack_code2, &num_attacks2, attack_points2, &defense_code2, NULL, NULL, tactical_life_results); for (k = 0; k < num_defenses; k++) vital_defenses[(*num_vital_defenses)++] = defense_points[k]; if (attack_code2 == WIN) { /* A chimera. 0022. */ set_eyevalue(result, 0, 0, 2, 2); for (k = 0; k < num_attacks2; k++) vital_attacks[(*num_vital_attacks)++] = attack_points2[k]; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Eyevalue: 0022.\n"); } else { int a = 1; for (k = 0; k < num_attacks; k++) { int move = attack_points[k]; if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-B", NO_MOVE)) { evaluate_eyespace(&result2, num_vertices, vertices, &num_vital_attacks2, vital_attacks2, &num_vital_defenses2, vital_defenses2, tactical_life_results); /* If result2 is 0011 for some move we have 0122 as final * result, otherwise 1122. */ if (min_eyes(&result2) == 0 && max_eyes(&result2) == 1 && max_eye_threat(&result2) == 1) { if (a == 1) *num_vital_attacks = 0; a = 0; vital_attacks[(*num_vital_attacks)++] = move; } else if (a == 1) vital_attacks[(*num_vital_attacks)++] = move; popgo(); } } set_eyevalue(result, a, 1, 2, 2); if (sgf_dumptree) { if (a == 0) sgftreeAddComment(sgf_dumptree, "Eyevalue: 0122.\n"); else sgftreeAddComment(sgf_dumptree, "Eyevalue: 1122.\n"); } } } else { /* Dead without extra eye. * Possible results: 0000, 0001, 0002, 0011, 0012, 0111, 0112, 1111, 1112 * * Now determine tactical life with an extra eye. */ if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Dead without extra eye.\n"); tactical_life(1, num_vertices, vertices, &attack_code, &num_attacks, attack_points, &defense_code, &num_defenses, defense_points, tactical_life_results); if (attack_code == 0) { /* Alive with extra eye. * Possible results: 0111, 0112, 1111, 1112 */ int a = 1; int d = 1; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Alive with extra eye.\n"); num_moves = eyegraph_order_moves(num_vertices, vertices, BLACK, moves); for (k = 0; k < num_moves; k++) { int acode, dcode; int move = moves[k]; if (eyegraph_trymove(move, BLACK, "evaluate_eyespace-C", NO_MOVE)) { tactical_life(1, num_vertices, vertices, &acode, NULL, NULL, &dcode, NULL, NULL, tactical_life_results); if (acode != 0) { evaluate_eyespace(&result2, num_vertices, vertices, &num_vital_attacks2, vital_attacks2, &num_vital_defenses2, vital_defenses2, tactical_life_results); /* This is either 0011 or 0012. Only the first is acceptable. */ if (max_eye_threat(&result2) == 1) { vital_attacks[(*num_vital_attacks)++] = move; a = 0; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Attacking ko threat.\n"); } } popgo(); } } num_moves = eyegraph_order_moves(num_vertices, vertices, WHITE, moves); for (k = 0; k < num_moves; k++) { int acode, dcode; int move = moves[k]; if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-D", NO_MOVE)) { tactical_life(0, num_vertices, vertices, &acode, NULL, NULL, &dcode, NULL, NULL, tactical_life_results); if (dcode != 0) { evaluate_eyespace(&result2, num_vertices, vertices, &num_vital_attacks2, vital_attacks2, &num_vital_defenses2, vital_defenses2, tactical_life_results); /* This is either 1122 or 0122. Only the first is acceptable. */ if (min_eye_threat(&result2) == 1) { vital_defenses[(*num_vital_defenses)++] = move; d = 2; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Defending ko threat.\n"); } } popgo(); } } set_eyevalue(result, a, 1, 1, d); if (sgf_dumptree) { if (a == 0 && d == 1) sgftreeAddComment(sgf_dumptree, "Eyevalue 0111.\n"); else if (a == 0 && d == 2) sgftreeAddComment(sgf_dumptree, "Eyevalue 0112.\n"); else if (a == 1 && d == 1) sgftreeAddComment(sgf_dumptree, "Eyevalue 1111.\n"); else sgftreeAddComment(sgf_dumptree, "Eyevalue 1112.\n"); } } else if (defense_code != 0) { /* Critical with extra eye. * Possible results: 0011, 0012 */ int d = 1; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Critical with extra eye.\n"); for (k = 0; k < num_attacks; k++) vital_attacks[(*num_vital_attacks)++] = attack_points[k]; for (k = 0; k < num_defenses; k++) { int move = defense_points[k]; if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-E", NO_MOVE)) { evaluate_eyespace(&result2, num_vertices, vertices, &num_vital_attacks2, vital_attacks2, &num_vital_defenses2, vital_defenses2, tactical_life_results); /* If result2 is 1122 for some move we have 0012 as final * result, otherwise 0011. */ if (min_eye_threat(&result2) == 1 && min_eyes(&result2) == 1 && max_eyes(&result2) == 2) { if (d == 1) *num_vital_defenses = 0; d = 2; vital_defenses[(*num_vital_defenses)++] = move; } else if (d == 1) vital_defenses[(*num_vital_defenses)++] = move; popgo(); } } set_eyevalue(result, 0, 0, 1, d); if (sgf_dumptree) { if (d == 1) sgftreeAddComment(sgf_dumptree, "Eyevalue: 0011.\n"); else sgftreeAddComment(sgf_dumptree, "Eyevalue: 0012.\n"); } } else { /* Dead with extra eye. * Possible results: 0000, 0001, 0002 * * Determine whether there are ko threats and how serious. */ int d = 0; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Dead with extra eye.\n"); num_moves = eyegraph_order_moves(num_vertices, vertices, WHITE, moves); for (k = 0; k < num_moves; k++) { int acode, dcode; int move = moves[k]; if (eyegraph_trymove(move, WHITE, "evaluate_eyespace-F", NO_MOVE)) { tactical_life(1, num_vertices, vertices, &acode, NULL, NULL, &dcode, NULL, NULL, tactical_life_results); if (dcode != 0) { tactical_life(0, num_vertices, vertices, &acode, NULL, NULL, &dcode, NULL, NULL, tactical_life_results); if (dcode != 0) { if (d == 1) *num_vital_defenses = 0; d = 2; vital_defenses[(*num_vital_defenses)++] = move; if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Ko threat to make two eyes.\n"); } else { if (d != 2) { vital_defenses[(*num_vital_defenses)++] = move; d = 1; } if (sgf_dumptree) sgftreeAddComment(sgf_dumptree, "Ko threat to make one eye.\n"); } } popgo(); } } set_eyevalue(result, 0, 0, 0, d); if (sgf_dumptree) { if (d == 0) sgftreeAddComment(sgf_dumptree, "Eyevalue 0000.\n"); else if (d == 1) sgftreeAddComment(sgf_dumptree, "Eyevalue 0001.\n"); else sgftreeAddComment(sgf_dumptree, "Eyevalue 0002.\n"); } } } } /* Add small invincible black groups in contact with the marginal * vertices, without destroying the connectivity of the white stones. * */ static int add_margins(int num_margins, int *margins, int mx[BOARDMAX]) { int k; int i, j; int old_mx[BOARDMAX]; int pos; if (num_margins == 0) return 1; memcpy(old_mx, mx, sizeof(old_mx)); pos = margins[num_margins - 1]; for (k = 0; k < 4; k++) { int up = delta[k]; int right = delta[(k + 1) % 4]; if (!ON_BOARD(pos + up)) continue; if (mx[pos + up] == WHITE && (!ON_BOARD(pos + up + right) || mx[pos + up + right] == WHITE) && (!ON_BOARD(pos + up - right) || mx[pos + up - right] == WHITE)) { for (i = -3; i <= 0; i++) { for (j = 2; j < 6; j++) { if (white_area(mx, pos + j * up + i * right, up, right, pos, j)) { int s = 1; while (mx[pos + s * up] == WHITE) { mx[pos + s * up] = BLACK; s++; } if (add_margins(num_margins - 1, margins, mx)) return 1; else memcpy(mx, old_mx, sizeof(old_mx)); } } } } } return 0; } /* Analyze an eye graph to determine the eye value and vital moves. * * The eye graph is given by a string which is encoded with "%" for * newlines and "O" for spaces. E.g., the eye graph * * ! * .X * !... * * is encoded as "OO!%O.X%!...". (The encoding is needed for the GTP * interface to this function.) * * The result is an eye value and a (nonencoded) pattern showing the * vital moves, using the same notation as eyes.db. In the example above * we would get the eye value 0112 and the graph (showing ko threat moves) * * @ * .X * !.*. * * If the eye graph cannot be realized, 0 is returned, 1 otherwise. */ int analyze_eyegraph(const char *coded_eyegraph, struct eyevalue *value, char *analyzed_eyegraph) { int k; int i, j; int mini, minj; int mx[BOARDMAX]; char mg[BOARDMAX]; int pos; int num_vital_attacks; int vital_attacks[BOARDMAX]; /* Way larger than necessary. */ int num_vital_defenses; int vital_defenses[BOARDMAX]; /* Way larger than necessary. */ int maxwidth; int current_width; int num_rows; int horizontal_edge; int vertical_edge; int num_margins; int margins[BOARDMAX]; /* Way larger than necessary. */ int num_vertices; int vertices[BOARDMAX]; /* Way larger than necessary. */ int table_size; unsigned char *tactical_life_results; if (0) gprintf("Analyze eyegraph %s\n", coded_eyegraph); /* Mark the eyespace in the mx array. We construct the position in * the mx array and copy it to the actual board later. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (ON_BOARD(pos)) mx[pos] = WHITE; /* Find out the size of the eye graph pattern so that we can center * it properly. */ maxwidth = 0; current_width = 0; num_rows = 1; horizontal_edge = -1; vertical_edge = -1; for (k = 0; k < (int) strlen(coded_eyegraph); k++) { if (coded_eyegraph[k] == '\n') continue; if (coded_eyegraph[k] == '%') { num_rows++; if (current_width > maxwidth) maxwidth = current_width; current_width = 0; } else { if (coded_eyegraph[k] == '-') horizontal_edge = num_rows - 1; else if (coded_eyegraph[k] == '|') vertical_edge = current_width; current_width++; } } if (current_width > maxwidth) maxwidth = current_width; /* Cut out the eyespace from the solid white string. */ num_margins = 0; num_vertices = 0; if (horizontal_edge == 0) mini = -1; else if (horizontal_edge > 0) mini = board_size - num_rows + 1; else mini = (board_size - num_rows) / 2; if (vertical_edge == 0) minj = -1; else if (vertical_edge > 0) minj = board_size - maxwidth + 1; else minj = (board_size - maxwidth) / 2; i = mini; j = minj; for (k = 0; k < (int) strlen(coded_eyegraph); k++) { char c = coded_eyegraph[k]; if (c == '\n') continue; if (c == '%') { i++; j = minj - 1; } else if (c == 'X' || c == '$') mx[POS(i, j)] = BLACK; else if (c == '.' || c == '*' || c == '<' || c == '>' || c == '!' || c == '@' || c == '(' || c == ')') mx[POS(i, j)] = EMPTY; if (c == '!' || c == '@' || c == '(' || c == ')' || c == '$') margins[num_margins++] = POS(i, j); if (c != '|' && c != '-' && c != '+' && c != '%' && ON_BOARD(POS(i, j)) && mx[POS(i, j)] != WHITE) vertices[num_vertices++] = POS(i, j); j++; } /* Add an invincible black group in the lower left plus two outer * liberties for the white string. However, if the eyespace is * placed in or near the lower left corner, we put this group in the * upper right instead. */ pos = POS(board_size - 2, 1); if ((vertical_edge == 0 && horizontal_edge != 0) || (horizontal_edge > 0 && vertical_edge <= 0)) pos = POS(1, board_size - 2); mx[pos] = EMPTY; mx[NORTH(pos)] = BLACK; mx[NW(pos)] = BLACK; mx[NE(pos)] = EMPTY; mx[WEST(pos)] = BLACK; mx[EAST(pos)] = BLACK; mx[SW(pos)] = EMPTY; mx[SOUTH(pos)] = BLACK; mx[SE(pos)] = BLACK; if (ON_BOARD(NN(pos))) mx[NN(pos)] = EMPTY; else mx[SS(pos)] = EMPTY; /* Add the two outer liberties in the lower left or upper right to * the list of vertices. */ if (ON_BOARD(NN(pos))) { vertices[num_vertices++] = NE(pos); vertices[num_vertices++] = NN(pos); } else { vertices[num_vertices++] = SW(pos); vertices[num_vertices++] = SS(pos); } /* Add an extra eye in the upper left corner. */ mx[POS(0, 0)] = EMPTY; vertices[num_vertices++] = POS(0, 0); if (!add_margins(num_margins, margins, mx)) return 0; /* Copy the mx array over to the board. */ clear_board(); for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (ON_BOARD(pos)) { if (mx[pos] == WHITE) add_stone(pos, WHITE); else if (mx[pos] == BLACK) add_stone(pos, BLACK); } if (verbose) showboard(0); /* If there are any isolated O stones, those should also be added to * the playable vertices. */ for (pos = BOARDMIN; pos < BOARDMAX; pos++) if (board[pos] == WHITE && !same_string(pos, POS(1, 0))) { vertices[num_vertices] = vertices[num_vertices - 1]; vertices[num_vertices - 1] = vertices[num_vertices - 2]; vertices[num_vertices - 2] = vertices[num_vertices - 3]; vertices[num_vertices - 3] = pos; num_vertices++; } if (verbose) { int k; gprintf("\nPlayable vertices:\n"); for (k = 0; k < num_vertices; k++) gprintf("%1m ", vertices[k]); gprintf("\n\n"); } /* Disable this test if you need to evaluate larger eyespaces, have * no shortage of memory, and know what you're doing. */ if (num_vertices > 17) { gprintf("analyze_eyegraph: too large eyespace, %d vertices\n", num_vertices); gg_assert(num_vertices <= 17); } /* The cache must have 2*3^num_vertices entries. */ table_size = 2; for (k = 0; k < num_vertices; k++) table_size *= 3; /* Allocate memory for the cache. */ tactical_life_results = malloc(table_size); if (!tactical_life_results) { gprintf("analyze_eyegraph: failed to allocate %d bytes\n", table_size); gg_assert(tactical_life_results != NULL); } memset(tactical_life_results, 0, table_size); if (sgf_dumptree) sgffile_printboard(sgf_dumptree); /* Evaluate the eyespace on the board. */ evaluate_eyespace(value, num_vertices, vertices, &num_vital_attacks, vital_attacks, &num_vital_defenses, vital_defenses, tactical_life_results); /* Return the cache memory. */ free(tactical_life_results); if (verbose) { gprintf("Eyevalue: %s\n", eyevalue_to_string(value)); for (k = 0; k < num_vital_attacks; k++) gprintf(" vital attack point %1m\n", vital_attacks[k]); for (k = 0; k < num_vital_defenses; k++) gprintf(" vital defense point %1m\n", vital_defenses[k]); } /* Encode the attack and defense points with symbols in the mg[] array. */ memset(mg, ' ', sizeof(mg)); for (k = 0; k < num_vertices - 2; k++) mg[vertices[k]] = (board[vertices[k]] == BLACK ? 'X' : '.'); for (k = 0; k < num_margins; k++) mg[margins[k]] = (mg[margins[k]] == 'X' ? '$' : '!'); for (k = 0; k < num_vital_attacks; k++) mg[vital_attacks[k]] = (mg[vital_attacks[k]] == '!' ? '(' : '<'); for (k = 0; k < num_vital_defenses; k++) { int pos = vital_defenses[k]; if (mg[pos] == '.') mg[pos] = '>'; else if (mg[pos] == '!') mg[pos] = ')'; else if (mg[pos] == '<') mg[pos] = '*'; else if (mg[pos] == '(') mg[pos] = '@'; } /* Return the central part of the mg[] array (corresponding to the * input eye graph). */ k = 0; for (i = mini; i < mini + num_rows; i++) { for (j = minj; j < minj + maxwidth; j++) { if ((i < 0 || i >= board_size) && (j < 0 || j >= board_size)) analyzed_eyegraph[k++] = '+'; else if (i < 0 || i >= board_size) analyzed_eyegraph[k++] = '-'; else if (j < 0 || j >= board_size) analyzed_eyegraph[k++] = '|'; else analyzed_eyegraph[k++] = mg[POS(i, j)]; } analyzed_eyegraph[k++] = '\n'; } analyzed_eyegraph[k - 1] = 0; return 1; } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */