/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see       *
 * http://www.gnu.org/software/gnugo/ for more information.          *
 *                                                                   *
 * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,   *
 * 2008 and 2009 by the Free Software Foundation.                    *
 *                                                                   *
 * This program is free software; you can redistribute it and/or     *
 * modify it under the terms of the GNU General Public License as    *
 * published by the Free Software Foundation - version 3 or          *
 * (at your option) any later version.                               *
 *                                                                   *
 * This program is distributed in the hope that it will be useful,   *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of    *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     *
 * GNU General Public License in file COPYING for more details.      *
 *                                                                   *
 * You should have received a copy of the GNU General Public         *
 * License along with this program; if not, write to the Free        *
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,       *
 * Boston, MA 02111, USA.                                            *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#include "gnugo.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "liberty.h"
#include "patterns.h"

static void compute_effective_worm_sizes(void);
static void do_compute_effective_worm_sizes(int color,
					    int (*cw)[MAX_CLOSE_WORMS],
					    int *ncw, int max_distance);
static void compute_unconditional_status(void);
static void find_worm_attacks_and_defenses(void);
static void find_worm_threats(void);
static int find_lunch(int str, int *lunch);
static void change_tactical_point(int str, int move, int code,
				  int points[MAX_TACTICAL_POINTS],
				  int codes[MAX_TACTICAL_POINTS]);
static void propagate_worm2(int str);
static int genus(int str);
static void markcomponent(int str, int pos, int mg[BOARDMAX]);
static int examine_cavity(int pos, int *edge);
static void cavity_recurse(int pos, int mx[BOARDMAX], 
			   int *border_color, int *edge, int str);
static void ping_cave(int str, int *result1,  int *result2,
		      int *result3, int *result4);
static void ping_recurse(int pos, int *counter, 
			 int mx[BOARDMAX], 
			 int mr[BOARDMAX], int color);
static int touching(int pos, int color);
static void find_attack_patterns(void);
static void attack_callback(int anchor, int color,
			    struct pattern *pattern, int ll, void *data);
static void find_defense_patterns(void);
static void defense_callback(int anchor, int color,
			     struct pattern *pattern, int ll, void *data);
static void build_worms(void);
static void report_worm(int pos);

/* A worm or string is a maximal connected set of stones of the same color, 
 * black or white.
 *
 * Cavities are sets of connected empty vertices.
 */


/* make_worms() finds all worms and assembles some data about them.
 *
 * Each worm is marked with an origin.  This is an arbitrarily chosen
 * element of the worm, in practice the algorithm puts the origin at
 * the first element when they are given the lexicographical order,
 * though its location is irrelevant for applications. To see if two
 * stones lie in the same worm, compare their origins.
 *
 * We will use the field dragon[ii].genus to keep track of
 * black- or white-bordered cavities (essentially eyes) which are found.  
 * so this field must be zero'd now.
 */

