/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * 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.                                            *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

/* A "dragon" is a union of strings of the same color which will be
 * treated as a unit. The dragons are generated anew at each
 * move. If two strings are in the dragon, it is GNU Go's working
 * hypothesis that they will live or die together and are
 * effectively connected.
 *
 *                    _____/|        (! !)
 *                   / ____/|        /@ @)
 *                  / /   __        //  +--oo
 *                 | /   |   >>    /<  _-v--}
 *                 | |   UUU\\\     / / \\
 *                 | |   __ _\\\    \ \  U
 *                 | |  /  V  \\-->  \ \ 
 *                 | <_/           \_/  }
 *                 |      __     ____  /
 *                  \    /  \___/   / /\
 *                  <  \<          < <\ \
 *                   ( )))         ( ))))) 
 */

#include "gnugo.h"

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

#include "liberty.h"
#include "gg_utils.h"

static void initialize_supplementary_dragon_data(void);
static void find_lunches(void);
static void eye_computations(void);
static void revise_inessentiality(void);
static void find_neighbor_dragons(void);
static void add_adjacent_dragons(int a, int b);
static void add_adjacent_dragon(int a, int b);
static int dragon_invincible(int pos);
static int dragon_looks_inessential(int origin);
static void identify_thrashing_dragons(void);
static void analyze_false_eye_territory(void);
static int connected_to_eye(int pos, int str, int color, int eye_color,
			    struct eye_data *eye);
static void connected_to_eye_recurse(int pos, int str, int color,
				     int eye_color, struct eye_data *eye,
				     signed char *mx, signed char *me,
				     int *halfeyes);
static enum dragon_status compute_crude_status(int pos);
static int compute_escape(int pos, int dragon_status_known);
static void compute_surrounding_moyo_sizes(const struct influence_data *q);
static void clear_cut_list(void);

static int dragon2_initialized;
static int lively_white_dragons;
static int lively_black_dragons;

/* This is a private array to obtain a list of worms belonging to each
 * dragon. Public access is via first_worm_in_dragon() and
 * next_worm_in_dragon().
 */
static int next_worm_list[BOARDMAX];

/* Alternative for DRAGON2 macro with asserts. */
struct dragon_data2 *
dragon2_func(int pos)
{
  ASSERT1(ON_BOARD1(pos)
          && dragon[pos].id >= 0 
          && dragon[pos].id < number_of_dragons, pos);
  return &dragon2[dragon[pos].id];
}

/* This basic function finds all dragons and collects some basic information
 * about them in the dragon array.
 *
 * color is the player in turn to move. This does in no way affect the
 * information collected about the dragons, but it does affect what
 * information is passed on to the move generation code. If
 * color == EMPTY no information at all is passed on to the move generation.
 */

void 
make_dragons(int stop_before_owl)
{
  int str;
  int d;

  dragon2_initialized = 0;
  initialize_dragon_data();

  /* Find explicit connections patterns in database and amalgamate
   * involved dragons.
   */
  memset(cutting_points, 0, sizeof(cutting_points));
  find_cuts();
  find_connections();

  /* At this time, all dragons have been finalized and we can
   * initialize the dragon2[] array. After that we can no longer allow
   * amalgamation of dragons.
   */
  initialize_supplementary_dragon_data();
  
  make_domains(black_eye, white_eye, 0);

  /* Find adjacent worms which can be easily captured: */
  find_lunches();

  /* Find topological half eyes and false eyes. */
  find_half_and_false_eyes(BLACK, black_eye, half_eye, NULL);
  find_half_and_false_eyes(WHITE, white_eye, half_eye, NULL);

  /* Compute the number of eyes, half eyes, determine attack/defense points
   * etc. for all eye spaces. */
  eye_computations();
  /* Try to determine whether topologically false and half eye points
   * contribute to territory even if the eye doesn't solidify.
   */
  analyze_false_eye_territory();

  /* Now we compute the genus. */
  for (d = 0; d < number_of_dragons; d++)
    compute_dragon_genus(dragon2[d].origin, &dragon2[d].genus, NO_MOVE);

  /* Compute the escape route measure. */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (IS_STONE(board[str]) && dragon[str].origin == str)
      DRAGON2(str).escape_route = compute_escape(str, 0);

  /* Set dragon weaknesses according to initial_influence. */
  compute_refined_dragon_weaknesses();
  for (d = 0; d < number_of_dragons; d++)
    dragon2[d].weakness_pre_owl = dragon2[d].weakness;

  /* Determine status: ALIVE, DEAD, CRITICAL or UNKNOWN */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str))
      if (dragon[str].origin == str && board[str])
	dragon[str].crude_status = compute_crude_status(str);
  
  /* We must update the dragon status at every intersection before we
   * call the owl code. This updates all fields.
   */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str) && board[str] != EMPTY)
      dragon[str] = dragon[dragon[str].origin];
  
  find_neighbor_dragons();

  for (d = 0; d < number_of_dragons; d++) {
    dragon2[d].surround_status 
      = compute_surroundings(dragon2[d].origin, NO_MOVE, 0,
			     &(dragon2[d].surround_size));
    if (dragon2[d].surround_status == SURROUNDED) {
      dragon2[d].escape_route = 0;
      if (debug & DEBUG_DRAGONS)
	gprintf("surrounded dragon found at %1m\n", dragon2[d].origin);
    }
    else if (dragon2[d].surround_status == WEAKLY_SURROUNDED) {
      dragon2[d].escape_route /= 2;
      if (debug & DEBUG_DRAGONS)
	gprintf("weakly surrounded dragon found at %1m\n", dragon2[d].origin);
    }
  }

  if (stop_before_owl)
    return;
  
  /* Determine life and death status of each dragon using the owl code
   * if necessary.
   */
  start_timer(2);
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str)) {
      int attack_point = NO_MOVE;
      int defense_point = NO_MOVE;
      struct eyevalue no_eyes;
      set_eyevalue(&no_eyes, 0, 0, 0, 0);
      
      if (board[str] == EMPTY
	  || dragon[str].origin != str)
	continue;
      
      /* Some dragons can be ignored but be extra careful with big dragons. */
      if (crude_dragon_weakness(ALIVE, &no_eyes, 0,
	    			DRAGON2(str).moyo_territorial_value,
				DRAGON2(str).escape_route - 10)
	  < 0.00001 + gg_max(0.12, 0.32 - 0.01*dragon[str].effective_size)) {
	DRAGON2(str).owl_status = UNCHECKED;
	DRAGON2(str).owl_attack_point  = NO_MOVE;
	DRAGON2(str).owl_defense_point = NO_MOVE;
      }
      else {
	int acode = 0;
	int dcode = 0;
	int kworm = NO_MOVE;
	int owl_nodes_before = get_owl_node_counter();
	start_timer(3);
	acode = owl_attack(str, &attack_point, 
			   &DRAGON2(str).owl_attack_certain, &kworm);
	DRAGON2(str).owl_attack_node_count
	  = get_owl_node_counter() - owl_nodes_before;
	if (acode != 0) {
	  DRAGON2(str).owl_attack_point = attack_point;
	  DRAGON2(str).owl_attack_code = acode;
	  DRAGON2(str).owl_attack_kworm = kworm;
	  if (attack_point != NO_MOVE) {
	    kworm = NO_MOVE;
	    dcode = owl_defend(str, &defense_point,
			       &DRAGON2(str).owl_defense_certain, &kworm);
	    if (dcode != 0) {
	      if (defense_point != NO_MOVE) {
		DRAGON2(str).owl_status = (acode == GAIN ? ALIVE : CRITICAL);
		DRAGON2(str).owl_defense_point = defense_point;
		DRAGON2(str).owl_defense_code = dcode;
		DRAGON2(str).owl_defense_kworm = kworm;
	      }
	      else {
		/* Due to irregularities in the owl code, it may
		 * occasionally happen that a dragon is found to be
		 * attackable but also alive as it stands. In this case
		 * we still choose to say that the owl_status is
		 * CRITICAL, although we don't have any defense move to
		 * propose. Having the status right is important e.g.
		 * for connection moves to be properly valued.
		 */
		DRAGON2(str).owl_status = (acode == GAIN ? ALIVE : CRITICAL);
		DEBUG(DEBUG_OWL_PERFORMANCE,
		      "Inconsistent owl attack and defense results for %1m.\n", 
		      str);
		/* Let's see whether the attacking move might be the right
		 * defense:
		 */
		dcode = owl_does_defend(DRAGON2(str).owl_attack_point,
					str, NULL);
		if (dcode != 0) {
		  DRAGON2(str).owl_defense_point
		    = DRAGON2(str).owl_attack_point;
		  DRAGON2(str).owl_defense_code = dcode;
		}
	      }
	    }
	  }
	  if (dcode == 0) {
	    DRAGON2(str).owl_status = DEAD; 
	    DRAGON2(str).owl_defense_point = NO_MOVE;
	    DRAGON2(str).owl_defense_code = 0;
	  }
	}
	else {
	  if (!DRAGON2(str).owl_attack_certain) {
	    kworm = NO_MOVE;
	    dcode = owl_defend(str, &defense_point, 
			       &DRAGON2(str).owl_defense_certain, &kworm);
	    if (dcode != 0) {
	      /* If the result of owl_attack was not certain, we may
	       * still want the result of owl_defend */
	      DRAGON2(str).owl_defense_point = defense_point;
	      DRAGON2(str).owl_defense_code = dcode;
	      DRAGON2(str).owl_defense_kworm = kworm;
	    }
	  }
	  DRAGON2(str).owl_status = ALIVE;
	  DRAGON2(str).owl_attack_point = NO_MOVE;
	  DRAGON2(str).owl_attack_code = 0;
	  
	}
      }
    }
  time_report(2, "  owl reading", NO_MOVE, 1.0);
  
  /* Compute the status to be used by the matcher. We most trust the
   * owl status, if it is available. If it's not we assume that we are
   * already confident that the dragon is alive, regardless of
   * crude_status.
   */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (IS_STONE(board[str])) {
      if (DRAGON2(str).owl_status != UNCHECKED)
	dragon[str].status = DRAGON2(str).owl_status;
      else
	dragon[str].status = ALIVE;
    }

  /* The dragon data is now correct at the origin of each dragon but
   * we need to copy it to every vertex.  
   */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str) && board[str] != EMPTY)
      dragon[str] = dragon[dragon[str].origin];

  identify_thrashing_dragons();
  
  /* Owl threats. */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str) 
	&& board[str] != EMPTY 
	&& dragon[str].origin == str) {
      struct eyevalue no_eyes;
      set_eyevalue(&no_eyes, 0, 0, 0, 0);
      if (crude_dragon_weakness(ALIVE, &no_eyes, 0,
	    			DRAGON2(str).moyo_territorial_value,
				DRAGON2(str).escape_route - 10)
	  < 0.00001 + gg_max(0.12, 0.32 - 0.01*dragon[str].effective_size)) {
	DRAGON2(str).owl_threat_status = UNCHECKED;
	DRAGON2(str).owl_second_attack_point  = NO_MOVE;
	DRAGON2(str).owl_second_defense_point = NO_MOVE;
      }
      else {
	int acode = DRAGON2(str).owl_attack_code;
	int dcode = DRAGON2(str).owl_defense_code;
	int defense_point, second_defense_point;

	if (get_level() >= 8
	    && !disable_threat_computation
	    && (owl_threats 
		|| thrashing_stone[str])) {
	  if (acode && !dcode && DRAGON2(str).owl_attack_point != NO_MOVE) {
	    if (owl_threaten_defense(str, &defense_point,
				     &second_defense_point)) {
	      DRAGON2(str).owl_threat_status = CAN_THREATEN_DEFENSE;
	      DRAGON2(str).owl_defense_point = defense_point;
	      DRAGON2(str).owl_second_defense_point = second_defense_point;
	    }
	    else
	      DRAGON2(str).owl_threat_status = DEAD;
	  }
	  else if (!acode) {
	    int attack_point, second_attack_point;
	    if (owl_threaten_attack(str, 
				    &attack_point, &second_attack_point)) {
	      DRAGON2(str).owl_threat_status = CAN_THREATEN_ATTACK;
	      DRAGON2(str).owl_attack_point = attack_point;
	      DRAGON2(str).owl_second_attack_point = second_attack_point;
	    }
	    else
	      DRAGON2(str).owl_threat_status = ALIVE;
	  }
	}
      }
    }
  
  /* Once again, the dragon data is now correct at the origin of each dragon
   * but we need to copy it to every vertex.  
   */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str) && board[str] != EMPTY)
      dragon[str] = dragon[dragon[str].origin];

  time_report(2, "  owl threats ", NO_MOVE, 1.0);
  

  /* Compute the safety value. */
  for (d = 0; d < number_of_dragons; d++) {
    int true_genus;
    int origin = dragon2[d].origin;
    struct eyevalue *genus = &dragon2[d].genus;

    /* FIXME: We lose information when constructing true_genus. This
     * code can be improved.
     */
    true_genus = max_eyes(genus) + min_eyes(genus);
    if (dragon_looks_inessential(origin))
      dragon2[d].safety = INESSENTIAL;
    else if (dragon[origin].size == worm[origin].size
	     && worm[origin].attack_codes[0] != 0
	     && worm[origin].defense_codes[0] == 0)
      dragon2[d].safety = TACTICALLY_DEAD;
    else if (0) /* Seki is detected by the call to semeai() below. */
      dragon2[d].safety = ALIVE_IN_SEKI;
    else if (dragon_invincible(origin)) {
      dragon2[d].safety = INVINCIBLE;
      /* Sometimes the owl analysis may have misevaluated invincible
       * dragons, typically if they live by topologically false eyes.
       * Therefore we also set the status here.
       */
      DRAGON(d).status = ALIVE;
    }
    else if (dragon2[d].owl_status == DEAD)
      dragon2[d].safety = DEAD;
    else if (dragon2[d].owl_status == CRITICAL)
      dragon2[d].safety = CRITICAL;
    else if (true_genus >= 6 || dragon2[d].moyo_size > 20)
      dragon2[d].safety = STRONGLY_ALIVE;
    else
      dragon2[d].safety = ALIVE;
  }

  /* The status is now correct at the origin of each dragon
   * but we need to copy it to every vertex.
   */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str))
      dragon[str].status = dragon[dragon[str].origin].status;

  /* Revise inessentiality of critical worms and dragons. */
  revise_inessentiality();

  semeai();
  time_report(2, "  semeai module", NO_MOVE, 1.0);
  
  /* Count the non-dead dragons. */
  lively_white_dragons = 0;
  lively_black_dragons = 0;
  for (d = 0; d < number_of_dragons; d++)
    if (DRAGON(d).status != DEAD) {
      if (DRAGON(d).color == WHITE)
        lively_white_dragons++;
      else
        lively_black_dragons++;
    }
}


