This is a rediff of pogonyshev_3_10.7a. ------- Paul's comments to pogonyshev_3_10.7a ------- i checked the speed. rewritten function is 4-8 times faster (depending on position) than the old one, which seems to be good enough ;) i also ran the complete regression suit with an assertion set, checking that new function always gives correct result. after one bugfix it started doing so ;) changes: - exactlib() is renamed to accuratelib() as Marco Scheurer proposed. gtp command is renamed to accuratelib as well. - accuratelib() is bugfixed and now seems to always tell the truth. - incremental_sloppy_self_atari() is finally integrated into is_self_atari(). code maintenance changes: - new 'neighbor' variable in extended_chainlinks() replaced (libs[r] + delta[k]) expression which was repeated 4 times there. - use variables for pos + delta[k] instead of for delta[k] alone in slow_approxlib(). regression delta is zero. Paul ------- Paul's comments to pogonyshev_3_10.7 ------- the main intent of this patch is to make accurate_approxlib() much faster. it is not widely used now, so it won't have noticable impact on performance. but it allows potentially wider use of the function (instead of approxlib() which ignores captures). this will hopefully give a couple of PASSes one day. here is the complete list of changes: - accurate_approxlib() is rewritten, moved to board.c and renamed to exactlib() (since it is complete and in contrast to approxlib()). it now never actually plays the move and only uses incremental board data. must be much faster. - is_self_atari() uses exactlib() instead of playing the move if incremental_sloppy_self_atari() don't know the answer. - change in the way fastlib() handles captures (generalized). it's #if'ed for now, for there must have been some reason for those conditions. i can't see it, but if anyone can, it will be easy to return to the old version. - updated comments before fastlib() (not related with the previous item). - two new macros in board.c: UNMARKED_COLOR_STRING and MARKED_COLOR_STRING. can potentially replace all instances of UNMARKED_OWN_STRING and UNMARKED_OPPONENT_STRING, but i replaced them only in obvious cases (so far). - minor changes in approxlib() which don't affect its behaviour. regression delta is zero, as it should be. practically, there might remain some small bug in exactlib(), since i've had lots of them in first versions. if anyone is willing to dig through the code and chcek it, that will be great ;) per Evan's profile, this patch should somewhat reduce the number of calls to do_trymove(). about 1% must be gone due to the fact that exactlib() (formerly accurate_approxlib()) doesn't call do_trymove() anymore. further, since is_self_atari() is called about as frequent as do_trymove(), number of calls to do_trymove() must be reduced by amount of incremental_sloppy_self_atari() fails. now it mustn't be too expensive to replace a call to approxlib() with a call to exactlib(). so, if such a replacement gives clear profit, it must be at least tried. i haven't looked for examples yet (but hope there are some ;). Paul Index: engine/board.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/board.c,v retrieving revision 1.53 diff -C7 -u -r1.53 board.c --- engine/board.c 14 Oct 2002 17:02:57 -0000 1.53 +++ engine/board.c 20 Oct 2002 14:35:47 -0000 @@ -198,14 +198,25 @@ (board[pos] == OTHER_COLOR(string[s].color)\ && string[string_number[pos]].mark != string_mark) #define UNMARKED_OWN_STRING(s, pos)\ (board[pos] == string[s].color\ && string[string_number[pos]].mark != string_mark) +/* Note that these two macros are not complementary. Both return + * false if board[pos] != color. + */ +#define UNMARKED_COLOR_STRING(pos, color)\ + (board[pos] == color\ + && string[string_number[pos]].mark != string_mark) + +#define MARKED_COLOR_STRING(pos, color)\ + (board[pos] == color\ + && string[string_number[pos]].mark == string_mark) + #define MARK_STRING(pos) string[string_number[pos]].mark = string_mark #define STRING_AT_VERTEX(pos, s)\ (IS_STONE(board[pos]) && string_number[pos] == (s)) #define LIBERTIES(pos)\ string[string_number[pos]].liberties @@ -271,15 +282,14 @@ static void new_position(void); static void init_board(void); static int propagate_string(int stone, int str); static void find_liberties_and_neighbors(int s); static int do_remove_string(int s); static void do_play_move(int pos, int color); static int slow_approxlib(int pos, int color, int maxlib, int *libs); -static int incremental_sloppy_self_atari(int pos, int color); /* Statistics. */ static int trymove_counter = 0; /* Coordinates for the eight directions, ordered * south, west, north, east, southwest, northwest, northeast, southeast. @@ -2005,24 +2015,25 @@ } return liberties; } /* Count the liberties a stone of the given color would get if played * at (pos). Captures are ignored based on the ignore_capture flag. - * (pos) must be empty. It will fail if there is more than one + * (pos) must be empty. It will fail if there is more than two * string neighbor of the same color. In this case, the return value - * is -1. Captures are not handled, so if ignore_capture is 0, and - * a capture is required, -1 is returned. + * is -1. Captures are handled in a very limited way, so if + * ignore_capture is 0, and a capture is required, it will often + * return -1. * * The intent of this function is to be as fast as possible, not - * necessarily complete. + * necessarily complete. But if it returns a positive value (meaning + * it has succeeded), the value is guaranteed to be correct. * - * Note well, that it relies on incremental data (?), and the number - * of liberties (if over MAX_LIBERTIES) may be inaccurate (?). + * Note well, that it relies on incremental data. */ int fastlib(int pos, int color, int ignore_capture) { int k; int ally1 = NO_MOVE; @@ -2051,26 +2062,32 @@ ally2 = neighbor; } } else ally1 = neighbor; } } + for (k = 0; k < 4; k++) { int neighbor = pos + delta[k]; int neighbor_color = board[neighbor]; if (!ignore_capture && neighbor_color == other && LIBERTIES(neighbor) == 1) { int neighbor_size = COUNTSTONES(neighbor); +#if 0 if ((neighbor_size <= 2 && !ally1) - || (neighbor_size == 1 + || (neighbor_size == 1 && ally1 && !ally2 && COUNTSTONES(ally1) == 1)) { +#else + if (neighbor_size == 1 + || (neighbor_size == 2 && !ally1)) { +#endif /* Here, we can gain only the adjacent new liberty. */ fast_liberties++; } else return -1; } if (neighbor_color == EMPTY) { @@ -2081,14 +2098,15 @@ } } if (ally1) fast_liberties += LIBERTIES(ally1) - 1; if (ally2) fast_liberties += LIBERTIES(ally2) - count_common_libs(ally1, ally2); + return fast_liberties; } /* Find the liberties a stone of the given color would get if played * at (pos), ignoring possible captures of opponent stones. (pos) * must be empty. If libs != NULL, the locations of up to maxlib * liberties are written into libs[]. The counting of @@ -2101,24 +2119,22 @@ */ int approxlib(int pos, int color, int maxlib, int *libs) { int k; int liberties = 0; - int fast_liberties = 0; ASSERT1(board[pos] == EMPTY, pos); ASSERT1(IS_STONE(color), pos); if (!libs) { - fast_liberties = fastlib(pos, color, 1); - if (fast_liberties >= 0) { + int fast_liberties = fastlib(pos, color, 1); + if (fast_liberties >= 0) return fast_liberties; - } } if (!strings_initialized) init_board(); /* Look for empty neighbors and the liberties of the adjacent * strings of the given color. The algorithm below won't work @@ -2132,15 +2148,15 @@ /* Start by marking pos itself so it isn't counted among its own * liberties. */ liberty_mark++; MARK_LIBERTY(pos); if (UNMARKED_LIBERTY(SOUTH(pos))) { - if (libs != NULL && liberties < maxlib) + if (libs != NULL) libs[liberties] = SOUTH(pos); liberties++; /* Stop counting if we reach maxlib. */ if (liberties >= maxlib) return liberties; MARK_LIBERTY(SOUTH(pos)); } @@ -2156,15 +2172,15 @@ return liberties; MARK_LIBERTY(lib); } } } if (UNMARKED_LIBERTY(WEST(pos))) { - if (libs != NULL && liberties < maxlib) + if (libs != NULL) libs[liberties] = WEST(pos); liberties++; /* Stop counting if we reach maxlib. */ if (liberties >= maxlib) return liberties; MARK_LIBERTY(WEST(pos)); } @@ -2180,15 +2196,15 @@ return liberties; MARK_LIBERTY(lib); } } } if (UNMARKED_LIBERTY(NORTH(pos))) { - if (libs != NULL && liberties < maxlib) + if (libs != NULL) libs[liberties] = NORTH(pos); liberties++; /* Stop counting if we reach maxlib. */ if (liberties >= maxlib) return liberties; MARK_LIBERTY(NORTH(pos)); } @@ -2234,14 +2250,202 @@ } } return liberties; } +/* Find the liberties a stone of the given color would get if played + * at (pos). This function takes into consideration all captures. Its + * return value is exact in that sense it counts all the liberties, + * unless (maxlib) allows it to stop earlier. (pos) must be empty. If + * libs != NULL, the locations of up to maxlib liberties are written + * into libs[]. The counting of liberties may or may not be halted + * when maxlib is reached. The number of liberties is returned. + * + * This function guarantees that liberties which are not results of + * captures come first in libs[] array. To find whether all the + * liberties starting from a given one are results of captures, one + * may use if (board[libs[k]] != EMPTY) construction. + * + * If you want the number or the locations of all liberties, however + * many they are, you should pass MAXLIBS as the value for maxlib and + * allocate space for libs[] accordingly. + */ + +int +accuratelib(int pos, int color, int maxlib, int *libs) +{ + int k, l; + int liberties = 0; + int lib; + int captured[4]; + int captures = 0; + + ASSERT1(board[pos] == EMPTY, pos); + ASSERT1(IS_STONE(color), pos); + + if (!libs) { + int fast_liberties = fastlib(pos, color, 0); + if (fast_liberties >= 0) + return fast_liberties; + } + + if (!strings_initialized) + init_board(); + + string_mark++; + liberty_mark++; + MARK_LIBERTY(pos); + + for (k = 0; k < 4; k++) { + int pos2 = pos + delta[k]; + if (UNMARKED_LIBERTY(pos2)) { + /* A trivial liberty */ + if (libs) + libs[liberties] = pos2; + liberties++; + if (liberties >= maxlib) + return liberties; + + MARK_LIBERTY(pos2); + } + else if (board[pos2] == color) { + /* An own neighbor string */ + struct string_data *s = &string[string_number[pos2]]; + + if (s->liberties <= MAX_LIBERTIES || maxlib <= MAX_LIBERTIES - 1) { + /* The easy case - we already have all (necessary) liberties of + * the string listed + */ + for (l = 0; l < s->liberties; l++) { + lib = s->libs[l]; + if (UNMARKED_LIBERTY(lib)) { + if(libs) + libs[liberties] = lib; + liberties++; + if(liberties >= maxlib) + return liberties; + + MARK_LIBERTY(lib); + } + } + } + else { + /* The harder case - we need to find all the liberties of the + * string by traversing its stones. We stop as soon as we have + * traversed all the stones or have reached maxlib. Unfortunately, + * we cannot use the trick from findlib() since some of the + * liberties may already have been marked. + */ + int stone = pos2; + do { + if (UNMARKED_LIBERTY(SOUTH(stone))) { + if (libs) + libs[liberties] = SOUTH(stone); + liberties++; + if (liberties >= maxlib) + return liberties; + + MARK_LIBERTY(SOUTH(stone)); + } + + if (UNMARKED_LIBERTY(WEST(stone))) { + if (libs) + libs[liberties] = WEST(stone); + liberties++; + if (liberties >= maxlib) + return liberties; + + MARK_LIBERTY(WEST(stone)); + } + + if (UNMARKED_LIBERTY(NORTH(stone))) { + if (libs) + libs[liberties] = NORTH(stone); + liberties++; + if (liberties >= maxlib) + return liberties; + + MARK_LIBERTY(NORTH(stone)); + } + + if (UNMARKED_LIBERTY(EAST(stone))) { + if (libs) + libs[liberties] = EAST(stone); + liberties++; + if (liberties >= maxlib) + return liberties; + + MARK_LIBERTY(EAST(stone)); + } + + stone = NEXT_STONE(stone); + } while (stone != pos2); + } + + MARK_STRING(pos2); + } + else if (board[pos2] == OTHER_COLOR(color) + && string[string_number[pos2]].liberties == 1) { + /* A capture. */ + captured[captures++] = pos2; + } + } + + /* Now we look at all the captures found in the previous step */ + for (k = 0; k < captures; k++) { + lib = captured[k]; + + /* Add the stone adjacent to (pos) to the list of liberties if + * it is not also adjacent to an own marked string (otherwise, + * it will be added later). + */ + if (!MARKED_COLOR_STRING(SOUTH(lib), color) + && !MARKED_COLOR_STRING(WEST(lib), color) + && !MARKED_COLOR_STRING(NORTH(lib), color) + && !MARKED_COLOR_STRING(EAST(lib), color)) { + if (libs) + libs[liberties] = lib; + liberties++; + if (liberties >= maxlib) + return liberties; + } + + /* Check if we already know of this capture. */ + for (l = 0; l < k; l++) + if (string_number[captured[l]] == string_number[lib]) + break; + + if (l == k) { + /* Traverse all the stones of the capture and add to the list + * of liberties those, which are adjacent to at least one own + * marked string. + */ + do { + if (MARKED_COLOR_STRING(SOUTH(lib), color) + || MARKED_COLOR_STRING(WEST(lib), color) + || MARKED_COLOR_STRING(NORTH(lib), color) + || MARKED_COLOR_STRING(EAST(lib), color)) { + if (libs) + libs[liberties] = lib; + liberties++; + if (liberties >= maxlib) + return liberties; + } + + lib = NEXT_STONE(lib); + } while (lib != captured[k]); + } + } + + return liberties; +} + + /* Find the number of common liberties of the two strings at str1 and str2. */ int count_common_libs(int str1, int str2) { int all_libs1[MAXLIBS], *libs1; @@ -2645,39 +2849,107 @@ * i.e. whether it would get more than one liberty. This function * returns true also for the case of a suicide move. */ int is_self_atari(int pos, int color) { - int liberties; - int result; + int other = OTHER_COLOR(color); + /* number of empty neighbors */ + int trivial_liberties = 0; + /* number of captured opponent strings */ + int captures = 0; + /* Whether there is a friendly neighbor with a spare liberty. If it + * has more than one spare liberty we immediately return 0. + */ + int far_liberties = 0; ASSERT_ON_BOARD1(pos); ASSERT1(board[pos] == EMPTY, pos); ASSERT1(IS_STONE(color), pos); if (!strings_initialized) init_board(); - /* 1. Try first without really putting the stone on the board. */ - /* FIXME: Integrate incremental_sloppy_self_atari() here. */ - result = incremental_sloppy_self_atari(pos, color); - if (result != -1) - return result; + /* 1. Try first to solve the problem without much work. */ + string_mark++; + + if (LIBERTY(SOUTH(pos))) + trivial_liberties++; + else if (board[SOUTH(pos)] == color) { + if (LIBERTIES(SOUTH(pos)) > 2) + return 0; + if (LIBERTIES(SOUTH(pos)) == 2) + far_liberties++; + } + else if (board[SOUTH(pos)] == other + && LIBERTIES(SOUTH(pos)) == 1 && UNMARKED_STRING(SOUTH(pos))) { + captures++; + MARK_STRING(SOUTH(pos)); + } - /* 2. It was not so easy. Now see if we can put the stone on the board. - * If we can't, this is a self atari. + if (LIBERTY(WEST(pos))) + trivial_liberties++; + else if (board[WEST(pos)] == color) { + if (LIBERTIES(WEST(pos)) > 2) + return 0; + if (LIBERTIES(WEST(pos)) == 2) + far_liberties++; + } + else if (board[WEST(pos)] == other + && LIBERTIES(WEST(pos)) == 1 && UNMARKED_STRING(WEST(pos))) { + captures++; + MARK_STRING(WEST(pos)); + } + + if (LIBERTY(NORTH(pos))) + trivial_liberties++; + else if (board[NORTH(pos)] == color) { + if (LIBERTIES(NORTH(pos)) > 2) + return 0; + if (LIBERTIES(NORTH(pos)) == 2) + far_liberties++; + } + else if (board[NORTH(pos)] == other + && LIBERTIES(NORTH(pos)) == 1 && UNMARKED_STRING(NORTH(pos))) { + captures++; + MARK_STRING(NORTH(pos)); + } + + if (LIBERTY(EAST(pos))) + trivial_liberties++; + else if (board[EAST(pos)] == color) { + if (LIBERTIES(EAST(pos)) > 2) + return 0; + if (LIBERTIES(EAST(pos)) == 2) + far_liberties++; + } + else if (board[EAST(pos)] == other + && LIBERTIES(EAST(pos)) == 1 && UNMARKED_STRING(EAST(pos))) { + captures++; +#if 0 + MARK_STRING(EAST(pos)); +#endif + } + + /* Each captured string is guaranteed to produce at least one + * liberty. These are disjoint from both trivial liberties and far + * liberties. The two latter may however coincide. */ - if (!do_trymove(pos, color, 1)) + if (trivial_liberties + captures >= 2) + return 0; + + if ((far_liberties > 0) + captures >= 2) + return 0; + + if (captures == 0 && far_liberties + trivial_liberties <= 1) return 1; - liberties = string[string_number[pos]].liberties; - silent_popgo(); - - return liberties <= 1; + + /* 2. It was not so easy. We use accuratelib() in this case. */ + return accuratelib(pos, color, 2, NULL) <= 1; } /* * Returns true if pos is a liberty of the string at str. */ @@ -3863,52 +4135,52 @@ DO_ADD_STONE(pos, color); /* Count the number of adjacent strings of my color and remove * pos as liberty for the adjacent opponent strings. */ string_mark++; - if (board[south] == color && UNMARKED_STRING(south)) { + if (UNMARKED_COLOR_STRING(south, color)) { neighbor_allies++; s = string_number[south]; MARK_STRING(south); } - else if (board[south] == other && UNMARKED_STRING(south)) { + else if (UNMARKED_COLOR_STRING(south, other)) { remove_liberty(string_number[south], pos); MARK_STRING(south); } - if (board[west] == color && UNMARKED_STRING(west)) { + if (UNMARKED_COLOR_STRING(west, color)) { neighbor_allies++; s = string_number[west]; MARK_STRING(west); } - else if (board[west] == other && UNMARKED_STRING(west)) { + else if (UNMARKED_COLOR_STRING(west, other)) { remove_liberty(string_number[west], pos); MARK_STRING(west); } - if (board[north] == color && UNMARKED_STRING(north)) { + if (UNMARKED_COLOR_STRING(north, color)) { neighbor_allies++; s = string_number[north]; MARK_STRING(north); } - else if (board[north] == other && UNMARKED_STRING(north)) { + else if (UNMARKED_COLOR_STRING(north, other)) { remove_liberty(string_number[north], pos); MARK_STRING(north); } - if (board[east] == color && UNMARKED_STRING(east)) { + if (UNMARKED_COLOR_STRING(east, color)) { neighbor_allies++; s = string_number[east]; #if 0 MARK_STRING(east); #endif } - else if (board[east] == other && UNMARKED_STRING(east)) { + else if (UNMARKED_COLOR_STRING(east, other)) { remove_liberty(string_number[east], pos); #if 0 MARK_STRING(east); #endif } Index: engine/liberty.h =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/liberty.h,v retrieving revision 1.127 diff -C7 -u -r1.127 liberty.h --- engine/liberty.h 20 Oct 2002 10:40:45 -0000 1.127 +++ engine/liberty.h 20 Oct 2002 14:35:48 -0000 @@ -155,27 +155,25 @@ /* Count and/or find liberties at (pos). */ int countlib(int str); int findlib(int str, int maxlib, int *libs); int fastlib(int pos, int color, int ignore_capture); int approxlib(int pos, int color, int maxlib, int *libs); +int accuratelib(int pos, int color, int maxlib, int *libs); int count_common_libs(int str1, int str2); int find_common_libs(int str1, int str2, int maxlib, int *libs); int have_common_lib(int str1, int str2, int *lib); void start_timer(int n); double time_report(int n, const char *occupation, int move, double mintime); void update_random_seed(void); - -/* Play at (pos) and then count the liberties. */ -int accurate_approxlib(int pos, int color, int maxlib, int *libs); /* Check for self atari. */ int is_self_atari(int pos, int color); /* Count the number of stones in a string. */ int countstones(int str); int findstones(int str, int maxstones, int *stones); Index: engine/owl.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/owl.c,v retrieving revision 1.113 diff -C7 -u -r1.113 owl.c --- engine/owl.c 14 Oct 2002 17:48:38 -0000 1.113 +++ engine/owl.c 20 Oct 2002 14:35:48 -0000 @@ -4271,16 +4271,16 @@ findlib(pos, 2, libs); if (libs[0] == defense_point) pos2 = libs[1]; else pos2 = libs[0]; - if (accurate_approxlib(pos2, color, MAXLIBS, NULL) - > accurate_approxlib(defense_point, color, MAXLIBS, NULL) + if (accuratelib(pos2, color, MAXLIBS, NULL) + > accuratelib(defense_point, color, MAXLIBS, NULL) && does_defend(pos2, lunch)) { TRACE("Moved defense of lunch %1m from %1m to %1m.\n", lunch, defense_point, pos2); return pos2; } } } Index: engine/readconnect.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/readconnect.c,v retrieving revision 1.38 diff -C7 -u -r1.38 readconnect.c --- engine/readconnect.c 15 Oct 2002 16:55:07 -0000 1.38 +++ engine/readconnect.c 20 Oct 2002 14:35:48 -0000 @@ -2949,15 +2949,15 @@ { int lib; int adj[MAXCHAIN]; if (findlib(str, 1, &lib) > 1) return 0; - if (accurate_approxlib(lib, board[str], 2, NULL) > 1) + if (accuratelib(lib, board[str], 2, NULL) > 1) return 0; /* FIXME: Should exclude snapback. */ if (chainlinks2(str, adj, 1) > 0) return 0; return 1; Index: engine/utils.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/engine/utils.c,v retrieving revision 1.58 diff -C7 -u -r1.58 utils.c --- engine/utils.c 9 Oct 2002 18:36:23 -0000 1.58 +++ engine/utils.c 20 Oct 2002 14:35:48 -0000 @@ -758,60 +758,14 @@ backfill2_depth = save_backfill2_depth; superstring_depth = save_superstring_depth; branch_depth = save_branch_depth; fourlib_depth = save_fourlib_depth; ko_depth = save_ko_depth; } -/* Play a stone at (pos) and count the number of liberties for the - * resulting string. This requires (pos) to be empty. - * - * This function differs from approxlib() by the fact that it removes - * captured stones before counting the liberties. - */ - -int -accurate_approxlib(int pos, int color, int maxlib, int *libs) -{ - int fast_liberties = -1; - int liberties = 0; - SGFTree *save_sgf_dumptree = sgf_dumptree; - int save_count_variations = count_variations; - - ASSERT1(board[pos] == EMPTY, pos); - ASSERT1(IS_STONE(color), pos); - - if (!libs) { - fast_liberties = fastlib(pos, color, 0); - if (fast_liberties >= 0) { - return fast_liberties; - } - } - - sgf_dumptree = 0; - /* Use tryko() since we don't care whether the move would violate - * the ko rule. - */ - if (tryko(pos, color, "accurate approxlib", EMPTY, 0)) { - if (libs != NULL) - liberties = findlib(pos, maxlib, libs); - else - liberties = countlib(pos); - popgo(); - } - - if (fast_liberties >= 0 && liberties > 0) { - ASSERT1(fast_liberties == liberties, pos); - } - - sgf_dumptree = save_sgf_dumptree; - count_variations = save_count_variations; - - return liberties; -} /******************* * Detect blunders * *******************/ static int detect_owl_blunder(int move, int color, int *defense_point, char safe_stones[BOARDMAX], int liberties, @@ -851,15 +805,15 @@ */ float blunder_size(int move, int color, int *defense_point, char safe_stones[BOARDMAX]) { int libs[5]; - int liberties = accurate_approxlib(move, color, 5, libs); + int liberties = accuratelib(move, color, 5, libs); int apos; int trouble = 0; int save_verbose = verbose; float return_value = 0.0; int atari; if (defense_point) Index: interface/play_gtp.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/interface/play_gtp.c,v retrieving revision 1.98 diff -C7 -u -r1.98 play_gtp.c --- interface/play_gtp.c 20 Oct 2002 10:58:22 -0000 1.98 +++ interface/play_gtp.c 20 Oct 2002 14:35:48 -0000 @@ -69,14 +69,15 @@ DECLARE(gtp_dragon_data); DECLARE(gtp_dragon_status); DECLARE(gtp_dragon_stones); DECLARE(gtp_dump_stack); DECLARE(gtp_echo); DECLARE(gtp_echo_err); DECLARE(gtp_eval_eye); +DECLARE(gtp_accuratelib); DECLARE(gtp_final_score); DECLARE(gtp_final_status); DECLARE(gtp_final_status_list); DECLARE(gtp_findlib); DECLARE(gtp_finish_sgftrace); DECLARE(gtp_fixed_handicap); DECLARE(gtp_get_handicap); @@ -180,14 +181,15 @@ {"dragon_data", gtp_dragon_data}, {"dragon_status", gtp_dragon_status}, {"dragon_stones", gtp_dragon_stones}, {"dump_stack", gtp_dump_stack}, {"echo" , gtp_echo}, {"echo_err" , gtp_echo_err}, {"estimate_score", gtp_estimate_score}, + {"accuratelib", gtp_accuratelib}, {"experimental_score", gtp_experimental_score}, {"eval_eye", gtp_eval_eye}, {"final_score", gtp_final_score}, {"final_status", gtp_final_status}, {"final_status_list", gtp_final_status_list}, {"findlib", gtp_findlib}, {"finish_sgftrace", gtp_finish_sgftrace}, @@ -884,14 +886,42 @@ if (!gtp_decode_coord(s, &i, &j)) return gtp_failure("invalid coordinate"); if (BOARD(i, j) == EMPTY) return gtp_failure("vertex must not be empty"); liberties = findlib(POS(i, j), MAXLIBS, libs); + gtp_start_response(GTP_SUCCESS); + gtp_print_vertices2(liberties, libs); + return gtp_finish_response(); +} + + +/* Function: Determine which liberties a stone of given color + * will get if played at given vertex. + * Arguments: move (color + vertex) + * Fails: invalid color, invalid vertex, occupied vertex + * Returns: Sorted space separated list of liberties + */ +static int +gtp_accuratelib(char *s) +{ + int i, j; + int color; + int libs[MAXLIBS]; + int liberties; + + if (!gtp_decode_move(s, &color, &i, &j)) + return gtp_failure("invalid color or coordinate"); + + if (BOARD(i, j) != EMPTY) + return gtp_failure("vertex must be empty"); + + liberties = accuratelib(POS(i, j), color, MAXLIBS, libs); + gtp_start_response(GTP_SUCCESS); gtp_print_vertices2(liberties, libs); return gtp_finish_response(); } /* Function: Tell whether a move is legal. Index: patterns/mkpat.c =================================================================== RCS file: /cvsroot/gnugo/gnugo/patterns/mkpat.c,v retrieving revision 1.83 diff -C7 -u -r1.83 mkpat.c --- patterns/mkpat.c 14 Oct 2002 15:41:07 -0000 1.83 +++ patterns/mkpat.c 20 Oct 2002 14:35:48 -0000 @@ -220,17 +220,17 @@ "(influence_territory_color(%s) == color)"}, {"genus", 1, 0.01, "dragon[%s].genus"}, {"approx_xlib", 1, 0.03, "approxlib(%s, OTHER_COLOR(color), MAX_LIBERTIES, NULL)"}, {"approx_olib", 1, 0.03, "approxlib(%s, color, MAX_LIBERTIES, NULL)"}, {"xlib", 1, 0.05, - "accurate_approxlib(%s, OTHER_COLOR(color), MAX_LIBERTIES, NULL)"}, + "accuratelib(%s, OTHER_COLOR(color), MAX_LIBERTIES, NULL)"}, {"olib", 1, 0.05, - "accurate_approxlib(%s,color, MAX_LIBERTIES, NULL)"}, + "accuratelib(%s,color, MAX_LIBERTIES, NULL)"}, {"xcut", 1, 0.01, "cut_possible(%s,OTHER_COLOR(color))"}, {"ocut", 1, 0.05, "cut_possible(%s,color)"}, {"edge_double_sente", 4, 3.00, "edge_double_sente_helper(%s, %s, %s, %s)"}, {"xplay_defend_both", -2, 3.00, "play_attack_defend2_n(OTHER_COLOR(color), 0, %d"}, {"oplay_defend_both", -2, 3.00, "play_attack_defend2_n(color, 0, %d"},