void
make_worms(void)
{
  int pos;

  /* Build the basic worm data:  color, origin, size, liberties. */
  build_worms();

  /* No point continuing if the board is completely empty. */
  if (stones_on_board(BLACK | WHITE) == 0)
    return;

  /* Compute effective sizes of all worms. */
  compute_effective_worm_sizes();

  /* Look for unconditionally alive and dead worms, and unconditional
   * territory.
   */
  compute_unconditional_status();
  
  find_worm_attacks_and_defenses();
  
  gg_assert(stackp == 0);

  /* Count liberties of different orders and initialize cutstone fields. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos]) && is_worm_origin(pos, pos)) {
      int lib1, lib2, lib3, lib4;
      
      ping_cave(pos, &lib1, &lib2, &lib3, &lib4);
      ASSERT1(worm[pos].liberties == lib1, pos);
      worm[pos].liberties2 = lib2;
      worm[pos].liberties3 = lib3;
      worm[pos].liberties4 = lib4;
      worm[pos].cutstone = 0;
      worm[pos].cutstone2 = 0;
      propagate_worm(pos);
    }
  }
  
  gg_assert(stackp == 0);

  /*
   * There are two concepts of cutting stones in the worm array.
   *
   * worm.cutstone:
   *
   *     A CUTTING STONE is one adjacent to two enemy strings,
   *     which do not have a liberty in common. The most common
   *     type of cutting string is in this situation.
   *  
   *     XO
   *     OX
   *     
   *     A POTENTIAL CUTTING STONE is adjacent to two enemy
   *     strings which do share a liberty. For example, X in:
   *     
   *     XO
   *     O.
   *     
   *     For cutting strings we set worm[m][n].cutstone=2. For potential
   *     cutting strings we set worm[m][n].cutstone=1. For other strings,
   *     worm[m][n].cutstone=0.
   *
   * worm.cutstone2:
   *
   *     Cutting points are identified by the patterns in the
   *     connections database. Proper cuts are handled by the fact
   *     that attacking and defending moves also count as moves
   *     cutting or connecting the surrounding dragons. 
   *
   * The cutstone field will now be set. The cutstone2 field is set
   * later, during find_cuts(), called from make_dragons().
   *
   * We maintain both fields because the historically older cutstone
   * field is needed to deal with the fact that e.g. in the position
   *
   *
   *    OXX.O
   *    .OOXO
   *    OXX.O
   *
   * the X stones are amalgamated into one dragon because neither cut
   * works as long as the two O stones are in atari. Therefore we add
   * one to the cutstone field for each potential cutting point,
   * indicating that these O stones are indeed worth rescuing.
   *
   * For the time being we use both concepts in parallel. It's
   * possible we also need the old concept for correct handling of lunches.
   */

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    int w1 = NO_MOVE;
    int w2 = NO_MOVE;
    int k;
    int pos2;
    
    /* Only work on each worm once. This is easiest done if we only 
     * work with the origin of each worm.
     */
    if (!IS_STONE(board[pos]) || !is_worm_origin(pos, pos))
      continue;
    
    /* Try to find two adjacent worms (w1) and (w2) 
     * of opposite colour from (pos).
     */
    for (pos2 = BOARDMIN; pos2 < BOARDMAX; pos2++) {
      /* Work only with the opposite color from (pos). */
      if (board[pos2] != OTHER_COLOR(board[pos])) 
	continue;
      
      for (k = 0; k < 4; k++) {
	if (!ON_BOARD(pos2 + delta[k])
	    || worm[pos2 + delta[k]].origin != pos)
	  continue;
	
	ASSERT1(board[pos2 + delta[k]] == board[pos], pos);
	
	/* If we have not already found a worm which meets the criteria,
	 * store it into (w1), otherwise store it into (w2).
	 */
	if (w1 == NO_MOVE)
	  w1 = worm[pos2].origin;
	else if (!is_same_worm(pos2, w1))
	  w2 = worm[pos2].origin;
      }
    }
    
    /* 
     *  We now verify the definition of cutting stones. We have
     *  verified that the string at (pos) is adjacent to two enemy
     *  strings at (w1) and (w2). We need to know if these
     *  strings share a liberty.
     */
    
    /* Only do this if we really found something. */
    if (w2 != NO_MOVE) {
      worm[pos].cutstone = 2;
      if (count_common_libs(w1, w2) > 0)
	worm[pos].cutstone = 1;
      
      DEBUG(DEBUG_WORMS, "Worm at %1m has w1 %1m and w2 %1m, cutstone %d\n",
	    pos, w1, w2, worm[pos].cutstone);
    }
  }
  
  gg_assert(stackp == 0);
  
  /* Set the genus of all worms. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos]) && is_worm_origin(pos, pos)) {
      worm[pos].genus = genus(pos);
      propagate_worm(pos);
    }
  }
  gg_assert(stackp == 0);

  /* Now we try to improve the values of worm.attack and worm.defend.
   * If we find that capturing the string at str also defends the
   * string at str2, or attacks it, then we add points of attack and
   * defense. We don't add attacking point for strings that can't be
   * defended.
   */
  {
    int color;
    int str;
    int moves_to_try[BOARDMAX];
    memset(moves_to_try, 0, sizeof(moves_to_try));

    /* Find which colors to try at what points. */
    for (str = BOARDMIN; str < BOARDMAX; str++) {
      if (IS_STONE(board[str]) && is_worm_origin(str, str)) {
	color = board[str];
	moves_to_try[worm[str].defense_points[0]] |= color;
	moves_to_try[worm[str].attack_points[0]] |= OTHER_COLOR(color);
      }
    }

    /* Loop over the board and over the colors and try the moves found
     * in the previous loop.
     */
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos))
	continue;

      for (color = WHITE; color <= BLACK; color++) {
	if (!(moves_to_try[pos] & color))
	  continue;
	
	/* Try to play color at pos and see what it leads to. */
	if (!trymove(pos, color, "make_worms", NO_MOVE))
	  continue;
	  
	/* We must read to the same depth that was used in the
	 * initial determination of worm.attack and worm.defend
	 * to avoid horizon effects. Since stackp has been
	 * incremented we must also increment depth values.
	 */
	
	DEBUG(DEBUG_WORMS, "trying %1m\n", pos);
	increase_depth_values();
	
	/* Now we try to find a group which is saved or attacked as well
	 * by this move.
	 */
	for (str = BOARDMIN; str < BOARDMAX; str++) {
	  if (!IS_STONE(board[str])
	      || !is_worm_origin(str, str))
	    continue;
	  
	  /* If the worm is of the opposite color to the move,
	   * then we try to defend it. If there was a previous 
	   * attack and defense of it, and there is no defense
	   * for the attack now...
	   */
	  if (worm[str].color == OTHER_COLOR(color)
	      && worm[str].attack_codes[0] != 0
	      && worm[str].defense_codes[0] != 0) {
	    int dcode = find_defense(str, NULL);
	    if (dcode < worm[str].defense_codes[0]) {
	      int attack_works = 1;

	      /* Sometimes find_defense() fails to find a
	       * defense which has been found by other means.
	       * Try if the old defense move still works.
	       *
	       * However, we first check if the _attack_ still exists,
	       * because we could, for instance, drive the worm into
	       * seki with our move.
	       */
	      if (attack(str, NULL) >= worm[str].attack_codes[0]) {
		if (worm[str].defense_codes[0] != 0
		    && trymove(worm[str].defense_points[0],
			       OTHER_COLOR(color), "make_worms", 0)) {
		  int this_dcode = REVERSE_RESULT(attack(str, NULL));
		  if (this_dcode > dcode) {
		    dcode = this_dcode;
		    if (dcode >= worm[str].defense_codes[0])
		      attack_works = 0;
		  }
		  popgo();
		}
	      }
	      else
		attack_works = 0;
	      
	      /* ...then add an attack point of that worm at pos. */
	      if (attack_works) {
		DEBUG(DEBUG_WORMS,
		      "adding point of attack of %1m at %1m with code %d\n",
		      str, pos, REVERSE_RESULT(dcode));
		change_attack(str, pos, REVERSE_RESULT(dcode));
	      }
	    }
	  }
	  
	  /* If the worm is of the same color as the move we try to
	   * attack it. If there previously was an attack on it, but
	   * there is none now, then add a defense point of str at
	   * pos.
	   */
	  else if (worm[str].color == color
		   && worm[str].attack_codes[0] != 0) {
	    int acode = attack(str, NULL);
	    if (acode < worm[str].attack_codes[0]) {
	      int defense_works = 1;
	      /* Sometimes attack() fails to find an
	       * attack which has been found by other means.
	       * Try if the old attack move still works.
	       */
	      if (worm[str].attack_codes[0] != 0
		  && trymove(worm[str].attack_points[0],
			     OTHER_COLOR(color), "make_worms", 0)) {
		int this_acode;
		if (board[str] == EMPTY)
		  this_acode = WIN;
		else
		  this_acode = REVERSE_RESULT(find_defense(str, NULL));
		if (this_acode > acode) {
		  acode = this_acode;
		  if (acode >= worm[str].attack_codes[0])
		    defense_works = 0;
		}
		popgo();
	      }
	      
	      /* ...then add an attack point of that worm at pos. */
	      if (defense_works) {
		DEBUG(DEBUG_WORMS,
		      "adding point of defense of %1m at %1m with code %d\n",
		      str, pos, REVERSE_RESULT(acode));
		change_defense(str, pos, REVERSE_RESULT(acode));
	      }
	    }
	  }
	}
	decrease_depth_values();
	popgo();
      }
    }
  }
  
  gg_assert(stackp == 0);
  
  /* Sometimes it happens that the tactical reading finds adjacent
   * strings which both can be attacked but not defended. (The reason
   * seems to be that the attacker tries harder to attack a string,
   * than the defender tries to capture it's neighbors.) When this
   * happens, the eyes code produces overlapping eye spaces and, still
   * worse, all the nondefendable stones actually get amalgamated with
   * their allies on the outside.
   *
   * To solve this we scan through the strings which can't be defended
   * and check whether they have a neighbor that can be attacked. In
   * this case we set the defense point of the former string to the
   * attacking point of the latter.
   *
   * Please notice that find_defense() will still read this out
   * incorrectly, which may lead to some confusion later.
   */

  /* First look for vertical neighbors. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos])
	&& IS_STONE(board[SOUTH(pos)])
	&& !is_same_worm(pos, SOUTH(pos))) {
      if (worm[pos].attack_codes[0] != 0
	  && worm[SOUTH(pos)].attack_codes[0] != 0) {
	if (worm[pos].defense_codes[0] == 0
	    && does_defend(worm[SOUTH(pos)].attack_points[0], pos)) {
	  /* FIXME: need to check ko relationship here */
	  change_defense(pos, worm[SOUTH(pos)].attack_points[0], WIN);
	}
	if (worm[SOUTH(pos)].defense_codes[0] == 0
	    && does_defend(worm[pos].attack_points[0], SOUTH(pos))) {
	  /* FIXME: need to check ko relationship here */	    
	  change_defense(SOUTH(pos), worm[pos].attack_points[0], WIN);
	}
      }
    }
  }
  
  /* Then look for horizontal neighbors. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos])
	&& IS_STONE(board[EAST(pos)])
	&& !is_same_worm(pos, EAST(pos))) {
      if (worm[pos].attack_codes[0] != 0
	  && worm[EAST(pos)].attack_codes[0] != 0) {
	if (worm[pos].defense_codes[0] == 0
	    && does_defend(worm[EAST(pos)].attack_points[0], pos)) {
	  /* FIXME: need to check ko relationship here */	    
	  change_defense(pos, worm[EAST(pos)].attack_points[0], WIN);
	}
	if (worm[EAST(pos)].defense_codes[0] == 0
	    && does_defend(worm[pos].attack_points[0], EAST(pos))) {
	  /* FIXME: need to check ko relationship here */	    
	  change_defense(EAST(pos), worm[pos].attack_points[0], WIN);
	}
      }
    }
  }
  
  gg_assert(stackp == 0);
  
  /* Find adjacent worms that can be easily captured, aka lunches. */

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    int lunch;
    
    if (!IS_STONE(board[pos]) || !is_worm_origin(pos, pos))
      continue;
    
    if (find_lunch(pos, &lunch)
	&& (worm[lunch].attack_codes[0] == WIN
	    || worm[lunch].attack_codes[0] == KO_A)) {
      DEBUG(DEBUG_WORMS, "lunch found for %1m at %1m\n", pos, lunch);
      worm[pos].lunch = lunch;
    }
    else
      worm[pos].lunch = NO_MOVE;
    
    propagate_worm(pos);
  }
  
  if (!disable_threat_computation)
    find_worm_threats();

  /* Identify INESSENTIAL strings.
   *
   * These are defined as surrounded strings which have no life
   * potential unless part of their surrounding chain can be captured.
   * We give a conservative definition of inessential:
   *  - the genus must be zero 
   *  - there can no second order liberties
   *  - there can be no more than two edge liberties
   *  - if it is removed from the board, the remaining cavity has
   *    border color the opposite color of the string 
   *  - it contains at most two edge vertices.
   *
   * If we get serious about identifying seki, we might want to add:
   *
   *  - if it has fewer than 4 liberties it is tactically dead.
   *
   * The last condition is helpful in excluding strings which are
   * alive in seki.
   *
   * An inessential string can be thought of as residing inside the
   * opponent's eye space.
   */

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos])
	&& worm[pos].origin == pos
	&& worm[pos].genus == 0
	&& worm[pos].liberties2 == 0
	&& !worm[pos].cutstone
	&& worm[pos].lunch == NO_MOVE) {
      int edge;
      int border_color = examine_cavity(pos, &edge);
      if (border_color != GRAY && edge < 3) {
	DEBUG(DEBUG_WORMS, "Worm %1m identified as inessential.\n", pos);
	worm[pos].inessential = 1;
	propagate_worm(pos);
      }
    }
  }
}