/* Find capturable worms adjacent to each dragon. */
static void
find_lunches()
{
  int str;
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str)) {
      int food;

      if (worm[str].origin != str
	  || board[str] == EMPTY
	  || worm[str].lunch == NO_MOVE)
	continue;

      food = worm[str].lunch;

      /* In contrast to worm lunches, a dragon lunch must also be
       * able to defend itself. 
       */
      if (worm[food].defense_codes[0] == 0)
	continue;

      /* Tell the move generation code about the lunch. */
      add_lunch(str, food);
	
      /* If several lunches are found, we pick the juiciest.
       * First maximize cutstone, then minimize liberties.
       */
      {
	int origin = dragon[str].origin;
	int lunch = DRAGON2(origin).lunch;

	if (lunch == NO_MOVE
	    || worm[food].cutstone > worm[lunch].cutstone
	    || (worm[food].cutstone == worm[lunch].cutstone
		&& (worm[food].liberties < worm[lunch].liberties))) {
	  DRAGON2(origin).lunch = worm[food].origin;
	  TRACE("at %1m setting %1m.lunch to %1m (cutstone=%d)\n",
		str, origin,
		worm[food].origin, worm[food].cutstone);
	}
      }
    }
}


/* Compute the value of each eye space. Store its attack and defense point.
 * A more comlete list of attack and defense points is stored in the lists
 * black_vital_points and white_vital_points.
 */
static void
eye_computations()
{ 
  int str;

  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (!ON_BOARD(str))
      continue;

    if (black_eye[str].color == BLACK
	&& black_eye[str].origin == str) {
      struct eyevalue value;
      int attack_point, defense_point;
      
      compute_eyes(str, &value, &attack_point, &defense_point, 
		   black_eye, half_eye, 1);
      DEBUG(DEBUG_EYES, "Black eyespace at %1m: %s\n", str,
	    eyevalue_to_string(&value));
      black_eye[str].value = value;
      propagate_eye(str, black_eye);
    }
    
    if (white_eye[str].color == WHITE
	&& white_eye[str].origin == str) {
      struct eyevalue value;
      int attack_point, defense_point;
      
      compute_eyes(str, &value, &attack_point, &defense_point,
		   white_eye, half_eye, 1);
      DEBUG(DEBUG_EYES, "White eyespace at %1m: %s\n", str,
	    eyevalue_to_string(&value));
      white_eye[str].value = value;
      propagate_eye(str, white_eye);
    }
  }
}


/* This function revises the inessentiality of critical worms and dragons
 * according to the criteria explained in the comments below.
 */
static void
revise_inessentiality()
{
  int str;
  /* Revise essentiality of critical worms. Specifically, a critical
   * worm which is adjacent to no enemy dragon with status
   * better than DEAD, is considered INESSENTIAL.
   *
   * A typical case of this is
   *
   * |.XXXX
   * |.OO.X
   * |X.O.X
   * |.OO.X
   * +-----
   *
   * However, to be able to deal with the position
   *
   * |.XXXX
   * |.OOOO
   * |..O.O
   * |X.OOO
   * |..O.O
   * +-----
   *
   * we need to extend "adjacent" to "adjacent or shares a liberty",
   * which is why we use extended_chainlinks() rather than
   * chainlinks().
   *
   * Finally, if the position above is slightly modified to
   *
   * |.XXXXX
   * |.OOOOO
   * |...O.O
   * |X..OOO
   * |...O.O
   * +------
   *
   * we have a position where the critical X stone doesn't share a
   * liberty with any string at all. Thus the revised rule is:
   *
   * A critical worm which is adjacent to or share a liberty with at
   * least one dead opponent dragon and no opponent dragon which is
   * not dead, is considered inessential.
   */

  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str)) {
      if (is_worm_origin(str, str)
	  && worm[str].attack_codes[0] != 0
	  && worm[str].defense_codes[0] != 0
	  && !worm[str].inessential) {
	int adjs[MAXCHAIN];
	int neighbors;
	int r;
	int essential = 0;
	
	neighbors = extended_chainlinks(str, adjs, 0);
	for (r = 0; r < neighbors; r++)
	  if (dragon[adjs[r]].status != DEAD) {
	    essential = 1;
	    break;
	  }

	if (!essential && neighbors > 0) {
	  DEBUG(DEBUG_WORMS, "Worm %1m revised to be inessential.\n", str);
	  worm[str].inessential = 1;
	  propagate_worm(str);
	}
      }
    }

  /* Revise essentiality of critical dragons. Specifically, a critical
   * dragon consisting entirely of inessential worms is considered
   * INESSENTIAL.
   */
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (ON_BOARD(str)
	&& board[str] != EMPTY
	&& dragon[str].origin == str
	&& DRAGON2(str).safety == CRITICAL) {
      int w;
      for (w = first_worm_in_dragon(str); w != NO_MOVE;
	   w = next_worm_in_dragon(w)) {
	if (!worm[w].inessential)
	  break;
      }

      if (w == NO_MOVE) {
	DEBUG(DEBUG_DRAGONS, "Dragon %1m revised to be inessential.\n", str);
	DRAGON2(str).safety = INESSENTIAL;
      }
    }
  }
}

/* Initialize the dragon[] array. */

void
initialize_dragon_data(void)
{
  int str;
  /* VALGRIND_MAKE_WRITABLE(dragon, BOARDMAX * sizeof(struct dragon_data)); */
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str)) {

      dragon[str].id                 = -1;
      dragon[str].size               = worm[str].size;
      dragon[str].effective_size     = worm[str].effective_size;
      dragon[str].color              = worm[str].color;
      dragon[str].origin             = worm[str].origin;
      dragon[str].crude_status       = UNKNOWN;
      dragon[str].status             = UNKNOWN;
      half_eye[str].type             =  0;
      half_eye[str].value            =  10.0; /* Something big. */
      
      if (IS_STONE(board[str]) && worm[str].origin == str)
	DEBUG(DEBUG_DRAGONS, 
	      "Initializing dragon from worm at %1m, size %d\n", 
	      str, worm[str].size);
    }
  memset(next_worm_list, 0, sizeof(next_worm_list));

  /* We need to reset this to avoid trouble on an empty board when
   * moves have previously been generated for a non-empty board.
   *
   * Comment: The cause of this is that make_dragons() is not called
   * for an empty board, only initialize_dragon_data(), so we never
   * reach initialize_supplementary_dragon_data().
   */
  number_of_dragons = 0;

  clear_cut_list();

  memset(black_vital_points, 0, BOARDMAX * sizeof(struct vital_eye_points));
  memset(white_vital_points, 0, BOARDMAX * sizeof(struct vital_eye_points));
}


