Commit | Line | Data |
---|---|---|
7eeb782e AT |
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 | */ |