/* 
 * Clear all worms and initialize the basic data fields:
 *   color, origin, size, liberties
 * This is a substep of make_worms().
 */

static void
build_worms()
{
  int pos;

  /* Set all worm data fields to 0. */
  memset(worm, 0 , sizeof(worm));

  /* Initialize the worm data for each worm. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos))
      worm[pos].origin = NO_MOVE;
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos) || worm[pos].origin != NO_MOVE)
      continue;
    worm[pos].color = board[pos];
    worm[pos].origin = pos;
    worm[pos].inessential = 0;
    worm[pos].invincible = 0;
    worm[pos].unconditional_status = UNKNOWN;
    worm[pos].effective_size = 0.0;
    if (IS_STONE(board[pos])) {
      worm[pos].liberties = countlib(pos);
      worm[pos].size = countstones(pos);
      propagate_worm(pos);
    }
  }
}


/* Compute effective size of each worm. 
 *
 * Effective size is the number of stones in a worm plus half the
 * number of empty intersections that are at least as close to this
 * worm as to any other worm. This is used to estimate the direct
 * territorial value of capturing a worm. Intersections that are
 * shared are counted with equal fractional values for each worm.
 *
 * We never count intersections further away than distance 3.
 *
 * This function is also used to compute arrays with information about
 * the distances to worms of both or either color. In the latter case
 * we count intersections up to a distance of 5.
 */

static void
compute_effective_worm_sizes()
{
  do_compute_effective_worm_sizes(BLACK | WHITE, close_worms,
				  number_close_worms, 3);
  do_compute_effective_worm_sizes(BLACK, close_black_worms,
				  number_close_black_worms, 5);
  do_compute_effective_worm_sizes(WHITE, close_white_worms,
				  number_close_white_worms, 5);
}

static void
do_compute_effective_worm_sizes(int color, int (*cw)[MAX_CLOSE_WORMS],
				int *ncw, int max_distance)
{
  int pos;

  /* Distance to closest worm, -1 means unassigned, 0 that there is
   * a stone at the location, 1 a liberty of a stone, and so on.
   */
  int distance[BOARDMAX];
  /* Pointer to the origin of the closest worms. A very large number of
   * worms may potentially be equally close, but no more than
   * 2*(board_size-1).
   */
  static int worms[BOARDMAX][2*(MAX_BOARD-1)];
  int nworms[BOARDMAX];   /* number of equally close worms */
  int found_one;
  int dist; /* current distance */
  int k, l;
  int r;
    
  /* Initialize arrays. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;

    for (k = 0; k < 2*(board_size-1); k++)
      worms[pos][k] = NO_MOVE;
    
    nworms[pos] = 0;
    
    if (board[pos] & color) {
      distance[pos] = 0;
      worms[pos][0] = worm[pos].origin;
      nworms[pos]++;
    }
    else
      distance[pos] = -1;
  }
  
  dist = 0;
  found_one = 1;
  while (found_one && dist <= max_distance) {
    found_one = 0;
    dist++;
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos) || distance[pos] != -1)
	continue; /* already claimed */

      for (r = 0; r < 4; r++) {
	int pos2 = pos + delta[r];
	
	if (ON_BOARD(pos2) && distance[pos2] == dist - 1) {
	  found_one = 1;
	  distance[pos] = dist;
	  for (k = 0; k < nworms[pos2]; k++) {
	    int already_counted = 0;
	    for (l = 0; l < nworms[pos]; l++)
	      if (worms[pos][l] == worms[pos2][k]) {
		already_counted = 1;
		break;
	      }
	    if (!already_counted) {
	      ASSERT1(nworms[pos] < 2*(board_size-1), pos);
	      worms[pos][nworms[pos]] = worms[pos2][k];
	      nworms[pos]++;
	    }
	  }
	}
      }
    }
  }

  /* Compute the effective sizes but only when all worms are considered. */
  if (color == (BLACK | WHITE)) {
    /* Distribute (fractional) contributions to the worms. */
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos))
	continue;
      
      for (k = 0; k < nworms[pos]; k++) {
	int w = worms[pos][k];
	if (board[pos] == EMPTY)
	  worm[w].effective_size += 0.5/nworms[pos];
	else
	  worm[w].effective_size += 1.0;
      }
    }
    
    /* Propagate the effective size values all over the worms. */
    for (pos = BOARDMIN; pos < BOARDMAX; pos++)
      if (IS_STONE(board[pos]) && is_worm_origin(pos, pos))
	propagate_worm(pos);
  }

  /* Fill in the appropriate close_*_worms (cw) and
   * number_close_*_worms (ncw) arrays.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;

    if (nworms[pos] > MAX_CLOSE_WORMS)
      ncw[pos] = 0;
    else
      ncw[pos] = nworms[pos];

    for (k = 0; k < ncw[pos]; k++)
      cw[pos][k] = worms[pos][k];
  }
}

/* Identify worms which are unconditionally uncapturable in the
 * strongest sense, i.e. even if the opponent is allowed an arbitrary
 * number of consecutive moves. Also identify worms which are
 * similarly unconditionally dead and empty points which are
 * unconditional territory for either player.
 */