/* Initialize the dragon2[] array. */
static void
initialize_supplementary_dragon_data(void)
{
  int str;
  int d;
  int origin;
  
  /* Give each dragon (caves excluded) an id number for indexing into
   * the dragon2 array. After this the DRAGON2 macro can be used.
   */
  number_of_dragons = 0;
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (!ON_BOARD(str))
      continue;
    origin = dragon[str].origin;
    
    if (board[str] == EMPTY)
      continue;
    
    if (dragon[origin].id == -1)
      dragon[origin].id = number_of_dragons++;
    dragon[str].id = dragon[origin].id;
  }
  
  /* Now number_of_dragons contains the number of dragons and we can
   * allocate a dragon2 array of the appropriate size. First throw
   * away the old array.
   *
   * FIXME: As a future optimization we should only allocate a new
   *       array if the old one is too small.
   */
  if (dragon2 != NULL)
    free(dragon2);
  
  dragon2 = malloc(number_of_dragons * sizeof(*dragon2));
  gg_assert(dragon2 != NULL);
  
  /* Find the origins of the dragons to establish the mapping back to
   * the board. After this the DRAGON macro can be used.
   */
  for (str = BOARDMIN; str < BOARDMAX; str++) {
    if (!ON_BOARD(str))
      continue;
    if (IS_STONE(board[str])
	&& dragon[str].origin == str) {
      DRAGON2(str).origin = str;
    }
  }
  
  /* Initialize the rest of the dragon2 data. */
  for (d = 0; d < number_of_dragons; d++) {
    dragon2[d].neighbors                = 0;
    dragon2[d].hostile_neighbors        = 0;

    dragon2[d].moyo_size	        = -1;
    dragon2[d].moyo_territorial_value   = 0.0;
    dragon2[d].safety                   = -1;
    dragon2[d].escape_route             = 0;
    dragon2[d].heye                     = NO_MOVE;
    dragon2[d].lunch                    = NO_MOVE;
    dragon2[d].surround_status          = 0;
    set_eyevalue(&dragon2[d].genus, 0, 0, 0, 0);

    dragon2[d].semeais                  = 0;
    dragon2[d].semeai_defense_code	= 0;
    dragon2[d].semeai_defense_point	= NO_MOVE;
    dragon2[d].semeai_attack_code	= 0;
    dragon2[d].semeai_attack_point	= NO_MOVE;
    dragon2[d].owl_attack_point         = NO_MOVE;
    dragon2[d].owl_attack_code          = 0;
    dragon2[d].owl_attack_certain       = 1;
    dragon2[d].owl_defense_point        = NO_MOVE;
    dragon2[d].owl_defense_code         = 0;
    dragon2[d].owl_defense_certain      = 1;
    dragon2[d].owl_status               = UNCHECKED;
    dragon2[d].owl_threat_status        = UNCHECKED;
    dragon2[d].owl_second_attack_point  = NO_MOVE;
    dragon2[d].owl_second_defense_point = NO_MOVE;
  }
  
  dragon2_initialized = 1;
}
 

/* Examine which dragons are adjacent to each other. This is
 * complicated by the fact that adjacency may involve a certain
 * amount of empty space.
 *
 * The approach we use is to extend the dragons into their
 * surrounding influence areas until they collide. We also accept
 * one step extensions into neutral regions. After having done this
 * we can look for immediate adjacencies.
 */
static void
find_neighbor_dragons()
{
  int m, n;
  int pos;
  int pos2;
  int i, j;
  int d;
  int dragons[BOARDMAX];
  int distances[BOARDMAX];
  int dist;
  int k;
  int color;

  gg_assert(dragon2_initialized);
  
  /* Initialize the arrays. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (IS_STONE(board[pos])) {
      dragons[pos] = dragon[pos].id;
      distances[pos] = 0;
    }
    else if (ON_BOARD(pos)) {
      dragons[pos] = -1;
      distances[pos] = -1;
    }
  }

  /* Expand from dist-1 to dist. Break out of the loop at the end if
   * we couldn't expand anything. Never expand more than five steps.
   */
  for (dist = 1; dist <= 5; dist++) {
    int found_one = 0;
      
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos))
	continue;
    
      if (distances[pos] != dist-1 || dragons[pos] < 0)
	continue;
      
      color = DRAGON(dragons[pos]).color;
      for (k = 0; k < 4; k++) {
	pos2 = pos + delta[k];
	
	if (!ON_BOARD1(pos2))
	  continue;
	
	/* Consider expansion from (pos) to adjacent intersection
	 * (pos2).
	 */
	if (distances[pos2] >= 0 && distances[pos2] < dist)
	  continue; /* (pos2) already occupied. */

	/* We can always expand the first step, regardless of influence. */
	if (dist == 1
	    || (whose_area(INITIAL_INFLUENCE(color), pos) == color
		&& whose_area(INITIAL_INFLUENCE(color), pos2)
		   != OTHER_COLOR(color))) {
	  /* Expansion ok. Now see if someone else has tried to
	   * expand here. In that case we indicate a collision by
	   * setting the dragon number to -2.
	   */
	  if (distances[pos2] == dist) {
	    if (dragons[pos2] != dragons[pos])
	      dragons[pos2] = -2;
	  }
	  else {
	    dragons[pos2] = dragons[pos];
	    distances[pos2] = dist;
	    found_one = 1;
	  }
	}
      }
    }
    if (!found_one)
      break;
  }
  
  if (0) {
    for (m = 0; m < board_size; m++) {
      for (n = 0; n < board_size; n++)
	fprintf(stderr, "%3d", dragons[POS(m, n)]);
      fprintf(stderr, "\n");
    }
    fprintf(stderr, "\n");
      
    for (m = 0; m < board_size; m++) {
      for (n = 0; n < board_size; n++)
	fprintf(stderr, "%3d", distances[POS(m, n)]);
      fprintf(stderr, "\n");
    }
    fprintf(stderr, "\n");
  }

  /* Now go through dragons to find neighbors. It suffices to look
   * south and east for neighbors. In the case of a collision zone
   * where dragons==-2 we set all the neighbors of this intersection
   * as adjacent to each other.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (dragons[pos] == -2) {
      int neighbors = 0;
      int adjacent[4];

      for (k = 0; k < 4; k++) {
	pos2 = pos + delta[k];

	if (ON_BOARD1(pos2) && dragons[pos2] >= 0)
	  adjacent[neighbors++] = dragons[pos2];
      }
      for (i = 0; i < neighbors; i++)
	for (j = i+1; j < neighbors; j++)
	  add_adjacent_dragons(adjacent[i], adjacent[j]);
    }
    else if (dragons[pos] >= 0) {
      if (ON_BOARD(NORTH(pos))) {
	if (dragons[NORTH(pos)] >= 0
	    && dragons[NORTH(pos)] != dragons[pos])
	  add_adjacent_dragons(dragons[pos], dragons[NORTH(pos)]);
      }
      if (ON_BOARD(EAST(pos))) {
	if (dragons[EAST(pos)] >= 0
	    && dragons[EAST(pos)] != dragons[pos])
	  add_adjacent_dragons(dragons[pos], dragons[EAST(pos)]);
      }
    }
  }
  
  if (0) {
    for (d = 0; d < number_of_dragons; d++) {
      gprintf("dragon %d at %1m:", d, dragon2[d].origin);
      for (i = 0; i < dragon2[d].neighbors; i++)
	gprintf(" %1m(%d)", dragon2[dragon2[d].adjacent[i]].origin,
		dragon2[d].adjacent[i]);
      gprintf("\n");
    }
  }
}

/* Add the dragons with id a and b as adjacent to each other. */
static void
add_adjacent_dragons(int a, int b)
{
  gg_assert(a >= 0 && a < number_of_dragons
	    && b >= 0 && b < number_of_dragons);
  if (a == b)
    return;
  add_adjacent_dragon(a, b);
  add_adjacent_dragon(b, a);
}

/* Add the dragon with id b as adjacent to a. */
static void
add_adjacent_dragon(int a, int b)
{
  int i;
  gg_assert(a >= 0 && a < number_of_dragons
	    && b >= 0 && b < number_of_dragons);
  /* If the array of adjacent dragons already is full, ignore
   * additional neighbors.
   */
  if (dragon2[a].neighbors == MAX_NEIGHBOR_DRAGONS)
    return;
  
  for (i = 0; i < dragon2[a].neighbors; i++)
    if (dragon2[a].adjacent[i] == b)
      return;

  dragon2[a].adjacent[dragon2[a].neighbors++] = b;

  if (DRAGON(a).color == OTHER_COLOR(DRAGON(b).color))
    dragon2[a].hostile_neighbors++;
}

/* A dragon is considered invincible if it satisfies either of the two
 * following conditions:
 * a) At least two distinct eyespaces without topological halfeyes,
 *    marginal vertices, or tactically critical or alive opponent strings.
 *    Furthermore there may not be an owl attack of the dragon.
 * b) At least one string which is unconditionally alive according to the
 *    unconditional_life() function in utils.c.
 *
 * For the requirement on opponent strings in a), see e.g.
 * seki:404,408,409,413,504,604,908.
 */

static int
dragon_invincible(int dr)
{
  struct eye_data *eye;
  int eye_color;
  int k;
  int pos;
  int strong_eyes = 0;
  int mx[BOARDMAX];
  
  ASSERT1(IS_STONE(board[dr]), dr);

  /* First look for invincible strings in the dragon. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (ON_BOARD(pos) && is_same_dragon(pos, dr) && worm[pos].invincible)
      return 1;
  }

  /* Can the dragon be owl attacked? */
  if (DRAGON2(dr).owl_status != UNCHECKED && DRAGON2(dr).owl_status != ALIVE)
    return 0;
  
  /* Examine the eye spaces. */
  if (board[dr] == BLACK) {
    eye = black_eye;
    eye_color = BLACK;
  }
  else {
    eye = white_eye;
    eye_color = WHITE;
  }

  memset(mx, 0, sizeof(mx));

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] == board[dr] && is_same_dragon(pos, dr)) {
      for (k = 0; k < 4; k++) {
	int pos2 = pos + delta[k];
	if (ON_BOARD(pos2)
	    && eye[pos2].color == eye_color
	    && eye[pos2].origin != NO_MOVE) {
	  if (eye[pos2].marginal
	      || is_halfeye(half_eye, pos2))
	    mx[eye[pos2].origin] = 2; /* bad eye */
	  else if (mx[eye[pos2].origin] == 0)
	    mx[eye[pos2].origin] = 1; /* good eye */
	  
	  if (board[pos2] == OTHER_COLOR(board[dr])
	      && (!attack(pos2, NULL) || find_defense(pos2, NULL)))
	    mx[eye[pos2].origin] = 2; /* bad eye */
	}
      }
    }
  }

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    /* Necessary to check eye margins here since the loop above only
     * considers margins which are directly adjacent to some stone of
     * the dragon.
     */
    if (mx[pos] == 1
	&& eye[pos].msize == 0)
      strong_eyes++;
  }

  if (strong_eyes >= 2)
    return 1;

  return 0;
}


