Ticket #156: gunnar_7_11.4.diff

File gunnar_7_11.4.diff, 65.0 KB (added by gunnar, 5 years ago)

Owl code restructuring.

  • engine/owl.c

     
    8181 
    8282  signed char escape_values[BOARDMAX]; 
    8383  int color; 
     84  int origin; 
    8485 
    8586  struct eye_data my_eye[BOARDMAX]; 
    8687  /* array of half-eye data for use during owl reading */ 
     
    9798 
    9899  signed char safe_move_cache[BOARDMAX]; 
    99100 
     101  int escape_moves; /* Number of escape moves. */ 
     102   
    100103  /* This is used to organize the owl stack. */ 
    101104  struct local_owl_data *restore_from; 
    102105}; 
     
    172175void dump_pattern_list(struct matched_patterns_list_data *list); 
    173176 
    174177 
    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); 
     178static int owl_attack_trymove(int mpos, struct local_owl_data **owl, 
     179                              struct owl_move_data *move, int *ko_move, 
     180                              int savecode); 
     181static int owl_defend_trymove(int mpos, struct local_owl_data **owl, 
     182                              struct owl_move_data *move, int *ko_move, 
     183                              int savecode); 
     184static void owl_popgo(struct local_owl_data **owl); 
     185static int do_owl_attack(struct local_owl_data *owl, int *move, int *wormid); 
     186static int do_owl_defend(struct local_owl_data *owl, int *move, int *wormid); 
    179187static void owl_shapes(struct matched_patterns_list_data *list, 
    180188                       struct owl_move_data moves[MAX_MOVES], int color, 
    181189                       struct local_owl_data *owl, struct pattern_db *type); 
     
    248256                        struct local_owl_data *owl); 
    249257static void eat_lunch_escape_bonus(int lunch, int *min, int *probable, 
    250258                                   int *max, struct local_owl_data *owl); 
    251 static int select_new_goal_origin(int origin, struct local_owl_data *owl); 
     259static int select_new_goal_origin(struct local_owl_data *owl); 
    252260static void compute_owl_escape_values(struct local_owl_data *owl); 
    253261static 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, 
     262static void do_owl_analyze_semeai(struct local_owl_data *owla, 
    256263                                  struct local_owl_data *owlb, 
    257264                                  int *resulta, int *resultb, 
    258265                                  int *move, int pass, int owl_phase); 
    259 static int semeai_trymove_and_recurse(int apos, int bpos, 
    260                                       struct local_owl_data *owla, 
     266static int semeai_trymove_and_recurse(struct local_owl_data *owla, 
    261267                                      struct local_owl_data *owlb, 
    262268                                      int owl_phase, 
    263269                                      int move, int color, int ko_allowed, 
     
    519525  else { 
    520526    reduced_init_owl(&owla, 1); 
    521527    reduced_init_owl(&owlb, 0); 
    522     local_owl_node_counter = 0; 
     528    owla->origin = apos; 
     529    owlb->origin = bpos; 
    523530    owl_mark_worm(apos, NO_MOVE, owla); 
    524531    owl_mark_worm(bpos, NO_MOVE, owlb); 
    525532  } 
     
    569576    prefer_ko = EMPTY; 
    570577   
    571578  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); 
    574580  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", 
    577583                               SAME_DRAGON_MAYBE_CONNECTED, NULL, NO_MOVE, 
    578584                               semeai_move, resultb, resulta); 
    579585    *resulta = REVERSE_RESULT(*resulta); 
     
    619625 */ 
    620626 
    621627static void 
    622 do_owl_analyze_semeai(int apos, int bpos,  
    623                       struct local_owl_data *owla, 
     628do_owl_analyze_semeai(struct local_owl_data *owla, 
    624629                      struct local_owl_data *owlb, 
    625630                      int *resulta, int *resultb, 
    626631                      int *move, int pass, int owl_phase) 
    627632{ 
     633  int apos = owla->origin; 
     634  int bpos = owlb->origin; 
    628635  int color = board[apos]; 
    629636  int other = OTHER_COLOR(color); 
    630637#if 0 
     
    11141121    /* Try playing the move at mpos and call ourselves recursively to 
    11151122     * determine the result obtained by this move. 
    11161123     */ 
    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, 
    11191125                                   best_resulta == 0 || best_resultb == 0, 
    11201126                                   moves[k].value, moves[k].name, 
    11211127                                   moves[k].same_dragon, moves[k].pattern_data, 
     
    12391245    } 
    12401246    else { 
    12411247    /* 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); 
    12441249      *resulta = REVERSE_RESULT(*resulta); 
    12451250      *resultb = REVERSE_RESULT(*resultb); 
    12461251      TRACE("No move found\n"); 
     
    12661271 * move. 
    12671272 */ 
    12681273static int 
    1269 semeai_trymove_and_recurse(int apos, int bpos, struct local_owl_data *owla, 
     1274semeai_trymove_and_recurse(struct local_owl_data *owla, 
    12701275                           struct local_owl_data *owlb, 
    12711276                           int owl_phase, 
    12721277                           int move, int color, int ko_allowed, 
     
    12761281                           int lunch, int *semeai_move, 
    12771282                           int *this_resulta, int *this_resultb) 
    12781283{ 
     1284  int apos = owla->origin; 
     1285  int bpos = owlb->origin; 
    12791286  int ko_move = 0; 
    12801287   
    12811288  gg_assert(this_resulta != NULL && this_resultb != NULL); 
     
    13171324    /* FIXME: Are all owl_data fields and relevant static 
    13181325     * variables properly set up for a call to do_owl_attack()? 
    13191326     */ 
    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)); 
    13211328    *this_resultb = *this_resulta; 
    13221329  } 
    13231330  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, 
    13261332                          0, owl_phase); 
    13271333    *this_resulta = REVERSE_RESULT(*this_resulta); 
    13281334    *this_resultb = REVERSE_RESULT(*this_resultb); 
     
    17541760} 
    17551761 
    17561762 
    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 */ 
     1767static int 
     1768owl_attack_trymove(int mpos, struct local_owl_data **owl, 
     1769                   struct owl_move_data *move, int *ko_move, int savecode) 
    17851770{ 
    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); 
    18041800  } 
     1801  mark_goal_in_sgf((*owl)->goal); 
    18051802 
    1806   if (search_persistent_owl_cache(OWL_ATTACK, target, 0, 0, &result, 
    1807                                   attack_point, kworm, certain)) 
    1808     return result; 
     1803  return 1; 
     1804} 
    18091805 
    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 */ 
     1811static int 
     1812owl_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; 
    18121819   
    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); 
    18211837 
    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} 
    18261840 
    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; 
    18371841 
    1838   return result; 
     1842/* Undo an owl reading move. */ 
     1843static void 
     1844owl_popgo(struct local_owl_data **owl) 
     1845{ 
     1846  pop_owl(owl); 
     1847  popgo(); 
    18391848} 
    18401849 
    18411850 
     
    18441853 */ 
    18451854 
    18461855static int 
    1847 do_owl_attack(int str, int *move, int *wormid, 
    1848               struct local_owl_data *owl, int escape) 
     1856do_owl_attack(struct local_owl_data *owl, int *move, int *wormid) 
    18491857{ 
    1850   int color = board[str]; 
     1858  int color = board[owl->origin]; 
    18511859  int other = OTHER_COLOR(color); 
    18521860  struct owl_move_data vital_moves[MAX_MOVES]; 
    18531861  struct owl_move_data shape_moves[MAX_MOVES]; 
     
    18701878  int value2; 
    18711879  int this_variation_number = count_variations - 1; 
    18721880   
    1873   SETUP_TRACE_INFO("owl_attack", str); 
     1881  SETUP_TRACE_INFO("owl_attack", owl->origin); 
    18741882 
    18751883  shape_patterns.initialized = 0; 
    18761884 
    1877   str = find_origin(str); 
     1885  owl->origin = find_origin(owl->origin); 
    18781886 
    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,  
    18801888             &value1, &value2, &xpos) == 2) { 
    18811889 
    18821890    TRACE_CACHED_RESULT(value1, xpos); 
     
    19031911  } 
    19041912 
    19051913 
    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. */ 
    19071915  if (reading_limit_reached(&live_reason, this_variation_number)) { 
    19081916    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); 
    19101918  } 
    19111919 
    19121920  memset(mw, 0, sizeof(mw)); 
     
    19481956    SGFTRACE(0, acode, live_reason); 
    19491957    TRACE("%oVariation %d: ALIVE (%s)\n", this_variation_number, live_reason); 
    19501958    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); 
    19521960    } 
    19531961    else { 
    19541962      if (wormid) 
    19551963        *wormid = saveworm; 
    1956       READ_RETURN2(OWL_ATTACK, str, depth - stackp, 
     1964      READ_RETURN2(OWL_ATTACK, owl->origin, depth - stackp, 
    19571965                   move, mpos, acode, saveworm); 
    19581966    } 
    19591967  } 
     
    20182026 
    20192027        sgf_dumptree = NULL; 
    20202028        count_variations = 0; 
    2021         result = attack(str, &apos); 
     2029        result = attack(owl->origin, &apos); 
    20222030        if (result == WIN 
    20232031            || (result != 0 && (min_eyes(&probable_eyes) >= 2 
    20242032                                || pass == 5))) { 
     
    20362044    case 4: 
    20372045      if (number_tried_moves == 0) { 
    20382046        int dpos; 
    2039         int dcode = do_owl_defend(str, &dpos, NULL, owl, escape); 
     2047        int dcode = do_owl_defend(owl, &dpos, NULL); 
    20402048        /* No defense, we won. */ 
    20412049        if (dcode == 0) { 
    20422050          TRACE("%oVariation %d: DEAD (no defense)\n", 
    20432051                this_variation_number); 
    20442052          SGFTRACE(0, WIN, "no defense"); 
    20452053          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); 
    20472055        } 
    20482056        else if (dpos != NO_MOVE) { 
    20492057          /* The dragon could be defended by one more move. Try to 
     
    20982106      TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); 
    20992107      SGFTRACE(0, 0, "escaped"); 
    21002108      close_pattern_list(other, &shape_patterns); 
    2101       READ_RETURN0(OWL_ATTACK, str, depth - stackp); 
     2109      READ_RETURN0(OWL_ATTACK, owl->origin, depth - stackp); 
    21022110    } 
    21032111#endif 
    21042112 
     
    21122120    for (k = 0; k < MAX_MOVES; k++) { 
    21132121      int mpos; 
    21142122      int ko_move = -1; 
    2115       int origin = NO_MOVE; 
    21162123      int captured; 
    21172124      int wid = MAX_GOAL_WORMS; 
    21182125      int dcode; 
     
    21442151      /* Have we already tested this move? */ 
    21452152      if (mw[mpos]) 
    21462153        continue; 
     2154      else 
     2155        mw[mpos] = 1; 
    21472156 
    21482157      captured = (color == WHITE ? white_captured : black_captured); 
    21492158 
    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)) 
    21532163        continue; 
    2154  
     2164       
    21552165      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       
    21652167      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); 
    21832172      } 
    2184       mark_goal_in_sgf(owl->goal); 
    21852173 
    2186       if (origin == NO_MOVE) 
    2187         dcode = 0; 
    2188       else 
    2189         dcode = do_owl_defend(origin, NULL, &wid, owl, escape); 
    2190  
    21912174      if (!ko_move) { 
    21922175        if (dcode == 0) { 
    2193           pop_owl(&owl); 
    2194           popgo(); 
     2176          owl_popgo(&owl); 
    21952177          if (sgf_dumptree) { 
    21962178            const char *wintxt; 
    21972179            char winstr[192]; 
    2198             if (origin == NO_MOVE) 
     2180            if (owl->origin == NO_MOVE) 
    21992181              wintxt = "all original stones captured"; 
    22002182            else 
    22012183              wintxt = "attack effective"; 
     
    22042186            SGFTRACE(mpos, WIN, winstr); 
    22052187          } 
    22062188          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); 
    22082191        } 
    22092192        else if (experimental_owl_ext && dcode == LOSS) { 
    22102193          if (saveworm == MAX_GOAL_WORMS 
     
    22702253          number_tried_moves--; 
    22712254        } 
    22722255      } 
    2273      
    2274       pop_owl(&owl); 
    2275       popgo(); 
     2256      owl_popgo(&owl); 
    22762257    } 
    22772258  } 
    22782259 
     
    22832264      SGFTRACE(savemove, savecode, "attack effective (gain) - E"); 
    22842265      if (wormid) 
    22852266        *wormid = saveworm; 
    2286       READ_RETURN2(OWL_ATTACK, str, depth - stackp, 
     2267      READ_RETURN2(OWL_ATTACK, owl->origin, depth - stackp, 
    22872268                   move, savemove, savecode, saveworm); 
    22882269    } 
    22892270    else { 
    22902271      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); 
    22922274    } 
    22932275  } 
    22942276 
     
    22992281    SGFTRACE(0, 0, winstr); 
    23002282  } 
    23012283   
    2302   READ_RETURN0(OWL_ATTACK, str, depth - stackp); 
     2284  READ_RETURN0(OWL_ATTACK, owl->origin, depth - stackp); 
    23032285} 
    23042286 
    23052287 
    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. */ 
    23102289 
    2311 int 
    2312 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 look 
    2357            * for another string which was part of the original dragon, 
    2358            * marked when stackp==0, which has not been captured. If no 
    2359            * 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_MOVE 
    2371                 || 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 dragon 
    2414  * 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 must 
    2418  * be maintained during reading. Call this function only when 
    2419  * stackp==0; otherwise you can call do_owl_attack but you must 
    2420  * 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 to 
    2425  *   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 threat 
    2427  *   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 the 
    2433  *   defender's is captured in during the defense. The location 
    2434  *   of the killed worm is returned through the *kworm field. 
    2435  * 
    2436  * The array goal marks the extent of the dragon. This must 
    2437  * be maintained during reading.   
    2438  */ 
    2439  
    2440 int 
    2441 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) 
    2454     return 0; 
    2455  
    2456   if (search_persistent_owl_cache(OWL_DEFEND, target, 0, 0, &result,  
    2457                                   defense_point, kworm, certain)) 
    2458     return result; 
    2459  
    2460   if (debug & DEBUG_OWL_PERFORMANCE) 
    2461     start = gg_cputime(); 
    2462  
    2463   TRACE("owl_defend %1m\n", target); 
    2464   init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); 
    2465   owl_make_domains(owl, NULL); 
    2466   prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
    2467                     kworm, 1); 
    2468   result = do_owl_defend(target, &move, &wid, owl, 0); 
    2469   finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
    2470   tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
    2471  
    2472   DEBUG(DEBUG_OWL_PERFORMANCE, 
    2473     "owl_defend %1m, result %d %1m (%d, %d nodes, %f seconds)\n", 
    2474             target, result, move, local_owl_node_counter, 
    2475             tactical_nodes, gg_cputime() - start); 
    2476  
    2477   store_persistent_owl_cache(OWL_DEFEND, target, 0, 0, result, move, wpos, 
    2478                              result_certain, tactical_nodes, owl->goal, 
    2479                              board[target]); 
    2480  
    2481   if (defense_point) 
    2482     *defense_point = move; 
    2483   if (kworm) 
    2484     *kworm = wpos; 
    2485   if (certain) 
    2486     *certain = result_certain; 
    2487    
    2488   return result; 
    2489 } 
    2490  
    2491  
    2492 /* Static function containing the main recursive code for owl_defend. 
    2493  */ 
    2494  
    24952290static int 
    2496 do_owl_defend(int str, int *move, int *wormid, struct local_owl_data *owl, 
    2497               int escape) 
     2291do_owl_defend(struct local_owl_data *owl, int *move, int *wormid) 
    24982292{ 
    2499   int color = board[str]; 
     2293  int color = board[owl->origin]; 
    25002294  struct owl_move_data shape_moves[MAX_MOVES]; 
    25012295  struct owl_move_data vital_moves[MAX_MOVES]; 
    25022296  struct owl_move_data *moves; 
     
    25192313  int value2; 
    25202314  int this_variation_number = count_variations - 1; 
    25212315 
    2522   SETUP_TRACE_INFO("owl_defend", str); 
     2316  SETUP_TRACE_INFO("owl_defend", owl->origin); 
    25232317 
    25242318  shape_patterns.initialized = 0; 
    25252319   
    2526   str = find_origin(str); 
     2320  owl->origin = find_origin(owl->origin); 
    25272321 
    2528   if (tt_get(&ttable, OWL_DEFEND, str, NO_MOVE, depth - stackp, NULL,  
     2322  if (tt_get(&ttable, OWL_DEFEND, owl->origin, NO_MOVE, depth - stackp, NULL,  
    25292323             &value1, &value2, &xpos) == 2) { 
    25302324     
    25312325    TRACE_CACHED_RESULT(value1, xpos); 
     
    25662360     */ 
    25672361    TRACE("%oVariation %d: ALIVE (escaped)\n", this_variation_number); 
    25682362    SGFTRACE(0, WIN, "escaped"); 
    2569     READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); 
     2363    READ_RETURN(OWL_DEFEND, owl->origin, depth - stackp, move, 0, WIN); 
    25702364  } 
    25712365 
    25722366  /* If reading goes to deep or we run out of nodes, we assume life. */ 
    25732367  if (reading_limit_reached(&live_reason, this_variation_number)) { 
    25742368    SGFTRACE(0, WIN, live_reason); 
    2575     READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); 
     2369    READ_RETURN(OWL_DEFEND, owl->origin, depth - stackp, move, 0, WIN); 
    25762370  } 
    25772371 
    25782372  memset(mw, 0, sizeof(mw)); 
     
    25822376  current_owl_data = owl; 
    25832377  memset(owl->safe_move_cache, 0, sizeof(owl->safe_move_cache)); 
    25842378 
    2585   /* First see whether we might already be alife. */ 
    2586   if (escape < MAX_ESCAPE) { 
     2379  /* First see whether we might already be alive. */ 
     2380  if (owl->escape_moves < MAX_ESCAPE) { 
    25872381    if (owl_estimate_life(owl, NULL, vital_moves, &live_reason, 0, 
    25882382                          &probable_eyes, &eyemin, &eyemax)) { 
    25892383      SGFTRACE(0, WIN, live_reason); 
    25902384      TRACE("%oVariation %d: ALIVE (%s)\n", 
    25912385            this_variation_number, live_reason); 
    2592       READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); 
     2386      READ_RETURN(OWL_DEFEND, owl->origin, depth - stackp, move, 0, WIN); 
    25932387    } 
    25942388  } 
    25952389  else { 
     
    26772471 
    26782472          sgf_dumptree = NULL; 
    26792473          count_variations = 0; 
    2680           if (attack_and_defend(str, NULL, NULL, NULL, &dpos) 
     2474          if (attack_and_defend(owl->origin, NULL, NULL, NULL, &dpos) 
    26812475              && (approxlib(dpos, color, 2, NULL) > 1 
    26822476                  || does_capture_something(dpos, color))) { 
    2683             TRACE("Found tactical defense for %1m at %1m.\n", str, dpos); 
     2477            TRACE("Found tactical defense for %1m at %1m.\n", 
     2478                  owl->origin, dpos); 
    26842479            set_single_owl_move(shape_moves, dpos, "tactical_defense"); 
    26852480            moves = shape_moves; 
    26862481          } 
     
    26992494    for (k = 0; k < MAX_MOVES; k++) { 
    27002495      int mpos; 
    27012496      int ko_move = -1; 
    2702       int new_escape; 
    27032497      int wid = MAX_GOAL_WORMS; 
    27042498       
    27052499      /* Consider only the highest scoring move if we're deeper than 
     
    27292523      /* Have we already tested this move? */ 
    27302524      if (mw[mpos]) 
    27312525        continue; 
     2526      else 
     2527        mw[mpos] = 1; 
     2528 
     2529      /* Play the move, push the owl data, and update the owl 
     2530       * strucures after the move. 
     2531       */ 
     2532      if (!owl_defend_trymove(mpos, &owl, &moves[k], &ko_move, savecode)) 
     2533        continue; 
    27322534       
    2733       /* Try to make the move. */ 
    2734       if (!komaster_trymove(mpos, color, moves[k].name, str, 
    2735                             &ko_move, savecode == 0)) 
    2736         continue; 
    2737  
    2738       new_escape = escape; 
    2739       if (moves[k].escape) 
    2740         new_escape++; 
    2741  
    2742       TRACE("Trying %C %1m. Escape = %d. Current stack: ", 
    2743             color, mpos, escape); 
    2744       if (verbose) 
    2745         dump_stack(); 
    2746  
    2747       /* We have now made a move. Analyze the new position. */ 
    2748       push_owl(&owl); 
    2749       mw[mpos] = 1; 
    27502535      number_tried_moves++; 
    2751  
    2752       /* Add the stone just played to the goal dragon, unless the 
    2753        * pattern explicitly asked for not doing this. 
    2754        */ 
    2755       owl_update_goal(mpos, moves[k].same_dragon, moves[k].lunch, owl, 0, 
    2756                       moves[k].pattern_data); 
    2757       mark_goal_in_sgf(owl->goal); 
    2758  
    27592536      if (!ko_move) { 
    2760         int acode = do_owl_attack(str, NULL, &wid, owl, new_escape); 
     2537        int acode = do_owl_attack(owl, NULL, &wid); 
    27612538        if (!acode) { 
    2762           pop_owl(&owl); 
    2763           popgo(); 
     2539          owl_popgo(&owl); 
    27642540          if (sgf_dumptree) { 
    27652541            char winstr[192]; 
    27662542            sprintf(winstr, "defense effective)\n  (%d variations",    
     
    27682544            SGFTRACE(mpos, WIN, winstr); 
    27692545          } 
    27702546          close_pattern_list(color, &shape_patterns); 
    2771           READ_RETURN(OWL_DEFEND, str, depth - stackp, move, mpos, WIN); 
     2547          READ_RETURN(OWL_DEFEND, owl->origin, depth - stackp, 
     2548                      move, mpos, WIN); 
    27722549        } 
    27732550        if (acode == GAIN) 
    27742551          saveworm = wid; 
    27752552        UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, mpos); 
    27762553      } 
    27772554      else { 
    2778         if (do_owl_attack(str, NULL, NULL, owl, new_escape) != WIN) { 
     2555        if (do_owl_attack(owl, NULL, NULL) != WIN) { 
    27792556          savemove = mpos; 
    27802557          savecode = KO_B; 
    27812558        } 
    27822559      } 
    27832560       
    27842561      /* Undo the tested move. */ 
    2785       pop_owl(&owl); 
    2786       popgo(); 
     2562      owl_popgo(&owl); 
    27872563    } 
    27882564  } 
    27892565 
     
    27942570      SGFTRACE(savemove, savecode, "defense effective (loss) - B"); 
    27952571      if (wormid) 
    27962572        *wormid = saveworm; 
    2797       READ_RETURN2(OWL_DEFEND, str, depth - stackp, 
     2573      READ_RETURN2(OWL_DEFEND, owl->origin, depth - stackp, 
    27982574                   move, savemove, savecode, saveworm); 
    27992575    } 
    28002576    else { 
    28012577      SGFTRACE(savemove, savecode, "defense effective (ko) - B"); 
    2802       READ_RETURN(OWL_DEFEND, str, depth - stackp, move, savemove, savecode); 
     2578      READ_RETURN(OWL_DEFEND, owl->origin, depth - stackp, 
     2579                  move, savemove, savecode); 
    28032580    } 
    28042581  } 
    28052582 
    28062583  if (number_tried_moves == 0 && min_eyes(&probable_eyes) >= 2) { 
    28072584    SGFTRACE(0, WIN, "genus probably >= 2"); 
    2808     READ_RETURN(OWL_DEFEND, str, depth - stackp, move, 0, WIN); 
     2585    READ_RETURN(OWL_DEFEND, owl->origin, depth - stackp, move, 0, WIN); 
    28092586  } 
    28102587   
    28112588 
     
    28172594    SGFTRACE(0, 0, winstr); 
    28182595  } 
    28192596 
    2820   READ_RETURN0(OWL_DEFEND, str, depth - stackp); 
     2597  READ_RETURN0(OWL_DEFEND, owl->origin, depth - stackp); 
    28212598} 
    28222599 
    28232600 
     2601/* Returns true if a move can be found to attack the dragon 
     2602 * at (target), in which case (*attack_point) is the recommended move. 
     2603 * (attack_point) can be a null pointer if only the result is needed. 
     2604 * 
     2605 * The array goal marks the extent of the dragon. This must 
     2606 * be maintained during reading. Call this function only when 
     2607 * stackp==0; otherwise you can call do_owl_attack but you must 
     2608 * set up the goal and boundary arrays by hand first. 
     2609 * 
     2610 * Returns KO_A or KO_B if the position is ko: 
     2611 * 
     2612 * - Returns KO_A if the attack prevails provided attacker is willing to 
     2613 *   ignore any ko threat (the attacker makes the first ko capture). 
     2614 * 
     2615 * - Returns KO_B if attack succeeds provided attacker has a ko threat 
     2616 *   which must be answered (the defender makes the first ko capture). 
     2617 * 
     2618 * If GNU Go is compiled with `configure --enable-experimental-owl-ext' 
     2619 * then a return codes of GAIN is also possible. 
     2620 * 
     2621 * - Returns GAIN if the attack fails but another worm of the 
     2622 *   opponent's is captured in during the failed attack. The location 
     2623 *   of the killed worm is returned through the *kworm field. 
     2624 * 
     2625 */ 
     2626 
     2627int 
     2628owl_attack(int target, int *attack_point, int *certain, int *kworm) 
     2629{ 
     2630  int result; 
     2631  struct local_owl_data *owl; 
     2632  int reading_nodes_when_called = get_reading_node_counter(); 
     2633  double start = 0.0; 
     2634  int tactical_nodes; 
     2635  int move = NO_MOVE; 
     2636  int wpos = NO_MOVE; 
     2637  int wid = MAX_GOAL_WORMS; 
     2638 
     2639  result_certain = 1; 
     2640  if (worm[target].unconditional_status == DEAD) { 
     2641    if (attack_point) 
     2642      *attack_point = NO_MOVE; 
     2643    if (kworm) 
     2644      *kworm = NO_MOVE; 
     2645    if (certain) 
     2646      *certain = 1; 
     2647    return 1; 
     2648  } 
     2649 
     2650  if (search_persistent_owl_cache(OWL_ATTACK, target, 0, 0, &result, 
     2651                                  attack_point, kworm, certain)) 
     2652    return result; 
     2653 
     2654  if (debug & DEBUG_OWL_PERFORMANCE) 
     2655    start = gg_cputime(); 
     2656   
     2657  TRACE("owl_attack %1m\n", target); 
     2658  init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); 
     2659  owl_make_domains(owl, NULL); 
     2660  prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
     2661                    kworm, 1); 
     2662  result = do_owl_attack(owl, &move, &wid); 
     2663  finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
     2664  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     2665 
     2666  DEBUG(DEBUG_OWL_PERFORMANCE, 
     2667        "owl_attack %1m, result %d %1m (%d, %d nodes, %f seconds)\n", 
     2668        target, result, move, local_owl_node_counter, 
     2669        tactical_nodes, gg_cputime() - start); 
     2670 
     2671  store_persistent_owl_cache(OWL_ATTACK, target, 0, 0, 
     2672                             result, move, wpos, 
     2673                             result_certain, tactical_nodes, 
     2674                             owl->goal, board[target]); 
     2675  if (attack_point) 
     2676    *attack_point = move; 
     2677  if (kworm) 
     2678    *kworm = wpos; 
     2679  if (certain) 
     2680    *certain = result_certain; 
     2681 
     2682  return result; 
     2683} 
     2684 
     2685 
     2686/* Returns true if a move can be found to defend the dragon 
     2687 * at (target), in which case (*defense_point) is the recommended move. 
     2688 * (defense_point) can be a null pointer if the result is not needed. 
     2689 * 
     2690 * The array goal marks the extent of the dragon. This must 
     2691 * be maintained during reading. Call this function only when 
     2692 * stackp==0; otherwise you can call do_owl_attack but you must 
     2693 * set up the goal and boundary arrays by hand first. 
     2694 * 
     2695 * Returns KO_A or KO_B if the position is ko: 
     2696 * 
     2697 * - Returns KO_A if the defendse succeeds provided the defender is willing to 
     2698 *   ignore any ko threat (the defender makes the first ko capture). 
     2699 * - Returns KO_B if the defense succeeds provided the defender has a ko threat 
     2700 *   which must be answered (the attacker makes the first ko capture). 
     2701 * 
     2702 * If GNU Go is compiled with `configure --enable-experimental-owl-ext' 
     2703 * then a return codes of GAIN is also possible. 
     2704 * 
     2705 * - Returns LOSS if the defense succeeds but another worm of the 
     2706 *   defender's is captured in during the defense. The location 
     2707 *   of the killed worm is returned through the *kworm field. 
     2708 * 
     2709 * The array goal marks the extent of the dragon. This must 
     2710 * be maintained during reading.   
     2711 */ 
     2712 
     2713int 
     2714owl_defend(int target, int *defense_point, int *certain, int *kworm) 
     2715{ 
     2716  int result; 
     2717  static struct local_owl_data *owl; 
     2718  int reading_nodes_when_called = get_reading_node_counter(); 
     2719  double start = 0.0; 
     2720  int tactical_nodes; 
     2721  int move = NO_MOVE; 
     2722  int wpos = NO_MOVE; 
     2723  int wid = MAX_GOAL_WORMS; 
     2724 
     2725  result_certain = 1; 
     2726  if (worm[target].unconditional_status == DEAD) 
     2727    return 0; 
     2728 
     2729  if (search_persistent_owl_cache(OWL_DEFEND, target, 0, 0, &result,  
     2730                                  defense_point, kworm, certain)) 
     2731    return result; 
     2732 
     2733  if (debug & DEBUG_OWL_PERFORMANCE) 
     2734    start = gg_cputime(); 
     2735 
     2736  TRACE("owl_defend %1m\n", target); 
     2737  init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); 
     2738  owl_make_domains(owl, NULL); 
     2739  prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
     2740                    kworm, 1); 
     2741  result = do_owl_defend(owl, &move, &wid); 
     2742  finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
     2743  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     2744 
     2745  DEBUG(DEBUG_OWL_PERFORMANCE, 
     2746        "owl_defend %1m, result %d %1m (%d, %d nodes, %f seconds)\n", 
     2747        target, result, move, local_owl_node_counter, 
     2748        tactical_nodes, gg_cputime() - start); 
     2749 
     2750  store_persistent_owl_cache(OWL_DEFEND, target, 0, 0, result, move, wpos, 
     2751                             result_certain, tactical_nodes, owl->goal, 
     2752                             board[target]); 
     2753 
     2754  if (defense_point) 
     2755    *defense_point = move; 
     2756  if (kworm) 
     2757    *kworm = wpos; 
     2758  if (certain) 
     2759    *certain = result_certain; 
     2760   
     2761  return result; 
     2762} 
     2763 
     2764 
     2765/* Returns true if the dragon at (target) can be captured given 
     2766 * two moves in a row. The first two moves to capture the 
     2767 * dragon are given as (*attack1) and (*attack2). 
     2768 */ 
     2769 
     2770int 
     2771owl_threaten_attack(int target, int *attack1, int *attack2) 
     2772{ 
     2773  struct owl_move_data moves[MAX_MOVES]; 
     2774  int k; 
     2775  int other = OTHER_COLOR(board[target]); 
     2776  struct local_owl_data *owl; 
     2777  int result = 0; 
     2778  int reading_nodes_when_called = get_reading_node_counter(); 
     2779  signed char saved_boundary[BOARDMAX]; 
     2780  double start = 0.0; 
     2781  int tactical_nodes; 
     2782  int move = 0; 
     2783  int move2 = 0; 
     2784  struct matched_patterns_list_data shape_patterns; 
     2785 
     2786  shape_patterns.initialized = 0; 
     2787  result_certain = 1; 
     2788  if (search_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, 
     2789                                  &result, attack1, attack2, NULL)) 
     2790    return result; 
     2791 
     2792  if (debug & DEBUG_OWL_PERFORMANCE) 
     2793    start = gg_cputime(); 
     2794   
     2795  gg_assert(stackp == 0); 
     2796  TRACE("owl_threaten_attack %1m\n", target); 
     2797  init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); 
     2798  memcpy(saved_boundary, owl->boundary, sizeof(saved_boundary)); 
     2799  owl_make_domains(owl, NULL); 
     2800  owl_shapes(&shape_patterns, moves, other, owl, &owl_attackpat_db); 
     2801  for (k = 0; k < MAX_MOVES; k++) { 
     2802    current_owl_data = owl; 
     2803    if (!get_next_move_from_list(&shape_patterns, other, moves, 1, owl)) 
     2804      break; 
     2805    else { 
     2806      int mpos = moves[k].pos; 
     2807 
     2808      if (mpos != NO_MOVE && moves[k].value > 0) 
     2809        if (trymove(mpos, other, moves[k].name, target)) { 
     2810          int pos; 
     2811          int origin = NO_MOVE; 
     2812          owl->lunches_are_current = 0; 
     2813          owl_update_boundary_marks(mpos, owl); 
     2814           
     2815          /* If the origin of the dragon has been captured, we look 
     2816           * for another string which was part of the original dragon, 
     2817           * marked when stackp==0, which has not been captured. If no 
     2818           * such string is found, owl_attack declares victory. 
     2819           */ 
     2820           
     2821          if (board[target] == EMPTY) { 
     2822            for (pos = BOARDMIN; pos < BOARDMAX; pos++) { 
     2823              if (IS_STONE(board[pos]) && owl->goal[pos] == 1) { 
     2824                origin = find_origin(pos); 
     2825                break; 
     2826              } 
     2827            } 
     2828            owl->origin = origin; 
     2829             
     2830            if (origin == NO_MOVE || do_owl_attack(owl, NULL, NULL)) { 
     2831              /* probably this can't happen */ 
     2832              popgo(); 
     2833              gg_assert(stackp == 0); 
     2834              result = 1; 
     2835              break; 
     2836            } 
     2837          } 
     2838          else if (do_owl_attack(owl, &move2, NULL) == WIN) { 
     2839            move = moves[k].pos; 
     2840            popgo(); 
     2841            gg_assert(stackp == 0); 
     2842            result = 1; 
     2843            break; 
     2844          } 
     2845          popgo(); 
     2846          memcpy(owl->boundary, saved_boundary, sizeof(saved_boundary)); 
     2847        } 
     2848    } 
     2849  } 
     2850  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     2851  gg_assert(stackp == 0); 
     2852 
     2853  DEBUG(DEBUG_OWL_PERFORMANCE, 
     2854    "owl_threaten_attack %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", 
     2855    target, move, move2, result, local_owl_node_counter, 
     2856    tactical_nodes, gg_cputime() - start); 
     2857 
     2858  store_persistent_owl_cache(OWL_THREATEN_ATTACK, target, 0, 0, 
     2859                             result, move, move2, 0, 
     2860                             tactical_nodes, owl->goal, board[target]); 
     2861 
     2862  if (attack1) 
     2863    *attack1 = move; 
     2864  if (attack2) 
     2865    *attack2 = move2; 
     2866 
     2867  close_pattern_list(other, &shape_patterns); 
     2868  return result; 
     2869} 
     2870 
     2871 
    28242872/* Returns true if the dragon at (target) can be defended given 
    28252873 * two moves in a row. The first two moves to defend the 
    28262874 * dragon are given as (*defend1) and (*defend2). 
     
    28702918          owl->lunches_are_current = 0; 
    28712919          owl_update_goal(moves[k].pos, moves[k].same_dragon, 
    28722920                          moves[k].lunch, owl, 0, moves[k].pattern_data); 
    2873           if (do_owl_defend(target, &move2, NULL, owl, 0) == WIN) { 
     2921          if (do_owl_defend(owl, &move2, NULL) == WIN) { 
    28742922            move = moves[k].pos; 
    28752923            popgo(); 
    28762924            /* Don't return the second move if occupied before trymove */ 
     
    29072955} 
    29082956 
    29092957 
     2958/* Use the owl code to determine whether the attack move at (move) of 
     2959 * the dragon (target) is effective, i.e. whether it kills the stones. 
     2960 * 
     2961 * Should be called only when stackp==0. 
     2962 */ 
    29102963 
     2964int 
     2965owl_does_attack(int move, int target, int *kworm) 
     2966{ 
     2967  int color = board[target]; 
     2968  int other = OTHER_COLOR(color); 
     2969  int result = 0; 
     2970  struct local_owl_data *owl; 
     2971  int reading_nodes_when_called = get_reading_node_counter(); 
     2972  int tactical_nodes; 
     2973  int origin; 
     2974  int dcode; 
     2975  int wpos = NO_MOVE; 
     2976  int wid = MAX_GOAL_WORMS; 
     2977  double start = 0.0; 
     2978 
     2979  if (debug & DEBUG_OWL_PERFORMANCE) 
     2980    start = gg_cputime(); 
     2981 
     2982  if (worm[target].unconditional_status == ALIVE) 
     2983    return 0; 
     2984 
     2985  origin = dragon[target].origin; 
     2986  TRACE("owl_does_attack %1m %1m(%1m)\n", move, target, origin); 
     2987 
     2988  if (search_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, 
     2989                                  &result, kworm, NULL, NULL)) 
     2990    return result; 
     2991 
     2992  /* FIXME: We want to do this after the trymove(), but currently 
     2993   * owl_mark_dragon() may crash if the trymove() happens to remove 
     2994   * some stones of the goal dragon from the board. 
     2995   */ 
     2996#if 1 
     2997  init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); 
     2998#endif 
     2999 
     3000  if (trymove(move, other, "owl_does_attack", target)) { 
     3001    /* Check if a compatible owl_defend() is cached. */ 
     3002    if (search_persistent_owl_cache(OWL_DEFEND, origin, 0, 0, 
     3003                                    &result, NULL, kworm, NULL)) { 
     3004      popgo(); 
     3005      return REVERSE_RESULT(result); 
     3006    } 
     3007 
     3008#if 0 
     3009    local_owl_node_counter = 0; 
     3010    owl->lunches_are_current = 0; 
     3011    owl_mark_dragon(target, NO_MOVE, owl); 
     3012#endif 
     3013    owl_update_boundary_marks(move, owl); 
     3014#if 0 
     3015    compute_owl_escape_values(owl); 
     3016#endif 
     3017    /* FIXME: Should also check if part of the dragon was captured, 
     3018     *        like do_owl_attack() does. 
     3019     */ 
     3020    if (board[target] == EMPTY) 
     3021      dcode = 0; 
     3022    else { 
     3023      prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
     3024                         kworm, 0); 
     3025      dcode = do_owl_defend(owl, NULL, &wid); 
     3026      finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
     3027    } 
     3028    result = REVERSE_RESULT(dcode); 
     3029    owl->lunches_are_current = 0; 
     3030    popgo(); 
     3031  } 
     3032  else 
     3033    return 0;  /* Don't cache anything in this case. */ 
     3034 
     3035  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     3036 
     3037  DEBUG(DEBUG_OWL_PERFORMANCE, 
     3038        "owl_does_attack %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", 
     3039        move, target, origin, result, local_owl_node_counter, 
     3040        tactical_nodes, gg_cputime() - start); 
     3041 
     3042  store_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, 
     3043                             result, wpos, 0, 0, 
     3044                             tactical_nodes, owl->goal, board[target]); 
     3045 
     3046  if (kworm) 
     3047    *kworm = wpos; 
     3048  return result; 
     3049} 
     3050 
     3051 
     3052/* Use the owl code to determine whether the move at (move) makes 
     3053 * the dragon at (target) owl safe. This is used to test whether 
     3054 * tactical defenses are strategically viable and whether a vital eye 
     3055 * point does kill an owl critical dragon.  
     3056 * 
     3057 * Should be called only when stackp==0. 
     3058 */ 
     3059 
     3060int 
     3061owl_does_defend(int move, int target, int *kworm) 
     3062{ 
     3063  int color = board[target]; 
     3064  int result = 0; 
     3065  struct local_owl_data *owl; 
     3066  int reading_nodes_when_called = get_reading_node_counter(); 
     3067  int tactical_nodes; 
     3068  int origin; 
     3069  int acode; 
     3070  int wpos = NO_MOVE; 
     3071  int wid = MAX_GOAL_WORMS; 
     3072  double start = 0.0; 
     3073 
     3074  if (debug & DEBUG_OWL_PERFORMANCE) 
     3075    start = gg_cputime(); 
     3076 
     3077  if (worm[target].unconditional_status == DEAD) 
     3078    return 0; 
     3079 
     3080  origin = dragon[target].origin; 
     3081  TRACE("owl_does_defend %1m %1m(%1m)\n", move, target, origin); 
     3082 
     3083  if (search_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, 
     3084                                  &result, kworm, NULL, NULL)) 
     3085    return result; 
     3086 
     3087  if (trymove(move, color, "owl_does_defend", target)) { 
     3088    /* Check if a compatible owl_attack() is cached. */ 
     3089    if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, 
     3090                                    &result, NULL, kworm, NULL)) { 
     3091      popgo(); 
     3092      return REVERSE_RESULT(result); 
     3093    } 
     3094     
     3095    /* 
     3096     * FIXME: (move) will be added to the goal dragon although we 
     3097     * do not know whether it is really connected. 
     3098     */ 
     3099    init_owl(&owl, target, NO_MOVE, move, 1, NULL); 
     3100    prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
     3101                      kworm, 0); 
     3102    acode = do_owl_attack(owl, NULL, &wid); 
     3103    finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
     3104    result = REVERSE_RESULT(acode); 
     3105    popgo(); 
     3106  } 
     3107  else 
     3108    return 0;  /* Don't cache anything in this case. */ 
     3109 
     3110  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     3111 
     3112  DEBUG(DEBUG_OWL_PERFORMANCE, 
     3113        "owl_does_defend %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", 
     3114        move, target, origin, result, local_owl_node_counter, 
     3115        tactical_nodes, gg_cputime() - start); 
     3116 
     3117  store_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, 
     3118                             result, wpos, 0, 0, 
     3119                             tactical_nodes, owl->goal, board[target]); 
     3120 
     3121  if (kworm) 
     3122    *kworm = wpos; 
     3123  return result; 
     3124} 
     3125 
     3126 
     3127/* Use the owl code to determine whether the dragon at (target) is owl 
     3128 * safe after an own move at (move). This is used to detect 
     3129 * blunders. In case the dragon is not safe, it also tries to find a 
     3130 * defense point making (target) safe in a later move. 
     3131 * 
     3132 * Should be called only when stackp==0. 
     3133 */ 
     3134 
     3135int 
     3136owl_confirm_safety(int move, int target, int *defense_point, int *kworm) 
     3137{ 
     3138  int color = board[target]; 
     3139  int result = 0; 
     3140  struct local_owl_data *owl; 
     3141  int reading_nodes_when_called = get_reading_node_counter(); 
     3142  int tactical_nodes; 
     3143  int origin; 
     3144  int defense = 0; 
     3145  double start = 0.0; 
     3146  int acode; 
     3147  int wpos = NO_MOVE; 
     3148  int wid = MAX_GOAL_WORMS; 
     3149 
     3150  if (debug & DEBUG_OWL_PERFORMANCE) 
     3151    start = gg_cputime(); 
     3152 
     3153  if (worm[target].unconditional_status == DEAD) 
     3154    return 0; 
     3155 
     3156  origin = dragon[target].origin; 
     3157  TRACE("owl_confirm_safety %1m %1m(%1m)\n", move, target, origin); 
     3158 
     3159  if (search_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, 
     3160                                  &result, defense_point, kworm, NULL)) 
     3161    return result; 
     3162 
     3163  if (trymove(move, color, "owl_confirm_safety", target)) { 
     3164    /* Check if a compatible owl_attack() is cached. */ 
     3165    if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, 
     3166                                    &result, defense_point, kworm, NULL)) { 
     3167      popgo(); 
     3168      if (result == 0) 
     3169        return WIN; 
     3170      else if (result == GAIN) 
     3171        return LOSS; 
     3172      else 
     3173        return 0; 
     3174    } 
     3175     
     3176    init_owl(&owl, target, NO_MOVE, move, 1, NULL); 
     3177    prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
     3178                      kworm, 0); 
     3179    acode = do_owl_attack(owl, &defense, &wid); 
     3180    finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
     3181    if (acode == 0) 
     3182      result = WIN; 
     3183    else if (acode == GAIN) 
     3184      result = LOSS; 
     3185    popgo(); 
     3186  } 
     3187  else 
     3188    return 0;  /* Don't cache anything in this case. */ 
     3189 
     3190  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     3191 
     3192  DEBUG(DEBUG_OWL_PERFORMANCE, 
     3193        "owl_confirm_safety %1m %1m(%1m), result %d %1m (%d, %d nodes, %f seconds)\n", 
     3194        move, target, origin, result, defense, 
     3195        local_owl_node_counter, tactical_nodes, 
     3196        gg_cputime() - start); 
     3197 
     3198  store_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, 
     3199                             result, defense, wpos, 0, 
     3200                             tactical_nodes, owl->goal, board[target]); 
     3201 
     3202  if (defense_point) 
     3203    *defense_point = defense; 
     3204  if (kworm) 
     3205    *kworm = wpos; 
     3206 
     3207  return result; 
     3208} 
     3209 
     3210 
     3211/* Use the owl code to determine whether connecting the two dragons 
     3212 * (target1) and (target2) by playing at (move) results in a living 
     3213 * dragon. Should be called only when stackp==0. 
     3214 */ 
     3215 
     3216int 
     3217owl_connection_defends(int move, int target1, int target2) 
     3218{ 
     3219  int color = board[target1]; 
     3220  int result = 0; 
     3221  int reading_nodes_when_called = get_reading_node_counter(); 
     3222  int tactical_nodes; 
     3223  double start = 0.0; 
     3224  struct local_owl_data *owl; 
     3225 
     3226  if (debug & DEBUG_OWL_PERFORMANCE) 
     3227    start = gg_cputime(); 
     3228 
     3229  ASSERT1(board[target2] == color, target2); 
     3230  TRACE("owl_connection_defends %1m %1m %1m\n", move, target1, target2); 
     3231 
     3232  if (worm[target1].unconditional_status == DEAD) 
     3233    return 0; 
     3234  if (worm[target2].unconditional_status == DEAD) 
     3235    return 0; 
     3236 
     3237  if (search_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, 
     3238                                  target2, &result, NULL, NULL, NULL)) 
     3239    return result; 
     3240 
     3241  init_owl(&owl, target1, target2, NO_MOVE, 1, NULL); 
     3242 
     3243  if (trymove(move, color, "owl_connection_defends", target1)) { 
     3244    owl_update_goal(move, SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, owl, 0, NULL); 
     3245    owl->origin = move; 
     3246    if (!do_owl_attack(owl, NULL, NULL)) 
     3247      result = WIN; 
     3248    owl->lunches_are_current = 0; 
     3249    popgo(); 
     3250  } 
     3251  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     3252   
     3253  DEBUG(DEBUG_OWL_PERFORMANCE, 
     3254        "owl_conn_defends %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", 
     3255        move, target1, target2, result, local_owl_node_counter, 
     3256        tactical_nodes, gg_cputime() - start); 
     3257 
     3258  store_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, target2, 
     3259                             result, 0, 0, 0, tactical_nodes, 
     3260                             owl->goal, color); 
     3261 
     3262  return result; 
     3263} 
     3264 
     3265 
     3266/* This function, called when stackp==0, returns true if capturing 
     3267 * the string at (str) results in a live group. 
     3268 */ 
     3269 
     3270#define MAX_SUBSTANTIAL_LIBS 10 
     3271 
     3272int 
     3273owl_substantial(int str) 
     3274{ 
     3275  int k; 
     3276  int libs[MAX_SUBSTANTIAL_LIBS + 1]; 
     3277  int liberties = findlib(str, MAX_SUBSTANTIAL_LIBS+1, libs); 
     3278  int reading_nodes_when_called = get_reading_node_counter(); 
     3279  int tactical_nodes; 
     3280  int result; 
     3281  double start = 0.0; 
     3282  struct local_owl_data *owl; 
     3283  int num_moves = 0; 
     3284 
     3285  if (debug & DEBUG_OWL_PERFORMANCE) 
     3286    start = gg_cputime(); 
     3287 
     3288  /* FIXME: We want to use the full init_owl here too (cf. similar 
     3289   * remark below). 
     3290   */ 
     3291  reduced_init_owl(&owl, 1); 
     3292 
     3293  owl->color = OTHER_COLOR(board[str]); 
     3294 
     3295  /* Big strings are always substantial since the biggest nakade is 
     3296   * six stones. (There are probably rare exceptions to this 
     3297   * rule, but they are unlikely to come up in a game.) 
     3298   */ 
     3299  if (countstones(str) > 6) 
     3300    return 1; 
     3301   
     3302  if (liberties > MAX_SUBSTANTIAL_LIBS) 
     3303    return 0; 
     3304 
     3305  memset(owl->goal, 0, sizeof(owl->goal)); 
     3306  /* Mark the neighbors of the string. If one is found which is alive, return 
     3307   * true. */ 
     3308  { 
     3309    int adjs[MAXCHAIN]; 
     3310    int adj; 
     3311 
     3312    adj = chainlinks(str, adjs); 
     3313    for (k = 0; k < adj; k++) { 
     3314      if (dragon[adjs[k]].status == ALIVE) 
     3315        return 1; 
     3316      mark_dragon(adjs[k], owl->goal, 1); 
     3317    } 
     3318  } 
     3319 
     3320  /* We must check the cache while stackp == 0, but we wait until the 
     3321   * trivial tests have been done. 
     3322   */ 
     3323  if (search_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, 
     3324                                  &result, NULL, NULL, NULL)) 
     3325    return result; 
     3326 
     3327  /* fill all the liberties */ 
     3328  for (k = 0; k < liberties; k++) { 
     3329    if (trymove(libs[k], owl->color, NULL, 0)) { 
     3330      if (get_level() >= 8) 
     3331        increase_depth_values(); 
     3332      owl->goal[libs[k]] = 1; 
     3333      num_moves++; 
     3334    } 
     3335    else { 
     3336      /* if we can't fill, try swapping with the next liberty */ 
     3337      if (k < liberties-1 
     3338          && trymove(libs[k+1], owl->color, NULL, 0)) { 
     3339        if (get_level() >= 8) 
     3340          increase_depth_values(); 
     3341        owl->goal[libs[k+1]] = 1; 
     3342        libs[k+1] = libs[k]; 
     3343        num_moves++; 
     3344      } 
     3345      else { 
     3346        /* Can't fill the liberties. Give up! */ 
     3347        while (num_moves-- > 0) { 
     3348          if (get_level() >= 8) 
     3349            decrease_depth_values(); 
     3350          popgo(); 
     3351        } 
     3352        return 0; 
     3353      } 
     3354    } 
     3355  } 
     3356  /* FIXME: We would want to use init_owl() here too, but it doesn't 
     3357   * fit very well with the construction of the goal array above. 
     3358   */ 
     3359  memcpy(owl->cumulative_goal, owl->goal, BOARDMAX); 
     3360  compute_owl_escape_values(owl); 
     3361  owl_mark_boundary(owl); 
     3362  owl->lunches_are_current = 0; 
     3363  owl->escape_moves = 0; 
     3364  owl->origin = libs[0]; 
     3365 
     3366  if (do_owl_attack(owl, NULL, NULL)) 
     3367    result = 0; 
     3368  else 
     3369    result = 1; 
     3370  while (num_moves-- > 0) { 
     3371    if (get_level() >= 8) 
     3372      decrease_depth_values(); 
     3373    popgo(); 
     3374  } 
     3375 
     3376  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
     3377  DEBUG(DEBUG_OWL_PERFORMANCE, 
     3378        "owl_substantial %1m, result %d (%d, %d nodes, %f seconds)\n", 
     3379        str, result, local_owl_node_counter, 
     3380        tactical_nodes, gg_cputime() - start); 
     3381 
     3382  store_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, result, 0, 0, 0, 
     3383                             tactical_nodes, owl->goal, owl->color); 
     3384 
     3385  return result; 
     3386} 
     3387 
     3388 
     3389 
    29113390/* 
    29123391 * This function calls owl_determine_life() to get an eye estimate, 
    29133392 * and matchpat() for vital attack moves, and decides according to 
     
    53195798  } 
    53205799} 
    53215800 
    5322 /* Use the owl code to determine whether the move at (move) makes 
    5323  * the dragon at (target) owl safe. This is used to test whether 
    5324  * tactical defenses are strategically viable and whether a vital eye 
    5325  * point does kill an owl critical dragon.  
    5326  * 
    5327  * Should be called only when stackp==0. 
    5328  */ 
    5329  
    5330 int 
    5331 owl_does_defend(int move, int target, int *kworm) 
    5332 { 
    5333   int color = board[target]; 
    5334   int result = 0; 
    5335   struct local_owl_data *owl; 
    5336   int reading_nodes_when_called = get_reading_node_counter(); 
    5337   int tactical_nodes; 
    5338   int origin; 
    5339   int acode; 
    5340   int wpos = NO_MOVE; 
    5341   int wid = MAX_GOAL_WORMS; 
    5342   double start = 0.0; 
    5343  
    5344   if (debug & DEBUG_OWL_PERFORMANCE) 
    5345     start = gg_cputime(); 
    5346  
    5347   if (worm[target].unconditional_status == DEAD) 
    5348     return 0; 
    5349  
    5350   origin = dragon[target].origin; 
    5351   TRACE("owl_does_defend %1m %1m(%1m)\n", move, target, origin); 
    5352  
    5353   if (search_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, 
    5354                                   &result, kworm, NULL, NULL)) 
    5355     return result; 
    5356  
    5357   if (trymove(move, color, "owl_does_defend", target)) { 
    5358     /* Check if a compatible owl_attack() is cached. */ 
    5359     if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, 
    5360                                     &result, NULL, kworm, NULL)) { 
    5361       popgo(); 
    5362       return REVERSE_RESULT(result); 
    5363     } 
    5364      
    5365     /* 
    5366      * FIXME: (move) will be added to the goal dragon although we 
    5367      * do not know whether it is really connected. 
    5368      */ 
    5369     init_owl(&owl, target, NO_MOVE, move, 1, NULL); 
    5370     prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
    5371                       kworm, 0); 
    5372     acode = do_owl_attack(target, NULL, &wid, owl, 0); 
    5373     finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
    5374     result = REVERSE_RESULT(acode); 
    5375     popgo(); 
    5376   } 
    5377   else 
    5378     return 0;  /* Don't cache anything in this case. */ 
    5379  
    5380   tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
    5381  
    5382   DEBUG(DEBUG_OWL_PERFORMANCE, 
    5383         "owl_does_defend %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", 
    5384         move, target, origin, result, local_owl_node_counter, 
    5385         tactical_nodes, gg_cputime() - start); 
    5386  
    5387   store_persistent_owl_cache(OWL_DOES_DEFEND, move, target, 0, 
    5388                              result, wpos, 0, 0, 
    5389                              tactical_nodes, owl->goal, board[target]); 
    5390  
    5391   if (kworm) 
    5392     *kworm = wpos; 
    5393   return result; 
    5394 } 
    5395  
    5396  
    5397 /* Use the owl code to determine whether the dragon at (target) is owl 
    5398  * safe after an own move at (move). This is used to detect 
    5399  * blunders. In case the dragon is not safe, it also tries to find a 
    5400  * defense point making (target) safe in a later move. 
    5401  * 
    5402  * Should be called only when stackp==0. 
    5403  */ 
    5404  
    5405 int 
    5406 owl_confirm_safety(int move, int target, int *defense_point, int *kworm) 
    5407 { 
    5408   int color = board[target]; 
    5409   int result = 0; 
    5410   struct local_owl_data *owl; 
    5411   int reading_nodes_when_called = get_reading_node_counter(); 
    5412   int tactical_nodes; 
    5413   int origin; 
    5414   int defense = 0; 
    5415   double start = 0.0; 
    5416   int acode; 
    5417   int wpos = NO_MOVE; 
    5418   int wid = MAX_GOAL_WORMS; 
    5419  
    5420   if (debug & DEBUG_OWL_PERFORMANCE) 
    5421     start = gg_cputime(); 
    5422  
    5423   if (worm[target].unconditional_status == DEAD) 
    5424     return 0; 
    5425  
    5426   origin = dragon[target].origin; 
    5427   TRACE("owl_confirm_safety %1m %1m(%1m)\n", move, target, origin); 
    5428  
    5429   if (search_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, 
    5430                                   &result, defense_point, kworm, NULL)) 
    5431     return result; 
    5432  
    5433   if (trymove(move, color, "owl_confirm_safety", target)) { 
    5434     /* Check if a compatible owl_attack() is cached. */ 
    5435     if (search_persistent_owl_cache(OWL_ATTACK, origin, 0, 0, 
    5436                                     &result, defense_point, kworm, NULL)) { 
    5437       popgo(); 
    5438       if (result == 0) 
    5439         return WIN; 
    5440       else if (result == GAIN) 
    5441         return LOSS; 
    5442       else 
    5443         return 0; 
    5444     } 
    5445      
    5446     init_owl(&owl, target, NO_MOVE, move, 1, NULL); 
    5447     prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
    5448                       kworm, 0); 
    5449     acode = do_owl_attack(target, &defense, &wid, owl, 0); 
    5450     finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
    5451     if (acode == 0) 
    5452       result = WIN; 
    5453     else if (acode == GAIN) 
    5454       result = LOSS; 
    5455     popgo(); 
    5456   } 
    5457   else 
    5458     return 0;  /* Don't cache anything in this case. */ 
    5459  
    5460   tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
    5461  
    5462   DEBUG(DEBUG_OWL_PERFORMANCE, 
    5463         "owl_confirm_safety %1m %1m(%1m), result %d %1m (%d, %d nodes, %f seconds)\n", 
    5464         move, target, origin, result, defense, 
    5465         local_owl_node_counter, tactical_nodes, 
    5466         gg_cputime() - start); 
    5467  
    5468   store_persistent_owl_cache(OWL_CONFIRM_SAFETY, move, target, 0, 
    5469                              result, defense, wpos, 0, 
    5470                              tactical_nodes, owl->goal, board[target]); 
    5471  
    5472   if (defense_point) 
    5473     *defense_point = defense; 
    5474   if (kworm) 
    5475     *kworm = wpos; 
    5476  
    5477   return result; 
    5478 } 
    5479  
    5480  
    5481 /* Use the owl code to determine whether the attack move at (move) of 
    5482  * the dragon (target) is effective, i.e. whether it kills the stones. 
    5483  * 
    5484  * Should be called only when stackp==0. 
    5485  */ 
    5486  
    5487 int 
    5488 owl_does_attack(int move, int target, int *kworm) 
    5489 { 
    5490   int color = board[target]; 
    5491   int other = OTHER_COLOR(color); 
    5492   int result = 0; 
    5493   struct local_owl_data *owl; 
    5494   int reading_nodes_when_called = get_reading_node_counter(); 
    5495   int tactical_nodes; 
    5496   int origin; 
    5497   int dcode; 
    5498   int wpos = NO_MOVE; 
    5499   int wid = MAX_GOAL_WORMS; 
    5500   double start = 0.0; 
    5501  
    5502   if (debug & DEBUG_OWL_PERFORMANCE) 
    5503     start = gg_cputime(); 
    5504  
    5505   if (worm[target].unconditional_status == ALIVE) 
    5506     return 0; 
    5507  
    5508   origin = dragon[target].origin; 
    5509   TRACE("owl_does_attack %1m %1m(%1m)\n", move, target, origin); 
    5510  
    5511   if (search_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, 
    5512                                   &result, kworm, NULL, NULL)) 
    5513     return result; 
    5514  
    5515   /* FIXME: We want to do this after the trymove(), but currently 
    5516    * owl_mark_dragon() may crash if the trymove() happens to remove 
    5517    * some stones of the goal dragon from the board. 
    5518    */ 
    5519 #if 1 
    5520   init_owl(&owl, target, NO_MOVE, NO_MOVE, 1, NULL); 
    5521 #endif 
    5522  
    5523   if (trymove(move, other, "owl_does_attack", target)) { 
    5524     /* Check if a compatible owl_defend() is cached. */ 
    5525     if (search_persistent_owl_cache(OWL_DEFEND, origin, 0, 0, 
    5526                                     &result, NULL, kworm, NULL)) { 
    5527       popgo(); 
    5528       return REVERSE_RESULT(result); 
    5529     } 
    5530  
    5531 #if 0 
    5532     local_owl_node_counter = 0; 
    5533     owl->lunches_are_current = 0; 
    5534     owl_mark_dragon(target, NO_MOVE, owl); 
    5535 #endif 
    5536     owl_update_boundary_marks(move, owl); 
    5537 #if 0 
    5538     compute_owl_escape_values(owl); 
    5539 #endif 
    5540     /* FIXME: Should also check if part of the dragon was captured, 
    5541      *        like do_owl_attack() does. 
    5542      */ 
    5543     if (board[target] == EMPTY) 
    5544       dcode = 0; 
    5545     else { 
    5546       prepare_goal_list(target, owl, owl_goal_worm, &goal_worms_computed, 
    5547                          kworm, 0); 
    5548       dcode = do_owl_defend(target, NULL, &wid, owl, 0); 
    5549       finish_goal_list(&goal_worms_computed, &wpos, owl_goal_worm, wid); 
    5550     } 
    5551     result = REVERSE_RESULT(dcode); 
    5552     owl->lunches_are_current = 0; 
    5553     popgo(); 
    5554   } 
    5555   else 
    5556     return 0;  /* Don't cache anything in this case. */ 
    5557  
    5558   tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
    5559  
    5560   DEBUG(DEBUG_OWL_PERFORMANCE, 
    5561         "owl_does_attack %1m %1m(%1m), result %d (%d, %d nodes, %f seconds)\n", 
    5562         move, target, origin, result, local_owl_node_counter, 
    5563         tactical_nodes, gg_cputime() - start); 
    5564  
    5565   store_persistent_owl_cache(OWL_DOES_ATTACK, move, target, 0, 
    5566                              result, wpos, 0, 0, 
    5567                              tactical_nodes, owl->goal, board[target]); 
    5568  
    5569   if (kworm) 
    5570     *kworm = wpos; 
    5571   return result; 
    5572 } 
    5573  
    5574  
    5575 /* Use the owl code to determine whether connecting the two dragons 
    5576  * (target1) and (target2) by playing at (move) results in a living 
    5577  * dragon. Should be called only when stackp==0. 
    5578  */ 
    5579  
    5580 int 
    5581 owl_connection_defends(int move, int target1, int target2) 
    5582 { 
    5583   int color = board[target1]; 
    5584   int result = 0; 
    5585   int reading_nodes_when_called = get_reading_node_counter(); 
    5586   int tactical_nodes; 
    5587   double start = 0.0; 
    5588   struct local_owl_data *owl; 
    5589  
    5590   if (debug & DEBUG_OWL_PERFORMANCE) 
    5591     start = gg_cputime(); 
    5592  
    5593   ASSERT1(board[target2] == color, target2); 
    5594   TRACE("owl_connection_defends %1m %1m %1m\n", move, target1, target2); 
    5595  
    5596   if (worm[target1].unconditional_status == DEAD) 
    5597     return 0; 
    5598   if (worm[target2].unconditional_status == DEAD) 
    5599     return 0; 
    5600  
    5601   if (search_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, 
    5602                                   target2, &result, NULL, NULL, NULL)) 
    5603     return result; 
    5604  
    5605   init_owl(&owl, target1, target2, NO_MOVE, 1, NULL); 
    5606  
    5607   if (trymove(move, color, "owl_connection_defends", target1)) { 
    5608     owl_update_goal(move, SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, owl, 0, NULL); 
    5609     if (!do_owl_attack(move, NULL, NULL, owl, 0)) 
    5610       result = WIN; 
    5611     owl->lunches_are_current = 0; 
    5612     popgo(); 
    5613   } 
    5614   tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
    5615    
    5616   DEBUG(DEBUG_OWL_PERFORMANCE, 
    5617         "owl_conn_defends %1m %1m %1m, result %d (%d, %d nodes, %f seconds)\n", 
    5618         move, target1, target2, result, local_owl_node_counter, 
    5619         tactical_nodes, gg_cputime() - start); 
    5620  
    5621   store_persistent_owl_cache(OWL_CONNECTION_DEFENDS, move, target1, target2, 
    5622                              result, 0, 0, 0, tactical_nodes, 
    5623                              owl->goal, color); 
    5624  
    5625   return result; 
    5626 } 
    5627  
    5628  
    56295801/* This function attempts to make a list of dead strings 
    56305802 * which may be relevant to the life of the goal dragon. 
    56315803 * Such strings are called owl lunches. They are ignored 
     
    61046276} 
    61056277   
    61066278 
    6107 /* This function, called when stackp==0, returns true if capturing 
    6108  * the string at (str) results in a live group. 
    6109  */ 
    6110  
    6111 #define MAX_SUBSTANTIAL_LIBS 10 
    6112  
    6113 int 
    6114 owl_substantial(int str) 
    6115 { 
    6116   int k; 
    6117   int libs[MAX_SUBSTANTIAL_LIBS + 1]; 
    6118   int liberties = findlib(str, MAX_SUBSTANTIAL_LIBS+1, libs); 
    6119   int reading_nodes_when_called = get_reading_node_counter(); 
    6120   int tactical_nodes; 
    6121   int result; 
    6122   double start = 0.0; 
    6123   struct local_owl_data *owl; 
    6124   int num_moves = 0; 
    6125  
    6126   if (debug & DEBUG_OWL_PERFORMANCE) 
    6127     start = gg_cputime(); 
    6128  
    6129   /* FIXME: We want to use the full init_owl here too (cf. similar 
    6130    * remark below). 
    6131    */ 
    6132   reduced_init_owl(&owl, 1); 
    6133  
    6134   owl->color = OTHER_COLOR(board[str]); 
    6135   local_owl_node_counter = 0; 
    6136  
    6137   /* Big strings are always substantial since the biggest nakade is 
    6138    * six stones. (There are probably rare exceptions to this 
    6139    * rule, but they are unlikely to come up in a game.) 
    6140    */ 
    6141   if (countstones(str) > 6) 
    6142     return 1; 
    6143    
    6144   if (liberties > MAX_SUBSTANTIAL_LIBS) 
    6145     return 0; 
    6146  
    6147   memset(owl->goal, 0, sizeof(owl->goal)); 
    6148   /* Mark the neighbors of the string. If one is found which is alive, return 
    6149    * true. */ 
    6150   { 
    6151     int adjs[MAXCHAIN]; 
    6152     int adj; 
    6153  
    6154     adj = chainlinks(str, adjs); 
    6155     for (k = 0; k < adj; k++) { 
    6156       if (dragon[adjs[k]].status == ALIVE) 
    6157         return 1; 
    6158       mark_dragon(adjs[k], owl->goal, 1); 
    6159     } 
    6160   } 
    6161  
    6162   /* We must check the cache while stackp == 0, but we wait until the 
    6163    * trivial tests have been done. 
    6164    */ 
    6165   if (search_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, 
    6166                                   &result, NULL, NULL, NULL)) 
    6167     return result; 
    6168  
    6169   /* fill all the liberties */ 
    6170   for (k = 0; k < liberties; k++) { 
    6171     if (trymove(libs[k], owl->color, NULL, 0)) { 
    6172       if (get_level() >= 8) 
    6173         increase_depth_values(); 
    6174       owl->goal[libs[k]] = 1; 
    6175       num_moves++; 
    6176     } 
    6177     else { 
    6178       /* if we can't fill, try swapping with the next liberty */ 
    6179       if (k < liberties-1 
    6180           && trymove(libs[k+1], owl->color, NULL, 0)) { 
    6181         if (get_level() >= 8) 
    6182           increase_depth_values(); 
    6183         owl->goal[libs[k+1]] = 1; 
    6184         libs[k+1] = libs[k]; 
    6185         num_moves++; 
    6186       } 
    6187       else { 
    6188         /* Can't fill the liberties. Give up! */ 
    6189         while (num_moves-- > 0) { 
    6190           if (get_level() >= 8) 
    6191             decrease_depth_values(); 
    6192           popgo(); 
    6193         } 
    6194         return 0; 
    6195       } 
    6196     } 
    6197   } 
    6198   /* FIXME: We would want to use init_owl() here too, but it doesn't 
    6199    * fit very well with the construction of the goal array above. 
    6200    */ 
    6201   memcpy(owl->cumulative_goal, owl->goal, BOARDMAX); 
    6202   compute_owl_escape_values(owl); 
    6203   owl_mark_boundary(owl); 
    6204   owl->lunches_are_current = 0; 
    6205  
    6206   if (do_owl_attack(libs[0], NULL, NULL, owl, 0)) 
    6207     result = 0; 
    6208   else 
    6209     result = 1; 
    6210   while (num_moves-- > 0) { 
    6211     if (get_level() >= 8) 
    6212       decrease_depth_values(); 
    6213     popgo(); 
    6214   } 
    6215  
    6216   tactical_nodes = get_reading_node_counter() - reading_nodes_when_called; 
    6217   DEBUG(DEBUG_OWL_PERFORMANCE, 
    6218         "owl_substantial %1m, result %d (%d, %d nodes, %f seconds)\n", 
    6219         str, result, local_owl_node_counter, 
    6220         tactical_nodes, gg_cputime() - start); 
    6221  
    6222   store_persistent_owl_cache(OWL_SUBSTANTIAL, str, 0, 0, result, 0, 0, 0, 
    6223                              tactical_nodes, owl->goal, owl->color); 
    6224  
    6225   return result; 
    6226 } 
    6227  
    6228  
    6229  
    62306279/* Returns true if and only if (i, j) is a 1-2 vertex, i.e. next to a 
    62316280 * corner. 
    62326281 */ 
     
    64456494 * goal. Used in do_owl_attack() 
    64466495 */ 
    64476496static int 
    6448 select_new_goal_origin(int origin, struct local_owl_data *owl) 
     6497select_new_goal_origin(struct local_owl_data *owl) 
    64496498{ 
    64506499  int pos; 
     6500  int origin = NO_MOVE; 
     6501  if (IS_STONE(board[owl->origin])) 
     6502    origin = owl->origin; 
    64516503  for (pos = BOARDMIN; pos < BOARDMAX; pos++) 
    64526504    if (board[pos] == owl->color && owl->goal[pos] == 1) 
    64536505      return find_origin(pos); 
     
    67486800static void 
    67496801reduced_init_owl(struct local_owl_data **owl, int at_bottom_of_stack) 
    67506802{ 
     6803  local_owl_node_counter = 0; 
     6804 
    67516805  if (at_bottom_of_stack) 
    67526806    owl_stack_pointer = 0; 
    67536807  else 
     
    67696823{ 
    67706824  reduced_init_owl(owl, at_bottom_of_stack); 
    67716825 
    6772   local_owl_node_counter = 0; 
     6826  (*owl)->origin = target1; 
    67736827  (*owl)->lunches_are_current = 0; 
     6828  (*owl)->escape_moves = 0; 
    67746829  owl_mark_dragon(target1, target2, *owl, new_dragons); 
    67756830  if (move != NO_MOVE) 
    67766831    owl_update_goal(move, SAME_DRAGON_MAYBE_CONNECTED, NO_MOVE, *owl, 0, NULL); 
     
    68126867  memcpy(new_owl->escape_values, (*owl)->escape_values, 
    68136868         sizeof(new_owl->escape_values)); 
    68146869  new_owl->color = (*owl)->color; 
     6870  new_owl->origin = (*owl)->origin; 
    68156871 
    68166872  new_owl->lunches_are_current = 0; 
     6873  new_owl->escape_moves = (*owl)->escape_moves; 
    68176874 
    68186875  /* Needed for stack organization. Since there may be one or two sets 
    68196876   * of owl data active at we don't know whether to restore from the