static void
compute_unconditional_status()
{
  int unconditional_territory[BOARDMAX];
  int pos;
  int color;
  
  for (color = WHITE; color <= BLACK; color++) {
    unconditional_life(unconditional_territory, color);
    if (get_level() >= 10)
      find_unconditionally_meaningless_moves(unconditional_territory, color);

    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos) || !unconditional_territory[pos])
	continue;
	
      if (board[pos] == color) {
	worm[pos].unconditional_status = ALIVE;
	if (unconditional_territory[pos] == 1)
	  worm[pos].invincible = 1;
      }
      else if (board[pos] == EMPTY) {
	if (color == WHITE)
	  worm[pos].unconditional_status = WHITE_TERRITORY;
	else
	  worm[pos].unconditional_status = BLACK_TERRITORY;
      }
      else
	worm[pos].unconditional_status = DEAD;
    }
  }
  gg_assert(stackp == 0);
}

/*
 * Analyze tactical safety of each worm. 
 */

static void
find_worm_attacks_and_defenses()
{
  int str;
  int k;
  int acode, dcode;
  int attack_point;
  int defense_point;
  static int libs[MAXLIBS];
  int liberties;
  int color;
  int other;

   /* 1. Start with finding attack points. */
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (!IS_STONE(board[str]) || !is_worm_origin(str, str))
      continue;

    TRACE("considering attack of %1m\n", str);
    /* Initialize all relevant fields at once. */
    for (k = 0; k < MAX_TACTICAL_POINTS; k++) {
      worm[str].attack_codes[k]   = 0;
      worm[str].attack_points[k]  = 0;
      worm[str].defense_codes[k]  = 0;
      worm[str].defense_points[k] = 0;
    }
    propagate_worm(str);
    
    acode = attack(str, &attack_point);
    if (acode != 0) {
      DEBUG(DEBUG_WORMS, "worm at %1m can be attacked at %1m\n",
	    str, attack_point);
      change_attack(str, attack_point, acode);
    }
  }
  gg_assert(stackp == 0);
  
  /* 2. Use pattern matching to find a few more attacks. */
  find_attack_patterns();
  gg_assert(stackp == 0);
  
  /* 3. Now find defense moves. */
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (!IS_STONE(board[str]) || !is_worm_origin(str, str))
      continue;

    if (worm[str].attack_codes[0] != 0) {

      TRACE("considering defense of %1m\n", str);
      dcode = find_defense(str, &defense_point);
      if (dcode != 0) {
	TRACE("worm at %1m can be defended at %1m\n", str, defense_point);
	if (defense_point != NO_MOVE)
	  change_defense(str, defense_point, dcode);
      }
      else {
	/* If the point of attack is not adjacent to the worm, 
	 * it is possible that this is an overlooked point of
	 * defense, so we try and see if it defends.
	 */
	attack_point = worm[str].attack_points[0];
	if (!liberty_of_string(attack_point, str))
	  if (trymove(attack_point, worm[str].color, "make_worms", NO_MOVE)) {
	    int acode = attack(str, NULL);
	    if (acode != WIN) {
	      change_defense(str, attack_point, REVERSE_RESULT(acode));
	      TRACE("worm at %1m can be defended at %1m with code %d\n",
		    str, attack_point, REVERSE_RESULT(acode));
	    }	 
	    popgo();
	  }
      }
    }
  }
  gg_assert(stackp == 0);

  /* 4. Use pattern matching to find a few more defense moves. */
  find_defense_patterns();
  gg_assert(stackp == 0);
  
  /*
   * 5. Find additional attacks and defenses by testing all immediate
   *    liberties. Further attacks and defenses are found by pattern
   *    matching and by trying whether each attack or defense point
   *    attacks or defends other strings.
   */
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    color = board[str];
    if (!IS_STONE(color) || !is_worm_origin(str, str))
      continue;
    
    other = OTHER_COLOR(color);
    
    if (worm[str].attack_codes[0] == 0)
      continue;
    
    /* There is at least one attack on this group. Try the
     * liberties.
     */
    liberties = findlib(str, MAXLIBS, libs);
    
    for (k = 0; k < liberties; k++) {
      int pos = libs[k];
      if (!attack_move_known(pos, str)) {
	/* Try to attack on the liberty. Don't consider
	 * send-two-return-one moves.
	 */
	if (!send_two_return_one(pos, other)
	    && trymove(pos, other, "make_worms", str)) {
	  if (board[str] == EMPTY || attack(str, NULL)) {
	    if (board[str] == EMPTY)
	      dcode = 0;
	    else
	      dcode = find_defense(str, NULL);
	    
	    if (dcode != WIN)
	      change_attack(str, pos, REVERSE_RESULT(dcode));
	  }
	  popgo();
	}
      }
      /* Try to defend at the liberty. */
      if (!defense_move_known(pos, str)) {
	if (worm[str].defense_codes[0] != 0)
	  if (trymove(pos, color, "make_worms", NO_MOVE)) {
	    acode = attack(str, NULL);
	    if (acode != WIN)
	      change_defense(str, pos, REVERSE_RESULT(acode));
	    popgo();
	  }
      }
    }
  }
  gg_assert(stackp == 0);
}


/*
 * Find moves threatening to attack or save all worms.
 */

