/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ * 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 #include "liberty.h" #include "cache.h" #include "gg_utils.h" /* If nonzero, attack() and find_defense() write all results to * stderr. Use this to if you have deviations in results, but cannot * find where they come from. * * Redirect results to a file. Take dumps of two versions and * (assuming GNU tools) do `sort -t= -s' on both. Then join the * sorted dumps: * * join -t= sorted-first-dump sorted-second-dump \ * | sed -e "s/^[^=]\+=\([^=]\+\)=\1$//" | tr -s "\n" | tr = "\t" \ * | uniq * * to get a nice list of deviations. This is only meaningful if you * dump results of a single test (or at least tests originating at a * same position). */ #define DUMP_ALL_RESULTS 0 /* Size of array where candidate moves are stored. */ #define MAX_MOVES 50 /* Please notice that message had better be a fixed string. Only the * pointer to it is saved and there is no attempt to free up any * storage. */ #define ADD_CANDIDATE_MOVE(move, this_score, moves, this_message) \ do { \ int u; \ for (u = 0; u < (moves).num; u++) \ if ((moves).pos[u] == (move)) { \ (moves).score[u] += this_score; \ break; \ } \ if ((u == (moves).num) && ((moves).num < MAX_MOVES)) { \ (moves).pos[(moves).num] = move; \ (moves).score[(moves).num] = this_score; \ (moves).message[(moves).num] = this_message; \ (moves).num++; \ } \ } while (0) #define REMOVE_CANDIDATE_MOVE(move, moves) \ do { \ int u; \ for (u = (moves).num_tried; u < (moves).num; u++) { \ if ((moves).pos[u] == (move)) { \ (moves).pos[u] = (moves).pos[(moves).num - 1]; \ (moves).score[u] = (moves).score[(moves).num - 1]; \ (moves).message[u] = (moves).message[(moves).num - 1]; \ (moves).num--; \ break; \ } \ } \ } while (0) /* This macro checks whether the reported result is a loss, so we have won * and can exit, or else if it is the best result so far. * Note that SGFTRACE must have been setup. */ #define CHECK_RESULT(savecode, savemove, code, move_pos, move_ptr, \ trace_message) \ do { \ if (code == 0) { \ if (move_ptr) \ *(move_ptr) = (move_pos); \ SGFTRACE(move_pos, WIN, trace_message); \ return WIN; \ } \ else if (REVERSE_RESULT(code) > savecode) { \ savemove = move_pos; \ savecode = REVERSE_RESULT(code); \ } \ } while (0) /* Reverse of CHECK_RESULT, for results passed from a helper function. */ #define CHECK_RESULT_UNREVERSED(savecode, savemove, code, move_pos, \ move_ptr, trace_message) \ CHECK_RESULT(savecode, savemove, REVERSE_RESULT(code), move_pos, \ move_ptr, trace_message) #define RETURN_RESULT(savecode, savemove, move_ptr, trace_message) \ do { \ if (savecode) { \ if (move_ptr) \ *(move_ptr) = (savemove); \ SGFTRACE(savemove, savecode, trace_message); \ } \ else \ SGFTRACE(0, 0, NULL); \ return savecode; \ } while (0) /* Play a collected batch of moves and see if any of them works. This * is a defense version. */ #define DEFEND_TRY_MOVES(no_deep_branching, attack_hint) \ do { \ int k; \ \ for (k = moves.num_tried; k < moves.num; k++) { \ int ko_move; \ int dpos = moves.pos[k]; \ \ if (komaster_trymove(dpos, color, moves.message[k], str, &ko_move,\ stackp <= ko_depth && savecode == 0)) { \ int acode = do_attack(str, (attack_hint)); \ popgo(); \ \ if (!ko_move) { \ CHECK_RESULT(savecode, savemove, acode, dpos, move, \ "defense effective"); \ } \ else { \ if (acode != WIN) { \ savemove = dpos; \ savecode = KO_B; \ } \ } \ } \ \ if ((no_deep_branching) && stackp >= branch_depth) \ RETURN_RESULT(savecode, savemove, move, "branching limit"); \ } \ \ moves.num_tried = moves.num; \ } while (0) /* Attack version of the macro above. This one is a bit more * complicated, because when defender fails to defend, attacker has to * prove that he can capture the string before claiming victory. */ #define ATTACK_TRY_MOVES(no_deep_branching, defense_hint) \ do { \ int k; \ \ for (k = moves.num_tried; k < moves.num; k++) { \ int ko_move; \ int apos = moves.pos[k]; \ \ if ((board_ko_pos != NO_MOVE || !send_two_return_one(apos, other))\ && komaster_trymove(apos, other, moves.message[k], \ str, &ko_move, \ stackp <= ko_depth && savecode == 0)) { \ int dcode = do_find_defense(str, (defense_hint)); \ \ if (REVERSE_RESULT(dcode) > savecode \ && do_attack(str, NULL)) { \ if (!ko_move) { \ if (dcode == 0) { \ popgo(); \ RETURN_RESULT(WIN, apos, move, "attack effective"); \ } \ \ savemove = apos; \ savecode = REVERSE_RESULT(dcode); \ } \ else { \ savemove = apos; \ savecode = KO_B; \ } \ } \ \ popgo(); \ } \ \ if ((no_deep_branching) && stackp >= branch_depth) \ RETURN_RESULT(savecode, savemove, move, "branching limit"); \ } \ \ moves.num_tried = moves.num; \ } while (0) struct reading_moves { int pos[MAX_MOVES]; int score[MAX_MOVES]; const char *message[MAX_MOVES]; int num; int num_tried; }; /* * The functions in reading.c are used to read whether groups * can be captured or not. See the Texinfo documentation * (Reading) for more information. * * NULL POINTERS: Many functions in this file can use pointers * to return the locations of recommended plays. These can be * set NULL in which case these values are not returned. */ static int do_find_defense(int str, int *move); static int defend1(int str, int *move); static int defend2(int str, int *move); static int defend3(int str, int *move); static int defend4(int str, int *move); static void special_rescue_moves(int str, int lib, struct reading_moves *moves); static void bamboo_rescue_moves(int str, int num_libs, int libs[], struct reading_moves *moves); static void special_rescue2_moves(int str, int libs[2], struct reading_moves *moves); static void special_rescue3_moves(int str, int libs[3], struct reading_moves *moves); static void special_rescue4_moves(int str, int libs[2], struct reading_moves *moves); static void hane_rescue_moves(int str, int libs[4], struct reading_moves *moves); static void special_rescue5_moves(int str, int libs[3], struct reading_moves *moves); static void special_rescue6_moves(int str, int libs[3], struct reading_moves *moves); static void set_up_snapback_moves(int str, int lib, struct reading_moves *moves); static void edge_clamp_moves(int str, struct reading_moves *moves); static int do_attack(int str, int *move); static int attack1(int str, int *move); static int attack2(int str, int *move); static int attack3(int str, int *move); static int attack4(int str, int *move); static void find_cap_moves(int str, struct reading_moves *moves); static void special_attack2_moves(int str, int libs[2], struct reading_moves *moves); static void special_attack3_moves(int str, int libs[2], struct reading_moves *moves); static void special_attack4_moves(int str, int libs[2], struct reading_moves *moves); static void draw_back_moves(int str, struct reading_moves *moves); static void edge_closing_backfill_moves(int str, int apos, struct reading_moves *moves); static void edge_block_moves(int str, int apos, struct reading_moves *moves); static void propose_edge_moves(int str, int *libs, int liberties, struct reading_moves *moves, int color); static void break_chain_moves(int str, struct reading_moves *moves); static int defend_secondary_chain1_moves(int str, struct reading_moves *moves, int min_liberties); static void defend_secondary_chain2_moves(int str, struct reading_moves *moves, int min_liberties); static void break_chain2_efficient_moves(int str, struct reading_moves *moves); static void do_find_break_chain2_efficient_moves(int str, int adj, struct reading_moves *moves); static void break_chain2_moves(int str, struct reading_moves *moves, int require_safe, int be_aggressive); static void break_chain2_defense_moves(int str, struct reading_moves *moves, int be_aggressive); static void break_chain3_moves(int str, struct reading_moves *moves, int be_aggressive); static void break_chain4_moves(int str, struct reading_moves *moves, int be_aggressive); static void superstring_moves(int str, struct reading_moves *moves, int liberty_cap, int does_attack); static void squeeze_moves(int str, struct reading_moves *moves); static void superstring_break_chain_moves(int str, int liberty_cap, struct reading_moves *moves); static void double_atari_chain2_moves(int str, struct reading_moves *moves, int generate_more_moves); static void order_moves(int str, struct reading_moves *moves, int color, const char *funcname, int killer); static int simple_ladder_defend(int str, int *move); static int in_list(int move, int num_moves, int *moves); /* Statistics. */ static int reading_node_counter = 0; static int nodes_when_called = 0; /* ================================================================ */ /* Goal functions */ /* ================================================================ */ /* * These functions define goals for the reading process. They use * the rest of the reading machinery to evaluate whether the goal * is fulfillable. * * The simplest goals are defined by attack() and find_defense(), * namely to see if it is possible to capture or defend a single * string. More complex goals are defined by e.g. attack_either() * or defend_both(). * * The functions in this section and the next are the only ones which are * callable from outside this file. */ /* attack(str, *move) determines if the string at (str) can be * captured, and if so, (*move) returns the attacking move, unless * (move) is a null pointer. Use a null pointer if you are interested * in the result of the attack but not the attacking move itself. * * Return WIN if the attack succeeds unconditionally, 0 if it doesn't. * Returns KO_A or KO_B if the result depends on ko: * - Returns KO_A if the attack succeeds provided attacker is willing to * ignore any ko threat (the attacker makes the first ko capture). * - Returns KO_B if attack succeeds provided attacker has a ko threat * which must be answered (the defender makes the first ko capture). */ int attack(int str, int *move) { int result; int nodes; int origin; int the_move = NO_MOVE; int liberties = countlib(str); nodes_when_called = reading_node_counter; /* Don't even spend time looking in the cache if there are more than * enough liberties. We need this before the persistent cache lookup * to avoid results inconsistent with find_defense(). */ if (liberties > 4 || (liberties == 4 && stackp > fourlib_depth) || (liberties == 3 && stackp > depth)) return 0; origin = find_origin(str); if (search_persistent_reading_cache(ATTACK, origin, &result, &the_move)) { if (move) *move = the_move; return result; } memset(shadow, 0, sizeof(shadow)); result = do_attack(str, &the_move); nodes = reading_node_counter - nodes_when_called; if (debug & DEBUG_READING_PERFORMANCE) { if (reading_node_counter - nodes_when_called >= MIN_READING_NODES_TO_REPORT) { if (result != 0) gprintf("%oattack %1m(%1m) = %d %1M, %d nodes ", str, origin, result, the_move, nodes); else gprintf("%oattack %1m(%1m) = %d, %d nodes ", str, origin, result, nodes); dump_stack(); } } store_persistent_reading_cache(ATTACK, origin, result, the_move, nodes); if (move) *move = the_move; #if DUMP_ALL_RESULTS do_dump_stack(); gprintf("%oattack %1m (%d)=%d %1m\n", str, depth, result, the_move); #endif return result; } /* find_defense(str, *move) attempts to find a move that will save * the string at (str). It returns WIN if such a move is found, with * (*move) the location of the saving move, unless (move) is a * null pointer. It is not checked that tenuki defends, so this may * give an erroneous answer if !attack(str). * * Returns KO_A or KO_B if the result depends on ko. Returns KO_A if the * string can be defended provided the defender is willing to ignore * any ko threat. Returns KO_B if the defender wins by having a ko threat * which must be answered. */ int find_defense(int str, int *move) { int result; int nodes; int origin; int the_move = NO_MOVE; int liberties = countlib(str); nodes_when_called = reading_node_counter; /* Don't even spend time looking in the cache if there are more than * enough liberties. */ if (liberties > 4 || (liberties == 4 && stackp > fourlib_depth)) { if (move) *move = NO_MOVE; return WIN; } origin = find_origin(str); if (search_persistent_reading_cache(FIND_DEFENSE, origin, &result, &the_move)) { if (move) *move = the_move; return result; } memset(shadow, 0, sizeof(shadow)); result = do_find_defense(str, &the_move); nodes = reading_node_counter - nodes_when_called; if (debug & DEBUG_READING_PERFORMANCE) { if (reading_node_counter - nodes_when_called >= MIN_READING_NODES_TO_REPORT) { if (result != 0) gprintf("%odefend %1m(%1m) = %d %1M, %d nodes ", str, origin, result, the_move, nodes); else gprintf("%odefend %1m(%1m) = %d, %d nodes ", str, origin, result, nodes); dump_stack(); } } store_persistent_reading_cache(FIND_DEFENSE, origin, result, the_move, nodes); if (move) *move = the_move; #if DUMP_ALL_RESULTS do_dump_stack(); gprintf("%odefend %1m (%d)=%d %1m\n", str, depth, result, the_move); #endif return result; } /* attack_and_defend(str, &acode, &attack_point, * &dcode, &defense_point) * is a frontend to the attack() and find_defense() functions, which * guarantees a consistent result. If a string cannot be attacked, 0 * is returned and acode is 0. If a string can be attacked and * defended, WIN is returned, acode and dcode are both non-zero, and * (attack_point), (defense_point) both point to vertices on the board. * If a string can be attacked but not defended, 0 is again returned, * acode is non-zero, dcode is 0, and (attack_point) points to a vertex * on the board. * * This function in particular guarantees that if there is an attack, * it will never return (defense_point) = NO_MOVE, which means the string is * safe without defense. Separate calls to attack() and find_defense() * may occasionally give this result, due to irregularities introduced * by the persistent reading cache. */ int attack_and_defend(int str, int *attack_code, int *attack_point, int *defend_code, int *defense_point) { int acode = 0; int apos = NO_MOVE; int dcode = 0; int dpos = NO_MOVE; acode = attack(str, &apos); if (acode != 0) dcode = find_defense(str, &dpos); ASSERT1(!(acode != 0 && dcode == WIN && dpos == NO_MOVE), str); if (attack_code) *attack_code = acode; if (attack_point) *attack_point = apos; if (defend_code) *defend_code = dcode; if (defense_point) *defense_point = dpos; return acode != 0 && dcode != 0; } /* * attack_either(astr, bstr) returns true if there is a move which * guarantees that at least one of the strings (astr) and (bstr) * can be captured. A typical application for this is in connection * patterns, where after a cut it suffices to capture one of the cutting * stones. * * FIXME: The current implementation only looks for uncoordinated * attacks. This is insufficient to find double ataris or * moves such as 'a' in positions like * * XOOOOOOOX * XOXXOXXOX * XX..a..XX * --------- * * where neither of the threatened X stones can be captured right * out. Still either can be captured by a move down to a. */ int attack_either(int astr, int bstr) { int asuccess = 0; int bsuccess = 0; int color = board[astr]; ASSERT1(IS_STONE(color) , astr); ASSERT1(color == board[bstr], bstr); /* Start by attacking the string with the fewest liberties. On * average this seems to be slightly more efficient. */ if (countlib(astr) > countlib(bstr)) { int t = astr; astr = bstr; bstr = t; } asuccess = attack(astr, NULL); if (asuccess == WIN) return asuccess; bsuccess = attack(bstr, NULL); if (asuccess || bsuccess) { return (asuccess > bsuccess) ? asuccess : bsuccess; } /* Try (a little) harder */ { int alibs[2]; int blibs[2]; int alib = findlib(astr, 2, alibs); int defended0 = WIN; int defended1 = WIN; int other = OTHER_COLOR(color); /* Let's just try the case where the group with the fewest liberties * has only 2, and try each atari in turn. */ if (alib == 2) { if (trymove(alibs[0], other, "attack_either-A", astr)) { defended0 = defend_both(astr, bstr); popgo(); } if (defended0 && trymove(alibs[1], other, "attack_either-B", astr)) { defended1 = defend_both(astr, bstr); popgo(); } } /* The second string is possibly also short in liberties. * Let's try to improve the result. */ if (defended0 > 0 && defended1 > 0 && findlib(bstr, 2, blibs) == 2) { defended0 = gg_min(defended0, defended1); defended1 = defended0; /* We may get here even if alib==1, in case there is a snapback. * To avoid referencing uninitialized memory in this case we * explicitly set alibs[1] to NO_MOVE. */ if (alib == 1) alibs[1] = NO_MOVE; if (blibs[0] != alibs[0] && blibs[0] != alibs[1] && trymove(blibs[0], other, "attack_either-C", bstr)) { int defended = defend_both(astr, bstr); defended0 = gg_min(defended0, defended); popgo(); } if (defended0 && blibs[1] != alibs[0] && blibs[1] != alibs[1] && trymove(blibs[1], other, "attack_either-D", bstr)) { int defended = defend_both(astr, bstr); defended1 = gg_min(defended1, defended); popgo(); } } return REVERSE_RESULT(gg_min(defended0, defended1)); } } /* * defend_both(astr, bstr) returns true if both the strings (astr) * and (bstr) can be defended simultaneously or if there is no attack. * A typical application for this is in connection patterns, where * after a cut it's necessary to defend both cutting stones. * * FIXME: The current implementation only makes halfhearted * attempts to find coordinated defense moves. A proper implementation * would require some serious reading. */ int defend_both(int astr, int bstr) { int a_threatened = 0; int b_threatened = 0; int a_savepos; int b_savepos; int acode = 0; int dcode = 0; int color = board[astr]; ASSERT1(IS_STONE(color) , astr); ASSERT1(color == board[bstr], bstr); /* This probably helps here too... * (see attack_either) */ if (countlib(astr) > countlib(bstr)) { int t = astr; astr = bstr; bstr = t; } attack_and_defend(astr, &acode, NULL, &dcode, &a_savepos); if (acode != 0) { a_threatened = 1; if (dcode != WIN) return 0; /* (astr) already lost */ } attack_and_defend(bstr, &acode, NULL, &dcode, &b_savepos); if (acode != 0) { b_threatened = 1; if (dcode != WIN) return 0; /* (bstr) already lost */ } /* Neither string can be attacked or only one of them, in which case * we have time to save it. */ if (!a_threatened || !b_threatened) return WIN; /* If both strings are threatened we assume that one will become lost, * unless find_defense() happened to return the same defense point for * both (which e.g. may happen if they are in fact the same string). * This is still a bit too pessimistic, as there may be one move which * saves both strings. To do this right we should try each move which * defends either string and see if it also defends the other string. */ if (a_savepos == b_savepos) return WIN; /* Both strings can be attacked but also defended * by one move. */ /* We also try each of the returned defense points and see whether * the other string can still be attacked. This still gives a * somewhat pessimistic estimation. */ if (trymove(a_savepos, color, "defend_both-A", astr)) { if (board[bstr] && !attack(bstr, NULL)) { popgo(); return WIN; } popgo(); } if (trymove(b_savepos, color, "defend_both-B", bstr)) { if (board[astr] && !attack(astr, NULL)) { popgo(); return WIN; } popgo(); } /* The next improvement is to try to attack a common adjacent string. */ { int adjs1[MAXCHAIN]; int neighbors1; int adjs2[MAXCHAIN]; int neighbors2; int r; int s; int epos; int fpos; neighbors1 = chainlinks(astr, adjs1); neighbors2 = chainlinks(bstr, adjs2); for (r = 0; r < neighbors1; r++) { epos = adjs1[r]; if (countlib(epos) <= 4 && (epos != a_savepos) && (epos != b_savepos)) { /* Is (epos) also adjacent to (bstr)? */ for (s = 0; s < neighbors2; s++) { if (adjs2[s] == adjs1[r]) break; } if (s == neighbors2) continue; /* No, it wasn't. */ if (attack(epos, &fpos)) { if (trymove(fpos, color, "defend_both-C", astr)) { if (board[astr] && board[bstr] && !attack(astr, NULL) && !attack(bstr, NULL)) { popgo(); return WIN; } popgo(); } } } } } /* Both strings can be attacked but we have only time to defend one. */ return 0; } /* * break_through(apos, bpos, cpos) returns WIN if a position can * succesfully be broken through and CUT if it can be cut. The position * is assumed to have the shape (the colors may be reversed) * * .O. dbe * OXO aFc * * It is X to move and try to capture at least one of a, b, and c. If * this succeeds, X is said to have broken through the position. * Otherwise X may try to cut through the position, which means * keeping F safe and getting a tactically safe string at either d or * e. * * Important notice: a, b, and c must be given in the correct order. * * FIXME: The reading involved here can most likely be improved. * * FIXME: We need to take ko results properly into account. */ static int break_through_helper(int apos, int bpos, int cpos, int dpos, int epos, int Fpos, int color, int other); int break_through(int apos, int bpos, int cpos) { int color = board[apos]; int other = OTHER_COLOR(color); int dpos; int epos; int Fpos; int gpos; int success = 0; int success2 = 0; /* Basic sanity checking. */ ASSERT1(IS_STONE(color) , apos); ASSERT1(color == board[bpos], bpos); ASSERT1(color == board[cpos], cpos); /* Construct the rest of the points in the pattern. */ Fpos = (apos + cpos) / 2; /* F midpoint between a and c. */ dpos = apos + bpos - Fpos; /* Use diagonal relation a+b = d+F. */ epos = bpos + cpos - Fpos; /* Use diagonal relation b+c = e+F. */ /* More sanity checking. */ ASSERT1(board[dpos] == EMPTY , dpos); ASSERT1(board[epos] == EMPTY , epos); /* F might already have been captured. (play_break_through_n() can't * detect this. */ if (board[Fpos] == EMPTY) return 0; ASSERT1(board[Fpos] == other, Fpos); /* First X tries to play at d. */ success = break_through_helper(apos, bpos, cpos, dpos, epos, Fpos, color, other); if (success == WIN) return WIN; success2 = break_through_helper(cpos, bpos, apos, epos, dpos, Fpos, color, other); if (success2 == WIN) return WIN; if (success2 == CUT) success = CUT; /* If we haven't been lucky yet, we might need to start by * defending F. * * FIXME: The function would probably be considerably faster if we * start by checking whether F needs defense. Beware of ko potential * though. */ success2 = 0; if (attack_and_defend(Fpos, NULL, NULL, NULL, &gpos)) { if (trymove(gpos, other, "break_through-A", Fpos)) { /* Now we let O defend his position by playing either d or e. * FIXME: There may be other plausible moves too. */ if (trymove(dpos, color, "break_through-B", Fpos)) { /* O connects at d, so X cuts at e. */ if (safe_move(epos, other)) { success2 = CUT; if (!board[cpos] || attack(cpos, NULL)) success2 = WIN; } popgo(); } if (success2 > 0 && trymove(epos, color, "break_through-C", Fpos)) { /* O connects at e, so X cuts at d. */ if (safe_move(dpos, other)) { /* success2 is already WIN or CUT. */ if (board[apos] && !attack(apos, NULL)) success2 = CUT; } else success2 = 0; popgo(); } popgo(); } } if (success2 > 0) return success2; return success; } /* Helper function for break_through(). Since we can symmetrically * start by cutting at d or e, we use the same code for both attacks, * simply switching positions between the two calls. */ static int break_through_helper(int apos, int bpos, int cpos, int dpos, int epos, int Fpos, int color, int other) { int success = 0; int gpos; if (trymove(dpos, other, "break_through_helper-A", Fpos)) { /* If F can be attacked we can't start in this way. */ if (!attack(Fpos, NULL)) { /* If d is safe too, we have at least managed to break through. */ if (!attack(dpos, &gpos)) success = CUT; /* Too bad, d could be attacked. We let O play the attack and * then try to make a second cut at e. But first we must test if * O at e is sufficient to capture d. */ else { if (trymove(epos, color, "break_through_helper-E", Fpos)) { if (!board[dpos] || !find_defense(dpos, NULL)) { popgo(); popgo(); return 0; } popgo(); } if (gpos == epos) { popgo(); return 0; } if (trymove(gpos, color, "break_through_helper-F", Fpos)) { if (trymove(epos, other, "break_through_helper-G", Fpos)) { if (!attack(epos, NULL)) { success = CUT; /* Make sure b and c are safe. If not, back up & let O try * to defend in a different way. */ if (board[bpos] && board[cpos] && defend_both(bpos, cpos)) { /* Can't do better than CUT. */ popgo(); popgo(); popgo(); return CUT; } } else { /* Lost everything. (Note we ignore ko at the moment.) */ popgo(); popgo(); popgo(); return 0; } popgo(); } else { /* Failed to cut at all. */ popgo(); popgo(); return 0; } popgo(); } } /* By now, we're sure a cut works, so now we can try * to capture something. */ if (!board[apos] || !board[bpos] || !defend_both(apos, bpos)) success = WIN; else { /* Both a and b could be defended, or didn't need to be. * Let's see if a move at e is sufficient for O. */ int attack_on_b = 0; int attack_on_a = 0; if (trymove(epos, color, "break_through_helper-B", Fpos)) { if (attack(bpos, NULL)) attack_on_b = 1; else if (attack(apos, NULL)) attack_on_a = 1; popgo(); } /* Let O find a defense and play it. */ if (attack_on_a || attack_on_b) { int hpos = NO_MOVE; if (((attack_on_a && find_defense(apos, &hpos)) || (attack_on_b && find_defense(bpos, &hpos))) && hpos != NO_MOVE && trymove(hpos, color, "break_through_helper-C", Fpos)) { /* Now we make a second cut at e, trying to capture * either b or c. */ if (trymove(epos, other, "break_through_helper-D", Fpos)) { if (!board[bpos] || !board[cpos] || !defend_both(bpos, cpos)) success = WIN; popgo(); } popgo(); } else success = WIN; /* This should have been covered by * defend_both(), so probably unnecessary. */ } } } popgo(); } return success; } /* ---------------------------------------------------------------- */ /* Threats */ /* ---------------------------------------------------------------- */ /* Return up to max_threats threats to capture the string at str. * If the string is directly attackable the number of threats * is reported to be 0. * * NOTE: You can call attack_threats with moves[] and codes[] * already partly filled in. So if you want to get the * threats from scratch, you have to set them to 0 * yourself. * * FIXME: Shall we report upgrades, like we can capture in ko but * have a threat to capture unconditionally? */ int attack_threats(int str, int max_points, int moves[], int codes[]) { int other; int num_threats; int liberties; int libs[MAXLIBS]; int num_adj; int adjs[MAXCHAIN]; int k; int l; int r; ASSERT1(IS_STONE(board[str]), str); other = OTHER_COLOR(board[str]); /* Only handle strings with no way to capture immediately. * For now, we treat ko the same as unconditionally. */ if (attack(str, NULL) != 0) return 0; /* This test would seem to be unnecessary since we only threaten * strings with attack_code == 0, but it turns out that single * stones with one liberty that can be captured, but come to * live again in a snap-back get attack_code == 0. * * The test against 6 liberties is just an optimization. */ liberties = findlib(str, MAXLIBS, libs); if (liberties > 1 && liberties < 6) { for (k = 0; k < liberties; k++) { int aa = libs[k]; /* Try to threaten on the liberty. */ if (trymove(aa, other, "attack_threats-A", str)) { int acode = attack(str, NULL); if (acode != 0) movelist_change_point(aa, acode, max_points, moves, codes); popgo(); } /* Try to threaten on second order liberties. */ for (l = 0; l < 4; l++) { int bb = libs[k] + delta[l]; if (!ON_BOARD(bb) || IS_STONE(board[bb]) || liberty_of_string(bb, str)) continue; if (trymove(bb, other, "attack_threats-B", str)) { int acode = attack(str, NULL); if (acode != 0) movelist_change_point(bb, acode, max_points, moves, codes); popgo(); } } } } /* Threaten to attack by saving weak neighbors. */ num_adj = chainlinks(str, adjs); for (k = 0; k < num_adj; k++) { int bb; int dd; /* Defense point of weak neighbor. */ int acode; int dcode; attack_and_defend(adjs[k], &acode, NULL, &dcode, &dd); if (acode == 0 || dcode == 0) continue; /* The strange code using r == -1 below is only avoid duplication * of the code starting with "if (trymove..)" below. * If r == -1 and stackp == 0 then use the defense point what we got from * attack_and_defend above. Otherwise step through all defense points. */ for (r = -1; r < max_points; r++) { if (stackp == 0) { if (r == -1) continue; if (worm[adjs[k]].defense_codes[r] == 0) break; bb = worm[adjs[k]].defense_points[r]; } else { if (r == -1) bb = dd; else break; } /* Test the move and see if it is a threat. */ if (trymove(bb, other, "attack_threats-C", str)) { if (board[str] == EMPTY) acode = WIN; else acode = attack(str, NULL); if (acode != 0) movelist_change_point(bb, acode, max_points, moves, codes); popgo(); } } } /* Return actual number of threats found regardless of attack code. */ if (codes[max_points - 1] > 0) return max_points; for (num_threats = 0; num_threats < max_points; num_threats++) if (codes[num_threats] == 0) break; return num_threats; } /* ================================================================ */ /* Defensive functions */ /* ================================================================ */ /* Like find_defense, but takes the komaster argument. If the * opponent is reading functions will not try * to take ko. */ static int do_find_defense(int str, int *move) { int xpos = NO_MOVE; int dcode = 0; int liberties; int retval; SETUP_TRACE_INFO("find_defense", str); /* We first check if the number of liberties is larger than four. In * that case we don't cache the result and to avoid needlessly * storing the position in the hash table, we must do this test * before we look for cached results. */ str = find_origin(str); liberties = countlib(str); if (liberties > 4 || (liberties == 4 && stackp > fourlib_depth) || (liberties == 3 && stackp > depth)) { /* No need to cache the result in these cases. */ SGFTRACE(0, WIN, "too many liberties or stackp > depth"); if (move) *move = 0; return WIN; } /* Set "killer move" up. This move (if set) was successful in * another variation, so it is reasonable to try it now. However, * we only do this if the string has at least 3 liberties - * otherwise the situation changes too much from variation to * variation. */ if (liberties > 2 && move) xpos = *move; if (stackp <= depth && tt_get(&ttable, FIND_DEFENSE, str, NO_MOVE, depth - stackp, NULL, &retval, NULL, &xpos) == 2) { /* Note that if return value is 1 (too small depth), the move will * still be used for move ordering. */ TRACE_CACHED_RESULT(retval, xpos); SGFTRACE(xpos, retval, "cached"); if (move) *move = xpos; return retval; } if (liberties == 1) dcode = defend1(str, &xpos); else if (liberties == 2) dcode = defend2(str, &xpos); else if (liberties == 3) dcode = defend3(str, &xpos); else if (liberties == 4) dcode = defend4(str, &xpos); if (dcode) { READ_RETURN(FIND_DEFENSE, str, depth - stackp, move, xpos, dcode); } READ_RETURN0(FIND_DEFENSE, str, depth - stackp); } /* Determine if a `move' by `color' allows under-the-stones tesuji * a.k.a. "big snapback". Here is an example: * * |XXXX... * |XXOOXXX * |OOOXOOX * |..O*OOX * +------- * * Even though the move at '*' allows black to capture four white * stones, white can later recapture black stones and create a second * eye. This is very similar to a snapback. * * This function returns true if a move creates a string of with two * liberties, which can, however, be instantly recaptured by opponent. * It is actually not required that the move captures something. If * the caller needs captures, it should check for them itself. */ static int allows_under_the_stones_tesuji(int move, int color) { int result = 0; SGFTree *save_sgf_dumptree; int save_count_variations; if (accuratelib(move, color, 3, NULL) != 2) return 0; save_sgf_dumptree = sgf_dumptree; save_count_variations = count_variations; sgf_dumptree = NULL; count_variations = 0; if (trymove(move, color, "allows_under_the_stones_tesuji", NO_MOVE)) { int libs[2]; findlib(move, 2, libs); if ((!is_self_atari(libs[0], color) && accuratelib(libs[1], OTHER_COLOR(color), 3, NULL) <= 2) || (!is_self_atari(libs[1], color) && accuratelib(libs[0], OTHER_COLOR(color), 3, NULL) <= 2)) result = 1; popgo(); } sgf_dumptree = save_sgf_dumptree; count_variations = save_count_variations; return result; } /* Called by the defendN functions. Don't think too much if there's * an easy way to get enough liberties. */ static int fast_defense(int str, int liberties, int *libs, int *move) { int color = board[str]; int j, k, l; int goal_liberties = (stackp < fourlib_depth ? 5 : 4); int adj, adjs[MAXCHAIN]; /* We would like to initialize liberty_mark to -1, but some * compilers warn, quite correctly, that -1 is not an unsigned * number. */ static unsigned liberty_mark = ~0U; static unsigned lm[BOARDMAX]; ASSERT1(libs != NULL, str); ASSERT1(move != NULL, str); for (k = 0; k < liberties; k++) { /* accuratelib() seems to be more efficient than fastlib() here, * probably because it catches more cases. */ if (accuratelib(libs[k], color, goal_liberties, NULL) >= goal_liberties) { *move = libs[k]; return 1; } } /* Check the cases where an opponent neighbor string is in * atari. */ adj = chainlinks2(str, adjs, 1); for (j = 0; j < adj; j++) { int lib; int missing = goal_liberties - liberties; int total = 0; int adj2, adjs2[MAXCHAIN]; int alib, alibs[MAXLIBS]; int num_adjacent_stones; findlib(adjs[j], 1, &lib); /* We aren't interested in ko (at this stage). And playing * our own last liberty to capture is prone to snapbacks, * so better let the 'normal' reading routines do the job. */ if ((liberties == 1 && lib == libs[0] && countstones(adjs[j]) <= 2) || is_ko(lib, color, NULL)) continue; /* Would the capture already gain enough liberties ? * No need to test the case if the move is one of our liberties, * it has already been done in the first loop of this function. */ num_adjacent_stones = count_adjacent_stones(adjs[j], str, missing); if (!liberty_of_string(lib, str) && num_adjacent_stones >= missing) { *move = lib; return 1; } ASSERT1(num_adjacent_stones >= 1, str); /* What is the total number of liberties of the friendly strings around * the lunch? */ if (++liberty_mark == 0) { memset(lm, 0, sizeof(lm)); liberty_mark++; } /* Loop over all neighbors of the lunch. */ adj2 = chainlinks(adjs[j], adjs2); for (k = 0; k < adj2; k++) { /* Loop over all liberties of the neighbor. */ alib = findlib(adjs2[k], MAXLIBS, alibs); for (l = 0; l < alib; l++) { if (lm[alibs[l]] != liberty_mark) { lm[alibs[l]] = liberty_mark; total++; } } } /* The captured string is treated as common liberties, and * some adjustements are made : * - we're adding a stone for capturing the lunch (-1) * - opponent might be able to remove a liberty (-1) * - and possibly force us to connect (-1) * - reduce us by one more liberty with a throw-in; this * is only possible if there is only one adjacent stone in the * lunch to the string (-1) * Probably there are more damezumari-type cases, but as a heuristic, * it seems good enough. */ total += countstones(adjs[j]) - 2; if (lm[lib] == liberty_mark) total--; if (num_adjacent_stones == 1) total--; if (total >= goal_liberties) { /* One case when this code can give a false defense is an * under-the-stones tesuji or "big snapback." See reading:199 * for an example. While this position is probably very rare, * it is nice to make GNU Go understand "neat" tesujis. */ if (liberties == 1 && lib == libs[0] && allows_under_the_stones_tesuji(lib, color)) { /* This is a bad "fast defense". */ continue; } *move = lib; return 1; } } return 0; } /* If str points to a string with exactly one liberty, defend1 * determines whether it can be saved by extending or capturing * a boundary chain having one liberty. The function returns WIN if the string * can be saved, otherwise 0. It returns KO_A or KO_B if it can be saved, * conditioned on ko. Returns KO_A if it can be saved provided (color) is * willing to ignore any ko threat. Returns KO_B if it can be saved if (color) * has a ko threat which must be answered. * * The pair defend1-attack2 call each other recursively to * read situations such as ladders. They read all ladders to the end. * If the reading ply (stackp) is deeper than the deep-reading cutoff * parameter depth, whose default value DEPTH is defined in gnugo.h, then a * string is assumed alive if it can get 3 liberties. When * fourlib_depth < stackp < depth, a string is considered alive if it can get * four liberties. When stackp < fourlib_depth, it is considered alive * if it can get 5 liberties. * */ static int defend1(int str, int *move) { int color = board[str]; int other = OTHER_COLOR(color); int xpos; int lib; struct reading_moves moves; int savemove = 0; int savecode = 0; int liberties; int k; SETUP_TRACE_INFO("defend1", str); reading_node_counter++; ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 1, str); /* lib will be the liberty of the string. */ liberties = findlib(str, 1, &lib); ASSERT1(liberties == 1, str); if (fast_defense(str, liberties, &lib, &xpos)) RETURN_RESULT(WIN, xpos, move, "fast defense"); /* Collect moves to try in the first batch. * 1. First order liberty. * 2. Chain breaking moves. * 3. Moves to set up a snapback. */ moves.pos[0] = lib; moves.score[0] = 0; moves.message[0] = "liberty"; moves.num = 1; moves.num_tried = 0; break_chain_moves(str, &moves); set_up_snapback_moves(str, lib, &moves); order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(0, NULL); /* If the string is a single stone and a capture would give a ko, * try to defend it with ko by backfilling. * * FIXME: What is an example of this? Is it correct that the * return value is WIN and not KO_A or KO_B? */ if (stackp <= backfill_depth && countstones(str) == 1 && is_ko(lib, other, NULL)) { int libs2[6]; liberties = approxlib(lib, color, 6, libs2); if (liberties <= 5) { for (k = 0; k < liberties; k++) { int apos = libs2[k]; if ((liberties == 1 || !is_self_atari(apos, other)) && trymove(apos, color, "defend1-C", str)) { int acode = do_attack(str, NULL); popgo(); CHECK_RESULT(savecode, savemove, acode, apos, move, "backfilling"); } } } } RETURN_RESULT(savecode, savemove, move, "saved move"); } /* If str points to a group with two liberties, defend2 determines * whether the group can be saved by extending, or by capturing part of * its surrounding chain. A group is considered safe if either part of * the surrounding chain may be captured, or if it can get 3 * liberties. It is presumed that the opponent could kill if tenuki. * If both extensions work, it prefers the one which maximizes * liberties. * * *move returns the move to save the stones. */ static int defend2(int str, int *move) { int color, other; int xpos = NO_MOVE; int liberties; int libs[2]; int liberties2; int libs2[6]; struct reading_moves moves; int savemove = 0; int savecode = 0; int k; int r; int suggest_move = NO_MOVE; int string_size; int be_aggressive; SETUP_TRACE_INFO("defend2", str); reading_node_counter++; color = board[str]; other = OTHER_COLOR(color); ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 2, str); liberties = findlib(str, 2, libs); if (fast_defense(str, liberties, libs, &xpos)) RETURN_RESULT(WIN, xpos, move, "fast defense"); /* Collect moves to try in the first batch. * 1. First order liberties. * 2. Chain breaking moves. * 3. Second order liberties moving up from first line to second. * 4. Edge clamps. */ moves.num = 0; moves.num_tried = 0; /* We don't want to play self-atari liberties, unless the string is a * single stone (in which case it might be a snapback move). Sacrifices * might be good moves, but not in tactical reading. */ string_size = countstones(str); if (string_size == 1 || !is_self_atari(libs[0], color)) ADD_CANDIDATE_MOVE(libs[0], 0, moves, "liberty"); if (string_size == 1 || !is_self_atari(libs[1], color)) ADD_CANDIDATE_MOVE(libs[1], 0, moves, "liberty"); break_chain_moves(str, &moves); break_chain2_efficient_moves(str, &moves); propose_edge_moves(str, libs, liberties, &moves, color); edge_clamp_moves(str, &moves); if (stackp <= depth) { for (k = 0; k < liberties; k++) special_rescue_moves(str, libs[k], &moves); bamboo_rescue_moves(str, liberties, libs, &moves); } if (stackp <= backfill_depth) special_rescue2_moves(str, libs, &moves); order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(0, &suggest_move); /* Look for backfilling moves. */ for (k = 0; k < liberties; k++) { if (is_self_atari(libs[k], other)) { liberties2 = approxlib(libs[k], color, 6, libs2); /* Note: liberties2 must be smaller than 5, otherwise libs[k] had been * a direct defense. */ for (r = 0; r < liberties2; r++) { xpos = libs2[r]; /* If the newly placed stone would be in atari, but not a single * stone, we don't even try. */ if (!is_self_atari(xpos, color) && has_neighbor(xpos, color)) ADD_CANDIDATE_MOVE(xpos, 0, moves, "backfill-A"); } } liberties2 = approxlib(libs[k], other, 3, libs2); if (liberties2 <= 2) { for (r = 0; r < liberties2; r++) { xpos = libs2[r]; if (!is_self_atari(xpos, color)) ADD_CANDIDATE_MOVE(xpos, 0, moves, "backfill-B"); } } } special_rescue4_moves(str, libs, &moves); /* Only order and test the new set of moves. */ order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(0, &suggest_move); /* If we haven't found any useful moves in first batches, be more * aggressive in break_chain[23]_moves(). */ be_aggressive = (moves.num == 0); if (stackp <= superstring_depth) superstring_break_chain_moves(str, 4, &moves); /* If nothing else works, we try playing a liberty of the * super_string. */ if (stackp <= superstring_depth) { superstring_moves(str, &moves, 3, 0); squeeze_moves(str, &moves); } break_chain2_defense_moves(str, &moves, be_aggressive); if (stackp <= backfill_depth) special_rescue5_moves(str, libs, &moves); if (stackp <= break_chain_depth || (be_aggressive && stackp <= backfill_depth)) break_chain3_moves(str, &moves, be_aggressive); if (be_aggressive && stackp <= backfill_depth) break_chain4_moves(str, &moves, be_aggressive); /* Only order and test the new set of moves. */ order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(0, &suggest_move); RETURN_RESULT(savecode, savemove, move, "saved move"); } /* defend3(str, *move) attempts to find a move rescuing the * string at (str) with 3 liberties. If such a move can be found, * it returns true and the saving move in *move. */ static int defend3(int str, int *move) { int color; int xpos = NO_MOVE; int liberties; int libs[3]; struct reading_moves moves; int savemove = 0; int savecode = 0; int k; int suggest_move = NO_MOVE; SETUP_TRACE_INFO("defend3", str); reading_node_counter++; color = board[str]; ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 3, str); liberties = findlib(str, 3, libs); if (fast_defense(str, liberties, libs, &xpos)) RETURN_RESULT(WIN, xpos, move, "fast defense"); /* Collect moves to try in the first batch. * 1. First order liberties. * 2. Chain breaking moves. * 3. Second order liberties moving up from first line to second. * 4. Edge clamps. */ for (k = 0; k < liberties; k++) { moves.pos[k] = libs[k]; moves.score[k] = 0; moves.message[k] = "liberty"; } moves.num = liberties; moves.num_tried = 0; break_chain_moves(str, &moves); break_chain2_efficient_moves(str, &moves); propose_edge_moves(str, libs, liberties, &moves, color); edge_clamp_moves(str, &moves); if (stackp <= backfill2_depth) hane_rescue_moves(str, libs, &moves); order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(1, &suggest_move); /* This looks a little too expensive. */ #if 0 /* Look for backfilling moves. */ if (stackp <= backfill_depth) { int other = OTHER_COLOR(color); int liberties2; int libs2[6]; int r; int s; for (k = 0; k < liberties; k++) { if (is_self_atari(libs[k], other)) { liberties2 = approxlib(libs[k], color, 6, libs2); for (r = 0; r < liberties2; r++) { xpos = libs2[r]; /* Don't reconsider previously tested moves. */ for (s = 0; s < moves.num; s++) if (xpos == moves.pos[s]) break; if (s < moves.num) continue; if (trymove(xpos, color, "defend3-D", str)) { int acode; /* If the newly placed stone is in atari, we give up * without fight. */ if (countlib(xpos) == 1) acode = WIN; else acode = do_attack(str, NULL); popgo(); CHECK_RESULT(savecode, savemove, acode, xpos, move, "backfill effective"); } } } else { liberties2 = approxlib(libs[k], other, 3, libs2); if (liberties2 <= 3) { for (r = 0; r < liberties2; r++) { xpos = libs2[r]; /* Don't reconsider previously tested moves. */ for (s = 0; s < moves.num; s++) if (xpos == moves.pos[s]) break; if (s < moves.num) continue; if (!is_self_atari(xpos, color) && trymove(xpos, color, "defend2-G", str)) { int acode = do_attack(str, NULL); popgo(); CHECK_RESULT(savecode, savemove, acode, xpos, move "backfill effective"); } } } } } } #endif /* If nothing else works, try to defend with second order liberties. */ if (stackp <= backfill_depth) special_rescue3_moves(str, libs, &moves); if (stackp <= depth) { for (k = 0; k < liberties; k++) special_rescue_moves(str, libs[k], &moves); bamboo_rescue_moves(str, liberties, libs, &moves); } if (get_level() >= 8 && stackp <= backfill2_depth) superstring_break_chain_moves(str, 4, &moves); if (stackp <= break_chain_depth) break_chain2_defense_moves(str, &moves, 0); if (stackp <= backfill_depth) { special_rescue5_moves(str, libs, &moves); special_rescue6_moves(str, libs, &moves); } /* Only order and test the new set of moves. */ order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(1, &suggest_move); /* If nothing else works, we try playing a liberty of the * super_string. */ if (get_level() >= 8 && stackp <= backfill2_depth) { superstring_moves(str, &moves, 3, 0); squeeze_moves(str, &moves); } if (stackp <= break_chain_depth) break_chain3_moves(str, &moves, 0); /* Only order and test the new set of moves. */ order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(1, &suggest_move); RETURN_RESULT(savecode, savemove, move, "saved move"); } /* defend4(str, *move) attempts to find a move rescuing the * string at (str) with 4 liberties. If such a move can be found, * it returns true, and if the pointer move is not NULL, * then it returns the saving move in *move. */ static int defend4(int str, int *move) { int color; int xpos = NO_MOVE; int liberties; int libs[4]; struct reading_moves moves; int savemove = 0; int savecode = 0; int k; int suggest_move = NO_MOVE; SETUP_TRACE_INFO("defend4", str); reading_node_counter++; color = board[str]; ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 4, str); liberties = findlib(str, 4, libs); if (fast_defense(str, liberties, libs, &xpos)) RETURN_RESULT(WIN, xpos, move, "fast defense"); /* Collect moves to try in the first batch. * 1. First order liberties. * 2. Chain breaking moves. */ for (k = 0; k < liberties; k++) { moves.pos[k] = libs[k]; moves.score[k] = 0; moves.message[k] = "liberty"; } moves.num = liberties; moves.num_tried = 0; break_chain_moves(str, &moves); break_chain2_efficient_moves(str, &moves); if (stackp <= backfill_depth) { break_chain2_defense_moves(str, &moves, 0); break_chain3_moves(str, &moves, 0); break_chain4_moves(str, &moves, 0); #if 0 hane_rescue_moves(str, libs, &moves); #endif if (stackp <= superstring_depth) superstring_moves(str, &moves, 4, 0); squeeze_moves(str, &moves); } order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(1, &suggest_move); if (stackp <= depth) { for (k = 0; k < liberties; k++) special_rescue_moves(str, libs[k], &moves); bamboo_rescue_moves(str, liberties, libs, &moves); } order_moves(str, &moves, color, read_function_name, *move); DEFEND_TRY_MOVES(1, &suggest_move); RETURN_RESULT(savecode, savemove, move, "saved move"); } /* * special_rescue_moves(str, lib, *move) is called with (str) a * string having a liberty at (lib). * * This adds moves on a second order liberty to the list of candidate * moves in the struct *moves; e.g. in shapes like: * * . O O X.XXO * O.* or ..* or O.* or XOOXO * O O O ...*. * ----- * * This will occasionally save a string where no other move will. To * reduce the branching caused by these moves, we require that the * opponent can be trivially captured when trying to intercept on the * corresponding first order liberty. */ static void special_rescue_moves(int str, int lib, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int otherlib; int k; /* Use approxlib() to test for trivial capture. */ otherlib = approxlib(lib, other, 3, NULL); if (otherlib > 2) return; /* Loop over the four neighbours of the liberty, (lib + d). */ for (k = 0; k < 4; k++) { int d = delta[k]; if (board[lib + d] == EMPTY) { /* Don't play into a self atari unless we have a potential snapback. */ if (is_self_atari(lib + d, color) && otherlib > 1) continue; /* Be more demanding when the string has four liberties. (Mostly * because attack4() otherwise would need more move generators.) * More precisely we require not only the first order liberty to * become a self atari for the opponent but also one more of the * neighbors of the proposed move. See reading:144 for a * position where we otherwise would try to defend at D9 and * attack4() then lacks move generators to stop black from * continuing towards the top left corner. */ if (countlib(str) > 3) { int r; int number_protected = 0; for (r = 0; r < 4; r++) { if (board[lib + d + delta[r]] == EMPTY && approxlib(lib + d + delta[r], other, 3, NULL) < 3) number_protected++; if (number_protected == 2) break; } if (number_protected < 2) continue; } ADD_CANDIDATE_MOVE(lib + d, 0, *moves, "special_rescue"); } } } /* * In situations like * * XXXXXO * XO.*.O * XO.O.O * XXXXXO * * playing at * is the correct rescue move, although the opponent cannot * be captured at the respective first-order liberty. */ static void bamboo_rescue_moves(int str, int num_libs, int libs[], struct reading_moves *moves) { int color = board[str]; int l1, l2; for (l1 = 0; l1 < num_libs; l1++) for (l2 = 0; l2 < num_libs; l2++) { if (l1 == l2) continue; if (libs[l1] == WEST(libs[l2]) || libs[l1] == EAST(libs[l2])) { if (board[SOUTH(libs[l1])] == EMPTY && board[SOUTH(libs[l2])] == color && !is_self_atari(SOUTH(libs[l1]), color)) ADD_CANDIDATE_MOVE(SOUTH(libs[l1]), 0, *moves, "bamboo_rescue"); if (board[NORTH(libs[l1])] == EMPTY && board[NORTH(libs[l2])] == color && !is_self_atari(NORTH(libs[l1]), color)) ADD_CANDIDATE_MOVE(NORTH(libs[l1]), 0, *moves, "bamboo_rescue"); } else if (libs[l1] == NORTH(libs[l2]) || libs[l1] == SOUTH(libs[l2])) { if (board[WEST(libs[l1])] == EMPTY && board[WEST(libs[l2])] == color && !is_self_atari(WEST(libs[l1]), color)) ADD_CANDIDATE_MOVE(WEST(libs[l1]), 0, *moves, "bamboo_rescue"); if (board[EAST(libs[l1])] == EMPTY && board[EAST(libs[l2])] == color && !is_self_atari(EAST(libs[l1]), color)) ADD_CANDIDATE_MOVE(EAST(libs[l1]), 0, *moves, "bamboo_rescue"); } } } /* In a situation like this: * * OOXXXX the following code can find the * .OXOOX defensive move at 'c'. * .cO.OX * .X.OOX * ------ * * OOXXXX It also can find more general moves like 'c' here. * .OXOOX * cXO.OX * ...OOX * ------ */ static void special_rescue2_moves(int str, int libs[2], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int newlibs[4]; int liberties; int newstr; int k, r, s; for (r = 0; r < 2; r++) { /* Let alib be one of the liberties and require it to be suicide * for the opponent. */ int alib = libs[r]; if (!is_suicide(alib, other)) continue; for (k = 0; k < 4; k++) { if (board[alib + delta[k]] == color && !same_string(alib + delta[k], str)) { newstr = alib + delta[k]; liberties = findlib(newstr, 4, newlibs); for (s = 0; s < liberties && s < 4; s++) { if (!is_self_atari(newlibs[s], color)) ADD_CANDIDATE_MOVE(newlibs[s], 0, *moves, "special_rescue2"); } break_chain_moves(newstr, moves); break_chain2_efficient_moves(newstr, moves); edge_clamp_moves(newstr, moves); } } } } /* In a situation like this: * * ...X.XXO * .XXXOOXO * XXOO.OXO the following code can find the * .O..X.*. defensive move at '*'. * -------- * * OXO cde * .*. afg * --- b-- */ static void special_rescue3_moves(int str, int libs[3], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int apos, bpos, cpos, dpos, epos, fpos, gpos; int k, l, r; ASSERT1(countlib(str) == 3, str); for (r = 0; r < 3; r++) { /* Let (apos) be one of the three liberties. */ apos = libs[r]; /* Try to find the configuration above. */ for (k = 0; k < 4; k++) { bpos = apos + delta[k]; if (ON_BOARD(bpos)) continue; cpos = apos - delta[k]; if (board[cpos] != color) continue; if (!same_string(cpos, str)) continue; for (l = 0; l < 2; l++) { int normal = delta[(k+1)%4]; if (l == 1) normal = -normal; dpos = cpos + normal; if (board[dpos] != other) continue; epos = dpos + normal; if (board[epos] != color) continue; fpos = apos + normal; if (board[fpos] != EMPTY) continue; gpos = fpos + normal; if (board[gpos] != EMPTY) continue; /* Configuration found. Now require an X move at 'a' not * getting too many liberties. */ if (approxlib(apos, other, 4, NULL) > 3) continue; /* Try to play at (fpos). */ ADD_CANDIDATE_MOVE(fpos, 0, *moves, "special_rescue3"); } } } } /* This code can find moves to counter attack moves generated by * special_attack3_moves(). In case such an attack move has only two * liberties, this function finds the liberty which is not common with * the attacked string. * * For a typical example, see reading:198 where black L7 is generated * by special_attack3_moves() and the response at L8 is generated by * this function. */ static void special_rescue4_moves(int str, int libs[2], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int xpos; int apos; int bpos; int libs2[2]; int k; int r; ASSERT1(countlib(str) == 2, str); for (k = 0; k < 2; k++) { apos = libs[k]; bpos = libs[1-k]; if (apos == SOUTH(bpos) || apos == NORTH(bpos)) { if (board[WEST(apos)] == other) xpos = WEST(apos); else if (board[EAST(apos)] == other) xpos = EAST(apos); else continue; } else if (apos == WEST(bpos) || apos == EAST(bpos)) { if (board[SOUTH(apos)] == other) xpos = SOUTH(apos); else if (board[NORTH(apos)] == other) xpos = NORTH(apos); else continue; } else return; /* Incorrect configuration, give up. */ if (findlib(xpos, 2, libs2) == 2) { for (r = 0; r < 2; r++) if (libs2[r] != apos && libs2[r] != bpos && !is_self_atari(libs2[r], color)) ADD_CANDIDATE_MOVE(libs2[r], 0, *moves, "special_rescue4"); } } } /* In a situation like this: * * .XXXXX * XX.*OO * X.OX.. the following code can find the * ...... defensive move at '*'. * ------ * * .* ac * OX bd * * The only requirement is that d has at most as many liberties as b, * and as the newly placed stone at c. */ static void hane_rescue_moves(int str, int libs[4], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int apos, bpos, cpos, dpos; int num_libs = countlib(str); int k, l, r; ASSERT1(num_libs <= 4, str); for (r = 0; r < num_libs; r++) { /* Let (apos) be one of the three liberties. */ apos = libs[r]; /* Try to find the configuration above. */ for (k = 0; k < 4; k++) { bpos = apos + delta[k]; if (board[bpos] != color) continue; if (!same_string(bpos, str)) continue; for (l = 0; l < 2; l++) { int normal = delta[(k+1)%4]; if (l == 1) normal = -normal; cpos = apos + normal; if (board[cpos] != EMPTY) continue; dpos = bpos + normal; if (board[dpos] != other) continue; /* Configuration found. Now check liberty constraint. */ { int dlibs = countlib(dpos); if (dlibs > num_libs || dlibs > accuratelib(cpos, color, dlibs, NULL)) continue; } if (0 && !in_list(cpos, moves->num, moves->pos)) { gprintf("hane_rescue_move added for %1m at %1m\n", str, cpos); dump_stack(); showboard(0); } ADD_CANDIDATE_MOVE(cpos, 0, *moves, "hane_rescue"); } } } } /* In situations like these * * |XXXX |.X... |.X... * |OOOX |.XOO. |XXOO. * |..OX |OOXO. |OOXO. * |O.OX |O.X*O |O.XOO * |.X*. |O.X.O |O.X*O * +---- +----- +----- * * the smaller of the O strings can be defended by *. The property * they have in common is that the defended string has (at least) two * liberties in common with an X string and it's effective to play on * an exterior liberty of this string. Similarly it may be worth * defending a weak neighbor of the X string. * * This function may be called for strings with 2 or 3 liberties and * returns moves which are potentially useful in these positions. */ static void special_rescue5_moves(int str, int libs[3], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int apos, bpos; int k, r, s; int liberties = countlib(str); int libs2[4]; int liberties2; ASSERT1(liberties == 2 || liberties == 3, str); for (r = 0; r < liberties; r++) { apos = libs[r]; for (k = 0; k < 4; k++) { bpos = apos + delta[k]; if (board[bpos] != other) continue; /* Don't bother if it has too many liberties. */ if (countlib(bpos) > liberties + 1) continue; if (count_common_libs(str, bpos) < 2) continue; liberties2 = findlib(bpos, 4, libs2); for (s = 0; s < liberties2; s++) if (!liberty_of_string(libs2[s], str) && !is_self_atari(libs2[s], color)) ADD_CANDIDATE_MOVE(libs2[s], 0, *moves, "special_rescue5-A"); /* Reinforce the second order chain. */ if (liberties2 <= liberties) { int adj; int adjs[MAXCHAIN]; int t; adj = chainlinks2(bpos, adjs, 1); for (t = 0; t < adj; t++) { int cpos; break_chain_moves(adjs[t], moves); findlib(adjs[t], 1, &cpos); if (!is_self_atari(cpos, color)) ADD_CANDIDATE_MOVE(cpos, 0, *moves, "special_rescue5-B"); } /* Defend against double atari in the surrounding chain early. */ double_atari_chain2_moves(bpos, moves, 0); } } } } /* In situations like this * * |.bOX * |.Xa. * |.OXX * |.O.. * |.XX. * * the lower O string can often be defended at a or b. * * This function may be called for strings with 3 or 4 liberties and * returns the * moves in the configuration below: * * |..O |.*O * |.X. |.c* * |.O? |ab? * * It also adds the * move in these configurations: * * |.X. |.c* * |.OX |abX * * |.X. |.c* * |.O. |ab. * * Provided that * is not a self atari and that the X strings have * sufficiently few liberties. */ static void special_rescue6_moves(int str, int libs[3], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int apos, bpos, cpos; int right, up; int k, l, r; int liberties = countlib(str); ASSERT1(liberties == 3 || liberties == 4, str); for (r = 0; r < liberties; r++) { apos = libs[r]; for (k = 0; k < 4; k++) { right = delta[k]; if (ON_BOARD(apos - right)) continue; bpos = apos + right; if (board[bpos] != color || !same_string(str, bpos)) continue; for (l = 0; l < 2; l++) { up = delta[(k+1) % 4]; if (l == 1) up = -up; cpos = bpos + up; if (board[cpos] != other) continue; if (board[apos + up] != EMPTY) continue; if (board[cpos + right] != EMPTY) continue; if (board[apos + up + up] == EMPTY && board[cpos + up] == EMPTY && board[cpos + up + right] == color) { ADD_CANDIDATE_MOVE(cpos + right, 0, *moves, "special_rescue6-A"); ADD_CANDIDATE_MOVE(cpos + up, 0, *moves, "special_rescue6-B"); } else if (countlib(cpos) <= 3 && (board[bpos + right] == EMPTY || (board[bpos + right] == other && countlib(bpos + right) <= 4)) && !is_self_atari(cpos + right, color)) { ADD_CANDIDATE_MOVE(cpos + right, 0, *moves, "special_rescue6-C"); } } } } } /* * set_up_snapback_moves() is called with (str) a string having a * single liberty at (lib). * * This adds moves which may defend a string in atari by capturing a * neighbor in a snapback. One example is this position: * * OOOOO * OXXXO * OX.OX * OXOXX * OX*.. * ----- * * This code also finds the move * to defend the lone O stone with ko * in this position: * * |XXXXX * |XOOOX * |OX.OO * |.*... * +----- * */ static void set_up_snapback_moves(int str, int lib, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int libs2[2]; ASSERT1(countlib(str) == 1, str); /* This can only work if our string is a single stone and the * opponent is short of liberties. */ if (stackp <= backfill_depth && countstones(str) == 1 && approxlib(lib, other, 2, libs2) == 1 && (!is_self_atari(libs2[0], color) || is_ko(libs2[0], color, NULL))) ADD_CANDIDATE_MOVE(libs2[0], 0, *moves, "set_up_snapback"); } /* This function adds liberties of the superstring as candidate moves. * For performance, this is restricted to strings with liberty_cap * liberties, and to cases where at most 5 liberties would get considered. * * When attacking, we also try backfilling in case the direct approach * would be self-atari. * When defending, we also try second order liberties. */ static void superstring_moves(int str, struct reading_moves *moves, int liberty_cap, int does_attack) { int ss_liberties; int ss_libs[MAX_LIBERTIES + 4]; int color = board[str]; int other = OTHER_COLOR(color); int k; find_superstring_liberties(str, &ss_liberties, ss_libs, liberty_cap); if (ss_liberties <= 5) { for (k = 0; k < ss_liberties; k++) { int apos = ss_libs[k]; int alibs[2]; int alib = accuratelib(apos, other, 2, alibs); if (liberty_of_string(apos, str)) continue; if (alib >= 2) ADD_CANDIDATE_MOVE(apos, 0, *moves, "superstring liberty"); else if (alib == 1 && does_attack && board[alibs[0]] == EMPTY && approxlib(alibs[0], other, 3, NULL) >= 3) ADD_CANDIDATE_MOVE(alibs[0], 0, *moves, "superstring backfill"); if (!does_attack) special_rescue_moves(str, apos, moves); } } } /* This function is somewhat related to superstring_moves() but tries * to find moves to squeeze out liberties from the superstring, aiming * to capture the main string in a shortage of liberties. * * For a typical example, see the move E9 in reading:203,204. It is * assumed that the same move is effective both for attack and * defense. */ static void squeeze_moves(int str, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int libs[4]; int num_libs; int libs2[4]; int num_libs2; int k; int r; int potential_move = NO_MOVE; int previous_liberty; num_libs = findlib(str, 4, libs); gg_assert(num_libs <= 4); for (k = 0; k < num_libs; k++) { if (!is_suicide(libs[k], other)) continue; num_libs2 = approxlib(libs[k], color, 4, libs2); if (num_libs2 != num_libs) continue; for (r = 0; r < num_libs2; r++) if (!liberty_of_string(libs2[r], str)) { potential_move = libs2[r]; break; } previous_liberty = libs[k]; while (is_suicide(potential_move, other)) { num_libs2 = approxlib(potential_move, color, 3, libs2); if (num_libs2 != 2) { potential_move = NO_MOVE; break; } if (libs2[0] == previous_liberty) { previous_liberty = potential_move; potential_move = libs2[1]; } else { previous_liberty = potential_move; potential_move = libs2[0]; } if (liberty_of_string(potential_move, str)) { potential_move = NO_MOVE; break; } } if (potential_move == NO_MOVE || !is_self_atari(potential_move, other)) continue; approxlib(potential_move, other, 1, libs2); num_libs2 = approxlib(libs2[0], color, MAXLIBS, NULL); if (num_libs2 < 3 && num_libs2 < approxlib(potential_move, color, MAXLIBS, NULL)) ADD_CANDIDATE_MOVE(potential_move, 0, *moves, "squeeze move"); } } /* In positions like * * |.XXOO. * |XXOX.. * |OOOX*. * |...... * +------ * * the O stones to the left are best defended by the move at *. * * This function tries to find an adjacent string (apos) with exactly * three liberties. One of the liberties (bpos) must be on the edge * (but not in the corner). Diagonal to this liberty must be one stone * of the attacked string (cpos) and another liberty (dpos) of the * adjacent string. The third liberty (epos) must be adjacent to * (dpos). Furthermore must an O stone at (dpos) get at least three * liberties and and X stone at (epos) must get at most three * liberties. * * |.XXOO. * |XXOXe. * |OOcad. * |...b.. * +------ * * The defense move at (dpos) is proposed if the above conditions * are satisfied. */ static void edge_clamp_moves(int str, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int apos; int bpos; int cpos; int dpos; int epos; int adj, adjs[MAXCHAIN]; int libs[3]; int k, l, r; /* Pick up neighbors with three liberties. */ adj = chainlinks2(str, adjs, 3); for (r = 0; r < adj; r++) { apos = adjs[r]; /* Find a liberty at the edge. */ bpos = NO_MOVE; findlib(apos, 3, libs); for (k = 0; k < 3; k++) { if (is_edge_vertex(libs[k])) { bpos = libs[k]; break; } } if (bpos == NO_MOVE) continue; /* Edge liberty found. Establish up and right directions. */ for (k = 0; k < 4; k++) { int up = delta[k]; if (ON_BOARD(bpos - up)) continue; if (board[bpos + up] != other) continue; for (l = 0; l < 2; l++) { int right = delta[(k+1)%4]; if (l == 1) right = -right; cpos = bpos + up - right; dpos = bpos + up + right; if (board[cpos] != color || !same_string(cpos, str)) continue; if (board[dpos] != EMPTY || !liberty_of_string(dpos, apos)) continue; epos = dpos + up; if (board[epos] != EMPTY || !liberty_of_string(epos, apos)) continue; if (approxlib(dpos, color, 3, NULL) < 3) continue; if (approxlib(epos, other, 4, NULL) > 3) continue; /* (dpos) looks like a good move. Add it to the list with a * substantial initial score. */ ADD_CANDIDATE_MOVE(dpos, 10, *moves, "edge_clamp"); } } } } /* * This function handles some special cases on the edge. * * 1. If (str) points to a string and 'a' an edge liberty of it, * there is no point of trying to defend the string by crawling * along the edge if there is no hope of ever getting more liberties. * This is of course if the blocking enemy group has enough liberties * of its own. * * XX XX * O. Oa * -- -- * * This function searches the edge towards the corner and sees if there * is a friendly stone on one of the two first lines. If not, the move * is removed from the list of moves. * * 2. If (str) points to a string and 'a' an edge liberty of it, * the drawing back/climbing up move 'b' is often correct attack or * defense. Another good move to try is 'c' (but usually not for * defense of a 2 liberty string). * * X.? Xbc * O.. Oa. * --- --- * * This function adds the points configured like 'b' and 'c' relative to * (str) to the list of moves. * * color is the color to move. */ static void propose_edge_moves(int str, int *libs, int liberties, struct reading_moves *moves, int to_move) { int color = board[str]; int other = OTHER_COLOR(color); int right; int up; int apos; int k, l; int r; for (r = 0; r < liberties; r++) { apos = libs[r]; for (k = 0; k < 4; k++) { up = delta[k]; if (ON_BOARD(apos - up)) continue; for (l = 0; l < 2; l++) { right = delta[(k+1)%4]; if (l == 1) right = -right; if (board[apos + up] == other /* other on top of liberty */ && countlib(apos + up) > 4 /* blocking group must be secure */ && color == to_move) { /* only applicable as defense */ /* Case 1: other above the liberty (crawl along the edge). */ int xpos = apos; while (ON_BOARD(xpos)) { if (board[xpos] == color || board[xpos + up] == color) break; xpos += right; } /* If no friendly stone found, then it is pointless and we * can just as well remove the move. */ if (!ON_BOARD(xpos)) { REMOVE_CANDIDATE_MOVE(apos, *moves); } } else if (board[apos + up] == EMPTY /* empty above the liberty */ && board[apos - right + up] == other && board[apos + right] == EMPTY) { /* empty to the right */ /* Case 2: Try to escape or contain. */ /* Add b * If adjacent X stone in atari, boost the initial score of this * move. */ if (countlib(apos + up - right) == 1) ADD_CANDIDATE_MOVE(apos + up, 10, *moves, "propose_edge-A"); else { ADD_CANDIDATE_MOVE(apos + up, 0, *moves, "propose_edge-B"); /* Add c if empty */ if (board[apos + right + up] == EMPTY && (liberties != 2 || color != to_move)) ADD_CANDIDATE_MOVE(apos + right + up, 0, *moves, "propose_edge-C"); } } } } } } /* ================================================================ */ /* Attacking functions */ /* ================================================================ */ /* Like attack. If the opponent is komaster reading functions will not try * to take ko. */ static int do_attack(int str, int *move) { int color = board[str]; int xpos = NO_MOVE; int liberties; int result = 0; int retval; SETUP_TRACE_INFO("attack", str); ASSERT1(color != 0, str); if (color == 0) /* if assertions are turned off, silently fails */ return 0; str = find_origin(str); liberties = countlib(str); if (liberties > 4 || (liberties == 4 && stackp > fourlib_depth) || (liberties == 3 && stackp > depth)) { /* No need to cache the result in these cases. */ if (sgf_dumptree) { char buf[100]; sprintf(buf, "got 4 liberties (stackp:%d>%d)", stackp, fourlib_depth); SGFTRACE(0, 0, buf); } return 0; } /* Set "killer move" up. This move (if set) was successful in * another variation, so it is reasonable to try it now. However, * we only do this if the string has 4 liberties - otherwise the * situation changes too much from variation to variation. */ if (liberties > 3 && move) xpos = *move; /* Note that if return value is 1 (too small depth), the move will * still be used for move ordering. */ if (stackp <= depth && tt_get(&ttable, ATTACK, str, NO_MOVE, depth - stackp, NULL, &retval, NULL, &xpos) == 2) { TRACE_CACHED_RESULT(retval, xpos); SGFTRACE(xpos, retval, "cached"); if (move) *move = xpos; return retval; } /* Treat the attack differently depending on how many liberties the string at (str) has. */ if (liberties == 1) result = attack1(str, &xpos); else if (liberties == 2) { if (stackp > depth + 10) result = simple_ladder(str, &xpos); else result = attack2(str, &xpos); } else if (liberties == 3) result = attack3(str, &xpos); else if (liberties == 4) result = attack4(str, &xpos); ASSERT1(result >= 0 && result <= WIN, str); if (result) { READ_RETURN(ATTACK, str, depth - stackp, move, xpos, result); } READ_RETURN0(ATTACK, str, depth - stackp); } /* If (str) points to a group with exactly one liberty, attack1 * determines whether it can be captured by playing at this liberty. * If successful, (*move) is the killing move. move may be NULL if * caller is only interested in whether it can be captured. * * The attack may fail for two different reasons. The first one is * that the attack may be an illegal ko capture, in this case KO_B is * returned (need to play a ko threat before the attack can be * fulfilled). * * The second cause for failure is that the attack is caught in a * snapback. We must require that it is a proper snapback, though. By * proper snapback we mean a position like * * XXXXO * XO.XO * XOXOO * ----- * * where capture by O and recapture by X leaves the X stone intact * with at least two liberties: * * XXXXO * X..XO * X.XOO * ----- * * There are a number of different kinds of improper snapbacks, which * have in common that the attacked string ends up captured. We don't * consider these as failures to attack. Three examples are given below. * * XXOOOOO (X can recapture but loses most of the string.) * X.XXXXO * ------- * * XXXOOOOOOOO (Like the previous example, except O loses one more stone) * XO*XXXXXXXO * ----------- * * XXXOO (After three captures, the lone X stone is gone.) * XO.XO * ----- * * This function is fast and never branches. There's little point in * caching the result. */ static int attack1(int str, int *move) { int color = board[str]; int other = OTHER_COLOR(color); int xpos; int savemove = 0; int savecode = 0; int liberties; int libs[6]; int k; int r; int adjs[MAXCHAIN]; int adj; int apos; SETUP_TRACE_INFO("attack1", str); reading_node_counter++; /* Pick up the position of the liberty. */ findlib(str, 1, &xpos); /* If the attacked string consists of more than one stone, the * attack never fails. (This assumes simple ko rule. With superko * rule it could still be a ko violation.) */ if (countstones(str) > 1) { RETURN_RESULT(WIN, xpos, move, "last liberty"); } /* Try to play on the liberty. This fails if and only if it is an * illegal ko capture. */ if (trymove(xpos, other, "attack1-A", str)) { /* Is the attacker in atari? If not the attack was successful. */ if (countlib(xpos) > 1) { popgo(); RETURN_RESULT(WIN, xpos, move, "last liberty"); } /* If the attacking string is also a single stone, a possible * recapture would be a ko violation, so the defender has to make * a ko threat first. */ else if (countstones(xpos) == 1) { if (get_komaster() != other) { /* If the defender is allowed to take the ko the result is KO_A. */ CHECK_RESULT_UNREVERSED(savecode, savemove, KO_A, xpos, move, "last liberty - ko"); } else { /* But if the attacker is the attack was successful. */ popgo(); RETURN_RESULT(WIN, xpos, move, "last liberty"); } } /* Otherwise, do recapture. Notice that the liberty must be * at (str) since we have already established that this string * was a single stone. */ else if (trymove(str, color, "attack1-B", str)) { /* If this was a proper snapback, (str) will now have more * than one liberty. */ if (countlib(str) > 1) { /* Proper snapback, attack fails. */ popgo(); } else { popgo(); popgo(); RETURN_RESULT(WIN, xpos, move, "last liberty"); } } popgo(); } else {/* Illegal ko capture. */ if (get_komaster() != color) { CHECK_RESULT_UNREVERSED(savecode, savemove, KO_B, xpos, move, "last liberty - ko"); } } /* If not yet successful, try backfilling and back-capturing. * An example of back-capturing can be found in reading:234. * Backfilling is maybe only meaningful in positions involving ko. */ liberties = approxlib(xpos, color, 6, libs); if (liberties <= 5) for (k = 0; k < liberties; k++) { apos = libs[k]; if (!is_self_atari(apos, other) && trymove(apos, other, "attack1-C", str)) { int dcode = do_find_defense(str, NULL); if (dcode != WIN && do_attack(str, NULL)) { if (dcode == 0) { popgo(); RETURN_RESULT(WIN, apos, move, "backfilling"); } UPDATE_SAVED_KO_RESULT(savecode, savemove, dcode, apos); } popgo(); } } adj = chainlinks2(str, adjs, 1); for (r = 0; r < adj; r++) { if (liberty_of_string(xpos, adjs[r])) { int adjs2[MAXCHAIN]; int adj2; adj2 = chainlinks2(adjs[r], adjs2, 1); for (k = 0; k < adj2; k++) { int ko_move; if (adjs2[k] == str) continue; findlib(adjs2[k], 1, &apos); if (komaster_trymove(apos, other, "attack1-D", str, &ko_move, stackp <= ko_depth && savecode == 0)) { if (!ko_move) { int dcode = do_find_defense(str, NULL); if (dcode != WIN && do_attack(str, NULL)) { popgo(); CHECK_RESULT(savecode, savemove, dcode, apos, move, "attack effective"); } else popgo(); } else { if (do_find_defense(str, NULL) != WIN && do_attack(str, NULL) != 0) { savemove = apos; savecode = KO_B; } popgo(); } } } } } if (savecode == 0) { RETURN_RESULT(0, 0, move, NULL); } RETURN_RESULT(savecode, savemove, move, "saved move"); } /* If str points to a group with exactly two liberties * attack2 determines whether it can be captured in ladder or net. * If yes, *move is the killing move. move may be null if caller * is only interested in whether it can be captured. * * Returns KO_A or KO_B if it can be killed conditioned on ko. Returns * KO_A if it can be killed provided (other) is willing to ignore any * ko threat. Returns KO_B if (other) wins provided he has a ko threat * which must be answered. Can give a return code KO_B yet *move=0 if * the winning move is an illegal ko capture. In this case, making a * ko threat and having it answered should transform the position to * one where the return code is KO_A. * * See the comment before defend1 about ladders and reading depth. */ static int attack2(int str, int *move) { int color = board[str]; int other = OTHER_COLOR(color); int hpos; int xpos = NO_MOVE; int liberties, r; int libs[2]; int libs2[2]; int adj, adjs[MAXCHAIN]; int savemove = 0; int savecode = 0; int k; int atari_possible = 0; struct reading_moves moves; int adjacent_liberties = 0; int pass; int suggest_move = NO_MOVE; SETUP_TRACE_INFO("attack2", str); reading_node_counter++; moves.num = 0; moves.num_tried = 0; str = find_origin(str); ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 2, str); for (pass = 0; pass < 4; pass++) { switch (pass) { case 0: /* The attack may fail if a boundary string is in atari and cannot * be defended. First we must try defending such a string. * * We start by trying to defend the boundary string by looking for an * adjacent string which is in atari. */ adj = chainlinks2(str, adjs, 1); for (r = 0; r < adj; r++) { /* If stackp > depth and any boundary chain is in atari, assume safe. * However, if the captured chain is only of size 1, there can still * be a working ladder, so continue if that is the case. * Also if the string in atari shares its liberty with the * attacked string, drawing it out may enable the ladder to * continue. */ if (stackp > depth && countstones(adjs[r]) > 1 && !have_common_lib(str, adjs[r], NULL)) { RETURN_RESULT(0, 0, move, "boundary in atari"); } /* Pick up moves breaking the second order chain. */ if (stackp <= depth) break_chain_moves(adjs[r], &moves); findlib(adjs[r], 1, &hpos); ADD_CANDIDATE_MOVE(hpos, 0, moves, "save_boundary"); } /* Get the two liberties of (str). */ liberties = findlib(str, 2, libs); ASSERT1(liberties == 2, str); if (DIRECT_NEIGHBORS(libs[0], libs[1])) adjacent_liberties = 1; for (k = 0; k < 2; k++) { int apos = libs[k]; if (!is_self_atari(apos, other)) atari_possible = 1; /* We only want to consider the move at (apos) if: * stackp <= backfill_depth * -or- stackp <= depth and it is an isolated stone * -or- it is not in immediate atari */ if (stackp <= backfill_depth || ((stackp <= depth || adjacent_liberties) && !has_neighbor(apos, other)) || !is_self_atari(apos, other)) ADD_CANDIDATE_MOVE(apos, 0, moves, "liberty"); /* Try backfilling if atari is impossible. */ if (stackp <= backfill_depth && approxlib(apos, other, 2, libs2) == 1) { ADD_CANDIDATE_MOVE(libs2[0], 0, moves, "backfill"); /* If there is a neighbor in atari, we also try back-capturing. */ for (r = 0; r < 4; r++) { int bpos = libs2[0] + delta[r]; if (board[bpos] == other && chainlinks2(bpos, adjs, 1) > 0) { /* FIXME: If there is more than one neighbor in atari, we * currently just take one randomly. This is maybe not good * enough. We might also want to check against snapback. * * FIXME: What is the purpose of this? It produces some * completely irrelevant moves (e.g. if bpos is a huge string * with many liberties and adjs[0] is somewhere else on the * board). */ findlib(adjs[0], 1, &xpos); ADD_CANDIDATE_MOVE(xpos, 0, moves, "back-capture"); } } } } /* If we can't make a direct atari, look for edge blocking moves. */ if (!atari_possible) for (k = 0; k < 2; k++) edge_block_moves(str, libs[k], &moves); /* If one of the surrounding chains have only two liberties, which * coincide with the liberties of the attacked string, we try to * backcapture. */ adj = chainlinks2(str, adjs, 2); for (r = 0; r < adj; r++) { int apos = adjs[r]; if (liberty_of_string(libs[0], apos) && liberty_of_string(libs[1], apos)) break_chain_moves(apos, &moves); } propose_edge_moves(str, libs, liberties, &moves, other); break; case 1: if (stackp <= backfill_depth) { special_attack2_moves(str, libs, &moves); special_attack3_moves(str, libs, &moves); special_attack4_moves(str, libs, &moves); } break; case 2: find_cap_moves(str, &moves); break; case 3: /* If it is not possible to make a direct atari, we try filling * a liberty of the superstring. */ if (get_level() >= 8 && stackp <= backfill_depth && (stackp <= superstring_depth || !atari_possible)) { int liberty_cap = 2; if (stackp <= backfill2_depth) liberty_cap = 3; superstring_moves(str, &moves, liberty_cap, 1); squeeze_moves(str, &moves); } break; default: abort(); } /* switch (pass) */ order_moves(str, &moves, other, read_function_name, *move); ATTACK_TRY_MOVES(0, &suggest_move); } RETURN_RESULT(savecode, savemove, move, "saved move"); } /* attack3(str, *move) is used when (str) points to a group with * three liberties. It returns true if it finds a way to kill the group. * * Return code is KO_A if the group can be killed if the attacker is * willing to ignore any ko threat. * * Return code is KO_B if the group can be killed if the attacker is * able to find a ko threat which must be answered. * * If non-NULL (*move) will be set to the move which makes the * attack succeed. */ static int attack3(int str, int *move) { int color = board[str]; int other = OTHER_COLOR(color); int adj, adjs[MAXCHAIN]; int liberties; int libs[3]; int r; int k; struct reading_moves moves; int savemove = 0; int savecode = 0; int pass; int suggest_move = NO_MOVE; SETUP_TRACE_INFO("attack3", str); reading_node_counter++; moves.num = 0; moves.num_tried = 0; ASSERT1(IS_STONE(board[str]), str); ASSERT1(stackp <= depth, str); for (pass = 0; pass < 4; pass++) { switch (pass) { case 0: adj = chainlinks2(str, adjs, 1); for (r = 0; r < adj; r++) { int hpos; break_chain_moves(adjs[r], &moves); findlib(adjs[r], 1, &hpos); ADD_CANDIDATE_MOVE(hpos, 0, moves, "save_boundary"); } /* Defend against double atari in the surrounding chain early. */ double_atari_chain2_moves(str, &moves, stackp <= superstring_depth); /* Get the three liberties of (str). */ liberties = findlib(str, 3, libs); ASSERT1(liberties == 3, str); for (k = 0; k < 3; k++) { #if 0 int libs2[2]; #endif int apos = libs[k]; /* We only want to consider the move at (apos) if: * stackp <= backfill_depth * -or- stackp <= depth and it is an isolated stone * -or- it is not in immediate atari */ if (stackp <= backfill_depth || (stackp <= depth && !has_neighbor(apos, other)) || !is_self_atari(apos, other)) ADD_CANDIDATE_MOVE(apos, 0, moves, "liberty"); edge_closing_backfill_moves(str, apos, &moves); #if 0 /* Try backfilling if atari is impossible. */ if (stackp <= backfill_depth && approxlib(apos, other, 2, libs2) == 1) { ADD_CANDIDATE_MOVE(libs2[0], 0, moves, "backfill"); } #endif /* Look for edge blocking moves. */ edge_block_moves(str, apos, &moves); } /* Pick up some edge moves. */ propose_edge_moves(str, libs, liberties, &moves, other); break; case 1: /* The simple ataris didn't work. Try something more fancy. */ if (stackp <= backfill_depth) find_cap_moves(str, &moves); if (stackp <= fourlib_depth) draw_back_moves(str, &moves); break; case 2: /* Try to defend chain links with two liberties. */ if (stackp <= backfill2_depth) { adj = chainlinks2(str, adjs, 2); for (r = 0; r < adj; r++) { int libs2[2]; findlib(adjs[r], 2, libs2); if (approxlib(libs2[0], other, 4, NULL) > 3 && approxlib(libs2[1], other, 4, NULL) > 3) continue; break_chain_moves(adjs[r], &moves); break_chain2_moves(adjs[r], &moves, 1, 0); for (k = 0; k < 2; k++) ADD_CANDIDATE_MOVE(libs2[k], 0, moves, "save_boundary-2"); } } break; case 3: /* If nothing else works, we try filling a liberty of the * super_string. */ if (get_level() >= 8 && stackp <= backfill2_depth) { superstring_moves(str, &moves, 3, 1); squeeze_moves(str, &moves); } break; default: abort(); } order_moves(str, &moves, other, read_function_name, *move); ATTACK_TRY_MOVES(1, &suggest_move); } /* for (pass... */ RETURN_RESULT(savecode, savemove, move, "saved move"); } /* attack4 tries to capture a string with 4 liberties. */ static int attack4(int str, int *move) { int color = board[str]; int other = OTHER_COLOR(color); int r; int k; int liberties; int libs[4]; int adj, adjs[MAXCHAIN]; struct reading_moves moves; int savemove = 0; int savecode = 0; int pass; int suggest_move = NO_MOVE; SETUP_TRACE_INFO("attack4", str); ASSERT1(IS_STONE(board[str]), str); reading_node_counter++; moves.num = 0; moves.num_tried = 0; if (stackp > depth) { SGFTRACE(0, 0, "stackp > depth"); return 0; } for (pass = 0; pass < 2; pass++) { switch (pass) { case 0: adj = chainlinks2(str, adjs, 1); for (r = 0; r < adj; r++) { int hpos; break_chain_moves(adjs[r], &moves); findlib(adjs[r], 1, &hpos); ADD_CANDIDATE_MOVE(hpos, 0, moves, "save_boundary"); } /* Defend against double atari in the surrounding chain early. */ double_atari_chain2_moves(str, &moves, stackp <= superstring_depth); /* Give a score bonus to the chain preserving moves. */ for (k = 0; k < moves.num; k++) moves.score[k] += 5; /* Get the four liberties of (str). */ liberties = findlib(str, 4, libs); ASSERT1(liberties == 4, str); for (k = 0; k < 4; k++) { int apos = libs[k]; /* We only want to consider the move at (apos) if: * stackp <= backfill_depth * -or- stackp <= depth and it is an isolated stone * -or- it is not in immediate atari */ if (stackp <= backfill_depth || (stackp <= depth && !has_neighbor(apos, other)) || !is_self_atari(apos, other)) ADD_CANDIDATE_MOVE(apos, 0, moves, "liberty"); edge_closing_backfill_moves(str, apos, &moves); /* Look for edge blocking moves. */ edge_block_moves(str, apos, &moves); } /* Pick up some edge moves. */ propose_edge_moves(str, libs, liberties, &moves, other); break; case 1: if (stackp <= backfill_depth) find_cap_moves(str, &moves); break; default: abort(); } order_moves(str, &moves, other, read_function_name, *move); ATTACK_TRY_MOVES(1, &suggest_move); } /* for (pass = ... */ RETURN_RESULT(savecode, savemove, move, "saved move"); } /* If (str) points to a string with 2 - 4 liberties, * find_cap_moves(str, &moves) * looks for a configuration of the following type: * * Xa * b* * * where X are elements of the string in question and a and b are * two of its liberties. * * For larger strings, this can find moves like * * XXXXX * XX.XX * X.*.X * XX.XX * XXXXX * * even though they are not capping moves. */ static void find_cap_moves(int str, struct reading_moves *moves) { int alib, blib; int numlibs; int libs[4]; int i, j; int ai, aj; int bi, bj; numlibs = findlib(str, 4, libs); if (numlibs > 4 || numlibs < 2) return; for (i = 0; i < numlibs - 1; i++) { for (j = i + 1; j < numlibs; j++) { alib = libs[i]; blib = libs[j]; /* Check if the two liberties are located like the figure above. */ if (!DIAGONAL_NEIGHBORS(alib, blib)) continue; ai = I(alib); aj = J(alib); bi = I(blib); bj = J(blib); /* Which of the two corner points should we use? One of them is * always occupied by the string at (str), the other one is either * free or occupied by something else. */ if (BOARD(bi, aj) == EMPTY) ADD_CANDIDATE_MOVE(POS(bi, aj), 10, *moves, "find_cap"); else if (BOARD(ai, bj) == EMPTY) ADD_CANDIDATE_MOVE(POS(ai, bj), 10, *moves, "find_cap"); } } } /* In a situation like this: * * ----- the code that * cO.OX follows can find * XXOOX the attacking move * XO.OX at c. * XOOOX * XXXXX * * The name of the function corresponds to special_rescue2, which is * fairly similar to this situation. */ static void special_attack2_moves(int str, int libs[2], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int newlibs[3]; int xpos; int k; for (k = 0; k < 2; k++) { if (is_suicide(libs[k], other) && (approxlib(libs[k], color, 3, newlibs) == 2)) { if (newlibs[0] != libs[1-k]) xpos = newlibs[0]; else xpos = newlibs[1]; if (!is_self_atari(xpos, other)) { ADD_CANDIDATE_MOVE(xpos, 0, *moves, "special_attack2"); } } } } /* In situations like these: * * ..XXX.. ...XX * .XX.XX. .cO.X * XXOOOXX ....X * XO.O.OX XOOXX * XO.c.OX XXXX. * ------- * * the code that follows can find the attacking move at c. */ static void special_attack3_moves(int str, int libs[2], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int xpos; int apos; int bpos; int k; ASSERT1(countlib(str) == 2, str); for (k = 0; k < 2; k++) { apos = libs[k]; bpos = libs[1-k]; if (apos == SOUTH(bpos) || apos == NORTH(bpos)) { if (board[WEST(apos)] == EMPTY) xpos = WEST(apos); else if (board[EAST(apos)] == EMPTY) xpos = EAST(apos); else continue; } else if (apos == WEST(bpos) || apos == EAST(bpos)) { if (board[SOUTH(apos)] == EMPTY) xpos = SOUTH(apos); else if (board[NORTH(apos)] == EMPTY) xpos = NORTH(apos); else continue; } else return; /* Incorrect configuration, give up. */ if (!is_self_atari(xpos, other)) ADD_CANDIDATE_MOVE(xpos, 0, *moves, "special_attack3"); } } /* In situations like these: * * ...O.O... ...O.O... * XXXXOOXXX XXXXOOXXX * XOOOXXO*. Xsssbbcd. * .X.O..... .X.sa.e.. * --------- --------- * * the code that follows can find the attacking move at *. * * Also for situations in which c has three liberties, one of which in common * with b, the respective attacking move is found (see reading:52 for an * example). */ static void special_attack4_moves(int str, int libs[2], struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int adj, adjs[MAXCHAIN]; int adj2, adjs2[MAXCHAIN]; int libs2[3]; int apos; int bpos = 0; int cpos; int dpos; int epos; int clibs; int dlibs; int elibs; int bc_common_lib; int k, s, t, u; ASSERT1(countlib(str) == 2, str); /* To avoid making this too general, we require that both * liberties are self ataris for X. */ if (!is_self_atari(libs[0], other) || !is_self_atari(libs[1], other)) return; /* Pick up chain links with 2 liberties. */ adj = chainlinks2(str, adjs, 2); for (k = 0; k < 2; k++) { apos = libs[k]; /* Check that (apos) also is a liberty of one of the two liberty * chain links. */ for (s = 0; s < adj; s++) if (liberty_of_string(apos, adjs[s])) { bpos = adjs[s]; break; } /* Nothing found. */ if (s == adj) continue; /* Now require that (bpos) has a chain link, different from (str), * also with two liberties, or with three liberties, but one in common * with (bpos). */ adj2 = chainlinks3(bpos, adjs2, 3); for (s = 0; s < adj2; s++) { cpos = adjs2[s]; if (same_string(cpos, str)) continue; /* Pick up the liberties of (cpos). */ clibs = findlib(cpos, 3, libs2); /* No need to do something fancy if it is in atari already. */ if (clibs < 2) continue; /* (cpos) has three liberties, none of which in commmon with (bpos) * attacking it seems too difficult. */ bc_common_lib = have_common_lib(bpos, cpos, NULL); if (clibs > 2 && !bc_common_lib) continue; /* Try playing at a liberty. Before doing this, verify that * (cpos) cannot get more than three liberties by answering on * another liberty and that we are not putting ourselves in atari. * We also should only allow ourselves to get fewer liberties than * the defender in case (bpos) and (cpos) have a common liberty. */ for (t = 0; t < clibs; t++) { dpos = libs2[t]; if (is_self_atari(dpos, other)) continue; for (u = 0; u < clibs; u++) { if (t == u) continue; epos = libs2[u]; elibs = approxlib(epos, color, 4, NULL); if (elibs > 3) break; dlibs = approxlib(dpos, other, 3, NULL); if (elibs > dlibs && !bc_common_lib) break; } if (u >= clibs) /* No break occurred. */ ADD_CANDIDATE_MOVE(dpos, 0, *moves, "special_attack4"); } } } } /* * If (str) points to a string, draw_back(str, &moves) * looks for a move in the following configuration which attacks * the string: * * X* X=attacker, O=defender * O. * * In the initial implementation we consider cases * where X has exactly 2 liberties. * */ static void draw_back_moves(int str, struct reading_moves *moves) { int r, k; int adj, adjs[MAXCHAIN]; int libs[2]; adj = chainlinks2(str, adjs, 2); for (r = 0; r < adj; r++) { findlib(adjs[r], 2, libs); for (k = 0; k < 2; k++) { if (!liberty_of_string(libs[k], str) && ((ON_BOARD1(SOUTH(libs[k])) && liberty_of_string(SOUTH(libs[k]), str)) || (ON_BOARD1(WEST(libs[k])) && liberty_of_string(WEST(libs[k]), str)) || (ON_BOARD1(NORTH(libs[k])) && liberty_of_string(NORTH(libs[k]), str)) || (ON_BOARD1(EAST(libs[k])) && liberty_of_string(EAST(libs[k]), str)))) { ADD_CANDIDATE_MOVE(libs[k], 0, *moves, "draw_back"); } } } } /* In the following position the reading is much simplifed if we start * with the edge closing backfilling move at *. * * |OO... * |.OOO. * |.X.O. * |XXXO. * |.X.*. * +----- * * This function identifies the situation * * ?XOb * Xatc * ---- * * where a is a liberty of the attacked string, t is the proposed move, * and b and c do not contain more O stones than X stones. */ static void edge_closing_backfill_moves(int str, int apos, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int k; int bpos; int cpos; int number_x, number_o; for (k = 0; k < 4; k++) { int up = delta[k]; int right = delta[(k+1)%4]; if (ON_BOARD(apos - up)) continue; if (board[apos + up] != color) return; if (board[apos + right] == EMPTY && (!ON_BOARD(apos - right) || board[apos - right] == color)) ; /* Everything ok so far. */ else if (board[apos - right] == EMPTY && (!ON_BOARD(apos + right) || board[apos + right] == color)) { /* Negate right direction. */ right = -right; } else return; if (board[apos + up + right] != other) return; bpos = apos + up + 2 * right; if (!ON_BOARD(bpos)) return; cpos = apos + 2 * right; number_x = 0; number_o = 0; if (board[bpos] == color) number_x++; else if (board[bpos] == other) number_o++; if (board[cpos] == color) number_x++; else if (board[cpos] == other) number_o++; if (number_o > number_x) return; ADD_CANDIDATE_MOVE(apos + right, 0, *moves, "edge_closing_backfill"); return; } } /* The first version of this function seemed to induce too many * variations and has therefore been replaced by a much more limited * version. */ #if 0 /* In positions like * * OO... * XXO*. * x.X*. * ----- * * where the X stones to the left are being attacked, it is often a * good idea to first consider either or both of the moves marked by * * in the diagram. Notice that propose_edge_moves() doesn't help with * this, since the rightmost X stone is not part of the attacked * string, only the corresponding superstring. * * This function identifies the situation * * ?XO.? ?bdf? * ?.X.o haceg * ----- ----- * * where a is a liberty of the attacked string, b is a stone of the * attacked string, and e and f are the considered moves. Also * considered is the situation where the conditions to the right are * not correct but c has only two liberties anyway. If safe, the move * to make atari on c is proposed. * * Notice, this code is disabled, as commented above. */ static void edge_block_moves(int str, int apos, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int cpos; int dpos; int epos; int fpos; int gpos; int hpos; int score; int k, l; /* Search for the right orientation. */ for (k = 0; k < 4; k++) { int up = delta[k]; if (ON_BOARD(apos - up)) continue; if (board[apos + up] != color || !same_string(apos + up, str)) return; for (l = 0; l < 2; l++) { int right = delta[(k+1)%4]; if (l == 1) right = -right; cpos = apos + right; dpos = apos + right + up; if (board[cpos] != color || board[dpos] != other) continue; epos = cpos + right; fpos = dpos + right; gpos = epos + right; hpos = apos - right; if (!ON_BOARD(epos)) continue; if (board[epos] == EMPTY && board[fpos] == EMPTY && (board[gpos] != color)) { /* Everything is set up, suggest moves at e and f. */ if (!ON_BOARD(hpos) || board[hpos] == color) score = 0; else score = -5; if (countlib(str) == 2) score -= 10; ADD_CANDIDATE_MOVE(epos, score, *moves, "edge_block-A"); if (countlib(dpos) == 1) score = 25; else score = 0; if (countlib(str) == 2) score -= 10; ADD_CANDIDATE_MOVE(fpos, score, *moves, "edge_block-B"); } else if (countlib(cpos) == 2 && countlib(dpos) > 1) { int libs[2]; int move; findlib(cpos, 2, libs); if (libs[0] == apos) move = libs[1]; else move = libs[0]; if (!is_self_atari(move, other)) ADD_CANDIDATE_MOVE(move, 0, *moves, "edge_block-C"); } } } } #else /* In positions like * * OOX.. * XXO*. * x.X.. * ----- * * where the X stones to the left are being attacked, it is usually * important to start by considering the move at *. Thus we propose * the move at * with a high initial score. * * Also, it is often needed to prevent "crawling" along first line * which can eventually give defender more liberties, like here: * * O.OO..X * OXXO..X * ...X*.. * ------- * * This function identifies the situation * * XO.? bdf? * .X.o aceg * ---- ---- * * where a is a liberty of the attacked string, b is a stone of the * attacked string, and e and f are the considered moves. */ static void edge_block_moves(int str, int apos, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int k; /* Search for the right orientation. */ for (k = 0; k < 4; k++) { int l; int up = delta[k]; if (ON_BOARD(apos - up)) continue; if (board[apos + up] != color || !same_string(apos + up, str)) return; for (l = 0; l < 2; l++) { int right = delta[(k+1)%4]; int cpos; int dpos; int epos; int fpos; if (l == 1) right = -right; cpos = apos + right; dpos = apos + right + up; epos = cpos + right; fpos = dpos + right; if (board[cpos] == color && board[dpos] == other && board[epos] == EMPTY && board[fpos] == EMPTY) { if (countlib(dpos) == 1) { int gpos = epos + right; /* Check if we have the first situation. */ if (board[gpos] != color) ADD_CANDIDATE_MOVE(fpos, 30, *moves, "edge_block-A"); } else { int edge_scan; /* Look along board edge to see if the defender's string can * run away to a friend. */ for (edge_scan = epos; ; edge_scan += right) { if (board[edge_scan] == color || board[edge_scan + up] == color) { ADD_CANDIDATE_MOVE(epos, 10, *moves, "edge_block-B"); break; } if (board[edge_scan] != EMPTY || board[edge_scan + up] != EMPTY) break; } } } } } } #endif /* ================================================================ */ /* Defending by attacking surrounding strings */ /* ================================================================ */ /* Add the chainbreaking moves relative to the string (str) to the * (moves) struct. */ static void break_chain_moves(int str, struct reading_moves *moves) { int r; int xpos; int adj, adjs[MAXCHAIN]; /* Find links in atari. */ adj = chainlinks2(str, adjs, 1); for (r = 0; r < adj; r++) { findlib(adjs[r], 1, &xpos); ADD_CANDIDATE_MOVE(xpos, 1, *moves, "break_chain"); } } /* defend_secondary_chain1_moves() tries to break a chain by defending * "secondary chain", that is, own strings surrounding a given * opponent string (which is in turn a chainlink for another own * string, phew... :). It only defends own strings in atari. * * When defending is done by stretching, it is required that the defending * stone played gets at least `min_liberties', or one less if it is * adjacent to the opponent chainlink. * * Returns true if there where any secondary strings that needed defence * (which does not imply they actually where defended). */ static int defend_secondary_chain1_moves(int str, struct reading_moves *moves, int min_liberties) { int r, s; int color = OTHER_COLOR(board[str]); int xpos; int adj; int adj2; int adjs[MAXCHAIN]; int adjs2[MAXCHAIN]; /* Find links in atari. */ adj = chainlinks2(str, adjs, 1); for (r = 0; r < adj; r++) { /* Stretch out. */ findlib(adjs[r], 1, &xpos); if (approxlib(xpos, color, min_liberties, NULL) + neighbor_of_string(xpos, str) >= min_liberties) ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain1-A"); /* Capture adjacent stones in atari, if any. */ adj2 = chainlinks2(adjs[r], adjs2, 1); for (s = 0; s < adj2; s++) { findlib(adjs2[s], 1, &xpos); if (!is_self_atari(xpos, color)) ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain1-B"); } } return adj; } /* defend_secondary_chain2_moves() tries to break a chain by defending * "secondary chain", that is, own strings surrounding a given * opponent string (which is in turn a chainlink for another own * string, phew... :). It only defends own strings in * with two liberties. * * When defending is done by stretching, it is required that the defending * stone played gets at least `min_liberties', or one less if it is * adjacent to the opponent chainlink. Defence can also be done by capturing * opponent stones or trying to capture them with an atari. */ static void defend_secondary_chain2_moves(int str, struct reading_moves *moves, int min_liberties) { int r, s, t; int color = OTHER_COLOR(board[str]); int xpos; int adj; int adj2; int adjs[MAXCHAIN]; int adjs2[MAXCHAIN]; int libs[2]; /* Find links with two liberties. */ adj = chainlinks2(str, adjs, 2); for (r = 0; r < adj; r++) { if (!have_common_lib(str, adjs[r], NULL)) continue; /* Stretch out. */ findlib(adjs[r], 2, libs); for (t = 0; t < 2; t++) { xpos = libs[t]; if (approxlib(xpos, color, min_liberties, NULL) + neighbor_of_string(xpos, str) >= min_liberties) ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain2-A"); } /* Capture adjacent stones in atari, if any. */ adj2 = chainlinks2(adjs[r], adjs2, 1); for (s = 0; s < adj2; s++) { findlib(adjs2[s], 1, &xpos); if (!is_self_atari(xpos, color)) ADD_CANDIDATE_MOVE(xpos, 0, *moves, "defend_secondary_chain2-B"); } /* Look for neighbours we can atari. */ adj2 = chainlinks2(adjs[r], adjs2, 2); for (s = 0; s < adj2; s++) { findlib(adjs2[s], 2, libs); for (t = 0; t < 2; t++) { /* Only atari if target has no easy escape with his other liberty. */ if (approxlib(libs[1-t], OTHER_COLOR(color), 3, NULL) < 3 && !is_self_atari(libs[t], color)) { ADD_CANDIDATE_MOVE(libs[t], 0, *moves, "defend_secondary_chain2-C"); } } } } } /* * Find moves which immediately capture chain links with 2 * liberties, in the sense that the links cannot escape atari. * * The used heuristics are slightly sloppy, so useless moves may * appear occasionally. This should, however, only lead to slightly * worse performance but not to incorrect results. */ static void break_chain2_efficient_moves(int str, struct reading_moves *moves) { int r; int adj, adjs[MAXCHAIN]; /* Find links with 2 liberties. */ adj = chainlinks2(str, adjs, 2); for (r = 0; r < adj; r++) do_find_break_chain2_efficient_moves(str, adjs[r], moves); } /* Helper function for break_chain2_efficient_moves(). */ static void do_find_break_chain2_efficient_moves(int str, int adj, struct reading_moves *moves) { int color = board[str]; int other = OTHER_COLOR(color); int k; int adj2, adjs2[MAXCHAIN]; int libs[2]; int pos1; int pos2; ASSERT1(countlib(adj) == 2, adj); adj2 = chainlinks2(adj, adjs2, 1); if (adj2 == 1 && countlib(str) > 2) { int apos; break_chain_moves(adjs2[0], moves); findlib(adjs2[0], 1, &apos); if (!is_self_atari(apos, color)) ADD_CANDIDATE_MOVE(apos, 0, *moves, "break_chain2_efficient-A"); return; } if (adj2 > 1) return; findlib(adj, 2, libs); for (k = 0; k < 2; k++) if (approxlib(libs[k], other, 3, NULL) <= 2 && !is_self_atari(libs[1 - k], color)) ADD_CANDIDATE_MOVE(libs[1 - k], 0, *moves, "break_chain2_efficient-B"); /* A common special case is this kind of edge position * * ..XXX. * X.XOO. * XOOX*. * ...... * ------ * * where a move at * is most effective for saving the two stones * to the left. * * The code below tries to identify this case. We use the crude * heuristic that the two liberties of the X stone we want to * capture should be placed diagonally and that one liberty should * be on the edge. Then we propose to play the other liberty. * Notice that both moves may be proposed when attacking a stone * on 2-2. * * Update: This was too crude. Also require that the X stone is on * the second line and that the proposed move is not a self-atari. */ if (!DIAGONAL_NEIGHBORS(libs[0], libs[1])) return; /* Since we know that the two liberties are diagonal, the following * construction gives the two vertices "between" the liberties. */ pos1 = NORTH(gg_max(libs[0], libs[1])); pos2 = SOUTH(gg_min(libs[0], libs[1])); if ((board[pos1] != other || !is_edge_vertex(pos2) || !same_string(pos1, adj)) && (board[pos2] != other || !is_edge_vertex(pos1) || !same_string(pos2, adj))) return; if (is_edge_vertex(libs[0]) && !is_self_atari(libs[1], color)) ADD_CANDIDATE_MOVE(libs[1], 1, *moves, "break_chain2_efficient-C"); if (is_edge_vertex(libs[1]) && !is_self_atari(libs[0], color)) ADD_CANDIDATE_MOVE(libs[0], 1, *moves, "break_chain2_efficient-C"); } /* (str) points to a string with two or more liberties. break_chain2_moves() * tries to defend this string by attacking a neighbouring string with * two liberties. * This is done by playing on either of its liberties * (if (require_safe) is true these are only used if they are not * self-ataris), taking a neighbour out of atari or by backfilling if * both liberties are self-ataris. */ static void break_chain2_moves(int str, struct reading_moves *moves, int require_safe, int be_aggressive) { int color = board[str]; int other = OTHER_COLOR(color); int r; int adj; int adjs[MAXCHAIN]; adj = chainlinks2(str, adjs, 2); for (r = 0; r < adj; r++) { int k; int apos = adjs[r]; int libs[2]; int unsafe[2]; int dummy_adjs[MAXCHAIN]; findlib(apos, 2, libs); /* If stackp > backfill_depth, don't bother playing liberties of * 2-liberty strings if those also have at least one neighbor in * atari. This is intended to solve reading:171 and generally reduce * the number of nodes. */ if (stackp > backfill_depth && chainlinks2(apos, dummy_adjs, 1) > 0) continue; for (k = 0; k < 2; k++) { unsafe[k] = is_self_atari(libs[k], color); if (!unsafe[k] || is_ko(libs[k], color, NULL) || (!require_safe && approxlib(libs[k], other, 5, NULL) < 5)) ADD_CANDIDATE_MOVE(libs[k], 0, *moves, "break_chain2-A"); } if (stackp <= break_chain_depth || (be_aggressive && stackp <= backfill_depth)) { /* If the chain link cannot escape easily, try to defend all adjacent * friendly stones in atari (if any). If there are none, defend * adjacent friendly stones with only two liberties. */ if (approxlib(libs[0], other, 4, NULL) < 4 && approxlib(libs[1], other, 4, NULL) < 4) { if (!defend_secondary_chain1_moves(adjs[r], moves, 2)) defend_secondary_chain2_moves(adjs[r], moves, 2); } } if (unsafe[0] && unsafe[1] && (stackp <= backfill2_depth || have_common_lib(str, apos, NULL))) { int lib; /* Find backfilling moves. */ for (k = 0; k < 2; k++) { int libs2[3]; if (approxlib(libs[k], other, 3, libs2) == 2) { if (!is_self_atari(libs2[0], color)) ADD_CANDIDATE_MOVE(libs2[0], 0, *moves, "break_chain2-B"); if (!is_self_atari(libs2[1], color)) ADD_CANDIDATE_MOVE(libs2[1], 0, *moves, "break_chain2-B"); } } /* Consider this case (reading:188): * * |.OOOXXX * |OXXXOOO * |.X.O... * +------- * * We cannot atari the corner X string immediatly, so we need to * backfill. However, to avoid generating too many variations, * we require that the opponent string is well restrained. * Otherwise it could just run away while we backfill. */ if (approxlib(libs[0], other, 3, NULL) <= 2 && approxlib(libs[1], other, 3, NULL) <= 2) { if (approxlib(libs[0], color, 1, &lib) == 1 && approxlib(lib, color, 3, NULL) >= 3) ADD_CANDIDATE_MOVE(lib, 0, *moves, "break_chain2-C"); if (approxlib(libs[1], color, 1, &lib) == 1 && approxlib(lib, color, 3, NULL) >= 3) ADD_CANDIDATE_MOVE(lib, 0, *moves, "break_chain2-C"); } } } } /* * (str) points to a group to be defended. * break_chain2_defense_moves is a wrapper around break_chain2_moves. * It devalues all entries by 2. * * Rationale: Otherwise, these moves get overvalued by order_moves. In * particular, if there is both a direct and a break_chain2 defense, * then the latter one might be just an irrelevant intermediate forcing * move. Hence, we should rather return the direct defense. */ static void break_chain2_defense_moves(int str, struct reading_moves *moves, int be_aggressive) { int saved_num_moves = moves->num; int k; break_chain2_moves(str, moves, !(stackp <= backfill_depth), be_aggressive); for (k = saved_num_moves; k < moves->num; k++) moves->score[k] = -2; } /* Helper function for break_chain3_moves() and * superstring_break_chain_moves(). */ static void do_find_break_chain3_moves(int *chain_links, int num_chain_links, struct reading_moves *moves, int be_aggressive, const char *caller_function_name) { int other = board[chain_links[0]]; int color = OTHER_COLOR(other); signed char move_added[BOARDMAX]; int possible_moves[MAX_MOVES]; int num_possible_moves = 0; int r; int k; gg_assert(num_chain_links > 0); memset(move_added, 0, sizeof move_added); for (r = 0; r < num_chain_links; r++) { int lib1; int lib2; int lib3; int libs[3]; /* We make a list in the (adjs) array of the liberties * of boundary strings having exactly three liberties. We mark * each liberty in the mw array so that we do not list any * more than once. */ findlib(chain_links[r], 3, libs); /* If the 3 liberty chain easily can run away through one of the * liberties, we don't play on any of the other liberties. */ lib1 = approxlib(libs[0], other, 4, NULL); lib2 = approxlib(libs[1], other, 4, NULL); if (lib1 >= 4 && lib2 >= 4) continue; lib3 = approxlib(libs[2], other, 4, NULL); if ((lib1 >= 4 || lib2 >= 4) && lib3 >= 4) continue; if (lib1 >= 4) { if (!move_added[libs[0]]) { possible_moves[num_possible_moves++] = libs[0]; move_added[libs[0]] = 1; } continue; } if (lib2 >= 4) { if (!move_added[libs[1]]) { possible_moves[num_possible_moves++] = libs[1]; move_added[libs[1]] = 1; } continue; } if (lib3 >= 4) { if (!move_added[libs[2]]) { possible_moves[num_possible_moves++] = libs[2]; move_added[libs[2]] = 1; } continue; } /* No easy escape, try all liberties. */ for (k = 0; k < 3; k++) { if (!move_added[libs[k]]) { possible_moves[num_possible_moves++] = libs[k]; move_added[libs[k]] = 1; } } if (stackp <= backfill2_depth || (be_aggressive && stackp <= backfill_depth)) defend_secondary_chain1_moves(chain_links[r], moves, 3); } for (k = 0; k < num_possible_moves; k++) { /* We do not wish to consider the move if it can be immediately * recaptured, unless stackp < backfill2_depth. Beyond * backfill2_depth, the necessary capturing move might not get * generated in follow-up for the attacker. (This currently only * makes a difference at stackp == backfill2_depth.) */ int move = possible_moves[k]; if (stackp <= break_chain_depth || (be_aggressive && stackp <= backfill_depth) || approxlib(move, color, 2, NULL) > 1) /* We use a negative initial score here as we prefer to find * direct defense moves. */ ADD_CANDIDATE_MOVE(move, -2, *moves, caller_function_name); } } /* * (str) points to a group. * If there is a string in the surrounding chain having * exactly three liberties whose attack leads to the rescue of * (str), break_chain3_moves(str, *moves) adds attack moves against * the surrounding string as candidate moves. */ static void break_chain3_moves(int str, struct reading_moves *moves, int be_aggressive) { int chain_links[MAXCHAIN]; int num_chain_links = chainlinks2(str, chain_links, 3); if (num_chain_links > 0) { do_find_break_chain3_moves(chain_links, num_chain_links, moves, be_aggressive, "break_chain3"); } } /* * (str) points to a group. * If there is a string in the surrounding chain having * exactly four liberties whose attack leads to the rescue of * (str), break_chain4_moves(str, *moves) adds attack moves against * the surrounding string as candidate moves. */ static void break_chain4_moves(int str, struct reading_moves *moves, int be_aggressive) { int color = board[str]; int other = OTHER_COLOR(color); int r; int k; int u = 0, v; int apos; int adj; int adjs[MAXCHAIN]; int libs[4]; int possible_moves[MAX_MOVES]; int mw[BOARDMAX]; memset(mw, 0, sizeof(mw)); adj = chainlinks2(str, adjs, 4); for (r = 0; r < adj; r++) { int lib1 = 0, lib2 = 0, lib3 = 0, lib4 = 0; apos = adjs[r]; /* We make a list in the (adjs) array of the liberties * of boundary strings having exactly four liberties. We mark * each liberty in the mw array so that we do not list any * more than once. */ findlib(apos, 4, libs); /* If the 4 liberty chain easily can run away through one of the * liberties, we don't play on any of the other liberties. */ lib1 = approxlib(libs[0], other, 5, NULL); lib2 = approxlib(libs[1], other, 5, NULL); if (lib1 >= 5 && lib2 >= 5) continue; lib3 = approxlib(libs[2], other, 5, NULL); if ((lib1 >= 5 || lib2 >= 5) && lib3 >= 5) continue; lib4 = approxlib(libs[3], other, 5, NULL); if ((lib1 >= 5 || lib2 >= 5 || lib3 >= 5) && lib4 >= 5) continue; if (lib1 >= 5 && !mw[libs[0]]) { mw[libs[0]] = 1; possible_moves[u++] = libs[0]; continue; } if (lib2 >= 5 && !mw[libs[1]]) { mw[libs[1]] = 1; possible_moves[u++] = libs[1]; continue; } if (lib3 >= 5 && !mw[libs[2]]) { mw[libs[2]] = 1; possible_moves[u++] = libs[2]; continue; } if (lib4 >= 5 && !mw[libs[3]]) { mw[libs[3]] = 1; possible_moves[u++] = libs[3]; continue; } /* No easy escape, try all liberties. */ for (k = 0; k < 4; k++) { if (!mw[libs[k]]) { mw[libs[k]] = 1; possible_moves[u++] = libs[k]; } } if (stackp <= backfill2_depth || (be_aggressive && stackp <= backfill_depth)) defend_secondary_chain1_moves(adjs[r], moves, 4); } for (v = 0; v < u; v++) { /* We do not wish to consider the move if it can be * immediately recaptured, unless stackp < backfill2_depth. * Beyond backfill2_depth, the necessary capturing move might not * get generated in follow-up for the attacker. * (This currently only makes a difference at stackp == backfill2_depth.) */ int xpos = possible_moves[v]; if (stackp <= break_chain_depth || (be_aggressive && stackp <= backfill_depth) || approxlib(xpos, color, 2, NULL) > 1) /* We use a negative initial score here as we prefer to find * direct defense moves. */ ADD_CANDIDATE_MOVE(xpos, -2, *moves, "break_chain4"); } } /* This function looks for moves attacking those components * of the surrounding chain of the superstring (see find_superstring * for the definition) which have fewer than liberty_cap liberties, * and which are not adjacent to the string itself, since those * are tested by break_chain_moves. */ static void superstring_break_chain_moves(int str, int liberty_cap, struct reading_moves *moves) { int adj; int adjs[MAXCHAIN]; int chain_links3[MAXCHAIN]; int num_chain_links3 = 0; int k; int apos; proper_superstring_chainlinks(str, &adj, adjs, liberty_cap); for (k = 0; k < adj; k++) { int liberties = countlib(adjs[k]); if (liberties == 1) { findlib(adjs[k], 1, &apos); ADD_CANDIDATE_MOVE(apos, 0, *moves, "superstring_break_chain"); } else if (liberties == 2) do_find_break_chain2_efficient_moves(str, adjs[k], moves); else if (liberties == 3) chain_links3[num_chain_links3++] = adjs[k]; } if (num_chain_links3 > 0) { do_find_break_chain3_moves(chain_links3, num_chain_links3, moves, 0, "superstring_break_chain-3"); } } /* * If `str' points to a group, double_atari_chain2_moves() adds all * moves which make a double atari on some strings in the surrounding * chain to the moves[] array. In addition, if `generate_more_moves' * is set, it adds moves that make atari on a string in the * surrounding chain and are adjacent to another string with 3 * liberties. */ static void double_atari_chain2_moves(int str, struct reading_moves *moves, int generate_more_moves) { int r, k; int adj; int adjs[MAXCHAIN]; int libs[3]; int mw[BOARDMAX]; memset(mw, 0, sizeof(mw)); adj = chainlinks2(str, adjs, 2); for (r = 0; r < adj; r++) { findlib(adjs[r], 2, libs); for (k = 0; k < 2; k++) { mw[libs[k]]++; if (mw[libs[k]] == 2) { /* Found a double atari, but don't play there unless the move * is safe for the defender. */ if (!is_self_atari(libs[k], board[str])) ADD_CANDIDATE_MOVE(libs[k], 1, *moves, "double_atari_chain2-A"); } } } if (generate_more_moves) { int adj3; int adjs3[MAXCHAIN]; adj3 = chainlinks2(str, adjs3, 3); for (r = 0; r < adj3; r++) { findlib(adjs3[r], 3, libs); for (k = 0; k < 3; k++) { if (mw[libs[k]] == 1) { mw[libs[k]] = 2; if (!is_self_atari(libs[k], board[str])) ADD_CANDIDATE_MOVE(libs[k], -3, *moves, "double_atari_chain2-B"); } } } } } /* ================================================================ */ /* Restricted Attack and Defense */ /* ================================================================ */ /* These functions try to attack and defend a string, avoiding moves * from a certain set. It is assumed that as soon as the string gets * three liberties, it is alive. * * These functions can be used to generate backfilling moves as * follows: Suppose that we would like to make atari on a * string, but the atari is not safe until we make a backfilling * move. To find the backfilling move, we make a list of the * liberties of the string under attack, declaring these moves * forbidden. Neither player will play them while the restricted * functions are in effect. We fill the liberty, creating a * string which is under attack, and look for a defensive move * which avoids the forbidden moves. This is the backfilling * move. * * These are minimalist functions capable of reading a ladder * and not much more. */ /* Given a list of moves, restricted_defend1 tries to find a * move that defends the string (str) with one liberty, * not considering moves from the list. */ int restricted_defend1(int str, int *move, int num_forbidden_moves, int *forbidden_moves) { int color = board[str]; int other = OTHER_COLOR(color); int xpos; int lib; struct reading_moves moves; int savemove = 0; int savecode = 0; int liberties; int k; SETUP_TRACE_INFO("restricted_defend1", str); reading_node_counter++; moves.num = 0; ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 1, str); /* (lib) will be the liberty of the string. */ liberties = findlib(str, 1, &lib); ASSERT1(liberties == 1, str); /* Collect moves to try in the first batch. * 1. First order liberty. * 2. Chain breaking moves. * 3. Moves to set up a snapback. */ moves.pos[0] = lib; moves.score[0] = 0; moves.message[0] = "liberty"; moves.num = 1; moves.num_tried = 0; break_chain_moves(str, &moves); set_up_snapback_moves(str, lib, &moves); order_moves(str, &moves, color, read_function_name, NO_MOVE); for (k = 0; k < moves.num; k++) { int ko_capture; xpos = moves.pos[k]; if (in_list(xpos, num_forbidden_moves, forbidden_moves)) continue; /* To avoid loops with double ko, we do not allow any ko captures, * even legal ones, if the opponent is komaster. */ if (is_ko(xpos, color, NULL)) ko_capture = 1; else ko_capture = 0; if ((get_komaster() != other || !ko_capture) && trymove(xpos, color, moves.message[k], str)) { int libs = countlib(str); if (libs > 2) { popgo(); SGFTRACE(xpos, WIN, "defense effective"); if (move) *move = xpos; return WIN; } if (libs == 2) { int acode; if (!ko_capture) acode = restricted_attack2(str, NULL, num_forbidden_moves, forbidden_moves); else acode = restricted_attack2(str, NULL, num_forbidden_moves, forbidden_moves); popgo(); if (acode == 0) { SGFTRACE(xpos, WIN, "defense effective"); if (move) *move = xpos; return WIN; } /* if the move works with ko we save it, then look for something * better. */ UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, xpos); } else popgo(); } else { int ko_pos; if (stackp <= ko_depth && savecode == 0 && (get_komaster() == EMPTY || (get_komaster() == color && get_kom_pos() == xpos)) && is_ko(xpos, color, &ko_pos) && tryko(xpos, color, "restricted_defend1-B")) { int libs = countlib(str); if (libs > 2) { popgo(); UPDATE_SAVED_KO_RESULT(savecode, savemove, 2, xpos); } else if (libs == 2) { int acode; acode = restricted_attack2(str, NULL, num_forbidden_moves, forbidden_moves); popgo(); UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, xpos); } else popgo(); } } } if (savecode != 0) { if (move) *move = savemove; SGFTRACE(savemove, savecode, "saved move"); return savecode; } SGFTRACE(0, 0, NULL); return 0; } /* Given a list of moves, restricted_attack2 tries to find a * move that attacks the string (str) with two liberties, * not considering moves from the list. */ int restricted_attack2(int str, int *move, int num_forbidden_moves, int *forbidden_moves) { int color = board[str]; int other = OTHER_COLOR(color); int apos; int liberties; int libs[2]; int savemove = 0; int savecode = 0; int k; SETUP_TRACE_INFO("restricted_attack2", str); reading_node_counter++; str = find_origin(str); ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 2, str); /* The attack may fail if a boundary string is in atari and cannot * be defended. First we must try defending such a string. */ /* Get the two liberties of (str). */ liberties = findlib(str, 2, libs); ASSERT1(liberties == 2, str); for (k = 0; k < 2; k++) { int ko_pos; int ko_capture; apos = libs[k]; if (in_list(apos, num_forbidden_moves, forbidden_moves)) continue; /* To avoid loops with double ko, we do not allow any ko captures, * even legal ones, if the opponent is komaster. */ if (is_ko(apos, other, &ko_pos)) ko_capture = 1; else ko_capture = 0; if ((get_komaster() != color || !ko_capture) && trymove(apos, other, "restricted_attack2", str)) { if ((!ko_capture && !restricted_defend1(str, NULL, num_forbidden_moves, forbidden_moves)) || (ko_capture && !restricted_defend1(str, NULL, num_forbidden_moves, forbidden_moves))) { popgo(); SGFTRACE(apos, WIN, "attack effective"); if (move) *move = apos; return WIN; } popgo(); } else if (savecode == 0 && (get_komaster() == EMPTY || (get_komaster() == other && get_kom_pos() == apos)) && tryko(apos, other, "restricted_attack2")) { if (!restricted_defend1(str, NULL, num_forbidden_moves, forbidden_moves)) { popgo(); savecode = KO_B; savemove = apos; } else popgo(); } } if (savecode != 0) { if (move) *move = savemove; SGFTRACE(savemove, savecode, "saved move"); return savecode; } SGFTRACE(0, 0, NULL); return 0; } /* * Returns true if the move is in a given list of moves. */ static int in_list(int move, int num_moves, int *moves) { int k; for (k = 0; k < num_moves; k++) if (moves[k] == move) return 1; return 0; } /* ================================================================ */ /* Move ordering */ /* ================================================================ */ /* Parameters used in the ordering of moves to try in the tactical * reading. */ /* 0 1 2 3 4 >4 */ static int defend_lib_score[6] = {-5, -4, 0, 3, 5, 50}; static int defend_not_adjacent_lib_score[5] = { 0, 0, 2, 3, 5}; static int defend_capture_score[6] = { 0, 6, 9, 13, 18, 24}; static int defend_atari_score[6] = { 0, 2, 4, 6, 7, 8}; static int defend_save_score[6] = { 0, 3, 6, 8, 10, 12}; static int defend_open_score[5] = { 0, 1, 2, 3, 4}; static int attack_own_lib_score[5] = {10, -4, 2, 3, 4}; static int attack_string_lib_score[6] = {-5, 2, 3, 7, 10, 19}; static int attack_capture_score[6] = {-4, 4, 10, 15, 20, 25}; static int attack_save_score[6] = { 0, 10, 13, 18, 21, 24}; static int attack_open_score[5] = { 0, 0, 2, 4, 4}; static int defend_not_edge_score = 5; static int attack_not_edge_score = 1; static int attack_ko_score = -15; static int cannot_defend_penalty = -20; static int safe_atari_score = 8; static void sgf_dumpmoves(struct reading_moves *moves, const char *funcname) { char buf[500]; char *pos; int i, chars; sprintf(buf, "Move order for %s: %n", funcname, &chars); pos = buf + chars; for (i = moves->num_tried; i < moves->num; i++) { sprintf(pos, "%c%d (%d) %n", J(moves->pos[i]) + 'A' + (J(moves->pos[i]) >= 8), board_size - I(moves->pos[i]), moves->score[i], &chars); pos += chars; } sgftreeAddComment(sgf_dumptree, buf); } /* The string at (str) is under attack. The moves.num moves in * (moves) for color have been deemed interesting in * the attack or defense of the group. Most of these moves will be * immediate liberties of the group. * * This function orders the moves in the order where the move most * likely to succeed to attack or defend the string will be first and * so on. * * Currently, this is defined as: * 1) Moves which let the defending string get more liberties are more * interesting. * 2) Moves adjacent to the most open liberties are more * interesting than those with fewer open liberties. * 3) Moves on the edge are less interesting. * * Moves below first_move are ignored and assumed to be sorted already. */ static void order_moves(int str, struct reading_moves *moves, int color, const char *funcname, int killer) { int string_color = board[str]; int string_libs = countlib(str); int r; int i, j; /* Don't bother sorting if only one move, or none at all. */ if (moves->num - moves->num_tried < 2) { /* But let's still have a single candidate in the sgf output */ if (sgf_dumptree && moves->num > moves->num_tried) sgf_dumpmoves(moves, funcname); return; } /* Assign a score to each move. */ for (r = moves->num_tried; r < moves->num; r++) { int move = moves->pos[r]; /* Look at the neighbors of this move and count the things we * find. Friendly and opponent stones are related to color, i.e. * the player to move, not to the color of the string. */ int number_edges = 0; /* outside board */ int number_same_string = 0; /* the string being attacked */ int number_own = 0; /* friendly stone */ int number_opponent = 0; /* opponent stone */ int captured_stones = 0; /* number of stones captured by this move */ int threatened_stones = 0; /* number of stones threatened by this move */ int saved_stones = 0; /* number of stones in atari saved */ int number_open = 0; /* empty intersection */ /* We let the incremental_board code do the heavy work. */ incremental_order_moves(move, color, str, &number_edges, &number_same_string, &number_own, &number_opponent, &captured_stones, &threatened_stones, &saved_stones, &number_open); if (0) gprintf("%o %1m values: %d %d %d %d %d %d %d %d\n", move, number_edges, number_same_string, number_own, number_opponent, captured_stones, threatened_stones, saved_stones, number_open); /* Different score strategies depending on whether the move is * attacking or defending the string. */ if (color == string_color) { /* Defense move. * * 1) Add twice the number of liberties the group receives by * extending to the intersection of the move, if more than one. * Only applicable if the move is adjacent to the group. */ int libs = approxlib(move, color, 10, NULL); if (number_same_string > 0) { if (libs > 5 || (libs == 4 && stackp > fourlib_depth)) moves->score[r] += defend_lib_score[5] + (libs - 4); else moves->score[r] += defend_lib_score[libs]; } else { /* Add points for the number of liberties the played stone * obtains when not adjacent to the attacked string. */ if (libs > 4) moves->score[r] += defend_not_adjacent_lib_score[4]; else moves->score[r] += defend_not_adjacent_lib_score[libs]; } /* 2) Add the number of open liberties near the move to its score. */ gg_assert(number_open <= 4); moves->score[r] += defend_open_score[number_open]; /* 3) Add a bonus if the move is not on the edge. */ if (number_edges == 0 || captured_stones > 0) moves->score[r] += defend_not_edge_score; /* 4) Add thrice the number of captured stones. */ if (captured_stones <= 5) moves->score[r] += defend_capture_score[captured_stones]; else moves->score[r] += defend_capture_score[5] + captured_stones; /* 5) Add points for stones put into atari, unless this is a * self atari. */ if (libs + captured_stones > 1) { if (threatened_stones <= 5) moves->score[r] += defend_atari_score[threatened_stones]; else moves->score[r] += defend_atari_score[5] + threatened_stones; } /* 6) Add a bonus for saved stones. */ if (saved_stones <= 5) moves->score[r] += defend_save_score[saved_stones]; else moves->score[r] += defend_save_score[5]; } else { /* Attack move. * * 1) Add the number of liberties the attacker gets when playing * there, but never more than four. */ int libs = approxlib(move, color, 4, NULL); if (libs > 4) libs = 4; moves->score[r] += attack_own_lib_score[libs]; if (libs == 0 && captured_stones == 1) moves->score[r] += attack_ko_score; /* 2) If the move is not a self atari and adjacent to the * string, add the number of liberties the opponent would * gain by playing there. If the string has two liberties, * self-ataris are also ok since they may be snapbacks, but * only if a single stone is sacrificed. */ if ((libs + captured_stones > 1 || (string_libs <= 2 && number_own == 0)) && number_same_string > 0) { int safe_atari; int liberties = approxlib(move, string_color, 5, NULL); if (liberties > 5 || (liberties == 4 && stackp > fourlib_depth)) liberties = 5; moves->score[r] += attack_string_lib_score[liberties]; safe_atari = (string_libs <= 2 && libs + captured_stones > 1); /* The defender can't play here without getting into atari, so * we probably souldn't either. */ if (liberties == 1 && saved_stones == 0 && !safe_atari) moves->score[r] += cannot_defend_penalty; /* Bonus if we put the attacked string into atari without * ourselves getting into atari. */ if (safe_atari) moves->score[r] += safe_atari_score; } /* 3) Add the number of open liberties near the move to its score. */ gg_assert(number_open <= 4); moves->score[r] += attack_open_score[number_open]; /* 4) Add a bonus if the move is not on the edge. */ if (number_edges == 0) moves->score[r] += attack_not_edge_score; /* 5) Add twice the number of captured stones. */ if (captured_stones <= 5) moves->score[r] += attack_capture_score[captured_stones]; else moves->score[r] += attack_capture_score[5]; /* 6) Add a bonus for saved stones. */ if (saved_stones <= 5) moves->score[r] += attack_save_score[saved_stones]; else moves->score[r] += attack_save_score[5]; } if (moves->pos[r] == killer) moves->score[r] += 50; } /* Now sort the moves. We use selection sort since this array will * probably never be more than 10 moves long. In this case, the * overhead imposed by quicksort will probably overshadow the gains * given by the O(n*log(n)) behaviour over the O(n^2) behaviour of * selection sort. */ for (i = moves->num_tried; i < moves->num-1; i++) { int maxscore = moves->score[i]; int max_at = 0; /* This is slightly faster than max_at = i. */ /* Find the move with the biggest score. */ for (j = i + 1; j < moves->num; j++) { if (moves->score[j] > maxscore) { maxscore = moves->score[j]; max_at = j; } } /* Now exchange the move at i with the move at max_at. * Don't forget to exchange the scores as well. */ if (max_at != 0) { int temp = moves->pos[max_at]; const char *temp_message = moves->message[max_at]; moves->pos[max_at] = moves->pos[i]; moves->score[max_at] = moves->score[i]; moves->message[max_at] = moves->message[i]; moves->pos[i] = temp; moves->score[i] = maxscore; moves->message[i] = temp_message; } } if (0) { gprintf("%oVariation %d:\n", count_variations); for (i = moves->num_tried; i < moves->num; i++) gprintf("%o %1M %d\n", moves->pos[i], moves->score[i]); } if (sgf_dumptree) sgf_dumpmoves(moves, funcname); } /* Set new values for the move ordering parameters. */ void tune_move_ordering(int params[MOVE_ORDERING_PARAMETERS]) { int k; for (k = 0; k < 6; k++) { defend_lib_score[k] = params[k]; if (k < 5) defend_not_adjacent_lib_score[k] = params[k + 6]; defend_capture_score[k] = params[k + 11]; defend_atari_score[k] = params[k + 17]; defend_save_score[k] = params[k + 23]; if (k < 5) { defend_open_score[k] = params[k + 29]; attack_own_lib_score[k] = params[k + 34]; } attack_string_lib_score[k] = params[k + 39]; attack_capture_score[k] = params[k + 45]; attack_save_score[k] = params[k + 51]; if (k < 5) attack_open_score[k] = params[k + 57]; } defend_not_edge_score = params[62]; attack_not_edge_score = params[63]; attack_ko_score = params[64]; cannot_defend_penalty = params[65]; safe_atari_score = params[66]; if (verbose) { gprintf("static int defend_lib_score[6] = {%d, %d, %d, %d, %d, %d};\n", defend_lib_score[0], defend_lib_score[1], defend_lib_score[2], defend_lib_score[3], defend_lib_score[4], defend_lib_score[5]); gprintf("static int defend_not_adjacent_lib_score[5] = {%d, %d, %d, %d, %d};\n", defend_not_adjacent_lib_score[0], defend_not_adjacent_lib_score[1], defend_not_adjacent_lib_score[2], defend_not_adjacent_lib_score[3], defend_not_adjacent_lib_score[4]); gprintf("static int defend_capture_score[6] = {%d, %d, %d, %d, %d, %d};\n", defend_capture_score[0], defend_capture_score[1], defend_capture_score[2], defend_capture_score[3], defend_capture_score[4], defend_capture_score[5]); gprintf("static int defend_atari_score[6] = {%d, %d, %d, %d, %d, %d};\n", defend_atari_score[0], defend_atari_score[1], defend_atari_score[2], defend_atari_score[3], defend_atari_score[4], defend_atari_score[5]); gprintf("static int defend_save_score[6] = {%d, %d, %d, %d, %d, %d};\n", defend_save_score[0], defend_save_score[1], defend_save_score[2], defend_save_score[3], defend_save_score[4], defend_save_score[5]); gprintf("static int defend_open_score[5] = {%d, %d, %d, %d, %d};\n", defend_open_score[0], defend_open_score[1], defend_open_score[2], defend_open_score[3], defend_open_score[4]); gprintf("static int attack_own_lib_score[5] = {%d, %d, %d, %d, %d};\n", attack_own_lib_score[0], attack_own_lib_score[1], attack_own_lib_score[2], attack_own_lib_score[3], attack_own_lib_score[4]); gprintf("static int attack_string_lib_score[6] = {%d, %d, %d, %d, %d, %d};\n", attack_string_lib_score[0], attack_string_lib_score[1], attack_string_lib_score[2], attack_string_lib_score[3], attack_string_lib_score[4], attack_string_lib_score[5]); gprintf("static int attack_capture_score[6] = {%d, %d, %d, %d, %d, %d};\n", attack_capture_score[0], attack_capture_score[1], attack_capture_score[2], attack_capture_score[3], attack_capture_score[4], attack_capture_score[5]); gprintf("static int attack_save_score[6] = {%d, %d, %d, %d, %d, %d};\n", attack_save_score[0], attack_save_score[1], attack_save_score[2], attack_save_score[3], attack_save_score[4], attack_save_score[5]); gprintf("static int attack_open_score[5] = {%d, %d, %d, %d, %d};\n", attack_open_score[0], attack_open_score[1], attack_open_score[2], attack_open_score[3], attack_open_score[4]); gprintf("static int defend_not_edge_score = %d;\n", defend_not_edge_score); gprintf("static int attack_not_edge_score = %d;\n", attack_not_edge_score); gprintf("static int attack_ko_score = %d;\n", attack_ko_score); gprintf("static int cannot_defend_penalty = %d;\n", cannot_defend_penalty); gprintf("static int safe_atari_score = %d;\n", safe_atari_score); } } /* ================================================================ */ /* Reading utilities */ /* ================================================================ */ static int safe_move_cache[BOARDMAX][2]; static int safe_move_cache_when[BOARDMAX][2]; static void clear_safe_move_cache(void); static void clear_safe_move_cache(void) { int k; for (k = BOARDMIN; k < BOARDMAX; k++) { safe_move_cache_when[k][0] = -1; safe_move_cache_when[k][1] = -1; } } /* safe_move(move, color) checks whether a move at (move) is illegal * or can immediately be captured. If stackp==0 the result is cached. * If the move only can be captured by a ko, it's considered safe. * This may or may not be a good convention. * * For performance reasons, the result of this function is cached. */ int safe_move(int move, int color) { int safe = 0; static int initialized = 0; int ko_move; if (!initialized) { clear_safe_move_cache(); initialized = 1; } /* If we have this position cached, use the previous value. * Only use cached values when stackp is 0 and reading is not being done * at a modified depth. */ if (stackp == 0 && depth_offset == 0 && safe_move_cache_when[move][color == BLACK] == position_number) return safe_move_cache[move][color == BLACK]; /* Otherwise calculate the value... */ if (komaster_trymove(move, color, "safe_move", 0, &ko_move, 1)) { safe = REVERSE_RESULT(attack(move, NULL)); if (ko_move && safe != 0) safe = KO_B; popgo(); } /* ...and store it in the cache. * FIXME: Only store result in cache when we're working at * full depth. * * Comment: This is currently not a problem since no reduced depth * reading is performed. */ if (stackp == 0 && depth_offset == 0) { if (0) gprintf("Safe move at %1m for %s cached when depth=%d, position number=%d\n", move, color_to_string(color), depth, position_number); safe_move_cache_when[move][color == BLACK] = position_number; safe_move_cache[move][color == BLACK] = safe; } return safe; } /* Checks if a move by color makes an opponent move at pos a self atari. */ int does_secure(int color, int move, int pos) { int result = 0; if (trymove(move, color, NULL, NO_MOVE)) { if (is_self_atari(pos, OTHER_COLOR(color))) result = 1; popgo(); } return result; } /* ===================== Statistics ============================= */ /* Clear statistics. */ void reset_reading_node_counter() { reading_node_counter = 0; } /* Retrieve statistics. */ int get_reading_node_counter() { return reading_node_counter; } /* ============ Reading shadow =============== */ /* Draw the reading shadow, for debugging purposes */ void draw_reading_shadow() { int i, j; int c = ' '; int pos; start_draw_board(); for (i = 0; i < board_size; i++) { fprintf(stderr, "\n%2d", board_size - i); for (j = 0; j < board_size; j++) { pos = POS(i, j); if (!shadow[pos] && board[pos] == EMPTY) c = '.'; else if (!shadow[pos] && board[pos] == WHITE) c = 'O'; else if (!shadow[pos] && board[pos] == BLACK) c = 'X'; if (shadow[pos] && board[pos] == EMPTY) c = ','; else if (shadow[pos] && board[pos] == WHITE) c = 'o'; else if (shadow[pos] && board[pos] == BLACK) c = 'x'; fprintf(stderr, " %c", c); } fprintf(stderr, " %d", board_size - i); } end_draw_board(); } /* ================================================================ */ /* Code for special purposes. */ /* ================================================================ */ /* simple_ladder(str, &move) tries to capture a string (str) * with exactly two liberties under simplified assumptions, which are * adequate in a ladder. The rules are as follows: * * 1. The attacker is allowed to play at each of the two liberties, * but no other move. If the move was legal, the string now has * exactly one liberty. * 2. The defender must move out of atari. This can only be done by * either extending at the liberty or capturing a neighboring * string which was in atari. All such moves may be tested. * 3. Depending on the resulting number of liberties of the string * after the defender's move, we value each node as follows: * * 3 or more liberties: the attack has failed * 2 liberties: recurse * 1 liberty: the attack has succeeded * * illegal move for the defender: successful attack * illegal move for the attacker: failed attack * * Return codes are as usual 0 for failure, WIN for success, KO_A for * a ko where the defender must make the first ko threat and KO_B for * a ko where the attacked has to make the first threat. If the attack * was successful, (*move) contains the attacking move, unless it is a * null pointer. * * The differences compared to the attack2()/defend1() combination for * reading ladders is that this one is a strict ladder reader which * never allows the defender to have more than one liberty when it's * in turn to move. This has a number of consequences. * * 1. This function will miss tactical captures involving other * techniques than the ladder. * * 2. This function is faster because it gives up faster when the * ladder doesn't work. In particular it can't branch out in a huge * tree of exotic variations. * * 3. This function always reads ladders to the very end. There are no * depth limits or other assumptions to stop reading prematurely. * * 4. If this function returns WIN, it is guaranteed that the defender * has no way whatsoever to escape, all possibilities are tried. * The converse is definitely not true. */ int simple_ladder(int str, int *move) { int color = board[str]; int other = OTHER_COLOR(color); int apos; int libs[2]; int savemove = 0; int savecode = 0; int dcode; int k; struct reading_moves moves; SETUP_TRACE_INFO("simple_ladder", str); reading_node_counter++; moves.num = 0; moves.num_tried = 0; str = find_origin(str); ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 2, str); /* Give up if we attacked depending on ko for too long. */ if (stackp > depth + 20 && get_komaster() == OTHER_COLOR(board[str])) { SGFTRACE(0, 0, NULL); if (move) *move = PASS_MOVE; return 0; } /* Get the two liberties of (str). */ findlib(str, 2, libs); /* If the defender can get enough liberties by playing one of these * two, then we have no choice but to block there and consequently, * it is unnecesary to try the other liberty. */ if (approxlib(libs[0], color, 4, NULL) <= 3) ADD_CANDIDATE_MOVE(libs[1], 0, moves, "simple_ladder"); if (approxlib(libs[1], color, 4, NULL) <= 3) ADD_CANDIDATE_MOVE(libs[0], 0, moves, "simple_ladder"); order_moves(str, &moves, other, read_function_name, NO_MOVE); for (k = 0; k < moves.num; k++) { int ko_move; apos = moves.pos[k]; if (komaster_trymove(apos, other, moves.message[k], str, &ko_move, savecode == 0)) { if (!ko_move) { dcode = simple_ladder_defend(str, NULL); if (dcode != WIN) { if (dcode == 0) { popgo(); SGFTRACE(apos, WIN, "attack effective"); if (move) *move = apos; return WIN; } UPDATE_SAVED_KO_RESULT(savecode, savemove, dcode, apos); } } else { if (simple_ladder_defend(str, NULL) != WIN) { savemove = apos; savecode = KO_B; } } popgo(); } } RETURN_RESULT(savecode, savemove, move, "saved move"); } static int simple_ladder_defend(int str, int *move) { int color = board[str]; int xpos; int lib; struct reading_moves moves; int savemove = 0; int savecode = 0; int k; SETUP_TRACE_INFO("simple_ladder_defend", str); reading_node_counter++; ASSERT1(IS_STONE(board[str]), str); ASSERT1(countlib(str) == 1, str); /* lib will be the liberty of the string. */ findlib(str, 1, &lib); moves.pos[0] = lib; moves.score[0] = 0; moves.message[0] = "liberty"; moves.num = 1; moves.num_tried = 0; break_chain_moves(str, &moves); order_moves(str, &moves, color, read_function_name, NO_MOVE); for (k = 0; k < moves.num; k++) { int ko_move; xpos = moves.pos[k]; if (komaster_trymove(xpos, color, moves.message[k], str, &ko_move, savecode == 0)) { int acode; int new_libs = countlib(str); if (new_libs > 2) acode = 0; else if (new_libs < 2) acode = WIN; else acode = simple_ladder(str, NULL); popgo(); if (!ko_move) CHECK_RESULT(savecode, savemove, acode, xpos, move, "defense effective"); else { if (acode != WIN) { savemove = xpos; savecode = KO_B; } } } } RETURN_RESULT(savecode, savemove, move, "saved move"); } /* * Local Variables: * tab-width: 8 * c-basic-offset: 2 * End: */