| 1 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ |
| 2 | * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see * |
| 3 | * http://www.gnu.org/software/gnugo/ for more information. * |
| 4 | * * |
| 5 | * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * |
| 6 | * 2008 and 2009 by the Free Software Foundation. * |
| 7 | * * |
| 8 | * This program is free software; you can redistribute it and/or * |
| 9 | * modify it under the terms of the GNU General Public License as * |
| 10 | * published by the Free Software Foundation - version 3 or * |
| 11 | * (at your option) any later version. * |
| 12 | * * |
| 13 | * This program is distributed in the hope that it will be useful, * |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
| 16 | * GNU General Public License in file COPYING for more details. * |
| 17 | * * |
| 18 | * You should have received a copy of the GNU General Public * |
| 19 | * License along with this program; if not, write to the Free * |
| 20 | * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * |
| 21 | * Boston, MA 02111, USA. * |
| 22 | \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ |
| 23 | |
| 24 | #include "gnugo.h" |
| 25 | |
| 26 | #include <stdio.h> |
| 27 | #include <stdlib.h> |
| 28 | |
| 29 | #include "liberty.h" |
| 30 | |
| 31 | #define INFINITY 1000 |
| 32 | |
| 33 | static void find_moves_to_make_seki(void); |
| 34 | static void update_status(int dr, enum dragon_status new_status, |
| 35 | enum dragon_status new_safety); |
| 36 | static int close_enough_for_proper_semeai(int apos, int bpos); |
| 37 | |
| 38 | /* semeai() searches for pairs of dragons of opposite color which |
| 39 | * have safety DEAD. If such a pair is found, owl_analyze_semeai is |
| 40 | * called to read out which dragon will prevail in a semeai, and |
| 41 | * whether a move now will make a difference in the outcome. The |
| 42 | * dragon statuses are revised, and if a move now will make a |
| 43 | * difference in the outcome this information is stored in |
| 44 | * dragon_data2 and an owl reason is later generated by |
| 45 | * semeai_move_reasons(). |
| 46 | */ |
| 47 | |
| 48 | #define MAX_DRAGONS 50 |
| 49 | |
| 50 | void |
| 51 | semeai() |
| 52 | { |
| 53 | int semeai_results_first[MAX_DRAGONS][MAX_DRAGONS]; |
| 54 | int semeai_results_second[MAX_DRAGONS][MAX_DRAGONS]; |
| 55 | int semeai_move[MAX_DRAGONS][MAX_DRAGONS]; |
| 56 | signed char semeai_certain[MAX_DRAGONS][MAX_DRAGONS]; |
| 57 | int d1, d2; |
| 58 | int k; |
| 59 | int num_dragons = number_of_dragons; |
| 60 | |
| 61 | if (num_dragons > MAX_DRAGONS) { |
| 62 | TRACE("Too many dragons!!! Semeai analysis disabled."); |
| 63 | return; |
| 64 | } |
| 65 | |
| 66 | for (d1 = 0; d1 < num_dragons; d1++) |
| 67 | for (d2 = 0; d2 < num_dragons; d2++) { |
| 68 | semeai_results_first[d1][d2] = -1; |
| 69 | semeai_results_second[d1][d2] = -1; |
| 70 | } |
| 71 | |
| 72 | for (d1 = 0; d1 < num_dragons; d1++) |
| 73 | for (k = 0; k < dragon2[d1].neighbors; k++) { |
| 74 | int apos = DRAGON(d1).origin; |
| 75 | int bpos = DRAGON(dragon2[d1].adjacent[k]).origin; |
| 76 | int result_certain; |
| 77 | |
| 78 | d2 = dragon[bpos].id; |
| 79 | |
| 80 | /* Look for semeais */ |
| 81 | |
| 82 | if (dragon[apos].color == dragon[bpos].color |
| 83 | || (dragon[apos].status != DEAD |
| 84 | && dragon[apos].status != CRITICAL) |
| 85 | || (dragon[bpos].status != DEAD |
| 86 | && dragon[bpos].status != CRITICAL)) |
| 87 | continue; |
| 88 | |
| 89 | |
| 90 | /* Ignore inessential worms or dragons */ |
| 91 | |
| 92 | if (worm[apos].inessential |
| 93 | || DRAGON2(apos).safety == INESSENTIAL |
| 94 | || worm[bpos].inessential |
| 95 | || DRAGON2(bpos).safety == INESSENTIAL) |
| 96 | continue; |
| 97 | |
| 98 | /* Sometimes the dragons are considered neighbors but are too |
| 99 | * distant to constitute a proper semeai, e.g. in nngs4:650, P2 |
| 100 | * vs. R3. Then the result of semeai reading may be meaningless |
| 101 | * and can confuse the analysis. In order to avoid this we check |
| 102 | * that the dragons either are directly adjacent or at least |
| 103 | * have one common liberty. |
| 104 | */ |
| 105 | if (!close_enough_for_proper_semeai(apos, bpos)) |
| 106 | continue; |
| 107 | |
| 108 | /* The array semeai_results_first[d1][d2] will contain the status |
| 109 | * of d1 after the d1 d2 semeai, giving d1 the first move. |
| 110 | * The array semeai_results_second[d1][d2] will contain the status |
| 111 | * of d1 after the d1 d2 semeai, giving d2 the first move. |
| 112 | */ |
| 113 | |
| 114 | DEBUG(DEBUG_SEMEAI, "Considering semeai between %1m and %1m\n", |
| 115 | apos, bpos); |
| 116 | owl_analyze_semeai(apos, bpos, |
| 117 | &(semeai_results_first[d1][d2]), |
| 118 | &(semeai_results_second[d1][d2]), |
| 119 | &(semeai_move[d1][d2]), 1, &result_certain); |
| 120 | DEBUG(DEBUG_SEMEAI, "results if %s moves first: %s %s, %1m%s\n", |
| 121 | board[apos] == BLACK ? "black" : "white", |
| 122 | result_to_string(semeai_results_first[d1][d2]), |
| 123 | result_to_string(semeai_results_second[d1][d2]), |
| 124 | semeai_move[d1][d2], result_certain ? "" : " (uncertain)"); |
| 125 | semeai_certain[d1][d2] = result_certain; |
| 126 | } |
| 127 | |
| 128 | /* Look for dragons which lose all their semeais outright. The |
| 129 | * winners in those semeais are considered safe and further semeais |
| 130 | * they are involved in are disregarded. See semeai:81-86 and |
| 131 | * nicklas5:1211 for examples of where this is useful. |
| 132 | * |
| 133 | * Note: To handle multiple simultaneous semeais properly we would |
| 134 | * have to make simultaneous semeai reading. Lacking that we can |
| 135 | * only get rough guesses of the correct status of the involved |
| 136 | * dragons. This code is not guaranteed to be correct in all |
| 137 | * situations but should usually be an improvement. |
| 138 | */ |
| 139 | for (d1 = 0; d1 < num_dragons; d1++) { |
| 140 | int involved_in_semeai = 0; |
| 141 | int all_lost = 1; |
| 142 | for (d2 = 0; d2 < num_dragons; d2++) { |
| 143 | if (semeai_results_first[d1][d2] != -1) { |
| 144 | involved_in_semeai = 1; |
| 145 | if (semeai_results_first[d1][d2] != 0) { |
| 146 | all_lost = 0; |
| 147 | break; |
| 148 | } |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | if (involved_in_semeai && all_lost) { |
| 153 | /* Leave the status changes to the main loop below. Here we just |
| 154 | * remove the presumably irrelevant semeai results. |
| 155 | */ |
| 156 | for (d2 = 0; d2 < num_dragons; d2++) { |
| 157 | if (semeai_results_first[d1][d2] == 0) { |
| 158 | int d3; |
| 159 | for (d3 = 0; d3 < num_dragons; d3++) { |
| 160 | if (semeai_results_second[d3][d2] > 0) { |
| 161 | semeai_results_first[d3][d2] = -1; |
| 162 | semeai_results_second[d3][d2] = -1; |
| 163 | semeai_results_first[d2][d3] = -1; |
| 164 | semeai_results_second[d2][d3] = -1; |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | for (d1 = 0; d1 < num_dragons; d1++) { |
| 173 | int semeais_found = 0; |
| 174 | int best_defense = 0; |
| 175 | int best_attack = 0; |
| 176 | int defense_move = PASS_MOVE; |
| 177 | int attack_move = PASS_MOVE; |
| 178 | int defense_certain = -1; |
| 179 | int attack_certain = -1; |
| 180 | int semeai_attack_target = NO_MOVE; |
| 181 | int semeai_defense_target = NO_MOVE; |
| 182 | |
| 183 | for (d2 = 0; d2 < num_dragons; d2++) { |
| 184 | if (semeai_results_first[d1][d2] == -1) |
| 185 | continue; |
| 186 | gg_assert(semeai_results_second[d1][d2] != -1); |
| 187 | semeais_found++; |
| 188 | |
| 189 | if (best_defense < semeai_results_first[d1][d2] |
| 190 | || (best_defense == semeai_results_first[d1][d2] |
| 191 | && defense_certain < semeai_certain[d1][d2])) { |
| 192 | best_defense = semeai_results_first[d1][d2]; |
| 193 | defense_move = semeai_move[d1][d2]; |
| 194 | defense_certain = semeai_certain[d1][d2]; |
| 195 | gg_assert(board[dragon2[d2].origin] == OTHER_COLOR(board[dragon2[d1].origin])); |
| 196 | semeai_defense_target = dragon2[d2].origin; |
| 197 | } |
| 198 | if (best_attack < semeai_results_second[d2][d1] |
| 199 | || (best_attack == semeai_results_second[d2][d1] |
| 200 | && attack_certain < semeai_certain[d2][d1])) { |
| 201 | best_attack = semeai_results_second[d2][d1]; |
| 202 | attack_move = semeai_move[d2][d1]; |
| 203 | attack_certain = semeai_certain[d2][d1]; |
| 204 | semeai_attack_target = dragon2[d2].origin; |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | if (semeais_found) { |
| 209 | dragon2[d1].semeais = semeais_found; |
| 210 | if (best_defense != 0 && best_attack != 0) |
| 211 | update_status(DRAGON(d1).origin, CRITICAL, CRITICAL); |
| 212 | else if (best_attack == 0 && attack_certain) |
| 213 | update_status(DRAGON(d1).origin, ALIVE, ALIVE); |
| 214 | dragon2[d1].semeai_defense_code = best_defense; |
| 215 | dragon2[d1].semeai_defense_point = defense_move; |
| 216 | dragon2[d1].semeai_defense_certain = defense_certain; |
| 217 | ASSERT1(board[semeai_defense_target] |
| 218 | == OTHER_COLOR(board[dragon2[d1].origin]), |
| 219 | dragon2[d1].origin); |
| 220 | dragon2[d1].semeai_defense_target = semeai_defense_target; |
| 221 | dragon2[d1].semeai_attack_code = best_attack; |
| 222 | dragon2[d1].semeai_attack_point = attack_move; |
| 223 | dragon2[d1].semeai_attack_certain = attack_certain; |
| 224 | dragon2[d1].semeai_attack_target = semeai_attack_target; |
| 225 | } |
| 226 | } |
| 227 | find_moves_to_make_seki(); |
| 228 | } |
| 229 | |
| 230 | /* Find moves turning supposed territory into seki. This is not |
| 231 | * detected above since it either involves an ALIVE dragon adjacent to |
| 232 | * a CRITICAL dragon, or an ALIVE dragon whose eyespace can be invaded |
| 233 | * and turned into a seki. |
| 234 | * |
| 235 | * Currently we only search for tactically critical strings with |
| 236 | * dragon status dead, which are neighbors of only one opponent |
| 237 | * dragon, which is alive. Through semeai analysis we then determine |
| 238 | * whether such a string can in fact live in seki. Relevant testcases |
| 239 | * include gunnar:42 and gifu03:2. |
| 240 | */ |
| 241 | static void |
| 242 | find_moves_to_make_seki() |
| 243 | { |
| 244 | int str; |
| 245 | int defend_move; |
| 246 | int resulta, resultb; |
| 247 | |
| 248 | for (str = BOARDMIN; str < BOARDMAX; str++) { |
| 249 | if (IS_STONE(board[str]) && is_worm_origin(str, str) |
| 250 | && attack_and_defend(str, NULL, NULL, NULL, &defend_move) |
| 251 | && dragon[str].status == DEAD |
| 252 | && DRAGON2(str).hostile_neighbors == 1) { |
| 253 | int k; |
| 254 | int color = board[str]; |
| 255 | int opponent = NO_MOVE; |
| 256 | int certain; |
| 257 | struct eyevalue reduced_genus; |
| 258 | |
| 259 | for (k = 0; k < DRAGON2(str).neighbors; k++) { |
| 260 | opponent = dragon2[DRAGON2(str).adjacent[k]].origin; |
| 261 | if (board[opponent] != color) |
| 262 | break; |
| 263 | } |
| 264 | |
| 265 | ASSERT1(opponent != NO_MOVE, opponent); |
| 266 | |
| 267 | if (dragon[opponent].status != ALIVE) |
| 268 | continue; |
| 269 | |
| 270 | /* FIXME: These heuristics are used for optimization. We don't |
| 271 | * want to call expensive semeai code if the opponent |
| 272 | * dragon has more than one eye elsewhere. However, the |
| 273 | * heuristics might still need improvement. |
| 274 | */ |
| 275 | compute_dragon_genus(opponent, &reduced_genus, str); |
| 276 | |
| 277 | if (min_eyes(&reduced_genus) > 1 |
| 278 | || DRAGON2(opponent).moyo_size > 10 |
| 279 | || DRAGON2(opponent).moyo_territorial_value > 2.999 |
| 280 | || DRAGON2(opponent).escape_route > 0 |
| 281 | || DRAGON2(str).escape_route > 0) |
| 282 | continue; |
| 283 | |
| 284 | owl_analyze_semeai_after_move(defend_move, color, opponent, str, |
| 285 | &resulta, &resultb, NULL, 1, &certain, 0); |
| 286 | |
| 287 | if (resultb == WIN) { |
| 288 | owl_analyze_semeai(str, opponent, &resultb, &resulta, |
| 289 | &defend_move, 1, &certain); |
| 290 | resulta = REVERSE_RESULT(resulta); |
| 291 | resultb = REVERSE_RESULT(resultb); |
| 292 | } |
| 293 | |
| 294 | /* Do not trust uncertain results. In fact it should only take a |
| 295 | * few nodes to determine the semeai result, if it is a proper |
| 296 | * potential seki position. |
| 297 | */ |
| 298 | if (resultb != WIN && certain) { |
| 299 | int d = dragon[str].id; |
| 300 | DEBUG(DEBUG_SEMEAI, "Move to make seki at %1m (%1m vs %1m)\n", |
| 301 | defend_move, str, opponent); |
| 302 | dragon2[d].semeais++; |
| 303 | update_status(str, CRITICAL, CRITICAL); |
| 304 | dragon2[d].semeai_defense_code = REVERSE_RESULT(resultb); |
| 305 | dragon2[d].semeai_defense_point = defend_move; |
| 306 | dragon2[d].semeai_defense_certain = certain; |
| 307 | gg_assert(board[opponent] == OTHER_COLOR(board[dragon2[d].origin])); |
| 308 | dragon2[d].semeai_defense_target = opponent; |
| 309 | |
| 310 | /* We need to determine a proper attack move (the one that |
| 311 | * prevents seki). Currently we try the defense move first, |
| 312 | * and if it doesn't work -- all liberties of the string. |
| 313 | */ |
| 314 | owl_analyze_semeai_after_move(defend_move, OTHER_COLOR(color), |
| 315 | str, opponent, &resulta, NULL, |
| 316 | NULL, 1, NULL, 0); |
| 317 | if (resulta != WIN) { |
| 318 | dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta); |
| 319 | dragon2[d].semeai_attack_point = defend_move; |
| 320 | } |
| 321 | else { |
| 322 | int k; |
| 323 | int libs[MAXLIBS]; |
| 324 | int liberties = findlib(str, MAXLIBS, libs); |
| 325 | |
| 326 | for (k = 0; k < liberties; k++) { |
| 327 | owl_analyze_semeai_after_move(libs[k], OTHER_COLOR(color), |
| 328 | str, opponent, &resulta, NULL, |
| 329 | NULL, 1, NULL, 0); |
| 330 | if (resulta != WIN) { |
| 331 | dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta); |
| 332 | dragon2[d].semeai_attack_point = libs[k]; |
| 333 | break; |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | if (k == liberties) { |
| 338 | DEBUG(DEBUG_SEMEAI, |
| 339 | "No move to attack in semeai (%1m vs %1m), seki assumed.\n", |
| 340 | str, opponent); |
| 341 | dragon2[d].semeai_attack_code = 0; |
| 342 | dragon2[d].semeai_attack_point = NO_MOVE; |
| 343 | update_status(str, ALIVE, ALIVE_IN_SEKI); |
| 344 | } |
| 345 | } |
| 346 | |
| 347 | DEBUG(DEBUG_SEMEAI, "Move to prevent seki at %1m (%1m vs %1m)\n", |
| 348 | dragon2[d].semeai_attack_point, opponent, str); |
| 349 | |
| 350 | dragon2[d].semeai_attack_certain = certain; |
| 351 | dragon2[d].semeai_attack_target = opponent; |
| 352 | } |
| 353 | } |
| 354 | } |
| 355 | |
| 356 | /* Now look for dead strings inside a single eyespace of a living dragon. |
| 357 | * |
| 358 | * FIXME: Clearly this loop should share most of its code with the |
| 359 | * one above. It would also be good to reimplement so that |
| 360 | * moves invading a previously empty single eyespace to make |
| 361 | * seki can be found. |
| 362 | */ |
| 363 | for (str = BOARDMIN; str < BOARDMAX; str++) { |
| 364 | if (IS_STONE(board[str]) && is_worm_origin(str, str) |
| 365 | && !find_defense(str, NULL) |
| 366 | && dragon[str].status == DEAD |
| 367 | && DRAGON2(str).hostile_neighbors == 1) { |
| 368 | int k; |
| 369 | int color = board[str]; |
| 370 | int opponent = NO_MOVE; |
| 371 | int certain; |
| 372 | struct eyevalue reduced_genus; |
| 373 | |
| 374 | for (k = 0; k < DRAGON2(str).neighbors; k++) { |
| 375 | opponent = dragon2[DRAGON2(str).adjacent[k]].origin; |
| 376 | if (board[opponent] != color) |
| 377 | break; |
| 378 | } |
| 379 | |
| 380 | ASSERT1(opponent != NO_MOVE, opponent); |
| 381 | |
| 382 | if (dragon[opponent].status != ALIVE) |
| 383 | continue; |
| 384 | |
| 385 | /* FIXME: These heuristics are used for optimization. We don't |
| 386 | * want to call expensive semeai code if the opponent |
| 387 | * dragon has more than one eye elsewhere. However, the |
| 388 | * heuristics might still need improvement. |
| 389 | */ |
| 390 | compute_dragon_genus(opponent, &reduced_genus, str); |
| 391 | if (DRAGON2(opponent).moyo_size > 10 || min_eyes(&reduced_genus) > 1) |
| 392 | continue; |
| 393 | |
| 394 | owl_analyze_semeai(str, opponent, &resulta, &resultb, |
| 395 | &defend_move, 1, &certain); |
| 396 | |
| 397 | /* Do not trust uncertain results. In fact it should only take a |
| 398 | * few nodes to determine the semeai result, if it is a proper |
| 399 | * potential seki position. |
| 400 | */ |
| 401 | if (resulta != 0 && certain) { |
| 402 | int d = dragon[str].id; |
| 403 | DEBUG(DEBUG_SEMEAI, "Move to make seki at %1m (%1m vs %1m)\n", |
| 404 | defend_move, str, opponent); |
| 405 | dragon2[d].semeais++; |
| 406 | update_status(str, CRITICAL, CRITICAL); |
| 407 | dragon2[d].semeai_defense_code = resulta; |
| 408 | dragon2[d].semeai_defense_point = defend_move; |
| 409 | dragon2[d].semeai_defense_certain = certain; |
| 410 | gg_assert(board[opponent] == OTHER_COLOR(board[dragon2[d].origin])); |
| 411 | dragon2[d].semeai_defense_target = opponent; |
| 412 | |
| 413 | /* We need to determine a proper attack move (the one that |
| 414 | * prevents seki). Currently we try the defense move first, |
| 415 | * and if it doesn't work -- all liberties of the string. |
| 416 | */ |
| 417 | owl_analyze_semeai_after_move(defend_move, OTHER_COLOR(color), |
| 418 | str, opponent, &resulta, NULL, |
| 419 | NULL, 1, NULL, 0); |
| 420 | if (resulta != WIN) { |
| 421 | dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta); |
| 422 | dragon2[d].semeai_attack_point = defend_move; |
| 423 | } |
| 424 | else { |
| 425 | int k; |
| 426 | int libs[MAXLIBS]; |
| 427 | int liberties = findlib(str, MAXLIBS, libs); |
| 428 | |
| 429 | for (k = 0; k < liberties; k++) { |
| 430 | owl_analyze_semeai_after_move(libs[k], OTHER_COLOR(color), |
| 431 | str, opponent, &resulta, NULL, |
| 432 | NULL, 1, NULL, 0); |
| 433 | if (resulta != WIN) { |
| 434 | dragon2[d].semeai_attack_code = REVERSE_RESULT(resulta); |
| 435 | dragon2[d].semeai_attack_point = libs[k]; |
| 436 | break; |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | if (k == liberties) { |
| 441 | DEBUG(DEBUG_SEMEAI, |
| 442 | "No move to attack in semeai (%1m vs %1m), seki assumed.\n", |
| 443 | str, opponent); |
| 444 | dragon2[d].semeai_attack_code = 0; |
| 445 | dragon2[d].semeai_attack_point = NO_MOVE; |
| 446 | update_status(str, ALIVE, ALIVE_IN_SEKI); |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | DEBUG(DEBUG_SEMEAI, "Move to prevent seki at %1m (%1m vs %1m)\n", |
| 451 | dragon2[d].semeai_attack_point, opponent, str); |
| 452 | |
| 453 | dragon2[d].semeai_attack_certain = certain; |
| 454 | dragon2[d].semeai_attack_target = opponent; |
| 455 | } |
| 456 | } |
| 457 | } |
| 458 | } |
| 459 | |
| 460 | |
| 461 | /* neighbor_of_dragon(pos, origin) returns true if the vertex at (pos) is a |
| 462 | * neighbor of the dragon with origin at (origin). |
| 463 | */ |
| 464 | static int |
| 465 | neighbor_of_dragon(int pos, int origin) |
| 466 | { |
| 467 | int k; |
| 468 | if (pos == NO_MOVE) |
| 469 | return 0; |
| 470 | |
| 471 | for (k = 0; k < 4; k++) |
| 472 | if (ON_BOARD(pos + delta[k]) && dragon[pos + delta[k]].origin == origin) |
| 473 | return 1; |
| 474 | |
| 475 | return 0; |
| 476 | } |
| 477 | |
| 478 | /* Check whether two dragons are directly adjacent or have at least |
| 479 | * one common liberty. |
| 480 | */ |
| 481 | static int |
| 482 | close_enough_for_proper_semeai(int apos, int bpos) |
| 483 | { |
| 484 | int pos; |
| 485 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { |
| 486 | if (board[pos] == EMPTY |
| 487 | && neighbor_of_dragon(pos, apos) |
| 488 | && neighbor_of_dragon(pos, bpos)) |
| 489 | return 1; |
| 490 | else if (IS_STONE(board[pos])) { |
| 491 | if (is_same_dragon(pos, apos) && neighbor_of_dragon(pos, bpos)) |
| 492 | return 1; |
| 493 | if (is_same_dragon(pos, bpos) && neighbor_of_dragon(pos, apos)) |
| 494 | return 1; |
| 495 | } |
| 496 | } |
| 497 | |
| 498 | return 0; |
| 499 | } |
| 500 | |
| 501 | /* This function adds the semeai related move reasons, using the information |
| 502 | * stored in the dragon2 array. |
| 503 | * |
| 504 | * If the semeai had an uncertain result, and there is a owl move with |
| 505 | * certain result doing the same, we don't trust the semeai move. |
| 506 | */ |
| 507 | void |
| 508 | semeai_move_reasons(int color) |
| 509 | { |
| 510 | int other = OTHER_COLOR(color); |
| 511 | int d; |
| 512 | int liberties; |
| 513 | int libs[MAXLIBS]; |
| 514 | int r; |
| 515 | |
| 516 | for (d = 0; d < number_of_dragons; d++) |
| 517 | if (dragon2[d].semeais && DRAGON(d).status == CRITICAL) { |
| 518 | if (DRAGON(d).color == color |
| 519 | && dragon2[d].semeai_defense_point |
| 520 | && (dragon2[d].owl_defense_point == NO_MOVE |
| 521 | || dragon2[d].semeai_defense_certain >= |
| 522 | dragon2[d].owl_defense_certain)) { |
| 523 | /* My dragon can be defended. */ |
| 524 | add_semeai_move(dragon2[d].semeai_defense_point, dragon2[d].origin); |
| 525 | DEBUG(DEBUG_SEMEAI, "Adding semeai defense move for %1m at %1m\n", |
| 526 | DRAGON(d).origin, dragon2[d].semeai_defense_point); |
| 527 | if (neighbor_of_dragon(dragon2[d].semeai_defense_point, |
| 528 | dragon2[d].semeai_defense_target) |
| 529 | && !neighbor_of_dragon(dragon2[d].semeai_defense_point, |
| 530 | dragon2[d].origin) |
| 531 | && !is_self_atari(dragon2[d].semeai_defense_point, color)) { |
| 532 | |
| 533 | /* If this is a move to fill the non-common liberties of the |
| 534 | * target, and is not a ko or snap-back, then we mark all |
| 535 | * non-common liberties of the target as potential semeai moves. |
| 536 | */ |
| 537 | |
| 538 | liberties = findlib(dragon2[d].semeai_defense_target, MAXLIBS, libs); |
| 539 | |
| 540 | for (r = 0; r < liberties; r++) { |
| 541 | if (!neighbor_of_dragon(libs[r], dragon2[d].origin) |
| 542 | && !is_self_atari(libs[r], color) |
| 543 | && libs[r] != dragon2[d].semeai_defense_point) |
| 544 | add_potential_semeai_defense(libs[r], dragon2[d].origin, |
| 545 | dragon2[d].semeai_defense_target); |
| 546 | } |
| 547 | } |
| 548 | } |
| 549 | else if (DRAGON(d).color == other |
| 550 | && dragon2[d].semeai_attack_point |
| 551 | && (dragon2[d].owl_attack_point == NO_MOVE |
| 552 | || dragon2[d].owl_defense_point == NO_MOVE |
| 553 | || dragon2[d].semeai_attack_certain >= |
| 554 | dragon2[d].owl_attack_certain)) { |
| 555 | /* Your dragon can be attacked. */ |
| 556 | add_semeai_move(dragon2[d].semeai_attack_point, dragon2[d].origin); |
| 557 | DEBUG(DEBUG_SEMEAI, "Adding semeai attack move for %1m at %1m\n", |
| 558 | DRAGON(d).origin, dragon2[d].semeai_attack_point); |
| 559 | if (neighbor_of_dragon(dragon2[d].semeai_attack_point, |
| 560 | dragon2[d].origin) |
| 561 | && !neighbor_of_dragon(dragon2[d].semeai_attack_point, |
| 562 | dragon2[d].semeai_attack_target) |
| 563 | && !is_self_atari(dragon2[d].semeai_attack_point, color)) { |
| 564 | |
| 565 | liberties = findlib(dragon2[d].origin, MAXLIBS, libs); |
| 566 | |
| 567 | for (r = 0; r < liberties; r++) { |
| 568 | if (!neighbor_of_dragon(libs[r], dragon2[d].semeai_attack_target) |
| 569 | && !is_self_atari(libs[r], color) |
| 570 | && libs[r] != dragon2[d].semeai_attack_point) |
| 571 | add_potential_semeai_attack(libs[r], dragon2[d].origin, |
| 572 | dragon2[d].semeai_attack_target); |
| 573 | } |
| 574 | } |
| 575 | } |
| 576 | } |
| 577 | } |
| 578 | |
| 579 | |
| 580 | /* Change the status and safety of a dragon. In addition, if the new |
| 581 | * status is not DEAD, make all worms of the dragon essential, so that |
| 582 | * results found by semeai code don't get ignored. |
| 583 | */ |
| 584 | static void |
| 585 | update_status(int dr, enum dragon_status new_status, |
| 586 | enum dragon_status new_safety) |
| 587 | { |
| 588 | int pos; |
| 589 | |
| 590 | if (dragon[dr].status != new_status |
| 591 | && (dragon[dr].status != CRITICAL || new_status != DEAD)) { |
| 592 | DEBUG(DEBUG_SEMEAI, "Changing status of %1m from %s to %s.\n", dr, |
| 593 | status_to_string(dragon[dr].status), |
| 594 | status_to_string(new_status)); |
| 595 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) |
| 596 | if (IS_STONE(board[pos]) && is_same_dragon(dr, pos)) { |
| 597 | dragon[pos].status = new_status; |
| 598 | if (new_status != DEAD) |
| 599 | worm[pos].inessential = 0; |
| 600 | } |
| 601 | } |
| 602 | |
| 603 | if (DRAGON2(dr).safety != new_safety |
| 604 | && (DRAGON2(dr).safety != CRITICAL || new_safety != DEAD)) { |
| 605 | DEBUG(DEBUG_SEMEAI, "Changing safety of %1m from %s to %s.\n", dr, |
| 606 | status_to_string(DRAGON2(dr).safety), status_to_string(new_safety)); |
| 607 | DRAGON2(dr).safety = new_safety; |
| 608 | } |
| 609 | } |
| 610 | |
| 611 | |
| 612 | /* |
| 613 | * Local Variables: |
| 614 | * tab-width: 8 |
| 615 | * c-basic-offset: 2 |
| 616 | * End: |
| 617 | */ |