static void
find_worm_threats()
{
  int str;
  static int libs[MAXLIBS];
  int liberties;
  
  int k;
  int l;
  int color;
  
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    color = board[str];
    if (!IS_STONE(color) || !is_worm_origin(str, str))
      continue;

    /* 1. Start with finding attack threats. */
    /* Only try those worms that have no attack. */
    if (worm[str].attack_codes[0] == 0) {
      attack_threats(str, MAX_TACTICAL_POINTS,
		     worm[str].attack_threat_points,
		     worm[str].attack_threat_codes);
#if 0
      /* Threaten to attack by saving weak neighbors. */
      num_adj = chainlinks(str, adjs);
      for (k = 0; k < num_adj; k++) {
	if (worm[adjs[k]].attack_codes[0] != 0
	    && worm[adjs[k]].defense_codes[0] != 0)
	  for (r = 0; r < MAX_TACTICAL_POINTS; r++) {
	    int bb;
	    
	    if (worm[adjs[k]].defense_codes[r] == 0)
	      break;
	    bb = worm[adjs[k]].defense_points[r];
	    if (trymove(bb, other, "threaten attack", str,
			EMPTY, NO_MOVE)) {
	      int acode;
	      if (board[str] == EMPTY)
		acode = WIN;
	      else
		acode = attack(str, NULL);
	      if (acode != 0)
		change_attack_threat(str, bb, acode);
	      popgo();
	    }
	  }
      }
#endif
      /* FIXME: Try other moves also (patterns?). */
    }
    
    /* 2. Continue with finding defense threats. */
    /* Only try those worms that have an attack. */
    if (worm[str].attack_codes[0] != 0
	&& worm[str].defense_codes[0] == 0) {
      
      liberties = findlib(str, MAXLIBS, libs);
      
      for (k = 0; k < liberties; k++) {
	int aa = libs[k];
	
	/* Try to threaten on the liberty. */
	if (trymove(aa, color, "threaten defense", NO_MOVE)) {
	  if (attack(str, NULL) == WIN) {
	    int dcode = find_defense(str, NULL);
	    if (dcode != 0)
	      change_defense_threat(str, aa, dcode);
	  }
	  popgo();
	}
	
	/* Try to threaten on second order liberties. */
	for (l = 0; l < 4; l++) {
	  int bb = libs[k] + delta[l];
	  
	  if (!ON_BOARD(bb)
	      || IS_STONE(board[bb])
	      || liberty_of_string(bb, str))
	    continue;
	  
	  if (trymove(bb, color, "threaten defense", str)) {
	    if (attack(str, NULL) == WIN) {
	      int dcode = find_defense(str, NULL);
	      if (dcode != 0)
		change_defense_threat(str, bb, dcode);
	    }
	    popgo();
	  }
	}
      }
      
      /* It might be interesting to look for defense threats by
       * attacking weak neighbors, similar to threatening attack by
       * defending a weak neighbor. However, in this case it seems
       * probable that if there is such an attack, it's a real
       * defense, not only a threat. 
       */
      
      /* FIXME: Try other moves also (patterns?). */
    }
  }
}


/* find_lunch(str, &worm) looks for a worm adjoining the
 * string at (str) which can be easily captured. Whether or not it can
 * be defended doesn't matter.
 *
 * Returns the location of the string in (*lunch).
 */
	
static int
find_lunch(int str, int *lunch)
{
  int pos;
  int k;

  ASSERT1(IS_STONE(board[str]), str);
  ASSERT1(stackp == 0, str);

  *lunch = NO_MOVE;
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] != OTHER_COLOR(board[str]))
      continue;
    for (k = 0; k < 8; k++) {
      int apos = pos + delta[k];
      if (ON_BOARD(apos) && is_same_worm(apos, str)) {
	if (worm[pos].attack_codes[0] != 0 && !is_ko_point(pos)) {
	  /*
	   * If several adjacent lunches are found, we pick the 
	   * juiciest. First maximize cutstone, then minimize liberties. 
	   * We can only do this if the worm data is available, 
	   * i.e. if stackp==0.
	   */
	  if (*lunch == NO_MOVE
	      || worm[pos].cutstone > worm[*lunch].cutstone 
	      || (worm[pos].cutstone == worm[*lunch].cutstone 
		  && worm[pos].liberties < worm[*lunch].liberties)) {
	    *lunch = worm[pos].origin;
	  }
	}
	break;
      }
    }
  }
  
  if (*lunch != NO_MOVE)
    return 1;

  return 0;
}


/*
 * Test whether two worms are the same. Used by autohelpers.
 * Before this function can be called, build_worms must have been run.
 */

int
is_same_worm(int w1, int w2)
{
  return worm[w1].origin == worm[w2].origin;
}


/*
 * Test whether the origin of the worm at (w) is (pos).
 */

int
is_worm_origin(int w, int pos)
{
  return worm[w].origin == pos;
}


/* 
 * change_defense(str, move, dcode) is used to add and remove defense
 * points. It can also be used to change the defense code. The meaning
 * of the call is that the string (str) can be defended by (move) with
 * defense code (dcode). If (dcode) is zero, the move is removed from
 * the list of defense moves if it was previously listed.
 */

void
change_defense(int str, int move, int dcode)
{
  str = worm[str].origin;
  change_tactical_point(str, move, dcode,
			worm[str].defense_points, worm[str].defense_codes);
}


/* 
 * change_attack(str, move, acode) is used to add and remove attack
 * points. It can also be used to change the attack code. The meaning
 * of the call is that the string (str) can be attacked by (move) with
 * attack code (acode). If (acode) is zero, the move is removed from
 * the list of attack moves if it was previously listed.
 */

void
change_attack(int str, int move, int acode)
{
  str = worm[str].origin;
  DEBUG(DEBUG_WORMS, "change_attack: %1m %1m %d\n", str, move, acode);
  change_tactical_point(str, move, acode,
			worm[str].attack_points, worm[str].attack_codes);
}


/* 
 * change_defense_threat(str, move, dcode) is used to add and remove
 * defense threat points. It can also be used to change the defense
 * threat code. The meaning of the call is that the string (str) can
 * threaten to be defended by (move) with defense threat code (dcode).
 * If (dcode) is zero, the move is removed from the list of defense
 * threat moves if it was previously listed.
 */

void
change_defense_threat(int str, int move, int dcode)
{
  str = worm[str].origin;
  change_tactical_point(str, move, dcode,
			worm[str].defense_threat_points,
			worm[str].defense_threat_codes);
}


/* 
 * change_attack_threat(str, move, acode) is used to add and remove
 * attack threat points. It can also be used to change the attack
 * threat code. The meaning of the call is that the string (str) can
 * threaten to be attacked by (move) with attack threat code (acode).
 * If (acode) is zero, the move is removed from the list of attack
 * threat moves if it was previously listed.
 */

void
change_attack_threat(int str, int move, int acode)
{
  str = worm[str].origin;
  change_tactical_point(str, move, acode,
			worm[str].attack_threat_points,
			worm[str].attack_threat_codes);
}


/* Check whether (move) is listed as an attack point for (str) and
 * return the attack code. If (move) is not listed, return 0.
 */
int
attack_move_known(int move, int str)
{
  return movelist_move_known(move, MAX_TACTICAL_POINTS,
			     worm[str].attack_points,
			     worm[str].attack_codes);
}

/* Check whether (move) is listed as a defense point for (str) and
 * return the defense code. If (move) is not listed, return 0.
 */
int
defense_move_known(int move, int str)
{
  return movelist_move_known(move, MAX_TACTICAL_POINTS,
			     worm[str].defense_points,
			     worm[str].defense_codes);
}

/* Check whether (move) is listed as an attack threat point for (str)
 * and return the attack threat code. If (move) is not listed, return
 * 0.
 */
int
attack_threat_move_known(int move, int str)
{
  return movelist_move_known(move, MAX_TACTICAL_POINTS,
			     worm[str].attack_threat_points,
			     worm[str].attack_threat_codes);
}

