Ticket #156: gunnar_7_11.4.diff

File gunnar_7_11.4.diff, 65.0 kB (added by gunnar, 20 months 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)