/* A dragon looks inessential if it satisfies all of
 * 1. Is a single string.
 * 2. Is not owl substantial.
 *
 * FIXME: Probably need a better definition of INESSENTIAL dragons.
 *        There are cases where a string is owl insubstantial
 *        yet allowing it to be captured greatly weakens our
 *        position.
 */
static int
dragon_looks_inessential(int origin)
{
#if 0
  int d;
  int k;
#endif
  
  if (dragon[origin].size != worm[origin].size)
    return 0;

  if (owl_substantial(origin))
    return 0;

#if 0
  /* This is a proposed modification which solves 13x13:72 but
   * breaks buzco:5. It adds the two requirements:
   *
   * 3. Has no opponent neighbor with status better than DEAD.
   * 4. Has no opponent neighbor with escape value bigger than 0.
   *
   * This probably needs to be revised before it's enabled.
   */
  for (k = 0; k < DRAGON2(origin).neighbors; k++) {
    d = DRAGON2(origin).adjacent[k];
    if (DRAGON(d).color != board[origin]
	&& (DRAGON(d).status != DEAD
	    || dragon2[d].escape_route > 0))
      return 0;
  }
#endif
  
  return 1;
}


/* Report which stones are alive if it's (color)'s turn to move. I.e.
 * critical stones belonging to (color) are considered alive.
 * A stone is dead resp. critical if the tactical reading code _or_ the
 * owl code thinks so.
 */
static void
get_alive_stones(int color, signed char safe_stones[BOARDMAX])
{
  int d;
  get_lively_stones(color, safe_stones);
  for (d = 0; d < number_of_dragons; d++) {
    if (dragon2[d].safety == DEAD
	|| (dragon2[d].safety == CRITICAL
	    && board[dragon2[d].origin] == OTHER_COLOR(color))) {
      mark_dragon(dragon2[d].origin, safe_stones, 0);
    }
  }
}


/* If the opponent's last move is a dead dragon, this is called a
 * *thrashing dragon*. We must be careful because the opponent may be
 * trying to trick us, so even though GNU Go thinks the stone is dead,
 * we should consider attacking it, particularly if we are ahead.
 *
 * This function determines whether the last played move is part of a
 * dead dragon. It also looks for dead friendly neighbors of the
 * thrashing dragon, which are also considered as thrashing. The
 * stones of the primary thrashing dragon are marked by 1 in the
 * thrashing_stone[] array and its neighbors are marked by 2.
 * Neighbors of neighbors are marked 3, and so on, up to at most
 * distance 5.
 */
static void
identify_thrashing_dragons()
{
  int k;
  int dist;
  int last_move;
  int color;

  thrashing_dragon = 0;
  memset(thrashing_stone, 0, sizeof(thrashing_stone));

  last_move = get_last_move();
  if (last_move == NO_MOVE
      || dragon[last_move].status != DEAD)
    return;

  thrashing_dragon = dragon[last_move].origin;
  DEBUG(DEBUG_DRAGONS, "thrashing dragon found at %1m\n", thrashing_dragon);
  mark_dragon(thrashing_dragon, thrashing_stone, 1);
  color = board[thrashing_dragon];
  
  for (dist = 1; dist < 5; dist++) {
    int pos;
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (board[pos] != color
	  || dragon[pos].origin != pos
	  || thrashing_stone[pos] != dist)
	continue;
      
      for (k = 0; k < DRAGON2(pos).neighbors; k++) {
	int d = DRAGON2(pos).adjacent[k];
	if (DRAGON(d).color == color
	    && DRAGON(d).status == DEAD
	    && thrashing_stone[dragon2[d].origin] == 0) {
	  DEBUG(DEBUG_DRAGONS,
		"neighbor at distance %d of thrashing dragon found at %1m\n",
		dist + 1, DRAGON(d).origin);
	  mark_dragon(DRAGON(d).origin, thrashing_stone,
		      (signed char)(dist + 1));
	}
      }
    }
  }
}


static void
set_dragon_strengths(const signed char safe_stones[BOARDMAX],
    		     float strength[BOARDMAX])
{
  int ii;
  for (ii = BOARDMIN; ii < BOARDMAX; ii++)
    if (ON_BOARD(ii)) {
      if (safe_stones[ii]) {
	ASSERT1(IS_STONE(board[ii]), ii);
	strength[ii] = DEFAULT_STRENGTH
	  	       * (1.0 - 0.3 * DRAGON2(ii).weakness_pre_owl);
      }
      else
	strength[ii] = 0.0;
    }
}

/* Marks all inessential stones with INFLUENCE_SAFE_STONE, leaves
 * everything else unchanged.
 */
void
mark_inessential_stones(int color, signed char safe_stones[BOARDMAX])
{
  int ii;
  for (ii = BOARDMIN; ii < BOARDMAX; ii++)
    if (IS_STONE(board[ii])
	&& (DRAGON2(ii).safety == INESSENTIAL
	    || (worm[ii].inessential
	        /* FIXME: Why is the check below needed?
		 * Why does it use .safety, not .status? /ab
		 */
		&& ((DRAGON2(ii).safety != DEAD
		     && DRAGON2(ii).safety != TACTICALLY_DEAD
		     && DRAGON2(ii).safety != CRITICAL)
		    || (DRAGON2(ii).safety == CRITICAL
			&& board[ii] == color)))))
      safe_stones[ii] = INFLUENCE_SAFE_STONE;
}

void
set_strength_data(int color, signed char safe_stones[BOARDMAX],
    		  float strength[BOARDMAX])
{
  gg_assert(IS_STONE(color) || color == EMPTY);

  get_alive_stones(color, safe_stones);
  set_dragon_strengths(safe_stones, strength);
  mark_inessential_stones(color, safe_stones);
}


void
compute_dragon_influence()
{
  signed char safe_stones[BOARDMAX];
  float strength[BOARDMAX];

  set_strength_data(BLACK, safe_stones, strength);
  compute_influence(BLACK, safe_stones, strength, &initial_black_influence,
                    NO_MOVE, "initial black influence, dragons known");
  break_territories(BLACK, &initial_black_influence, 1, NO_MOVE);

  set_strength_data(WHITE, safe_stones, strength);
  compute_influence(WHITE, safe_stones, strength, &initial_white_influence,
                    NO_MOVE, "initial white influence, dragons known");
  break_territories(WHITE, &initial_white_influence, 1, NO_MOVE);
}


/* Compute dragon's genus, possibly excluding one given eye.  To
 * compute full genus, just set `eye_to_exclude' to NO_MOVE.
 */
void
compute_dragon_genus(int d, struct eyevalue *genus, int eye_to_exclude)
{
  int pos;
  int dr;

  ASSERT1(IS_STONE(board[d]), d);
  gg_assert(eye_to_exclude == NO_MOVE || ON_BOARD1(eye_to_exclude));

  set_eyevalue(genus, 0, 0, 0, 0);

  if (board[d] == BLACK) {
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos))
	continue;

      if (black_eye[pos].color == BLACK
	  && black_eye[pos].origin == pos
	  && (eye_to_exclude == NO_MOVE
	      || black_eye[eye_to_exclude].origin != pos)
	  && find_eye_dragons(pos, black_eye, BLACK, &dr, 1) == 1
	  && is_same_dragon(dr, d)) {
	TRACE("eye at %1m (%s) found for dragon at %1m--augmenting genus\n",
	      pos, eyevalue_to_string(&black_eye[pos].value), dr);

	if (eye_to_exclude == NO_MOVE
	    && (eye_move_urgency(&black_eye[pos].value)
		> eye_move_urgency(genus)))
	  DRAGON2(d).heye = black_vital_points[pos].defense_points[0];

	add_eyevalues(genus, &black_eye[pos].value, genus);
      }
    }
  }
  else {
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (!ON_BOARD(pos))
	continue;

      if (white_eye[pos].color == WHITE
	  && white_eye[pos].origin == pos
	  && (eye_to_exclude == NO_MOVE
	      || white_eye[eye_to_exclude].origin != pos)
	  && find_eye_dragons(pos, white_eye, WHITE, &dr, 1) == 1
	  && is_same_dragon(dr, d)) {
	TRACE("eye at %1m (%s) found for dragon at %1m--augmenting genus\n",
	      pos, eyevalue_to_string(&white_eye[pos].value), dr);

	if (eye_to_exclude == NO_MOVE
	    && (eye_move_urgency(&white_eye[pos].value)
		> eye_move_urgency(genus)))
	  DRAGON2(d).heye = white_vital_points[pos].defense_points[0];

	add_eyevalues(genus, &white_eye[pos].value, genus);
      }
    }
  }
}


/* Try to determine whether topologically false and half eye points
 * contribute to territory even if the eye doesn't solidify. The purpose
 * is to be able to distinguish between, e.g., these positions:
 *
 * |.OOOOO       |.OOOOO
 * |.O.XXO       |.O.OXO
 * |OOX.XO       |OOX.XO
 * |O*XXXO  and  |O*XXXO
 * |OX.XOO       |OX.XOO
 * |X.XOO.       |X.XOO.
 * |.XXO..       |.XXO..
 * +------       +------
 * 
 * In the left one the move at * is a pure dame point while in the
 * right one it is worth one point of territory for either player.
 *
 * In general the question is whether a topologically false eye vertex
 * counts as territory or not and the answer depends on whether each
 * string adjoining the eye is externally connected to at least one
 * proper eye.
 *
 * This function loops over the topologically false and half eye
 * vertices and calls connected_to_eye() for each adjoining string to
 * determine whether they all have external connection to an eye. The
 * result is stored in the false_eye_territory[] array.
 */