/* Check whether (move) is listed as a defense threat point for (str)
 * and return the defense threat code. If (move) is not listed, return
 * 0.
 */
int
defense_threat_move_known(int move, int str)
{
  return movelist_move_known(move, MAX_TACTICAL_POINTS,
			     worm[str].defense_threat_points,
			     worm[str].defense_threat_codes);
}


/*
 * This function does the real work for change_attack(),
 * change_defense(), change_attack_threat(), and
 * change_defense_threat().
 */

static void
change_tactical_point(int str, int move, int code,
		      int points[MAX_TACTICAL_POINTS],
		      int codes[MAX_TACTICAL_POINTS])
{
  ASSERT_ON_BOARD1(str);
  ASSERT1(str == worm[str].origin, str);
  
  movelist_change_point(move, code, MAX_TACTICAL_POINTS, points, codes);
  propagate_worm2(str);
}


/* 
 * propagate_worm() takes the worm data at one stone and copies it to 
 * the remaining members of the worm.
 *
 * Even though we don't need to copy all the fields, it's probably
 * better to do a structure copy which should compile to a block copy.
 */

void 
propagate_worm(int pos)
{
  int k;
  int num_stones;
  int stones[MAX_BOARD * MAX_BOARD];
  gg_assert(stackp == 0);
  ASSERT1(IS_STONE(board[pos]), pos);

  num_stones = findstones(pos, MAX_BOARD * MAX_BOARD, stones);
  for (k = 0; k < num_stones; k++)
    if (stones[k] != pos)
      worm[stones[k]] = worm[pos];
}


/* 
 * propagate_worm2() is a relative to propagate_worm() which can be
 * used when stackp>0 but not for the initial construction of the
 * worms.
 */

static void 
propagate_worm2(int str)
{
  int pos;
  ASSERT_ON_BOARD1(str);
  ASSERT1(IS_STONE(worm[str].color), str);

  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (board[pos] == board[str] && is_same_worm(pos, str)
	&& pos != str)
      worm[pos] = worm[str];
}


/* Report all known attack, defense, attack threat, and defense threat
 * moves. But limit this to the moves which can be made by (color).
 */
void
worm_reasons(int color)
{
  int pos;
  int k;
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos) || board[pos] == EMPTY)
      continue;

    if (!is_worm_origin(pos, pos))
      continue;

    if (board[pos] == OTHER_COLOR(color)) {
      for (k = 0; k < MAX_TACTICAL_POINTS; k++) {
	if (worm[pos].attack_codes[k] != 0)
	  add_attack_move(worm[pos].attack_points[k], pos,
			  worm[pos].attack_codes[k]);
	if (worm[pos].attack_threat_codes[k] != 0)
	  add_attack_threat_move(worm[pos].attack_threat_points[k], pos,
				 worm[pos].attack_threat_codes[k]);
      }
    }
      
    if (board[pos] == color) {
      for (k = 0; k < MAX_TACTICAL_POINTS; k++) {
	if (worm[pos].defense_codes[k] != 0)
	  add_defense_move(worm[pos].defense_points[k], pos,
			   worm[pos].defense_codes[k]);

	if (worm[pos].defense_threat_codes[k] != 0)
	  add_defense_threat_move(worm[pos].defense_threat_points[k], pos,
				  worm[pos].defense_threat_codes[k]);
      }
    }
  }
}


/* ping_cave(str, *lib1, ...) is applied when (str) points to a string.
 * It computes the vector (*lib1, *lib2, *lib3, *lib4), 
 * where *lib1 is the number of liberties of the string, 
 * *lib2 is the number of second order liberties (empty vertices
 * at distance two) and so forth.
 *
 * The definition of liberties of order >1 is adapted to the problem
 * of detecting the shape of the surrounding cavity. In particular
 * we want to be able to see if a group is loosely surrounded.
 *
 * A liberty of order n is an empty space which may be connected
 * to the string by placing n stones of the same color on the board, 
 * but no fewer. The path of connection may pass through an intervening group
 * of the same color. The stones placed at distance >1 may not touch a
 * group of the opposite color. At the edge, also diagonal neighbors
 * count as touching. The path may also not pass through a liberty at distance
 * 1 if that liberty is flanked by two stones of the opposing color. This
 * reflects the fact that the O stone is blocked from expansion to the
 * left by the two X stones in the following situation:
 * 
 *          X.
 *          .O
 *          X.
 *
 * On the edge, one stone is sufficient to block expansion:
 *
 *          X.
 *          .O
 *          --
 */

static void 
ping_cave(int str, int *lib1, int *lib2, int *lib3, int *lib4)
{
  int pos;
  int k;
  int libs[MAXLIBS];
  int mrc[BOARDMAX];
  int mse[BOARDMAX];
  int color = board[str];
  int other = OTHER_COLOR(color);

  memset(mse, 0, sizeof(mse));

  /* Find and mark the first order liberties. */
  *lib1 = findlib(str, MAXLIBS, libs);
  for (k = 0; k < *lib1; k++)
    mse[libs[k]] = 1;

  /* Reset mse at liberties which are flanked by two stones of the
   * opposite color, or one stone and the edge.
   */

  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos)
	&& mse[pos]
	&& (((      !ON_BOARD(SOUTH(pos)) || board[SOUTH(pos)] == other)
	     && (   !ON_BOARD(NORTH(pos)) || board[NORTH(pos)] == other))
	    || ((   !ON_BOARD(WEST(pos))  || board[WEST(pos)]  == other)
		&& (!ON_BOARD(EAST(pos))  || board[EAST(pos)]  == other))))
      mse[pos] = 0;
  
  *lib2 = 0;
  memset(mrc, 0, sizeof(mrc));
  ping_recurse(str, lib2, mse, mrc, color);

  *lib3 = 0;
  memset(mrc, 0, sizeof(mrc));
  ping_recurse(str, lib3, mse, mrc, color);

  *lib4 = 0;
  memset(mrc, 0, sizeof(mrc));
  ping_recurse(str, lib4, mse, mrc, color);
}


/* recursive function called by ping_cave */

static void 
ping_recurse(int pos, int *counter,
	     int mx[BOARDMAX], int mr[BOARDMAX],
	     int color)
{
  int k;
  mr[pos] = 1;

  for (k = 0; k < 4; k++) {
    int apos = pos + delta[k];
    if (board[apos] == EMPTY
	&& mx[apos] == 0
	&& mr[apos] == 0
	&& !touching(apos, OTHER_COLOR(color))) {
      (*counter)++;
      mr[apos] = 1;
      mx[apos] = 1;
    }
  }
  
  if (!is_ko_point(pos)) {
    for (k = 0; k < 4; k++) {
      int apos = pos + delta[k];
      if (ON_BOARD(apos)
	  && mr[apos] == 0
	  && (mx[apos] == 1
	      || board[apos] == color))
	ping_recurse(apos, counter, mx, mr, color);
    }
  }
}


/* touching(pos, color) returns true if the vertex at (pos) is
 * touching any stone of (color).
 */

