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 | /* | |
25 | * The code in this file implements "Optics With Limit-negotiation (OWL)." | |
26 | * | |
27 | * The life and death code in optics.c, works reasonably well as long as the | |
28 | * position is in a *terminal position*, which we define to be one where there | |
29 | * are no moves left which can expand the eye space, or limit it. In | |
30 | * situations where the dragon is surrounded, yet has room to thrash around a | |
31 | * bit making eyes, a simple application of the graph-based analysis will not | |
32 | * work. Instead, a bit of reading is needed to reach a terminal position. | |
33 | * The defender tries to expand his eyespace, the attacker to limit it, and | |
34 | * when neither finds an effective move, the position is evaluated. We call | |
35 | * this type of life and death reading *Optics With Limit-negotiation* (OWL). | |
36 | * | |
37 | * (|__|) | |
38 | * (@)(@)) | |
39 | * |:v:: | | |
40 | * ( ) | |
41 | * \| |/ | |
42 | * =#===#= | |
43 | * /___/ | |
44 | * | |
45 | * The owl is noted for its keen vision | |
46 | * and (purported) wisdom. | |
47 | */ | |
48 | ||
49 | #include "gnugo.h" | |
50 | ||
51 | #include <stdio.h> | |
52 | #include <stdlib.h> | |
53 | #include <string.h> | |
54 | #include "liberty.h" | |
55 | #include "readconnect.h" | |
56 | #include "patterns.h" | |
57 | #include "cache.h" | |
58 | #include "sgftree.h" | |
59 | #include "gg_utils.h" | |
60 | ||
61 | #define MAX_MOVES 3 /* maximum number of branches at each node */ | |
62 | #define MAX_SEMEAI_MOVES 6 /* semeai branch factor */ | |
63 | #define MAX_SEMEAI_DEPTH 100 /* Don't read below this depth */ | |
64 | #define MAX_LUNCHES 10 | |
65 | #define MAX_GOAL_WORMS 15 /* maximum number of worms in a dragon to be */ | |
66 | /* cataloged. NOTE: Must fit in value2 in hashnode! */ | |
67 | #define MAX_ESCAPE 3 /* After this many escape moves, owl_determine_life is */ | |
68 | /* not called */ | |
69 | ||
70 | struct local_owl_data { | |
71 | signed char goal[BOARDMAX]; | |
72 | signed char boundary[BOARDMAX]; | |
73 | /* Same as goal, except never anything is removed from it. */ | |
74 | signed char cumulative_goal[BOARDMAX]; | |
75 | ||
76 | /* FIXME: neighbors[] and escape_values[] are never recomputed. | |
77 | * Consider moving these arrays from stack to a static or | |
78 | * dynamic variable so it is not copied around in | |
79 | * do_push_owl(). Be aware of semeai code though. | |
80 | */ | |
81 | signed char neighbors[BOARDMAX]; | |
82 | ||
83 | signed char escape_values[BOARDMAX]; | |
84 | int color; | |
85 | ||
86 | struct eye_data my_eye[BOARDMAX]; | |
87 | /* array of half-eye data for use during owl reading */ | |
88 | struct half_eye_data half_eye[BOARDMAX]; | |
89 | ||
90 | int lunch[MAX_LUNCHES]; | |
91 | int lunch_attack_code[MAX_LUNCHES]; | |
92 | int lunch_attack_point[MAX_LUNCHES]; | |
93 | int lunch_defend_code[MAX_LUNCHES]; | |
94 | int lunch_defense_point[MAX_LUNCHES]; | |
95 | signed char inessential[BOARDMAX]; | |
96 | ||
97 | int lunches_are_current; /* If true, owl lunch data is current */ | |
98 | ||
99 | signed char safe_move_cache[BOARDMAX]; | |
100 | ||
101 | /* This is used to organize the owl stack. */ | |
102 | struct local_owl_data *restore_from; | |
103 | }; | |
104 | ||
105 | ||
106 | static int result_certain; | |
107 | ||
108 | /* Statistics. */ | |
109 | static int local_owl_node_counter; | |
110 | /* Node limitation. */ | |
111 | static int global_owl_node_counter = 0; | |
112 | ||
113 | static struct local_owl_data *current_owl_data; | |
114 | static struct local_owl_data *other_owl_data; | |
115 | ||
116 | static int goal_worms_computed = 0; | |
117 | static int owl_goal_worm[MAX_GOAL_WORMS]; | |
118 | ||
119 | ||
120 | #define MAX_CUTS 5 | |
121 | ||
122 | enum same_dragon_value { | |
123 | SAME_DRAGON_NOT_CONNECTED, | |
124 | SAME_DRAGON_MAYBE_CONNECTED, | |
125 | SAME_DRAGON_CONNECTED, | |
126 | SAME_DRAGON_ALL_CONNECTED | |
127 | }; | |
128 | ||
129 | struct matched_pattern_data; | |
130 | ||
131 | struct owl_move_data { | |
132 | int pos; /* move coordinate */ | |
133 | int value; /* value */ | |
134 | const char *name; /* name of the pattern suggesting the move */ | |
135 | /* whether the move extends the dragon or not */ | |
136 | enum same_dragon_value same_dragon; | |
137 | int lunch; /* Position of a lunch, if applicable.*/ | |
138 | int escape; /* true if an escape pattern is matched */ | |
139 | int defense_pos; /* defense coordinate for vital owl attack patterns. */ | |
140 | int cuts[MAX_CUTS]; /* strings of the goal that might get cut off */ | |
141 | /* pointer to pattern data, used for SAME_DRAGON_ALL_CONNECTED */ | |
142 | struct matched_pattern_data *pattern_data; | |
143 | }; | |
144 | ||
145 | #define USE_BDIST 1 | |
146 | ||
147 | struct matched_pattern_data { | |
148 | int move; | |
149 | int value; | |
150 | int ll; | |
151 | int anchor; | |
152 | #if USE_BDIST | |
153 | int bdist; | |
154 | #endif | |
155 | struct pattern *pattern; | |
156 | ||
157 | /* To link combinable patterns in chains. */ | |
158 | int next_pattern_index; | |
159 | }; | |
160 | ||
161 | struct matched_patterns_list_data { | |
162 | int initialized; | |
163 | int counter; /* Number of patterns in the list. */ | |
164 | int used; /* How many patterns have already been used?*/ | |
165 | int list_size; | |
166 | struct matched_pattern_data *pattern_list; | |
167 | int first_pattern_index[BOARDMAX]; | |
168 | ||
169 | int heap_num_patterns; | |
170 | struct matched_pattern_data **pattern_heap; | |
171 | }; | |
172 | ||
173 | void dump_pattern_list(struct matched_patterns_list_data *list); | |
174 | ||
175 | ||
176 | static int do_owl_attack(int str, int *move, int *wormid, | |
177 | struct local_owl_data *owl, int escape); | |
178 | static int do_owl_defend(int str, int *move, int *wormid, | |
179 | struct local_owl_data *owl, int escape); | |
180 | static void owl_shapes(struct matched_patterns_list_data *list, | |
181 | struct owl_move_data moves[MAX_MOVES], int color, | |
182 | struct local_owl_data *owl, struct pattern_db *type); | |
183 | static void collect_owl_shapes_callbacks(int anchor, int color, | |
184 | struct pattern *pattern_db, | |
185 | int ll, void *data); | |
186 | ||
187 | static void pattern_list_prepare(struct matched_patterns_list_data *list); | |
188 | static void pattern_list_build_heap(struct matched_patterns_list_data *list); | |
189 | static void pattern_list_pop_heap_once(struct matched_patterns_list_data *list); | |
190 | static void pattern_list_sink_heap_top_element(struct matched_patterns_list_data | |
191 | *list); | |
192 | ||
193 | static int get_next_move_from_list(struct matched_patterns_list_data *list, | |
194 | int color, struct owl_move_data *moves, | |
195 | int cutoff, struct local_owl_data *owl); | |
196 | static void init_pattern_list(struct matched_patterns_list_data *list); | |
197 | static void close_pattern_list(int color, | |
198 | struct matched_patterns_list_data *list); | |
199 | static void owl_shapes_callback(int anchor, int color, | |
200 | struct pattern *pattern_db, | |
201 | int ll, void *data); | |
202 | static void owl_add_move(struct owl_move_data *moves, int move, int value, | |
203 | const char *reason, | |
204 | enum same_dragon_value same_dragon, int lunch, | |
205 | int escape, int defense_pos, int max_moves, | |
206 | struct matched_pattern_data *pattern_data); | |
207 | static void owl_determine_life(struct local_owl_data *owl, | |
208 | struct local_owl_data *second_owl, | |
209 | int does_attack, | |
210 | struct owl_move_data *moves, | |
211 | struct eyevalue *probable_eyes, | |
212 | int *eyemin, int *eyemax); | |
213 | static void owl_find_relevant_eyespaces(struct local_owl_data *owl, | |
214 | int mw[BOARDMAX], int mz[BOARDMAX]); | |
215 | static int owl_estimate_life(struct local_owl_data *owl, | |
216 | struct local_owl_data *second_owl, | |
217 | struct owl_move_data vital_moves[MAX_MOVES], | |
218 | const char **live_reason, | |
219 | int does_attack, | |
220 | struct eyevalue *probable_eyes, | |
221 | int *eyemin, int *eyemax); | |
222 | static int modify_stupid_eye_vital_point(struct local_owl_data *owl, | |
223 | int *vital_point, | |
224 | int is_attack_point); | |
225 | static int modify_eyefilling_move(int *move, int color); | |
226 | static int estimate_lunch_half_eye_bonus(int lunch, | |
227 | struct half_eye_data half_eye[BOARDMAX]); | |
228 | static void owl_mark_dragon(int apos, int bpos, | |
229 | struct local_owl_data *owl, | |
230 | int new_dragons[BOARDMAX]); | |
231 | static void owl_mark_worm(int apos, int bpos, | |
232 | struct local_owl_data *owl); | |
233 | static void owl_mark_boundary(struct local_owl_data *owl); | |
234 | static void owl_update_goal(int pos, enum same_dragon_value same_dragon, | |
235 | int lunch, struct local_owl_data *owl, | |
236 | int semeai_call, | |
237 | struct matched_pattern_data *pattern_data); | |
238 | static void owl_test_cuts(signed char goal[BOARDMAX], int color, | |
239 | int cuts[MAX_CUTS]); | |
240 | static void componentdump(const signed char component[BOARDMAX]); | |
241 | static void owl_update_boundary_marks(int pos, struct local_owl_data *owl); | |
242 | static void owl_find_lunches(struct local_owl_data *owl); | |
243 | static int improve_lunch_attack(int lunch, int attack_point); | |
244 | static int improve_lunch_defense(int lunch, int defense_point); | |
245 | static void owl_make_domains(struct local_owl_data *owla, | |
246 | struct local_owl_data *owlb); | |
247 | static int owl_safe_move(int move, int color); | |
248 | static void sniff_lunch(int lunch, int *min, int *probable, int *max, | |
249 | struct local_owl_data *owl); | |
250 | static void eat_lunch_escape_bonus(int lunch, int *min, int *probable, | |
251 | int *max, struct local_owl_data *owl); | |
252 | static int select_new_goal_origin(int origin, struct local_owl_data *owl); | |
253 | static void compute_owl_escape_values(struct local_owl_data *owl); | |
254 | static int owl_escape_route(struct local_owl_data *owl); | |
255 | static void do_owl_analyze_semeai(int apos, int bpos, | |
256 | struct local_owl_data *owla, | |
257 | struct local_owl_data *owlb, | |
258 | int *resulta, int *resultb, | |
259 | int *move, int pass, int owl_phase); | |
260 | static int semeai_trymove_and_recurse(int apos, int bpos, | |
261 | struct local_owl_data *owla, | |
262 | struct local_owl_data *owlb, | |
263 | int owl_phase, | |
264 | int move, int color, int ko_allowed, | |
265 | int move_value, const char *move_name, | |
266 | enum same_dragon_value same_dragon, | |
267 | struct matched_pattern_data *pattern_data, | |
268 | int lunch, int *semeai_move, | |
269 | int *this_resulta, int *this_resultb); | |
270 | static void semeai_add_sgf_comment(int value, int owl_phase); | |
271 | static int semeai_trust_tactical_attack(int str); | |
272 | static int semeai_propose_eyespace_filling_move(struct local_owl_data *owla, | |
273 | struct local_owl_data *owlb); | |
274 | static void semeai_review_owl_moves(struct owl_move_data owl_moves[MAX_MOVES], | |
275 | struct local_owl_data *owla, | |
276 | struct local_owl_data *owlb, int color, | |
277 | int *safe_outside_liberty_found, | |
278 | int *safe_common_liberty_found, | |
279 | int *riskless_move_found, | |
280 | signed char mw[BOARDMAX], | |
281 | struct owl_move_data semeai_moves[MAX_SEMEAI_MOVES], | |
282 | int guess_same_dragon, int value_bonus, | |
283 | int *critical_semeai_worms); | |
284 | static int semeai_move_value(int move, struct local_owl_data *owla, | |
285 | struct local_owl_data *owlb, int raw_value, | |
286 | int *critical_semeai_worms); | |
287 | static int semeai_is_riskless_move(int move, struct local_owl_data *owla); | |
288 | static void remove_eye_filling_moves(struct local_owl_data *our_owl, | |
289 | struct owl_move_data *moves); | |
290 | static int find_semeai_backfilling_move(int worm, int liberty); | |
291 | static int liberty_of_goal(int pos, struct local_owl_data *owl); | |
292 | static int second_liberty_of_goal(int pos, struct local_owl_data *owl); | |
293 | static int matches_found; | |
294 | static signed char found_matches[BOARDMAX]; | |
295 | ||
296 | static void reduced_init_owl(struct local_owl_data **owl, | |
297 | int at_bottom_of_stack); | |
298 | static void init_owl(struct local_owl_data **owl, int target1, int target2, | |
299 | int move, int use_stack, int new_dragons[BOARDMAX]); | |
300 | ||
301 | static struct local_owl_data *owl_stack[2 * MAXSTACK]; | |
302 | static int owl_stack_size = 0; | |
303 | static int owl_stack_pointer = 0; | |
304 | static void check_owl_stack_size(void); | |
305 | static void push_owl(struct local_owl_data **owl); | |
306 | static void do_push_owl(struct local_owl_data **owl); | |
307 | static void pop_owl(struct local_owl_data **owl); | |
308 | ||
309 | #if 0 | |
310 | static int catalog_goal(struct local_owl_data *owl, | |
311 | int goal_worm[MAX_GOAL_WORMS]); | |
312 | #endif | |
313 | ||
314 | static int list_goal_worms(struct local_owl_data *owl, | |
315 | int goal_worm[MAX_GOAL_WORMS]); | |
316 | ||
317 | /* FIXME: taken from move_reasons.h */ | |
318 | #define MAX_DRAGONS 2 * MAX_BOARD * MAX_BOARD / 3 | |
319 | ||
320 | static int dragon_goal_worms[MAX_DRAGONS][MAX_GOAL_WORMS]; | |
321 | ||
322 | static void | |
323 | prepare_goal_list(int str, struct local_owl_data *owl, | |
324 | int list[MAX_GOAL_WORMS], int *flag, int *kworm, | |
325 | int do_list); | |
326 | static void | |
327 | finish_goal_list(int *flag, int *wpos, int list[MAX_GOAL_WORMS], int index); | |
328 | ||
329 | ||
330 | /* Semeai worms are worms whose capture wins the semeai. */ | |
331 | ||
332 | #define MAX_SEMEAI_WORMS 20 | |
333 | static int s_worms = 0; | |
334 | static int semeai_worms[MAX_SEMEAI_WORMS]; | |
335 | static int important_semeai_worms[MAX_SEMEAI_WORMS]; | |
336 | ||
337 | /* Whether one color prefers to get a ko over a seki. */ | |
338 | static int prefer_ko; | |
339 | ||
340 | /* Usually it's a bad idea to include the opponent worms involved in | |
341 | * the semeai in the eyespace. For some purposes (determining a | |
342 | * definite lack of eyespace, finding certain vital moves), however, | |
343 | * we want to do that anyway. Then set this variable to 1 before | |
344 | * calling owl_estimate_life() and reset it afterwards. | |
345 | * | |
346 | * FIXME: We should implement a nicer mechanism to propagate this | |
347 | * information to owl_lively(), where it's used. | |
348 | */ | |
349 | static int include_semeai_worms_in_eyespace = 0; | |
350 | ||
351 | ||
352 | ||
353 | static void | |
354 | clear_cut_list(int cuts[MAX_CUTS]) | |
355 | { | |
356 | int i; | |
357 | for (i = 0; i < MAX_CUTS; i++) | |
358 | cuts[i] = NO_MOVE; | |
359 | } | |
360 | ||
361 | ||
362 | ||
363 | /* Called when (apos) and (bpos) point to adjacent dragons | |
364 | * of the opposite color, both with matcher_status DEAD or | |
365 | * CRITICAL, analyzes the semeai, assuming that the player | |
366 | * of the (apos) dragon moves first. The results returned | |
367 | * by *resulta and *resultb are the results of the defense | |
368 | * of the apos dragon and the attack of the bpos dragon, | |
369 | * respectively. Thus if these results are 1 and 0, | |
370 | * respectively, the usual meaning is that a move by the | |
371 | * apos player produces seki. | |
372 | * | |
373 | * owl determines whether owl moves are being generated | |
374 | * or simple liberty filling is taking place. | |
375 | * | |
376 | */ | |
377 | ||
378 | void | |
379 | owl_analyze_semeai(int apos, int bpos, int *resulta, int *resultb, | |
380 | int *semeai_move, int owl, int *semeai_result_certain) | |
381 | { | |
382 | owl_analyze_semeai_after_move(PASS_MOVE, EMPTY, apos, bpos, resulta, resultb, | |
383 | semeai_move, owl, semeai_result_certain, 0); | |
384 | } | |
385 | ||
386 | /* Same as the function above with the addition that an arbitrary move | |
387 | * may be made before the analysis is performed. | |
388 | */ | |
389 | void | |
390 | owl_analyze_semeai_after_move(int move, int color, int apos, int bpos, | |
391 | int *resulta, int *resultb, int *semeai_move, | |
392 | int owl, int *semeai_result_certain, | |
393 | int recompute_dragons) | |
394 | { | |
395 | signed char ms[BOARDMAX]; | |
396 | int w1, w2; | |
397 | int str; | |
398 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
399 | int save_verbose = verbose; | |
400 | int dummy_resulta; | |
401 | int dummy_resultb; | |
402 | int dummy_semeai_move; | |
403 | double start = 0.0; | |
404 | int reading_nodes_when_called = get_reading_node_counter(); | |
405 | int nodes_used; | |
406 | int new_dragons[BOARDMAX]; | |
407 | ||
408 | struct local_owl_data *owla; | |
409 | struct local_owl_data *owlb; | |
410 | Hash_data goal_hash; | |
411 | ||
412 | if (!resulta) | |
413 | resulta = &dummy_resulta; | |
414 | if (!resultb) | |
415 | resultb = &dummy_resultb; | |
416 | if (!semeai_move) | |
417 | semeai_move = &dummy_semeai_move; | |
418 | ||
419 | if (debug & DEBUG_OWL_PERFORMANCE) | |
420 | start = gg_cputime(); | |
421 | ||
422 | if (recompute_dragons) { | |
423 | if (tryko(move, color, "Recompute dragons for semeai.")) { | |
424 | compute_new_dragons(new_dragons); | |
425 | popgo(); | |
426 | } | |
427 | else | |
428 | recompute_dragons = 0; | |
429 | } | |
430 | ||
431 | ||
432 | /* Look for owl substantial worms of either dragon adjoining | |
433 | * the other dragon. Capturing such a worm wins the semeai. | |
434 | * These are the semeai_worms. This code must come before | |
435 | * the owl_init() calls because the owl_substantial | |
436 | * | |
437 | * FIXME: The sentence above is unfinished. | |
438 | */ | |
439 | s_worms = 0; | |
440 | memset(ms, 0, sizeof(ms)); | |
441 | for (w1 = first_worm_in_dragon(apos); | |
442 | w1 != NO_MOVE; | |
443 | w1 = next_worm_in_dragon(w1)) { | |
444 | for (w2 = first_worm_in_dragon(bpos); | |
445 | w2 != NO_MOVE; | |
446 | w2 = next_worm_in_dragon(w2)) { | |
447 | if (adjacent_strings(w1, w2) || have_common_lib(w1, w2, NULL)) { | |
448 | mark_string(w1, ms, 1); | |
449 | mark_string(w2, ms, 1); | |
450 | } | |
451 | } | |
452 | } | |
453 | ||
454 | ||
455 | ||
456 | sgf_dumptree = NULL; | |
457 | if (verbose > 0) | |
458 | verbose--; | |
459 | for (str = BOARDMIN; str < BOARDMAX; str++) | |
460 | if (ON_BOARD(str) && ms[str] && worm[str].origin == str) { | |
461 | int adj; | |
462 | int adjs[MAXCHAIN]; | |
463 | int k; | |
464 | int adjacent_to_outside = 0; | |
465 | ||
466 | /* Is the string adjacent to a living dragon outside the semeai? | |
467 | * In that case it's important to attack/defend it for the life | |
468 | * of the opponent. | |
469 | * | |
470 | * FIXME: Checking crude_status here isn't quite appropriate but | |
471 | * owl_status is not always computed and status itself is unsafe | |
472 | * since it might change before later calls to this code, e.g. | |
473 | * when checking for blunders. | |
474 | * | |
475 | * Not checking for aliveness at all gives problems in e.g. | |
476 | * ld_owl:302 where S19 is a separate dragon and R19 should not | |
477 | * be considered critically important. What we really would like | |
478 | * to determine is whether it's outside the semeai, however. | |
479 | */ | |
480 | adj = chainlinks(str, adjs); | |
481 | for (k = 0; k < adj; k++) { | |
482 | if (!is_same_dragon(adjs[k], apos) | |
483 | && !is_same_dragon(adjs[k], bpos) | |
484 | && dragon[adjs[k]].crude_status == ALIVE) | |
485 | adjacent_to_outside = 1; | |
486 | } | |
487 | ||
488 | if ((adjacent_to_outside || countstones(str) > 6) | |
489 | && s_worms < MAX_SEMEAI_WORMS) { | |
490 | important_semeai_worms[s_worms] = 1; | |
491 | semeai_worms[s_worms++] = str; | |
492 | DEBUG(DEBUG_SEMEAI, "important semeai worm: %1m\n", str); | |
493 | } | |
494 | else if (owl_substantial(str) && s_worms < MAX_SEMEAI_WORMS) { | |
495 | important_semeai_worms[s_worms] = 0; | |
496 | semeai_worms[s_worms++] = str; | |
497 | DEBUG(DEBUG_SEMEAI, "semeai worm: %1m\n", str); | |
498 | } | |
499 | } | |
500 | verbose = save_verbose; | |
501 | sgf_dumptree = save_sgf_dumptree; | |
502 | ||
503 | ASSERT1(board[apos] == OTHER_COLOR(board[bpos]), apos); | |
504 | count_variations = 1; | |
505 | if (move == PASS_MOVE) | |
506 | DEBUG(DEBUG_SEMEAI, "owl_analyze_semeai: %1m vs. %1m\n", apos, bpos); | |
507 | else | |
508 | DEBUG(DEBUG_SEMEAI, "owl_analyze_semeai_after_move %C %1m: %1m vs. %1m\n", | |
509 | color, move, apos, bpos); | |
510 | ||
511 | if (owl) { | |
512 | if (recompute_dragons) { | |
513 | init_owl(&owla, apos, NO_MOVE, NO_MOVE, 1, new_dragons); | |
514 | init_owl(&owlb, bpos, NO_MOVE, NO_MOVE, 0, new_dragons); | |
515 | } | |
516 | else { | |
517 | init_owl(&owla, apos, NO_MOVE, NO_MOVE, 1, NULL); | |
518 | init_owl(&owlb, bpos, NO_MOVE, NO_MOVE, 0, NULL); | |
519 | } | |
520 | owl_make_domains(owla, owlb); | |
521 | } | |
522 | else { | |
523 | reduced_init_owl(&owla, 1); | |
524 | reduced_init_owl(&owlb, 0); | |
525 | local_owl_node_counter = 0; | |
526 | owl_mark_worm(apos, NO_MOVE, owla); | |
527 | owl_mark_worm(bpos, NO_MOVE, owlb); | |
528 | } | |
529 | ||
530 | result_certain = 1; | |
531 | ||
532 | { | |
533 | Hash_data temp = goal_to_hashvalue(owla->goal); | |
534 | goal_hash = goal_to_hashvalue(owlb->goal); | |
535 | hashdata_xor(goal_hash, temp); | |
536 | } | |
537 | if (owl | |
538 | && search_persistent_semeai_cache(ANALYZE_SEMEAI, | |
539 | apos, bpos, move, color, &goal_hash, | |
540 | resulta, resultb, semeai_move, | |
541 | semeai_result_certain)) { | |
542 | if (move == PASS_MOVE) { | |
543 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
544 | "analyze_semeai %1m vs. %1m, result %d %d %1m (cached)\n", | |
545 | apos, bpos, *resulta, *resultb, *semeai_move); | |
546 | } | |
547 | else { | |
548 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
549 | "analyze_semeai_after_move %C %1m: %1m vs. %1m, result %d %d %1m (cached)\n", | |
550 | color, move, apos, bpos, *resulta, *resultb, *semeai_move); | |
551 | } | |
552 | return; | |
553 | } | |
554 | ||
555 | /* In some semeai situations one or both players have the option to | |
556 | * choose between seki and ko for the life and death of both. In | |
557 | * general this choice depends on the ko threat situation, the | |
558 | * overall score, and the strategical effects on surrounding | |
559 | * dragons, but we don't try to correctly estimate this. Instead we | |
560 | * make the reasonable assumption that if one dragon is | |
561 | * substantially smaller than the other dragon, ko is to be | |
562 | * preferred for the smaller dragon and seki for the larger dragon. | |
563 | * | |
564 | * prefer_ko can be either WHITE, BLACK, or EMPTY and tells which | |
565 | * color, if any, prefers to get ko. | |
566 | */ | |
567 | if (dragon[apos].size <= 5 && dragon[bpos].size > 3 * dragon[apos].size) | |
568 | prefer_ko = board[apos]; | |
569 | else if (dragon[bpos].size <= 5 && dragon[apos].size > 3 * dragon[bpos].size) | |
570 | prefer_ko = board[bpos]; | |
571 | else | |
572 | prefer_ko = EMPTY; | |
573 | ||
574 | if (move == PASS_MOVE) | |
575 | do_owl_analyze_semeai(apos, bpos, owla, owlb, | |
576 | resulta, resultb, semeai_move, 0, owl); | |
577 | else { | |
578 | semeai_trymove_and_recurse(bpos, apos, owlb, owla, owl, | |
579 | move, color, 1, 0, "mandatory move", | |
580 | SAME_DRAGON_MAYBE_CONNECTED, NULL, NO_MOVE, | |
581 | semeai_move, resultb, resulta); | |
582 | *resulta = REVERSE_RESULT(*resulta); | |
583 | *resultb = REVERSE_RESULT(*resultb); | |
584 | } | |
585 | ||
586 | nodes_used = get_reading_node_counter() - reading_nodes_when_called; | |
587 | if (move == PASS_MOVE) { | |
588 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
589 | "analyze_semeai %1m vs. %1m, result %d %d %1m (%d, %d nodes, %f seconds)\n", | |
590 | apos, bpos, *resulta, *resultb, *semeai_move, local_owl_node_counter, | |
591 | nodes_used, gg_cputime() - start); | |
592 | } | |
593 | else { | |
594 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
595 | "analyze_semeai_after_move %C %1m: %1m vs. %1m, result %d %d %1m (%d, %d nodes, %f seconds)\n", | |
596 | color, move, apos, bpos, *resulta, *resultb, *semeai_move, | |
597 | local_owl_node_counter, | |
598 | nodes_used, gg_cputime() - start); | |
599 | } | |
600 | ||
601 | if (semeai_result_certain) | |
602 | *semeai_result_certain = result_certain; | |
603 | ||
604 | if (owl) | |
605 | store_persistent_semeai_cache(ANALYZE_SEMEAI, apos, bpos, move, color, | |
606 | &goal_hash, | |
607 | *resulta, *resultb, *semeai_move, | |
608 | result_certain, nodes_used, | |
609 | owla->goal, owlb->goal); | |
610 | } | |
611 | ||
612 | ||
613 | ||
614 | /* It is assumed that the 'a' player moves first, and | |
615 | * determines the best result for both players. The | |
616 | * parameter "pass" is 1 if the opponent's last move is | |
617 | * pass. In this case, if no move is found but the genus | |
618 | * is less than 2, then the position is declared seki. | |
619 | * | |
620 | * If a move is needed to get this result, then (*move) is | |
621 | * the location, otherwise this field returns PASS. | |
622 | */ | |
623 | ||
624 | static void | |
625 | do_owl_analyze_semeai(int apos, int bpos, | |
626 | struct local_owl_data *owla, | |
627 | struct local_owl_data *owlb, | |
628 | int *resulta, int *resultb, | |
629 | int *move, int pass, int owl_phase) | |
630 | { | |
631 | int color = board[apos]; | |
632 | int other = OTHER_COLOR(color); | |
633 | #if 0 | |
634 | int wormsa, wormsb; | |
635 | int goal_wormsa[MAX_GOAL_WORMS], goal_wormsb[MAX_GOAL_WORMS]; | |
636 | #endif | |
637 | struct owl_move_data vital_defensive_moves[MAX_MOVES]; | |
638 | struct owl_move_data vital_offensive_moves[MAX_MOVES]; | |
639 | struct owl_move_data shape_defensive_moves[MAX_MOVES]; | |
640 | struct owl_move_data shape_offensive_moves[MAX_MOVES]; | |
641 | struct matched_patterns_list_data shape_offensive_patterns; | |
642 | struct matched_patterns_list_data shape_defensive_patterns; | |
643 | struct owl_move_data moves[MAX_SEMEAI_MOVES]; | |
644 | struct owl_move_data outside_liberty; | |
645 | struct owl_move_data common_liberty; | |
646 | struct owl_move_data backfill_outside_liberty; | |
647 | struct owl_move_data backfill_common_liberty; | |
648 | int safe_outside_liberty_found = 0; | |
649 | int safe_common_liberty_found = 0; | |
650 | int riskless_move_found = 0; | |
651 | signed char mw[BOARDMAX]; | |
652 | int k; | |
653 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
654 | int save_count_variations = count_variations; | |
655 | int move_value; | |
656 | int best_resulta = 0; | |
657 | int best_resultb = 0; | |
658 | int best_move = 0; | |
659 | const char *best_move_name = NULL; | |
660 | int this_resulta = -1; | |
661 | int this_resultb = -1; | |
662 | int xpos; | |
663 | int value1; | |
664 | int value2; | |
665 | int this_variation_number = count_variations - 1; | |
666 | int you_look_alive = 0; | |
667 | int I_look_alive = 0; | |
668 | int dummy_move; | |
669 | int tested_moves; | |
670 | int critical_semeai_worms[MAX_SEMEAI_WORMS]; | |
671 | int sworm; | |
672 | int we_might_be_inessential; | |
673 | struct eyevalue probable_eyes_a; | |
674 | struct eyevalue probable_eyes_b; | |
675 | struct eyevalue dummy_eyes; | |
676 | int I_have_more_eyes; | |
677 | ||
678 | SETUP_TRACE_INFO2("do_owl_analyze_semeai", apos, bpos); | |
679 | ||
680 | if (!move) | |
681 | move = &dummy_move; | |
682 | ||
683 | ASSERT1(board[apos] == owla->color, apos); | |
684 | ASSERT1(board[bpos] == owlb->color, bpos); | |
685 | ||
686 | apos = find_origin(apos); | |
687 | bpos = find_origin(bpos); | |
688 | ||
689 | if (stackp <= semeai_branch_depth | |
690 | && owl_phase | |
691 | && tt_get(&ttable, SEMEAI, apos, bpos, depth - stackp, NULL, | |
692 | &value1, &value2, &xpos) == 2) { | |
693 | TRACE_CACHED_RESULT2(value1, value2, xpos); | |
694 | *move = xpos; | |
695 | ||
696 | *resulta = value1; | |
697 | *resultb = value2; | |
698 | ||
699 | TRACE("%oVariation %d: %1m %1m %s %s %1m (cached) ", | |
700 | this_variation_number, apos, bpos, | |
701 | result_to_string(*resulta), | |
702 | result_to_string(*resultb), | |
703 | *move); | |
704 | SGFTRACE_SEMEAI(xpos, *resulta, *resultb, "cached"); | |
705 | return; | |
706 | } | |
707 | ||
708 | global_owl_node_counter++; | |
709 | local_owl_node_counter++; | |
710 | ||
711 | shape_offensive_patterns.initialized = 0; | |
712 | shape_defensive_patterns.initialized = 0; | |
713 | ||
714 | #if 0 | |
715 | wormsa = catalog_goal(owla, goal_wormsa); | |
716 | wormsb = catalog_goal(owlb, goal_wormsb); | |
717 | #endif | |
718 | ||
719 | outside_liberty.pos = NO_MOVE; | |
720 | common_liberty.pos = NO_MOVE; | |
721 | backfill_outside_liberty.pos = NO_MOVE; | |
722 | backfill_common_liberty.pos = NO_MOVE; | |
723 | for (k = 0; k < MAX_SEMEAI_MOVES; k++) { | |
724 | moves[k].pos = 0; | |
725 | moves[k].value = -1; | |
726 | moves[k].name = NULL; | |
727 | moves[k].same_dragon = SAME_DRAGON_CONNECTED; | |
728 | moves[k].lunch = NO_MOVE; | |
729 | clear_cut_list(moves[k].cuts); | |
730 | } | |
731 | ASSERT1(other == board[bpos], bpos); | |
732 | memset(mw, 0, sizeof(mw)); | |
733 | ||
734 | /* Turn off the sgf file and variation counting. */ | |
735 | sgf_dumptree = NULL; | |
736 | count_variations = 0; | |
737 | ||
738 | /* Look for a tactical attack. We seek a semeai worm of owlb which | |
739 | * can be attacked. If such exists and is considered critical, we | |
740 | * declare victory. If it's not considered critical we add the | |
741 | * attacking move as a high priority move to try. | |
742 | */ | |
743 | ||
744 | { | |
745 | int upos; | |
746 | ||
747 | for (sworm = 0; sworm < s_worms; sworm++) { | |
748 | critical_semeai_worms[sworm] = 0; | |
749 | if (board[semeai_worms[sworm]] == other) { | |
750 | int acode = attack(semeai_worms[sworm], &upos); | |
751 | if (acode == WIN | |
752 | && semeai_trust_tactical_attack(semeai_worms[sworm]) | |
753 | && important_semeai_worms[sworm]) { | |
754 | *resulta = WIN; | |
755 | *resultb = WIN; | |
756 | *move = upos; | |
757 | sgf_dumptree = save_sgf_dumptree; | |
758 | count_variations = save_count_variations; | |
759 | SGFTRACE_SEMEAI(upos, WIN, WIN, "tactical win found"); | |
760 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
761 | move, upos, WIN, WIN); | |
762 | } | |
763 | else if (acode != 0 | |
764 | && find_defense(semeai_worms[sworm], NULL)) { | |
765 | critical_semeai_worms[sworm] = 1; | |
766 | owl_add_move(moves, upos, 105, "attack semeai worm", | |
767 | SAME_DRAGON_MAYBE_CONNECTED, | |
768 | NO_MOVE, 0, NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
769 | TRACE("Added %1m %d (-1)\n", upos, 105); | |
770 | } | |
771 | else if (acode == WIN | |
772 | && important_semeai_worms[sworm]) { | |
773 | owl_add_move(moves, upos, 100, "attack semeai worm", | |
774 | SAME_DRAGON_MAYBE_CONNECTED, | |
775 | NO_MOVE, 0, NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
776 | TRACE("Added %1m %d (-1)\n", upos, 100); | |
777 | } | |
778 | } | |
779 | } | |
780 | /* Look for a tactical rescue. If a semeai worm of owla is tactically | |
781 | * threatened, try to save it. | |
782 | */ | |
783 | ||
784 | we_might_be_inessential = 1; | |
785 | for (sworm = 0; sworm < s_worms; sworm++) | |
786 | if (board[semeai_worms[sworm]] == color) { | |
787 | if (important_semeai_worms[sworm]) | |
788 | we_might_be_inessential = 0; | |
789 | ||
790 | if (attack(semeai_worms[sworm], NULL) | |
791 | && find_defense(semeai_worms[sworm], &upos)) { | |
792 | critical_semeai_worms[sworm] = 1; | |
793 | owl_add_move(moves, upos, 85, "defend semeai worm", | |
794 | SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, | |
795 | 0, NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
796 | TRACE("Added %1m %d (0)\n", upos, 85); | |
797 | } | |
798 | } | |
799 | } | |
800 | ||
801 | /* We generate the candidate moves. During the early stages of | |
802 | * the semeai, there may be moves to expand or shrink the | |
803 | * eyespaces of the two dragons. During the later stages, the | |
804 | * picture is simplified and reading the semeai is a matter | |
805 | * of filling liberties until one of the dragons may be removed, | |
806 | * or a seki results. The first stage we call the owl phase. | |
807 | */ | |
808 | if (!owl_phase) { | |
809 | set_eyevalue(&probable_eyes_a, 0, 0, 0, 0); | |
810 | set_eyevalue(&probable_eyes_b, 0, 0, 0, 0); | |
811 | I_have_more_eyes = 0; | |
812 | } | |
813 | else { | |
814 | /* First the vital moves. These include moves to attack or | |
815 | * defend the eyespace (e.g. nakade, or hane to reduce the | |
816 | * number of eyes) or moves to capture a lunch. | |
817 | */ | |
818 | int eyemin_a; | |
819 | int eyemin_b; | |
820 | int eyemax_a; | |
821 | int eyemax_b; | |
822 | const char *live_reasona; | |
823 | const char *live_reasonb; | |
824 | ||
825 | /* We do not wish for any string of the 'b' dragon to be | |
826 | * counted as a lunch of the 'a' dragon since owl_determine_life | |
827 | * can give a wrong result in the case of a semeai. So we eliminate | |
828 | * such lunches. | |
829 | */ | |
830 | ||
831 | owl_find_lunches(owla); | |
832 | owl_find_lunches(owlb); | |
833 | for (k = 0; k < MAX_LUNCHES; k++) { | |
834 | if (owla->lunch[k] != NO_MOVE | |
835 | && owlb->goal[owla->lunch[k]]) { | |
836 | owla->lunch[k] = NO_MOVE; | |
837 | } | |
838 | } | |
839 | #if 1 | |
840 | for (k = 0; k < MAX_LUNCHES; k++) { | |
841 | if (owlb->lunch[k] != NO_MOVE | |
842 | && owla->goal[owlb->lunch[k]]) { | |
843 | owlb->lunch[k] = NO_MOVE; | |
844 | } | |
845 | } | |
846 | #endif | |
847 | ||
848 | if (owl_estimate_life(owla, owlb, vital_defensive_moves, | |
849 | &live_reasona, 0, &probable_eyes_a, | |
850 | &eyemin_a, &eyemax_a)) | |
851 | I_look_alive = 1; | |
852 | else if (stackp > 2 && owl_escape_route(owla) >= 5) { | |
853 | live_reasona = "escaped"; | |
854 | I_look_alive = 1; | |
855 | } | |
856 | ||
857 | if (owl_estimate_life(owlb, owla, vital_offensive_moves, | |
858 | &live_reasonb, 1, &probable_eyes_b, | |
859 | &eyemin_b, &eyemax_b)) | |
860 | you_look_alive = 1; | |
861 | else if (stackp > 2 && owl_escape_route(owlb) >= 5) { | |
862 | live_reasonb = "escaped"; | |
863 | you_look_alive = 1; | |
864 | } | |
865 | ||
866 | if (verbose) { | |
867 | gprintf("probable_eyes_a: %s eyemin: %d eyemax: %d", | |
868 | eyevalue_to_string(&probable_eyes_a), eyemin_a, eyemax_a); | |
869 | if (I_look_alive) | |
870 | gprintf("%o I look alive (%s)", live_reasona); | |
871 | gprintf("%o\n"); | |
872 | gprintf("probable_eyes_b: %s eyemin: %d eyemax: %d", | |
873 | eyevalue_to_string(&probable_eyes_b), eyemin_b, eyemax_b); | |
874 | if (you_look_alive) | |
875 | gprintf("%o you look alive(%s)", live_reasonb); | |
876 | gprintf("%o\n"); | |
877 | } | |
878 | ||
879 | /* Stop here if both look certain to live. */ | |
880 | if (I_look_alive && you_look_alive) { | |
881 | *resulta = WIN; | |
882 | *resultb = 0; | |
883 | *move = PASS_MOVE; | |
884 | sgf_dumptree = save_sgf_dumptree; | |
885 | count_variations = save_count_variations; | |
886 | TRACE("Both live\n"); | |
887 | SGFTRACE_SEMEAI(PASS_MOVE, WIN, 0, "Both live"); | |
888 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
889 | move, PASS_MOVE, WIN, 0); | |
890 | } | |
891 | ||
892 | /* Next the shape moves. */ | |
893 | if (!I_look_alive) { | |
894 | owl_shapes(&shape_defensive_patterns, shape_defensive_moves, color, | |
895 | owla, &owl_defendpat_db); | |
896 | for (k = 0; k < MAX_MOVES-1; k++) | |
897 | if (!get_next_move_from_list(&shape_defensive_patterns, color, | |
898 | shape_defensive_moves, 1, owla)) | |
899 | break; | |
900 | } | |
901 | else | |
902 | shape_defensive_moves[0].pos = NO_MOVE; | |
903 | ||
904 | if (!you_look_alive) { | |
905 | owl_shapes(&shape_offensive_patterns, shape_offensive_moves, color, | |
906 | owlb, &owl_attackpat_db); | |
907 | for (k = 0; k < MAX_MOVES-1; k++) | |
908 | if (!get_next_move_from_list(&shape_offensive_patterns, color, | |
909 | shape_offensive_moves, 1, owlb)) | |
910 | break; | |
911 | } | |
912 | else | |
913 | shape_offensive_moves[0].pos = NO_MOVE; | |
914 | ||
915 | /* Filter out moves, which fill our eye (and not split it). */ | |
916 | if (eyemax_a > 0) { | |
917 | remove_eye_filling_moves(owla, vital_defensive_moves); | |
918 | remove_eye_filling_moves(owla, vital_offensive_moves); | |
919 | remove_eye_filling_moves(owla, shape_defensive_moves); | |
920 | remove_eye_filling_moves(owla, shape_offensive_moves); | |
921 | } | |
922 | ||
923 | /* Now we review the moves already considered, while collecting | |
924 | * them into a single list. | |
925 | */ | |
926 | ||
927 | if (!I_look_alive) { | |
928 | semeai_review_owl_moves(vital_defensive_moves, owla, owlb, color, | |
929 | &safe_outside_liberty_found, | |
930 | &safe_common_liberty_found, | |
931 | &riskless_move_found, | |
932 | mw, moves, 0, 30, | |
933 | critical_semeai_worms); | |
934 | ||
935 | semeai_review_owl_moves(shape_defensive_moves, owla, owlb, color, | |
936 | &safe_outside_liberty_found, | |
937 | &safe_common_liberty_found, | |
938 | &riskless_move_found, | |
939 | mw, moves, 0, 0, | |
940 | critical_semeai_worms); | |
941 | } | |
942 | ||
943 | if (!you_look_alive) { | |
944 | semeai_review_owl_moves(vital_offensive_moves, owla, owlb, color, | |
945 | &safe_outside_liberty_found, | |
946 | &safe_common_liberty_found, | |
947 | &riskless_move_found, | |
948 | mw, moves, 1, 30, | |
949 | critical_semeai_worms); | |
950 | ||
951 | semeai_review_owl_moves(shape_offensive_moves, owla, owlb, color, | |
952 | &safe_outside_liberty_found, | |
953 | &safe_common_liberty_found, | |
954 | &riskless_move_found, | |
955 | mw, moves, 1, 0, | |
956 | critical_semeai_worms); | |
957 | } | |
958 | ||
959 | /* If no moves were found so far, also check the eyespaces when | |
960 | * opponent semeai worms are allowed to be included for vital | |
961 | * moves. | |
962 | */ | |
963 | if (moves[0].pos == NO_MOVE || we_might_be_inessential) { | |
964 | include_semeai_worms_in_eyespace = 1; | |
965 | if (!owl_estimate_life(owlb, owla, vital_offensive_moves, | |
966 | &live_reasonb, 1, &dummy_eyes, | |
967 | &eyemin_b, &eyemax_b)) | |
968 | semeai_review_owl_moves(vital_offensive_moves, owla, owlb, color, | |
969 | &safe_outside_liberty_found, | |
970 | &safe_common_liberty_found, | |
971 | &riskless_move_found, | |
972 | mw, moves, 1, 30, | |
973 | critical_semeai_worms); | |
974 | include_semeai_worms_in_eyespace = 0; | |
975 | } | |
976 | ||
977 | if (eyemin_a == eyemax_a) | |
978 | /* We have stable number of eyes, so we can try to reduce | |
979 | * opponent eyes. | |
980 | */ | |
981 | I_have_more_eyes = (eyemin_a > min_eyes(&probable_eyes_b)); | |
982 | else { | |
983 | if (min_eyes(&probable_eyes_a) == max_eyes(&probable_eyes_a)) | |
984 | /* If we can't increase our number of eyes, we try to reduce | |
985 | * opponent eyes. | |
986 | */ | |
987 | I_have_more_eyes = (max_eyes(&probable_eyes_a) > min_eyes(&probable_eyes_b)); | |
988 | else | |
989 | /* If we can increase our number of eyes, we do it and let | |
990 | * opponent to increase his. | |
991 | */ | |
992 | I_have_more_eyes = (max_eyes(&probable_eyes_a) > max_eyes(&probable_eyes_b)); | |
993 | } | |
994 | ||
995 | if (get_level() < 8) { | |
996 | /* If no owl moves were found on two consecutive moves, | |
997 | * turn off the owl phase. | |
998 | */ | |
999 | if (moves[0].pos == NO_MOVE) { | |
1000 | if (owl_phase == 1) | |
1001 | owl_phase = 2; | |
1002 | else if (owl_phase == 2) | |
1003 | owl_phase = 0; | |
1004 | } | |
1005 | else | |
1006 | owl_phase = 1; | |
1007 | } | |
1008 | } | |
1009 | ||
1010 | if (1 && verbose) { | |
1011 | showboard(0); | |
1012 | goaldump(owla->goal); | |
1013 | goaldump(owlb->goal); | |
1014 | } | |
1015 | ||
1016 | /* Now we look for a move to fill a liberty. This is only | |
1017 | * interesting if the opponent doesn't already have two eyes. | |
1018 | * If we have more eyes, always check for a backfilling move. | |
1019 | */ | |
1020 | if (!you_look_alive | |
1021 | && !safe_outside_liberty_found | |
1022 | && (moves[0].value < 110 || I_have_more_eyes)) { | |
1023 | int pos; | |
1024 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
1025 | if (!ON_BOARD(pos)) | |
1026 | continue; | |
1027 | ||
1028 | if (board[pos] == EMPTY && !mw[pos]) { | |
1029 | if (liberty_of_goal(pos, owlb)) { | |
1030 | if (!liberty_of_goal(pos, owla)) { | |
1031 | /* outside liberty */ | |
1032 | if (safe_move(pos, color) == WIN) { | |
1033 | safe_outside_liberty_found = 1; | |
1034 | outside_liberty.pos = pos; | |
1035 | break; | |
1036 | } | |
1037 | else if (backfill_outside_liberty.pos == NO_MOVE) | |
1038 | backfill_outside_liberty.pos = find_semeai_backfilling_move(bpos, | |
1039 | pos); | |
1040 | } | |
1041 | else { | |
1042 | /* common liberty */ | |
1043 | if (safe_move(pos, color) == WIN) { | |
1044 | safe_common_liberty_found = 1; | |
1045 | common_liberty.pos = pos; | |
1046 | } | |
1047 | else if (backfill_common_liberty.pos == NO_MOVE) | |
1048 | backfill_common_liberty.pos = find_semeai_backfilling_move(bpos, | |
1049 | pos); | |
1050 | } | |
1051 | } | |
1052 | } | |
1053 | } | |
1054 | } | |
1055 | ||
1056 | /* Add the best liberty filling move available. We first want to | |
1057 | * play outer liberties, second backfilling moves required before | |
1058 | * filling an outer liberty. If no such moves are available we try | |
1059 | * to fill a mutual liberty or play a corresponding backfilling | |
1060 | * move. | |
1061 | */ | |
1062 | if (!you_look_alive) { | |
1063 | if (safe_outside_liberty_found | |
1064 | && outside_liberty.pos != NO_MOVE) { | |
1065 | move_value = semeai_move_value(outside_liberty.pos, | |
1066 | owla, owlb, 50, | |
1067 | critical_semeai_worms); | |
1068 | owl_add_move(moves, outside_liberty.pos, move_value, | |
1069 | "safe outside liberty", SAME_DRAGON_NOT_CONNECTED, | |
1070 | NO_MOVE, 0, NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
1071 | riskless_move_found = 1; | |
1072 | TRACE("Added %1m %d (5)\n", outside_liberty.pos, move_value); | |
1073 | } | |
1074 | else if (backfill_outside_liberty.pos != NO_MOVE) { | |
1075 | move_value = semeai_move_value(backfill_outside_liberty.pos, | |
1076 | owla, owlb, 50, | |
1077 | critical_semeai_worms); | |
1078 | owl_add_move(moves, backfill_outside_liberty.pos, move_value, | |
1079 | "backfilling move", SAME_DRAGON_NOT_CONNECTED, NO_MOVE, 0, | |
1080 | NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
1081 | riskless_move_found = 1; | |
1082 | TRACE("Added %1m %d (6)\n", backfill_outside_liberty.pos, move_value); | |
1083 | } | |
1084 | else if (safe_common_liberty_found | |
1085 | && common_liberty.pos != NO_MOVE) { | |
1086 | move_value = semeai_move_value(common_liberty.pos, | |
1087 | owla, owlb, 10, | |
1088 | critical_semeai_worms); | |
1089 | owl_add_move(moves, common_liberty.pos, move_value, | |
1090 | "safe common liberty", SAME_DRAGON_MAYBE_CONNECTED, | |
1091 | NO_MOVE, 0, NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
1092 | if (semeai_is_riskless_move(common_liberty.pos, owla)) | |
1093 | riskless_move_found = 1; | |
1094 | TRACE("Added %1m %d (7)\n", common_liberty.pos, move_value); | |
1095 | } | |
1096 | else if (backfill_common_liberty.pos != NO_MOVE) { | |
1097 | move_value = semeai_move_value(backfill_common_liberty.pos, | |
1098 | owla, owlb, 10, | |
1099 | critical_semeai_worms); | |
1100 | owl_add_move(moves, backfill_common_liberty.pos, move_value, | |
1101 | "backfilling move", SAME_DRAGON_NOT_CONNECTED, NO_MOVE, 0, | |
1102 | NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
1103 | if (semeai_is_riskless_move(backfill_common_liberty.pos, owla)) | |
1104 | riskless_move_found = 1; | |
1105 | TRACE("Added %1m %d (6)\n", backfill_common_liberty.pos, move_value); | |
1106 | } | |
1107 | } | |
1108 | ||
1109 | if (moves[0].pos == NO_MOVE) { | |
1110 | /* If no move has been found yet, see if we can fill opponent's | |
1111 | * eye (i.e. put more stones in "bulky five" shape). | |
1112 | */ | |
1113 | if (min_eyes(&probable_eyes_b) == 1) { | |
1114 | int move = semeai_propose_eyespace_filling_move(owla, owlb); | |
1115 | ||
1116 | if (move) { | |
1117 | owl_add_move(moves, move, 70, "eyespace filling", | |
1118 | SAME_DRAGON_NOT_CONNECTED, NO_MOVE, | |
1119 | 0, NO_MOVE, MAX_SEMEAI_MOVES, NULL); | |
1120 | } | |
1121 | } | |
1122 | ||
1123 | if (moves[0].pos == NO_MOVE) | |
1124 | TRACE("No move found\n"); | |
1125 | } | |
1126 | ||
1127 | /* Now we are ready to try moves. Turn on the sgf output ... */ | |
1128 | sgf_dumptree = save_sgf_dumptree; | |
1129 | count_variations = save_count_variations; | |
1130 | tested_moves = 0; | |
1131 | for (k = 0; k < MAX_SEMEAI_MOVES; k++) { | |
1132 | int mpos = moves[k].pos; | |
1133 | if (mpos == NO_MOVE) | |
1134 | break; | |
1135 | ||
1136 | if (moves[k].value == 0) | |
1137 | continue; | |
1138 | ||
1139 | /* Do not try too many moves. */ | |
1140 | if (tested_moves > 2 | |
1141 | || (stackp > semeai_branch_depth2 && tested_moves > 1) | |
1142 | || (stackp > semeai_branch_depth && tested_moves > 0)) { | |
1143 | /* If allpats, try and pop to get the move in the sgf record. */ | |
1144 | if (!allpats) | |
1145 | break; | |
1146 | else if (trymove(mpos, color, moves[k].name, apos)) { | |
1147 | semeai_add_sgf_comment(moves[k].value, owl_phase); | |
1148 | popgo(); | |
1149 | } | |
1150 | continue; | |
1151 | } | |
1152 | ||
1153 | if (count_variations >= semeai_node_limit | |
1154 | || stackp >= MAX_SEMEAI_DEPTH) | |
1155 | continue; | |
1156 | ||
1157 | /* Try playing the move at mpos and call ourselves recursively to | |
1158 | * determine the result obtained by this move. | |
1159 | */ | |
1160 | if (semeai_trymove_and_recurse(apos, bpos, owla, owlb, | |
1161 | owl_phase, mpos, color, | |
1162 | best_resulta == 0 || best_resultb == 0, | |
1163 | moves[k].value, moves[k].name, | |
1164 | moves[k].same_dragon, moves[k].pattern_data, | |
1165 | moves[k].lunch, NULL, | |
1166 | &this_resulta, &this_resultb)) { | |
1167 | tested_moves++; | |
1168 | if (this_resultb == WIN && this_resulta == WIN) { | |
1169 | /* Ideal result, no need to try any more moves. */ | |
1170 | *resulta = WIN; | |
1171 | *resultb = WIN; | |
1172 | *move = mpos; | |
1173 | TRACE("After %1m I (%C) am alive, you are dead\n", mpos, color); | |
1174 | SGFTRACE_SEMEAI(mpos, WIN, WIN, moves[k].name); | |
1175 | close_pattern_list(color, &shape_defensive_patterns); | |
1176 | close_pattern_list(color, &shape_offensive_patterns); | |
1177 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1178 | move, mpos, WIN, WIN); | |
1179 | } | |
1180 | /* When there is a choice between ko and seki, the prefer_ko | |
1181 | * variable decides policy. Thus if prefer_ko == color we | |
1182 | * consider attacking the opponent more important than defending | |
1183 | * our dragon, and vise versa otherwise. | |
1184 | */ | |
1185 | else if ((prefer_ko != color | |
1186 | && (this_resulta > best_resulta | |
1187 | || (this_resulta == best_resulta | |
1188 | && this_resultb > best_resultb))) | |
1189 | || (prefer_ko == color | |
1190 | && (this_resultb > best_resultb | |
1191 | || (this_resultb == best_resultb | |
1192 | && this_resulta > best_resulta)))) { | |
1193 | best_resulta = this_resulta; | |
1194 | best_resultb = this_resultb; | |
1195 | best_move = mpos; | |
1196 | best_move_name = moves[k].name; | |
1197 | } | |
1198 | } | |
1199 | } | |
1200 | ||
1201 | close_pattern_list(color, &shape_defensive_patterns); | |
1202 | close_pattern_list(color, &shape_offensive_patterns); | |
1203 | ||
1204 | /* If we can't find a move and the opponent looks alive, we have | |
1205 | * lost. | |
1206 | */ | |
1207 | if (best_resulta == 0 && best_resultb == 0 && you_look_alive) { | |
1208 | *resulta = 0; | |
1209 | *resultb = 0; | |
1210 | *move = PASS_MOVE; | |
1211 | SGFTRACE_SEMEAI(PASS_MOVE, 0, 0, "You live, I die"); | |
1212 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1213 | move, PASS_MOVE, 0, 0); | |
1214 | } | |
1215 | ||
1216 | /* If we didn't find a working move and we look dead even if including the | |
1217 | * opponent stones in our eyespace, we have lost. | |
1218 | */ | |
1219 | if (best_resulta == 0 && best_resultb == 0 | |
1220 | && !riskless_move_found) { | |
1221 | const char *live_reasona; | |
1222 | int eyemin_a; | |
1223 | int eyemax_a; | |
1224 | for (sworm = 0; sworm < s_worms; sworm++) { | |
1225 | if (board[semeai_worms[sworm]] == other) { | |
1226 | if (important_semeai_worms[sworm]) | |
1227 | break; | |
1228 | } | |
1229 | } | |
1230 | ||
1231 | if (sworm == s_worms) { | |
1232 | include_semeai_worms_in_eyespace = 1; | |
1233 | if (!owl_estimate_life(owla, owlb, vital_defensive_moves, | |
1234 | &live_reasona, 0, &dummy_eyes, | |
1235 | &eyemin_a, &eyemax_a) | |
1236 | && eyemax_a < 2) { | |
1237 | include_semeai_worms_in_eyespace = 0; | |
1238 | *resulta = 0; | |
1239 | *resultb = 0; | |
1240 | *move = PASS_MOVE; | |
1241 | SGFTRACE_SEMEAI(PASS_MOVE, 0, 0, "You live, I die - 2"); | |
1242 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1243 | move, PASS_MOVE, 0, 0); | |
1244 | } | |
1245 | include_semeai_worms_in_eyespace = 0; | |
1246 | } | |
1247 | } | |
1248 | ||
1249 | /* If we can't find a useful move and opponent passed, it's seki, unless | |
1250 | * one dragon has more eyes than the other. | |
1251 | */ | |
1252 | if (best_resulta == 0 && best_resultb == 0 | |
1253 | && !riskless_move_found) { | |
1254 | if (pass) { | |
1255 | if (max_eyes(&probable_eyes_a) < min_eyes(&probable_eyes_b)) { | |
1256 | *resulta = 0; | |
1257 | *resultb = 0; | |
1258 | *move = PASS_MOVE; | |
1259 | TRACE("You have more eyes.\n"); | |
1260 | SGFTRACE_SEMEAI(PASS_MOVE, 0, 0, "You have more eyes"); | |
1261 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1262 | move, PASS_MOVE, 0, 0); | |
1263 | } | |
1264 | else if (max_eyes(&probable_eyes_b) < min_eyes(&probable_eyes_a)) { | |
1265 | *resulta = WIN; | |
1266 | *resultb = WIN; | |
1267 | *move = PASS_MOVE; | |
1268 | TRACE("I have more eyes\n"); | |
1269 | SGFTRACE_SEMEAI(PASS_MOVE, WIN, WIN, "I have more eyes"); | |
1270 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1271 | move, PASS_MOVE, WIN, WIN); | |
1272 | } | |
1273 | else { | |
1274 | *resulta = WIN; | |
1275 | *resultb = 0; | |
1276 | *move = PASS_MOVE; | |
1277 | TRACE("Seki\n"); | |
1278 | SGFTRACE_SEMEAI(PASS_MOVE, WIN, 0, "Seki"); | |
1279 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1280 | move, PASS_MOVE, WIN, 0); | |
1281 | } | |
1282 | } | |
1283 | else { | |
1284 | /* No working move was found, but opponent hasn't passed. Then we pass. */ | |
1285 | do_owl_analyze_semeai(bpos, apos, owlb, owla, | |
1286 | resultb, resulta, NULL, 1, owl_phase); | |
1287 | *resulta = REVERSE_RESULT(*resulta); | |
1288 | *resultb = REVERSE_RESULT(*resultb); | |
1289 | TRACE("No move found\n"); | |
1290 | SGFTRACE_SEMEAI(PASS_MOVE, *resulta, *resultb, "No move found"); | |
1291 | *move = PASS_MOVE; | |
1292 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1293 | move, PASS_MOVE, *resulta, *resultb); | |
1294 | } | |
1295 | } | |
1296 | ||
1297 | /* There are a few selected cases where we should try to see if it | |
1298 | * would be better to pass rather than playing any move in the semeai. | |
1299 | * | |
1300 | * A first simple example is the case of positions where there is nothing | |
1301 | * left to play but common liberties. In case the above analysis concluded | |
1302 | * the result is seki and if the best (and only) move happens to be a | |
1303 | * common liberty, we attempt to pass, so that the engine considers tenuki | |
1304 | * as a viable option in case it actually is. | |
1305 | * | |
1306 | * Another example is related to "disturbing" kos. | |
1307 | * | |
1308 | * .OOOOOOOO. In this position (similar to semeai:130), X has just taken | |
1309 | * OOXXXXXXOO the ko on the left. The semeai code finds the ko recapture | |
1310 | * OX.XXOOXXO as the only attacking move and concludes the result is KO_B. | |
1311 | * OOXX.OO.XO | |
1312 | * ---------- | |
1313 | * | |
1314 | * In such cases too, we try to pass to see if it doesn't actually yield | |
1315 | * a better result. | |
1316 | * | |
1317 | * FIXME: there might be more cases where passing would be valuable. | |
1318 | */ | |
1319 | if (!pass && k == 1) { | |
1320 | if ((best_resulta == WIN && best_resultb == 0 | |
1321 | && best_move != NO_MOVE | |
1322 | && best_move == common_liberty.pos) | |
1323 | || (best_resulta == KO_B && best_resultb == KO_B | |
1324 | && is_ko(best_move, owla->color, NULL))) { | |
1325 | do_owl_analyze_semeai(bpos, apos, owlb, owla, &this_resultb, | |
1326 | &this_resulta, NULL, 1, owl_phase); | |
1327 | if (REVERSE_RESULT(this_resulta) >= best_resulta | |
1328 | && REVERSE_RESULT(this_resultb) >= best_resultb) { | |
1329 | best_move = PASS_MOVE; | |
1330 | best_resulta = REVERSE_RESULT(this_resulta); | |
1331 | best_resultb = REVERSE_RESULT(this_resultb); | |
1332 | best_move_name = "Pass"; | |
1333 | } | |
1334 | } | |
1335 | } | |
1336 | ||
1337 | *resulta = best_resulta; | |
1338 | *resultb = best_resultb; | |
1339 | if (best_resulta == 0) | |
1340 | best_move = PASS_MOVE; | |
1341 | *move = best_move; | |
1342 | SGFTRACE_SEMEAI(best_move, best_resulta, best_resultb, best_move_name); | |
1343 | READ_RETURN_SEMEAI(SEMEAI, apos, bpos, depth - stackp, | |
1344 | move, best_move, best_resulta, best_resultb); | |
1345 | } | |
1346 | ||
1347 | /* Play a move, update goal and boundaries appropriately, and call | |
1348 | * do_owl_analyze_semeai() recursively to determine the result of this | |
1349 | * move. | |
1350 | */ | |
1351 | static int | |
1352 | semeai_trymove_and_recurse(int apos, int bpos, struct local_owl_data *owla, | |
1353 | struct local_owl_data *owlb, | |
1354 | int owl_phase, | |
1355 | int move, int color, int ko_allowed, | |
1356 | int move_value, const char *move_name, | |
1357 | enum same_dragon_value same_dragon, | |
1358 | struct matched_pattern_data *pattern_data, | |
1359 | int lunch, int *semeai_move, | |
1360 | int *this_resulta, int *this_resultb) | |
1361 | { | |
1362 | int ko_move = 0; | |
1363 | ||
1364 | gg_assert(this_resulta != NULL && this_resultb != NULL); | |
1365 | *this_resulta = 0; | |
1366 | *this_resultb = 0; | |
1367 | ||
1368 | if (!komaster_trymove(move, color, move_name, apos, &ko_move, ko_allowed)) { | |
1369 | int kpos; | |
1370 | if (is_ko(move, color, &kpos)) { | |
1371 | /* Move was not allowed because of komaster. We want to check | |
1372 | * if this situation is double ko and when it is, we won semeai. | |
1373 | */ | |
1374 | int libs[MAX_LIBERTIES]; | |
1375 | int n; | |
1376 | int nlib; | |
1377 | int sworm; | |
1378 | int worm_color; | |
1379 | int other = OTHER_COLOR(color); | |
1380 | ||
1381 | for (sworm = 0; sworm < s_worms; sworm++) { | |
1382 | worm_color = board[semeai_worms[sworm]]; | |
1383 | if (worm_color == color) { | |
1384 | /* We only check up to MAX_LIBERTIES, due to performance | |
1385 | * reasons. When we have more liberties we have some outside | |
1386 | * liberties to fill and these moves will be tried later | |
1387 | * (and double ko situation will be found). | |
1388 | */ | |
1389 | nlib = findlib(semeai_worms[sworm], MAX_LIBERTIES, libs); | |
1390 | if (nlib > MAX_LIBERTIES) | |
1391 | return 0; | |
1392 | ||
1393 | for (n = 0; n < nlib; n++) | |
1394 | if (is_ko(libs[n], other, NULL)) { | |
1395 | /* Check if situation is not a nested ko capture. */ | |
1396 | if (DIAGONAL_NEIGHBORS(libs[n], kpos)) | |
1397 | return 0; | |
1398 | ||
1399 | /* Our dragon has double ko, but we have to check if | |
1400 | * opponent dragon doesn't have outside liberties or | |
1401 | * double ko. | |
1402 | */ | |
1403 | *this_resulta = WIN; | |
1404 | *this_resultb = WIN; | |
1405 | } | |
1406 | } | |
1407 | else if (worm_color == other) { | |
1408 | if (countlib(semeai_worms[sworm]) > 2) | |
1409 | /* In double ko situation the opponent can have only a | |
1410 | * single eye and a ko outside liberty to be sure that we | |
1411 | * will always win double ko. | |
1412 | */ | |
1413 | return 0; | |
1414 | } | |
1415 | } | |
1416 | if (*this_resulta == WIN) | |
1417 | return 1; | |
1418 | } | |
1419 | ||
1420 | return 0; | |
1421 | } | |
1422 | ||
1423 | semeai_add_sgf_comment(move_value, owl_phase); | |
1424 | TRACE("Trying %C %1m. Current stack: ", color, move); | |
1425 | if (verbose) { | |
1426 | dump_stack(); | |
1427 | goaldump(owla->goal); | |
1428 | gprintf("\n"); | |
1429 | goaldump(owlb->goal); | |
1430 | gprintf("\n"); | |
1431 | } | |
1432 | TRACE("%s, value %d, same_dragon %d\n", move_name, move_value, same_dragon); | |
1433 | ||
1434 | push_owl(&owla); | |
1435 | push_owl(&owlb); | |
1436 | ||
1437 | if (owla->color == color) { | |
1438 | owl_update_goal(move, same_dragon, lunch, owla, 1, pattern_data); | |
1439 | owl_update_boundary_marks(move, owlb); | |
1440 | } | |
1441 | else { | |
1442 | owl_update_goal(move, same_dragon, lunch, owlb, 1, pattern_data); | |
1443 | owl_update_boundary_marks(move, owla); | |
1444 | } | |
1445 | mark_goal_in_sgf(owla->goal); | |
1446 | mark_goal_in_sgf(owlb->goal); | |
1447 | ||
1448 | /* Do a recursive call to read the semeai after the move we just | |
1449 | * tried. If dragon b was captured by the move, call | |
1450 | * do_owl_attack() to see whether it sufficed for us to live. | |
1451 | */ | |
1452 | if (board[bpos] == EMPTY) { | |
1453 | /* FIXME: Are all owl_data fields and relevant static | |
1454 | * variables properly set up for a call to do_owl_attack()? | |
1455 | */ | |
1456 | *this_resulta = REVERSE_RESULT(do_owl_attack(apos, semeai_move, NULL, owla, 0)); | |
1457 | *this_resultb = *this_resulta; | |
1458 | } | |
1459 | else { | |
1460 | do_owl_analyze_semeai(bpos, apos, owlb, owla, | |
1461 | this_resultb, this_resulta, semeai_move, | |
1462 | 0, owl_phase); | |
1463 | *this_resulta = REVERSE_RESULT(*this_resulta); | |
1464 | *this_resultb = REVERSE_RESULT(*this_resultb); | |
1465 | } | |
1466 | ||
1467 | pop_owl(&owlb); | |
1468 | pop_owl(&owla); | |
1469 | ||
1470 | popgo(); | |
1471 | ||
1472 | /* Does success require ko? */ | |
1473 | if (ko_move) { | |
1474 | if (*this_resulta != 0) | |
1475 | *this_resulta = KO_B; | |
1476 | if (*this_resultb != 0) | |
1477 | *this_resultb = KO_B; | |
1478 | } | |
1479 | ||
1480 | if (count_variations >= semeai_node_limit) { | |
1481 | TRACE("Out of nodes, claiming win.\n"); | |
1482 | result_certain = 0; | |
1483 | *this_resulta = WIN; | |
1484 | *this_resultb = WIN; | |
1485 | } | |
1486 | return 1; | |
1487 | } | |
1488 | ||
1489 | /* Add details in sgf file about move value and whether owl_phase is active. */ | |
1490 | static void | |
1491 | semeai_add_sgf_comment(int value, int owl_phase) | |
1492 | { | |
1493 | char buf[100]; | |
1494 | ||
1495 | if (!sgf_dumptree) | |
1496 | return; | |
1497 | ||
1498 | if (owl_phase) | |
1499 | gg_snprintf(buf, 100, "value %d, owl_phase", value); | |
1500 | else | |
1501 | gg_snprintf(buf, 100, "value %d", value); | |
1502 | sgftreeAddComment(sgf_dumptree, buf); | |
1503 | } | |
1504 | ||
1505 | ||
1506 | /* In semeai situations tactical attacks often cannot be trusted. This | |
1507 | * in particular holds for strings with three or more liberties. Two | |
1508 | * liberties can usually be trusted, but if neither liberty can be | |
1509 | * played immediately, the need for backfilling moves gives an | |
1510 | * effective liberty count of more than two, again making the attack | |
1511 | * untrustworthy. | |
1512 | * | |
1513 | * This function decides whether an attack should be trusted. It does | |
1514 | * not check whether there actually is an attack, though. | |
1515 | */ | |
1516 | static int | |
1517 | semeai_trust_tactical_attack(int str) | |
1518 | { | |
1519 | int liberties; | |
1520 | int libs[3]; | |
1521 | int other = OTHER_COLOR(board[str]); | |
1522 | ||
1523 | liberties = findlib(str, 3, libs); | |
1524 | if (liberties > 2) | |
1525 | return 0; | |
1526 | ||
1527 | if (liberties < 2) | |
1528 | return 1; | |
1529 | ||
1530 | if (!is_self_atari(libs[0], other) | |
1531 | || !is_self_atari(libs[1], other)) | |
1532 | return 1; | |
1533 | ||
1534 | return 0; | |
1535 | } | |
1536 | ||
1537 | ||
1538 | /* A move is deemed riskless (i.e., doesn't kill ourself in a seki situation) | |
1539 | * if it doesn't decrease the liberty count of any goal string of our | |
1540 | * dragon. | |
1541 | */ | |
1542 | static int | |
1543 | semeai_is_riskless_move(int move, struct local_owl_data *owla) | |
1544 | { | |
1545 | int k; | |
1546 | int liberties = accuratelib(move, owla->color, MAXLIBS, NULL); | |
1547 | if (!liberty_of_goal(move, owla)) | |
1548 | return 1; | |
1549 | for (k = 0; k < 4; k++) { | |
1550 | int pos = move + delta[k]; | |
1551 | if (board[pos] == owla->color | |
1552 | && owla->goal[pos] | |
1553 | && countlib(pos) > liberties) | |
1554 | return 0; | |
1555 | } | |
1556 | return 1; | |
1557 | } | |
1558 | ||
1559 | ||
1560 | /* Review the moves in owl_moves[] and add them into semeai_moves[]. | |
1561 | * This is used to merge multiple sets of owl moves into one move | |
1562 | * list, while revising the values for use in semeai reading. | |
1563 | * | |
1564 | * We also record whether the moves include an outer or common liberty | |
1565 | * in the semeai. | |
1566 | */ | |
1567 | static void | |
1568 | semeai_review_owl_moves(struct owl_move_data owl_moves[MAX_MOVES], | |
1569 | struct local_owl_data *owla, | |
1570 | struct local_owl_data *owlb, int color, | |
1571 | int *safe_outside_liberty_found, | |
1572 | int *safe_common_liberty_found, | |
1573 | int *riskless_move_found, | |
1574 | signed char mw[BOARDMAX], | |
1575 | struct owl_move_data semeai_moves[MAX_SEMEAI_MOVES], | |
1576 | int guess_same_dragon, int value_bonus, | |
1577 | int *critical_semeai_worms) | |
1578 | { | |
1579 | int move; | |
1580 | int move_value; | |
1581 | enum same_dragon_value same_dragon; | |
1582 | struct matched_pattern_data *pattern_data = NULL; | |
1583 | int k; | |
1584 | ||
1585 | for (k = 0; k < MAX_MOVES-1; k++) { | |
1586 | move = owl_moves[k].pos; | |
1587 | if (move == NO_MOVE) | |
1588 | break; | |
1589 | ||
1590 | if (owl_moves[k].value == 0) | |
1591 | continue; | |
1592 | ||
1593 | /* Does the move fill a liberty in the semeai? */ | |
1594 | if (liberty_of_goal(move, owlb) | |
1595 | && safe_move(move, color)) { | |
1596 | if (!liberty_of_goal(move, owla)) | |
1597 | *safe_outside_liberty_found = 1; | |
1598 | else | |
1599 | *safe_common_liberty_found = 1; | |
1600 | } | |
1601 | if (is_legal(move, color) && !is_ko(move, color, NULL) | |
1602 | && semeai_is_riskless_move(move, owla)) | |
1603 | *riskless_move_found = 1; | |
1604 | ||
1605 | /* For some types of owl moves we don't have same_dragon | |
1606 | * information recorded and need to guess. | |
1607 | */ | |
1608 | if (guess_same_dragon) { | |
1609 | if (liberty_of_goal(move, owla) | |
1610 | || second_liberty_of_goal(move, owla)) | |
1611 | same_dragon = SAME_DRAGON_MAYBE_CONNECTED; | |
1612 | else | |
1613 | same_dragon = SAME_DRAGON_NOT_CONNECTED; | |
1614 | } | |
1615 | else { | |
1616 | same_dragon = owl_moves[k].same_dragon; | |
1617 | pattern_data = owl_moves[k].pattern_data; | |
1618 | } | |
1619 | ||
1620 | mw[move] = 1; | |
1621 | move_value = (semeai_move_value(move, owla, owlb, owl_moves[k].value, | |
1622 | critical_semeai_worms) | |
1623 | + value_bonus); | |
1624 | owl_add_move(semeai_moves, move, move_value, owl_moves[k].name, | |
1625 | same_dragon, NO_MOVE, owl_moves[k].escape, | |
1626 | NO_MOVE, MAX_SEMEAI_MOVES, pattern_data); | |
1627 | TRACE("Added %1m %d\n", move, move_value); | |
1628 | } | |
1629 | } | |
1630 | ||
1631 | ||
1632 | /* Propose an eyespace filling move. Such a move can, for instance, | |
1633 | * add a stone to opponent's "bulky five" shape. We of course choose | |
1634 | * a move that doesn't allow opponent to turn his dead eyeshape into a | |
1635 | * two eyes eyeshape. E.g. in this position, the function will | |
1636 | * propose the move at '*', not at the '.': | |
1637 | * | |
1638 | * XXX | |
1639 | * XXOX | |
1640 | * XOOX | |
1641 | * X.*X | |
1642 | * ---- | |
1643 | */ | |
1644 | static int | |
1645 | semeai_propose_eyespace_filling_move(struct local_owl_data *owla, | |
1646 | struct local_owl_data *owlb) | |
1647 | { | |
1648 | int color = OTHER_COLOR(owlb->color); | |
1649 | int pos; | |
1650 | int mw[BOARDMAX]; | |
1651 | int mz[BOARDMAX]; | |
1652 | ||
1653 | owl_find_relevant_eyespaces(owlb, mw, mz); | |
1654 | ||
1655 | /* Never try to fill opponent's eyes which contain our dragon. This | |
1656 | * is nothing else than suicide. | |
1657 | */ | |
1658 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
1659 | if (ON_BOARD(pos) && owla->goal[pos]) | |
1660 | mw[owlb->my_eye[pos].origin] = 0; | |
1661 | } | |
1662 | ||
1663 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
1664 | if (board[pos] == EMPTY) { | |
1665 | int origin = owlb->my_eye[pos].origin; | |
1666 | ||
1667 | if (mw[origin] > 1 | |
1668 | && min_eyes(&owlb->my_eye[origin].value) == 1) { | |
1669 | int good_move = 0; | |
1670 | ||
1671 | if (trymove(pos, color, "eyespace_filling", NO_MOVE)) { | |
1672 | struct eyevalue new_value; | |
1673 | int dummy_attack; | |
1674 | int dummy_defense; | |
1675 | ||
1676 | compute_eyes(origin, &new_value, &dummy_attack, &dummy_defense, | |
1677 | owlb->my_eye, owlb->half_eye, 0); | |
1678 | if (max_eyes(&new_value) <= 1) | |
1679 | good_move = 1; | |
1680 | ||
1681 | popgo(); | |
1682 | } | |
1683 | ||
1684 | if (good_move) | |
1685 | return pos; | |
1686 | } | |
1687 | } | |
1688 | } | |
1689 | ||
1690 | return NO_MOVE; | |
1691 | } | |
1692 | ||
1693 | ||
1694 | /* Try to estimate the value of a semeai move. This has two | |
1695 | * components. The first is the change in the total number of | |
1696 | * liberties for strings involved in the semeai. The second is a bonus | |
1697 | * for attacks and defenses of critical semeai worms. | |
1698 | */ | |
1699 | ||
1700 | static int | |
1701 | semeai_move_value(int move, struct local_owl_data *owla, | |
1702 | struct local_owl_data *owlb, | |
1703 | int raw_value, int *critical_semeai_worms) | |
1704 | { | |
1705 | int pos; | |
1706 | int net = 0; | |
1707 | int color = owla->color; | |
1708 | int save_verbose = verbose; | |
1709 | int k; | |
1710 | int bonus = 0; | |
1711 | ||
1712 | ASSERT1(board[move] == EMPTY, move); | |
1713 | verbose = 0; | |
1714 | if (safe_move(move, color)) { | |
1715 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
1716 | if (IS_STONE(board[pos]) | |
1717 | && pos == find_origin(pos)) { | |
1718 | int count_lib = -1; | |
1719 | if (owla->goal[pos]) { | |
1720 | count_lib = countlib(pos); | |
1721 | net -= 75 * count_lib; | |
1722 | } | |
1723 | if (owlb->goal[pos]) { | |
1724 | if (count_lib < 0) | |
1725 | count_lib = countlib(pos); | |
1726 | net += 100 * count_lib; | |
1727 | } | |
1728 | } | |
1729 | } | |
1730 | if (!trymove(move, color, NULL, 0)) { | |
1731 | verbose = save_verbose; | |
1732 | return 0; | |
1733 | } | |
1734 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
1735 | if (IS_STONE(board[pos]) | |
1736 | && pos == find_origin(pos)) { | |
1737 | int count_lib = -1; | |
1738 | if (owla->goal[pos] | |
1739 | || (pos == move && liberty_of_goal(move, owla))) { | |
1740 | count_lib = countlib(pos); | |
1741 | net += 75 * count_lib; | |
1742 | } | |
1743 | if (owlb->goal[pos]) { | |
1744 | if (count_lib < 0) | |
1745 | count_lib = countlib(pos); | |
1746 | net -= 100 * count_lib; | |
1747 | } | |
1748 | } | |
1749 | } | |
1750 | ||
1751 | increase_depth_values(); | |
1752 | for (k = 0; k < s_worms; k++) { | |
1753 | if (!critical_semeai_worms[k]) | |
1754 | continue; | |
1755 | if (board[semeai_worms[k]] == color | |
1756 | && !attack(semeai_worms[k], NULL)) | |
1757 | bonus += 50; | |
1758 | else if (board[semeai_worms[k]] == OTHER_COLOR(color) | |
1759 | && !find_defense(semeai_worms[k], NULL)) | |
1760 | bonus += 50; | |
1761 | } | |
1762 | decrease_depth_values(); | |
1763 | ||
1764 | popgo(); | |
1765 | } | |
1766 | ||
1767 | verbose = save_verbose; | |
1768 | ||
1769 | if (net < 0) | |
1770 | net = 0; | |
1771 | ||
1772 | net /= 25; | |
1773 | net *= 3; | |
1774 | ||
1775 | return raw_value + net + bonus; | |
1776 | } | |
1777 | ||
1778 | ||
1779 | /* Remove all moves from the list that would fill our own eye. */ | |
1780 | static void | |
1781 | remove_eye_filling_moves(struct local_owl_data *our_owl, | |
1782 | struct owl_move_data *moves) | |
1783 | { | |
1784 | int k; | |
1785 | int color = our_owl->color; | |
1786 | ||
1787 | for (k = 0; k < MAX_MOVES; k++) { | |
1788 | if (moves[k].pos == NO_MOVE) | |
1789 | break; | |
1790 | else { | |
1791 | struct eye_data *eye = &our_owl->my_eye[moves[k].pos]; | |
1792 | ||
1793 | /* If esize==1 this eye must not be a real eye (at least one | |
1794 | * worm is capturable, otherwise this move would not be | |
1795 | * proposed). | |
1796 | */ | |
1797 | if (eye->color == color && eye->msize == 0 && eye->neighbors <= 1 | |
1798 | && eye->esize != 1 | |
1799 | && our_owl->half_eye[moves[k].pos].type != HALF_EYE | |
1800 | && !has_neighbor(moves[k].pos, OTHER_COLOR(color))) | |
1801 | moves[k].value = 0; | |
1802 | } | |
1803 | } | |
1804 | } | |
1805 | ||
1806 | /* Is the vertex at pos adjacent to an element of the owl goal? */ | |
1807 | static int | |
1808 | liberty_of_goal(int pos, struct local_owl_data *owl) | |
1809 | { | |
1810 | int k; | |
1811 | for (k = 0; k < 4; k++) | |
1812 | if (IS_STONE(board[pos + delta[k]]) && owl->goal[pos + delta[k]]) | |
1813 | return 1; | |
1814 | ||
1815 | return 0; | |
1816 | } | |
1817 | ||
1818 | /* Is the vertex at pos a second liberty of the owl goal? */ | |
1819 | static int | |
1820 | second_liberty_of_goal(int pos, struct local_owl_data *owl) | |
1821 | { | |
1822 | int k; | |
1823 | for (k = 0; k < 4; k++) | |
1824 | if (board[pos + delta[k]] == EMPTY && liberty_of_goal(pos + delta[k], owl)) | |
1825 | return 1; | |
1826 | ||
1827 | return 0; | |
1828 | } | |
1829 | ||
1830 | ||
1831 | /* 'liberty' is a liberty of 'worm' which we would like to fill. | |
1832 | * However it is not safe to play there, so we look for a | |
1833 | * backfilling move. For example in this situation: | |
1834 | * | |
1835 | * ------+ | |
1836 | * O.OaXc| | |
1837 | * OOOOOX| | |
1838 | * XXXXXb| | |
1839 | * ......| | |
1840 | * | |
1841 | * If 'worm' is the O string and 'liberty' is 'a', the | |
1842 | * function returns 'b'. To fill at 'a', X must first | |
1843 | * fill 'b' and 'c' and it is better to fill at 'b' first | |
1844 | * since that will sometimes leave fewer or smaller ko threats. | |
1845 | * | |
1846 | * Returns NO_MOVE if no move is found. | |
1847 | */ | |
1848 | ||
1849 | static int | |
1850 | find_semeai_backfilling_move(int worm, int liberty) | |
1851 | { | |
1852 | int color = board[worm]; | |
1853 | int other = OTHER_COLOR(color); | |
1854 | int result = NO_MOVE; | |
1855 | ||
1856 | if (safe_move(liberty, other) == WIN) | |
1857 | return liberty; | |
1858 | if (is_self_atari(liberty, other)) { | |
1859 | int fill; | |
1860 | if (approxlib(liberty, other, 1, &fill) > 0 | |
1861 | && trymove(fill, other, "find_semeai_backfilling_move", worm)) { | |
1862 | if (safe_move(liberty, other)) | |
1863 | result = fill; | |
1864 | else if (board[worm] != EMPTY) | |
1865 | result = find_semeai_backfilling_move(worm, liberty); | |
1866 | popgo(); | |
1867 | } | |
1868 | } | |
1869 | if (ON_BOARD(result) && safe_move(result, other)) | |
1870 | return result; | |
1871 | else | |
1872 | return NO_MOVE; | |
1873 | } | |
1874 | ||
1875 | /* Some helper function for do_owl_attack/defend. */ | |
1876 | ||
1877 | static int | |
1878 | reading_limit_reached(const char **live_reason, int this_variation_number) | |
1879 | { | |
1880 | /* If (stackp > owl_reading_depth), interpret deep reading | |
1881 | * conservatively as escape. | |
1882 | */ | |
1883 | if (stackp > owl_reading_depth) { | |
1884 | TRACE("%oVariation %d: ALIVE (maximum reading depth reached)\n", | |
1885 | this_variation_number); | |
1886 | *live_reason = "max reading depth reached"; | |
1887 | return 1; | |
1888 | } | |
1889 | /* If the owl node limit has been reached, assume the dragon has | |
1890 | * managed to escape. | |
1891 | */ | |
1892 | if (local_owl_node_counter >= owl_node_limit) { | |
1893 | result_certain = 0; | |
1894 | TRACE("%oVariation %d: ALIVE (owl node limit reached)\n", | |
1895 | this_variation_number); | |
1896 | *live_reason = "owl node limit reached"; | |
1897 | return 1; | |
1898 | } | |
1899 | return 0; | |
1900 | } | |
1901 | ||
1902 | static void | |
1903 | clear_owl_move_data(struct owl_move_data moves[MAX_MOVES]) | |
1904 | { | |
1905 | int k; | |
1906 | for (k = 0; k < MAX_MOVES; k++) { | |
1907 | moves[k].pos = NO_MOVE; | |
1908 | moves[k].value = -1; | |
1909 | moves[k].name = NULL; | |
1910 | moves[k].same_dragon = SAME_DRAGON_CONNECTED; | |
1911 | moves[k].escape = 0; | |
1912 | moves[k].lunch = NO_MOVE; | |
1913 | moves[k].pattern_data = NULL; | |
1914 | clear_cut_list(moves[k].cuts); | |
1915 | } | |
1916 | } | |
1917 | ||
1918 | static void | |
1919 | set_single_owl_move(struct owl_move_data moves[MAX_MOVES], | |
1920 | int pos, const char *name) | |
1921 | { | |
1922 | moves[0].pos = pos; | |
1923 | moves[0].value = 25; | |
1924 | moves[0].name = name; | |
1925 | moves[0].same_dragon = SAME_DRAGON_MAYBE_CONNECTED; | |
1926 | moves[0].escape = 0; | |
1927 | moves[0].lunch = NO_MOVE; | |
1928 | moves[0].pattern_data = NULL; | |
1929 | clear_cut_list(moves[0].cuts); | |
1930 | moves[1].value = 0; | |
1931 | } | |
1932 | ||
1933 | ||
1934 | /* Returns true if a move can be found to attack the dragon | |
1935 | * at (target), in which case (*attack_point) is the recommended move. | |
1936 | * (attack_point) can be a null pointer if only the result is needed. | |
1937 | * | |
1938 | * The array goal marks the extent of the dragon. This must | |
1939 | * be maintained during reading. Call this function only when | |
1940 | * stackp==0; otherwise you can call do_owl_attack but you must | |
1941 | * set up the goal and boundary arrays by hand first. | |
1942 | * | |
1943 | * Returns KO_A or KO_B if the position is ko: | |
1944 | * | |
1945 | * - Returns KO_A if the attack prevails provided attacker is willing to | |
1946 | * ignore any ko threat (the attacker makes the first ko capture). | |
1947 | * | |
1948 | * - Returns KO_B if attack succeeds provided attacker has a ko threat | |
1949 | * which must be answered (the defender makes the first ko capture). | |
1950 | * | |
1951 | * If GNU Go is compiled with `configure --enable-experimental-owl-ext' | |
1952 | * then a return codes of GAIN is also possible. | |
1953 | * | |
1954 | * - Returns GAIN if the attack fails but another worm of the | |
1955 | * opponent's is captured in during the failed attack. The location | |
1956 | * of the killed worm is returned through the *kworm field. | |
1957 | * | |
1958 | * */ | |
1959 | ||
1960 | int | |
1961 | owl_attack(int target, int *attack_point, int *certain, int *kworm) | |
1962 | { | |
1963 | int result; | |
1964 | struct local_owl_data *owl; | |
1965 | int reading_nodes_when_called = get_reading_node_counter(); | |
1966 | double start = 0.0; | |
1967 | int tactical_nodes; | |
1968 | int move = NO_MOVE; | |
1969 | int wpos = NO_MOVE; | |
1970 | int wid = MAX_GOAL_WORMS; | |
1971 | ||
1972 | result_certain = 1; | |
1973 | if (worm[target].unconditional_status == DEAD) { | |
1974 | if (attack_point) | |
1975 | *attack_point = NO_MOVE; | |
1976 | if (kworm) | |
1977 | *kworm = NO_MOVE; | |
1978 | if (certain) | |
1979 | *certain = 1; | |
1980 | return 1; | |
1981 | } | |
1982 | ||
1983 | if (search_persistent_owl_cache(OWL_ATTACK, target, 0, 0, &result, | |
1984 | attack_point, kworm, certain)) | |
1985 | return result; | |
1986 | ||
1987 | if (debug & DEBUG_OWL_PERFORMANCE) | |
1988 | start = gg_cputime(); | |
1989 | ||
1990 | TRACE("owl_attack %1m\n", target); | |
1991 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); | |
1992 | owl_make_domains(owl, NULL); | |
1993 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, | |
1994 | kworm, 1); | |
1995 | result = do_owl_attack(target, &move, &wid, owl, 0); | |
1996 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); | |
1997 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
1998 | ||
1999 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
2000 | "owl_attack %1m, result %d %1m (%d, %d nodes, %f seconds)\n", | |
2001 | target, result, move, local_owl_node_counter, | |
2002 | tactical_nodes, gg_cputime() - start); | |
2003 | ||
2004 | store_persistent_owl_cache(OWL_ATTACK, target, 0, 0, | |
2005 | result, move, wpos, | |
2006 | result_certain, tactical_nodes, | |
2007 | owl->goal, board[target]); | |
2008 | if (attack_point) | |
2009 | *attack_point = move; | |
2010 | if (kworm) | |
2011 | *kworm = wpos; | |
2012 | if (certain) | |
2013 | *certain = result_certain; | |
2014 | ||
2015 | return result; | |
2016 | } | |
2017 | ||
2018 | ||
2019 | /* Static function containing the main recursive code for | |
2020 | * owl_attack. | |
2021 | */ | |
2022 | ||
2023 | static int | |
2024 | do_owl_attack(int str, int *move, int *wormid, | |
2025 | struct local_owl_data *owl, int escape) | |
2026 | { | |
2027 | int color = board[str]; | |
2028 | int other = OTHER_COLOR(color); | |
2029 | struct owl_move_data vital_moves[MAX_MOVES]; | |
2030 | struct owl_move_data shape_moves[MAX_MOVES]; | |
2031 | struct owl_move_data *moves; | |
2032 | struct matched_patterns_list_data shape_patterns; | |
2033 | signed char mw[BOARDMAX]; | |
2034 | int number_tried_moves = 0; | |
2035 | int pass; | |
2036 | int k; | |
2037 | int savemove = 0; | |
2038 | int saveworm = MAX_GOAL_WORMS; | |
2039 | int savecode = 0; | |
2040 | int eyemin = -1; /* Lower bound on the number of eyes. */ | |
2041 | int eyemax = -1; /* Upper bound on the number of eyes. */ | |
2042 | struct eyevalue probable_eyes; /* Best guess of eyevalue. */ | |
2043 | const char *live_reason; | |
2044 | int move_cutoff; | |
2045 | int xpos; | |
2046 | int value1; | |
2047 | int value2; | |
2048 | int this_variation_number = count_variations - 1; | |
2049 | ||
2050 | SETUP_TRACE_INFO("owl_attack", str); | |
2051 | ||
2052 | shape_patterns.initialized = 0; | |
2053 | ||
2054 | str = find_origin(str); | |
2055 | ||
2056 | if (tt_get(&ttable, OWL_ATTACK, str, NO_MOVE, depth - stackp, NULL, | |
2057 | &value1, &value2, &xpos) == 2) { | |
2058 | ||
2059 | TRACE_CACHED_RESULT(value1, xpos); | |
2060 | if (move) | |
2061 | *move = xpos; | |
2062 | ||
2063 | if (value1 == GAIN) { | |
2064 | if (wormid) { | |
2065 | if (goal_worms_computed) | |
2066 | *wormid = value2; | |
2067 | else | |
2068 | *wormid = MAX_GOAL_WORMS; | |
2069 | } | |
2070 | } | |
2071 | ||
2072 | if (value1 == WIN) | |
2073 | TRACE("%oVariation %d: DEAD (cached)\n", this_variation_number); | |
2074 | else | |
2075 | TRACE("%oVariation %d: ALIVE (cached)\n", this_variation_number); | |
2076 | ||
2077 | SGFTRACE(xpos, value1, "cached"); | |
2078 | ||
2079 | return value1; | |
2080 | } | |
2081 | ||
2082 | ||
2083 | /* If reading goes to deep or we run out of nodes, we assume life. */ | |
2084 | if (reading_limit_reached(&live_reason, this_variation_number)) { | |
2085 | SGFTRACE(0, 0, live_reason); | |
2086 | READ_RETURN(OWL_ATTACK, str, depth - stackp, move, 0, 0); | |
2087 | } | |
2088 | ||
2089 | memset(mw, 0, sizeof(mw)); | |
2090 | global_owl_node_counter++; | |
2091 | local_owl_node_counter++; | |
2092 | ||
2093 | current_owl_data = owl; | |
2094 | memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache)); | |
2095 | ||
2096 | /* First see whether there is any chance to kill. */ | |
2097 | if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, 1, | |
2098 | &probable_eyes, &eyemin, &eyemax)) { | |
2099 | /* | |
2100 | * We need to check here if there's a worm under atari. If yes, | |
2101 | * locate it and report a (gote) GAIN. | |
2102 | */ | |
2103 | int acode = 0; | |
2104 | int mpos = NO_MOVE; | |
2105 | if (experimental_owl_ext && goal_worms_computed) { | |
2106 | int size = 0; | |
2107 | saveworm = MAX_GOAL_WORMS; | |
2108 | for (k = 0; k < MAX_GOAL_WORMS; k++) { | |
2109 | if (owl_goal_worm[k] == NO_MOVE) | |
2110 | break; | |
2111 | if (board[owl_goal_worm[k]] == EMPTY | |
2112 | || countlib(owl_goal_worm[k]) > 1) | |
2113 | continue; | |
2114 | if (worm[owl_goal_worm[k]].size > size) { | |
2115 | saveworm = k; | |
2116 | size = worm[owl_goal_worm[k]].size; | |
2117 | } | |
2118 | } | |
2119 | if (saveworm != MAX_GOAL_WORMS && size >= 3) { | |
2120 | acode = GAIN; | |
2121 | findlib(worm[owl_goal_worm[saveworm]].origin, 1, &mpos); | |
2122 | /* ASSERT1( ... */ | |
2123 | } | |
2124 | } | |
2125 | SGFTRACE(0, acode, live_reason); | |
2126 | TRACE("%oVariation %d: ALIVE (%s)\n", this_variation_number, live_reason); | |
2127 | if (acode == 0) { | |
2128 | READ_RETURN(OWL_ATTACK, str, depth - stackp, move, 0, 0); | |
2129 | } | |
2130 | else { | |
2131 | if (wormid) | |
2132 | *wormid = saveworm; | |
2133 | READ_RETURN2(OWL_ATTACK, str, depth - stackp, | |
2134 | move, mpos, acode, saveworm); | |
2135 | } | |
2136 | } | |
2137 | ||
2138 | /* We try moves in five passes. | |
2139 | * stackp==0 stackp>0 | |
2140 | * 0. Vital moves in the interval [70..] [45..] | |
2141 | * 1. Shape moves | |
2142 | * 2. Vital moves in the interval [..69] [..44] | |
2143 | * 3. Tactical attack moves (except certain kos) | |
2144 | * 4. Moves found by the defender | |
2145 | * 5. Tactical ko attack moves which were not tried in pass 3 | |
2146 | */ | |
2147 | for (pass = 0; pass < 6; pass++) { | |
2148 | moves = NULL; | |
2149 | move_cutoff = 1; | |
2150 | ||
2151 | current_owl_data = owl; | |
2152 | /* Get the shape moves if we are in the right pass. */ | |
2153 | switch (pass) { | |
2154 | case 1: | |
2155 | if (stackp > owl_branch_depth && number_tried_moves > 0) | |
2156 | continue; | |
2157 | ||
2158 | owl_shapes(&shape_patterns, shape_moves, other, owl, &owl_attackpat_db); | |
2159 | moves = shape_moves; | |
2160 | break; | |
2161 | ||
2162 | case 0: | |
2163 | case 2: | |
2164 | if (stackp > owl_branch_depth && number_tried_moves > 0) | |
2165 | continue; | |
2166 | ||
2167 | moves = vital_moves; | |
2168 | if (pass == 0 || stackp > owl_distrust_depth) { | |
2169 | if (stackp == 0) | |
2170 | move_cutoff = 70; | |
2171 | else | |
2172 | move_cutoff = 45; | |
2173 | } | |
2174 | if (eyemax < 2 && stackp > 2) | |
2175 | move_cutoff = 99; /* Effectively disable vital moves. */ | |
2176 | break; | |
2177 | ||
2178 | case 3: | |
2179 | case 5: | |
2180 | { | |
2181 | /* Look for a tactical attack. This is primarily intended for | |
2182 | * the case where the whole dragon is a single string, therefore | |
2183 | * we only look at the string at the "origin". | |
2184 | * | |
2185 | * We must be wary with attacks giving ko. Unless the dragon | |
2186 | * otherwise looks alive, this may turn a dead dragon into one | |
2187 | * which can live by ko. Such moves will be tried anyway in | |
2188 | * pass 5. Notice though that we can only reach there if an owl | |
2189 | * defense was found in pass 4. | |
2190 | */ | |
2191 | int apos; | |
2192 | int result; | |
2193 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
2194 | int save_count_variations = count_variations; | |
2195 | ||
2196 | sgf_dumptree = NULL; | |
2197 | count_variations = 0; | |
2198 | result = attack(str, &apos); | |
2199 | if (result == WIN | |
2200 | || (result != 0 && (min_eyes(&probable_eyes) >= 2 | |
2201 | || pass == 5))) { | |
2202 | set_single_owl_move(shape_moves, apos, "tactical attack"); | |
2203 | moves = shape_moves; | |
2204 | } | |
2205 | sgf_dumptree = save_sgf_dumptree; | |
2206 | count_variations = save_count_variations; | |
2207 | } | |
2208 | break; | |
2209 | ||
2210 | /* If we found no move in the first four passes we ask the defender | |
2211 | * for a move suggestion. | |
2212 | */ | |
2213 | case 4: | |
2214 | if (number_tried_moves == 0) { | |
2215 | int dpos; | |
2216 | int dcode = do_owl_defend(str, &dpos, NULL, owl, escape); | |
2217 | /* No defense, we won. */ | |
2218 | if (dcode == 0) { | |
2219 | TRACE("%oVariation %d: DEAD (no defense)\n", | |
2220 | this_variation_number); | |
2221 | SGFTRACE(0, WIN, "no defense"); | |
2222 | close_pattern_list(other, &shape_patterns); | |
2223 | READ_RETURN(OWL_ATTACK, str, depth - stackp, move, 0, WIN); | |
2224 | } | |
2225 | else if (dpos != NO_MOVE) { | |
2226 | /* The dragon could be defended by one more move. Try to | |
2227 | * attack with this move. | |
2228 | * | |
2229 | * If the move is suicide for us, try to find a backfilling | |
2230 | * move to play instead. Do this also if the move is a | |
2231 | * send-two-return-one sacrifice. | |
2232 | */ | |
2233 | const char *name = "defense move"; | |
2234 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
2235 | int save_count_variations = count_variations; | |
2236 | ||
2237 | sgf_dumptree = NULL; | |
2238 | count_variations = 0; | |
2239 | ||
2240 | if (is_suicide(dpos, other) || send_two_return_one(dpos, other)) { | |
2241 | int dpos2; | |
2242 | for (k = 0; k < 4; k++) { | |
2243 | if (board[dpos + delta[k]] == other | |
2244 | && find_defense(dpos + delta[k], &dpos2)) { | |
2245 | dpos = dpos2; | |
2246 | name = "defense move (backfill)"; | |
2247 | break; | |
2248 | } | |
2249 | } | |
2250 | } | |
2251 | ||
2252 | sgf_dumptree = save_sgf_dumptree; | |
2253 | count_variations = save_count_variations; | |
2254 | ||
2255 | if (dpos != NO_MOVE) { | |
2256 | set_single_owl_move(shape_moves, dpos, name); | |
2257 | moves = shape_moves; | |
2258 | } | |
2259 | } | |
2260 | } | |
2261 | break; | |
2262 | } /* switch (pass) */ | |
2263 | ||
2264 | ||
2265 | /* FIXME: This block probably should reappear somewhere in this | |
2266 | * function. | |
2267 | */ | |
2268 | #if 0 | |
2269 | /* First test whether the dragon has escaped. */ | |
2270 | if (owl_escape_route(owl) >= 5) { | |
2271 | /* FIXME: We probably should make distinction in the returned | |
2272 | * result whether the dragon lives by making two eyes or by | |
2273 | * escaping. | |
2274 | */ | |
2275 | TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); | |
2276 | SGFTRACE(0, 0, "escaped"); | |
2277 | close_pattern_list(other, &shape_patterns); | |
2278 | READ_RETURN0(OWL_ATTACK, str, depth - stackp); | |
2279 | } | |
2280 | #endif | |
2281 | ||
2282 | if (!moves) | |
2283 | continue; | |
2284 | ||
2285 | /* For the up to MAX_MOVES best moves with value equal to | |
2286 | * move_cutoff or higher, try to attack the dragon and see if it | |
2287 | * can then be defended. | |
2288 | */ | |
2289 | for (k = 0; k < MAX_MOVES; k++) { | |
2290 | int mpos; | |
2291 | int ko_move = -1; | |
2292 | int origin = NO_MOVE; | |
2293 | int captured; | |
2294 | int wid = MAX_GOAL_WORMS; | |
2295 | int dcode; | |
2296 | ||
2297 | /* Consider only the highest scoring move if we're deeper than | |
2298 | * owl_branch_depth. | |
2299 | * | |
2300 | * FIXME: To behave as intended, k should be replaced by | |
2301 | * number_tried_moves. | |
2302 | */ | |
2303 | if (stackp > owl_branch_depth && k > 0) | |
2304 | break; | |
2305 | ||
2306 | current_owl_data = owl; | |
2307 | ||
2308 | /* Shape moves are selected on demand. */ | |
2309 | if (pass == 1) { | |
2310 | if (!get_next_move_from_list(&shape_patterns, other, | |
2311 | shape_moves, move_cutoff, owl)) | |
2312 | break; | |
2313 | } | |
2314 | else | |
2315 | if (moves[k].value < move_cutoff) | |
2316 | break; | |
2317 | ||
2318 | mpos = moves[k].pos; | |
2319 | ASSERT_ON_BOARD1(mpos); | |
2320 | ||
2321 | /* Have we already tested this move? */ | |
2322 | if (mw[mpos]) | |
2323 | continue; | |
2324 | ||
2325 | captured = (color == WHITE ? white_captured : black_captured); | |
2326 | ||
2327 | /* Try to make the move. */ | |
2328 | if (!komaster_trymove(mpos, other, moves[k].name, str, | |
2329 | &ko_move, savecode == 0)) | |
2330 | continue; | |
2331 | ||
2332 | captured = (color == WHITE ? white_captured : black_captured) - captured; | |
2333 | ||
2334 | TRACE("Trying %C %1m. Escape = %d. Current stack: ", | |
2335 | other, mpos, escape); | |
2336 | if (verbose) | |
2337 | dump_stack(); | |
2338 | ||
2339 | /* We have now made a move. Analyze the new position. */ | |
2340 | push_owl(&owl); | |
2341 | mw[mpos] = 1; | |
2342 | number_tried_moves++; | |
2343 | owl_update_boundary_marks(mpos, owl); | |
2344 | ||
2345 | /* If the origin of the dragon has been captured, we look | |
2346 | * for another string which was part of the original dragon, | |
2347 | * marked when stackp==0, which has not been captured. If no | |
2348 | * such string is found, owl_attack declares victory. | |
2349 | */ | |
2350 | if (IS_STONE(board[str])) | |
2351 | origin = str; | |
2352 | else | |
2353 | origin = select_new_goal_origin(NO_MOVE, owl); | |
2354 | ||
2355 | /* Test whether the move cut the goal dragon apart. */ | |
2356 | if (moves[k].cuts[0] != NO_MOVE && origin != NO_MOVE) { | |
2357 | owl_test_cuts(owl->goal, owl->color, moves[k].cuts); | |
2358 | if (!owl->goal[origin]) | |
2359 | origin = select_new_goal_origin(origin, owl); | |
2360 | } | |
2361 | mark_goal_in_sgf(owl->goal); | |
2362 | ||
2363 | if (origin == NO_MOVE) | |
2364 | dcode = 0; | |
2365 | else | |
2366 | dcode = do_owl_defend(origin, NULL, &wid, owl, escape); | |
2367 | ||
2368 | if (!ko_move) { | |
2369 | if (dcode == 0) { | |
2370 | pop_owl(&owl); | |
2371 | popgo(); | |
2372 | if (sgf_dumptree) { | |
2373 | const char *wintxt; | |
2374 | char winstr[192]; | |
2375 | if (origin == NO_MOVE) | |
2376 | wintxt = "all original stones captured"; | |
2377 | else | |
2378 | wintxt = "attack effective"; | |
2379 | sprintf(winstr, "%s)\n (%d variations", wintxt, | |
2380 | count_variations - this_variation_number); | |
2381 | SGFTRACE(mpos, WIN, winstr); | |
2382 | } | |
2383 | close_pattern_list(other, &shape_patterns); | |
2384 | READ_RETURN(OWL_ATTACK, str, depth - stackp, move, mpos, WIN); | |
2385 | } | |
2386 | else if (experimental_owl_ext && dcode == LOSS) { | |
2387 | if (saveworm == MAX_GOAL_WORMS | |
2388 | || worm[owl_goal_worm[wid]].size | |
2389 | > worm[owl_goal_worm[saveworm]].size) | |
2390 | saveworm = wid; | |
2391 | } | |
2392 | /* The conditions here are set so that this code doesn't get | |
2393 | * triggered when the capture is immediate (the tactical | |
2394 | * reading code should take care of these). | |
2395 | */ | |
2396 | else if (experimental_owl_ext && goal_worms_computed | |
2397 | #if 0 | |
2398 | && stackp > 1 | |
2399 | #endif | |
2400 | && captured >= 3) { | |
2401 | int w = MAX_GOAL_WORMS; | |
2402 | int size = 0; | |
2403 | int l; | |
2404 | /* locate the biggest captured worm */ | |
2405 | for (l = 0; l < MAX_GOAL_WORMS; l++) { | |
2406 | if (owl_goal_worm[l] == NO_MOVE) | |
2407 | break; | |
2408 | if (board[owl_goal_worm[l]] == EMPTY) | |
2409 | if (size == 0 || worm[owl_goal_worm[l]].size > size) { | |
2410 | w = l; | |
2411 | size = worm[owl_goal_worm[l]].size; | |
2412 | } | |
2413 | } | |
2414 | if (w != MAX_GOAL_WORMS) { | |
2415 | if (GAIN > savecode) { | |
2416 | /* if new result better, just update */ | |
2417 | dcode = LOSS; | |
2418 | saveworm = w; | |
2419 | } | |
2420 | else if (GAIN == savecode) { | |
2421 | /* bigger ? */ | |
2422 | int wpos = owl_goal_worm[saveworm]; | |
2423 | if (size > worm[wpos].size) | |
2424 | saveworm = w; | |
2425 | } | |
2426 | } | |
2427 | } | |
2428 | UPDATE_SAVED_KO_RESULT(savecode, savemove, dcode, mpos); | |
2429 | } | |
2430 | else { /* ko_move */ | |
2431 | if (dcode != WIN) { | |
2432 | if (mpos == 0) { | |
2433 | SGFTRACE(mpos, KO_B, "all original stones captured with ko"); | |
2434 | } | |
2435 | else { | |
2436 | SGFTRACE(mpos, KO_B, "attack effective - ko"); | |
2437 | } | |
2438 | /* We already know the savecode was previously 0. */ | |
2439 | savemove = mpos; | |
2440 | savecode = KO_B; | |
2441 | ||
2442 | /* It's possible that the defender has no defense even if we | |
2443 | * give up the ko. In order to force a test of this, | |
2444 | * assuming this was our only move, we decrease the number | |
2445 | * of tried moves counter, disregarding this move. | |
2446 | */ | |
2447 | number_tried_moves--; | |
2448 | } | |
2449 | } | |
2450 | ||
2451 | pop_owl(&owl); | |
2452 | popgo(); | |
2453 | } | |
2454 | } | |
2455 | ||
2456 | close_pattern_list(other, &shape_patterns); | |
2457 | ||
2458 | if (savecode) { | |
2459 | if (savecode == GAIN) { | |
2460 | SGFTRACE(savemove, savecode, "attack effective (gain) - E"); | |
2461 | if (wormid) | |
2462 | *wormid = saveworm; | |
2463 | READ_RETURN2(OWL_ATTACK, str, depth - stackp, | |
2464 | move, savemove, savecode, saveworm); | |
2465 | } | |
2466 | else { | |
2467 | SGFTRACE(savemove, savecode, "attack effective (ko) - E"); | |
2468 | READ_RETURN(OWL_ATTACK, str, depth - stackp, move, savemove, savecode); | |
2469 | } | |
2470 | } | |
2471 | ||
2472 | if (sgf_dumptree) { | |
2473 | char winstr[128]; | |
2474 | sprintf(winstr, "attack failed)\n (%d variations", | |
2475 | count_variations - this_variation_number); | |
2476 | SGFTRACE(0, 0, winstr); | |
2477 | } | |
2478 | ||
2479 | READ_RETURN0(OWL_ATTACK, str, depth - stackp); | |
2480 | } | |
2481 | ||
2482 | ||
2483 | /* Returns true if the dragon at (target) can be captured given | |
2484 | * two moves in a row. The first two moves to capture the | |
2485 | * dragon are given as (*attack1) and (*attack2). | |
2486 | */ | |
2487 | ||
2488 | int | |
2489 | owl_threaten_attack(int target, int *attack1, int *attack2) | |
2490 | { | |
2491 | struct owl_move_data moves[MAX_MOVES]; | |
2492 | int k; | |
2493 | int other = OTHER_COLOR(board[target]); | |
2494 | struct local_owl_data *owl; | |
2495 | int result = 0; | |
2496 | int reading_nodes_when_called = get_reading_node_counter(); | |
2497 | signed char saved_boundary[BOARDMAX]; | |
2498 | double start = 0.0; | |
2499 | int tactical_nodes; | |
2500 | int move = 0; | |
2501 | int move2 = 0; | |
2502 | struct matched_patterns_list_data shape_patterns; | |
2503 | ||
2504 | shape_patterns.initialized = 0; | |
2505 | result_certain = 1; | |
2506 | if (search_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, | |
2507 | &result, attack1, attack2, NULL)) | |
2508 | return result; | |
2509 | ||
2510 | if (debug & DEBUG_OWL_PERFORMANCE) | |
2511 | start = gg_cputime(); | |
2512 | ||
2513 | gg_assert(stackp == 0); | |
2514 | TRACE("owl_threaten_attack %1m\n", target); | |
2515 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); | |
2516 | memcpy(saved_boundary, owl->boundary, sizeof(saved_boundary)); | |
2517 | owl_make_domains(owl, NULL); | |
2518 | owl_shapes(&shape_patterns, moves, other, owl, &owl_attackpat_db); | |
2519 | for (k = 0; k < MAX_MOVES; k++) { | |
2520 | current_owl_data = owl; | |
2521 | if (!get_next_move_from_list(&shape_patterns, other, moves, 1, owl)) | |
2522 | break; | |
2523 | else { | |
2524 | int mpos = moves[k].pos; | |
2525 | ||
2526 | if (mpos != NO_MOVE && moves[k].value > 0) | |
2527 | if (trymove(mpos, other, moves[k].name, target)) { | |
2528 | int pos; | |
2529 | int origin = NO_MOVE; | |
2530 | owl->lunches_are_current = 0; | |
2531 | owl_update_boundary_marks(mpos, owl); | |
2532 | ||
2533 | /* If the origin of the dragon has been captured, we look | |
2534 | * for another string which was part of the original dragon, | |
2535 | * marked when stackp==0, which has not been captured. If no | |
2536 | * such string is found, owl_attack declares victory. | |
2537 | */ | |
2538 | ||
2539 | if (board[target] == EMPTY) { | |
2540 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
2541 | if (IS_STONE(board[pos]) && owl->goal[pos] == 1) { | |
2542 | origin = find_origin(pos); | |
2543 | break; | |
2544 | } | |
2545 | } | |
2546 | ||
2547 | if (origin == NO_MOVE | |
2548 | || do_owl_attack(origin, NULL, NULL, owl, 0)) { | |
2549 | /* probably this can't happen */ | |
2550 | popgo(); | |
2551 | gg_assert(stackp == 0); | |
2552 | result = 1; | |
2553 | break; | |
2554 | } | |
2555 | } | |
2556 | else if (do_owl_attack(target, &move2, NULL, owl, 0) == WIN) { | |
2557 | move = moves[k].pos; | |
2558 | popgo(); | |
2559 | gg_assert(stackp == 0); | |
2560 | result = 1; | |
2561 | break; | |
2562 | } | |
2563 | popgo(); | |
2564 | memcpy(owl->boundary, saved_boundary, sizeof(saved_boundary)); | |
2565 | } | |
2566 | } | |
2567 | } | |
2568 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
2569 | gg_assert(stackp == 0); | |
2570 | ||
2571 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
2572 | "owl_threaten_attack %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", | |
2573 | target, move, move2, result, local_owl_node_counter, | |
2574 | tactical_nodes, gg_cputime() - start); | |
2575 | ||
2576 | store_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, | |
2577 | result, move, move2, 0, | |
2578 | tactical_nodes, owl->goal, board[target]); | |
2579 | ||
2580 | if (attack1) | |
2581 | *attack1 = move; | |
2582 | if (attack2) | |
2583 | *attack2 = move2; | |
2584 | ||
2585 | close_pattern_list(other, &shape_patterns); | |
2586 | return result; | |
2587 | } | |
2588 | ||
2589 | ||
2590 | /* Returns true if a move can be found to defend the dragon | |
2591 | * at (target), in which case (*defense_point) is the recommended move. | |
2592 | * (defense_point) can be a null pointer if the result is not needed. | |
2593 | * | |
2594 | * The array goal marks the extent of the dragon. This must | |
2595 | * be maintained during reading. Call this function only when | |
2596 | * stackp==0; otherwise you can call do_owl_attack but you must | |
2597 | * set up the goal and boundary arrays by hand first. | |
2598 | * | |
2599 | * Returns KO_A or KO_B if the position is ko: | |
2600 | * | |
2601 | * - Returns KO_A if the defendse succeeds provided the defender is willing to | |
2602 | * ignore any ko threat (the defender makes the first ko capture). | |
2603 | * - Returns KO_B if the defense succeeds provided the defender has a ko threat | |
2604 | * which must be answered (the attacker makes the first ko capture). | |
2605 | * | |
2606 | * If GNU Go is compiled with `configure --enable-experimental-owl-ext' | |
2607 | * then a return codes of GAIN is also possible. | |
2608 | * | |
2609 | * - Returns LOSS if the defense succeeds but another worm of the | |
2610 | * defender's is captured in during the defense. The location | |
2611 | * of the killed worm is returned through the *kworm field. | |
2612 | * | |
2613 | * The array goal marks the extent of the dragon. This must | |
2614 | * be maintained during reading. | |
2615 | */ | |
2616 | ||
2617 | int | |
2618 | owl_defend(int target, int *defense_point, int *certain, int *kworm) | |
2619 | { | |
2620 | int result; | |
2621 | static struct local_owl_data *owl; | |
2622 | int reading_nodes_when_called = get_reading_node_counter(); | |
2623 | double start = 0.0; | |
2624 | int tactical_nodes; | |
2625 | int move = NO_MOVE; | |
2626 | int wpos = NO_MOVE; | |
2627 | int wid = MAX_GOAL_WORMS; | |
2628 | ||
2629 | result_certain = 1; | |
2630 | if (worm[target].unconditional_status == DEAD) | |
2631 | return 0; | |
2632 | ||
2633 | if (search_persistent_owl_cache(OWL_DEFEND, target, 0, 0, &result, | |
2634 | defense_point, kworm, certain)) | |
2635 | return result; | |
2636 | ||
2637 | if (debug & DEBUG_OWL_PERFORMANCE) | |
2638 | start = gg_cputime(); | |
2639 | ||
2640 | TRACE("owl_defend %1m\n", target); | |
2641 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); | |
2642 | owl_make_domains(owl, NULL); | |
2643 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, | |
2644 | kworm, 1); | |
2645 | result = do_owl_defend(target, &move, &wid, owl, 0); | |
2646 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); | |
2647 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
2648 | ||
2649 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
2650 | "owl_defend %1m, result %d %1m (%d, %d nodes, %f seconds)\n", | |
2651 | target, result, move, local_owl_node_counter, | |
2652 | tactical_nodes, gg_cputime() - start); | |
2653 | ||
2654 | store_persistent_owl_cache(OWL_DEFEND, target, 0, 0, result, move, wpos, | |
2655 | result_certain, tactical_nodes, owl->goal, | |
2656 | board[target]); | |
2657 | ||
2658 | if (defense_point) | |
2659 | *defense_point = move; | |
2660 | if (kworm) | |
2661 | *kworm = wpos; | |
2662 | if (certain) | |
2663 | *certain = result_certain; | |
2664 | ||
2665 | return result; | |
2666 | } | |
2667 | ||
2668 | ||
2669 | /* Static function containing the main recursive code for owl_defend. | |
2670 | */ | |
2671 | ||
2672 | static int | |
2673 | do_owl_defend(int str, int *move, int *wormid, struct local_owl_data *owl, | |
2674 | int escape) | |
2675 | { | |
2676 | int color = board[str]; | |
2677 | struct owl_move_data shape_moves[MAX_MOVES]; | |
2678 | struct owl_move_data vital_moves[MAX_MOVES]; | |
2679 | struct owl_move_data *moves; | |
2680 | struct matched_patterns_list_data shape_patterns; | |
2681 | signed char mw[BOARDMAX]; | |
2682 | int number_tried_moves = 0; | |
2683 | int pass; | |
2684 | int k; | |
2685 | int savemove = 0; | |
2686 | int saveworm = MAX_GOAL_WORMS; | |
2687 | int savecode = 0; | |
2688 | int eyemin = -1; /* Lower bound on the number of eyes. */ | |
2689 | int eyemax = -1; /* Upper bound on the number of eyes. */ | |
2690 | struct eyevalue probable_eyes; /* Best guess of eyevalue. */ | |
2691 | int escape_route; | |
2692 | const char *live_reason; | |
2693 | int move_cutoff; | |
2694 | int xpos; | |
2695 | int value1; | |
2696 | int value2; | |
2697 | int this_variation_number = count_variations - 1; | |
2698 | ||
2699 | SETUP_TRACE_INFO("owl_defend", str); | |
2700 | ||
2701 | shape_patterns.initialized = 0; | |
2702 | ||
2703 | str = find_origin(str); | |
2704 | ||
2705 | if (tt_get(&ttable, OWL_DEFEND, str, NO_MOVE, depth - stackp, NULL, | |
2706 | &value1, &value2, &xpos) == 2) { | |
2707 | ||
2708 | TRACE_CACHED_RESULT(value1, xpos); | |
2709 | if (move) | |
2710 | *move = xpos; | |
2711 | ||
2712 | if (value1 == LOSS) { | |
2713 | if (wormid) { | |
2714 | if (goal_worms_computed) | |
2715 | *wormid = value2; | |
2716 | else | |
2717 | *wormid = MAX_GOAL_WORMS; | |
2718 | } | |
2719 | } | |
2720 | ||
2721 | if (value1 == WIN || value1 == LOSS) | |
2722 | TRACE("%oVariation %d: ALIVE (cached)\n", this_variation_number); | |
2723 | else | |
2724 | TRACE("%oVariation %d: DEAD (cached)\n", this_variation_number); | |
2725 | ||
2726 | SGFTRACE(xpos, value1, "cached"); | |
2727 | ||
2728 | return value1; | |
2729 | } | |
2730 | ||
2731 | /* In order to get a defense move even if we seem to already have | |
2732 | * escaped and to reduce the impact of overestimated escape | |
2733 | * possibilities, we don't declare escape victory on the first move. | |
2734 | * | |
2735 | * FIXME: Should introduce a new owl depth value rather than having | |
2736 | * this hardwired value. | |
2737 | */ | |
2738 | escape_route = owl_escape_route(owl); | |
2739 | if (stackp > 2 && escape_route >= 5) { | |
2740 | /* FIXME: We probably should make distinction in the returned | |
2741 | * result whether the dragon lives by making two eyes or by | |
2742 | * escaping. | |
2743 | */ | |
2744 | TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); | |
2745 | SGFTRACE(0, WIN, "escaped"); | |
2746 | READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); | |
2747 | } | |
2748 | ||
2749 | /* If reading goes to deep or we run out of nodes, we assume life. */ | |
2750 | if (reading_limit_reached(&live_reason, this_variation_number)) { | |
2751 | SGFTRACE(0, WIN, live_reason); | |
2752 | READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); | |
2753 | } | |
2754 | ||
2755 | memset(mw, 0, sizeof(mw)); | |
2756 | local_owl_node_counter++; | |
2757 | global_owl_node_counter++; | |
2758 | ||
2759 | current_owl_data = owl; | |
2760 | memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache)); | |
2761 | ||
2762 | /* First see whether we might already be alive. */ | |
2763 | if (escape < MAX_ESCAPE) { | |
2764 | if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, 0, | |
2765 | &probable_eyes, &eyemin, &eyemax)) { | |
2766 | SGFTRACE(0, WIN, live_reason); | |
2767 | TRACE("%oVariation %d: ALIVE (%s)\n", | |
2768 | this_variation_number, live_reason); | |
2769 | READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); | |
2770 | } | |
2771 | } | |
2772 | else { | |
2773 | /* In this case we don't recompute eyes. However, to avoid accessing | |
2774 | * partially-random data left on stack, we copy eye data from the | |
2775 | * previous depth level. It should be reasonably close to the actual | |
2776 | * state of eyes. | |
2777 | */ | |
2778 | memcpy(owl->my_eye, owl->restore_from->my_eye, sizeof(owl->my_eye)); | |
2779 | memcpy(owl->half_eye, owl->restore_from->half_eye, sizeof(owl->half_eye)); | |
2780 | ||
2781 | vital_moves[0].pos = 0; | |
2782 | vital_moves[0].value = -1; | |
2783 | set_eyevalue(&probable_eyes, 0, 0, 0, 0); | |
2784 | } | |
2785 | ||
2786 | /* We try moves in four passes. | |
2787 | * stackp==0 stackp>0 | |
2788 | * 0. Vital moves in the interval [70..] [45..] | |
2789 | * 1. Shape moves | |
2790 | * 2. Vital moves in the interval [..69] [..44] | |
2791 | * 3. Tactical defense moves | |
2792 | */ | |
2793 | for (pass = 0; pass < 4; pass++) { | |
2794 | moves = NULL; | |
2795 | move_cutoff = 1; | |
2796 | ||
2797 | current_owl_data = owl; | |
2798 | switch (pass) { | |
2799 | /* Get the shape moves if we are in the right pass. */ | |
2800 | case 1: | |
2801 | ||
2802 | if (stackp > owl_branch_depth && number_tried_moves > 0) | |
2803 | continue; | |
2804 | ||
2805 | owl_shapes(&shape_patterns, shape_moves, color, owl, &owl_defendpat_db); | |
2806 | moves = shape_moves; | |
2807 | break; | |
2808 | ||
2809 | case 0: | |
2810 | case 2: | |
2811 | if (stackp > owl_branch_depth && number_tried_moves > 0) | |
2812 | continue; | |
2813 | ||
2814 | moves = vital_moves; | |
2815 | if (pass == 0 || stackp > owl_distrust_depth) { | |
2816 | if (stackp == 0) | |
2817 | move_cutoff = 70; | |
2818 | else if (eyemin + min_eyes(&probable_eyes) > 3) | |
2819 | move_cutoff = 25; | |
2820 | else if (eyemin + min_eyes(&probable_eyes) >= 3) | |
2821 | move_cutoff = 35; | |
2822 | else | |
2823 | move_cutoff = 45; | |
2824 | } | |
2825 | if (eyemax < 2 && stackp > 2) | |
2826 | move_cutoff = 99; /* Effectively disable vital moves. */ | |
2827 | break; | |
2828 | ||
2829 | case 3: | |
2830 | { | |
2831 | int goalcount = 0; | |
2832 | ||
2833 | /* If the goal is small, try a tactical defense. */ | |
2834 | ||
2835 | for (k = BOARDMIN; k < BOARDMAX; k++) | |
2836 | if (ON_BOARD(k)) | |
2837 | goalcount += owl->goal[k]; | |
2838 | ||
2839 | if (goalcount < 5) { | |
2840 | ||
2841 | /* Look for a tactical defense. This is primarily intended for | |
2842 | * the case where the whole dragon is a single string, therefore | |
2843 | * we only look at the string at the "origin". | |
2844 | * | |
2845 | * We only accept clearly effective tactical defenses here, | |
2846 | * using a liberty heuristic. The reason for this is problems | |
2847 | * with ineffective self ataris which do defend tactically but | |
2848 | * have no strategical effect other than wasting owl nodes or | |
2849 | * confusing the eye analysis. | |
2850 | */ | |
2851 | int dpos; | |
2852 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
2853 | int save_count_variations = count_variations; | |
2854 | ||
2855 | sgf_dumptree = NULL; | |
2856 | count_variations = 0; | |
2857 | if (attack_and_defend(str, NULL, NULL, NULL, &dpos) | |
2858 | && (approxlib(dpos, color, 2, NULL) > 1 | |
2859 | || does_capture_something(dpos, color))) { | |
2860 | TRACE("Found tactical defense for %1m at %1m.\n", str, dpos); | |
2861 | set_single_owl_move(shape_moves, dpos, "tactical_defense"); | |
2862 | moves = shape_moves; | |
2863 | } | |
2864 | sgf_dumptree = save_sgf_dumptree; | |
2865 | count_variations = save_count_variations; | |
2866 | } | |
2867 | if (!moves) | |
2868 | continue; | |
2869 | } | |
2870 | } /* switch (pass) */ | |
2871 | ||
2872 | /* For the up to MAX_MOVES best moves with value equal to | |
2873 | * move_cutoff or higher, try to defend the dragon and see if it | |
2874 | * can then be attacked. | |
2875 | */ | |
2876 | for (k = 0; k < MAX_MOVES; k++) { | |
2877 | int mpos; | |
2878 | int ko_move = -1; | |
2879 | int new_escape; | |
2880 | int wid = MAX_GOAL_WORMS; | |
2881 | ||
2882 | /* Consider only the highest scoring move if we're deeper than | |
2883 | * owl_branch_depth. | |
2884 | * | |
2885 | * FIXME: To behave as intended, k should be replaced by | |
2886 | * number_tried_moves. | |
2887 | */ | |
2888 | if (stackp > owl_branch_depth && k > 0) | |
2889 | break; | |
2890 | ||
2891 | current_owl_data = owl; | |
2892 | ||
2893 | if (pass == 1) { | |
2894 | if (!get_next_move_from_list(&shape_patterns, color, shape_moves, | |
2895 | move_cutoff, owl)) | |
2896 | break; | |
2897 | } | |
2898 | else | |
2899 | if (moves[k].value < move_cutoff) | |
2900 | break; | |
2901 | ||
2902 | mpos = moves[k].pos; | |
2903 | modify_eyefilling_move(&mpos, color); | |
2904 | ASSERT_ON_BOARD1(mpos); | |
2905 | ||
2906 | /* Have we already tested this move? */ | |
2907 | if (mw[mpos]) | |
2908 | continue; | |
2909 | ||
2910 | /* Try to make the move. */ | |
2911 | if (!komaster_trymove(mpos, color, moves[k].name, str, | |
2912 | &ko_move, savecode == 0)) | |
2913 | continue; | |
2914 | ||
2915 | new_escape = escape; | |
2916 | if (moves[k].escape) | |
2917 | new_escape++; | |
2918 | ||
2919 | TRACE("Trying %C %1m. Escape = %d. Current stack: ", | |
2920 | color, mpos, escape); | |
2921 | if (verbose) | |
2922 | dump_stack(); | |
2923 | ||
2924 | /* We have now made a move. Analyze the new position. */ | |
2925 | push_owl(&owl); | |
2926 | mw[mpos] = 1; | |
2927 | number_tried_moves++; | |
2928 | ||
2929 | /* Add the stone just played to the goal dragon, unless the | |
2930 | * pattern explicitly asked for not doing this. | |
2931 | */ | |
2932 | owl_update_goal(mpos, moves[k].same_dragon, moves[k].lunch, owl, 0, | |
2933 | moves[k].pattern_data); | |
2934 | mark_goal_in_sgf(owl->goal); | |
2935 | ||
2936 | if (!ko_move) { | |
2937 | int acode = do_owl_attack(str, NULL, &wid, owl, new_escape); | |
2938 | if (!acode) { | |
2939 | pop_owl(&owl); | |
2940 | popgo(); | |
2941 | if (sgf_dumptree) { | |
2942 | char winstr[192]; | |
2943 | sprintf(winstr, "defense effective)\n (%d variations", | |
2944 | count_variations - this_variation_number); | |
2945 | SGFTRACE(mpos, WIN, winstr); | |
2946 | } | |
2947 | close_pattern_list(color, &shape_patterns); | |
2948 | READ_RETURN(OWL_DEFEND, str, depth - stackp, move, mpos, WIN); | |
2949 | } | |
2950 | if (acode == GAIN) | |
2951 | saveworm = wid; | |
2952 | UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, mpos); | |
2953 | } | |
2954 | else { | |
2955 | if (do_owl_attack(str, NULL, NULL, owl, new_escape) != WIN) { | |
2956 | savemove = mpos; | |
2957 | savecode = KO_B; | |
2958 | } | |
2959 | } | |
2960 | ||
2961 | /* Undo the tested move. */ | |
2962 | pop_owl(&owl); | |
2963 | popgo(); | |
2964 | } | |
2965 | } | |
2966 | ||
2967 | close_pattern_list(color, &shape_patterns); | |
2968 | ||
2969 | if (savecode) { | |
2970 | if (savecode == LOSS) { | |
2971 | SGFTRACE(savemove, savecode, "defense effective (loss) - B"); | |
2972 | if (wormid) | |
2973 | *wormid = saveworm; | |
2974 | READ_RETURN2(OWL_DEFEND, str, depth - stackp, | |
2975 | move, savemove, savecode, saveworm); | |
2976 | } | |
2977 | else { | |
2978 | SGFTRACE(savemove, savecode, "defense effective (ko) - B"); | |
2979 | READ_RETURN(OWL_DEFEND, str, depth - stackp, move, savemove, savecode); | |
2980 | } | |
2981 | } | |
2982 | ||
2983 | if (number_tried_moves == 0 && min_eyes(&probable_eyes) >= 2) { | |
2984 | SGFTRACE(0, WIN, "genus probably >= 2"); | |
2985 | READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); | |
2986 | } | |
2987 | ||
2988 | ||
2989 | if (sgf_dumptree) { | |
2990 | char winstr[196]; | |
2991 | int print_genus = eyemin == 1 ? 1 : 0; | |
2992 | sprintf(winstr, "defense failed - genus %d)\n (%d variations", | |
2993 | print_genus, count_variations - this_variation_number); | |
2994 | SGFTRACE(0, 0, winstr); | |
2995 | } | |
2996 | ||
2997 | READ_RETURN0(OWL_DEFEND, str, depth - stackp); | |
2998 | } | |
2999 | ||
3000 | ||
3001 | /* Returns true if the dragon at (target) can be defended given | |
3002 | * two moves in a row. The first two moves to defend the | |
3003 | * dragon are given as (*defend1) and (*defend2). | |
3004 | */ | |
3005 | ||
3006 | int | |
3007 | owl_threaten_defense(int target, int *defend1, int *defend2) | |
3008 | { | |
3009 | struct owl_move_data moves[MAX_MOVES]; | |
3010 | int k; | |
3011 | int color = board[target]; | |
3012 | int result = 0; | |
3013 | struct local_owl_data *owl; | |
3014 | int reading_nodes_when_called = get_reading_node_counter(); | |
3015 | signed char saved_goal[BOARDMAX]; | |
3016 | double start = 0.0; | |
3017 | int tactical_nodes; | |
3018 | int move = 0; | |
3019 | int move2 = 0; | |
3020 | struct matched_patterns_list_data shape_patterns; | |
3021 | ||
3022 | shape_patterns.initialized = 0; | |
3023 | ||
3024 | result_certain = 1; | |
3025 | if (worm[target].unconditional_status == DEAD) | |
3026 | return 0; | |
3027 | ||
3028 | if (search_persistent_owl_cache(OWL_THREATEN_DEFENSE, target, 0, 0, | |
3029 | &result, defend1, defend2, NULL)) | |
3030 | return result; | |
3031 | ||
3032 | if (debug & DEBUG_OWL_PERFORMANCE) | |
3033 | start = gg_cputime(); | |
3034 | ||
3035 | TRACE("owl_threaten_defense %1m\n", target); | |
3036 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); | |
3037 | memcpy(saved_goal, owl->goal, sizeof(saved_goal)); | |
3038 | owl_make_domains(owl, NULL); | |
3039 | owl_shapes(&shape_patterns, moves, color, owl, &owl_defendpat_db); | |
3040 | for (k = 0; k < MAX_MOVES; k++) { | |
3041 | current_owl_data = owl; | |
3042 | if (!get_next_move_from_list(&shape_patterns, color, moves, 1, owl)) | |
3043 | break; | |
3044 | else { | |
3045 | if (moves[k].pos != NO_MOVE && moves[k].value > 0) | |
3046 | if (trymove(moves[k].pos, color, moves[k].name, target)) { | |
3047 | owl->lunches_are_current = 0; | |
3048 | owl_update_goal(moves[k].pos, moves[k].same_dragon, | |
3049 | moves[k].lunch, owl, 0, moves[k].pattern_data); | |
3050 | if (do_owl_defend(target, &move2, NULL, owl, 0) == WIN) { | |
3051 | move = moves[k].pos; | |
3052 | popgo(); | |
3053 | /* Don't return the second move if occupied before trymove */ | |
3054 | if (move2 != NO_MOVE && IS_STONE(board[move2])) | |
3055 | move2 = NO_MOVE; | |
3056 | result = WIN; | |
3057 | break; | |
3058 | } | |
3059 | else | |
3060 | popgo(); | |
3061 | memcpy(owl->goal, saved_goal, sizeof(saved_goal)); | |
3062 | } | |
3063 | } | |
3064 | } | |
3065 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
3066 | gg_assert(stackp == 0); | |
3067 | ||
3068 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
3069 | "owl_threaten_defense %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", | |
3070 | target, move, move2, result, local_owl_node_counter, | |
3071 | tactical_nodes, gg_cputime() - start); | |
3072 | ||
3073 | store_persistent_owl_cache(OWL_THREATEN_DEFENSE, target, 0, 0, | |
3074 | result, move, move2, 0, | |
3075 | tactical_nodes, owl->goal, board[target]); | |
3076 | ||
3077 | if (defend1) | |
3078 | *defend1 = move; | |
3079 | if (defend2) | |
3080 | *defend2 = move2; | |
3081 | ||
3082 | close_pattern_list(color, &shape_patterns); | |
3083 | return result; | |
3084 | } | |
3085 | ||
3086 | ||
3087 | ||
3088 | /* | |
3089 | * This function calls owl_determine_life() to get an eye estimate, | |
3090 | * and matchpat() for vital attack moves, and decides according to | |
3091 | * various policies (depth-dependant) whether the dragon should thus | |
3092 | * be considered alive. | |
3093 | */ | |
3094 | static int | |
3095 | owl_estimate_life(struct local_owl_data *owl, | |
3096 | struct local_owl_data *second_owl, | |
3097 | struct owl_move_data vital_moves[MAX_MOVES], | |
3098 | const char **live_reason, int does_attack, | |
3099 | struct eyevalue *probable_eyes, int *eyemin, int *eyemax) | |
3100 | { | |
3101 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
3102 | int save_count_variations = count_variations; | |
3103 | struct owl_move_data dummy_moves[MAX_MOVES]; | |
3104 | int other = OTHER_COLOR(owl->color); | |
3105 | ||
3106 | sgf_dumptree = NULL; | |
3107 | count_variations = 0; | |
3108 | ||
3109 | owl_determine_life(owl, second_owl, does_attack, vital_moves, | |
3110 | probable_eyes, eyemin, eyemax); | |
3111 | ||
3112 | matches_found = 0; | |
3113 | memset(found_matches, 0, sizeof(found_matches)); | |
3114 | ||
3115 | if (get_level() >= 8) { | |
3116 | memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache)); | |
3117 | if (!does_attack) { | |
3118 | clear_owl_move_data(dummy_moves); | |
3119 | matchpat(owl_shapes_callback, other, | |
3120 | &owl_vital_apat_db, dummy_moves, owl->goal); | |
3121 | } | |
3122 | else if (max_eyes(probable_eyes) >= 2) | |
3123 | matchpat(owl_shapes_callback, other, | |
3124 | &owl_vital_apat_db, vital_moves, owl->goal); | |
3125 | } | |
3126 | ||
3127 | if ((debug & DEBUG_EYES) && (debug & DEBUG_OWL)) | |
3128 | gprintf("owl: eyemin=%d matches_found=%d\n", *eyemin, matches_found); | |
3129 | if (*eyemin >= matches_found) | |
3130 | *eyemin -= matches_found; | |
3131 | else | |
3132 | *eyemin = 0; | |
3133 | ||
3134 | sgf_dumptree = save_sgf_dumptree; | |
3135 | count_variations = save_count_variations; | |
3136 | ||
3137 | if (*eyemin >= 2 | |
3138 | || (*eyemin == 1 && min_eyes(probable_eyes) >= 4) | |
3139 | || (stackp > owl_distrust_depth | |
3140 | && min_eyes(probable_eyes) >= 2 | |
3141 | && !matches_found)) { | |
3142 | if (*eyemin >= 2) | |
3143 | *live_reason = "2 or more secure eyes"; | |
3144 | else if (*eyemin == 1 && min_eyes(probable_eyes) >= 4) | |
3145 | *live_reason = "1 secure eye, likely >= 4"; | |
3146 | else if (stackp > owl_distrust_depth | |
3147 | && min_eyes(probable_eyes) >= 2 | |
3148 | && !matches_found) | |
3149 | *live_reason = "getting deep, looks lively"; | |
3150 | else | |
3151 | gg_assert(0); | |
3152 | return 1; | |
3153 | } | |
3154 | ||
3155 | if (!does_attack | |
3156 | && (*eyemin + matches_found >= 2 | |
3157 | || (*eyemin + matches_found == 1 && min_eyes(probable_eyes) >= 4) | |
3158 | || (stackp > owl_distrust_depth | |
3159 | && min_eyes(probable_eyes) >= 2))) { | |
3160 | /* We are not yet alive only due to owl vital attack patterns matching. | |
3161 | * Let's try to defend against it. | |
3162 | */ | |
3163 | owl_add_move(vital_moves, dummy_moves[0].defense_pos, | |
3164 | dummy_moves[0].value, dummy_moves[0].name, | |
3165 | SAME_DRAGON_CONNECTED, NO_MOVE, 0, NO_MOVE, MAX_MOVES, NULL); | |
3166 | } | |
3167 | ||
3168 | return 0; | |
3169 | } | |
3170 | ||
3171 | ||
3172 | /* | |
3173 | * This function is invoked from do_owl_attack() and do_owl_defend() | |
3174 | * for each node to determine whether the the dragon has sufficient | |
3175 | * eye potential to live. It also generates vital moves to attack or | |
3176 | * defend the eyes. There are two distinct sources for eyes. The first | |
3177 | * is the eyespaces found by make_domains() and evaluated by | |
3178 | * compute_eyes_pessimistic(). The second is the lunches found by | |
3179 | * owl_find_lunches() and evaluated by sniff_lunch(). | |
3180 | * | |
3181 | * The best guess of the eye potential is stored as an eyevalue in | |
3182 | * *probable_eyes. This is not entirely reliable though since the | |
3183 | * graph matching techniques in optics.c fail to understand subtleties | |
3184 | * like atari inside the eyespace, cutting points in the wall, and | |
3185 | * shortage of outside liberties. (The patterns in owl_vital_apats.db | |
3186 | * are used to compensate for this. See do_owl_attack() and | |
3187 | * do_owl_defend() for how these are used.) Also the estimates from | |
3188 | * sniff_lunch() are fairly unreliable. | |
3189 | * | |
3190 | * A lower and upper bound on the number of eyes are returned in | |
3191 | * *eyemin and *eyemax. The value of *eyemin must be offset by the | |
3192 | * matches of owl_vital_apats.db. If that number is 2 or larger, we | |
3193 | * should be certain of life. | |
3194 | * | |
3195 | * Vital moves to attack or defend eyes are returned in the moves[] | |
3196 | * array. Also moves to reduce the uncertainty of the eye estimates | |
3197 | * are added to this array, but with smaller move values. The | |
3198 | * parameter does_attack determines whether to generate vital attack | |
3199 | * moves or vital defense moves. | |
3200 | * | |
3201 | * The dragon is specified by the information in the owl struct. The | |
3202 | * color of the dragon is passed in the color parameter. | |
3203 | * | |
3204 | * For use in the semeai code, a second dragon can be provided. Set | |
3205 | * this to NULL when only one dragon is involved. | |
3206 | */ | |
3207 | ||
3208 | static void | |
3209 | owl_determine_life(struct local_owl_data *owl, | |
3210 | struct local_owl_data *second_owl, | |
3211 | int does_attack, | |
3212 | struct owl_move_data *moves, | |
3213 | struct eyevalue *probable_eyes, int *eyemin, int *eyemax) | |
3214 | { | |
3215 | int color = owl->color; | |
3216 | struct eye_data *eye = owl->my_eye; | |
3217 | int mw[BOARDMAX]; /* mark relevant eye origins */ | |
3218 | int mz[BOARDMAX]; /* mark potentially irrelevant eye origins */ | |
3219 | int vital_values[BOARDMAX]; | |
3220 | int dummy_eyemin = 0; | |
3221 | int dummy_eyemax = 0; | |
3222 | struct eyevalue eyevalue; | |
3223 | struct eyevalue eyevalue_list[BOARDMAX/2]; | |
3224 | int eyes_attack_points[BOARDMAX/2]; | |
3225 | int pessimistic_min; | |
3226 | int attack_point; | |
3227 | int defense_point; | |
3228 | int pos; | |
3229 | int k; | |
3230 | int lunch; | |
3231 | int num_eyes = 0; | |
3232 | int num_lunches = 0; | |
3233 | int save_debug = debug; | |
3234 | memset(vital_values, 0, sizeof(vital_values)); | |
3235 | ||
3236 | if (!eyemin) | |
3237 | eyemin = &dummy_eyemin; | |
3238 | if (!eyemax) | |
3239 | eyemax = &dummy_eyemax; | |
3240 | ||
3241 | *eyemin = 0; | |
3242 | *eyemax = 0; | |
3243 | ||
3244 | /* Turn off eye debugging if we're not also debugging owl. */ | |
3245 | if (!(debug & DEBUG_OWL)) | |
3246 | debug &= ~DEBUG_EYES; | |
3247 | ||
3248 | clear_owl_move_data(moves); | |
3249 | ||
3250 | if (!owl->lunches_are_current) | |
3251 | owl_find_lunches(owl); | |
3252 | ||
3253 | if (0) { | |
3254 | for (k = 0; k < MAX_LUNCHES; k++) | |
3255 | if (owl->lunch[k] != NO_MOVE) | |
3256 | gprintf("owl lunch %1m, attack %1m, defend %1m\n", | |
3257 | owl->lunch[k], | |
3258 | owl->lunch_attack_point[k], | |
3259 | owl->lunch_defense_point[k]); | |
3260 | } | |
3261 | ||
3262 | owl_make_domains(owl, second_owl); | |
3263 | ||
3264 | owl_find_relevant_eyespaces(owl, mw, mz); | |
3265 | ||
3266 | /* Reset halfeye data. Set topological eye value to something big. */ | |
3267 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
3268 | if (ON_BOARD(pos)) { | |
3269 | owl->half_eye[pos].type = 0; | |
3270 | owl->half_eye[pos].value = 10.0; | |
3271 | } | |
3272 | } | |
3273 | ||
3274 | /* Find topological half eyes and false eyes. */ | |
3275 | find_half_and_false_eyes(color, eye, owl->half_eye, mw); | |
3276 | ||
3277 | /* The eyespaces may have been split or changed in other ways by the | |
3278 | * topological analysis, so we need to regenerate them and once more | |
3279 | * determine which ones are relevant. | |
3280 | */ | |
3281 | partition_eyespaces(owl->my_eye, owl->color); | |
3282 | owl_find_relevant_eyespaces(owl, mw, mz); | |
3283 | ||
3284 | set_eyevalue(probable_eyes, 0, 0, 0, 0); | |
3285 | ||
3286 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
3287 | if (ON_BOARD(pos) && mw[pos] > 1) { | |
3288 | int value = 0; | |
3289 | const char *reason = ""; | |
3290 | compute_eyes_pessimistic(pos, &eyevalue, &pessimistic_min, | |
3291 | &attack_point, &defense_point, | |
3292 | eye, owl->half_eye); | |
3293 | ||
3294 | /* If the eyespace is more in contact with own stones not in the goal, | |
3295 | * than with ones in the goal, there is a risk that we can be cut off | |
3296 | * from a major part of the eyespace. Thus we can't trust the opinion | |
3297 | * of compute_eyes(). | |
3298 | * | |
3299 | * (Obviously this is a quite fuzzy heuristic. With more accurate | |
3300 | * connection analysis in the owl code we could do this more robustly.) | |
3301 | */ | |
3302 | if (mw[pos] < mz[pos] | |
3303 | || (mw[pos] < 3 * mz[pos] && mz[pos] > 5)) | |
3304 | pessimistic_min = 0; | |
3305 | ||
3306 | /* It appears that this policy is needed no longer. */ | |
3307 | #if 0 | |
3308 | /* If this eyespace includes an owl inessential string, we must assume | |
3309 | * that the pessimistic min is 0. | |
3310 | */ | |
3311 | if (pessimistic_min > 0) { | |
3312 | for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { | |
3313 | if (ON_BOARD(pos2) | |
3314 | && eye[pos2].origin == pos | |
3315 | && owl->inessential[pos2]) { | |
3316 | pessimistic_min = 0; | |
3317 | break; | |
3318 | } | |
3319 | } | |
3320 | } | |
3321 | #endif | |
3322 | ||
3323 | eyes_attack_points[num_eyes] = NO_MOVE; | |
3324 | eyevalue_list[num_eyes] = eyevalue; | |
3325 | *eyemin += pessimistic_min; | |
3326 | ||
3327 | /* Fill in the value field for use by the owl_eyespace() function. */ | |
3328 | eye[pos].value = eyevalue; | |
3329 | ||
3330 | /* This shortcut has been disabled for two reasons: | |
3331 | * 1. Due to the vital attack moves being able to later reduce | |
3332 | * the *eyemin, we can't say that a certain *eyemin is | |
3333 | * sufficient. | |
3334 | * 2. This part of the code is in no way time critical. | |
3335 | */ | |
3336 | #if 0 | |
3337 | /* Found two certain eyes---look no further. */ | |
3338 | if (*eyemin >= 2) { | |
3339 | debug = save_debug; | |
3340 | return 2; | |
3341 | } | |
3342 | #endif | |
3343 | ||
3344 | if (eye_move_urgency(&eyevalue)) { | |
3345 | value = 50; | |
3346 | if (max_eyes(&eyevalue) - min_eyes(&eyevalue) == 2) | |
3347 | value = 70; | |
3348 | else if (max_eyes(&eyevalue) - pessimistic_min == 2) | |
3349 | value = 60; | |
3350 | reason = "vital move"; | |
3351 | } | |
3352 | else if (max_eyes(&eyevalue) != pessimistic_min) { | |
3353 | if (max_eyes(&eyevalue) - pessimistic_min == 2) | |
3354 | value = 40; | |
3355 | else | |
3356 | value = 30; | |
3357 | reason = "marginal eye space"; | |
3358 | } | |
3359 | ||
3360 | if (value > 0) { | |
3361 | if (does_attack && attack_point != NO_MOVE) { | |
3362 | if (vital_values[attack_point] > 0) { | |
3363 | value += vital_values[attack_point]; | |
3364 | if (value > 98) | |
3365 | value = 98; /* Higher values may get special interpretation. */ | |
3366 | } | |
3367 | ||
3368 | TRACE("%s at %1m, score %d (eye at %1m, value %s, pessimistic_min %d)\n", | |
3369 | reason, attack_point, value, | |
3370 | pos, eyevalue_to_string(&eyevalue), pessimistic_min); | |
3371 | ||
3372 | if (eye[attack_point].marginal | |
3373 | && modify_stupid_eye_vital_point(owl, &attack_point, 1)) | |
3374 | TRACE("vital point looked stupid, moved it to %1m\n", | |
3375 | attack_point); | |
3376 | ||
3377 | if (attack_point != NO_MOVE) { | |
3378 | owl_add_move(moves, attack_point, value, reason, | |
3379 | SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, | |
3380 | 0, NO_MOVE, MAX_MOVES, NULL); | |
3381 | vital_values[attack_point] = value; | |
3382 | eyes_attack_points[num_eyes] = attack_point; | |
3383 | } | |
3384 | } | |
3385 | ||
3386 | /* The reason for the last set of tests is that we don't | |
3387 | * want to play a self atari in e.g. this position | |
3388 | * | |
3389 | * |XXX. | |
3390 | * |OOX. | |
3391 | * |.OX. | |
3392 | * |XOXX | |
3393 | * |XOOX | |
3394 | * |O*OX | |
3395 | * +---- | |
3396 | * | |
3397 | * but it's okay in this position | |
3398 | * | |
3399 | * |XXXXX | |
3400 | * |....X | |
3401 | * |OOOOX | |
3402 | * |.XXOX | |
3403 | * |.*XOX | |
3404 | * +----- | |
3405 | * | |
3406 | * In both cases * is the vital point according to the graph | |
3407 | * matching. The significant difference is that in the first | |
3408 | * case the vital point is adjacent to stones in the goal. | |
3409 | */ | |
3410 | else if (!does_attack | |
3411 | && defense_point != NO_MOVE | |
3412 | && board[defense_point] == EMPTY | |
3413 | && (!liberty_of_goal(defense_point, owl) | |
3414 | || !is_self_atari(defense_point, color) | |
3415 | || is_ko(defense_point, color, NULL) | |
3416 | || safe_move(defense_point, color) != 0)) { | |
3417 | if (vital_values[defense_point] > 0) { | |
3418 | value += vital_values[defense_point]; | |
3419 | if (value > 98) | |
3420 | value = 98; /* Higher values may get special interpretation. */ | |
3421 | } | |
3422 | ||
3423 | TRACE("%s at %1m, score %d (eye at %1m, value %s, pessimistic_min %d)\n", | |
3424 | reason, defense_point, value, pos, | |
3425 | eyevalue_to_string(&eyevalue), pessimistic_min); | |
3426 | ||
3427 | if ((eye[defense_point].marginal | |
3428 | || eye[defense_point].origin != pos) | |
3429 | && modify_stupid_eye_vital_point(owl, &defense_point, 0)) | |
3430 | TRACE("vital point looked stupid, moved it to %1m\n", | |
3431 | defense_point); | |
3432 | ||
3433 | if (defense_point != NO_MOVE) { | |
3434 | owl_add_move(moves, defense_point, value, reason, | |
3435 | SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, | |
3436 | 0, NO_MOVE, MAX_MOVES, NULL); | |
3437 | vital_values[defense_point] = value; | |
3438 | } | |
3439 | } | |
3440 | } | |
3441 | num_eyes++; | |
3442 | } | |
3443 | } | |
3444 | ||
3445 | /* Sniff each lunch for nutritional value. The assumption is that | |
3446 | * capturing the lunch is gote, therefore the number of half eyes | |
3447 | * equals the MINIMUM number of eyes yielded by the resulting eye | |
3448 | * space. | |
3449 | */ | |
3450 | { | |
3451 | for (lunch = 0; (lunch < MAX_LUNCHES); lunch++) | |
3452 | if (owl->lunch[lunch] != NO_MOVE | |
3453 | && owl->lunch_defense_point[lunch] != NO_MOVE) { | |
3454 | int value = 0; | |
3455 | int lunch_min; | |
3456 | int lunch_probable; | |
3457 | int lunch_max; | |
3458 | struct eyevalue e; | |
3459 | sniff_lunch(owl->lunch[lunch], | |
3460 | &lunch_min, &lunch_probable, &lunch_max, owl); | |
3461 | ||
3462 | set_eyevalue(&e, 0, 0, lunch_probable, lunch_probable); | |
3463 | *eyemax += lunch_max; | |
3464 | ||
3465 | if (lunch_probable == 0) { | |
3466 | if (countstones(owl->lunch[lunch]) == 1) | |
3467 | continue; | |
3468 | value = 20; | |
3469 | } | |
3470 | else if (lunch_probable == 1 && lunch_max == 1) | |
3471 | value = 60 + countstones(owl->lunch[lunch]); | |
3472 | else if (lunch_probable == 1 && lunch_max == 2) | |
3473 | value = 70 + countstones(owl->lunch[lunch]); | |
3474 | else | |
3475 | value = 75 + countstones(owl->lunch[lunch]); | |
3476 | ||
3477 | if (owl->lunch_attack_code[lunch] != WIN) | |
3478 | value -= 10; | |
3479 | ||
3480 | if (does_attack) { | |
3481 | defense_point = improve_lunch_defense(owl->lunch[lunch], | |
3482 | owl->lunch_defense_point[lunch]); | |
3483 | ||
3484 | if (vital_values[defense_point]) { | |
3485 | /* The point here is that the move which saves the lunch also | |
3486 | * attacks an eye. So this attack move reduces the global eye | |
3487 | * potential. The eyes arithmetic for probable_eyes has then | |
3488 | * to be adapted accordingly. | |
3489 | */ | |
3490 | int ne; | |
3491 | for (ne = 0; ne < num_eyes - num_lunches; ne++) | |
3492 | if (eyes_attack_points[ne] == defense_point) | |
3493 | break; | |
3494 | gg_assert(ne < num_eyes - num_lunches); | |
3495 | /* merge eye values */ | |
3496 | add_eyevalues(&eyevalue_list[ne], &e, &eyevalue_list[ne]); | |
3497 | /* and adjust */ | |
3498 | eyevalue_list[ne].a = 0; | |
3499 | eyevalue_list[ne].b = 0; | |
3500 | } | |
3501 | else { | |
3502 | num_lunches++; | |
3503 | eyevalue_list[num_eyes++] = e; | |
3504 | } | |
3505 | ||
3506 | TRACE("save lunch at %1m with %1m, score %d, probable eye %d, max eye %d\n", | |
3507 | owl->lunch[lunch], defense_point, value, | |
3508 | lunch_probable, lunch_max); | |
3509 | owl_add_move(moves, defense_point, value, | |
3510 | "save lunch", SAME_DRAGON_MAYBE_CONNECTED, | |
3511 | NO_MOVE, 0, NO_MOVE, MAX_MOVES, NULL); | |
3512 | } | |
3513 | else { | |
3514 | attack_point = improve_lunch_attack(owl->lunch[lunch], | |
3515 | owl->lunch_attack_point[lunch]); | |
3516 | TRACE("eat lunch at %1m with %1m, score %d, probable eye %d, max eye %d\n", | |
3517 | owl->lunch[lunch], attack_point, value, | |
3518 | lunch_probable, lunch_max); | |
3519 | /* We only remember the lunch for owl_update_goal() if the lunch | |
3520 | * cannot be defended with ko after the move. | |
3521 | * If we capture the lunch by an illegal ko capture, we become | |
3522 | * ko master with this move, and hence the above is true. | |
3523 | */ | |
3524 | if (owl->lunch_attack_code[lunch] == WIN | |
3525 | || is_illegal_ko_capture(attack_point, owl->color)) | |
3526 | owl_add_move(moves, attack_point, value, "eat lunch", | |
3527 | SAME_DRAGON_MAYBE_CONNECTED, owl->lunch[lunch], | |
3528 | 0, NO_MOVE, MAX_MOVES, NULL); | |
3529 | else | |
3530 | owl_add_move(moves, attack_point, value, "eat lunch", | |
3531 | SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, 0, NO_MOVE, | |
3532 | MAX_MOVES, NULL); | |
3533 | num_lunches++; | |
3534 | eyevalue_list[num_eyes++] = e; | |
3535 | } | |
3536 | } | |
3537 | } | |
3538 | ||
3539 | /* now, totalize the eye potential */ | |
3540 | { | |
3541 | int ne; | |
3542 | for (ne = 0; ne < num_eyes - num_lunches; ne++) | |
3543 | add_eyevalues(probable_eyes, &eyevalue_list[ne], probable_eyes); | |
3544 | ||
3545 | *eyemax += max_eyes(probable_eyes); | |
3546 | /* If we have at least two different eyespaces and can create one eye | |
3547 | * in sente, we assume there's a chance to create another one. This is | |
3548 | * needed because optics code don't know about eyespaces influencing | |
3549 | * each other and combination moves (i.e. double threats to create an | |
3550 | * eye). | |
3551 | */ | |
3552 | if (num_eyes - num_lunches > 1 && max_eye_threat(probable_eyes) > 1) | |
3553 | *eyemax += 1; | |
3554 | ||
3555 | for (; ne < num_eyes; ne++) | |
3556 | add_eyevalues(probable_eyes, &eyevalue_list[ne], probable_eyes); | |
3557 | } | |
3558 | ||
3559 | debug = save_debug; | |
3560 | } | |
3561 | ||
3562 | ||
3563 | /* The eyespaces we want to evaluate are the ones which | |
3564 | * are adjacent to the dragon (whose stones comprise the | |
3565 | * support of goal) which are not GRAY bordered. These | |
3566 | * are the eyespaces of the dragon. Now we find their | |
3567 | * origins. | |
3568 | * | |
3569 | * It is required that there are at least two distinct connections, | |
3570 | * adjacent or diagonal, between non-marginal eyespace vertices and | |
3571 | * stones of the goal dragon. Otherwise there is a risk that we | |
3572 | * include irrelevant eye spaces. | |
3573 | */ | |
3574 | ||
3575 | static void | |
3576 | owl_find_relevant_eyespaces(struct local_owl_data *owl, | |
3577 | int mw[BOARDMAX], int mz[BOARDMAX]) | |
3578 | { | |
3579 | int pos; | |
3580 | int eye_color; | |
3581 | int k; | |
3582 | struct eye_data *eye = owl->my_eye; | |
3583 | ||
3584 | if (owl->color == WHITE) | |
3585 | eye_color = WHITE; | |
3586 | else | |
3587 | eye_color = BLACK; | |
3588 | ||
3589 | memset(mw, 0, BOARDMAX * sizeof(mw[0])); | |
3590 | memset(mz, 0, BOARDMAX * sizeof(mz[0])); | |
3591 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
3592 | if (board[pos] == owl->color) { | |
3593 | for (k = 0; k < 8; k++) { | |
3594 | int pos2 = pos + delta[k]; | |
3595 | if (ON_BOARD(pos2) | |
3596 | && eye[pos2].color == eye_color | |
3597 | && !eye[pos2].marginal) { | |
3598 | if (owl->goal[pos]) | |
3599 | mw[eye[pos2].origin]++; | |
3600 | else | |
3601 | mz[eye[pos2].origin]++; | |
3602 | } | |
3603 | } | |
3604 | } | |
3605 | } | |
3606 | } | |
3607 | ||
3608 | /* Case 1. | |
3609 | * | |
3610 | * The optics code occasionally comes up with stupid vital moves, like | |
3611 | * a in this position: | |
3612 | * | |
3613 | * ----+ | |
3614 | * O...| | |
3615 | * OX..| | |
3616 | * OX..| | |
3617 | * O.X.| | |
3618 | * .O.a| | |
3619 | * ....| | |
3620 | * | |
3621 | * This function moves such moves to the second line. | |
3622 | * | |
3623 | * Case 2. | |
3624 | * | |
3625 | * In this position the optics code can suggest the empty 1-2 point as | |
3626 | * vital move for the eyespace on the right edge. That is okay for attack | |
3627 | * but obviously not for defense. | |
3628 | * | |
3629 | * ----+ | |
3630 | * XO.O| | |
3631 | * XOOX| | |
3632 | * XXO.| | |
3633 | * .XOO| | |
3634 | * .XXX| | |
3635 | * | |
3636 | * Case 3. | |
3637 | * | |
3638 | * Playing into a snapback is usually not an effective way to destroy | |
3639 | * an eye. | |
3640 | * | |
3641 | * XOOO| | |
3642 | * XOXX| | |
3643 | * XXO.| | |
3644 | * .XXO| | |
3645 | * ....| | |
3646 | * | |
3647 | * This function changes the attack point to NO_MOVE (i.e. removes it). | |
3648 | */ | |
3649 | static int | |
3650 | modify_stupid_eye_vital_point(struct local_owl_data *owl, int *vital_point, | |
3651 | int is_attack_point) | |
3652 | { | |
3653 | int up; | |
3654 | int right; | |
3655 | int k; | |
3656 | int libs[2]; | |
3657 | ||
3658 | /* Case 1. */ | |
3659 | for (k = 0; k < 4; k++) { | |
3660 | up = delta[k]; | |
3661 | if (ON_BOARD(*vital_point - up)) | |
3662 | continue; | |
3663 | ||
3664 | if (board[*vital_point + up] != EMPTY) | |
3665 | continue; | |
3666 | ||
3667 | right = delta[(k+1) % 4]; | |
3668 | ||
3669 | if (board[*vital_point + right] != EMPTY | |
3670 | || board[*vital_point - right] != EMPTY) | |
3671 | continue; | |
3672 | ||
3673 | if (board[*vital_point + 2 * up] != EMPTY | |
3674 | || board[*vital_point + up + right] != EMPTY | |
3675 | || board[*vital_point + up - right] != EMPTY) { | |
3676 | *vital_point += up; | |
3677 | return 1; | |
3678 | } | |
3679 | } | |
3680 | ||
3681 | /* Case 2. */ | |
3682 | if (!is_attack_point) { | |
3683 | if (approxlib(*vital_point, OTHER_COLOR(owl->color), 1, NULL) == 0) { | |
3684 | for (k = 4; k < 8; k++) { | |
3685 | int pos = *vital_point + delta[k]; | |
3686 | if (board[pos] == OTHER_COLOR(owl->color) | |
3687 | && countlib(pos) == 1) { | |
3688 | findlib(pos, 1, vital_point); | |
3689 | return 1; | |
3690 | } | |
3691 | } | |
3692 | } | |
3693 | } | |
3694 | ||
3695 | /* Case 3. */ | |
3696 | if (is_attack_point | |
3697 | && does_capture_something(*vital_point, OTHER_COLOR(owl->color)) | |
3698 | && accuratelib(*vital_point, OTHER_COLOR(owl->color), 2, libs) == 1 | |
3699 | && !attack(libs[0], NULL)) { | |
3700 | *vital_point = NO_MOVE; | |
3701 | return 1; | |
3702 | } | |
3703 | ||
3704 | return 0; | |
3705 | } | |
3706 | ||
3707 | ||
3708 | /* The purpose of this function is to avoid moves which needlessly | |
3709 | * fill in an eye. A typical example, from ld_owl:188, is | |
3710 | * | |
3711 | * -----+ | |
3712 | * .O.OX| | |
3713 | * XOOXX| | |
3714 | * XXOOX| | |
3715 | * .XXO.| | |
3716 | * ..XOO| | |
3717 | * ..XXX| | |
3718 | * | |
3719 | * where various patterns manage to propose the eye-filling move on | |
3720 | * the top edge instead of capturing the opponent stones and get two | |
3721 | * solid eyes. This function modifies the move accordingly. | |
3722 | */ | |
3723 | static int | |
3724 | modify_eyefilling_move(int *move, int color) | |
3725 | { | |
3726 | int k; | |
3727 | int r; | |
3728 | int other = OTHER_COLOR(color); | |
3729 | /* Only do this for a small eye. */ | |
3730 | for (k = 0; k < 4; k++) | |
3731 | if (ON_BOARD(*move + delta[k]) && board[*move + delta[k]] != color) | |
3732 | return 0; | |
3733 | ||
3734 | for (r = 4; r < 8; r++) | |
3735 | if (board[*move + delta[r]] == other | |
3736 | && countlib(*move + delta[r]) == 1) { | |
3737 | for (k = 0; k < 4; k++) | |
3738 | if (board[*move + delta[k]] == color | |
3739 | && countlib(*move + delta[k]) == 1 | |
3740 | && !adjacent_strings(*move + delta[r], *move + delta[k])) | |
3741 | break; | |
3742 | ||
3743 | if (k == 4) { | |
3744 | int new_move; | |
3745 | findlib(*move + delta[r], 1, &new_move); | |
3746 | TRACE("Changing eyefilling move at %1m to capture at %1m.\n", | |
3747 | *move, new_move); | |
3748 | *move = new_move; | |
3749 | return 1; | |
3750 | } | |
3751 | } | |
3752 | ||
3753 | return 0; | |
3754 | } | |
3755 | ||
3756 | ||
3757 | /* | |
3758 | * Generates up to max_moves moves, attempting to attack or defend the goal | |
3759 | * dragon. The found moves are put in moves, an array of owl_move_data | |
3760 | * structs, starting in the position 'initial'. The entries in the array are | |
3761 | * sorted by value with moves[initial] having highest priority. When no more | |
3762 | * moves are available this is indicated by value and coordinates in the array | |
3763 | * being -1. | |
3764 | * | |
3765 | * This function automatically initializes the owl_safe_move cache the | |
3766 | * pattern list. WATCH OUT: This has to be matched with a call to | |
3767 | * close_pattern_list(pattern_list)!!! | |
3768 | * | |
3769 | * Returns 1 if at least one move is found, or 0 if no move is found. | |
3770 | */ | |
3771 | ||
3772 | static void | |
3773 | owl_shapes(struct matched_patterns_list_data *pattern_list, | |
3774 | struct owl_move_data moves[MAX_MOVES], | |
3775 | int color, struct local_owl_data *owl, struct pattern_db *type) | |
3776 | { | |
3777 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
3778 | int save_count_variations = count_variations; | |
3779 | sgf_dumptree = NULL; | |
3780 | count_variations = 0; | |
3781 | ||
3782 | current_owl_data = owl; | |
3783 | ||
3784 | clear_owl_move_data(moves); | |
3785 | ||
3786 | /* We must reset the owl safe_move_cache before starting the | |
3787 | * pattern matching. The cache is used by owl_shapes_callback(). | |
3788 | */ | |
3789 | memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache)); | |
3790 | init_pattern_list(pattern_list); | |
3791 | matchpat(collect_owl_shapes_callbacks, color, type, pattern_list, owl->goal); | |
3792 | ||
3793 | sgf_dumptree = save_sgf_dumptree; | |
3794 | count_variations = save_count_variations; | |
3795 | } | |
3796 | ||
3797 | ||
3798 | /* This function contains all the expensive checks for a matched pattern. */ | |
3799 | static int | |
3800 | check_pattern_hard(int move, int color, struct pattern *pattern, int ll) | |
3801 | { | |
3802 | int constraint_checked = 0; | |
3803 | int safe_move_checked = 0; | |
3804 | ||
3805 | /* The very first check is whether we can disregard the pattern due | |
3806 | * due to an owl safe_move_cache lookup. | |
3807 | */ | |
3808 | if (!(pattern->class & CLASS_s)) | |
3809 | if (current_owl_data->safe_move_cache[move]) { | |
3810 | if (current_owl_data->safe_move_cache[move] == 1) | |
3811 | return 0; | |
3812 | else | |
3813 | safe_move_checked = 1; | |
3814 | } | |
3815 | ||
3816 | /* If the constraint is cheap to check, we do this first. */ | |
3817 | if ((pattern->autohelper_flag & HAVE_CONSTRAINT) | |
3818 | && pattern->constraint_cost < 0.45) { | |
3819 | if (!pattern->autohelper(ll, move, color, 0)) | |
3820 | return 0; | |
3821 | constraint_checked = 1; | |
3822 | } | |
3823 | ||
3824 | /* For sacrifice patterns, the survival of the stone to be played is | |
3825 | * not checked. Otherwise we discard moves which can be captured. | |
3826 | * Illegal ko captures are accepted for ko analysis. | |
3827 | */ | |
3828 | if (!(pattern->class & CLASS_s) && !safe_move_checked) { | |
3829 | if (!owl_safe_move(move, color)) { | |
3830 | if (0) | |
3831 | TRACE(" move at %1m wasn't safe, discarded\n", move); | |
3832 | return 0; | |
3833 | } | |
3834 | if (!is_legal(move, color)) { | |
3835 | if (0) | |
3836 | TRACE(" move at %1m wasn't legal, discarded\n", move); | |
3837 | return 0; | |
3838 | } | |
3839 | } | |
3840 | ||
3841 | /* For class n patterns, the pattern is contingent on an opponent | |
3842 | * move at * not being captured. | |
3843 | * | |
3844 | * We can't use owl_safe_move() here because we would try the wrong color. | |
3845 | */ | |
3846 | if (pattern->class & CLASS_n) { | |
3847 | if (safe_move(move, OTHER_COLOR(color)) == 0) { | |
3848 | if (0) | |
3849 | TRACE(" opponent can't play safely at %1m, move discarded\n", move); | |
3850 | return 0; | |
3851 | } | |
3852 | } | |
3853 | ||
3854 | /* If the pattern has a constraint, call the autohelper to see | |
3855 | * if the pattern must be rejected. | |
3856 | */ | |
3857 | if ((pattern->autohelper_flag & HAVE_CONSTRAINT) && !constraint_checked) | |
3858 | if (!pattern->autohelper(ll, move, color, 0)) | |
3859 | return 0; | |
3860 | return 1; | |
3861 | } | |
3862 | ||
3863 | ||
3864 | /* This initializes a pattern list, allocating memory for 200 patterns. | |
3865 | * If more patterns need to be stored, collect_owl_shapes_callbacks will | |
3866 | * dynamically reallocate additional memory. | |
3867 | * The space for list->pattern_list is allocated here. | |
3868 | * | |
3869 | * This function is automatically called from owl_shapes. Every call here | |
3870 | * has to be matched by a call to close_pattern_list below. | |
3871 | */ | |
3872 | static void | |
3873 | init_pattern_list(struct matched_patterns_list_data *list) | |
3874 | { | |
3875 | gg_assert(!list->initialized); | |
3876 | ||
3877 | list->counter = 0; | |
3878 | list->used = 0; | |
3879 | ||
3880 | list->pattern_list = malloc(200 * sizeof(list->pattern_list[0])); | |
3881 | list->list_size = 200; | |
3882 | gg_assert(list->pattern_list != NULL); | |
3883 | list->pattern_heap = NULL; | |
3884 | ||
3885 | if (0) | |
3886 | gprintf("List at %x has new array at %x\n", list, list->pattern_list); | |
3887 | ||
3888 | list->initialized = 1; | |
3889 | } | |
3890 | ||
3891 | ||
3892 | /* This function has to get called before the memory of *list is freed | |
3893 | * in the calling function. | |
3894 | */ | |
3895 | static void | |
3896 | close_pattern_list(int color, struct matched_patterns_list_data *list) | |
3897 | { | |
3898 | if (list->initialized) { | |
3899 | if (0) | |
3900 | gprintf("%d patterns matched, %d patterns checked\n", list->counter, | |
3901 | list->used); | |
3902 | if (0) | |
3903 | gprintf("Pattern list at %x freed for list at %x\n", | |
3904 | list->pattern_list, list); | |
3905 | if (allpats && verbose) { | |
3906 | int i; | |
3907 | int found_one = 0; | |
3908 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
3909 | int save_count_variations = count_variations; | |
3910 | sgf_dumptree = NULL; | |
3911 | count_variations = 0; | |
3912 | ||
3913 | if (!current_owl_data->lunches_are_current) | |
3914 | owl_find_lunches(current_owl_data); | |
3915 | ||
3916 | if (!list->pattern_heap) | |
3917 | pattern_list_build_heap(list); | |
3918 | ||
3919 | for (i = 0; i < list->heap_num_patterns; i++) | |
3920 | if (check_pattern_hard(list->pattern_heap[i]->move, color, | |
3921 | list->pattern_heap[i]->pattern, | |
3922 | list->pattern_heap[i]->ll)) { | |
3923 | if (!found_one) { | |
3924 | TRACE("Remaining valid (but unused) patterns at stack: "); | |
3925 | dump_stack(); | |
3926 | found_one = 1; | |
3927 | } | |
3928 | TRACE("Pattern %s found at %1m with value %d\n", | |
3929 | list->pattern_heap[i]->pattern->name, | |
3930 | list->pattern_heap[i]->move, | |
3931 | (int) list->pattern_heap[i]->pattern->value); | |
3932 | } | |
3933 | ||
3934 | sgf_dumptree = save_sgf_dumptree; | |
3935 | count_variations = save_count_variations; | |
3936 | } | |
3937 | ||
3938 | free(list->pattern_list); | |
3939 | free(list->pattern_heap); | |
3940 | } | |
3941 | list->counter = -1; | |
3942 | } | |
3943 | ||
3944 | ||
3945 | /* Can be called from gdb for debugging: | |
3946 | * (gdb) set dump_pattern_list(&shape_patterns) | |
3947 | */ | |
3948 | void | |
3949 | dump_pattern_list(struct matched_patterns_list_data *list) | |
3950 | { | |
3951 | int i; | |
3952 | struct matched_pattern_data *matched_pattern; | |
3953 | if (!list->initialized) | |
3954 | return; | |
3955 | gprintf("%oList size %d. %d Patterns in list, %d have been used.", | |
3956 | list->list_size, list->counter, list->used); | |
3957 | for (i = 0; i < list->counter; i++) { | |
3958 | matched_pattern = &list->pattern_list[i]; | |
3959 | gprintf("%o\n Pattern %s (orient. %d) at %1m, value %f.", | |
3960 | matched_pattern->pattern->name, matched_pattern->ll, | |
3961 | matched_pattern->move, matched_pattern->pattern->value); | |
3962 | if (matched_pattern->next_pattern_index != -1) | |
3963 | gprintf("%o * "); | |
3964 | } | |
3965 | gprintf("%o\n"); | |
3966 | ||
3967 | gprintf("%oCurrent heap ordering: \n"); | |
3968 | for (i = 0; i < list->heap_num_patterns; i++) { | |
3969 | matched_pattern = list->pattern_heap[i]; | |
3970 | gprintf("%o %s (%1m), %f; ", matched_pattern->pattern->name, | |
3971 | matched_pattern->move, matched_pattern->pattern->value); | |
3972 | } | |
3973 | gprintf("\n"); | |
3974 | } | |
3975 | ||
3976 | ||
3977 | /* This function stores a found pattern in the list for later evaluation. | |
3978 | * The only processing done is computing the position of the move, and | |
3979 | * forgetting the color. | |
3980 | */ | |
3981 | static void | |
3982 | collect_owl_shapes_callbacks(int anchor, int color, struct pattern *pattern, | |
3983 | int ll, void *data) | |
3984 | { | |
3985 | struct matched_patterns_list_data *matched_patterns = data; | |
3986 | struct matched_pattern_data *next_pattern; | |
3987 | ||
3988 | UNUSED(color); /* The calling function has to remember that. */ | |
3989 | ||
3990 | if (matched_patterns->counter >= matched_patterns->list_size) { | |
3991 | matched_patterns->list_size += 100; | |
3992 | matched_patterns->pattern_list | |
3993 | = realloc(matched_patterns->pattern_list, | |
3994 | matched_patterns->list_size | |
3995 | * sizeof(matched_patterns->pattern_list[0])); | |
3996 | } | |
3997 | ||
3998 | next_pattern = &matched_patterns->pattern_list[matched_patterns->counter]; | |
3999 | next_pattern->move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor); | |
4000 | next_pattern->value = pattern->value; | |
4001 | next_pattern->ll = ll; | |
4002 | next_pattern->anchor = anchor; | |
4003 | next_pattern->pattern = pattern; | |
4004 | next_pattern->next_pattern_index = -1; | |
4005 | ||
4006 | matched_patterns->counter++; | |
4007 | } | |
4008 | ||
4009 | ||
4010 | #define MAX_STORED_REASONS 4 | |
4011 | ||
4012 | static int | |
4013 | valuate_combinable_pattern_chain(struct matched_patterns_list_data *list, | |
4014 | int pos) | |
4015 | { | |
4016 | /* FIXME: This is just a first attempt at pattern combination. | |
4017 | * Improve it. The first idea is to differentiate between | |
4018 | * move reason types. For instance, when there is a secure | |
4019 | * eye already, a threat to create another is more severe. | |
4020 | * | |
4021 | * This will certainly involve splitting the function into | |
4022 | * attack and defense versions. | |
4023 | */ | |
4024 | ||
4025 | int pattern_index = list->first_pattern_index[pos]; | |
4026 | int num_capture_threats = 0; | |
4027 | int capture_threats[MAX_STORED_REASONS]; | |
4028 | int num_eye_threats = 0; | |
4029 | int eye_threats[MAX_STORED_REASONS]; | |
4030 | int num_reverse_sente = 0; | |
4031 | int reverse_sente_against[MAX_STORED_REASONS]; | |
4032 | int num_move_reasons; | |
4033 | float full_value = 0.0; | |
4034 | ||
4035 | ASSERT1(pattern_index != -1, pos); | |
4036 | ||
4037 | do { | |
4038 | struct matched_pattern_data *pattern_data = (list->pattern_list | |
4039 | + pattern_index); | |
4040 | struct pattern_attribute *attribute; | |
4041 | ||
4042 | /* Skip patterns that haven't passed constraint validation. */ | |
4043 | if (pattern_data->pattern) { | |
4044 | for (attribute = pattern_data->pattern->attributes; | |
4045 | attribute->type != LAST_ATTRIBUTE; | |
4046 | attribute++) { | |
4047 | int k; | |
4048 | int target = AFFINE_TRANSFORM(attribute->offset, pattern_data->ll, | |
4049 | pattern_data->move); | |
4050 | ||
4051 | switch (attribute->type) { | |
4052 | case THREATENS_TO_CAPTURE: | |
4053 | if (num_capture_threats < MAX_STORED_REASONS) { | |
4054 | ASSERT1(IS_STONE(board[target]), target); | |
4055 | target = find_origin(target); | |
4056 | ||
4057 | for (k = 0; k < num_capture_threats; k++) { | |
4058 | if (capture_threats[k] == target) | |
4059 | break; | |
4060 | } | |
4061 | ||
4062 | if (k == num_capture_threats) { | |
4063 | capture_threats[num_capture_threats++] = target; | |
4064 | full_value += pattern_data->pattern->value; | |
4065 | } | |
4066 | } | |
4067 | ||
4068 | break; | |
4069 | ||
4070 | case THREATENS_EYE: | |
4071 | if (num_eye_threats < MAX_STORED_REASONS) { | |
4072 | target = current_owl_data->my_eye[target].origin; | |
4073 | ||
4074 | for (k = 0; k < num_eye_threats; k++) { | |
4075 | if (eye_threats[k] == target) | |
4076 | break; | |
4077 | } | |
4078 | ||
4079 | if (k == num_eye_threats) { | |
4080 | eye_threats[num_eye_threats++] = target; | |
4081 | full_value += pattern_data->pattern->value; | |
4082 | } | |
4083 | } | |
4084 | ||
4085 | break; | |
4086 | ||
4087 | case REVERSE_SENTE: | |
4088 | if (num_reverse_sente < MAX_STORED_REASONS) { | |
4089 | ASSERT1(board[target] == EMPTY, target); | |
4090 | ||
4091 | for (k = 0; k < num_reverse_sente; k++) { | |
4092 | if (reverse_sente_against[k] == target) | |
4093 | break; | |
4094 | } | |
4095 | ||
4096 | if (k == num_reverse_sente) { | |
4097 | reverse_sente_against[num_reverse_sente++] = target; | |
4098 | full_value += pattern_data->pattern->value; | |
4099 | } | |
4100 | } | |
4101 | ||
4102 | break; | |
4103 | ||
4104 | default: | |
4105 | gg_assert(0); | |
4106 | } | |
4107 | } | |
4108 | } | |
4109 | ||
4110 | pattern_index = pattern_data->next_pattern_index; | |
4111 | } while (pattern_index >= 0); | |
4112 | ||
4113 | ||
4114 | num_move_reasons = num_capture_threats + num_eye_threats + num_reverse_sente; | |
4115 | if (num_move_reasons <= 1) { | |
4116 | /* Not much to combine, eh? */ | |
4117 | return 0; | |
4118 | } | |
4119 | ||
4120 | if (num_move_reasons == 2) | |
4121 | return gg_min(gg_normalize_float2int(full_value, 1.0), 75); | |
4122 | if (num_move_reasons == 3) | |
4123 | return gg_min(gg_normalize_float2int(full_value * 0.85, 1.0), 90); | |
4124 | return gg_min(gg_normalize_float2int(full_value * 0.75, 1.0), 99); | |
4125 | } | |
4126 | ||
4127 | ||
4128 | #if USE_BDIST | |
4129 | ||
4130 | /* Compute the squared of the distance of a point on the board to the | |
4131 | * center of the board. | |
4132 | */ | |
4133 | static int | |
4134 | bdist(int move) | |
4135 | { | |
4136 | /* i = 0: idist = - (board_size - 1) | |
4137 | * i = board_size -1 : idist = board_size - 1 | |
4138 | */ | |
4139 | int idist = 2*I(move) - board_size + 1; | |
4140 | int jdist = 2*J(move) - board_size + 1; | |
4141 | return idist*idist + jdist*jdist; | |
4142 | } | |
4143 | ||
4144 | ||
4145 | /* NOTICE : In order to stabilize the regression test results, | |
4146 | * arbitrary parameters like pattern memory address and move position | |
4147 | * have been included in the sorting algorithm. | |
4148 | */ | |
4149 | ||
4150 | #define BETTER_PATTERN(a, b) \ | |
4151 | ((a)->value > (b)->value \ | |
4152 | || ((a)->value == (b)->value \ | |
4153 | && ((a)->pattern < (b)->pattern \ | |
4154 | || ((a)->pattern == (b)->pattern \ | |
4155 | && ((a)->bdist < (b)->bdist \ | |
4156 | || ((a)->bdist == (b)->bdist \ | |
4157 | && (a)->move < (b)->move)))))) | |
4158 | ||
4159 | #else /* not USE_BDIST */ | |
4160 | ||
4161 | #define BETTER_PATTERN(a, b) \ | |
4162 | ((a)->value > (b)->value \ | |
4163 | || ((a)->value == (b)->value \ | |
4164 | && ((a)->pattern < (b)->pattern \ | |
4165 | || ((a)->pattern == (b)->pattern \ | |
4166 | && (a)->move < (b)->move)))) | |
4167 | ||
4168 | #endif /* not USE_BDIST */ | |
4169 | ||
4170 | ||
4171 | static void | |
4172 | pattern_list_prepare(struct matched_patterns_list_data *list) | |
4173 | { | |
4174 | int k; | |
4175 | int pos; | |
4176 | ||
4177 | list->heap_num_patterns = 0; | |
4178 | ||
4179 | /* This is more than needed in case of (combinable) pattern chains, | |
4180 | * but it is easier to allocate more than to count real number of | |
4181 | * heap elements first. | |
4182 | */ | |
4183 | if (list->counter > 0) { /* avoid malloc(0) */ | |
4184 | list->pattern_heap = malloc(list->counter * sizeof(*(list->pattern_heap))); | |
4185 | gg_assert(list->pattern_heap != NULL); | |
4186 | } | |
4187 | else { | |
4188 | /* free() has defined behaviour for NULL pointer */ | |
4189 | list->pattern_heap = NULL; | |
4190 | } | |
4191 | ||
4192 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
4193 | list->first_pattern_index[pos] = -1; | |
4194 | ||
4195 | for (k = 0; k < list->counter; k++) { | |
4196 | int move = list->pattern_list[k].move; | |
4197 | ||
4198 | #if USE_BDIST | |
4199 | list->pattern_list[k].bdist = bdist(move); | |
4200 | #endif | |
4201 | ||
4202 | /* Allocate heap elements for normal patterns. Link combinable | |
4203 | * patterns in chains. | |
4204 | */ | |
4205 | if (!(list->pattern_list[k].pattern->class & CLASS_c)) | |
4206 | list->pattern_heap[list->heap_num_patterns++] = &list->pattern_list[k]; | |
4207 | else { | |
4208 | list->pattern_list[k].next_pattern_index = list->first_pattern_index[move]; | |
4209 | list->first_pattern_index[move] = k; | |
4210 | } | |
4211 | } | |
4212 | ||
4213 | /* Allocate one heap element for each chain of combinable patterns | |
4214 | * and calculate initial chain values (as if all patterns passed | |
4215 | * constraint validation). | |
4216 | */ | |
4217 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
4218 | if (list->first_pattern_index[pos] != -1) { | |
4219 | struct matched_pattern_data *pattern_data | |
4220 | = &list->pattern_list[list->first_pattern_index[pos]]; | |
4221 | ||
4222 | pattern_data->value = valuate_combinable_pattern_chain(list, pos); | |
4223 | list->pattern_heap[list->heap_num_patterns++] = pattern_data; | |
4224 | } | |
4225 | } | |
4226 | ||
4227 | if (list->heap_num_patterns > 0) | |
4228 | pattern_list_build_heap(list); | |
4229 | } | |
4230 | ||
4231 | ||
4232 | /* Fast heap building. Takes O(n) only. */ | |
4233 | static void | |
4234 | pattern_list_build_heap(struct matched_patterns_list_data *list) | |
4235 | { | |
4236 | int k; | |
4237 | int limit = list->heap_num_patterns / 2; | |
4238 | ||
4239 | for (k = limit; --k >= 0;) { | |
4240 | int parent; | |
4241 | int child; | |
4242 | struct matched_pattern_data *pattern_data = list->pattern_heap[k]; | |
4243 | ||
4244 | for (parent = k; parent < limit; parent = child) { | |
4245 | child = 2 * parent + 1; | |
4246 | if (child + 1 < list->heap_num_patterns | |
4247 | && BETTER_PATTERN(list->pattern_heap[child + 1], | |
4248 | list->pattern_heap[child])) | |
4249 | child++; | |
4250 | ||
4251 | if (BETTER_PATTERN(pattern_data, list->pattern_heap[child])) | |
4252 | break; | |
4253 | ||
4254 | list->pattern_heap[parent] = list->pattern_heap[child]; | |
4255 | } | |
4256 | ||
4257 | list->pattern_heap[parent] = pattern_data; | |
4258 | } | |
4259 | } | |
4260 | ||
4261 | ||
4262 | /* Pops patterns list's heap once. */ | |
4263 | static void | |
4264 | pattern_list_pop_heap_once(struct matched_patterns_list_data *list) | |
4265 | { | |
4266 | int parent; | |
4267 | int child; | |
4268 | ||
4269 | list->heap_num_patterns--; | |
4270 | for (parent = 0; 2 * parent + 1 < list->heap_num_patterns; parent = child) { | |
4271 | child = 2 * parent + 1; | |
4272 | if (BETTER_PATTERN(list->pattern_heap[child + 1], | |
4273 | list->pattern_heap[child])) | |
4274 | child++; | |
4275 | ||
4276 | if (BETTER_PATTERN(list->pattern_heap[list->heap_num_patterns], | |
4277 | list->pattern_heap[child])) | |
4278 | break; | |
4279 | ||
4280 | list->pattern_heap[parent] = list->pattern_heap[child]; | |
4281 | } | |
4282 | ||
4283 | list->pattern_heap[parent] = list->pattern_heap[list->heap_num_patterns]; | |
4284 | } | |
4285 | ||
4286 | ||
4287 | /* Sink top element of heap because it got devalued. This happens | |
4288 | * when a combinable pattern doesn't pass check_pattern_hard() -- it | |
4289 | * is no longer counted and its whole chain's value is reduced. | |
4290 | */ | |
4291 | static void | |
4292 | pattern_list_sink_heap_top_element(struct matched_patterns_list_data *list) | |
4293 | { | |
4294 | int parent; | |
4295 | int child; | |
4296 | struct matched_pattern_data *heap_top_element = list->pattern_heap[0]; | |
4297 | ||
4298 | for (parent = 0; 2 * parent + 1 < list->heap_num_patterns; parent = child) { | |
4299 | child = 2 * parent + 1; | |
4300 | if (child + 1 < list->heap_num_patterns | |
4301 | && BETTER_PATTERN(list->pattern_heap[child + 1], | |
4302 | list->pattern_heap[child])) | |
4303 | child++; | |
4304 | ||
4305 | if (BETTER_PATTERN(heap_top_element, | |
4306 | list->pattern_heap[child])) | |
4307 | break; | |
4308 | ||
4309 | list->pattern_heap[parent] = list->pattern_heap[child]; | |
4310 | } | |
4311 | ||
4312 | list->pattern_heap[parent] = heap_top_element; | |
4313 | } | |
4314 | ||
4315 | ||
4316 | /* Adds all goal strings in the pattern area to the cuts[] list, if there | |
4317 | * is more than one. | |
4318 | */ | |
4319 | static void | |
4320 | generate_cut_list(struct pattern *pattern, int ll, int anchor, | |
4321 | int cuts[MAX_CUTS], struct local_owl_data *owl) | |
4322 | { | |
4323 | int k; | |
4324 | int num = 0; | |
4325 | signed char mark[BOARDMAX]; | |
4326 | ||
4327 | memset(mark, 0, BOARDMAX); | |
4328 | for (k = 0; k < pattern->patlen; k++) { | |
4329 | int pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor); | |
4330 | if (!IS_STONE(board[pos])) | |
4331 | continue; | |
4332 | pos = find_origin(pos); | |
4333 | if (!mark[pos] && board[pos] == owl->color && owl->goal[pos]) { | |
4334 | cuts[num++] = pos; | |
4335 | mark[pos] = 1; | |
4336 | if (num == MAX_CUTS) | |
4337 | return; | |
4338 | } | |
4339 | } | |
4340 | if (num == 1) | |
4341 | cuts[0] = NO_MOVE; | |
4342 | else if ((debug & DEBUG_SPLIT_OWL) && num > 1) | |
4343 | gprintf("Move provokes %d cuts, among them %1m and %1m.\n", num, | |
4344 | cuts[0], cuts[1]); | |
4345 | } | |
4346 | ||
4347 | /* This function searches in the previously stored list of matched | |
4348 | * patterns for the highest valued unused patterns that have a valid | |
4349 | * constraint. It returns the moves at the next empty positions in | |
4350 | * the array moves[]. Empty positions in the moves array are marked | |
4351 | * by having value <= 0. There must be enough empty positions in the | |
4352 | * list. | |
4353 | * | |
4354 | * If the highest valued pattern found has a value less than cutoff, | |
4355 | * no move is returned. Returns 1 if a move is found, 0 otherwise. | |
4356 | * | |
4357 | * This function also dispatches constraint validation of combinable | |
4358 | * pattern chains. Whenever a pattern from a chain fails constraints, | |
4359 | * the chain is reevaluated and most likely drops in value enough to | |
4360 | * let other patterns (or chains) climb to the top of pattern heap. | |
4361 | * | |
4362 | * This function loops until enough moves are found or the end of the | |
4363 | * list is reached. | |
4364 | */ | |
4365 | ||
4366 | static int | |
4367 | get_next_move_from_list(struct matched_patterns_list_data *list, int color, | |
4368 | struct owl_move_data *moves, int cutoff, | |
4369 | struct local_owl_data *owl) | |
4370 | { | |
4371 | int move_found = 0; | |
4372 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
4373 | int save_count_variations = count_variations; | |
4374 | ||
4375 | sgf_dumptree = NULL; | |
4376 | count_variations = 0; | |
4377 | ||
4378 | /* Prepare pattern list if needed. */ | |
4379 | if (!list->pattern_heap) | |
4380 | pattern_list_prepare(list); | |
4381 | ||
4382 | while (list->heap_num_patterns > 0) { | |
4383 | int k; | |
4384 | struct matched_pattern_data *pattern_data; | |
4385 | struct pattern *pattern; | |
4386 | int move; | |
4387 | int value; | |
4388 | int ll; | |
4389 | int anchor; | |
4390 | int next_pattern_index; | |
4391 | ||
4392 | /* Peek top element of heap associated with pattern list. */ | |
4393 | if (list->pattern_heap[0]->value < cutoff) | |
4394 | break; | |
4395 | ||
4396 | pattern_data = list->pattern_heap[0]; | |
4397 | pattern = list->pattern_heap[0]->pattern; | |
4398 | move = list->pattern_heap[0]->move; | |
4399 | value = list->pattern_heap[0]->value; | |
4400 | ll = list->pattern_heap[0]->ll; | |
4401 | anchor = list->pattern_heap[0]->anchor; | |
4402 | next_pattern_index = list->pattern_heap[0]->next_pattern_index; | |
4403 | ||
4404 | list->used++; | |
4405 | ||
4406 | ASSERT_ON_BOARD1(move); | |
4407 | for (k = 0; k < MAX_MOVES; k++) { | |
4408 | if (moves[k].pos == move || moves[k].value <= 0) | |
4409 | break; | |
4410 | } | |
4411 | ||
4412 | if (moves[k].pos == move) { | |
4413 | /* No point in testing this pattern/chain. Throw it out. */ | |
4414 | pattern_list_pop_heap_once(list); | |
4415 | continue; | |
4416 | } | |
4417 | ||
4418 | /* There has to be an empty space. */ | |
4419 | gg_assert(k < MAX_MOVES); | |
4420 | ||
4421 | /* If a pattern chain was devalued because its last pattern didn't | |
4422 | * pass constraint validation, `pattern' is set NULL (i.e. nothing | |
4423 | * more to test). Note that devalued chains might still be | |
4424 | * useful, i.e. if 2 of 3 patterns passed check_pattern_hard(). | |
4425 | */ | |
4426 | if (pattern == NULL | |
4427 | || check_pattern_hard(move, color, pattern, ll)) { | |
4428 | if (next_pattern_index == -1) { | |
4429 | /* Normal pattern or last one in a chain. */ | |
4430 | pattern_list_pop_heap_once(list); | |
4431 | } | |
4432 | else { | |
4433 | /* We just validated a non-last pattern in a chain. Since the | |
4434 | * chain remains at the same value, we keep the heap structure | |
4435 | * untouched. However, we need to set heap's top to point to | |
4436 | * next pattern of the chain. | |
4437 | */ | |
4438 | list->pattern_heap[0] = list->pattern_list + next_pattern_index; | |
4439 | list->pattern_heap[0]->value = value; | |
4440 | continue; | |
4441 | } | |
4442 | ||
4443 | moves[k].pos = move; | |
4444 | moves[k].value = value; | |
4445 | clear_cut_list(moves[k].cuts); | |
4446 | move_found = 1; | |
4447 | ||
4448 | if (pattern && !(pattern->class & CLASS_c)) { | |
4449 | moves[k].name = pattern->name; | |
4450 | TRACE("Pattern %s found at %1m with value %d\n", | |
4451 | pattern->name, move, moves[k].value); | |
4452 | ||
4453 | if (pattern->class & CLASS_C) { | |
4454 | /* Cut possible. (Only used in attack patterns). Try to find | |
4455 | * goal strings in the pattern area and store them in the cut list | |
4456 | * if there is more than one. | |
4457 | */ | |
4458 | DEBUG(DEBUG_SPLIT_OWL, | |
4459 | "Generating cut list for move at %1m.\n", move); | |
4460 | generate_cut_list(pattern, ll, anchor, moves[k].cuts, owl); | |
4461 | } | |
4462 | ||
4463 | if (pattern->class & CLASS_B) | |
4464 | moves[k].same_dragon = SAME_DRAGON_NOT_CONNECTED; | |
4465 | else if (pattern->class & CLASS_a) { | |
4466 | moves[k].same_dragon = SAME_DRAGON_ALL_CONNECTED; | |
4467 | moves[k].pattern_data = pattern_data; | |
4468 | } | |
4469 | else if (!(pattern->class & CLASS_b)) | |
4470 | moves[k].same_dragon = SAME_DRAGON_CONNECTED; | |
4471 | else { | |
4472 | int i; | |
4473 | enum same_dragon_value same_dragon = SAME_DRAGON_MAYBE_CONNECTED; | |
4474 | ||
4475 | /* If we do not yet know whether the move belongs to the | |
4476 | * same dragon, we see whether another pattern can clarify. | |
4477 | */ | |
4478 | for (i = 0; i < list->heap_num_patterns; i++) { | |
4479 | pattern_data = list->pattern_heap[i]; | |
4480 | ||
4481 | if (pattern_data->pattern | |
4482 | && pattern_data->move == move | |
4483 | && ((pattern_data->pattern->class & CLASS_B) | |
4484 | || !(pattern_data->pattern->class & CLASS_b))) { | |
4485 | if (check_pattern_hard(move, color, pattern_data->pattern, | |
4486 | pattern_data->ll)) { | |
4487 | TRACE("Additionally pattern %s found at %1m\n", | |
4488 | pattern_data->pattern->name, move); | |
4489 | if (pattern_data->pattern->class & CLASS_B) | |
4490 | same_dragon = SAME_DRAGON_NOT_CONNECTED; | |
4491 | else if (pattern_data->pattern->class & CLASS_a) { | |
4492 | same_dragon = SAME_DRAGON_ALL_CONNECTED; | |
4493 | moves[k].pattern_data = pattern_data; | |
4494 | } | |
4495 | else | |
4496 | same_dragon = SAME_DRAGON_CONNECTED; | |
4497 | ||
4498 | break; | |
4499 | } | |
4500 | } | |
4501 | } | |
4502 | ||
4503 | moves[k].same_dragon = same_dragon; | |
4504 | } | |
4505 | } | |
4506 | else { | |
4507 | moves[k].name = "Pattern combination"; | |
4508 | if (verbose) { | |
4509 | /* FIXME: write names of all patterns in chain. */ | |
4510 | } | |
4511 | ||
4512 | /* FIXME: Add handling of CLASS_b. | |
4513 | * | |
4514 | * FIXME: It is silently assumed that all patterns in the | |
4515 | * chain have the same class. When the last pattern in | |
4516 | * chain didn't match, this will not work at all. | |
4517 | */ | |
4518 | if (pattern && pattern->class & CLASS_B) | |
4519 | moves[k].same_dragon = SAME_DRAGON_NOT_CONNECTED; | |
4520 | else if (pattern && pattern->class & CLASS_a) { | |
4521 | moves[k].same_dragon = SAME_DRAGON_ALL_CONNECTED; | |
4522 | moves[k].pattern_data = list->pattern_heap[0]; | |
4523 | } | |
4524 | else | |
4525 | moves[k].same_dragon = SAME_DRAGON_CONNECTED; | |
4526 | } | |
4527 | ||
4528 | if (pattern && pattern->class & CLASS_E) | |
4529 | moves[k].escape = 1; | |
4530 | else | |
4531 | moves[k].escape = 0; | |
4532 | ||
4533 | break; | |
4534 | } | |
4535 | else { /* !check_pattern_hard(...) */ | |
4536 | if (!(pattern->class & CLASS_c)) { | |
4537 | /* Just forget about it. */ | |
4538 | pattern_list_pop_heap_once(list); | |
4539 | } | |
4540 | else { | |
4541 | /* Set this pattern to not matched and advance to next one in | |
4542 | * the chain, if any. | |
4543 | */ | |
4544 | list->pattern_heap[0]->pattern = NULL; | |
4545 | if (next_pattern_index != -1) | |
4546 | list->pattern_heap[0] = list->pattern_list + next_pattern_index; | |
4547 | ||
4548 | /* Reevaluate chain and adjust heap structure accordingly. */ | |
4549 | list->pattern_heap[0]->value = valuate_combinable_pattern_chain(list, | |
4550 | move); | |
4551 | pattern_list_sink_heap_top_element(list); | |
4552 | } | |
4553 | } | |
4554 | } | |
4555 | ||
4556 | sgf_dumptree = save_sgf_dumptree; | |
4557 | count_variations = save_count_variations; | |
4558 | ||
4559 | return move_found; | |
4560 | } | |
4561 | ||
4562 | ||
4563 | /* This function takes an array of already found moves (passed as | |
4564 | * 'data') and looks for moves to replace these. Only moves near | |
4565 | * the goal dragon are considered. | |
4566 | */ | |
4567 | static void | |
4568 | owl_shapes_callback(int anchor, int color, struct pattern *pattern, | |
4569 | int ll, void *data) | |
4570 | { | |
4571 | int tval; /* trial move and its value */ | |
4572 | int move; | |
4573 | struct owl_move_data *moves = data; /* considered moves passed as data */ | |
4574 | enum same_dragon_value same_dragon = SAME_DRAGON_MAYBE_CONNECTED; | |
4575 | int escape = 0; | |
4576 | int defense_pos; | |
4577 | ||
4578 | /* Pick up the location of the move */ | |
4579 | move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor); | |
4580 | ||
4581 | /* Before we do any expensive reading, check whether this move | |
4582 | * already is known with a higher value or if there are too many | |
4583 | * other moves with higher value. | |
4584 | */ | |
4585 | if (!allpats) { | |
4586 | int k; | |
4587 | for (k = 0; k < MAX_MOVES; k++) { | |
4588 | if (moves[k].value == -1) | |
4589 | break; | |
4590 | if (moves[k].pos == move) { | |
4591 | if (moves[k].value >= pattern->value) | |
4592 | return; | |
4593 | else | |
4594 | break; | |
4595 | } | |
4596 | } | |
4597 | if (k == MAX_MOVES && moves[MAX_MOVES - 1].value >= pattern->value) | |
4598 | return; | |
4599 | } | |
4600 | ||
4601 | if (!check_pattern_hard(move, color, pattern, ll)) | |
4602 | return; | |
4603 | ||
4604 | /* and work out the value of this move */ | |
4605 | if (pattern->helper) { | |
4606 | /* ask helper function to consider the move */ | |
4607 | gg_assert(0); | |
4608 | DEBUG(DEBUG_HELPER, " asking helper to consider '%s'+%d at %1m\n", | |
4609 | pattern->name, ll, move); | |
4610 | tval = pattern->helper(pattern, ll, move, color); | |
4611 | ||
4612 | if (tval > 0) { | |
4613 | DEBUG(DEBUG_HELPER, "helper likes pattern '%s' value %d at %1m\n", | |
4614 | pattern->name, tval, move); | |
4615 | } | |
4616 | else { | |
4617 | DEBUG(DEBUG_HELPER, " helper does not like pattern '%s' at %1m\n", | |
4618 | pattern->name, move); | |
4619 | return; /* pattern matcher does not like it */ | |
4620 | } | |
4621 | } | |
4622 | else { /* no helper */ | |
4623 | tval = (int) pattern->value; | |
4624 | } | |
4625 | ||
4626 | /* having made it here, we have made it through all the extra checks */ | |
4627 | ||
4628 | TRACE("Pattern %s found at %1m with value %d\n", pattern->name, move, tval); | |
4629 | ||
4630 | if (pattern->class & CLASS_B) | |
4631 | same_dragon = SAME_DRAGON_NOT_CONNECTED; | |
4632 | else if (pattern->class & CLASS_b) | |
4633 | same_dragon = SAME_DRAGON_MAYBE_CONNECTED; | |
4634 | else if (pattern->class & CLASS_a) { | |
4635 | same_dragon = SAME_DRAGON_ALL_CONNECTED; | |
4636 | /* FIXME: Currently this code is only used with vital attack | |
4637 | * moves, so there is no use for the "a" classification. If it | |
4638 | * would be needed in the future it's necessary to set up a struct | |
4639 | * matched_pattern_data here to be passed to owl_add_move(). This | |
4640 | * is not all that simple with respect to memory management | |
4641 | * however. Notice that a local variable in this function would go | |
4642 | * out of scope too early. | |
4643 | */ | |
4644 | gg_assert(0); | |
4645 | } | |
4646 | else | |
4647 | same_dragon = SAME_DRAGON_CONNECTED; | |
4648 | ||
4649 | if (pattern->class & CLASS_E) | |
4650 | escape = 1; | |
4651 | else | |
4652 | escape = 0; | |
4653 | ||
4654 | /* Finally, check for position of defense move. */ | |
4655 | { | |
4656 | int k; | |
4657 | defense_pos = move; | |
4658 | for (k = 0; k < pattern->patlen; k++) | |
4659 | if (pattern->patn[k].att == ATT_not) | |
4660 | defense_pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor); | |
4661 | } | |
4662 | ||
4663 | owl_add_move(moves, move, tval, pattern->name, same_dragon, NO_MOVE, | |
4664 | escape, defense_pos, MAX_MOVES, NULL); | |
4665 | } | |
4666 | ||
4667 | ||
4668 | /* Add a move to the list of candidate moves */ | |
4669 | ||
4670 | static void | |
4671 | owl_add_move(struct owl_move_data *moves, int move, int value, | |
4672 | const char *reason, enum same_dragon_value same_dragon, int lunch, | |
4673 | int escape, int defense_pos, int max_moves, | |
4674 | struct matched_pattern_data *pattern_data) | |
4675 | { | |
4676 | int k; | |
4677 | ||
4678 | if (!found_matches[move]) { | |
4679 | found_matches[move] = 1; | |
4680 | matches_found++; | |
4681 | } | |
4682 | ||
4683 | /* Add the new move to the list of already found moves, if the value | |
4684 | * is sufficently large. We keep the list sorted. | |
4685 | * | |
4686 | * First we must see if this move already is in the list. | |
4687 | */ | |
4688 | for (k = 0; k < max_moves; k++) { | |
4689 | if (moves[k].value == -1) | |
4690 | break; | |
4691 | if (moves[k].pos == move) { | |
4692 | if (same_dragon > moves[k].same_dragon) { | |
4693 | moves[k].same_dragon = same_dragon; | |
4694 | moves[k].pattern_data = pattern_data; | |
4695 | } | |
4696 | if (!moves[k].escape) | |
4697 | escape = 0; | |
4698 | break; | |
4699 | } | |
4700 | } | |
4701 | ||
4702 | /* Did we already have this move in the list with a higher value? */ | |
4703 | if (k < max_moves && moves[k].value >= value) | |
4704 | return; | |
4705 | ||
4706 | /* Insert the move at the right place in the list and adjust other | |
4707 | * entries as needed. | |
4708 | */ | |
4709 | for (; k >= 0; k--) { | |
4710 | if (k == 0 || value <= moves[k-1].value) { | |
4711 | /* Can't get higher. Insert the move below this point and quit | |
4712 | * looping. | |
4713 | */ | |
4714 | if (k < max_moves) { | |
4715 | moves[k].pos = move; | |
4716 | moves[k].value = value; | |
4717 | moves[k].name = reason; | |
4718 | /* If B or b class pattern, this move shouldn't be added to the | |
4719 | * dragon under consideration. | |
4720 | */ | |
4721 | moves[k].same_dragon = same_dragon; | |
4722 | moves[k].pattern_data = pattern_data; | |
4723 | moves[k].lunch = lunch; | |
4724 | moves[k].escape = escape; | |
4725 | moves[k].defense_pos = defense_pos; | |
4726 | } | |
4727 | break; | |
4728 | } | |
4729 | /* Shuffle the passed move one step downwards. */ | |
4730 | if (k < max_moves) | |
4731 | moves[k] = moves[k-1]; /* struct copy */ | |
4732 | } | |
4733 | ||
4734 | /* Assert that the list contains unique moves. */ | |
4735 | if (0) { | |
4736 | int l; | |
4737 | for (k = 0; k < max_moves; k++) | |
4738 | for (l = k+1; l < max_moves; l++) | |
4739 | gg_assert(moves[k].pos == 0 | |
4740 | || moves[k].pos != moves[l].pos); | |
4741 | } | |
4742 | } | |
4743 | ||
4744 | ||
4745 | /* Marks the dragons at apos and bpos. If only one dragon | |
4746 | * needs marking, bpos should be passed as NO_MOVE. | |
4747 | */ | |
4748 | ||
4749 | static void | |
4750 | owl_mark_dragon(int apos, int bpos, struct local_owl_data *owl, | |
4751 | int new_dragons[BOARDMAX]) | |
4752 | { | |
4753 | int pos; | |
4754 | int color = board[apos]; | |
4755 | ||
4756 | ASSERT1(bpos == NO_MOVE || board[bpos] == color, bpos); | |
4757 | ||
4758 | if (new_dragons == NULL) { | |
4759 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
4760 | if (ON_BOARD(pos)) { | |
4761 | if (is_same_dragon(pos, apos) || is_same_dragon(pos, bpos)) | |
4762 | owl->goal[pos] = 1; | |
4763 | else | |
4764 | owl->goal[pos] = 0; | |
4765 | } | |
4766 | } | |
4767 | else { | |
4768 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
4769 | if (ON_BOARD(pos)) { | |
4770 | if (IS_STONE(board[pos]) | |
4771 | && (new_dragons[pos] == new_dragons[apos] | |
4772 | || new_dragons[pos] == new_dragons[bpos])) | |
4773 | owl->goal[pos] = 1; | |
4774 | else | |
4775 | owl->goal[pos] = 0; | |
4776 | } | |
4777 | } | |
4778 | ||
4779 | memcpy(owl->cumulative_goal, owl->goal, sizeof(owl->goal)); | |
4780 | owl->color = color; | |
4781 | owl_mark_boundary(owl); | |
4782 | } | |
4783 | ||
4784 | ||
4785 | /* Marks the worms at apos and bpos. If only one worm | |
4786 | * needs marking, bpos should be passed as NO_MOVE. | |
4787 | */ | |
4788 | ||
4789 | static void | |
4790 | owl_mark_worm(int apos, int bpos, struct local_owl_data *owl) | |
4791 | { | |
4792 | int pos; | |
4793 | int color = board[apos]; | |
4794 | ||
4795 | ASSERT1(bpos == NO_MOVE || board[bpos] == color, bpos); | |
4796 | ||
4797 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
4798 | if (ON_BOARD(pos)) { | |
4799 | if (is_same_worm(pos, apos) || is_same_worm(pos, bpos)) | |
4800 | owl->goal[pos] = 1; | |
4801 | else | |
4802 | owl->goal[pos] = 0; | |
4803 | } | |
4804 | ||
4805 | owl->color = color; | |
4806 | } | |
4807 | ||
4808 | ||
4809 | /* Mark the boundary strings of the dragon. A boundary string is marked 2 | |
4810 | * if it adjoins a friendly live dragon, 1 otherwise. | |
4811 | */ | |
4812 | ||
4813 | static void | |
4814 | owl_mark_boundary(struct local_owl_data *owl) | |
4815 | { | |
4816 | int k; | |
4817 | int pos; | |
4818 | int color = owl->color; | |
4819 | int other = OTHER_COLOR(color); | |
4820 | ||
4821 | memset(owl->boundary, 0, sizeof(owl->boundary)); | |
4822 | memset(owl->neighbors, 0, sizeof(owl->neighbors)); | |
4823 | ||
4824 | /* Find all friendly neighbors of the dragon in goal. */ | |
4825 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
4826 | if (board[pos] == color && owl->goal[pos]) { | |
4827 | for (k = 0; k < 4; k++) { | |
4828 | if (board[pos + delta[k]] == EMPTY | |
4829 | && board[pos + 2 * delta[k]] == color | |
4830 | && !owl->neighbors[pos + 2 * delta[k]]) | |
4831 | mark_string(pos + 2 * delta[k], owl->neighbors, 1); | |
4832 | } | |
4833 | ||
4834 | for (; k < 8; k++) { | |
4835 | int pos2 = pos + delta[k]; | |
4836 | ||
4837 | if (board[pos2] == color | |
4838 | && !owl->neighbors[pos2] | |
4839 | && (board[SOUTH(gg_min(pos, pos2))] == EMPTY | |
4840 | || board[NORTH(gg_max(pos, pos2))] == EMPTY)) | |
4841 | mark_string(pos2, owl->neighbors, 1); | |
4842 | } | |
4843 | } | |
4844 | } | |
4845 | ||
4846 | /* First find all boundary strings (including those adjacent not to | |
4847 | * the goal dragon, but one of its neighbors). | |
4848 | */ | |
4849 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
4850 | if (board[pos] == other && !owl->boundary[pos]) { | |
4851 | for (k = 0; k < 8; k++) | |
4852 | if (ON_BOARD(pos + delta[k]) | |
4853 | && (owl->goal[pos + delta[k]] || owl->neighbors[pos + delta[k]])) { | |
4854 | mark_string(pos, owl->boundary, 1); | |
4855 | break; | |
4856 | } | |
4857 | } | |
4858 | ||
4859 | /* Upgrade the mark of a boundary string if it adjoins a safe | |
4860 | * friendly dragon. | |
4861 | */ | |
4862 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
4863 | if (owl->boundary[pos] == 1) { | |
4864 | for (k = 0; k < 8; k++) { | |
4865 | int pos2 = pos + delta[k]; | |
4866 | if (board[pos2] == color | |
4867 | && !owl->goal[pos2] | |
4868 | && !owl->neighbors[pos2] | |
4869 | && ((dragon[pos2].crude_status != DEAD && countstones(pos2) > 2) | |
4870 | || dragon[pos2].crude_status == ALIVE)) { | |
4871 | mark_string(pos, owl->boundary, 2); | |
4872 | break; | |
4873 | } | |
4874 | } | |
4875 | } | |
4876 | ||
4877 | /* During the owl reading, stones farther away may become parts of | |
4878 | * the boundary. We mark those strings neighboring some other | |
4879 | * friendly dragon with boundary value 2 right away, since we have | |
4880 | * no mechanism for detecting this later. | |
4881 | */ | |
4882 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
4883 | if (board[pos] == other && owl->boundary[pos] == 0) { | |
4884 | /* If a lunch has been amalgamated into a larger dragon, we | |
4885 | * have to back out now. | |
4886 | * | |
4887 | * Notice that we assume that no stone of the attacking color | |
4888 | * has been placed on the board with trymove() when this | |
4889 | * function is called. Thus we can (mostly) trust the worm data for | |
4890 | * stones of this color. | |
4891 | */ | |
4892 | if (worm[pos].attack_codes[0] != 0 | |
4893 | && worm[pos].size != dragon[pos].size) | |
4894 | continue; | |
4895 | ||
4896 | /* This can happen if called when stackp > 0 */ | |
4897 | if (dragon[pos].id == -1) | |
4898 | continue; | |
4899 | ||
4900 | for (k = 0; k < DRAGON2(pos).neighbors; k++) { | |
4901 | int d = DRAGON2(pos).adjacent[k]; | |
4902 | int apos = dragon2[d].origin; | |
4903 | ||
4904 | if (board[apos] == color && !owl->goal[apos]) { | |
4905 | owl->boundary[pos] = 2; | |
4906 | break; | |
4907 | } | |
4908 | } | |
4909 | } | |
4910 | } | |
4911 | ||
4912 | /* Add the stone just played to the goal dragon if same_dragon is | |
4913 | * SAME_DRAGON_CONNECTED. We also add all stones belonging to the same | |
4914 | * generalized string to the goal. If same_dragon is | |
4915 | * SAME_DRAGON_MAYBE_CONNECTED, we only add the stones if at least one | |
4916 | * stone of the generalized string already was part of the goal. If | |
4917 | * same_dragon is SAME_DRAGON_NOT_CONNECTED, we don't add any stones | |
4918 | * at all. | |
4919 | * | |
4920 | * The SAME_DRAGON_ALL_CONNECTED case is like SAME_DRAGON_CONNECTED | |
4921 | * but additionally all other own stones in the pattern suggesting the | |
4922 | * move are also added to the goal. | |
4923 | */ | |
4924 | static void | |
4925 | owl_update_goal(int pos, enum same_dragon_value same_dragon, int lunch, | |
4926 | struct local_owl_data *owl, int semeai_call, | |
4927 | struct matched_pattern_data *pattern_data) | |
4928 | { | |
4929 | int stones[MAX_BOARD * MAX_BOARD]; | |
4930 | int num_stones; | |
4931 | int k; | |
4932 | int do_add = 1; | |
4933 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
4934 | int save_count_variations = count_variations; | |
4935 | ||
4936 | ||
4937 | /* Turn off sgf output during find_superstring(). */ | |
4938 | sgf_dumptree = NULL; | |
4939 | count_variations = 0; | |
4940 | ||
4941 | if (same_dragon == SAME_DRAGON_NOT_CONNECTED) | |
4942 | num_stones = findstones(pos, MAX_BOARD * MAX_BOARD, stones); | |
4943 | else if (semeai_call) | |
4944 | find_superstring_conservative(pos, &num_stones, stones); | |
4945 | else | |
4946 | find_superstring(pos, &num_stones, stones); | |
4947 | ||
4948 | /* Turn sgf output back on. */ | |
4949 | sgf_dumptree = save_sgf_dumptree; | |
4950 | count_variations = save_count_variations; | |
4951 | ||
4952 | /* If same_dragon field is 1, only add if the played stone | |
4953 | * clearly is in contact with the goal dragon. | |
4954 | */ | |
4955 | if (same_dragon <= SAME_DRAGON_MAYBE_CONNECTED) { | |
4956 | do_add = 0; | |
4957 | for (k = 0; k < num_stones; k++) | |
4958 | if (owl->goal[stones[k]] != 0) { | |
4959 | do_add = 1; | |
4960 | break; | |
4961 | } | |
4962 | } | |
4963 | ||
4964 | if (do_add) | |
4965 | for (k = 0; k < num_stones; k++) { | |
4966 | if (owl->goal[stones[k]] == 0) { | |
4967 | if (0) | |
4968 | TRACE("Added %1m to goal.\n", stones[k]); | |
4969 | owl->goal[stones[k]] = 2; | |
4970 | owl->cumulative_goal[stones[k]] = 1; | |
4971 | } | |
4972 | } | |
4973 | ||
4974 | /* If this move captures a lunch, we add all it's direct neighbours to the | |
4975 | * goal. | |
4976 | */ | |
4977 | if (!semeai_call && lunch != NO_MOVE && board[lunch] != EMPTY) { | |
4978 | int adj, adjs[MAXCHAIN]; | |
4979 | int k; | |
4980 | adj = chainlinks(lunch, adjs); | |
4981 | for (k = 0; k < adj; k++) | |
4982 | if (!owl->goal[adjs[k]]) { | |
4983 | mark_string(adjs[k], owl->goal, 2); | |
4984 | mark_string(adjs[k], owl->cumulative_goal, 2); | |
4985 | } | |
4986 | } | |
4987 | ||
4988 | /* Now we handle the SAME_DRAGON_ALL_CONNECTED case. The move has | |
4989 | * already been added to the goal above, so it remains to find all | |
4990 | * other friendly stones in the pattern and add them too. We do that | |
4991 | * by a recursive call to this function in SAME_DRAGON_CONNECTED mode. | |
4992 | * This is maybe not the most elegant technique, however. | |
4993 | */ | |
4994 | if (same_dragon == SAME_DRAGON_ALL_CONNECTED) { | |
4995 | gg_assert(pattern_data != NULL); | |
4996 | for (k = 0; k < pattern_data->pattern->patlen; k++) { | |
4997 | int pos2; | |
4998 | ||
4999 | /* all the following stuff (currently) applies only at occupied cells */ | |
5000 | if (pattern_data->pattern->patn[k].att != ATT_O) | |
5001 | continue; | |
5002 | ||
5003 | /* transform pattern real coordinate */ | |
5004 | pos2 = AFFINE_TRANSFORM(pattern_data->pattern->patn[k].offset, | |
5005 | pattern_data->ll, pattern_data->anchor); | |
5006 | ||
5007 | if (!owl->goal[pos2]) | |
5008 | owl_update_goal(pos2, SAME_DRAGON_CONNECTED, NO_MOVE, owl, semeai_call, | |
5009 | pattern_data); | |
5010 | } | |
5011 | } | |
5012 | ||
5013 | if (1 && verbose) | |
5014 | goaldump(owl->goal); | |
5015 | } | |
5016 | ||
5017 | ||
5018 | /* Computes the connected components of a the graph that is given by | |
5019 | * having graph[i][j] = 1 if i and j are connected, and that has size | |
5020 | * graph_size. | |
5021 | * | |
5022 | * This function is generic, but without having the fixed MAX_CUTS | |
5023 | * array size it is ugly to write in ANSI C89 (no variably sized arrays), | |
5024 | * so we leave it here for now. | |
5025 | */ | |
5026 | static int | |
5027 | connected_components(signed char graph[MAX_CUTS][MAX_CUTS], int graph_size, | |
5028 | signed char component[MAX_CUTS]) | |
5029 | { | |
5030 | int num_components = 0; | |
5031 | int k, j; | |
5032 | ||
5033 | if (graph_size <= 0) | |
5034 | return 0; | |
5035 | ||
5036 | memset(component, -1, MAX_CUTS); | |
5037 | for (;;) { | |
5038 | int found_one; | |
5039 | /* Find unidentified string. */ | |
5040 | for (k = 0; k < graph_size; k++) | |
5041 | if (component[k] == -1) | |
5042 | break; | |
5043 | if (k == graph_size) | |
5044 | break; /* All are identified. */ | |
5045 | component[k] = num_components; /* Start new component. */ | |
5046 | do { /* Spread new component. */ | |
5047 | found_one = 0; | |
5048 | for (j = k+1; j < graph_size; j++) | |
5049 | if (graph[k][j] && component[j] == -1) { | |
5050 | component[j] = num_components; | |
5051 | found_one = 1; | |
5052 | } | |
5053 | } while (found_one); | |
5054 | num_components++; | |
5055 | } | |
5056 | gg_assert(num_components > 0); | |
5057 | return num_components; | |
5058 | } | |
5059 | ||
5060 | /* This functions gets called after a move has been made that threatens | |
5061 | * to cut the owl goal dragon. It cuts the goal if necessary, and sets it | |
5062 | * to the biggest remaining component. | |
5063 | */ | |
5064 | static void | |
5065 | owl_test_cuts(signed char goal[BOARDMAX], int color, int cuts[MAX_CUTS]) | |
5066 | { | |
5067 | int k, j; | |
5068 | signed char connected[MAX_CUTS][MAX_CUTS]; | |
5069 | /* int connect_move[MAX_CUTS][MAX_CUTS]; */ | |
5070 | int num_cuts; | |
5071 | int found_cut = 0; | |
5072 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
5073 | int save_count_variations = count_variations; | |
5074 | ||
5075 | sgf_dumptree = NULL; | |
5076 | count_variations = 0; | |
5077 | ||
5078 | memset(connected, 1, MAX_CUTS*MAX_CUTS); | |
5079 | if (debug & DEBUG_SPLIT_OWL) { | |
5080 | gprintf("Called for this goal: "); | |
5081 | goaldump(goal); | |
5082 | gprintf("At this position:\n"); | |
5083 | showboard(0); | |
5084 | } | |
5085 | ||
5086 | /* Delete captured strings from list. */ | |
5087 | for (k = 0; k < MAX_CUTS; k++) { | |
5088 | if (cuts[k] == NO_MOVE) | |
5089 | break; | |
5090 | if (board[cuts[k]] == EMPTY) { | |
5091 | for (j = k + 1; j < MAX_CUTS; j++) { | |
5092 | if (cuts[j] == NO_MOVE) | |
5093 | break; | |
5094 | cuts[j-1] = cuts[j]; | |
5095 | } | |
5096 | cuts[k] = NO_MOVE; | |
5097 | k--; | |
5098 | } | |
5099 | } | |
5100 | num_cuts = k; | |
5101 | ||
5102 | /* Test for each pair of strings in cuts[] whether it can now be | |
5103 | * disconnected. | |
5104 | */ | |
5105 | for (k = 0; k < num_cuts; k++) { | |
5106 | ASSERT1(board[cuts[k]] == color, cuts[k]); | |
5107 | for (j = k + 1; j < num_cuts; j++) | |
5108 | if (fast_disconnect(cuts[k], cuts[j], NULL) == WIN) { | |
5109 | found_cut = 1; | |
5110 | connected[k][j] = 0; | |
5111 | connected[j][k] = 0; | |
5112 | } | |
5113 | } | |
5114 | ||
5115 | if (found_cut) { | |
5116 | signed char component[MAX_CUTS]; | |
5117 | signed char component2[BOARDMAX]; | |
5118 | int component_size[MAX_CUTS]; | |
5119 | int num_components; | |
5120 | int biggest_component = -1; | |
5121 | struct connection_data *conn_data; | |
5122 | int c_id; | |
5123 | int pos; | |
5124 | ||
5125 | /* Start by computing the connected components among the strings | |
5126 | * listed in cuts[]. | |
5127 | */ | |
5128 | num_components = connected_components(connected, num_cuts, component); | |
5129 | if (num_components <= 1) { | |
5130 | sgf_dumptree = save_sgf_dumptree; | |
5131 | count_variations = save_count_variations; | |
5132 | return; | |
5133 | } | |
5134 | ||
5135 | /* Now break up the goal by associating each goal stone to one of | |
5136 | * the connected components. | |
5137 | * | |
5138 | * First we compute the connection distances from each of the | |
5139 | * partial goals we have found. | |
5140 | */ | |
5141 | memset(component2, -1, BOARDMAX); | |
5142 | memset(component_size, 0, sizeof(int) * num_components); | |
5143 | conn_data = malloc(sizeof(struct connection_data) * num_components); | |
5144 | for (c_id = 0; c_id < num_components; c_id++) { | |
5145 | signed char this_goal[BOARDMAX]; | |
5146 | memset(this_goal, 0, BOARDMAX); | |
5147 | ||
5148 | for (k = 0; k < num_cuts; k++) | |
5149 | if (component[k] == c_id) { | |
5150 | mark_string(cuts[k], this_goal, 1); | |
5151 | mark_string(cuts[k], component2, (signed char) c_id); | |
5152 | } | |
5153 | init_connection_data(color, this_goal, NO_MOVE, FP(3.01), | |
5154 | conn_data + c_id, 1); | |
5155 | spread_connection_distances(color, conn_data + c_id); | |
5156 | } | |
5157 | ||
5158 | /* Now put each goal string to the component to which it has the | |
5159 | * smallest distance. | |
5160 | */ | |
5161 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
5162 | int closest_dist = HUGE_CONNECTION_DISTANCE; | |
5163 | int closest_component = -1; | |
5164 | if (board[pos] != color || !goal[pos]) | |
5165 | continue; | |
5166 | if (pos != find_origin(pos)) | |
5167 | continue; | |
5168 | for (c_id = 0; c_id < num_components; c_id++) { | |
5169 | if (conn_data[c_id].distances[pos] < closest_dist) { | |
5170 | closest_dist = conn_data[c_id].distances[pos]; | |
5171 | closest_component = c_id; | |
5172 | } | |
5173 | } | |
5174 | /* FIXME: What to do if no close component found? */ | |
5175 | if (closest_component != -1) { | |
5176 | mark_string(pos, component2, (signed char) closest_component); | |
5177 | component_size[closest_component] += countstones(pos); | |
5178 | } | |
5179 | } | |
5180 | ||
5181 | /* Now find the biggest_component. */ | |
5182 | { | |
5183 | int biggest_size = 0; | |
5184 | for (c_id = 0; c_id < num_components; c_id++) | |
5185 | if (component_size[c_id] > biggest_size) { | |
5186 | biggest_size = component_size[c_id]; | |
5187 | biggest_component = c_id; | |
5188 | } | |
5189 | gg_assert(biggest_component != -1); | |
5190 | } | |
5191 | ||
5192 | /* Now delete everything except the biggest component from the goal. */ | |
5193 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
5194 | if (component2[pos] != biggest_component) | |
5195 | goal[pos] = 0; | |
5196 | if (debug & DEBUG_SPLIT_OWL) { | |
5197 | gprintf("Split dragon. Biggest component is %d (of %d).\n", | |
5198 | biggest_component, num_components); | |
5199 | showboard(0); | |
5200 | componentdump(component2); | |
5201 | } | |
5202 | free(conn_data); | |
5203 | } | |
5204 | sgf_dumptree = save_sgf_dumptree; | |
5205 | count_variations = save_count_variations; | |
5206 | } | |
5207 | ||
5208 | /* We update the boundary marks. The boundary mark must be | |
5209 | * constant on each string. It is nonzero if the string | |
5210 | * adjoins the goal dragon, or if the string includes a | |
5211 | * stone played in the course of analysis. If the string | |
5212 | * adjoins a live friendly dragon, the boundary mark is 2. | |
5213 | */ | |
5214 | static void | |
5215 | owl_update_boundary_marks(int pos, struct local_owl_data *owl) | |
5216 | { | |
5217 | signed char boundary_mark = 0; | |
5218 | int k; | |
5219 | ||
5220 | for (k = 0; k < 4; k++) { | |
5221 | int pos2 = pos + delta[k]; | |
5222 | ||
5223 | if (ON_BOARD(pos2) && owl->boundary[pos2] > boundary_mark) | |
5224 | boundary_mark = owl->boundary[pos2]; | |
5225 | ||
5226 | if (board[pos2] == owl->color | |
5227 | && dragon[pos2].color == owl->color | |
5228 | && dragon[pos2].status == ALIVE | |
5229 | && !owl->goal[pos2] | |
5230 | && !owl->neighbors[pos2]) | |
5231 | boundary_mark = 2; | |
5232 | } | |
5233 | ||
5234 | mark_string(pos, owl->boundary, boundary_mark); | |
5235 | } | |
5236 | ||
5237 | /* Lists the goal array. For use in GDB: | |
5238 | * (gdb) set goaldump(goal). | |
5239 | */ | |
5240 | ||
5241 | void | |
5242 | goaldump(const signed char goal[BOARDMAX]) | |
5243 | { | |
5244 | int pos; | |
5245 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
5246 | if (ON_BOARD(pos) && goal[pos]) | |
5247 | gprintf("%o%1m (%d) ", pos, (int) goal[pos]); | |
5248 | gprintf("\n"); | |
5249 | } | |
5250 | ||
5251 | void | |
5252 | componentdump(const signed char component[BOARDMAX]) | |
5253 | { | |
5254 | int pos; | |
5255 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
5256 | if (ON_BOARD(pos) && component[pos] != -1) | |
5257 | gprintf("%o%1m (%d) ", pos, (int) component[pos]); | |
5258 | gprintf("\n"); | |
5259 | } | |
5260 | ||
5261 | /* | |
5262 | * Owl attack moves are ineffective when the dragon can still live in a | |
5263 | * semeai. This function tests whether an owl attack move has this problem. | |
5264 | * If not, an owl attack move reason is added, otherwise we treat the | |
5265 | * move as a strategic attack. | |
5266 | */ | |
5267 | static void | |
5268 | test_owl_attack_move(int pos, int dr, int kworm, int acode) | |
5269 | { | |
5270 | int color = OTHER_COLOR(board[dr]); | |
5271 | if (DRAGON2(dr).semeais == 0 | |
5272 | || DRAGON2(dr).semeai_defense_point == NO_MOVE | |
5273 | || (DRAGON2(dr).semeais == 1 && semeai_move_reason_known(pos, dr)) | |
5274 | || acode == GAIN) { | |
5275 | add_owl_attack_move(pos, dr, kworm, acode); | |
5276 | DEBUG(DEBUG_OWL, "owl: %1m attacks %1m (%s) at move %d\n", | |
5277 | pos, dr, result_to_string(DRAGON2(dr).owl_attack_code), | |
5278 | movenum+1); | |
5279 | } | |
5280 | else { | |
5281 | int dr2 = DRAGON2(dr).semeai_defense_target; | |
5282 | int semeai_result, certain; | |
5283 | int save_verbose = verbose; | |
5284 | if (verbose > 0) | |
5285 | verbose--; | |
5286 | owl_analyze_semeai_after_move(pos, color, dr, dr2, &semeai_result, | |
5287 | NULL, NULL, 1, &certain, 0); | |
5288 | verbose = save_verbose; | |
5289 | if (certain >= DRAGON2(dr).semeai_defense_certain | |
5290 | && (semeai_result >= REVERSE_RESULT(acode))) { | |
5291 | /* Demote the move reasons. */ | |
5292 | DEBUG(DEBUG_OWL, "owl: %1m ineffective owl attack on %1m (can live in semeai with %1m)\n", pos, dr, dr2); | |
5293 | add_strategical_attack_move(pos, dr); | |
5294 | } | |
5295 | else { | |
5296 | add_owl_attack_move(pos, dr, kworm, acode); | |
5297 | DEBUG(DEBUG_OWL, "owl: %1m attacks %1m (%s) at move %d\n", | |
5298 | pos, dr, result_to_string(DRAGON2(dr).owl_attack_code), | |
5299 | movenum+1); | |
5300 | } | |
5301 | } | |
5302 | } | |
5303 | ||
5304 | /* Add owl move reasons. This function should be called once during | |
5305 | * genmove. It has to be called after semeai_move_reasons(). | |
5306 | */ | |
5307 | ||
5308 | void | |
5309 | owl_reasons(int color) | |
5310 | { | |
5311 | int pos; | |
5312 | ||
5313 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
5314 | if (!IS_STONE(board[pos]) | |
5315 | || dragon[pos].origin != pos) | |
5316 | continue; | |
5317 | ||
5318 | if (dragon[pos].status == CRITICAL | |
5319 | && DRAGON2(pos).owl_attack_point != NO_MOVE) { | |
5320 | if (board[pos] == color) { | |
5321 | if (DRAGON2(pos).owl_defense_point != NO_MOVE) { | |
5322 | if (DRAGON2(pos).owl_defense_code == LOSS) { | |
5323 | add_loss_move(DRAGON2(pos).owl_defense_point, pos, | |
5324 | DRAGON2(pos).owl_defense_kworm); | |
5325 | DEBUG(DEBUG_OWL, "owl: %1m defends %1m with loss at move %d\n", | |
5326 | DRAGON2(pos).owl_defense_point, pos, movenum+1); | |
5327 | } | |
5328 | else { | |
5329 | add_owl_defense_move(DRAGON2(pos).owl_defense_point, pos, | |
5330 | DRAGON2(pos).owl_defense_code); | |
5331 | DEBUG(DEBUG_OWL, "owl: %1m defends %1m at move %d\n", | |
5332 | DRAGON2(pos).owl_defense_point, pos, movenum+1); | |
5333 | } | |
5334 | } | |
5335 | } | |
5336 | else { /* opponent's dragon */ | |
5337 | /* We don't want to add this move reason if the attacker | |
5338 | * dies because the victim only formed a nakade shape. | |
5339 | * | |
5340 | * FIXME: This code overlaps heavily with some code in | |
5341 | * examine_move_safety() in move_reasons.c. The caching | |
5342 | * scheme should minimize the performance hit, but of course | |
5343 | * it's unfortunate to have the code duplication. | |
5344 | */ | |
5345 | int move = DRAGON2(pos).owl_attack_point; | |
5346 | ||
5347 | /* No worries if we catch something big. */ | |
5348 | if (dragon[pos].effective_size < 8) { | |
5349 | /* Look through the neighbors of the victim for dragons of | |
5350 | * our color. If we find at least one being thought alive | |
5351 | * everything is ok. Otherwise we keep track of the | |
5352 | * largest one for further examination. | |
5353 | */ | |
5354 | int largest = 0; | |
5355 | int k; | |
5356 | int bpos = NO_MOVE; | |
5357 | int kworm = NO_MOVE; | |
5358 | int safe = 0; | |
5359 | for (k = 0; k < DRAGON2(pos).neighbors; k++) { | |
5360 | int d = DRAGON2(pos).adjacent[k]; | |
5361 | if (DRAGON(d).color == color) { | |
5362 | if (DRAGON(d).status == ALIVE) { | |
5363 | safe = 1; | |
5364 | break; | |
5365 | } | |
5366 | if (DRAGON(d).size > largest) { | |
5367 | bpos = dragon2[d].origin; | |
5368 | largest = DRAGON(d).size; | |
5369 | } | |
5370 | } | |
5371 | } | |
5372 | ||
5373 | /* It may occasionally happen that no neighbor of our | |
5374 | * color was found. Assume safe in that case. | |
5375 | */ | |
5376 | if (bpos == NO_MOVE) | |
5377 | safe = 1; | |
5378 | ||
5379 | /* If not yet thought safe, ask the owl code whether the | |
5380 | * owl attack defends the (largest) attacker. | |
5381 | */ | |
5382 | if (!safe && owl_does_defend(move, bpos, &kworm) != WIN) { | |
5383 | DEBUG(DEBUG_OWL, | |
5384 | "owl: %1m attacks %1m at move %d, but the attacker dies.\n", | |
5385 | move, pos, movenum+1); | |
5386 | DRAGON2(pos).safety = INESSENTIAL; | |
5387 | continue; | |
5388 | } | |
5389 | } | |
5390 | ||
5391 | /* If we've reached this far, it only remains to check the move | |
5392 | * against semeai complications. */ | |
5393 | test_owl_attack_move(move, pos, DRAGON2(pos).owl_attack_kworm, | |
5394 | DRAGON2(pos).owl_attack_code); | |
5395 | } | |
5396 | } | |
5397 | else if (DRAGON2(pos).owl_status == DEAD | |
5398 | && DRAGON2(pos).owl_threat_status == CAN_THREATEN_DEFENSE) { | |
5399 | if (board[pos] == color | |
5400 | && DRAGON2(pos).owl_defense_point != NO_MOVE) { | |
5401 | add_owl_defense_threat_move(DRAGON2(pos).owl_defense_point, pos, WIN); | |
5402 | DEBUG(DEBUG_OWL, "owl: %1m threatens to defend %1m at move %d\n", | |
5403 | DRAGON2(pos).owl_defense_point, pos, movenum+1); | |
5404 | } | |
5405 | if (board[pos] == color | |
5406 | && DRAGON2(pos).owl_second_defense_point != NO_MOVE | |
5407 | && is_legal(DRAGON2(pos).owl_second_defense_point, color)) { | |
5408 | add_owl_defense_threat_move(DRAGON2(pos).owl_second_defense_point, | |
5409 | pos, WIN); | |
5410 | DEBUG(DEBUG_OWL, "owl: %1m threatens to defend %1m at move %d\n", | |
5411 | DRAGON2(pos).owl_second_defense_point, pos, movenum+1); | |
5412 | } | |
5413 | ||
5414 | /* If the opponent can threaten to live, an attacking | |
5415 | * move gets a small value to make sure it's really dead. | |
5416 | */ | |
5417 | if (board[pos] == OTHER_COLOR(color) | |
5418 | && DRAGON2(pos).owl_threat_status == CAN_THREATEN_DEFENSE | |
5419 | && DRAGON2(pos).owl_attack_point != NO_MOVE) { | |
5420 | add_owl_prevent_threat_move(DRAGON2(pos).owl_attack_point, pos); | |
5421 | DEBUG(DEBUG_OWL, "owl: %1m prevents a threat against %1m at move %d\n", | |
5422 | DRAGON2(pos).owl_attack_point, pos, movenum+1); | |
5423 | } | |
5424 | } | |
5425 | else if (DRAGON2(pos).owl_status == ALIVE) { | |
5426 | if (board[pos] == OTHER_COLOR(color) | |
5427 | && DRAGON2(pos).owl_threat_status == CAN_THREATEN_ATTACK) { | |
5428 | if (DRAGON2(pos).owl_attack_point != NO_MOVE) { | |
5429 | add_owl_attack_threat_move(DRAGON2(pos).owl_attack_point, pos, WIN); | |
5430 | DEBUG(DEBUG_OWL, "owl: %1m threatens %1m at move %d\n", | |
5431 | DRAGON2(pos).owl_attack_point, pos, movenum+1); | |
5432 | } | |
5433 | if (DRAGON2(pos).owl_second_attack_point != NO_MOVE | |
5434 | && is_legal(DRAGON2(pos).owl_second_attack_point, color)) { | |
5435 | add_owl_attack_threat_move(DRAGON2(pos).owl_second_attack_point, pos, | |
5436 | WIN); | |
5437 | DEBUG(DEBUG_OWL, "owl: %1m threatens %1m at move %d\n", | |
5438 | DRAGON2(pos).owl_second_attack_point, pos, movenum+1); | |
5439 | } | |
5440 | } | |
5441 | else if (board[pos] == OTHER_COLOR(color) | |
5442 | && DRAGON2(pos).owl_attack_point != NO_MOVE | |
5443 | && DRAGON2(pos).owl_attack_code == GAIN) { | |
5444 | add_owl_attack_move(DRAGON2(pos).owl_attack_point, pos, | |
5445 | DRAGON2(pos).owl_attack_kworm, GAIN); | |
5446 | DEBUG(DEBUG_OWL, "owl: %1m attacks %1m with gain at move %d\n", | |
5447 | DRAGON2(pos).owl_attack_point, pos, movenum+1); | |
5448 | } | |
5449 | else if (board[pos] == color | |
5450 | && DRAGON2(pos).owl_defense_point != NO_MOVE | |
5451 | && DRAGON2(pos).owl_defense_code == LOSS) { | |
5452 | add_loss_move(DRAGON2(pos).owl_defense_point, pos, | |
5453 | DRAGON2(pos).owl_defense_kworm); | |
5454 | DEBUG(DEBUG_OWL, "owl: %1m defends %1m with loss at move %d\n", | |
5455 | DRAGON2(pos).owl_defense_point, pos, movenum+1); | |
5456 | } | |
5457 | else if (board[pos] == color | |
5458 | && DRAGON2(pos).owl_attack_point != NO_MOVE | |
5459 | && DRAGON2(pos).owl_attack_code == GAIN | |
5460 | && DRAGON2(pos).owl_defense_code == WIN | |
5461 | && DRAGON2(pos).owl_defense_point != NO_MOVE) { | |
5462 | add_owl_defense_move(DRAGON2(pos).owl_defense_point, pos, | |
5463 | DRAGON2(pos).owl_defense_code); | |
5464 | DEBUG(DEBUG_OWL, "owl: %1m defends %1m against possible loss at move %d\n", | |
5465 | DRAGON2(pos).owl_defense_point, pos, movenum+1); | |
5466 | ||
5467 | } | |
5468 | /* The owl code found the friendly dragon alive, but was uncertain, | |
5469 | * and an extra point of defense was found, so this might | |
5470 | * be a good place to play. | |
5471 | */ | |
5472 | else if (board[pos] == color | |
5473 | && !DRAGON2(pos).owl_attack_certain | |
5474 | && DRAGON2(pos).owl_defense_certain | |
5475 | && ON_BOARD(DRAGON2(pos).owl_defense_point)) { | |
5476 | add_owl_uncertain_defense_move(DRAGON2(pos).owl_defense_point, pos); | |
5477 | DEBUG(DEBUG_OWL, | |
5478 | "owl: %1m defends the uncertain dragon at %1m at move %d\n", | |
5479 | DRAGON2(pos).owl_defense_point, pos, movenum+1); | |
5480 | } | |
5481 | } | |
5482 | ||
5483 | /* The owl code found the dragon dead, but was uncertain, | |
5484 | * and an extra point of attack was found, so this might | |
5485 | * be a good place to play. | |
5486 | */ | |
5487 | else if (DRAGON2(pos).owl_status == DEAD | |
5488 | && board[pos] == OTHER_COLOR(color) | |
5489 | && !DRAGON2(pos).owl_attack_certain | |
5490 | && ON_BOARD(DRAGON2(pos).owl_attack_point)) { | |
5491 | add_owl_uncertain_defense_move(DRAGON2(pos).owl_attack_point, pos); | |
5492 | DEBUG(DEBUG_OWL, | |
5493 | "owl: %1m might defend the uncertain dragon at %1m at move %d\n", | |
5494 | DRAGON2(pos).owl_attack_point, pos, movenum+1); | |
5495 | } | |
5496 | } | |
5497 | } | |
5498 | ||
5499 | /* Use the owl code to determine whether the move at (move) makes | |
5500 | * the dragon at (target) owl safe. This is used to test whether | |
5501 | * tactical defenses are strategically viable and whether a vital eye | |
5502 | * point does kill an owl critical dragon. | |
5503 | * | |
5504 | * Should be called only when stackp==0. | |
5505 | */ | |
5506 | ||
5507 | int | |
5508 | owl_does_defend(int move, int target, int *kworm) | |
5509 | { | |
5510 | int color = board[target]; | |
5511 | int result = 0; | |
5512 | struct local_owl_data *owl; | |
5513 | int reading_nodes_when_called = get_reading_node_counter(); | |
5514 | int tactical_nodes; | |
5515 | int origin; | |
5516 | int acode; | |
5517 | int wpos = NO_MOVE; | |
5518 | int wid = MAX_GOAL_WORMS; | |
5519 | double start = 0.0; | |
5520 | ||
5521 | if (debug & DEBUG_OWL_PERFORMANCE) | |
5522 | start = gg_cputime(); | |
5523 | ||
5524 | if (worm[target].unconditional_status == DEAD) | |
5525 | return 0; | |
5526 | ||
5527 | origin = dragon[target].origin; | |
5528 | TRACE("owl_does_defend %1m %1m(%1m)\n", move, target, origin); | |
5529 | ||
5530 | if (search_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, | |
5531 | &result, kworm, NULL, NULL)) | |
5532 | return result; | |
5533 | ||
5534 | if (trymove(move, color, "owl_does_defend", target)) { | |
5535 | /* Check if a compatible owl_attack() is cached. */ | |
5536 | if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, | |
5537 | &result, NULL, kworm, NULL)) { | |
5538 | popgo(); | |
5539 | return REVERSE_RESULT(result); | |
5540 | } | |
5541 | ||
5542 | /* | |
5543 | * FIXME: (move) will be added to the goal dragon although we | |
5544 | * do not know whether it is really connected. | |
5545 | */ | |
5546 | init_owl(&owl, target, NO_MOVE, move, 1, NULL); | |
5547 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, | |
5548 | kworm, 0); | |
5549 | acode = do_owl_attack(target, NULL, &wid, owl, 0); | |
5550 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); | |
5551 | result = REVERSE_RESULT(acode); | |
5552 | popgo(); | |
5553 | } | |
5554 | else | |
5555 | return 0; /* Don't cache anything in this case. */ | |
5556 | ||
5557 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
5558 | ||
5559 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
5560 | "owl_does_defend %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", | |
5561 | move, target, origin, result, local_owl_node_counter, | |
5562 | tactical_nodes, gg_cputime() - start); | |
5563 | ||
5564 | store_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, | |
5565 | result, wpos, 0, 0, | |
5566 | tactical_nodes, owl->goal, board[target]); | |
5567 | ||
5568 | if (kworm) | |
5569 | *kworm = wpos; | |
5570 | return result; | |
5571 | } | |
5572 | ||
5573 | ||
5574 | /* Use the owl code to determine whether the dragon at (target) is owl | |
5575 | * safe after an own move at (move). This is used to detect | |
5576 | * blunders. In case the dragon is not safe, it also tries to find a | |
5577 | * defense point making (target) safe in a later move. | |
5578 | * | |
5579 | * Should be called only when stackp==0. | |
5580 | */ | |
5581 | ||
5582 | int | |
5583 | owl_confirm_safety(int move, int target, int *defense_point, int *kworm) | |
5584 | { | |
5585 | int color = board[target]; | |
5586 | int result = 0; | |
5587 | struct local_owl_data *owl; | |
5588 | int reading_nodes_when_called = get_reading_node_counter(); | |
5589 | int tactical_nodes; | |
5590 | int origin; | |
5591 | int defense = 0; | |
5592 | double start = 0.0; | |
5593 | int acode; | |
5594 | int wpos = NO_MOVE; | |
5595 | int wid = MAX_GOAL_WORMS; | |
5596 | ||
5597 | if (debug & DEBUG_OWL_PERFORMANCE) | |
5598 | start = gg_cputime(); | |
5599 | ||
5600 | if (worm[target].unconditional_status == DEAD) | |
5601 | return 0; | |
5602 | ||
5603 | origin = dragon[target].origin; | |
5604 | TRACE("owl_confirm_safety %1m %1m(%1m)\n", move, target, origin); | |
5605 | ||
5606 | if (search_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, | |
5607 | &result, defense_point, kworm, NULL)) | |
5608 | return result; | |
5609 | ||
5610 | if (trymove(move, color, "owl_confirm_safety", target)) { | |
5611 | /* Check if a compatible owl_attack() is cached. */ | |
5612 | if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, | |
5613 | &result, defense_point, kworm, NULL)) { | |
5614 | popgo(); | |
5615 | if (result == 0) | |
5616 | return WIN; | |
5617 | else if (result == GAIN) | |
5618 | return LOSS; | |
5619 | else | |
5620 | return 0; | |
5621 | } | |
5622 | ||
5623 | init_owl(&owl, target, NO_MOVE, move, 1, NULL); | |
5624 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, | |
5625 | kworm, 0); | |
5626 | acode = do_owl_attack(target, &defense, &wid, owl, 0); | |
5627 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); | |
5628 | if (acode == 0) | |
5629 | result = WIN; | |
5630 | else if (acode == GAIN) | |
5631 | result = LOSS; | |
5632 | popgo(); | |
5633 | } | |
5634 | else | |
5635 | return 0; /* Don't cache anything in this case. */ | |
5636 | ||
5637 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
5638 | ||
5639 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
5640 | "owl_confirm_safety %1m %1m(%1m), result %d %1m (%d, %d nodes, %f seconds)\n", | |
5641 | move, target, origin, result, defense, | |
5642 | local_owl_node_counter, tactical_nodes, | |
5643 | gg_cputime() - start); | |
5644 | ||
5645 | store_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, | |
5646 | result, defense, wpos, 0, | |
5647 | tactical_nodes, owl->goal, board[target]); | |
5648 | ||
5649 | if (defense_point) | |
5650 | *defense_point = defense; | |
5651 | if (kworm) | |
5652 | *kworm = wpos; | |
5653 | ||
5654 | return result; | |
5655 | } | |
5656 | ||
5657 | ||
5658 | /* Use the owl code to determine whether the attack move at (move) of | |
5659 | * the dragon (target) is effective, i.e. whether it kills the stones. | |
5660 | * | |
5661 | * Should be called only when stackp==0. | |
5662 | */ | |
5663 | ||
5664 | int | |
5665 | owl_does_attack(int move, int target, int *kworm) | |
5666 | { | |
5667 | int color = board[target]; | |
5668 | int other = OTHER_COLOR(color); | |
5669 | int result = 0; | |
5670 | struct local_owl_data *owl; | |
5671 | int reading_nodes_when_called = get_reading_node_counter(); | |
5672 | int tactical_nodes; | |
5673 | int origin; | |
5674 | int dcode; | |
5675 | int wpos = NO_MOVE; | |
5676 | int wid = MAX_GOAL_WORMS; | |
5677 | double start = 0.0; | |
5678 | ||
5679 | if (debug & DEBUG_OWL_PERFORMANCE) | |
5680 | start = gg_cputime(); | |
5681 | ||
5682 | if (worm[target].unconditional_status == ALIVE) | |
5683 | return 0; | |
5684 | ||
5685 | origin = dragon[target].origin; | |
5686 | TRACE("owl_does_attack %1m %1m(%1m)\n", move, target, origin); | |
5687 | ||
5688 | if (search_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, | |
5689 | &result, kworm, NULL, NULL)) | |
5690 | return result; | |
5691 | ||
5692 | /* FIXME: We want to do this after the trymove(), but currently | |
5693 | * owl_mark_dragon() may crash if the trymove() happens to remove | |
5694 | * some stones of the goal dragon from the board. | |
5695 | */ | |
5696 | #if 1 | |
5697 | init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); | |
5698 | #endif | |
5699 | ||
5700 | if (trymove(move, other, "owl_does_attack", target)) { | |
5701 | /* Check if a compatible owl_defend() is cached. */ | |
5702 | if (search_persistent_owl_cache(OWL_DEFEND, origin, 0, 0, | |
5703 | &result, NULL, kworm, NULL)) { | |
5704 | popgo(); | |
5705 | return REVERSE_RESULT(result); | |
5706 | } | |
5707 | ||
5708 | #if 0 | |
5709 | local_owl_node_counter = 0; | |
5710 | owl->lunches_are_current = 0; | |
5711 | owl_mark_dragon(target, NO_MOVE, owl); | |
5712 | #endif | |
5713 | owl_update_boundary_marks(move, owl); | |
5714 | #if 0 | |
5715 | compute_owl_escape_values(owl); | |
5716 | #endif | |
5717 | /* FIXME: Should also check if part of the dragon was captured, | |
5718 | * like do_owl_attack() does. | |
5719 | */ | |
5720 | if (board[target] == EMPTY) | |
5721 | dcode = 0; | |
5722 | else { | |
5723 | prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, | |
5724 | kworm, 0); | |
5725 | dcode = do_owl_defend(target, NULL, &wid, owl, 0); | |
5726 | finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); | |
5727 | } | |
5728 | result = REVERSE_RESULT(dcode); | |
5729 | owl->lunches_are_current = 0; | |
5730 | popgo(); | |
5731 | } | |
5732 | else | |
5733 | return 0; /* Don't cache anything in this case. */ | |
5734 | ||
5735 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
5736 | ||
5737 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
5738 | "owl_does_attack %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", | |
5739 | move, target, origin, result, local_owl_node_counter, | |
5740 | tactical_nodes, gg_cputime() - start); | |
5741 | ||
5742 | store_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, | |
5743 | result, wpos, 0, 0, | |
5744 | tactical_nodes, owl->goal, board[target]); | |
5745 | ||
5746 | if (kworm) | |
5747 | *kworm = wpos; | |
5748 | return result; | |
5749 | } | |
5750 | ||
5751 | ||
5752 | /* Use the owl code to determine whether connecting the two dragons | |
5753 | * (target1) and (target2) by playing at (move) results in a living | |
5754 | * dragon. Should be called only when stackp==0. | |
5755 | */ | |
5756 | ||
5757 | int | |
5758 | owl_connection_defends(int move, int target1, int target2) | |
5759 | { | |
5760 | int color = board[target1]; | |
5761 | int result = 0; | |
5762 | int reading_nodes_when_called = get_reading_node_counter(); | |
5763 | int tactical_nodes; | |
5764 | double start = 0.0; | |
5765 | struct local_owl_data *owl; | |
5766 | ||
5767 | if (debug & DEBUG_OWL_PERFORMANCE) | |
5768 | start = gg_cputime(); | |
5769 | ||
5770 | ASSERT1(board[target2] == color, target2); | |
5771 | TRACE("owl_connection_defends %1m %1m %1m\n", move, target1, target2); | |
5772 | ||
5773 | if (worm[target1].unconditional_status == DEAD) | |
5774 | return 0; | |
5775 | if (worm[target2].unconditional_status == DEAD) | |
5776 | return 0; | |
5777 | ||
5778 | if (search_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, | |
5779 | target2, &result, NULL, NULL, NULL)) | |
5780 | return result; | |
5781 | ||
5782 | init_owl(&owl, target1, target2, NO_MOVE, 1, NULL); | |
5783 | ||
5784 | if (trymove(move, color, "owl_connection_defends", target1)) { | |
5785 | owl_update_goal(move, SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, owl, 0, NULL); | |
5786 | if (!do_owl_attack(move, NULL, NULL, owl, 0)) | |
5787 | result = WIN; | |
5788 | owl->lunches_are_current = 0; | |
5789 | popgo(); | |
5790 | } | |
5791 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
5792 | ||
5793 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
5794 | "owl_conn_defends %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", | |
5795 | move, target1, target2, result, local_owl_node_counter, | |
5796 | tactical_nodes, gg_cputime() - start); | |
5797 | ||
5798 | store_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, target2, | |
5799 | result, 0, 0, 0, tactical_nodes, | |
5800 | owl->goal, color); | |
5801 | ||
5802 | return result; | |
5803 | } | |
5804 | ||
5805 | ||
5806 | /* This function attempts to make a list of dead strings | |
5807 | * which may be relevant to the life of the goal dragon. | |
5808 | * Such strings are called owl lunches. They are ignored | |
5809 | * (treated as invisible) during the running of make_domains. | |
5810 | * | |
5811 | * In certain cases we also need to identify tactically safe strings | |
5812 | * which should be included in the eyespace, e.g. in this position: | |
5813 | * | |
5814 | * ------- | |
5815 | * OXXOOXO | |
5816 | * OX.O.XO | |
5817 | * OXX.XXO | |
5818 | * OOXXXOO | |
5819 | * .OOOOO. | |
5820 | * | |
5821 | * The three O stones cannot be captured, but they can't live | |
5822 | * independently without capturing the surrounding stones. We call | |
5823 | * such stones INESSENTIAL and identify them by the condition that for | |
5824 | * each liberty of the corresponding superstring, the following must | |
5825 | * hold: | |
5826 | * | |
5827 | * 1. At least one neighbor of the liberty is the goal dragon. | |
5828 | * 2. No neighbor of the liberty is the same color as the tested string, | |
5829 | * unless part of the same superstring. | |
5830 | * 3. No neighbor of the liberty of the same color as the goal dragon | |
5831 | * does not belong to the goal dragon. | |
5832 | * 4. No neighbor of the liberty belonging to the goal dragon can be | |
5833 | * tactically captured. | |
5834 | * | |
5835 | * There is a weakness with this characterization though, which can be | |
5836 | * seen in this position: | |
5837 | * | |
5838 | * -------- | |
5839 | * OX..OOX. | |
5840 | * OX.X.XOO | |
5841 | * OX.XX.O. | |
5842 | * O.XXOOO. | |
5843 | * .OOOO... | |
5844 | * | |
5845 | * The two O stones intruding in X's eyespace cannot be tactically | |
5846 | * captured and their liberties satisfy the requirements above. Still | |
5847 | * it doesn't make any sense to count those stones as | |
5848 | * inessential. Therefore we add another requirement on the stones | |
5849 | * themself: | |
5850 | * | |
5851 | * 5. No neighbor of the stones does not belong to the goal or can be | |
5852 | * tactically captured. | |
5853 | * | |
5854 | * A second weakness can be noticed in this position: | |
5855 | * | |
5856 | * |OOOO. | |
5857 | * |XXXO. | |
5858 | * |O.XOO | |
5859 | * |OXXXO | |
5860 | * |.O.XO | |
5861 | * +----- | |
5862 | * | |
5863 | * The white stones in the corner should qualify as inessential but | |
5864 | * the corner liberty doesn't satisfy requirement 1. Therefore we add | |
5865 | * an alternative requirement: | |
5866 | * | |
5867 | * 1b. The liberty is a topologically false eye with respect to the | |
5868 | * goal dragon. | |
5869 | * | |
5870 | * This is not quite good enough though, as shown in this position: | |
5871 | * | |
5872 | * ---------- | |
5873 | * OX.X.OO... | |
5874 | * OXX.OOX.O. | |
5875 | * O.XXXXX.O. | |
5876 | * OOOOOOOOO. | |
5877 | * | |
5878 | * The four O stones are regarded as inessential after inclusion of | |
5879 | * rule 1b, which is clearly inappropriate. To solve this problem we | |
5880 | * modify the rule: | |
5881 | * | |
5882 | * 1b'. The liberty is a topologically false eye with respect to the | |
5883 | * goal dragon and is adjacent to no empty vertex. | |
5884 | */ | |
5885 | ||
5886 | static void | |
5887 | owl_find_lunches(struct local_owl_data *owl) | |
5888 | { | |
5889 | int k; | |
5890 | int pos; | |
5891 | int lunches = 0; | |
5892 | int prevlunch; | |
5893 | int lunch; | |
5894 | int acode; | |
5895 | int apos; | |
5896 | int dcode; | |
5897 | int dpos; | |
5898 | int color = owl->color; | |
5899 | int other = OTHER_COLOR(color); | |
5900 | signed char already_checked[BOARDMAX]; | |
5901 | ||
5902 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
5903 | int save_count_variations = count_variations; | |
5904 | ||
5905 | sgf_dumptree = NULL; | |
5906 | count_variations = 0; | |
5907 | for (prevlunch = 0; prevlunch < MAX_LUNCHES; prevlunch++) | |
5908 | owl->lunch[prevlunch] = NO_MOVE; | |
5909 | memset(owl->inessential, 0, sizeof(owl->inessential)); | |
5910 | ||
5911 | memset(already_checked, 0, sizeof(already_checked)); | |
5912 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) { | |
5913 | if (board[pos] == color && owl->goal[pos]) { | |
5914 | /* Loop over the eight neighbors. */ | |
5915 | for (k = 0; k < 8; k++) { | |
5916 | int pos2 = pos + delta[k]; | |
5917 | ||
5918 | /* If the immediate neighbor is empty, we look two steps away. */ | |
5919 | if (k < 4 && board[pos2] == EMPTY) | |
5920 | pos2 += delta[k]; | |
5921 | ||
5922 | if (board[pos2] != other) | |
5923 | continue; | |
5924 | ||
5925 | lunch = find_origin(pos2); | |
5926 | if (already_checked[lunch]) | |
5927 | continue; | |
5928 | already_checked[lunch] = 1; | |
5929 | ||
5930 | attack_and_defend(lunch, &acode, &apos, &dcode, &dpos); | |
5931 | if (acode != 0) { | |
5932 | owl->lunch[lunches] = lunch; | |
5933 | owl->lunch_attack_code[lunches] = acode; | |
5934 | owl->lunch_attack_point[lunches] = apos; | |
5935 | owl->lunch_defend_code[lunches] = dcode; | |
5936 | ASSERT1(board[apos] == EMPTY, lunch); | |
5937 | if (dcode != 0) { | |
5938 | owl->lunch_defense_point[lunches] = dpos; | |
5939 | ASSERT1(board[dpos] == EMPTY, lunch); | |
5940 | } | |
5941 | else | |
5942 | owl->lunch_defense_point[lunches] = NO_MOVE; | |
5943 | lunches++; | |
5944 | if (lunches == MAX_LUNCHES) { | |
5945 | sgf_dumptree = save_sgf_dumptree; | |
5946 | count_variations = save_count_variations; | |
5947 | owl->lunches_are_current = 1; | |
5948 | return; | |
5949 | } | |
5950 | } | |
5951 | else if (!owl->inessential[lunch]) { | |
5952 | /* Test for inessentiality. */ | |
5953 | int adj; | |
5954 | int adjs[MAXCHAIN]; | |
5955 | int num_stones; | |
5956 | int stones[MAX_BOARD * MAX_BOARD]; | |
5957 | int liberties; | |
5958 | int libs[MAXLIBS]; | |
5959 | int r; | |
5960 | int essential = 0; | |
5961 | int superstring[BOARDMAX]; | |
5962 | ||
5963 | /* First check the neighbors of the string. */ | |
5964 | adj = chainlinks(lunch, adjs); | |
5965 | for (r = 0; r < adj; r++) { | |
5966 | if (!owl->goal[adjs[r]] || attack(adjs[r], NULL) != 0) { | |
5967 | essential = 1; | |
5968 | break; | |
5969 | } | |
5970 | } | |
5971 | ||
5972 | if (essential) | |
5973 | continue; | |
5974 | ||
5975 | find_superstring_stones_and_liberties(lunch, &num_stones, stones, | |
5976 | &liberties, libs, 0); | |
5977 | ||
5978 | memset(superstring, 0, sizeof(superstring)); | |
5979 | for (r = 0; r < num_stones; r++) | |
5980 | superstring[stones[r]] = 1; | |
5981 | ||
5982 | for (r = 0; r < liberties; r++) { | |
5983 | int bpos = libs[r]; | |
5984 | int goal_found = 0; | |
5985 | int s; | |
5986 | ||
5987 | for (s = 0; s < 4; s++) { | |
5988 | int cpos = bpos + delta[s]; | |
5989 | ||
5990 | if (!ON_BOARD(cpos)) | |
5991 | continue; | |
5992 | if (board[cpos] == color) { | |
5993 | if (attack(cpos, NULL) != 0) { | |
5994 | essential = 1; | |
5995 | break; | |
5996 | } | |
5997 | else if (owl->goal[cpos]) | |
5998 | goal_found = 1; | |
5999 | else { | |
6000 | essential = 1; | |
6001 | break; | |
6002 | } | |
6003 | } | |
6004 | else if (board[cpos] == other | |
6005 | && !superstring[cpos]) { | |
6006 | essential = 1; | |
6007 | break; | |
6008 | } | |
6009 | } | |
6010 | if (!goal_found) { | |
6011 | /* Requirement 1 not satisfied. Test requirement 1b. | |
6012 | * N.B. This is a simplified topological eye test. | |
6013 | * The simplification may be good, bad, or neutral. | |
6014 | */ | |
6015 | int off_board = 0; | |
6016 | int diagonal_goal = 0; | |
6017 | for (s = 4; s < 8; s++) { | |
6018 | if (!ON_BOARD(bpos + delta[s])) | |
6019 | off_board++; | |
6020 | else if (owl->goal[bpos + delta[s]]) | |
6021 | diagonal_goal++; | |
6022 | } | |
6023 | if (diagonal_goal + (off_board >= 2) < 2) | |
6024 | essential = 1; | |
6025 | else { | |
6026 | /* Check that the liberty is adjacent to no empty | |
6027 | * vertex, as required by 1b'. | |
6028 | */ | |
6029 | for (s = 0; s < 4; s++) { | |
6030 | if (board[bpos + delta[s]] == EMPTY) { | |
6031 | essential = 1; | |
6032 | break; | |
6033 | } | |
6034 | } | |
6035 | } | |
6036 | } | |
6037 | ||
6038 | if (essential) | |
6039 | break; | |
6040 | } | |
6041 | ||
6042 | if (!essential) { | |
6043 | TRACE("Inessential string found at %1m.\n", lunch); | |
6044 | for (r = 0; r < num_stones; r++) | |
6045 | owl->inessential[stones[r]] = 1; | |
6046 | } | |
6047 | } | |
6048 | } | |
6049 | } | |
6050 | } | |
6051 | ||
6052 | owl->lunches_are_current = 1; | |
6053 | sgf_dumptree = save_sgf_dumptree; | |
6054 | count_variations = save_count_variations; | |
6055 | } | |
6056 | ||
6057 | ||
6058 | /* Try to improve the move to attack a lunch. Essentially we try to avoid | |
6059 | * unsafe moves when there are less risky ways to attack. | |
6060 | * | |
6061 | * This function also improves lunch attack point in a special case when | |
6062 | * we capture a one- or two-stone lunch on the first line. If we eat it | |
6063 | * with a first line move, there is a huge risk we'll end up with a false | |
6064 | * eye. Therefore, we move the attack to the second line when it works. | |
6065 | * | |
6066 | * .*OO .*OOO .*OOOO | |
6067 | * .,XO .,X.O .,XX.O | |
6068 | * ---- ----- ------ | |
6069 | * | |
6070 | * In all these position the attack point is moved from ',' to '*'. | |
6071 | */ | |
6072 | static int | |
6073 | improve_lunch_attack(int lunch, int attack_point) | |
6074 | { | |
6075 | int color = OTHER_COLOR(board[lunch]); | |
6076 | int defense_point; | |
6077 | int k; | |
6078 | ||
6079 | if (safe_move(attack_point, color)) { | |
6080 | if (is_edge_vertex(lunch) | |
6081 | && is_edge_vertex(attack_point) | |
6082 | && neighbor_of_string(attack_point, lunch)) { | |
6083 | int stones = countstones(lunch); | |
6084 | int libs[2]; | |
6085 | ||
6086 | if (stones == 1 | |
6087 | || (stones == 2 | |
6088 | && findlib(lunch, 2, libs) == 2 | |
6089 | && is_edge_vertex(libs[0]) | |
6090 | && is_edge_vertex(libs[1]))) { | |
6091 | for (k = 0; k < 4; k++) { | |
6092 | int apos = attack_point + delta[k]; | |
6093 | if (!ON_BOARD(attack_point - delta[k]) && board[apos] == EMPTY) { | |
6094 | if (does_attack(apos, lunch) && safe_move(apos, color)) | |
6095 | return apos; | |
6096 | break; | |
6097 | } | |
6098 | } | |
6099 | } | |
6100 | } | |
6101 | ||
6102 | return attack_point; | |
6103 | } | |
6104 | ||
6105 | for (k = 0; k < 4; k++) { | |
6106 | int pos = attack_point + delta[k]; | |
6107 | if (board[pos] == color | |
6108 | && attack(pos, NULL) | |
6109 | && find_defense(pos, &defense_point) | |
6110 | && defense_point != NO_MOVE | |
6111 | && does_attack(defense_point, lunch)) { | |
6112 | TRACE("Moved attack of lunch %1m from %1m to %1m.\n", | |
6113 | lunch, attack_point, defense_point); | |
6114 | return defense_point; | |
6115 | } | |
6116 | } | |
6117 | ||
6118 | return attack_point; | |
6119 | } | |
6120 | ||
6121 | /* Try to improve the move to defend a lunch. | |
6122 | * | |
6123 | * An example where this is useful is the position below, where the | |
6124 | * defense of A is moved from b to c. This is a possible variation in | |
6125 | * ld_owl:182. | |
6126 | * | |
6127 | * ...X..| ...X..| | |
6128 | * ...X..| ...Xc.| | |
6129 | * ..XXO.| ..XXOb| | |
6130 | * XXXOOX| XXXOOA| | |
6131 | * XOOOX.| XOOOX.| | |
6132 | * .XOX.X| .XOX.X| | |
6133 | * ------+ ------+ | |
6134 | */ | |
6135 | static int | |
6136 | improve_lunch_defense(int lunch, int defense_point) | |
6137 | { | |
6138 | int color = board[lunch]; | |
6139 | int k; | |
6140 | ||
6141 | for (k = 0; k < 4; k++) { | |
6142 | int pos = defense_point + delta[k]; | |
6143 | if (board[pos] == OTHER_COLOR(color) | |
6144 | && countlib(pos) == 2) { | |
6145 | int libs[2]; | |
6146 | int pos2; | |
6147 | ||
6148 | findlib(pos, 2, libs); | |
6149 | if (libs[0] == defense_point) | |
6150 | pos2 = libs[1]; | |
6151 | else | |
6152 | pos2 = libs[0]; | |
6153 | ||
6154 | if (accuratelib(pos2, color, MAXLIBS, NULL) | |
6155 | > accuratelib(defense_point, color, MAXLIBS, NULL) | |
6156 | && does_defend(pos2, lunch)) { | |
6157 | TRACE("Moved defense of lunch %1m from %1m to %1m.\n", | |
6158 | lunch, defense_point, pos2); | |
6159 | return pos2; | |
6160 | } | |
6161 | } | |
6162 | } | |
6163 | ||
6164 | return defense_point; | |
6165 | } | |
6166 | ||
6167 | ||
6168 | /* Wrapper for make domains. The second set of owl data is optional. | |
6169 | * Use a null pointer if it is not needed. Otherwise, make_domains | |
6170 | * is run separately for the two owl data, but information about | |
6171 | * tactically dead lunches is used from *both* sources through | |
6172 | * the owl_lively() calls. | |
6173 | */ | |
6174 | ||
6175 | static void | |
6176 | owl_make_domains(struct local_owl_data *owla, struct local_owl_data *owlb) | |
6177 | { | |
6178 | /* We need to set this so that owl_lively() can be used. */ | |
6179 | struct eye_data *black_eye = NULL; | |
6180 | struct eye_data *white_eye = NULL; | |
6181 | ||
6182 | current_owl_data = owla; | |
6183 | other_owl_data = owlb; | |
6184 | ||
6185 | if (!owla->lunches_are_current) | |
6186 | owl_find_lunches(owla); | |
6187 | if (owla->color == BLACK) | |
6188 | black_eye = owla->my_eye; | |
6189 | else | |
6190 | white_eye = owla->my_eye; | |
6191 | ||
6192 | if (owlb) { | |
6193 | gg_assert(owla->color == OTHER_COLOR(owlb->color)); | |
6194 | if (!owlb->lunches_are_current) | |
6195 | owl_find_lunches(owlb); | |
6196 | if (owlb->color == BLACK) | |
6197 | black_eye = owlb->my_eye; | |
6198 | else | |
6199 | white_eye = owlb->my_eye; | |
6200 | } | |
6201 | make_domains(black_eye, white_eye, 1); | |
6202 | } | |
6203 | ||
6204 | /* True unless (pos) is EMPTY or occupied by a lunch for the goal dragon. | |
6205 | * Used during make_domains (see optics.c: lively macro). A ``lively'' | |
6206 | * worm is one that might be alive, hence cannot be ignored in | |
6207 | * determining eye spaces. | |
6208 | */ | |
6209 | ||
6210 | int | |
6211 | owl_lively(int pos) | |
6212 | { | |
6213 | int origin; | |
6214 | int lunch; | |
6215 | ASSERT_ON_BOARD1(pos); | |
6216 | ||
6217 | if (board[pos] == EMPTY) | |
6218 | return 0; | |
6219 | origin = find_origin(pos); | |
6220 | ||
6221 | /* When reading a semeai there is a second set of owl data to consider. | |
6222 | * Strings of the second owl are considered lively no matter what, | |
6223 | * since declaring such a string dead prematurely can prevent the | |
6224 | * semeai code from finishing its job. | |
6225 | * | |
6226 | * On the other hand a friendly string which is a lunch of the | |
6227 | * other dragon and can't be saved is not lively. | |
6228 | */ | |
6229 | if (other_owl_data) { | |
6230 | ||
6231 | if (include_semeai_worms_in_eyespace && other_owl_data->goal[pos]) | |
6232 | return 0; | |
6233 | ||
6234 | if (other_owl_data->goal[pos] && !semeai_trust_tactical_attack(pos)) | |
6235 | return 1; | |
6236 | /* FIXME: Shouldn't we check other_owl_data->inessential[origin] here? */ | |
6237 | for (lunch = 0; lunch < MAX_LUNCHES; lunch++) | |
6238 | if (other_owl_data->lunch[lunch] == origin | |
6239 | && other_owl_data->lunch_defense_point[lunch] == NO_MOVE) | |
6240 | return 0; | |
6241 | } | |
6242 | ||
6243 | /* Inessential stones are not lively. */ | |
6244 | if (current_owl_data->inessential[origin]) | |
6245 | return 0; | |
6246 | ||
6247 | /* Lunches that can't be saved are dead, so don't report them as lively. */ | |
6248 | for (lunch = 0; lunch < MAX_LUNCHES; lunch++) | |
6249 | if (current_owl_data->lunch[lunch] == origin | |
6250 | && current_owl_data->lunch_defense_point[lunch] == NO_MOVE) | |
6251 | return 0; | |
6252 | ||
6253 | return 1; | |
6254 | } | |
6255 | ||
6256 | ||
6257 | /* Caching version of safe_move for the callback. This function has | |
6258 | * its own cache, separate from the global safe move cache. Note that | |
6259 | * since the cache is reset by owl_shapes before starting pattern | |
6260 | * matching, and since (unlike safe_move) this function is always | |
6261 | * called from the same place in owl_shapes_callback, the color will | |
6262 | * be the same each time it is called. So there is no need to have | |
6263 | * separate caches for B and W. | |
6264 | */ | |
6265 | ||
6266 | static int | |
6267 | owl_safe_move(int move, int color) | |
6268 | { | |
6269 | int acode, safe = 0; | |
6270 | ||
6271 | if (trymove(move, color, "owl_safe_move", 0)) { | |
6272 | acode = attack(move, NULL); | |
6273 | if (acode != WIN) | |
6274 | safe = 1; | |
6275 | else | |
6276 | safe = 0; | |
6277 | popgo(); | |
6278 | } | |
6279 | current_owl_data->safe_move_cache[move] = safe+1; | |
6280 | return safe; | |
6281 | } | |
6282 | ||
6283 | ||
6284 | /* This function, called when stackp==0, returns true if capturing | |
6285 | * the string at (str) results in a live group. | |
6286 | */ | |
6287 | ||
6288 | #define MAX_SUBSTANTIAL_LIBS 10 | |
6289 | ||
6290 | int | |
6291 | owl_substantial(int str) | |
6292 | { | |
6293 | int k; | |
6294 | int libs[MAX_SUBSTANTIAL_LIBS + 1]; | |
6295 | int liberties = findlib(str, MAX_SUBSTANTIAL_LIBS+1, libs); | |
6296 | int reading_nodes_when_called = get_reading_node_counter(); | |
6297 | int tactical_nodes; | |
6298 | int result; | |
6299 | double start = 0.0; | |
6300 | struct local_owl_data *owl; | |
6301 | int num_moves = 0; | |
6302 | ||
6303 | if (debug & DEBUG_OWL_PERFORMANCE) | |
6304 | start = gg_cputime(); | |
6305 | ||
6306 | /* FIXME: We want to use the full init_owl here too (cf. similar | |
6307 | * remark below). | |
6308 | */ | |
6309 | reduced_init_owl(&owl, 1); | |
6310 | ||
6311 | owl->color = OTHER_COLOR(board[str]); | |
6312 | local_owl_node_counter = 0; | |
6313 | ||
6314 | /* Big strings are always substantial since the biggest nakade is | |
6315 | * six stones. (There are probably rare exceptions to this | |
6316 | * rule, but they are unlikely to come up in a game.) | |
6317 | */ | |
6318 | if (countstones(str) > 6) | |
6319 | return 1; | |
6320 | ||
6321 | if (liberties > MAX_SUBSTANTIAL_LIBS) | |
6322 | return 0; | |
6323 | ||
6324 | memset(owl->goal, 0, sizeof(owl->goal)); | |
6325 | /* Mark the neighbors of the string. If one is found which is alive, return | |
6326 | * true. */ | |
6327 | { | |
6328 | int adjs[MAXCHAIN]; | |
6329 | int adj; | |
6330 | ||
6331 | adj = chainlinks(str, adjs); | |
6332 | for (k = 0; k < adj; k++) { | |
6333 | if (dragon[adjs[k]].status == ALIVE) | |
6334 | return 1; | |
6335 | mark_dragon(adjs[k], owl->goal, 1); | |
6336 | } | |
6337 | } | |
6338 | ||
6339 | /* We must check the cache while stackp == 0, but we wait until the | |
6340 | * trivial tests have been done. | |
6341 | */ | |
6342 | if (search_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, | |
6343 | &result, NULL, NULL, NULL)) | |
6344 | return result; | |
6345 | ||
6346 | /* fill all the liberties */ | |
6347 | for (k = 0; k < liberties; k++) { | |
6348 | if (trymove(libs[k], owl->color, NULL, 0)) { | |
6349 | if (get_level() >= 8) | |
6350 | increase_depth_values(); | |
6351 | owl->goal[libs[k]] = 1; | |
6352 | num_moves++; | |
6353 | } | |
6354 | else { | |
6355 | /* if we can't fill, try swapping with the next liberty */ | |
6356 | if (k < liberties-1 | |
6357 | && trymove(libs[k+1], owl->color, NULL, 0)) { | |
6358 | if (get_level() >= 8) | |
6359 | increase_depth_values(); | |
6360 | owl->goal[libs[k+1]] = 1; | |
6361 | libs[k+1] = libs[k]; | |
6362 | num_moves++; | |
6363 | } | |
6364 | else { | |
6365 | /* Can't fill the liberties. Give up! */ | |
6366 | while (num_moves-- > 0) { | |
6367 | if (get_level() >= 8) | |
6368 | decrease_depth_values(); | |
6369 | popgo(); | |
6370 | } | |
6371 | return 0; | |
6372 | } | |
6373 | } | |
6374 | } | |
6375 | /* FIXME: We would want to use init_owl() here too, but it doesn't | |
6376 | * fit very well with the construction of the goal array above. | |
6377 | */ | |
6378 | memcpy(owl->cumulative_goal, owl->goal, BOARDMAX); | |
6379 | compute_owl_escape_values(owl); | |
6380 | owl_mark_boundary(owl); | |
6381 | owl->lunches_are_current = 0; | |
6382 | ||
6383 | if (do_owl_attack(libs[0], NULL, NULL, owl, 0)) | |
6384 | result = 0; | |
6385 | else | |
6386 | result = 1; | |
6387 | while (num_moves-- > 0) { | |
6388 | if (get_level() >= 8) | |
6389 | decrease_depth_values(); | |
6390 | popgo(); | |
6391 | } | |
6392 | ||
6393 | tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; | |
6394 | DEBUG(DEBUG_OWL_PERFORMANCE, | |
6395 | "owl_substantial %1m, result %d (%d, %d nodes, %f seconds)\n", | |
6396 | str, result, local_owl_node_counter, | |
6397 | tactical_nodes, gg_cputime() - start); | |
6398 | ||
6399 | store_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, result, 0, 0, 0, | |
6400 | tactical_nodes, owl->goal, owl->color); | |
6401 | ||
6402 | return result; | |
6403 | } | |
6404 | ||
6405 | ||
6406 | ||
6407 | /* Returns true if and only if (i, j) is a 1-2 vertex, i.e. next to a | |
6408 | * corner. | |
6409 | */ | |
6410 | static int | |
6411 | one_two_point(int pos) | |
6412 | { | |
6413 | int i = I(pos); | |
6414 | int j = J(pos); | |
6415 | ||
6416 | if ((i == 0 || i == board_size-1 || j == 0 || j == board_size-1) | |
6417 | && (i == 1 || i == board_size-2 || j == 1 || j == board_size-2)) | |
6418 | return 1; | |
6419 | ||
6420 | return 0; | |
6421 | } | |
6422 | ||
6423 | ||
6424 | ||
6425 | /* Reports the number of eyes gotten by capturing a boundary string. | |
6426 | * This implementation tends to give an optimistic view of the | |
6427 | * chances, so if it tells that the lunch is worthless, it truly | |
6428 | * should be. The converse is not true. | |
6429 | */ | |
6430 | ||
6431 | static void | |
6432 | sniff_lunch(int lunch, int *min, int *probable, int *max, | |
6433 | struct local_owl_data *owl) | |
6434 | { | |
6435 | int other = OTHER_COLOR(board[lunch]); | |
6436 | int libs[MAXLIBS]; | |
6437 | int liberties; | |
6438 | int r; | |
6439 | ||
6440 | ASSERT1(IS_STONE(board[lunch]), lunch); | |
6441 | ||
6442 | if (owl->boundary[lunch] == 2) { | |
6443 | *min = 2; | |
6444 | *probable = 2; | |
6445 | *max = 2; | |
6446 | return; | |
6447 | } | |
6448 | ||
6449 | /* Do we believe this capture would help escaping? */ | |
6450 | liberties = findlib(lunch, MAXLIBS, libs); | |
6451 | for (r = 0; r < liberties; r++) { | |
6452 | if (owl->escape_values[libs[r]] > 0 | |
6453 | && !is_self_atari(libs[r], other)) { | |
6454 | int k; | |
6455 | for (k = 0; k < 8; k++) | |
6456 | if (ON_BOARD(libs[r] + delta[k]) && owl->goal[libs[r] + delta[k]]) | |
6457 | break; | |
6458 | if (k == 8) { | |
6459 | *min = 2; | |
6460 | *probable = 2; | |
6461 | *max = 2; | |
6462 | return; | |
6463 | } | |
6464 | } | |
6465 | } | |
6466 | ||
6467 | estimate_lunch_eye_value(lunch, min, probable, max, 1); | |
6468 | ||
6469 | if (*min < 2) { | |
6470 | int bonus = estimate_lunch_half_eye_bonus(lunch, owl->half_eye); | |
6471 | *min += bonus/2; | |
6472 | *probable += bonus; | |
6473 | *max += (bonus + 1)/2; | |
6474 | } | |
6475 | ||
6476 | if (*probable < 2) | |
6477 | eat_lunch_escape_bonus(lunch, min, probable, max, owl); | |
6478 | } | |
6479 | ||
6480 | /* Capturing a lunch can give eyes by turning a false eye into a proper one, | |
6481 | * etc. This function returns the likely increase in half eyes | |
6482 | * by capturing the string at (lunch). | |
6483 | */ | |
6484 | static int | |
6485 | estimate_lunch_half_eye_bonus(int lunch, | |
6486 | struct half_eye_data half_eye[BOARDMAX]) | |
6487 | { | |
6488 | int stones[10]; | |
6489 | int k; | |
6490 | int size = findstones(lunch, 10, stones); | |
6491 | int half_eyes = 0; | |
6492 | ||
6493 | ASSERT1(size < 10, lunch); | |
6494 | ||
6495 | for (k = 0; k < size; k++) { | |
6496 | int stone = stones[k]; | |
6497 | int d; | |
6498 | for (d = 4; d < 8; d++) { | |
6499 | int pos = stone + delta[d]; | |
6500 | if (ON_BOARD(pos) | |
6501 | && (is_halfeye(half_eye, pos) || is_false_eye(half_eye, pos))) | |
6502 | half_eyes++; | |
6503 | } | |
6504 | } | |
6505 | return half_eyes; | |
6506 | } | |
6507 | ||
6508 | ||
6509 | void | |
6510 | estimate_lunch_eye_value(int lunch, int *min, int *probable, int *max, | |
6511 | int appreciate_one_two_lunches) | |
6512 | { | |
6513 | int other = OTHER_COLOR(board[lunch]); | |
6514 | int size = countstones(lunch); | |
6515 | ||
6516 | if (size > 6) { | |
6517 | *min = 2; | |
6518 | *probable = 2; | |
6519 | *max = 2; | |
6520 | } | |
6521 | else if (size > 4) { | |
6522 | *min = 1; | |
6523 | *probable = 2; | |
6524 | *max = 2; | |
6525 | } | |
6526 | else if (size > 2) { | |
6527 | *min = 0; | |
6528 | *probable = 1; | |
6529 | *max = 2; | |
6530 | } | |
6531 | else if (size == 2) { | |
6532 | int stones[2]; | |
6533 | findstones(lunch, 2, stones); | |
6534 | /* A lunch on a 1-2 point tends always to be worth contesting. */ | |
6535 | if ((obvious_false_eye(stones[0], other) | |
6536 | || obvious_false_eye(stones[1], other)) | |
6537 | && (!appreciate_one_two_lunches | |
6538 | || !(one_two_point(stones[0]) || one_two_point(stones[1])))) { | |
6539 | *min = 0; | |
6540 | *probable = 0; | |
6541 | *max = 0; | |
6542 | } | |
6543 | else { | |
6544 | *min = 0; | |
6545 | *probable = 1; | |
6546 | *max = 1; | |
6547 | } | |
6548 | } | |
6549 | else if (size == 1) { | |
6550 | if (!obvious_false_eye(lunch, other)) { | |
6551 | *min = 0; | |
6552 | *probable = 1; | |
6553 | *max = 1; | |
6554 | } | |
6555 | else { | |
6556 | *min = 0; | |
6557 | *probable = 0; | |
6558 | *max = 0; | |
6559 | } | |
6560 | } | |
6561 | } | |
6562 | ||
6563 | /* Gives a bonus for a lunch capture which joins a (or some) friendly | |
6564 | * string(s) to the goal dragon and improves the escape potential at | |
6565 | * the same time. This is indicated in some situations where the owl | |
6566 | * code would stop the analysis because of various cutoffs. See | |
6567 | * do_owl_defend() | |
6568 | * | |
6569 | * The following implementation tries to get a precise idea of the | |
6570 | * escape potential improvement by calling dragon_escape() twice. | |
6571 | */ | |
6572 | static void | |
6573 | eat_lunch_escape_bonus(int lunch, int *min, int *probable, int *max, | |
6574 | struct local_owl_data *owl) | |
6575 | { | |
6576 | int adjacent[MAXCHAIN]; | |
6577 | int neighbors; | |
6578 | int adjoins = 0; | |
6579 | int n; | |
6580 | /* Be very careful before touching this value. | |
6581 | * See owl_estimate_life() for details. | |
6582 | */ | |
6583 | UNUSED(min); | |
6584 | ||
6585 | /* Don't mess up with kos */ | |
6586 | if (is_ko_point(lunch)) | |
6587 | return; | |
6588 | ||
6589 | neighbors = chainlinks(lunch, adjacent); | |
6590 | for (n = 0; n < neighbors; n++) | |
6591 | adjoins |= !owl->goal[adjacent[n]]; | |
6592 | ||
6593 | if (adjoins) { | |
6594 | int before, after; | |
6595 | before = dragon_escape(owl->goal, owl->color, owl->escape_values); | |
6596 | /* if the escape route is already large enough to be considered | |
6597 | * a WIN by the owl code, then no need for more */ | |
6598 | if (before < 5) { | |
6599 | signed char new_goal[BOARDMAX]; | |
6600 | memcpy(new_goal, owl->goal, sizeof(new_goal)); | |
6601 | for (n = 0; n < neighbors; n++) | |
6602 | if (!owl->goal[adjacent[n]]) | |
6603 | mark_string(adjacent[n], new_goal, 2); | |
6604 | after = dragon_escape(new_goal, owl->color, owl->escape_values); | |
6605 | ||
6606 | /* Following is completely ad hoc. Another set of tests might | |
6607 | * very well get better results. */ | |
6608 | if (after - before >= 3) { | |
6609 | if (after >= 8 || (before == 0 && after >= 5)) { | |
6610 | *probable = 2; | |
6611 | *max = 2; | |
6612 | } | |
6613 | else if (*max < 2) | |
6614 | (*max)++; | |
6615 | } | |
6616 | } | |
6617 | } | |
6618 | } | |
6619 | ||
6620 | ||
6621 | /* Find a new origin when it has been captured or cut out of the | |
6622 | * goal. Used in do_owl_attack() | |
6623 | */ | |
6624 | static int | |
6625 | select_new_goal_origin(int origin, struct local_owl_data *owl) | |
6626 | { | |
6627 | int pos; | |
6628 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
6629 | if (board[pos] == owl->color && owl->goal[pos] == 1) | |
6630 | return find_origin(pos); | |
6631 | ||
6632 | return origin; | |
6633 | } | |
6634 | ||
6635 | ||
6636 | /* Retrieve topological eye values stored in the half_eye[] array of | |
6637 | * the current owl data. | |
6638 | * | |
6639 | * FIXME: Sooner or later we'll want this to return a non-rounded | |
6640 | * value. When we change this, we have to review all patterns using | |
6641 | * the autohelper owl_topological_eye(). | |
6642 | */ | |
6643 | int | |
6644 | owl_topological_eye(int pos, int color) | |
6645 | { | |
6646 | float value; | |
6647 | UNUSED(color); | |
6648 | value = current_owl_data->half_eye[pos].value; | |
6649 | if (value > 2.0 && value < 4.0) | |
6650 | return 3; | |
6651 | else if (value <= 2.0) | |
6652 | return (int) (value + 0.99); /* Round up. */ | |
6653 | else | |
6654 | return (int) value; /* Round down. */ | |
6655 | } | |
6656 | ||
6657 | /* This function returns true if it is judged that the capture of the | |
6658 | * string at (pos) is sufficient to create one eye. | |
6659 | * | |
6660 | * Update: Now it instead returns the max number of eyes. | |
6661 | */ | |
6662 | ||
6663 | int | |
6664 | vital_chain(int pos) | |
6665 | { | |
6666 | int min; | |
6667 | int probable; | |
6668 | int max; | |
6669 | sniff_lunch(pos, &min, &probable, &max, current_owl_data); | |
6670 | ||
6671 | return max; | |
6672 | } | |
6673 | ||
6674 | ||
6675 | static void | |
6676 | compute_owl_escape_values(struct local_owl_data *owl) | |
6677 | { | |
6678 | int pos; | |
6679 | int m, n; | |
6680 | signed char safe_stones[BOARDMAX]; | |
6681 | SGFTree *save_sgf_dumptree = sgf_dumptree; | |
6682 | int save_count_variations = count_variations; | |
6683 | signed char mx[BOARDMAX]; | |
6684 | memset(mx, 0, sizeof(mx)); | |
6685 | ||
6686 | sgf_dumptree = NULL; | |
6687 | count_variations = 0; | |
6688 | get_lively_stones(OTHER_COLOR(owl->color), safe_stones); | |
6689 | sgf_dumptree = save_sgf_dumptree; | |
6690 | count_variations = save_count_variations; | |
6691 | ||
6692 | compute_escape_influence(owl->color, safe_stones, NULL, NULL, | |
6693 | owl->escape_values); | |
6694 | ||
6695 | DEBUG(DEBUG_ESCAPE, "Owl escape values:\n"); | |
6696 | for (m = 0; m < board_size; m++) { | |
6697 | for (n = 0; n < board_size; n++) { | |
6698 | pos = POS(m, n); | |
6699 | if (dragon[pos].color == owl->color && !owl->goal[pos]) { | |
6700 | if (dragon[pos].crude_status == ALIVE) | |
6701 | owl->escape_values[pos] = 6; | |
6702 | else if (dragon[pos].crude_status == UNKNOWN) { | |
6703 | if (DRAGON2(pos).moyo_size > 5) | |
6704 | owl->escape_values[pos] = 4; | |
6705 | else if (DRAGON2(pos).escape_route > 5) { | |
6706 | if (mx[dragon[pos].origin]) | |
6707 | owl->escape_values[pos] = owl->escape_values[dragon[pos].origin]; | |
6708 | else { | |
6709 | int pos2; | |
6710 | signed char escape_values[BOARDMAX]; | |
6711 | signed char dragon_stones[BOARDMAX]; | |
6712 | ||
6713 | compute_escape_influence(owl->color, safe_stones, owl->goal, | |
6714 | NULL, escape_values); | |
6715 | ||
6716 | /* mark_dragon() can't be used here in case a string of | |
6717 | * the dragon was captured by the initial move in | |
6718 | * owl_does_attack(). Actually it isn't really proper to | |
6719 | * use is_same_dragon() at stackp>0 either but it's more | |
6720 | * robust at least. | |
6721 | */ | |
6722 | for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) { | |
6723 | if (ON_BOARD(pos2)) | |
6724 | dragon_stones[pos2] = is_same_dragon(pos2, pos); | |
6725 | } | |
6726 | ||
6727 | if (dragon_escape(dragon_stones, owl->color, escape_values) > 5) | |
6728 | owl->escape_values[dragon[pos].origin] = 4; | |
6729 | ||
6730 | mx[dragon[pos].origin] = 1; | |
6731 | } | |
6732 | } | |
6733 | } | |
6734 | } | |
6735 | DEBUG(DEBUG_ESCAPE, "%o%d", owl->escape_values[pos]); | |
6736 | } | |
6737 | DEBUG(DEBUG_ESCAPE, "%o\n"); | |
6738 | } | |
6739 | } | |
6740 | ||
6741 | ||
6742 | /* Used by autohelpers. */ | |
6743 | int | |
6744 | owl_escape_value(int pos) | |
6745 | { | |
6746 | /* FIXME: Should have a more robust mechanism to avoid | |
6747 | * escaping inwards. Returning a negative value is just a kludge. | |
6748 | */ | |
6749 | int k; | |
6750 | ASSERT_ON_BOARD1(pos); | |
6751 | if (current_owl_data->goal[pos]) | |
6752 | return -10; | |
6753 | ||
6754 | if (board[pos] == EMPTY) | |
6755 | for (k = 0; k < 8; k++) | |
6756 | if (ON_BOARD(pos + delta[k]) && current_owl_data->goal[pos + delta[k]]) | |
6757 | return -10; | |
6758 | ||
6759 | return current_owl_data->escape_values[pos]; | |
6760 | } | |
6761 | ||
6762 | ||
6763 | /* Used by autohelpers. */ | |
6764 | int | |
6765 | owl_goal_dragon(int pos) | |
6766 | { | |
6767 | return current_owl_data->goal[pos] != 0; | |
6768 | } | |
6769 | ||
6770 | /* Used by autohelpers. | |
6771 | * Returns 1 if (pos) is an eyespace for the color of the dragon currently | |
6772 | * under owl investigation. | |
6773 | */ | |
6774 | int | |
6775 | owl_eyespace(int pos) | |
6776 | { | |
6777 | int origin; | |
6778 | ASSERT_ON_BOARD1(pos); | |
6779 | ||
6780 | origin = current_owl_data->my_eye[pos].origin; | |
6781 | return (ON_BOARD(origin) | |
6782 | && (current_owl_data->my_eye[origin].color | |
6783 | == current_owl_data->color) | |
6784 | && max_eyes(¤t_owl_data->my_eye[origin].value) > 0); | |
6785 | } | |
6786 | ||
6787 | ||
6788 | /* Used by autohelpers. | |
6789 | * Returns 1 if (pos) is an eyespace for the color of the dragon currently | |
6790 | * under owl investigation, which is possibly worth (at least) 2 eyes. | |
6791 | */ | |
6792 | int | |
6793 | owl_big_eyespace(int pos) | |
6794 | { | |
6795 | int origin; | |
6796 | ASSERT_ON_BOARD1(pos); | |
6797 | ||
6798 | origin = current_owl_data->my_eye[pos].origin; | |
6799 | return (ON_BOARD(origin) | |
6800 | && (current_owl_data->my_eye[origin].color | |
6801 | == current_owl_data->color) | |
6802 | && max_eyes(¤t_owl_data->my_eye[origin].value) >= 2); | |
6803 | } | |
6804 | ||
6805 | ||
6806 | /* Used by autohelpers. | |
6807 | * Returns 1 if (pos) is an eyespace for the color of the dragon currently | |
6808 | * under owl investigation. | |
6809 | */ | |
6810 | int | |
6811 | owl_mineye(int pos) | |
6812 | { | |
6813 | int origin; | |
6814 | ASSERT_ON_BOARD1(pos); | |
6815 | ||
6816 | origin = current_owl_data->my_eye[pos].origin; | |
6817 | if (!ON_BOARD(origin) | |
6818 | || (current_owl_data->my_eye[origin].color | |
6819 | != current_owl_data->color)) | |
6820 | return 0; | |
6821 | ||
6822 | return min_eyes(¤t_owl_data->my_eye[origin].value); | |
6823 | } | |
6824 | ||
6825 | ||
6826 | /* Used by autohelpers. | |
6827 | * Returns 1 if (pos) is an eyespace for the color of the dragon currently | |
6828 | * under owl investigation. | |
6829 | */ | |
6830 | int | |
6831 | owl_maxeye(int pos) | |
6832 | { | |
6833 | int origin; | |
6834 | ASSERT_ON_BOARD1(pos); | |
6835 | ||
6836 | origin = current_owl_data->my_eye[pos].origin; | |
6837 | if (!ON_BOARD(origin) | |
6838 | || (current_owl_data->my_eye[origin].color | |
6839 | != current_owl_data->color)) | |
6840 | return 0; | |
6841 | ||
6842 | return max_eyes(¤t_owl_data->my_eye[origin].value); | |
6843 | } | |
6844 | ||
6845 | ||
6846 | /* Used by autohelpers. | |
6847 | * Returns 1 if (pos) is a non-marginal eyespace for the color of the | |
6848 | * dragon currently under owl investigation. | |
6849 | */ | |
6850 | int | |
6851 | owl_proper_eye(int pos) | |
6852 | { | |
6853 | ASSERT_ON_BOARD1(pos); | |
6854 | ||
6855 | return ((current_owl_data->my_eye[pos].color | |
6856 | == current_owl_data->color) | |
6857 | && !current_owl_data->my_eye[pos].marginal); | |
6858 | } | |
6859 | ||
6860 | ||
6861 | /* Used by autohelpers. | |
6862 | * Returns the effective size of the eyespace at pos. | |
6863 | */ | |
6864 | int | |
6865 | owl_eye_size(int pos) | |
6866 | { | |
6867 | int origin; | |
6868 | ASSERT_ON_BOARD1(pos); | |
6869 | ||
6870 | origin = current_owl_data->my_eye[pos].origin; | |
6871 | return current_owl_data->my_eye[origin].esize | |
6872 | - current_owl_data->my_eye[origin].msize; | |
6873 | } | |
6874 | ||
6875 | ||
6876 | /* Used by autohelpers. | |
6877 | * Returns whether str is a lunch. | |
6878 | */ | |
6879 | int | |
6880 | owl_lunch(int str) | |
6881 | { | |
6882 | int k; | |
6883 | int origin; | |
6884 | ASSERT_ON_BOARD1(str); | |
6885 | ASSERT1(current_owl_data->lunches_are_current, str); | |
6886 | origin = find_origin(str); | |
6887 | ||
6888 | for (k = 0; k < MAX_LUNCHES; k++) { | |
6889 | if (current_owl_data->lunch[k] == NO_MOVE) | |
6890 | break; | |
6891 | if (current_owl_data->lunch[k] == origin) | |
6892 | return 1; | |
6893 | } | |
6894 | ||
6895 | return 0; | |
6896 | } | |
6897 | ||
6898 | ||
6899 | /* Used by autohelpers. | |
6900 | ||
6901 | * Returns 1 if (pos) is considered to be a strong dragon. This is | |
6902 | * intended to be used to decide whether connecting to some external | |
6903 | * stones is an easy way to live. The current implementation is fairly | |
6904 | * conservative, requiring that (pos) was part of a dragon with two | |
6905 | * eyes according to the static analysis. This requirement may be | |
6906 | * relaxed considerably in the future. | |
6907 | * | |
6908 | * (pos) must not be part of the goal dragon. | |
6909 | */ | |
6910 | int | |
6911 | owl_strong_dragon(int pos) | |
6912 | { | |
6913 | ASSERT_ON_BOARD1(pos); | |
6914 | ASSERT1(IS_STONE(board[pos]), pos); | |
6915 | ||
6916 | return (!current_owl_data->goal[pos] | |
6917 | && dragon[pos].color == board[pos] | |
6918 | && dragon[pos].crude_status == ALIVE); | |
6919 | } | |
6920 | ||
6921 | ||
6922 | static int | |
6923 | owl_escape_route(struct local_owl_data *owl) | |
6924 | { | |
6925 | signed char modified_escape[BOARDMAX]; | |
6926 | int pos; | |
6927 | memcpy(modified_escape, owl->escape_values, sizeof(modified_escape)); | |
6928 | for (pos = BOARDMIN; pos < BOARDMAX; pos++) | |
6929 | if (ON_BOARD(pos) && owl->cumulative_goal[pos]) | |
6930 | modified_escape[pos] = 0; | |
6931 | return dragon_escape(owl->goal, owl->color, modified_escape); | |
6932 | } | |
6933 | ||
6934 | ||
6935 | /**************************** | |
6936 | * Initialization of owl data | |
6937 | ****************************/ | |
6938 | ||
6939 | /* This is a temporary solution. We want to be able to use the full | |
6940 | * init_owl() also in owl_substantial. | |
6941 | */ | |
6942 | static void | |
6943 | reduced_init_owl(struct local_owl_data **owl, int at_bottom_of_stack) | |
6944 | { | |
6945 | if (at_bottom_of_stack) | |
6946 | owl_stack_pointer = 0; | |
6947 | else | |
6948 | owl_stack_pointer++; | |
6949 | ||
6950 | check_owl_stack_size(); | |
6951 | *owl = owl_stack[owl_stack_pointer]; | |
6952 | VALGRIND_MAKE_WRITABLE(*owl, sizeof(struct local_owl_data)); | |
6953 | } | |
6954 | ||
6955 | ||
6956 | /* Initialize owl data. Set at_bottom_of_stack to 1 the first time you | |
6957 | * call init_owl() and to 0 any following time (only relevant if you | |
6958 | * need more than one set of owl data). | |
6959 | */ | |
6960 | static void | |
6961 | init_owl(struct local_owl_data **owl, int target1, int target2, int move, | |
6962 | int at_bottom_of_stack, int new_dragons[BOARDMAX]) | |
6963 | { | |
6964 | reduced_init_owl(owl, at_bottom_of_stack); | |
6965 | ||
6966 | local_owl_node_counter = 0; | |
6967 | (*owl)->lunches_are_current = 0; | |
6968 | owl_mark_dragon(target1, target2, *owl, new_dragons); | |
6969 | if (move != NO_MOVE) | |
6970 | owl_update_goal(move, SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, *owl, 0, NULL); | |
6971 | compute_owl_escape_values(*owl); | |
6972 | } | |
6973 | ||
6974 | ||
6975 | /*********************** | |
6976 | * Storage of owl data | |
6977 | ***********************/ | |
6978 | ||
6979 | /* Check the size of the owl stack and extend it if too small. */ | |
6980 | static void | |
6981 | check_owl_stack_size(void) | |
6982 | { | |
6983 | while (owl_stack_size <= owl_stack_pointer) { | |
6984 | owl_stack[owl_stack_size] = malloc(sizeof(*owl_stack[0])); | |
6985 | gg_assert(owl_stack[owl_stack_size] != NULL); | |
6986 | owl_stack_size++; | |
6987 | } | |
6988 | } | |
6989 | ||
6990 | /* Push owl data one step upwards in the stack. Gets called from | |
6991 | * push_owl. | |
6992 | */ | |
6993 | static void | |
6994 | do_push_owl(struct local_owl_data **owl) | |
6995 | { | |
6996 | struct local_owl_data *new_owl = owl_stack[owl_stack_pointer]; | |
6997 | ||
6998 | /* Mark all the data in *new_owl as uninitialized. */ | |
6999 | VALGRIND_MAKE_WRITABLE(new_owl, sizeof(struct local_owl_data)); | |
7000 | /* Copy the owl data. */ | |
7001 | memcpy(new_owl->goal, (*owl)->goal, sizeof(new_owl->goal)); | |
7002 | memcpy(new_owl->cumulative_goal, (*owl)->cumulative_goal, | |
7003 | sizeof(new_owl->cumulative_goal)); | |
7004 | memcpy(new_owl->boundary, (*owl)->boundary, sizeof(new_owl->boundary)); | |
7005 | memcpy(new_owl->neighbors, (*owl)->neighbors, sizeof(new_owl->neighbors)); | |
7006 | memcpy(new_owl->escape_values, (*owl)->escape_values, | |
7007 | sizeof(new_owl->escape_values)); | |
7008 | new_owl->color = (*owl)->color; | |
7009 | ||
7010 | new_owl->lunches_are_current = 0; | |
7011 | ||
7012 | /* Needed for stack organization. Since there may be one or two sets | |
7013 | * of owl data active at we don't know whether to restore from the | |
7014 | * previos stack entry or two steps back. | |
7015 | */ | |
7016 | new_owl->restore_from = *owl; | |
7017 | ||
7018 | /* Finally move the *owl pointer. */ | |
7019 | *owl = new_owl; | |
7020 | } | |
7021 | ||
7022 | ||
7023 | /* Push owl data one step upwards in the stack. The stack is extended | |
7024 | * with dynamically allocated memory if it is too small. | |
7025 | * | |
7026 | * This function no longer may move existing owl data around, so | |
7027 | * existing pointers do not risk becoming invalid. | |
7028 | */ | |
7029 | static void | |
7030 | push_owl(struct local_owl_data **owl) | |
7031 | { | |
7032 | owl_stack_pointer++; | |
7033 | check_owl_stack_size(); | |
7034 | do_push_owl(owl); | |
7035 | } | |
7036 | ||
7037 | ||
7038 | /* Retrieve owl data from the stack. */ | |
7039 | static void | |
7040 | pop_owl(struct local_owl_data **owl) | |
7041 | { | |
7042 | *owl = (*owl)->restore_from; | |
7043 | owl_stack_pointer--; | |
7044 | } | |
7045 | ||
7046 | ||
7047 | /* | |
7048 | * List worms in order to track captures during owl reading | |
7049 | * (GAIN/LOSS codes) | |
7050 | */ | |
7051 | static int | |
7052 | list_goal_worms(struct local_owl_data *owl, int goal_worm[MAX_GOAL_WORMS]) | |
7053 | { | |
7054 | int pos, k; | |
7055 | int w = 0; | |
7056 | ||
7057 | for (k = 0; k < MAX_GOAL_WORMS; k++) | |
7058 | goal_worm[k] = NO_MOVE; | |
7059 | ||
7060 | for (pos = BOARDMIN; pos < BOARDMAX && w < MAX_GOAL_WORMS; pos++) { | |
7061 | if (ON_BOARD(pos) | |
7062 | && board[pos] | |
7063 | && owl->goal[pos] == 1) { | |
7064 | int origin = find_origin(pos); | |
7065 | for (k = 0; k < w; k++) | |
7066 | if (goal_worm[k] == origin) | |
7067 | break; | |
7068 | if (k == w) | |
7069 | goal_worm[w++] = pos; | |
7070 | } | |
7071 | } | |
7072 | ||
7073 | /* experimental: let's try to fill up the array with other neighboring | |
7074 | * opponent worms | |
7075 | */ | |
7076 | if (1 && (w > 0) && (w < MAX_GOAL_WORMS)) { | |
7077 | pos = goal_worm[0]; | |
7078 | for (k = 0; k < DRAGON2(pos).neighbors && w < MAX_GOAL_WORMS; k++) { | |
7079 | int ii; | |
7080 | int d = DRAGON2(pos).adjacent[k]; | |
7081 | if (DRAGON(d).color != owl->color) | |
7082 | continue; | |
7083 | ||
7084 | for (ii = BOARDMIN; ii < BOARDMAX && w < MAX_GOAL_WORMS; ii++) | |
7085 | if (ON_BOARD(ii) && board[ii] && worm[ii].origin == ii | |
7086 | && worm[ii].size >= 3 && dragon[ii].id == d) | |
7087 | goal_worm[w++] = ii; | |
7088 | } | |
7089 | } | |
7090 | ||
7091 | return w; | |
7092 | } | |
7093 | ||
7094 | static void | |
7095 | prepare_goal_list(int str, struct local_owl_data *owl, | |
7096 | int list[MAX_GOAL_WORMS], int *flag, | |
7097 | int *kworm, int do_list) | |
7098 | { | |
7099 | gg_assert(flag != NULL); | |
7100 | ||
7101 | if (kworm) { | |
7102 | if (do_list) | |
7103 | list_goal_worms(owl, list); | |
7104 | /* N.B. We cannot use sizeof(list) below because a formal array | |
7105 | * parameter implicitly is converted to a pointer and sizeof(list) | |
7106 | * thus equals sizeof(int *), which is not what we want. | |
7107 | */ | |
7108 | memcpy(dragon_goal_worms[dragon[str].id], list, | |
7109 | sizeof(dragon_goal_worms[dragon[str].id])); | |
7110 | *flag = 1; | |
7111 | } | |
7112 | else | |
7113 | *flag = 0; | |
7114 | } | |
7115 | ||
7116 | static void | |
7117 | finish_goal_list(int *flag, int *wpos, int list[MAX_GOAL_WORMS], int index) | |
7118 | { | |
7119 | gg_assert(flag != NULL); | |
7120 | gg_assert(wpos != NULL); | |
7121 | ||
7122 | *flag = 0; | |
7123 | if (index == MAX_GOAL_WORMS) | |
7124 | *wpos = NO_MOVE; | |
7125 | else | |
7126 | *wpos = list[index]; | |
7127 | } | |
7128 | ||
7129 | ||
7130 | /* Returns the number of worms in the goal dragon, and a pointer to each */ | |
7131 | ||
7132 | #if 0 | |
7133 | static int | |
7134 | catalog_goal(struct local_owl_data *owl, int goal_worm[MAX_GOAL_WORMS]) | |
7135 | { | |
7136 | int pos; | |
7137 | int worms = 0; | |
7138 | int k; | |
7139 | ||
7140 | for (k = 0; k < MAX_WORMS; k++) | |
7141 | goal_worm[k] = NO_MOVE; | |
7142 | ||
7143 | for (pos = BOARDMIN; pos < BOARDMAX && worms < MAX_WORMS; pos++) | |
7144 | if (ON_BOARD(pos) | |
7145 | && board[pos] | |
7146 | && (owl->goal)[pos]) { | |
7147 | int origin = find_origin(pos); | |
7148 | if (pos == origin) { | |
7149 | if (0) { | |
7150 | DEBUG(DEBUG_SEMEAI, "goal worm: %1m\n", pos); | |
7151 | } | |
7152 | goal_worm[worms++] = pos; | |
7153 | } | |
7154 | } | |
7155 | return worms; | |
7156 | } | |
7157 | #endif | |
7158 | ||
7159 | /***********************/ | |
7160 | ||
7161 | /* Clear statistics. */ | |
7162 | void | |
7163 | reset_owl_node_counter() | |
7164 | { | |
7165 | global_owl_node_counter = 0; | |
7166 | } | |
7167 | ||
7168 | ||
7169 | /* Retrieve statistics. */ | |
7170 | int | |
7171 | get_owl_node_counter() | |
7172 | { | |
7173 | return global_owl_node_counter; | |
7174 | } | |
7175 | ||
7176 | ||
7177 | /* | |
7178 | * Local Variables: | |
7179 | * tab-width: 8 | |
7180 | * c-basic-offset: 2 | |
7181 | * End: | |
7182 | */ |