static void
analyze_false_eye_territory(void)
{
  int pos;
  int color;
  int eye_color;
  struct eye_data *eye;
  int k;

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    
    false_eye_territory[pos] = 0;

    /* The analysis only applies to false and half eyes. */
    if (half_eye[pos].type == 0)
      continue;

    /* Determine the color of the eye. */
    if (white_eye[pos].color == WHITE) {
      color = WHITE;
      eye_color = WHITE;
      eye = white_eye;
    }
    else if (black_eye[pos].color == BLACK) {
      color = BLACK;
      eye_color = BLACK;
      eye = black_eye;
    }
    else
      continue;

    /* Make sure we have a "closed" position. Positions like
     *
     * |XXXXXX.
     * |OOOOOXX
     * |.O.O*..
     * +-------
     *
     * disqualify without further analysis. (* is a false eye vertex)
     */
    for (k = 0; k < 4; k++)
      if (ON_BOARD(pos + delta[k])
	  && board[pos + delta[k]] != color
	  && eye[pos + delta[k]].color != eye_color)
	break;
     
    if (k < 4)
      continue;

    /* Check that all adjoining strings have external connection to an
     * eye.
     */
    for (k = 0; k < 4; k++)
      if (ON_BOARD(pos + delta[k])
	  && board[pos + delta[k]] == color
	  && !connected_to_eye(pos, pos + delta[k], color, eye_color, eye))
	break;

    if (k == 4) {
      false_eye_territory[pos] = 1;
      if (0)
	gprintf("False eye territory at %1m\n", pos);
    }
  }

  /* FIXME: This initialization doesn't really belong here but must be
   *        done somewhere within examine_position().
   *        The array is eventually filled by the endgame() function.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos))
      forced_backfilling_moves[pos] = 0;
}

/* 
 * This function (implicitly) finds the connected set of strings of a
 * dragon, starting from (str) which is next to the analyzed halfeye
 * at (pos). Strings are for this purpose considered connected if and
 * only if they have a common liberty, which is not allowed to be the
 * half eye itself or one of its diagonal neighbors. For these strings
 * it is examined whether their liberties are parts of eyespaces worth
 * at least two halfeyes (again not counting the eyespace at (pos)).
 *
 * The real work is done by the recursive function
 * connected_to_eye_recurse() below.
 */
static int
connected_to_eye(int pos, int str, int color, int eye_color,
		 struct eye_data *eye)
{
  signed char mx[BOARDMAX];
  signed char me[BOARDMAX];
  int k;
  int halfeyes;

  /* mx marks strings and liberties which have already been investigated.
   * me marks the origins of eyespaces which have already been counted.
   * Start by marking (pos) and the surrounding vertices in mx.
   */
  memset(mx, 0, sizeof(mx));
  memset(me, 0, sizeof(me));
  mx[pos] = 1;
  for (k = 0; k < 8; k++)
    if (ON_BOARD(pos + delta[k]))
      mx[pos + delta[k]] = 1;

  halfeyes = 0;
  connected_to_eye_recurse(pos, str, color, eye_color, eye, mx, me, &halfeyes);

  if (halfeyes >= 2)
    return 1;
  
  return 0;
}

/* Recursive helper for connected_to_eye(). Stop searching when we
 * have found at least two halfeyes.
 */
static void
connected_to_eye_recurse(int pos, int str, int color, int eye_color,
			 struct eye_data *eye, signed char *mx,
			 signed char *me, int *halfeyes)
{
  int liberties;
  int libs[MAXLIBS];
  int r;
  int k;

  mark_string(str, mx, 1);
  liberties = findlib(str, MAXLIBS, libs);

  /* Search the liberties of (str) for eyespaces. */
  for (r = 0; r < liberties; r++) {
    if (eye[libs[r]].color == eye_color
	&& libs[r] != pos
	&& !me[eye[libs[r]].origin]) {
      me[eye[libs[r]].origin] = 1;
      *halfeyes += (min_eyes(&eye[libs[r]].value)
		    + max_eyes(&eye[libs[r]].value));
    }
  }

  if (*halfeyes >= 2)
    return;

  /* Search for new strings in the same dragon with a liberty in
   * common with (str), and recurse.
   */
  for (r = 0; r < liberties; r++) {
    if (mx[libs[r]])
      continue;
    mx[libs[r]] = 1;
    for (k = 0; k < 4; k++) {
      if (ON_BOARD(libs[r] + delta[k])
	  && board[libs[r] + delta[k]] == color
	  && is_same_dragon(str, libs[r] + delta[k])
	  && !mx[libs[r] + delta[k]])
	connected_to_eye_recurse(pos, libs[r] + delta[k], color, eye_color,
				 eye, mx, me, halfeyes);
      if (*halfeyes >= 2)
	return;
    }
  }
}

/* print status info on all dragons. (Can be invoked from gdb) 
 */
void 
show_dragons(void)
{
  int pos;
  int k;

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    struct worm_data *w = &(worm[pos]);
    if (!IS_STONE(board[pos]))
      continue;

    if (w->origin == pos) {
      gprintf("%1m : (dragon %1m) %s string of size %d (%f), genus %d: (%d,%d,%d,%d)",
	      pos, dragon[pos].origin,
	      color_to_string(board[pos]),
	      w->size,
	      w->effective_size,
	      w->genus,
	      w->liberties,
	      w->liberties2,
	      w->liberties3,
	      w->liberties4);
      if (w->cutstone == 1)
	gprintf("%o - is a potential cutting stone\n");
      else if (w->cutstone == 2)
	gprintf("%o - is a cutting stone\n");
      else
	gprintf("%o\n");
      
      if (w->cutstone2 > 0)
	gprintf("- cutstone2 = %d\n", w->cutstone2);
      
      for (k = 0; k < MAX_TACTICAL_POINTS; k++) {
	if (w->attack_codes[k] == 0)
	  break;
	gprintf("- attackable at %1m, attack code = %d\n",
		w->attack_points[k], w->attack_codes[k]);
      }
      
      for (k = 0; k < MAX_TACTICAL_POINTS; k++) {
	if (w->defense_codes[k] == 0)
	  break;
	gprintf("- defendable at %1m, defend code = %d\n",
		w->defense_points[k], w->defense_codes[k]);
      }
      
      for (k = 0; k < MAX_TACTICAL_POINTS; k++) {
	if (w->attack_threat_codes[k] == 0)
	  break;
	gprintf("- attack threat at %1m, attack threat code = %d\n",
		w->attack_threat_points[k], w->attack_threat_codes[k]);
      }
      
      for (k = 0; k < MAX_TACTICAL_POINTS; k++) {
	if (w->defense_threat_codes[k] == 0)
	  break;
	gprintf("- defense threat at %1m, defense threat code = %d\n",
		w->defense_threat_points[k], w->defense_threat_codes[k]);
      }
      
      if (w->lunch != NO_MOVE)
	gprintf("... adjacent worm %1m is lunch\n", w->lunch);
      
      if (w->inessential)
	gprintf("- is inessential\n");
      
      if (w->invincible)
	gprintf("- is invincible\n");
      
      if (is_ko_point(pos))
	gprintf("- is a ko stone\n");
    }
  }
    
  gprintf("%o\n");
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    struct dragon_data *dd = &(dragon[pos]);
    struct dragon_data2 *d2;
    
    if (!IS_STONE(board[pos]))
      continue;
    
    d2 = &(dragon2[dd->id]);
    
    if (dd->origin == pos) {
      gprintf("%1m : %s dragon size %d (%f), genus %s, escape factor %d, crude status %s, status %s, moyo size %d, moyo territory value %f, safety %s, weakness pre owl %f, weakness %f",
	      pos,
	      board[pos] == BLACK ? "B" : "W",
	      dd->size,
	      dd->effective_size,
	      eyevalue_to_string(&d2->genus),
	      d2->escape_route,
	      status_to_string(dd->crude_status),
	      status_to_string(dd->status),
	      d2->moyo_size,
	      d2->moyo_territorial_value,
	      status_to_string(d2->safety),
	      d2->weakness_pre_owl,
	      d2->weakness);
      gprintf(", owl status %s\n", status_to_string(d2->owl_status));
      if (d2->owl_status == CRITICAL) {
	gprintf("... owl attackable at %1m, code %d\n",
		d2->owl_attack_point, d2->owl_attack_code);
	gprintf("... owl defendable at %1m, code %d\n",
		d2->owl_defense_point, d2->owl_defense_code);
      }
      if (dd->status == CRITICAL && d2->semeais) {
	if (d2->semeai_defense_point)
	  gprintf("... semeai defense move at %1m, result code %s\n",
		  d2->semeai_defense_point,
		  result_to_string(d2->semeai_defense_code));
	if (d2->semeai_attack_point)
	  gprintf("... semeai attack move at %1m, result code %s\n",
		  d2->semeai_attack_point,
		  result_to_string(d2->semeai_attack_code));
      }
      gprintf("... neighbors");
      for (k = 0; k < d2->neighbors; k++) {
	int d = d2->adjacent[k];
	gprintf(" %1m", dragon2[d].origin);
      }
      gprintf("\n");
      if (d2->lunch != NO_MOVE)
	gprintf("... adjacent worm %1m is lunch\n", d2->lunch);
    }
  }
}


static int new_dragon_origins[BOARDMAX];

/* Compute new dragons, e.g. after having made a move. This will not
 * affect any global state.
 */
void
compute_new_dragons(int dragon_origins[BOARDMAX])
{
  int pos;
  int saved_cutting_points[BOARDMAX];

  /* This is currently necessary in order not to mess up the
   * worm[].cutstone2 field. See cutstone2_helper in
   * patterns/helpers.c. On the other hand it shouldn't be very
   * interesting to recompute dragons in the original position.
   */
  gg_assert(stackp > 0);
  
  memcpy(saved_cutting_points, cutting_points, sizeof(cutting_points));
  memset(cutting_points, 0, sizeof(cutting_points));
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos)) {
      if (board[pos] == EMPTY)
	new_dragon_origins[pos] = NO_MOVE;
      else
	new_dragon_origins[pos] = find_origin(pos);
    }
  
  find_cuts();
  find_connections();

  memcpy(cutting_points, saved_cutting_points, sizeof(cutting_points));
  memcpy(dragon_origins, new_dragon_origins, sizeof(new_dragon_origins));
}


/* This gets called if we are trying to compute dragons outside of
 * make_dragons(), typically after a move has been made.
 */