static int
touching(int pos, int color)
{
  return (board[SOUTH(pos)] == color
	  || board[WEST(pos)] == color
	  || board[NORTH(pos)] == color
	  || board[EAST(pos)] == color);
}


/* The GENUS of a string is the number of connected components of
 * its complement, minus one. It is an approximation to the number of
 * eyes of the string.
 */

static int 
genus(int str)
{
  int pos;
  int mg[BOARDMAX];
  int gen = -1;

  memset(mg, 0, sizeof(mg));
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (ON_BOARD(pos)
	&& !mg[pos]
	&& (board[pos] == EMPTY || !is_same_worm(pos, str))) {
      markcomponent(str, pos, mg);
      gen++;
    }
  }

  return gen;
}


/* This recursive function marks the component at (pos) of 
 * the complement of the string with origin (str)
 */

static void 
markcomponent(int str, int pos, int mg[BOARDMAX])
{
  int k;
  mg[pos] = 1;
  for (k = 0; k < 4; k++) {
    int apos = pos + delta[k];
    if (ON_BOARD(apos)
	&& mg[apos] == 0
	&& (board[apos] == EMPTY || !is_same_worm(apos, str)))
      markcomponent(str, apos, mg);
  }
}


/* examine_cavity(pos, *edge), if (pos) is EMPTY, examines the
 * cavity at (m, n) and returns its bordercolor,
 * which can be BLACK, WHITE or GRAY. The edge parameter is set to the
 * number of edge vertices in the cavity.
 *
 * If (pos) is nonempty, it returns the same result, imagining
 * that the string at (pos) is removed. The edge parameter is
 * set to the number of vertices where the cavity meets the
 * edge in a point outside the removed string.  
 */

static int
examine_cavity(int pos, int *edge)
{
  int border_color = EMPTY;
  int ml[BOARDMAX];
  int origin = NO_MOVE;
  
  ASSERT_ON_BOARD1(pos);
  gg_assert(edge != NULL);
  
  memset(ml, 0, sizeof(ml));

  *edge = 0;

  if (IS_STONE(board[pos]))
    origin = find_origin(pos);
  
  cavity_recurse(pos, ml, &border_color, edge, origin);

  if (border_color != EMPTY)
    return border_color;

  /* We should have returned now, unless the board is completely empty.
   * Verify that this is the case and then return GRAY.
   *
   * Notice that the board appears completely empty if there's only a
   * single string and pos points to it.
   */
  gg_assert(border_color == EMPTY
	    && ((pos == NO_MOVE
		 && stones_on_board(BLACK | WHITE) == 0)
		|| (pos != NO_MOVE
		    && stones_on_board(BLACK | WHITE) == countstones(pos))));
  
  return GRAY;
}


/* helper function for examine_cavity.
 * border_color contains information so far : transitions allowed are
 *   EMPTY       -> BLACK/WHITE
 *   BLACK/WHITE -> BLACK | WHITE
 *
 * mx[pos] is 1 if (pos) has already been visited.
 *
 * if (str) points to the origin of a string, it will be ignored.
 *
 * On (fully-unwound) exit
 *   *border_color should be BLACK, WHITE or BLACK | WHITE
 *   *edge is the count of edge pieces
 *
 * *border_color should be EMPTY if and only if the board
 * is completely empty or only contains the ignored string.
 */

static void 
cavity_recurse(int pos, int mx[BOARDMAX], 
	       int *border_color, int *edge, int str)
{
  int k;
  ASSERT1(mx[pos] == 0, pos);

  mx[pos] = 1;

  if (is_edge_vertex(pos) && board[pos] == EMPTY) 
    (*edge)++;

  /* Loop over the four neighbors. */
  for (k = 0; k < 4; k++) {
    int apos = pos + delta[k];
    if (ON_BOARD(apos) && !mx[apos]) {
      int neighbor_empty = 0;
      
      if (board[apos] == EMPTY)
	neighbor_empty = 1;
      else {
	/* Count the neighbor as empty if it is part of the (ai, aj) string. */
	if (str == find_origin(apos))
	  neighbor_empty = 1;
	else
	  neighbor_empty = 0;
      }
      
      if (!neighbor_empty)
	*border_color |= board[apos];
      else
	cavity_recurse(apos, mx, border_color, edge, str);
    }
  }
}


/* Find attacking moves by pattern matching, for both colors. */
static void
find_attack_patterns(void)
{
  matchpat(attack_callback, ANCHOR_OTHER, &attpat_db, NULL, NULL);
}

/* Try to attack every X string in the pattern, whether there is an attack
 * before or not. Only exclude already known attacking moves.
 */
static void
attack_callback(int anchor, int color, struct pattern *pattern, int ll,
		void *data)
{
  int move;
  int k;
  UNUSED(data);

  move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);

  /* If the pattern has a constraint, call the autohelper to see
   * if the pattern must be rejected.
   */
  if (pattern->autohelper_flag & HAVE_CONSTRAINT) {
    if (!pattern->autohelper(ll, move, color, 0))
      return;
  }

  /* If the pattern has a helper, call it to see if the pattern must
   * be rejected.
   */
  if (pattern->helper) {
    if (!pattern->helper(pattern, ll, move, color)) {
      DEBUG(DEBUG_WORMS,
	    "Attack pattern %s+%d rejected by helper at %1m\n",
	    pattern->name, ll, move);
      return;
    }
  }

  /* Loop through pattern elements in search of X strings to attack. */
  for (k = 0; k < pattern->patlen; ++k) { /* match each point */
    if (pattern->patn[k].att == ATT_X) {
      /* transform pattern real coordinate */
      int pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor);

      int str = worm[pos].origin;

      /* A string with 5 liberties or more is considered tactically alive. */
      if (countlib(str) > 4)
	continue;

      if (attack_move_known(move, str))
	continue;

      /* No defenses are known at this time, so defend_code is always 0. */
#if 0
      /* If the string can be attacked but not defended, ignore it. */
      if (worm[str].attack_codes[0] == WIN && worm[str].defense_codes[0] == 0)
	continue;
#endif
      
      /* FIXME: Don't attack the same string more than once.
       * Play (move) and see if there is a defense.
       */
      if (trymove(move, color, "attack_callback", str)) {
	int dcode;
	if (!board[str])
	  dcode = 0;
	else if (!attack(str, NULL))
	  dcode = WIN;
	else
	  dcode = find_defense(str, NULL);

	popgo();

	/* Do not pick up suboptimal attacks at this time. Since we
         * don't know whether the string can be defended it's quite
         * possible that it only has a ko defense and then we would
         * risk to find an irrelevant move to attack with ko.
	 */
	if (dcode != WIN && REVERSE_RESULT(dcode) >= worm[str].attack_codes[0]) {
	  change_attack(str, move, REVERSE_RESULT(dcode));
	  DEBUG(DEBUG_WORMS,
		"Attack pattern %s+%d found attack on %1m at %1m with code %d\n",
		pattern->name, ll, str, move, REVERSE_RESULT(dcode));
	}
      }
    }
  }
}

static void
find_defense_patterns(void)
{
  matchpat(defense_callback, ANCHOR_COLOR, &defpat_db, NULL, NULL);
}

