diff -N -r -u -X .ignore gnugo-copy/doc/utils.texi gnugo/doc/utils.texi
|
old
|
new
|
|
| 35 | 35 | @item @code{int does_attack(int move, int str)} |
| 36 | 36 | @findex does_attack |
| 37 | 37 | @quotation |
| 38 | | returns true if the move at @code{move} attacks @code{str}. This means that it captures |
| 39 | | the string, and that @code{str} is not already dead. |
| | 38 | returns the result code for an attack on the string @code{'str'} by the move |
| | 39 | @code{'move'}. However, if the move does not improve the attack result compared |
| | 40 | to tenuki, 0 is returned. In particular if the string is already captured, |
| | 41 | @code{does_attack()} always returns 0. |
| 40 | 42 | @end quotation |
| 41 | 43 | @item @code{int does_defend(int move, int str)} |
| 42 | 44 | @findex does_defend |
| 43 | 45 | @quotation |
| 44 | | @code{does_defend(move, str)} returns true if the move at @code{move} |
| 45 | | defends @code{str}. This means that it defends the string, and that |
| 46 | | @code{str} can be captured if no defense is made. |
| | 46 | returns the result code for a defense on the string @code{'str'} by the move |
| | 47 | @code{'move'}. However, if the move does not improve the defense result compared |
| | 48 | to tenuki, 0 is returned. If the string (str) can't be captured if no defense |
| | 49 | is made, 0 is returned too. |
| 47 | 50 | @end quotation |
| 48 | 51 | @item @code{int somewhere(int color, int last_move, ...)} |
| 49 | 52 | @findex somewhere |
| … |
… |
|
| 770 | 773 | @item @code{int has_neighbor(int pos, int color)} |
| 771 | 774 | @findex has_neighbor |
| 772 | 775 | @quotation |
| 773 | | Returns true if @code{pos} has a neighbor of @code{color}. |
| | 776 | Returns true if @code{pos} has a neighbor of color @code{color}. |
| | 777 | @end quotation |
| | 778 | @item @code{int find_neighbor(int pos, int color)} |
| | 779 | @findex find_neighbor |
| | 780 | @quotation |
| | 781 | If @code{pos} has exactly one neighbor of color @code{color} returns |
| | 782 | position of this neighbor. Otherwise returns -1. |
| 774 | 783 | @end quotation |
| 775 | 784 | @item @code{int same_string(int str1_pos, int str2_pos)} |
| 776 | 785 | @findex same_string |
diff -N -r -u -X .ignore gnugo-copy/engine/board.c gnugo/engine/board.c
|
old
|
new
|
|
| 374 | 374 | const int deltai[8] = { 1, 0, -1, 0, 1, -1, -1, 1}; |
| 375 | 375 | const int deltaj[8] = { 0, -1, 0, 1, -1, -1, 1, 1}; |
| 376 | 376 | const int delta[8] = { NS, -WE, -NS, WE, NS-WE, -NS-WE, -NS+WE, NS+WE}; |
| | 377 | const int *last_delta_4 = delta + 4; |
| | 378 | const int *last_delta_8 = delta + 8; |
| 377 | 379 | |
| 378 | 380 | |
| 379 | 381 | /* ================================================================ */ |
| … |
… |
|
| 1542 | 1544 | * 5b) Unconditional ko capture is only allowed if it is nested ko capture. |
| 1543 | 1545 | * Kom_pos is moved to the old value of board_ko_pos. |
| 1544 | 1546 | * 5c) Conditional ko capture is allowed according to the rules of 1b. |
| | 1547 | * |
| | 1548 | * For description what is "conditional ko" look in the documentation: |
| | 1549 | * Tactical reading: Ko Handling |
| 1545 | 1550 | */ |
| 1546 | 1551 | int |
| 1547 | 1552 | komaster_trymove(int pos, int color, const char *message, int str, |
| … |
… |
|
| 1972 | 1977 | * it is not optimized much. |
| 1973 | 1978 | */ |
| 1974 | 1979 | else { |
| 1975 | | const int *cur_delta, *last_delta; |
| | 1980 | const int *cur_delta; |
| 1976 | 1981 | int other = OTHER_COLOR(color); |
| 1977 | 1982 | |
| 1978 | | cur_delta = delta; |
| 1979 | | last_delta = cur_delta + 4; |
| 1980 | | for (; cur_delta < last_delta; cur_delta++) { |
| | 1983 | for (cur_delta = delta; cur_delta < last_delta_4; cur_delta++) { |
| 1981 | 1984 | int neighbor_pos = pos + *cur_delta; |
| 1982 | 1985 | |
| 1983 | 1986 | if (LIBERTY(neighbor_pos) |
| … |
… |
|
| 2261 | 2264 | slow_approxlib(int pos, int color, int maxlib, int *libs, |
| 2262 | 2265 | struct board_cache_entry *entry) |
| 2263 | 2266 | { |
| 2264 | | const int *delta_o, *delta_i, *last_delta = delta + 4; |
| | 2267 | const int *delta_o, *delta_i; |
| 2265 | 2268 | int liberties = 0; |
| 2266 | 2269 | int checked_pos, checked_pos2; |
| 2267 | 2270 | int pos2, str_nr, first_stone; |
| … |
… |
|
| 2270 | 2273 | MARK_LIBERTY(pos); |
| 2271 | 2274 | string_mark++; |
| 2272 | 2275 | |
| 2273 | | for (delta_o = delta; delta_o < last_delta; delta_o++) { |
| | 2276 | for (delta_o = delta; delta_o < last_delta_4; delta_o++) { |
| 2274 | 2277 | checked_pos = pos + *delta_o; |
| 2275 | 2278 | if (board[checked_pos] == EMPTY) { |
| 2276 | 2279 | if (ml[checked_pos] != liberty_mark) { |
| … |
… |
|
| 2287 | 2290 | first_stone = FIRST_STONE(str_nr); |
| 2288 | 2291 | pos2 = first_stone; |
| 2289 | 2292 | do { |
| 2290 | | int l; |
| 2291 | | for (delta_i = delta; delta_i < last_delta; delta_i++) { |
| | 2293 | for (delta_i = delta; delta_i < last_delta_4; delta_i++) { |
| 2292 | 2294 | checked_pos2 = pos2 + *delta_i; |
| 2293 | 2295 | if (UNMARKED_LIBERTY(checked_pos2)) { |
| 2294 | 2296 | if (libs) |
| … |
… |
|
| 2416 | 2418 | do_accuratelib(int pos, int color, int maxlib, int *libs, |
| 2417 | 2419 | struct board_cache_entry *entry) |
| 2418 | 2420 | { |
| 2419 | | const int *act_delta, *last_delta; |
| | 2421 | const int *act_delta; |
| 2420 | 2422 | int *act_lib, *last_lib; |
| 2421 | 2423 | int lib_count = 0; |
| 2422 | 2424 | int captured[4]; |
| … |
… |
|
| 2430 | 2432 | liberty_mark++; |
| 2431 | 2433 | MARK_LIBERTY(pos); |
| 2432 | 2434 | |
| 2433 | | act_delta = delta; |
| 2434 | | last_delta = act_delta + 4; |
| 2435 | | for (; act_delta < last_delta; act_delta++) { |
| | 2435 | for (act_delta = delta; act_delta < last_delta_4; act_delta++) { |
| 2436 | 2436 | int checked_pos = pos + *act_delta; |
| 2437 | 2437 | if (board[checked_pos] == EMPTY) { |
| 2438 | 2438 | if (ml[checked_pos] != liberty_mark) { |
| … |
… |
|
| 3809 | 3809 | propagate_string(int stone_pos, int str_pos, int color) |
| 3810 | 3810 | { |
| 3811 | 3811 | int size = 1; |
| 3812 | | const int *act_delta, *last_delta; |
| | 3812 | const int *act_delta; |
| 3813 | 3813 | |
| 3814 | 3814 | if (stone_pos == str_pos) { |
| 3815 | 3815 | /* Start a new string. */ |
| … |
… |
|
| 3823 | 3823 | } |
| 3824 | 3824 | |
| 3825 | 3825 | /* Look in all four directions for more stones to add. */ |
| 3826 | | act_delta = delta; |
| 3827 | | last_delta = act_delta + 4; |
| 3828 | | for (; act_delta < last_delta; act_delta++) { |
| | 3826 | for (act_delta = delta; act_delta < last_delta_4; act_delta++) { |
| 3829 | 3827 | int new_pos = stone_pos + *act_delta; |
| 3830 | 3828 | if (board[new_pos] == color |
| 3831 | 3829 | && string_number[new_pos] == -1) |
diff -N -r -u -X .ignore gnugo-copy/engine/board.h gnugo/engine/board.h
|
old
|
new
|
|
| 2 | 2 | * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * |
| 3 | 3 | * http://www.gnu.org/software/gnugo/ for more information. * |
| 4 | 4 | * * |
| 5 | | * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005 and 2006 * |
| | 5 | * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 and 2007 * |
| 6 | 6 | * by the Free Software Foundation. * |
| 7 | 7 | * * |
| 8 | 8 | * This program is free software; you can redistribute it and/or * |
| … |
… |
|
| 393 | 393 | extern const int deltai[8]; /* = { 1, 0, -1, 0, 1, -1, -1, 1}; */ |
| 394 | 394 | extern const int deltaj[8]; /* = { 0, -1, 0, 1, -1, -1, 1, 1}; */ |
| 395 | 395 | extern const int delta[8]; /* = { NS, -WE, -NS, WE, NS-WE, -NS-WE, -NS+WE, NS+WE}; */ |
| | 396 | extern const int *last_delta_4; |
| | 397 | extern const int *last_delta_8; |
| 396 | 398 | |
| 397 | 399 | |
| 398 | 400 | |
diff -N -r -u -X .ignore gnugo-copy/engine/liberty.h gnugo/engine/liberty.h
|
old
|
new
|
|
| 835 | 835 | * points with corresponding result codes. (0 = loss, 1 = bad ko, 2 |
| 836 | 836 | * = good ko, 3 = win). The arrays are guaranteed to be sorted with |
| 837 | 837 | * respect to the codes so that the first element contains the best |
| 838 | | * result. |
| | 838 | * result. Checked and discarded moves are stored, too. |
| 839 | 839 | */ |
| 840 | 840 | int attack_points[MAX_TACTICAL_POINTS]; |
| 841 | 841 | int attack_codes[MAX_TACTICAL_POINTS]; |
diff -N -r -u -X .ignore gnugo-copy/engine/unconditional.c gnugo/engine/unconditional.c
|
old
|
new
|
|
| 45 | 45 | int moves_played = 0; |
| 46 | 46 | int save_moves; |
| 47 | 47 | int libs[MAXLIBS]; |
| 48 | | int liberties; |
| 49 | 48 | int pos; |
| 50 | 49 | int *cur_lib, *last_lib; |
| 51 | 50 | |
| … |
… |
|
| 347 | 346 | int stones[2]; |
| 348 | 347 | int pos2; |
| 349 | 348 | int checked_stone; |
| 350 | | const int *cur_delta, *last_delta; |
| | 349 | const int *cur_delta; |
| 351 | 350 | |
| 352 | 351 | if (board[pos] != color |
| 353 | 352 | || find_origin(pos) != pos |
| … |
… |
|
| 357 | 356 | findstones(pos, 2, stones); |
| 358 | 357 | for (k = 0; k < 2 && isolated; k++) { |
| 359 | 358 | checked_stone = stones[k]; |
| 360 | | cur_delta = delta; |
| 361 | | last_delta = cur_delta + 8; |
| 362 | | for (; cur_delta < last_delta; cur_delta++) { |
| | 359 | for (cur_delta = delta; cur_delta < last_delta_8; cur_delta++) { |
| 363 | 360 | pos2 = checked_stone + *cur_delta; |
| 364 | 361 | if (!ON_BOARD(pos2) |
| 365 | 362 | || (board[pos2] == color |
diff -N -r -u -X .ignore gnugo-copy/engine/worm.c gnugo/engine/worm.c
|
old
|
new
|
|
| 72 | 72 | * the first element when they are given the lexicographical order, |
| 73 | 73 | * though its location is irrelevant for applications. To see if two |
| 74 | 74 | * stones lie in the same worm, compare their origins. |
| 75 | | * |
| 76 | | * We will use the field dragon[pos].genus to keep track of |
| 77 | | * black- or white-bordered cavities (essentially eyes) which are found. |
| 78 | | * so this field must be zero'd now. |
| 79 | 75 | */ |
| 80 | 76 | |
| 81 | 77 | void |
| … |
… |
|
| 83 | 79 | { |
| 84 | 80 | int pos; |
| 85 | 81 | int str; |
| 86 | | int dcode; |
| | 82 | int acode, dcode; |
| | 83 | int other; |
| | 84 | const int *cur_delta; |
| 87 | 85 | |
| 88 | 86 | /* Build the basic worm data: color, origin, size, liberties. */ |
| 89 | 87 | build_worms(); |
| … |
… |
|
| 104 | 102 | |
| 105 | 103 | gg_assert(stackp == 0); |
| 106 | 104 | |
| 107 | | /* Count liberties of different orders. */ |
| 108 | | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 109 | | int lib1; |
| 110 | | if (IS_STONE(board[pos]) && is_worm_origin(pos, pos)) { |
| 111 | | ping_cave(pos, &lib1, &worm[pos].liberties2, &worm[pos].liberties3, |
| 112 | | &worm[pos].liberties4); |
| 113 | | ASSERT1(worm[pos].liberties == lib1, pos); |
| 114 | | propagate_worm(pos); |
| 115 | | } |
| 116 | | } |
| 117 | | |
| 118 | | gg_assert(stackp == 0); |
| 119 | | |
| 120 | 105 | /* |
| 121 | 106 | * There are two concepts of cutting stones in the worm array. |
| 122 | 107 | * |
| … |
… |
|
| 164 | 149 | * |
| 165 | 150 | * For the time being we use both concepts in parallel. It's |
| 166 | 151 | * possible we also need the old concept for correct handling of lunches. |
| | 152 | * |
| | 153 | * We also set liberties and genus fields of worm_data in this loop. |
| 167 | 154 | */ |
| 168 | 155 | |
| 169 | | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 170 | | int w1 = NO_MOVE; |
| 171 | | int w2 = NO_MOVE; |
| 172 | | int k; |
| 173 | | int pos2; |
| | 156 | for (str = BOARDMIN; str < BOARDMAX; str++) { |
| | 157 | int w1; |
| | 158 | int w2; |
| 174 | 159 | |
| 175 | 160 | /* Only work on each worm once. This is easiest done if we only |
| 176 | 161 | * work with the origin of each worm. |
| 177 | 162 | */ |
| 178 | | if (!IS_STONE(board[pos]) || !is_worm_origin(pos, pos)) |
| | 163 | if (!IS_STONE(board[str]) || !is_worm_origin(str, str)) |
| 179 | 164 | continue; |
| | 165 | |
| | 166 | /* Count liberties of different orders. */ |
| | 167 | ping_cave(str, &worm[str].liberties, &worm[str].liberties2, |
| | 168 | &worm[str].liberties3, &worm[str].liberties4); |
| | 169 | ASSERT1(worm[str].liberties == countlib(str), str); |
| | 170 | |
| | 171 | /* Set the genus of all worms. */ |
| | 172 | worm[str].genus = genus(str); |
| | 173 | |
| | 174 | w1 = 0; |
| | 175 | w2 = 0; |
| | 176 | other = OTHER_COLOR(board[str]); |
| 180 | 177 | |
| 181 | 178 | /* Try to find two adjacent worms (w1) and (w2) |
| 182 | | * of opposite colour from (pos). |
| | 179 | * of opposite color from (str). |
| 183 | 180 | */ |
| 184 | | for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { |
| 185 | | /* Work only with the opposite color from (pos). */ |
| 186 | | if (board[pos2] != OTHER_COLOR(board[pos])) |
| | 181 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| | 182 | /* Work only with the opposite color from (str). */ |
| | 183 | if (board[pos] != other) |
| 187 | 184 | continue; |
| 188 | 185 | |
| 189 | | for (k = 0; k < 4; k++) { |
| 190 | | if (!IS_STONE(board[pos2 + delta[k]]) |
| 191 | | || worm[pos2 + delta[k]].origin != pos) |
| | 186 | for (cur_delta = delta; cur_delta < last_delta_4; cur_delta++) { |
| | 187 | if (!IS_STONE(board[pos + *cur_delta]) |
| | 188 | || worm[pos + *cur_delta].origin != str) |
| 192 | 189 | continue; |
| 193 | 190 | |
| 194 | | ASSERT1(board[pos2 + delta[k]] == board[pos], pos); |
| | 191 | ASSERT1(board[pos + *cur_delta] == board[str], str); |
| 195 | 192 | |
| 196 | 193 | /* If we have not already found a worm which meets the criteria, |
| 197 | 194 | * store it into (w1), otherwise store it into (w2). |
| 198 | 195 | */ |
| 199 | | if (w1 == NO_MOVE) |
| 200 | | w1 = worm[pos2].origin; |
| 201 | | else if (!is_same_worm(pos2, w1)) |
| 202 | | w2 = worm[pos2].origin; |
| | 196 | if (!w1) |
| | 197 | w1 = pos; |
| | 198 | else if (!is_same_worm(pos, w1)) { |
| | 199 | w2 = pos; |
| | 200 | break; |
| | 201 | } |
| 203 | 202 | } |
| 204 | 203 | } |
| 205 | 204 | |
| … |
… |
|
| 211 | 210 | */ |
| 212 | 211 | |
| 213 | 212 | /* Only do this if we really found something. */ |
| 214 | | if (w2 != NO_MOVE) { |
| 215 | | worm[pos].cutstone = 2; |
| 216 | | if (count_common_libs(w1, w2) > 0) |
| 217 | | worm[pos].cutstone = 1; |
| | 213 | if (w2) { |
| | 214 | if (have_common_lib(w1, w2, NULL)) |
| | 215 | worm[str].cutstone = 1; |
| | 216 | else |
| | 217 | worm[str].cutstone = 2; |
| 218 | 218 | |
| 219 | 219 | DEBUG(DEBUG_WORMS, "Worm at %1m has w1 %1m and w2 %1m, cutstone %d\n", |
| 220 | | pos, w1, w2, worm[pos].cutstone); |
| | 220 | str, worm[w1].origin, worm[w2].origin, worm[str].cutstone); |
| 221 | 221 | } |
| | 222 | |
| | 223 | propagate_worm(str); |
| 222 | 224 | } |
| 223 | 225 | |
| 224 | 226 | gg_assert(stackp == 0); |
| 225 | 227 | |
| 226 | | /* Set the genus of all worms. */ |
| 227 | | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 228 | | if (IS_STONE(board[pos]) && is_worm_origin(pos, pos)) { |
| 229 | | worm[pos].genus = genus(pos); |
| 230 | | propagate_worm(pos); |
| 231 | | } |
| 232 | | } |
| 233 | | gg_assert(stackp == 0); |
| 234 | | |
| 235 | 228 | /* Now we try to improve the values of worm.attack and worm.defend. |
| 236 | 229 | * If we find that capturing the string at str also defends the |
| 237 | 230 | * string at str2, or attacks it, then we add points of attack and |
| … |
… |
|
| 240 | 233 | */ |
| 241 | 234 | { |
| 242 | 235 | int color; |
| | 236 | int attack_works, defense_works; |
| | 237 | int worm_acode, worm_dcode; |
| 243 | 238 | int moves_to_try[BOARDMAX]; |
| 244 | 239 | memset(moves_to_try, 0, sizeof(moves_to_try)); |
| 245 | 240 | |
| … |
… |
|
| 252 | 247 | } |
| 253 | 248 | } |
| 254 | 249 | |
| | 250 | increase_depth_values(); |
| | 251 | |
| 255 | 252 | /* Loop over the board and over the colors and try the moves found |
| 256 | | * in the previous loop. |
| | 253 | * in the previous loop. We must read to the same depth that was |
| | 254 | * used in the initial determination of worm.attack and worm.defend |
| | 255 | * to avoid horizon effects - that's why we must also increment |
| | 256 | * depth values. |
| 257 | 257 | */ |
| 258 | 258 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 259 | 259 | if (!ON_BOARD(pos)) |
| … |
… |
|
| 267 | 267 | if (!trymove(pos, color, "make_worms", NO_MOVE)) |
| 268 | 268 | continue; |
| 269 | 269 | |
| 270 | | /* We must read to the same depth that was used in the |
| 271 | | * initial determination of worm.attack and worm.defend |
| 272 | | * to avoid horizon effects. Since stackp has been |
| 273 | | * incremented we must also increment depth values. |
| 274 | | */ |
| 275 | | |
| 276 | 270 | DEBUG(DEBUG_WORMS, "trying %1m\n", pos); |
| 277 | | increase_depth_values(); |
| | 271 | |
| | 272 | other = OTHER_COLOR(color); |
| 278 | 273 | |
| 279 | 274 | /* Now we try to find a group which is saved or attacked as well |
| 280 | 275 | * by this move. |
| … |
… |
|
| 283 | 278 | if (!IS_STONE(board[str]) |
| 284 | 279 | || find_origin(str) != str) |
| 285 | 280 | continue; |
| | 281 | |
| | 282 | worm_acode = worm[str].attack_codes[0]; |
| 286 | 283 | |
| 287 | 284 | /* If the worm is of the opposite color to the move, |
| 288 | 285 | * then we try to defend it. If there was a previous |
| 289 | 286 | * attack and defense of it, and there is no defense |
| 290 | 287 | * for the attack now... |
| 291 | 288 | */ |
| 292 | | if (worm[str].color == OTHER_COLOR(color) |
| 293 | | && worm[str].attack_codes[0] != 0 |
| 294 | | && worm[str].attack_points[0] != pos |
| 295 | | && worm[str].defense_codes[0] != 0) { |
| | 289 | if (worm[str].color == other |
| | 290 | && worm_acode && worm[str].attack_points[0] != pos |
| | 291 | && (worm_dcode = worm[str].defense_codes[0])) { |
| 296 | 292 | dcode = find_defense(str, NULL); |
| 297 | | if (dcode < worm[str].defense_codes[0]) { |
| 298 | | int attack_works = 1; |
| | 293 | if (dcode < worm_dcode) { |
| | 294 | attack_works = 1; |
| 299 | 295 | |
| 300 | 296 | /* Sometimes find_defense() fails to find a |
| 301 | 297 | * defense which has been found by other means. |
| … |
… |
|
| 305 | 301 | * because we could, for instance, drive the worm into |
| 306 | 302 | * seki with our move. |
| 307 | 303 | */ |
| 308 | | if (attack(str, NULL) >= worm[str].attack_codes[0]) { |
| 309 | | if (worm[str].defense_codes[0] != 0 |
| 310 | | && trymove(worm[str].defense_points[0], |
| 311 | | OTHER_COLOR(color), "make_worms", 0)) { |
| | 304 | if (attack(str, NULL) >= worm_acode) { |
| | 305 | if (trymove(worm[str].defense_points[0], other, |
| | 306 | "make_worms", 0)) { |
| 312 | 307 | int this_dcode = REVERSE_RESULT(attack(str, NULL)); |
| 313 | 308 | if (this_dcode > dcode) { |
| 314 | | dcode = this_dcode; |
| 315 | | if (dcode >= worm[str].defense_codes[0]) |
| | 309 | if (this_dcode >= worm_dcode) |
| 316 | 310 | attack_works = 0; |
| | 311 | else |
| | 312 | dcode = this_dcode; |
| 317 | 313 | } |
| 318 | 314 | popgo(); |
| 319 | 315 | } |
| … |
… |
|
| 336 | 332 | * there is none now, then add a defense point of str at |
| 337 | 333 | * pos. |
| 338 | 334 | */ |
| 339 | | else if (worm[str].color == color |
| 340 | | && worm[str].attack_codes[0] != 0 |
| | 335 | else if (worm[str].color == color && worm_acode |
| 341 | 336 | && worm[str].defense_points[0] != pos) { |
| 342 | | int acode = attack(str, NULL); |
| 343 | | if (acode < worm[str].attack_codes[0]) { |
| 344 | | int defense_works = 1; |
| | 337 | acode = attack(str, NULL); |
| | 338 | if (acode < worm_acode) { |
| | 339 | defense_works = 1; |
| 345 | 340 | /* Sometimes attack() fails to find an |
| 346 | 341 | * attack which has been found by other means. |
| 347 | 342 | * Try if the old attack move still works. |
| 348 | 343 | */ |
| 349 | | if (worm[str].attack_codes[0] != 0 |
| 350 | | && trymove(worm[str].attack_points[0], |
| 351 | | OTHER_COLOR(color), "make_worms", 0)) { |
| 352 | | int this_acode; |
| 353 | | if (board[str] == EMPTY) |
| 354 | | this_acode = WIN; |
| 355 | | else |
| 356 | | this_acode = REVERSE_RESULT(find_defense(str, NULL)); |
| 357 | | if (this_acode > acode) { |
| 358 | | acode = this_acode; |
| 359 | | if (acode >= worm[str].attack_codes[0]) |
| 360 | | defense_works = 0; |
| | 344 | if (trymove(worm[str].attack_points[0], other, |
| | 345 | "make_worms", 0)) { |
| | 346 | if (!board[str]) |
| | 347 | defense_works = 0; |
| | 348 | else { |
| | 349 | int this_acode = REVERSE_RESULT(find_defense(str, NULL)); |
| | 350 | if (this_acode > acode) { |
| | 351 | if (this_acode >= worm_acode) |
| | 352 | defense_works = 0; |
| | 353 | else |
| | 354 | acode = this_acode; |
| | 355 | } |
| 361 | 356 | } |
| 362 | 357 | popgo(); |
| 363 | 358 | } |
| … |
… |
|
| 372 | 367 | } |
| 373 | 368 | } |
| 374 | 369 | } |
| 375 | | decrease_depth_values(); |
| 376 | 370 | popgo(); |
| 377 | 371 | } |
| 378 | 372 | } |
| | 373 | decrease_depth_values(); |
| 379 | 374 | } |
| 380 | 375 | |
| 381 | 376 | gg_assert(stackp == 0); |
| … |
… |
|
| 428 | 423 | |
| 429 | 424 | gg_assert(stackp == 0); |
| 430 | 425 | |
| 431 | | /* Find adjacent worms that can be easily captured, aka lunches. */ |
| 432 | | |
| 433 | | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 434 | | int lunch; |
| 435 | | |
| 436 | | if (!IS_STONE(board[pos]) || !is_worm_origin(pos, pos)) |
| 437 | | continue; |
| 438 | | |
| 439 | | if (find_lunch(pos, &lunch) |
| 440 | | && (worm[lunch].attack_codes[0] == WIN |
| 441 | | || worm[lunch].attack_codes[0] == KO_A)) { |
| 442 | | DEBUG(DEBUG_WORMS, "lunch found for %1m at %1m\n", pos, lunch); |
| 443 | | worm[pos].lunch = lunch; |
| 444 | | } |
| 445 | | else |
| 446 | | worm[pos].lunch = NO_MOVE; |
| 447 | | |
| 448 | | propagate_worm(pos); |
| 449 | | } |
| 450 | | |
| 451 | 426 | if (!disable_threat_computation) |
| 452 | 427 | find_worm_threats(); |
| 453 | 428 | |
| … |
… |
|
| 457 | 432 | * potential unless part of their surrounding chain can be captured. |
| 458 | 433 | * We give a conservative definition of inessential: |
| 459 | 434 | * - the genus must be zero |
| 460 | | * - there can no second order liberties |
| 461 | | * - there can be no more than two edge liberties |
| | 435 | * - there can be no second order liberties |
| | 436 | * - it can't be a cutstone |
| 462 | 437 | * - if it is removed from the board, the remaining cavity has |
| 463 | 438 | * border color the opposite color of the string |
| 464 | 439 | * - it contains at most two edge vertices. |
| … |
… |
|
| 472 | 447 | * |
| 473 | 448 | * An inessential string can be thought of as residing inside the |
| 474 | 449 | * opponent's eye space. |
| | 450 | * |
| | 451 | * We also find lunches here. |
| 475 | 452 | */ |
| 476 | 453 | |
| 477 | 454 | gg_assert(stackp == 0); |
| 478 | 455 | |
| 479 | 456 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| | 457 | int edge; |
| | 458 | int lunch; |
| | 459 | |
| 480 | 460 | if (IS_STONE(board[pos]) |
| 481 | | && worm[pos].origin == pos |
| 482 | | && worm[pos].genus == 0 |
| 483 | | && worm[pos].liberties2 == 0 |
| 484 | | && !worm[pos].cutstone |
| 485 | | && worm[pos].lunch == NO_MOVE) { |
| 486 | | int edge; |
| 487 | | int border_color = examine_cavity(pos, &edge); |
| 488 | | if (border_color != GRAY && edge < 3) { |
| 489 | | DEBUG(DEBUG_WORMS, "Worm %1m identified as inessential.\n", pos); |
| 490 | | worm[pos].inessential = 1; |
| | 461 | && worm[pos].origin == pos) { |
| | 462 | |
| | 463 | /* Find adjacent worms that can be easily captured, aka lunches. */ |
| | 464 | if (find_lunch(pos, &lunch) |
| | 465 | && ((acode = worm[lunch].attack_codes[0]) == WIN |
| | 466 | || acode == KO_A)) { |
| | 467 | DEBUG(DEBUG_WORMS, "lunch found for %1m at %1m\n", pos, lunch); |
| | 468 | worm[pos].lunch = lunch; |
| 491 | 469 | propagate_worm(pos); |
| 492 | 470 | } |
| | 471 | else { |
| | 472 | /* Identify INESSENTIAL strings. */ |
| | 473 | if (!worm[pos].genus |
| | 474 | && !worm[pos].liberties2 |
| | 475 | && !worm[pos].cutstone) { |
| | 476 | if (examine_cavity(pos, &edge) != GRAY && edge < 3) { |
| | 477 | DEBUG(DEBUG_WORMS, "Worm %1m identified as inessential.\n", pos); |
| | 478 | worm[pos].inessential = 1; |
| | 479 | propagate_worm(pos); |
| | 480 | } |
| | 481 | } |
| | 482 | } |
| 493 | 483 | } |
| 494 | 484 | } |
| 495 | 485 | } |
| … |
… |
|
| 746 | 736 | TRACE("considering attack of %1m\n", str); |
| 747 | 737 | |
| 748 | 738 | acode = attack(str, &attack_point); |
| 749 | | if (acode != 0) { |
| | 739 | if (acode) { |
| 750 | 740 | DEBUG(DEBUG_WORMS, "worm at %1m can be attacked at %1m\n", |
| 751 | 741 | str, attack_point); |
| 752 | 742 | change_attack(str, attack_point, acode); |
| … |
… |
|
| 1601 | 1591 | cur_el = pattern->patn; |
| 1602 | 1592 | last_el = cur_el + pattern->patlen; |
| 1603 | 1593 | for (; cur_el < last_el; ++cur_el) { /* match each point */ |
| | 1594 | int pos; |
| | 1595 | int str; |
| 1604 | 1596 | if (cur_el->att == ATT_X) { |
| 1605 | 1597 | /* transform pattern real coordinate */ |
| 1606 | | int pos = AFFINE_TRANSFORM(cur_el->offset, ll, anchor); |
| | 1598 | pos = AFFINE_TRANSFORM(cur_el->offset, ll, anchor); |
| 1607 | 1599 | |
| 1608 | | int str = worm[pos].origin; |
| | 1600 | str = worm[pos].origin; |
| 1609 | 1601 | |
| 1610 | 1602 | /* A string with 5 liberties or more is considered tactically alive. */ |
| 1611 | 1603 | if (countlib(str) > 4) |
| … |
… |
|
| 1699 | 1691 | cur_el = pattern->patn; |
| 1700 | 1692 | last_el = cur_el + pattern->patlen; |
| 1701 | 1693 | for (; cur_el < last_el; ++cur_el) { /* match each point */ |
| | 1694 | int pos; |
| | 1695 | int str; |
| 1702 | 1696 | if (cur_el->att == ATT_O) { |
| 1703 | 1697 | /* transform pattern real coordinate */ |
| 1704 | | int pos = AFFINE_TRANSFORM(cur_el->offset, ll, anchor); |
| 1705 | | int str = worm[pos].origin; |
| | 1698 | pos = AFFINE_TRANSFORM(cur_el->offset, ll, anchor); |
| | 1699 | str = worm[pos].origin; |
| 1706 | 1700 | |
| 1707 | | if (worm[str].attack_codes[0] == 0 |
| | 1701 | if (!worm[str].attack_codes[0] |
| 1708 | 1702 | || defense_move_known(move, str) |
| 1709 | 1703 | || discarded_move_known(move, MAX_TACTICAL_POINTS, |
| 1710 | 1704 | worm[str].discarded_defenses)) |