static void
join_new_dragons(int d1, int d2)
{
  int pos;
  /* Normalize dragon coordinates. */
  d1 = new_dragon_origins[d1];
  d2 = new_dragon_origins[d2];

  /* If d1 and d2 are the same dragon, we do nothing. */
  if (d1 == d2)
    return;

  ASSERT1(board[d1] == board[d2], d1);
  ASSERT1(IS_STONE(board[d1]), d1);

  /* Don't bother to do anything fancy with dragon origins. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos) && new_dragon_origins[pos] == d2)
      new_dragon_origins[pos] = d1;
}

/* 
 * join_dragons amalgamates the dragon at (d1) to the
 * dragon at (d2).
 */

void 
join_dragons(int d1, int d2)
{
  int ii;
  int origin; /* new origin */

  /* If not called from make_dragons(), we don't work on the main
   * dragon[] array.
   */
  if (stackp > 0) {
    join_new_dragons(d1, d2);
    return;
  }
  
  /* Normalize dragon coordinates. */
  d1 = dragon[d1].origin;
  d2 = dragon[d2].origin;

  /* If d1 and d2 are the same dragon, we do nothing. */
  if (d1 == d2)
    return;
  
  ASSERT1(board[d1] == board[d2], d1);
  gg_assert(dragon2_initialized == 0);
  ASSERT1(IS_STONE(board[d1]), d1);

  /* We want to have the origin pointing to the largest string of
   * the dragon.  If this is not unique, we take the "upper
   * leftmost" one.
   */
  if (worm[d1].size > worm[d2].size
      || (worm[d1].size == worm[d2].size
	  && d1 < d2)) {
    origin = d1;
    DEBUG(DEBUG_DRAGONS, "joining dragon at %1m to dragon at %1m\n", d2, d1);
  }
  else {
    origin = d2;
    DEBUG(DEBUG_DRAGONS, "joining dragon at %1m to dragon at %1m\n", d1, d2);
  }
  
  dragon[origin].size  = dragon[d2].size + dragon[d1].size;
  dragon[origin].effective_size  = (dragon[d2].effective_size
				    + dragon[d1].effective_size);

  /* Join the second next_worm_in_dragon chain at the end of the first one. */
  {
    int last_worm_origin_dragon = origin;
    while (next_worm_list[last_worm_origin_dragon] != NO_MOVE)
      last_worm_origin_dragon = next_worm_list[last_worm_origin_dragon];
    if (origin == d1)
      next_worm_list[last_worm_origin_dragon] = d2;
    else
      next_worm_list[last_worm_origin_dragon] = d1;
  }

  for (ii = BOARDMIN; ii < BOARDMAX; ii++) {
    if (ON_BOARD(ii)
	&& (dragon[ii].origin == d1 || dragon[ii].origin == d2))
      dragon[ii].origin = origin;
  }
}



/*
 * compute_crude_status(pos) tries to determine whether the dragon
 * at (pos) is ALIVE, DEAD, or UNKNOWN. The algorithm is not perfect
 * and can give incorrect answers.
 *
 * The dragon is judged alive if its genus is >1. It is judged dead if
 * the genus is <2, it has no escape route, and no adjoining string can
 * be easily captured. Otherwise it is judged UNKNOWN.  */

static enum dragon_status
compute_crude_status(int pos)
{
  /* FIXME: We lose information when constructing true_genus. This
   * code can be improved.
   */
  struct eyevalue *genus = &DRAGON2(pos).genus;
  int true_genus = max_eyes(genus) + min_eyes(genus);
  int lunch = DRAGON2(pos).lunch;

  gg_assert(dragon2_initialized);
  
  /* If it has two sure eyes, everything is just dandy. */
  if (true_genus > 3)
    return ALIVE;

  /* If the dragon consists of one worm, there is an attack, but 
   * no defense and there is less than one eye and one half eye,
   * the situation is hopeless.
   */
  if (dragon[pos].size == worm[pos].size
      && worm[pos].attack_codes[0] != 0 
      && worm[pos].defense_codes[0] == 0
      && true_genus < 3)
    return DEAD;
  
  if (lunch != NO_MOVE
      && true_genus < 3
      && worm[lunch].defense_codes[0] != 0
      && DRAGON2(pos).escape_route < 5)
    if (true_genus == 2 || worm[lunch].size > 2)
      return CRITICAL;

  if (lunch != NO_MOVE
      && true_genus >= 3)
    return ALIVE;

  if (lunch == NO_MOVE || worm[lunch].cutstone < 2) {
    if (true_genus < 3
	&& DRAGON2(pos).escape_route == 0
	&& DRAGON2(pos).moyo_size < 5)
      return DEAD;

    if (true_genus == 3
	&& DRAGON2(pos).escape_route < 5)
      return CRITICAL;
  }

  if (DRAGON2(pos).moyo_territorial_value > 9.99)
    return ALIVE;
  
  return UNKNOWN;
}


/* The dragon escape measure. This is defined as follows.
 *   
 * Let a PATH be a sequence of adjacent intersections that do nowhere
 * touch or include an opponent stone or touch the border. It may
 * include friendly stones and those are allowed to touch opponent
 * stones or the border). Let a DISTANCE N INTERSECTION be an
 * intersection connected to a dragon by a path of length N, but by no
 * shorter path. The connection of the path to the dragon may either
 * be by direct adjacency or, in the first step, diagonally if both
 * adjoining intersections are empty.
 *
 * It is assumed that each intersection has an escape value, which
 * would typically depend on influence and (preliminary) dragon
 * status. We define the escape potential as the sum of the escape
 * values over the distance four intersections of the dragon.
 * 
 * Example of distance N intersections, 1 <= N <= 4:
 * 
 * . . . . . . . . .    . . . . . . . . .
 * . . . . . X . . O    . . . . . X . . O
 * . . X . . . . . O    . . X . 2 . 4 . O
 * X . . . . . . . .    X . . 1 1 2 3 4 .
 * X O . O . . . . O    X O 1 O 1 2 3 4 O
 * X O . O . . . . .    X O 1 O 1 . 4 . .
 * X O . . . X . O O    X O 1 . . X . . O
 * . . . X . . . . .    . 1 . X . . . . .
 * X . . . . X . . .    X . . . . X . . .
 * . . . . . . . . .    . . . . . . . . .
 *
 * Additionally, a path may not pass a connection inhibited
 * intersection.
 */

#define ENQUEUE(pos) (queue[queue_end++] = (pos),\
		      mx[pos] = 1)

/* Compute the escape potential described above. The dragon is marked
 * in the goal array.
 */
int
dragon_escape(signed char goal[BOARDMAX], int color,
	      signed char escape_value[BOARDMAX])
{
  int ii;
  int k;
  static int mx[BOARDMAX];
  static int mx_initialized = 0;
  int queue[MAX_BOARD * MAX_BOARD];
  int queue_start = 0;
  int queue_end = 0;
  int other = OTHER_COLOR(color);
  int distance;
  int escape_potential = 0;

  gg_assert(IS_STONE(color));
  
  if (!mx_initialized) {
    memset(mx, 0, sizeof(mx));
    mx_initialized = 1;
  }

  /* Enter the stones of the dragon in the queue. */
  for (ii = BOARDMIN; ii < BOARDMAX; ii++)
    if (ON_BOARD(ii) && goal[ii])
      ENQUEUE(ii);
  
  /* Find points at increasing distances from the dragon. At distance
   * four, sum the escape values at those points to get the escape
   * potential.
   */
  for (distance = 0; distance <= 4; distance++) {
    int save_queue_end = queue_end;
    while (queue_start < save_queue_end) {
      ii = queue[queue_start];
      queue_start++;

      /* Do not pass connection inhibited intersections. */
      if (cut_possible(ii, OTHER_COLOR(color)))
	continue;
      if (distance == 4)
	escape_potential += escape_value[ii];
      else {
	if (ON_BOARD(SOUTH(ii))
	    && !mx[SOUTH(ii)]
	    && (board[SOUTH(ii)] == color
		|| (board[SOUTH(ii)] == EMPTY
		    && ON_BOARD(SE(ii)) && board[SE(ii)] != other
		    && ON_BOARD(SS(ii)) && board[SS(ii)] != other
		    && ON_BOARD(SW(ii)) && board[SW(ii)] != other)))
	  ENQUEUE(SOUTH(ii));
	
	if (ON_BOARD(WEST(ii))
	    && !mx[WEST(ii)]
	    && (board[WEST(ii)] == color
		|| (board[WEST(ii)] == EMPTY
		    && ON_BOARD(SW(ii)) && board[SW(ii)] != other
		    && ON_BOARD(WW(ii)) && board[WW(ii)] != other
		    && ON_BOARD(NW(ii)) && board[NW(ii)] != other)))
	  ENQUEUE(WEST(ii));
	
	if (ON_BOARD(NORTH(ii))
	    && !mx[NORTH(ii)]
	    && (board[NORTH(ii)] == color
		|| (board[NORTH(ii)] == EMPTY
		    && ON_BOARD(NW(ii)) && board[NW(ii)] != other
		    && ON_BOARD(NN(ii)) && board[NN(ii)] != other
		    && ON_BOARD(NE(ii)) && board[NE(ii)] != other)))
	  ENQUEUE(NORTH(ii));
	
	if (ON_BOARD(EAST(ii))
	    && !mx[EAST(ii)]
	    && (board[EAST(ii)] == color
		|| (board[EAST(ii)] == EMPTY
		    && ON_BOARD(NE(ii)) && board[NE(ii)] != other
		    && ON_BOARD(EE(ii)) && board[EE(ii)] != other
		    && ON_BOARD(SE(ii)) && board[SE(ii)] != other)))
	  ENQUEUE(EAST(ii));
	
	/* For distance one intersections, allow kosumi to move out. I.e.
	 *
	 * ??..
	 * X.*.
	 * ?O.?
	 * ??X?
	 *
	 */
	if (distance == 0) {
	  if (board[SOUTH(ii)] == EMPTY
	      && board[WEST(ii)] == EMPTY
	      && !mx[SW(ii)]
	      && (board[SW(ii)] == color
		  || (board[SW(ii)] == EMPTY
		      && ON_BOARD(SOUTH(SW(ii)))
		      && board[SOUTH(SW(ii))] != other
		      && ON_BOARD(WEST(SW(ii)))
		      && board[WEST(SW(ii))] != other)))
	    ENQUEUE(SW(ii));
		      
	  if (board[WEST(ii)] == EMPTY
	      && board[NORTH(ii)] == EMPTY
	      && !mx[NW(ii)]
	      && (board[NW(ii)] == color
		  || (board[NW(ii)] == EMPTY
		      && ON_BOARD(WEST(NW(ii)))
		      && board[WEST(NW(ii))] != other
		      && ON_BOARD(NORTH(NW(ii)))
		      && board[NORTH(NW(ii))] != other)))
	    ENQUEUE(NW(ii));
		      
	  if (board[NORTH(ii)] == EMPTY
	      && board[EAST(ii)] == EMPTY
	      && !mx[NE(ii)]
	      && (board[NE(ii)] == color
		  || (board[NE(ii)] == EMPTY
		      && ON_BOARD(NORTH(NE(ii)))
		      && board[NORTH(NE(ii))] != other
		      && ON_BOARD(EAST(NE(ii)))
		      && board[EAST(NE(ii))] != other)))
	    ENQUEUE(NE(ii));
		      
	  if (board[EAST(ii)] == EMPTY
	      && board[SOUTH(ii)] == EMPTY
	      && !mx[SE(ii)]
	      && (board[SE(ii)] == color
		  || (board[SE(ii)] == EMPTY
		      && ON_BOARD(EAST(SE(ii)))
		      && board[EAST(SE(ii))] != other
		      && ON_BOARD(SOUTH(SE(ii)))
		      && board[SOUTH(SE(ii))] != other)))
	    ENQUEUE(SE(ii));
	}
      }
    }
  }

  /* Reset used mx cells. */
  for (k = 0; k < queue_end; k++) {
    /* The assertion fails if the same element should have been queued
     * twice, which might happen if ENQUEUE() is called without
     * checking mx[].
     */
    ASSERT1(mx[queue[k]] == 1, queue[k]);
    mx[queue[k]] = 0;
  }

  return escape_potential;
}