static void
defense_callback(int anchor, int color, struct pattern *pattern, int ll,
		 void *data)
{
  int move;
  int k;
  UNUSED(data);

  move = AFFINE_TRANSFORM(pattern->move_offset, ll, anchor);
  
  /* If the pattern has a constraint, call the autohelper to see
   * if the pattern must be rejected.
   */
  if (pattern->autohelper_flag & HAVE_CONSTRAINT) {
    if (!pattern->autohelper(ll, move, color, 0))
      return;
  }

  /* If the pattern has a helper, call it to see if the pattern must
   * be rejected.
   */
  if (pattern->helper) {
    if (!pattern->helper(pattern, ll, move, color)) {
      DEBUG(DEBUG_WORMS,
	    "Defense pattern %s+%d rejected by helper at %1m\n",
	    pattern->name, ll, move);
      return;
    }
  }

  /* Loop through pattern elements in search for O strings to defend. */
  for (k = 0; k < pattern->patlen; ++k) { /* match each point */
    if (pattern->patn[k].att == ATT_O) {
      /* transform pattern real coordinate */
      int pos = AFFINE_TRANSFORM(pattern->patn[k].offset, ll, anchor);
      int str = worm[pos].origin;

      if (worm[str].attack_codes[0] == 0
	  || defense_move_known(move, str))
	continue;
      
      /* FIXME: Don't try to defend the same string more than once.
       * FIXME: For all attacks on this string, we should test whether
       *        the proposed move happens to refute the attack.
       * Play (move) and see if there is an attack.
       */
      if (trymove(move, color, "defense_callback", str)) {
	int acode = attack(str, NULL);

	popgo();
	
	if (acode < worm[str].attack_codes[0]) {
	  change_defense(str, move, REVERSE_RESULT(acode));
	  DEBUG(DEBUG_WORMS,
		"Defense pattern %s+%d found defense of %1m at %1m with code %d\n",
		pattern->name, ll, str, move, REVERSE_RESULT(acode));
	}
      }
    }
  }
}


void
get_lively_stones(int color, signed char safe_stones[BOARDMAX])
{
  int pos;
  memset(safe_stones, 0, BOARDMAX * sizeof(*safe_stones));
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (IS_STONE(board[pos]) && find_origin(pos) == pos) {
      if ((stackp == 0 && worm[pos].attack_codes[0] == 0) || !attack(pos, NULL)
	  || (board[pos] == color
	      && ((stackp == 0 && worm[pos].defense_codes[0] != 0)
		  || find_defense(pos, NULL))))
	mark_string(pos, safe_stones, 1);
    }
}


void
compute_worm_influence()
{
  signed char safe_stones[BOARDMAX];

  get_lively_stones(BLACK, safe_stones);
  compute_influence(BLACK, safe_stones, NULL, &initial_black_influence,
      		    NO_MOVE, "initial black influence");
  get_lively_stones(WHITE, safe_stones);
  compute_influence(WHITE, safe_stones, NULL, &initial_white_influence,
      		    NO_MOVE, "initial white influence");
}

/* ================================================================ */
/*                      Debugger functions                          */
/* ================================================================ */

/* For use in gdb, print details of the worm at (m, n). 
 * Add this to your .gdbinit file:
 *
 * define worm
 * set ascii_report_worm("$arg0")
 * end
 *
 * Now 'worm S8' will report the details of the S8 worm.
 *
 */

void
ascii_report_worm(char *string)
{
  int pos = string_to_location(board_size, string);
  report_worm(pos);
}


static void
report_worm(int pos)
{
  int i;

  if (board[pos] == EMPTY) {
    gprintf("There is no worm at %1m\n", pos);
    return;
  }

  gprintf("*** worm at %1m:\n", pos);
  gprintf("color: %s; origin: %1m; size: %d; effective size: %f\n",
	  (worm[pos].color == WHITE) ? "White" : "Black",
	  worm[pos].origin, worm[pos].size, worm[pos].effective_size);

  gprintf("liberties: %d order 2 liberties:%d order 3:%d order 4:%d\n",
	  worm[pos].liberties, 
	  worm[pos].liberties2, 
	  worm[pos].liberties3, 
	  worm[pos].liberties4);

  /* List all attack points. */
  if (worm[pos].attack_points[0] == NO_MOVE)
    gprintf("no attack point, ");
  else {
    gprintf("attack point(s):");
    i = 0;
    while (worm[pos].attack_points[i] != NO_MOVE) {
      if (i > 0)
	gprintf(",");
      gprintf(" %1m: %s", worm[pos].attack_points[i],
	      result_to_string(worm[pos].attack_codes[i]));
      i++;
    }
    gprintf("\n;");
  }

  /* List all defense points. */
  if (worm[pos].defense_points[0] == NO_MOVE)
    gprintf("no defense point, ");
  else {
    gprintf("defense point(s):");
    i = 0;
    while (worm[pos].defense_points[i] != NO_MOVE) {
      if (i > 0)
	gprintf(",");
      gprintf(" %1m: %s", worm[pos].defense_points[i],
	      result_to_string(worm[pos].defense_codes[i]));
      i++;
    }
    gprintf("\n;");
  }

  /* List all attack threat points. */
  if (worm[pos].attack_threat_points[0] == NO_MOVE)
    gprintf("no attack threat point, ");
  else {
    gprintf("attack threat point(s):");
    i = 0;
    while (worm[pos].attack_threat_points[i] != NO_MOVE) {
      if (i > 0)
	gprintf(",");
      gprintf(" %1m: %s", worm[pos].attack_threat_points[i],
	      result_to_string(worm[pos].attack_threat_codes[i]));
      i++;
    }
    gprintf("\n;");
  }

  /* List all defense threat points. */
  if (worm[pos].defense_threat_points[0] == NO_MOVE)
    gprintf("no defense threat point, ");
  else {
    gprintf("defense threat point(s):");
    i = 0;
    while (worm[pos].defense_threat_points[i] != NO_MOVE) {
      if (i > 0)
	gprintf(",");
      gprintf(" %1m: %s", worm[pos].defense_threat_points[i],
	      result_to_string(worm[pos].defense_threat_codes[i]));
      i++;
    }
    gprintf("\n;");
  }

  /* Report lunch if any. */
  if (worm[pos].lunch != NO_MOVE)
    gprintf("lunch at %1m\n", worm[pos].lunch);

  gprintf("cutstone: %d, cutstone2: %d\n",
	  worm[pos].cutstone, worm[pos].cutstone2);

  gprintf("genus: %d, ", worm[pos].genus);

  if (worm[pos].inessential)
    gprintf("inessential: YES, ");
  else
    gprintf("inessential: NO, ");

  if (worm[pos].invincible)
    gprintf("invincible: YES, \n");
  else
    gprintf("invincible: NO, \n");

  gprintf("unconditional status %s\n",
	  status_to_string(worm[pos].unconditional_status));
}


/*
 * Local Variables:
 * tab-width: 8
 * c-basic-offset: 2
 * End:
 */
