Ticket #156: gunnar_7_11.4.diff
| File gunnar_7_11.4.diff, 65.0 kB (added by gunnar, 20 months ago) |
|---|
-
engine/owl.c
81 81 82 82 signed char escape_values[BOARDMAX]; 83 83 int color; 84 int origin; 84 85 85 86 struct eye_data my_eye[BOARDMAX]; 86 87 /* array of half-eye data for use during owl reading */ … … 97 98 98 99 signed char safe_move_cache[BOARDMAX]; 99 100 101 int escape_moves; /* Number of escape moves. */ 102 100 103 /* This is used to organize the owl stack. */ 101 104 struct local_owl_data *restore_from; 102 105 }; … … 172 175 void dump_pattern_list(struct matched_patterns_list_data *list); 173 176 174 177 175 static int do_owl_attack(int str, int *move, int *wormid, 176 struct local_owl_data *owl, int escape); 177 static int do_owl_defend(int str, int *move, int *wormid, 178 struct local_owl_data *owl, int escape); 178 static int owl_attack_trymove(int mpos, struct local_owl_data **owl, 179 struct owl_move_data *move, int *ko_move, 180 int savecode); 181 static int owl_defend_trymove(int mpos, struct local_owl_data **owl, 182 struct owl_move_data *move, int *ko_move, 183 int savecode); 184 static void owl_popgo(struct local_owl_data **owl); 185 static int do_owl_attack(struct local_owl_data *owl, int *move, int *wormid); 186 static int do_owl_defend(struct local_owl_data *owl, int *move, int *wormid); 179 187 static void owl_shapes(struct matched_patterns_list_data *list, 180 188 struct owl_move_data moves[MAX_MOVES], int color, 181 189 struct local_owl_data *owl, struct pattern_db *type); … … 248 256 struct local_owl_data *owl); 249 257 static void eat_lunch_escape_bonus(int lunch, int *min, int *probable, 250 258 int *max, struct local_owl_data *owl); 251 static int select_new_goal_origin( int origin,struct local_owl_data *owl);259 static int select_new_goal_origin(struct local_owl_data *owl); 252 260 static void compute_owl_escape_values(struct local_owl_data *owl); 253 261 static int owl_escape_route(struct local_owl_data *owl); 254 static void do_owl_analyze_semeai(int apos, int bpos, 255 struct local_owl_data *owla, 262 static void do_owl_analyze_semeai(struct local_owl_data *owla, 256 263 struct local_owl_data *owlb, 257 264 int *resulta, int *resultb, 258 265 int *move, int pass, int owl_phase); 259 static int semeai_trymove_and_recurse(int apos, int bpos, 260 struct local_owl_data *owla, 266 static int semeai_trymove_and_recurse(struct local_owl_data *owla, 261 267 struct local_owl_data *owlb, 262 268 int owl_phase, 263 269 int move, int color, int ko_allowed, … … 519 525 else { 520 526 reduced_init_owl(&owla, 1); 521 527 reduced_init_owl(&owlb, 0); 522 local_owl_node_counter = 0; 528 owla->origin = apos; 529 owlb->origin = bpos; 523 530 owl_mark_worm(apos, NO_MOVE, owla); 524 531 owl_mark_worm(bpos, NO_MOVE, owlb); 525 532 } … … 569 576 prefer_ko = EMPTY; 570 577 571 578 if (move == PASS_MOVE) 572 do_owl_analyze_semeai(apos, bpos, owla, owlb, 573 resulta, resultb, semeai_move, 0, owl); 579 do_owl_analyze_semeai(owla, owlb, resulta, resultb, semeai_move, 0, owl); 574 580 else { 575 semeai_trymove_and_recurse( bpos, apos, owlb, owla, owl,576 move, color, 1, 0,"mandatory move",581 semeai_trymove_and_recurse(owlb, owla, owl, move, color, 1, 0, 582 "mandatory move", 577 583 SAME_DRAGON_MAYBE_CONNECTED, NULL, NO_MOVE, 578 584 semeai_move, resultb, resulta); 579 585 *resulta = REVERSE_RESULT(*resulta); … … 619 625 */ 620 626 621 627 static void 622 do_owl_analyze_semeai(int apos, int bpos, 623 struct local_owl_data *owla, 628 do_owl_analyze_semeai(struct local_owl_data *owla, 624 629 struct local_owl_data *owlb, 625 630 int *resulta, int *resultb, 626 631 int *move, int pass, int owl_phase) 627 632 { 633 int apos = owla->origin; 634 int bpos = owlb->origin; 628 635 int color = board[apos]; 629 636 int other = OTHER_COLOR(color); 630 637 #if 0 … … 1114 1121 /* Try playing the move at mpos and call ourselves recursively to 1115 1122 * determine the result obtained by this move. 1116 1123 */ 1117 if (semeai_trymove_and_recurse(apos, bpos, owla, owlb, 1118 owl_phase, mpos, color, 1124 if (semeai_trymove_and_recurse(owla, owlb, owl_phase, mpos, color, 1119 1125 best_resulta == 0 || best_resultb == 0, 1120 1126 moves[k].value, moves[k].name, 1121 1127 moves[k].same_dragon, moves[k].pattern_data, … … 1239 1245 } 1240 1246 else { 1241 1247 /* No working move was found, but opponent hasn't passed. Then we pass. */ 1242 do_owl_analyze_semeai(bpos, apos, owlb, owla, 1243 resultb, resulta, NULL, 1, owl_phase); 1248 do_owl_analyze_semeai(owlb, owla, resultb, resulta, NULL, 1, owl_phase); 1244 1249 *resulta = REVERSE_RESULT(*resulta); 1245 1250 *resultb = REVERSE_RESULT(*resultb); 1246 1251 TRACE("No move found\n"); … … 1266 1271 * move. 1267 1272 */ 1268 1273 static int 1269 semeai_trymove_and_recurse( int apos, int bpos,struct local_owl_data *owla,1274 semeai_trymove_and_recurse(struct local_owl_data *owla, 1270 1275 struct local_owl_data *owlb, 1271 1276 int owl_phase, 1272 1277 int move, int color, int ko_allowed, … … 1276 1281 int lunch, int *semeai_move, 1277 1282 int *this_resulta, int *this_resultb) 1278 1283 { 1284 int apos = owla->origin; 1285 int bpos = owlb->origin; 1279 1286 int ko_move = 0; 1280 1287 1281 1288 gg_assert(this_resulta != NULL && this_resultb != NULL); … … 1317 1324 /* FIXME: Are all owl_data fields and relevant static 1318 1325 * variables properly set up for a call to do_owl_attack()? 1319 1326 */ 1320 *this_resulta = REVERSE_RESULT(do_owl_attack( apos, semeai_move, NULL, owla, 0));1327 *this_resulta = REVERSE_RESULT(do_owl_attack(owla, semeai_move, NULL)); 1321 1328 *this_resultb = *this_resulta; 1322 1329 } 1323 1330 else { 1324 do_owl_analyze_semeai(bpos, apos, owlb, owla, 1325 this_resultb, this_resulta, semeai_move, 1331 do_owl_analyze_semeai(owlb, owla, this_resultb, this_resulta, semeai_move, 1326 1332 0, owl_phase); 1327 1333 *this_resulta = REVERSE_RESULT(*this_resulta); 1328 1334 *this_resultb = REVERSE_RESULT(*this_resultb); … … 1754 1760 } 1755 1761 1756 1762 1757 /* Returns true if a move can be found to attack the dragon 1758 * at (target), in which case (*attack_point) is the recommended move. 1759 * (attack_point) can be a null pointer if only the result is needed. 1760 * 1761 * The array goal marks the extent of the dragon. This must 1762 * be maintained during reading. Call this function only when 1763 * stackp==0; otherwise you can call do_owl_attack but you must 1764 * set up the goal and boundary arrays by hand first. 1765 * 1766 * Returns KO_A or KO_B if the position is ko: 1767 * 1768 * - Returns KO_A if the attack prevails provided attacker is willing to 1769 * ignore any ko threat (the attacker makes the first ko capture). 1770 * 1771 * - Returns KO_B if attack succeeds provided attacker has a ko threat 1772 * which must be answered (the defender makes the first ko capture). 1773 * 1774 * If GNU Go is compiled with `configure --enable-experimental-owl-ext' 1775 * then a return codes of GAIN is also possible. 1776 * 1777 * - Returns GAIN if the attack fails but another worm of the 1778 * opponent's is captured in during the failed attack. The location 1779 * of the killed worm is returned through the *kworm field. 1780 * 1781 * */ 1782 1783 int 1784 owl_attack(int target, int *attack_point, int *certain, int *kworm) 1763 /* Play an attacking move in owl reading. 1764 * In addition to playing the move this involves pushing the owl data 1765 * and then updating the owl information. 1766 */ 1767 static int 1768 owl_attack_trymove(int mpos, struct local_owl_data **owl, 1769 struct owl_move_data *move, int *ko_move, int savecode) 1785 1770 { 1786 int result; 1787 struct local_owl_data *owl; 1788 int reading_nodes_when_called = get_reading_node_counter(); 1789 double start = 0.0; 1790 int tactical_nodes; 1791 int move = NO_MOVE; 1792 int wpos = NO_MOVE; 1793 int wid = MAX_GOAL_WORMS; 1794 1795 result_certain = 1; 1796 if (worm[target].unconditional_status == DEAD) { 1797 if (attack_point) 1798 *attack_point = NO_MOVE; 1799 if (kworm) 1800 *kworm = NO_MOVE; 1801 if (certain) 1802 *certain = 1; 1803 return 1; 1771 int other = OTHER_COLOR((*owl)->color); 1772 /* Try to make the move. */ 1773 if (!komaster_trymove(mpos, other, move->name, (*owl)->origin, 1774 ko_move, savecode == 0)) 1775 return 0; 1776 1777 /* We have now made a move. Analyze the new position. */ 1778 push_owl(owl); 1779 1780 TRACE("Trying %C %1m. Escape = %d. Current stack: ", 1781 other, mpos, (*owl)->escape_moves); 1782 if (verbose) 1783 dump_stack(); 1784 1785 owl_update_boundary_marks(mpos, *owl); 1786 1787 /* If the origin of the dragon has been captured, we look 1788 * for another string which was part of the original dragon, 1789 * marked when stackp==0, which has not been captured. If no 1790 * such string is found, owl_attack declares victory. 1791 */ 1792 if (!IS_STONE(board[(*owl)->origin])) 1793 (*owl)->origin = select_new_goal_origin(*owl); 1794 1795 /* Test whether the move cut the goal dragon apart. */ 1796 if (move->cuts[0] != NO_MOVE && (*owl)->origin != NO_MOVE) { 1797 owl_test_cuts((*owl)->goal, (*owl)->color, move->cuts); 1798 if (!(*owl)->goal[(*owl)->origin]) 1799 (*owl)->origin = select_new_goal_origin(*owl); 1804 1800 } 1801 mark_goal_in_sgf((*owl)->goal); 1805 1802 1806 if (search_persistent_owl_cache(OWL_ATTACK, target, 0, 0, &result, 1807 attack_point, kworm, certain)) 1808 return result; 1803 return 1; 1804 } 1809 1805 1810 if (debug & DEBUG_OWL_PERFORMANCE) 1811 start = gg_cputime(); 1806 1807 /* Play a defending move in owl reading. 1808 * In addition to playing the move this involves pushing the owl data 1809 * and then updating the owl information. 1810 */ 1811 static int 1812 owl_defend_trymove(int mpos, struct local_owl_data **owl, 1813 struct owl_move_data *move, int *ko_move, int savecode) 1814 { 1815 /* Try to make the move. */ 1816 if (!komaster_trymove(mpos, (*owl)->color, move->name, (*owl)->origin, 1817 ko_move, savecode == 0)) 1818 return 0; 1812 1819 1813 TRACE("owl_attack %1m\n", target); 1814 init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); 1815 owl_make_domains(owl, NULL); 1816 prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 1817 kworm, 1); 1818 result = do_owl_attack(target, &move, &wid, owl, 0); 1819 finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 1820 tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 1820 /* We have now made a move. Analyze the new position. */ 1821 push_owl(owl); 1822 1823 TRACE("Trying %C %1m. Escape = %d. Current stack: ", 1824 (*owl)->color, mpos, (*owl)->escape_moves); 1825 if (verbose) 1826 dump_stack(); 1827 1828 if (move->escape) 1829 (*owl)->escape_moves++; 1830 1831 /* Add the stone just played to the goal dragon, unless the 1832 * pattern explicitly asked for not doing this. 1833 */ 1834 owl_update_goal(mpos, move->same_dragon, move->lunch, *owl, 0, 1835 move->pattern_data); 1836 mark_goal_in_sgf((*owl)->goal); 1821 1837 1822 DEBUG(DEBUG_OWL_PERFORMANCE, 1823 "owl_attack %1m, result %d %1m (%d, %d nodes, %f seconds)\n", 1824 target, result, move, local_owl_node_counter, 1825 tactical_nodes, gg_cputime() - start); 1838 return 1; 1839 } 1826 1840 1827 store_persistent_owl_cache(OWL_ATTACK, target, 0, 0,1828 result, move, wpos,1829 result_certain, tactical_nodes,1830 owl->goal, board[target]);1831 if (attack_point)1832 *attack_point = move;1833 if (kworm)1834 *kworm = wpos;1835 if (certain)1836 *certain = result_certain;1837 1841 1838 return result; 1842 /* Undo an owl reading move. */ 1843 static void 1844 owl_popgo(struct local_owl_data **owl) 1845 { 1846 pop_owl(owl); 1847 popgo(); 1839 1848 } 1840 1849 1841 1850 … … 1844 1853 */ 1845 1854 1846 1855 static int 1847 do_owl_attack(int str, int *move, int *wormid, 1848 struct local_owl_data *owl, int escape) 1856 do_owl_attack(struct local_owl_data *owl, int *move, int *wormid) 1849 1857 { 1850 int color = board[ str];1858 int color = board[owl->origin]; 1851 1859 int other = OTHER_COLOR(color); 1852 1860 struct owl_move_data vital_moves[MAX_MOVES]; 1853 1861 struct owl_move_data shape_moves[MAX_MOVES]; … … 1870 1878 int value2; 1871 1879 int this_variation_number = count_variations - 1; 1872 1880 1873 SETUP_TRACE_INFO("owl_attack", str);1881 SETUP_TRACE_INFO("owl_attack", owl->origin); 1874 1882 1875 1883 shape_patterns.initialized = 0; 1876 1884 1877 str = find_origin(str);1885 owl->origin = find_origin(owl->origin); 1878 1886 1879 if (tt_get(&ttable, OWL_ATTACK, str, NO_MOVE, depth - stackp, NULL,1887 if (tt_get(&ttable, OWL_ATTACK, owl->origin, NO_MOVE, depth - stackp, NULL, 1880 1888 &value1, &value2, &xpos) == 2) { 1881 1889 1882 1890 TRACE_CACHED_RESULT(value1, xpos); … … 1903 1911 } 1904 1912 1905 1913 1906 /* If reading goes to deep or we run out of nodes, we assume life. */1914 /* If reading goes too deep or we run out of nodes, we assume life. */ 1907 1915 if (reading_limit_reached(&live_reason, this_variation_number)) { 1908 1916 SGFTRACE(0, 0, live_reason); 1909 READ_RETURN(OWL_ATTACK, str, depth - stackp, move, 0, 0);1917 READ_RETURN(OWL_ATTACK, owl->origin, depth - stackp, move, 0, 0); 1910 1918 } 1911 1919 1912 1920 memset(mw, 0, sizeof(mw)); … … 1948 1956 SGFTRACE(0, acode, live_reason); 1949 1957 TRACE("%oVariation %d: ALIVE (%s)\n", this_variation_number, live_reason); 1950 1958 if (acode == 0) { 1951 READ_RETURN(OWL_ATTACK, str, depth - stackp, move, 0, 0);1959 READ_RETURN(OWL_ATTACK, owl->origin, depth - stackp, move, 0, 0); 1952 1960 } 1953 1961 else { 1954 1962 if (wormid) 1955 1963 *wormid = saveworm; 1956 READ_RETURN2(OWL_ATTACK, str, depth - stackp,1964 READ_RETURN2(OWL_ATTACK, owl->origin, depth - stackp, 1957 1965 move, mpos, acode, saveworm); 1958 1966 } 1959 1967 } … … 2018 2026 2019 2027 sgf_dumptree = NULL; 2020 2028 count_variations = 0; 2021 result = attack( str, &apos);2029 result = attack(owl->origin, &apos); 2022 2030 if (result == WIN 2023 2031 || (result != 0 && (min_eyes(&probable_eyes) >= 2 2024 2032 || pass == 5))) { … … 2036 2044 case 4: 2037 2045 if (number_tried_moves == 0) { 2038 2046 int dpos; 2039 int dcode = do_owl_defend( str, &dpos, NULL, owl, escape);2047 int dcode = do_owl_defend(owl, &dpos, NULL); 2040 2048 /* No defense, we won. */ 2041 2049 if (dcode == 0) { 2042 2050 TRACE("%oVariation %d: DEAD (no defense)\n", 2043 2051 this_variation_number); 2044 2052 SGFTRACE(0, WIN, "no defense"); 2045 2053 close_pattern_list(other, &shape_patterns); 2046 READ_RETURN(OWL_ATTACK, str, depth - stackp, move, 0, WIN);2054 READ_RETURN(OWL_ATTACK, owl->origin, depth - stackp, move, 0, WIN); 2047 2055 } 2048 2056 else if (dpos != NO_MOVE) { 2049 2057 /* The dragon could be defended by one more move. Try to … … 2098 2106 TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); 2099 2107 SGFTRACE(0, 0, "escaped"); 2100 2108 close_pattern_list(other, &shape_patterns); 2101 READ_RETURN0(OWL_ATTACK, str, depth - stackp);2109 READ_RETURN0(OWL_ATTACK, owl->origin, depth - stackp); 2102 2110 } 2103 2111 #endif 2104 2112 … … 2112 2120 for (k = 0; k < MAX_MOVES; k++) { 2113 2121 int mpos; 2114 2122 int ko_move = -1; 2115 int origin = NO_MOVE;2116 2123 int captured; 2117 2124 int wid = MAX_GOAL_WORMS; 2118 2125 int dcode; … … 2144 2151 /* Have we already tested this move? */ 2145 2152 if (mw[mpos]) 2146 2153 continue; 2154 else 2155 mw[mpos] = 1; 2147 2156 2148 2157 captured = (color == WHITE ? white_captured : black_captured); 2149 2158 2150 /* Try to make the move. */ 2151 if (!komaster_trymove(mpos, other, moves[k].name, str, 2152 &ko_move, savecode == 0)) 2159 /* Play the move, push the owl data, and update the owl 2160 * strucures after the move. 2161 */ 2162 if (!owl_attack_trymove(mpos, &owl, &moves[k], &ko_move, savecode)) 2153 2163 continue; 2154 2164 2155 2165 captured = (color == WHITE ? white_captured : black_captured) - captured; 2156 2157 TRACE("Trying %C %1m. Escape = %d. Current stack: ", 2158 other, mpos, escape); 2159 if (verbose) 2160 dump_stack(); 2161 2162 /* We have now made a move. Analyze the new position. */ 2163 push_owl(&owl); 2164 mw[mpos] = 1; 2166 2165 2167 number_tried_moves++; 2166 owl_update_boundary_marks(mpos, owl); 2167 2168 /* If the origin of the dragon has been captured, we look 2169 * for another string which was part of the original dragon, 2170 * marked when stackp==0, which has not been captured. If no 2171 * such string is found, owl_attack declares victory. 2172 */ 2173 if (IS_STONE(board[str])) 2174 origin = str; 2175 else 2176 origin = select_new_goal_origin(NO_MOVE, owl); 2177 2178 /* Test whether the move cut the goal dragon apart. */ 2179 if (moves[k].cuts[0] != NO_MOVE && origin != NO_MOVE) { 2180 owl_test_cuts(owl->goal, owl->color, moves[k].cuts); 2181 if (!owl->goal[origin]) 2182 origin = select_new_goal_origin(origin, owl); 2168 if (owl->origin == NO_MOVE) 2169 dcode = 0; 2170 else { 2171 dcode = do_owl_defend(owl, NULL, &wid); 2183 2172 } 2184 mark_goal_in_sgf(owl->goal);2185 2173 2186 if (origin == NO_MOVE)2187 dcode = 0;2188 else2189 dcode = do_owl_defend(origin, NULL, &wid, owl, escape);2190 2191 2174 if (!ko_move) { 2192 2175 if (dcode == 0) { 2193 pop_owl(&owl); 2194 popgo(); 2176 owl_popgo(&owl); 2195 2177 if (sgf_dumptree) { 2196 2178 const char *wintxt; 2197 2179 char winstr[192]; 2198 if (o rigin == NO_MOVE)2180 if (owl->origin == NO_MOVE) 2199 2181 wintxt = "all original stones captured"; 2200 2182 else 2201 2183 wintxt = "attack effective"; … … 2204 2186 SGFTRACE(mpos, WIN, winstr); 2205 2187 } 2206 2188 close_pattern_list(other, &shape_patterns); 2207 READ_RETURN(OWL_ATTACK, str, depth - stackp, move, mpos, WIN); 2189 READ_RETURN(OWL_ATTACK, owl->origin, depth - stackp, 2190 move, mpos, WIN); 2208 2191 } 2209 2192 else if (experimental_owl_ext && dcode == LOSS) { 2210 2193 if (saveworm == MAX_GOAL_WORMS … … 2270 2253 number_tried_moves--; 2271 2254 } 2272 2255 } 2273 2274 pop_owl(&owl); 2275 popgo(); 2256 owl_popgo(&owl); 2276 2257 } 2277 2258 } 2278 2259 … … 2283 2264 SGFTRACE(savemove, savecode, "attack effective (gain) - E"); 2284 2265 if (wormid) 2285 2266 *wormid = saveworm; 2286 READ_RETURN2(OWL_ATTACK, str, depth - stackp,2267 READ_RETURN2(OWL_ATTACK, owl->origin, depth - stackp, 2287 2268 move, savemove, savecode, saveworm); 2288 2269 } 2289 2270 else { 2290 2271 SGFTRACE(savemove, savecode, "attack effective (ko) - E"); 2291 READ_RETURN(OWL_ATTACK, str, depth - stackp, move, savemove, savecode); 2272 READ_RETURN(OWL_ATTACK, owl->origin, depth - stackp, 2273 move, savemove, savecode); 2292 2274 } 2293 2275 } 2294 2276 … … 2299 2281 SGFTRACE(0, 0, winstr); 2300 2282 } 2301 2283 2302 READ_RETURN0(OWL_ATTACK, str, depth - stackp);2284 READ_RETURN0(OWL_ATTACK, owl->origin, depth - stackp); 2303 2285 } 2304 2286 2305 2287 2306 /* Returns true if the dragon at (target) can be captured given 2307 * two moves in a row. The first two moves to capture the 2308 * dragon are given as (*attack1) and (*attack2). 2309 */ 2288 /* Static function containing the main recursive code for owl_defend. */ 2310 2289 2311 int2312 owl_threaten_attack(int target, int *attack1, int *attack2)2313 {2314 struct owl_move_data moves[MAX_MOVES];2315 int k;2316 int other = OTHER_COLOR(board[target]);2317 struct local_owl_data *owl;2318 int result = 0;2319 int reading_nodes_when_called = get_reading_node_counter();2320 signed char saved_boundary[BOARDMAX];2321 double start = 0.0;2322 int tactical_nodes;2323 int move = 0;2324 int move2 = 0;2325 struct matched_patterns_list_data shape_patterns;2326 2327 shape_patterns.initialized = 0;2328 result_certain = 1;2329 if (search_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0,2330 &result, attack1, attack2, NULL))2331 return result;2332 2333 if (debug & DEBUG_OWL_PERFORMANCE)2334 start = gg_cputime();2335 2336 gg_assert(stackp == 0);2337 TRACE("owl_threaten_attack %1m\n", target);2338 init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL);2339 memcpy(saved_boundary, owl->boundary, sizeof(saved_boundary));2340 owl_make_domains(owl, NULL);2341 owl_shapes(&shape_patterns, moves, other, owl, &owl_attackpat_db);2342 for (k = 0; k < MAX_MOVES; k++) {2343 current_owl_data = owl;2344 if (!get_next_move_from_list(&shape_patterns, other, moves, 1, owl))2345 break;2346 else {2347 int mpos = moves[k].pos;2348 2349 if (mpos != NO_MOVE && moves[k].value > 0)2350 if (trymove(mpos, other, moves[k].name, target)) {2351 int pos;2352 int origin = NO_MOVE;2353 owl->lunches_are_current = 0;2354 owl_update_boundary_marks(mpos, owl);2355 2356 /* If the origin of the dragon has been captured, we look2357 * for another string which was part of the original dragon,2358 * marked when stackp==0, which has not been captured. If no2359 * such string is found, owl_attack declares victory.2360 */2361 2362 if (board[target] == EMPTY) {2363 for (pos = BOARDMIN; pos < BOARDMAX; pos++) {2364 if (IS_STONE(board[pos]) && owl->goal[pos] == 1) {2365 origin = find_origin(pos);2366 break;2367 }2368 }2369 2370 if (origin == NO_MOVE2371 || do_owl_attack(origin, NULL, NULL, owl, 0)) {2372 /* probably this can't happen */2373 popgo();2374 gg_assert(stackp == 0);2375 result = 1;2376 break;2377 }2378 }2379 else if (do_owl_attack(target, &move2, NULL, owl, 0) == WIN) {2380 move = moves[k].pos;2381 popgo();2382 gg_assert(stackp == 0);2383 result = 1;2384 break;2385 }2386 popgo();2387 memcpy(owl->boundary, saved_boundary, sizeof(saved_boundary));2388 }2389 }2390 }2391 tactical_nodes = get_reading_node_counter() - reading_nodes_when_called;2392 gg_assert(stackp == 0);2393 2394 DEBUG(DEBUG_OWL_PERFORMANCE,2395 "owl_threaten_attack %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n",2396 target, move, move2, result, local_owl_node_counter,2397 tactical_nodes, gg_cputime() - start);2398 2399 store_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0,2400 result, move, move2, 0,2401 tactical_nodes, owl->goal, board[target]);2402 2403 if (attack1)2404 *attack1 = move;2405 if (attack2)2406 *attack2 = move2;2407 2408 close_pattern_list(other, &shape_patterns);2409 return result;2410 }2411 2412 2413 /* Returns true if a move can be found to defend the dragon2414 * at (target), in which case (*defense_point) is the recommended move.2415 * (defense_point) can be a null pointer if the result is not needed.2416 *2417 * The array goal marks the extent of the dragon. This must2418 * be maintained during reading. Call this function only when2419 * stackp==0; otherwise you can call do_owl_attack but you must2420 * set up the goal and boundary arrays by hand first.2421 *2422 * Returns KO_A or KO_B if the position is ko:2423 *2424 * - Returns KO_A if the defendse succeeds provided the defender is willing to2425 * ignore any ko threat (the defender makes the first ko capture).2426 * - Returns KO_B if the defense succeeds provided the defender has a ko threat2427 * which must be answered (the attacker makes the first ko capture).2428 *2429 * If GNU Go is compiled with `configure --enable-experimental-owl-ext'2430 * then a return codes of GAIN is also possible.2431 *2432 * - Returns LOSS if the defense succeeds but another worm of the2433 * defender's is captured in during the defense. The location2434 * of the killed worm is returned through the *kworm field.2435 *2436 * The array goal marks the extent of the dragon. This must2437 * be maintained during reading.2438 */2439 2440 int2441 owl_defend(int target, int *defense_point, int *certain, int *kworm)2442 {2443 int result;2444 static struct local_owl_data *owl;2445 int reading_nodes_when_called = get_reading_node_counter();2446 double start = 0.0;2447 int tactical_nodes;2448 int move = NO_MOVE;2449 int wpos = NO_MOVE;2450 int wid = MAX_GOAL_WORMS;2451 2452 result_certain = 1;2453 if (worm[target].unconditional_status == DEAD)