/* Wrapper to call the function above and compute the escape potential
 * for the dragon at (pos).
 */
static int
compute_escape(int pos, int dragon_status_known)
{
  int ii;
  signed char goal[BOARDMAX];
  signed char escape_value[BOARDMAX];
  signed char safe_stones[BOARDMAX];

  ASSERT1(IS_STONE(board[pos]), pos);

  for (ii = BOARDMIN; ii < BOARDMAX; ii++)
    if (ON_BOARD(ii))
      goal[ii] = is_same_dragon(ii, pos);

  /* Compute escape_value array.  Points are awarded for moyo (4),
   * area (2) or EMPTY (1).  Values may change without notice.
   */
  get_lively_stones(OTHER_COLOR(board[pos]), safe_stones);
  compute_escape_influence(board[pos], safe_stones, NULL, 0, escape_value);

  /* If we can reach a live group, award 6 points. */
  for (ii = BOARDMIN; ii < BOARDMAX; ii++) {
    if (!ON_BOARD(ii))
      continue;

    if (dragon_status_known) {
      if (dragon[ii].crude_status == ALIVE)
	escape_value[ii] = 6;
      else if (dragon[ii].crude_status == UNKNOWN
	       && (DRAGON2(ii).escape_route > 5
		   || DRAGON2(ii).moyo_size  > 5))
	escape_value[ii] = 4;
    }
    else {
      if (board[ii] == board[pos]
	  && !goal[ii]
	  && worm[ii].attack_codes[0] == 0)
	escape_value[ii] = 2;
    }
  }
  
  return dragon_escape(goal, board[pos], escape_value);
}

/*
 * Sum up the surrounding moyo sizes for each dragon. For this
 * we retrieve the moyo data stored in influence_data (*q) (which must
 * have been computed previously) from the influence module.
 * We set dragon2[].moyo_size and .moyo_value if it is smaller than the 
 * current entry.
 *
 * Currently this is implemented differently depending on whether
 * experimental connections are used or not. The reason why this is
 * needed is that most of the B patterns in conn.db are disabled for
 * experimental connections, which may cause the moyo segmentation to
 * pass through cutting points between dragons, making the surrounding
 * moyo size mostly useless. Instead we only use the part of the
 * surrounding moyo which is closest to some worm of the dragon.
 */
static void
compute_surrounding_moyo_sizes(const struct influence_data *q)
{
  int pos;
  int d;
  int k;
  int moyo_color;
  float moyo_sizes[BOARDMAX];
  float moyo_values[BOARDMAX];
    

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    moyo_sizes[pos] = 0.0;
    moyo_values[pos] = 0.0;
  }
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    moyo_color = whose_moyo_restricted(q, pos);
    
    if (moyo_color == board[pos])
      continue;
    
    if (moyo_color == WHITE) {
      for (k = 0; k < number_close_white_worms[pos]; k++) {
	int w = close_white_worms[pos][k];
	int dr = dragon[w].origin;
	
	moyo_sizes[dr] += 1.0 / number_close_white_worms[pos];
	moyo_values[dr] += (gg_min(influence_territory(q, pos, WHITE), 1.0)
			    / number_close_white_worms[pos]);
      }
    }
    
    if (moyo_color == BLACK) {
      for (k = 0; k < number_close_black_worms[pos]; k++) {
	int w = close_black_worms[pos][k];
	int dr = dragon[w].origin;
	
	moyo_sizes[dr] += 1.0 / number_close_black_worms[pos];
	moyo_values[dr] += (gg_min(influence_territory(q, pos, BLACK), 1.0)
			    / number_close_black_worms[pos]);
      }
    }
  }
  
  for (d = 0; d < number_of_dragons; d++) {
    int this_moyo_size = (int) moyo_sizes[dragon2[d].origin];
    float this_moyo_value = moyo_values[dragon2[d].origin];
    
    if (this_moyo_size < dragon2[d].moyo_size) {
      dragon2[d].moyo_size = this_moyo_size;
      dragon2[d].moyo_territorial_value = this_moyo_value;
    }
  }
}


static struct interpolation_data moyo_value2weakness =
  { 5, 0.0, 15.0, {1.0, 0.65, 0.3, 0.15, 0.05, 0.0}};
static struct interpolation_data escape_route2weakness =
  { 5, 0.0, 25.0, {1.0, 0.6, 0.3, 0.1, 0.05, 0.0}};
static struct interpolation_data genus2weakness =
  { 6, 0.0, 3.0, {1.0, 0.95, 0.8, 0.5, 0.2, 0.1, 0.0}};

float
crude_dragon_weakness(int safety, struct eyevalue *genus, int has_lunch,
    		      float moyo_value, float escape_route)
{
  /* FIXME: We lose information when constructing true_genus. This
   * code can be improved.
   */
  float true_genus = 0.5 * (max_eyes(genus) + min_eyes(genus)
      			    + (has_lunch != 0));
  float weakness_value[3];
  float weakness;
  int i, j;

  if (safety == INVINCIBLE || safety == INESSENTIAL)
    return 0.0;
  if (safety == TACTICALLY_DEAD || safety == DEAD || safety == CRITICAL)
    return 1.0;

  weakness_value[0] = gg_interpolate(&moyo_value2weakness, moyo_value);
  weakness_value[1] = gg_interpolate(&escape_route2weakness, escape_route);
  weakness_value[2] = gg_interpolate(&genus2weakness, true_genus);

  DEBUG(DEBUG_DRAGONS,
	"  moyo value %f -> %f, escape %f -> %f, eyes %f -> %f\n",
	moyo_value, weakness_value[0],
	escape_route, weakness_value[1],
	true_genus, weakness_value[2]);

  for (i = 0; i < 3; i++)
    for (j = i + 1; j < 3; j++)
      if (weakness_value[j] < weakness_value[i]) {
	float tmp = weakness_value[i];
	weakness_value[i] = weakness_value[j];
	weakness_value[j] = tmp;
      }

  /* The overall weakness is mostly, but not completely determined by the
   * best value found so far:
   */
  weakness = gg_min(0.7 * weakness_value[0] + 0.3 * weakness_value[1],
                    1.3 * weakness_value[0]);

  gg_assert(weakness >= 0.0 && weakness <= 1.0);

  return weakness;
}

/* This function tries to guess a coefficient measuring the weakness of
 * a dragon. This coefficient * the effective size of the dragon can be
 * used to award a strategic penalty for weak dragons.
 */
static float
compute_dragon_weakness_value(int d)
{
  int origin = dragon2[d].origin;
  float weakness;

  /* Possible ingredients for the computation:
   * 	'+' means currently used, '-' means not (yet?) used
   * - pre-owl moyo_size
   * + post-owl moyo_size and its territory value
   * + escape factor
   * + number of eyes
   *   - minus number of vital attack moves?
   * + from owl:
   *   + attack certain?
   *   - number of owl nodes
   *   - maybe reading shadow?
   *   + threat to attack?
   * - possible connections to neighbour dragons
   */

  DEBUG(DEBUG_DRAGONS, "Computing weakness of dragon at %1m:\n", origin);

  weakness = crude_dragon_weakness(dragon2[d].safety, &dragon2[d].genus,
				   dragon2[d].lunch != NO_MOVE,
      				   dragon2[d].moyo_territorial_value, 
				   (float) dragon2[d].escape_route);

  /* Now corrections due to (uncertain) owl results resp. owl threats. */
  if (!dragon2[d].owl_attack_certain)
    weakness += gg_min(0.25 * (1.0 - weakness), 0.25 * weakness);
  if (!dragon2[d].owl_defense_certain)
    weakness += gg_min(0.25 * (1.0 - weakness), 0.25 * weakness);
  if (dragon2[d].owl_threat_status == CAN_THREATEN_ATTACK)
    weakness += 0.15 * (1.0 - weakness);

  if (weakness < 0.0)
    weakness = 0.0;
  if (weakness > 1.0)
    weakness = 1.0;

  DEBUG(DEBUG_DRAGONS, " result: %f.\n", weakness);
  return weakness;
}


/* This function has to be called _after_ the owl analysis and the
 * subsequent re-run of the influence code.
 */
void
compute_refined_dragon_weaknesses()
{
  int d;

  /* Compute the surrounding moyo sizes. */
  for (d = 0; d < number_of_dragons; d++)
    dragon2[d].moyo_size = 2 * BOARDMAX;
  
  /* Set moyo sizes according to initial_influence. */
  compute_surrounding_moyo_sizes(&initial_black_influence);
  compute_surrounding_moyo_sizes(&initial_white_influence);

  for (d = 0; d < number_of_dragons; d++)
    dragon2[d].weakness = compute_dragon_weakness_value(d);
}

/* The strategic size is the effective size, plus a bonus for all weak
 * neighbouring dragons of the opponent.
 */
void
compute_strategic_sizes()
{
  float *bonus = calloc(number_of_dragons, sizeof(float));
  int d;
  int k;

  for (d = 0; d < number_of_dragons; d++) {
    /* Compute bonus for all neighbors of dragon (d). The total bonus for
     * all neighbors is effective_size(d) * weakness(d), and it is given
     * to a neighbor d2 proportionally to the value of
     * effective_size(d2) * weakness(d2).
     */
    float sum = 0.0;
    if (dragon2[d].safety == INESSENTIAL)
      continue;
    for (k = 0; k < dragon2[d].neighbors; k++) {
      int d2 = dragon2[d].adjacent[k];
      if (board[dragon2[d2].origin] == OTHER_COLOR(board[dragon2[d].origin])
	  && dragon2[d2].safety != INESSENTIAL)
	sum += DRAGON(d2).effective_size * dragon2[d2].weakness;
    }
    if (sum == 0.0)
      continue;
    for (k = 0; k < dragon2[d].neighbors; k++) {
      int d2 = dragon2[d].adjacent[k];
      if (board[dragon2[d2].origin] == OTHER_COLOR(board[dragon2[d].origin])
	  && dragon2[d2].safety != INESSENTIAL) {
	bonus[d2] += ((DRAGON(d2).effective_size * dragon2[d2].weakness) / sum)
		     * DRAGON(d).effective_size * dragon2[d].weakness;
	if (0)
	  gprintf("Dragon %1m receives %f effective size bonus from %1m.\n",
		  dragon2[d2].origin, 
		  ((DRAGON(d2).effective_size * dragon2[d2].weakness) / sum)
		  * DRAGON(d).effective_size * dragon2[d].weakness,
		  dragon2[d].origin);
      }
    }
  }

  for (d = 0; d < number_of_dragons; d++) {
    if (0)
      gprintf("Dragon %1m gets effective size bonus of %f.\n",
	      dragon2[d].origin, bonus[d]);
    /* We cap strategic size at 3 * effective_size. (This is ad hoc.) */
    dragon2[d].strategic_size = gg_min(bonus[d] + DRAGON(d).effective_size,
				       3 * DRAGON(d).effective_size);
  }

  free(bonus);
}


/* 
 * Test whether two dragons are the same. Used by autohelpers and elsewhere.
 */

int
is_same_dragon(int d1, int d2)
{
  if (d1 == NO_MOVE || d2 == NO_MOVE)
    return (d1 == d2);
  
  ASSERT_ON_BOARD1(d1);
  ASSERT_ON_BOARD1(d2);

  return (dragon[d1].origin == dragon[d2].origin);
}

/* Test whether two dragons are neighbors. */
int
are_neighbor_dragons(int d1, int d2)
{
  int k;
  d1 = dragon[d1].origin;
  d2 = dragon[d2].origin;
  
  for (k = 0; k < DRAGON2(d1).neighbors; k++)
    if (dragon2[DRAGON2(d1).adjacent[k]].origin == d2)
      return 1;

  /* Just to be make sure that this function is always symmetric, we
   * do it the other way round too.
   */
  for (k = 0; k < DRAGON2(d2).neighbors; k++)
    if (dragon2[DRAGON2(d2).adjacent[k]].origin == d1)
      return 1;

  return 0;
}


/* Mark the stones of a dragon. */
void
mark_dragon(int pos, signed char mx[BOARDMAX], signed char mark)
{
  int w;
  for (w = first_worm_in_dragon(dragon[pos].origin); w != NO_MOVE;
       w = next_worm_in_dragon(w))
    mark_string(w, mx, mark);
}


/* The following two functions allow to traverse all worms in a dragon:
 * for (ii = first_worm_in_dragon(pos); ii != NO_MOVE;
 *      ii = next_worm_in_dragon(ii);)
 *   ...
 * At the moment first_worm_in_dragon(pos) will always be the origin
 * of the dragon, but you should not rely on that.
 */
int
first_worm_in_dragon(int d)
{
  return dragon[d].origin;
}

int
next_worm_in_dragon(int w)
{
  ASSERT1(worm[w].origin == w, w);
  return next_worm_list[w];
}


/* ================================================================ */
/*                       A few status functions                     */
/* ================================================================ */

/*
 * These functions are only here because then we don't need to expose
 * the dragon structure to the external program.
 */

enum dragon_status
crude_status(int pos)
{
  return dragon[pos].crude_status;
}


enum dragon_status
dragon_status(int pos)
{
  return dragon[pos].status;
}


int
lively_dragon_exists(int color)
{
  if (color == WHITE)
    return lively_white_dragons > 0;
  else
    return lively_black_dragons > 0;
}


/* Is this dragon weak? */

int 
dragon_weak(int pos)
{
  ASSERT_ON_BOARD1(pos);
  /* FIXME: This should not happen, but avoids a crash.  What is
   *   the proper fix for calling this at stackp != 0 ?
   */
  if (dragon[pos].id < 0 || dragon[pos].id >= number_of_dragons)
     return 1;
  return (DRAGON2(pos).weakness > 0.40001);
}


/* Returns the size of the biggest critical dragon on the board. */

int  
size_of_biggest_critical_dragon(void)
{ 
  int str;
  int max_size = 0;
  
  for (str = BOARDMIN; str < BOARDMAX; str++)
    if (ON_BOARD(str)) {
      
      if (board[str] == EMPTY
	  || dragon[str].origin != str)
	continue;
        
      /* Get the best available status for the dragon */
      if (dragon[str].status == CRITICAL) {
        if (dragon[str].size >= max_size)
          max_size = dragon[str].size;
      }
    }
  return max_size;
}


/************************************************************************
 *         A list of all cuts found during connection matching          *
 ************************************************************************/

#define MAX_CUTS 	3 * MAX_BOARD * MAX_BOARD

struct cut_data {
  int apos;
  int bpos;
  int move;
};

static int num_cuts = 0;
static struct cut_data cut_list[MAX_CUTS];

static void
clear_cut_list()
{
  num_cuts = 0;
}

/* Store in the list that (move) disconnects the two strings at
 * apos and bpos.
 */
void
add_cut(int apos, int bpos, int move)
{
  gg_assert(board[apos] == board[bpos]);
  if (num_cuts == MAX_CUTS)
    return;
  if (apos > bpos) {
    int tmp = apos;
    apos = bpos;
    bpos = tmp;
  }
  if (move == NO_MOVE)
    return;
  cut_list[num_cuts].apos = apos;
  cut_list[num_cuts].bpos = bpos;
  cut_list[num_cuts].move = move;
  num_cuts++;
  if (0)
  gprintf("Added %d-th cut at %1m between %1m and %1m.\n", num_cuts,
          move, apos, bpos);
}

/* For every move in the cut list disconnecting two of opponent's strings,
 * test whether the two strings can be connected at all. If so, add a
 * CUT_MOVE reason.
 */
void
cut_reasons(int color)
{
  int k;
  for (k = 0; k < num_cuts; k++)
    if (board[cut_list[k].apos] == OTHER_COLOR(color)
	&& !is_same_dragon(cut_list[k].apos, cut_list[k].bpos)
	&& string_connect(cut_list[k].apos, cut_list[k].bpos, NULL) == WIN)
      add_cut_move(cut_list[k].move, cut_list[k].apos, cut_list[k].bpos);
}


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

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

void
ascii_report_dragon(char *string)
{
  int pos = string_to_location(board_size, string);

  if (!ON_BOARD(pos))
    fprintf(stderr, "unknown position %s\n", string);
  else
    report_dragon(stderr, pos);
}


void
report_dragon(FILE *outfile, int pos)
{
  int w;
  int k;
  struct dragon_data *d = &(dragon[pos]);
  struct dragon_data2 *d2 = &(dragon2[d->id]);

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

  if (d->id < 0) {
    gprintf("Dragon data not available at %1m\n", pos);
    return;
  }

  gfprintf(outfile, "color                   %s\n", color_to_string(d->color));
  gfprintf(outfile, "origin                  %1m\n", d->origin);
  gfprintf(outfile, "size                    %d\n", d->size);
  gfprintf(outfile, "effective_size          %f\n", d->effective_size);
  gfprintf(outfile, "strategic_size          %f\n", d2->strategic_size);
  gfprintf(outfile, "genus                   %s\n",
	   eyevalue_to_string(&d2->genus));
  gfprintf(outfile, "heye                    %1m\n", d2->heye);
  gfprintf(outfile, "escape_route            %d\n", d2->escape_route);
  gfprintf(outfile, "lunch                   %1m\n", d2->lunch);
  gfprintf(outfile, "crude_status            %s\n",
	   status_to_string(d->crude_status));
  gfprintf(outfile, "owl_status              %s\n",
	   status_to_string(d2->owl_status));
  gfprintf(outfile, "status                  %s\n",
	   status_to_string(d->status));
  gfprintf(outfile, "safety                  %s\n",
	   status_to_string(d2->safety));
  gfprintf(outfile, "weakness                %f\n", d2->weakness);
  gfprintf(outfile, "weakness_pre_owl        %f\n", d2->weakness_pre_owl);
  gfprintf(outfile, "surround_status         %d\n", d2->surround_status);
  gfprintf(outfile, "surround_size           %d\n", d2->surround_size);
  gfprintf(outfile, "moyo_size               %d\n", d2->moyo_size);
  gfprintf(outfile, "moyo_territorial_value  %f\n",
	   d2->moyo_territorial_value);
  gfprintf(outfile, "neighbors               ");
  for (k = 0; k < d2->neighbors; k++)
    gfprintf(outfile, "%1m ", DRAGON(d2->adjacent[k]).origin);
  gfprintf(outfile, "\nhostile_neighbors       %d\n", d2->hostile_neighbors);
  gfprintf(outfile, "owl_attack_code         %d\n", d2->owl_attack_code);
  gfprintf(outfile, "owl_attack_point        %1m\n", d2->owl_attack_point);
  gfprintf(outfile, "owl_attack_certain      %s\n",
	   (d2->owl_attack_certain) ? "YES" : "NO");
  gfprintf(outfile, "owl_2nd_attack_point    %1m\n",
	   d2->owl_second_attack_point);
  gfprintf(outfile, "owl_threat_status       %s\n",
	   status_to_string(d2->owl_threat_status));
  gfprintf(outfile, "owl_defense_code        %d\n", d2->owl_defense_code);
  gfprintf(outfile, "owl_defense_point       %1m\n", d2->owl_defense_point);
  gfprintf(outfile, "owl_defense_certain     %s\n",
	   (d2->owl_defense_certain) ? "YES" : "NO");
  gfprintf(outfile, "owl_2nd_defense_point   %1m\n",
           d2->owl_second_defense_point);
  gfprintf(outfile, "owl_attack_kworm        %1m\n", d2->owl_attack_kworm);
  gfprintf(outfile, "owl_defense_kworm       %1m\n", d2->owl_defense_kworm);
  gfprintf(outfile, "semeais                 %d\n", d2->semeais);
  gfprintf(outfile, "semeai_defense_code     %d\n", d2->semeai_defense_code);
  gfprintf(outfile, "semeai_defense_point    %1m\n", d2->semeai_defense_point);
  gfprintf(outfile, "semeai_defense_certain  %d\n",
	   d2->semeai_defense_certain);
  gfprintf(outfile, "semeai_defense_target   %1m\n",
      	   d2->semeai_defense_target);
  gfprintf(outfile, "semeai_attack_code      %d\n", d2->semeai_attack_code);
  gfprintf(outfile, "semeai_attack_point     %1m\n", d2->semeai_attack_point);
  gfprintf(outfile, "semeai_attack_certain   %d\n", d2->semeai_attack_certain);
  gfprintf(outfile, "semeai_attack_target    %1m\n", d2->semeai_attack_target);
  gfprintf(outfile, "strings                 ");
  for (w = first_worm_in_dragon(pos); w != NO_MOVE; w = next_worm_in_dragon(w))
    gfprintf(outfile, "%1m ", w);
  gfprintf(outfile, "\n");
}


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