/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * 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, 2009 and 2010 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 <stdarg.h>
#include <string.h>
#include <math.h>

#include "liberty.h"
#include "cache.h"
#include "gg_utils.h"
#include "readconnect.h"

/* Size of array where candidate moves are stored. */
#define MAX_MOVES 362

/* trace of a search */

typedef struct _zone {
  int array[BOARDMAX];
  unsigned int bits[1+BOARDMAX/32];
  int i;
} zone;

static int recursive_connect2(int str1, int str2, int *move,
			        int has_passed);
static int recursive_disconnect2(int str1, int str2, int *move,
				   int has_passed);
static int recursive_break(int str, const signed char goal[BOARDMAX],
			   int *move, int has_passed, Hash_data *goal_hash);
static int recursive_block(int str, const signed char goal[BOARDMAX],
			   int *move, int has_passed, Hash_data *goal_hash);

static int add_array(int *array, int elt);
static int element_array(int *array, int elt);
static void intersection_array(int *array1, int *array2);
static int snapback(int str);
static int connection_one_move(int str1, int str2);
static int prevent_connection_one_move(int *moves, int str1, int str2);
static int connected_one_move(int str1, int str2);
static int moves_to_connect_in_two_moves(int *moves, int str1, int str2);
static int connection_two_moves(int str1, int str2);
static int prevent_connection_two_moves(int *moves, int str1, int str2);
#if 0
static int connected_two_moves(int str1, int str2);
#endif
static int moves_to_connect_in_three_moves(int *moves, int str1, int str2,
					   int does_connect);
#if 0
static int simple_connection_three_moves(int str1, int str2);
static int prevent_simple_connection_three_moves(int *moves,
						 int str1, int str2);
#endif

static int recursive_connect(int str1, int str2, int *move);
static int recursive_disconnect(int str1, int str2, int *move);

static int quiescence_connect(int str1, int str2, int *move);
static int quiescence_capture(int str, int *move);
/* static int capture_one_move(int str); */
static int prevent_capture_one_move(int *moves, int str1);
static int recursive_transitivity(int str1, int str2, int str3, int *move);
static int recursive_non_transitivity(int str1, int str2, int str3, int *move);
static void order_connection_moves(int *moves, int str1, int str2,
				   int color_to_move, const char *funcname);

static int nodes_connect = 0;

/* Used by alternate connections. */
static signed char connection_shadow[BOARDMAX];

static signed char breakin_shadow[BOARDMAX]; 

/* Statistics. */
static int global_connection_node_counter = 0;

static void
init_zone(zone *zn)
{
  zn->array[0] = 0;
  memset(zn->bits, 0, 1 + BOARDMAX / 8);
}

/* send back 1 if the intersection is in the zone
 */

#if 0
static int
elt_zone(zone *zn, int elt)
{
  if ((zn->bits[elt >> 5] >> (elt & 31)) & 1)
    return 1;
  return 0;
}
#endif

/* Adds an intersection to a zone
 */

static void
add_zone(zone *zn, int elt)
{
  if (((zn->bits[elt >> 5] >> (elt & 31)) & 1) == 0) {
    zn->bits[elt >> 5] |= (1 << (elt & 31));
    zn->array[0]++;
    zn->array[zn->array[0]] = elt;
  }
}

/* start to loop over a zone
 */

#if 0
static int
start_zone(zone *zn)
{
  if (zn->array[0] < 1)
    return -1;
  zn->i = 1;
  return zn->array[1];
}
#endif

/* continue to loop over a zone
 */

#if 0
static int
next_zone(zone *zn)
{
  zn->i++;
  if (zn->i > zn->array[0])
    return -1;
  return zn->array[zn->i];
}
#endif

/* only keep the elements of zn1 which are also in zn2 */

#if 0
static void
intersection_zone(zone *zn1, zone *zn2)
{
  int r, s;
  
  for (r = start_zone(zn1); r > -1; r = next_zone(zn1))
    if (!elt_zone(zn2, r)) {
      for (s = r; s < zn1->array[0]; s++)
	zn1->array[s] = zn1->array[s+1];
      zn1->bits[r >> 5] &= ~ (1 << (r & 31));
      zn1->array[0]--;
      zn1->i--;
    }
}
#endif

/* Adds an integer to an array of integers if it is not already there.
 * The number of elements of the array is in array[0].
 */

static int
add_array(int *array, int elt)
{
  int r;
  
  for (r = 1; r < array[0] + 1; r++)
    if (array[r] == elt)
      return 0;

  array[0]++;
  array[array[0]] = elt;
  return 1;
}

/* test if an element is part of an array */

static int
element_array(int *array, int elt)
{
  int r;
  for (r = 1; r < array[0] + 1; r++)
    if (array[r] == elt)
      return 1;
  return 0;
}

/* only keep the elements of array1 which are also in array2 */

static void
intersection_array(int *array1, int *array2)
{
  int r, s;
  
  for (r = 1; r < array1[0] + 1; r++)
    if (!element_array(array2, array1[r])) {
      for (s = r; s < array1[0]; s++)
	array1[s] = array1[s+1];
      array1[0]--;
      r--;
    }
}

/* verifies that capturing the stone at str is not a snapback */

static int
snapback(int str)
{
  int stones, liberties, lib;
  SGFTree *save_sgf_dumptree = sgf_dumptree;

  /* if more than one stone captured, not a snapback */
  stones = countstones(str);
  if (stones > 1)
    return 0;

  /* if more than one liberty, not a snapback */
  liberties = findlib(str, 1, &lib);
  if (liberties > 1)
    return 0;

  /* turn off the sgf traces
   */
  sgf_dumptree = NULL;
  
  /* if only one liberty after capture */
  if (trymove(lib, OTHER_COLOR(board[str]), "snapback", str)) {
    liberties = 0;
    if (IS_STONE(board[lib]))
      liberties = countlib(lib);
    popgo();
    sgf_dumptree = save_sgf_dumptree;
    if (liberties > 1)
      return 0;
    return WIN;
  }
  
  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  
  return 0;
}

/* connection by playing and finding a ponnuki after play */

static int
ponnuki_connect(int *moves, int str1, int str2, zone *zn)
{
  int r, s, k, res = 0;
  int liberties, libs[MAXLIBS];
  int adj, adjs[MAXCHAIN];
  int neighb, neighbs[MAXCHAIN];

  /* finds connection through two forbidden liberties for
   * the opponent
   * + + + + + + +
   * + + @ O O @ +
   * + @ + @ @ x +
   * + + @ + + + +
   * - - - - - - -
   *
   * + + + + + + +
   * + + @ O O @ +
   * + @ + @ @ O @
   * + + @ + + x +
   * - - - - - - -
   */
  liberties = findlib(str1, MAXLIBS, libs);
  for (r = 0; r < liberties; r++)
    if (is_self_atari(libs[r], OTHER_COLOR(board[str1]))) 
      for (k = 0; k < 4; k++) {
	int pos = libs[r] + delta[k];
	if (board[pos] == board[str1]
	    && !same_string(pos, str1)
	    && !same_string(pos, str2)) {
	  /* try to connect pos to str2 in one move */
	  /* play a common liberty */
	  neighb = findlib(pos, MAXLIBS, neighbs);
	  for (s = 0; s < neighb; s++)
	    if (liberty_of_string(neighbs[s], str2)) {
	      res = 1;
	      add_zone(zn, libs[r]);
	      add_zone(zn, neighbs[s]);
	      add_array(moves, neighbs[s]);
	    }
	  /* or capture a common adjacent string */
	  adj = chainlinks2(pos, adjs, 1);
	  for (s = 0; s < adj; s++)
	    if (adjacent_strings(adjs[s], str2)
		&& !snapback(adjs[s])) {
	      res = 1;
	      neighb = findlib(adjs[s], 1, neighbs);
	      add_zone(zn, libs[r]);
	      add_zone(zn, neighbs[0]);
	      add_array(moves, neighbs[0]);
	    }
	}
      }
  
  return res;
}

/* connection in one move, finds all moves and memorizes intersections
 * involved in the connection.
 */

static int
moves_connection_one_move(int *moves, int str1, int str2, zone *zn)
{
  int r;
  int adj, adjs[MAXCHAIN];

  /* If one string is missing we have already failed. */
  if (board[str1] == EMPTY || board[str2] == EMPTY)
    return 0;

  /* Common liberties. */
  if (have_common_lib(str1, str2, NULL))
    return WIN;

  /* Common adjacent string in atari, more than one stone, no snapback. */
  adj = chainlinks2(str1, adjs, 1);
  for (r = 0; r < adj; r++)
    if (adjacent_strings(adjs[r], str2)
        && !snapback(adjs[r]))
      return WIN;
  
  /* Connections through a ponnuki */
  if (ponnuki_connect(moves, str1, str2, zn))
    return WIN;
  if (ponnuki_connect(moves, str2, str1, zn))
    return WIN;

  return 0;
}

/* Verifies that the strings str1 and str2 can be connected
 * directly by playing one move, either by playing a common liberty
 * of the two strings, or by capturing a common adjacent string.
 *
 * This is the gi1 game function.
 */

static int
connection_one_move(int str1, int str2)
{
  int moves[BOARDMAX];
  zone zn;
  init_zone(&zn);
  moves[0] = 0;
  return moves_connection_one_move(moves, str1, str2, &zn);
}

/* If the two strings str1 and str2 can be connected sends back WIN fill the
 * array moves with the only move that can prevent a connection in one move
 * (common liberties, liberties of common adjacent strings in atari).
 *
 * This is the ip1 game function. */

static int
prevent_connection_one_move(int *moves, int str1, int str2)
{
  int r, s;
  int libs[MAXLIBS];
  int adj, adjs[MAXCHAIN];
  int adjadj, adjadjs[MAXCHAIN];
  
  /* Common liberties. */
  if (have_common_lib(str1, str2, libs)) {
    add_array(moves, libs[0]);
    return WIN;
  }
  
  /* Save a common adjacent string in atari, more than one stone, no snapback.
   */
  adj = chainlinks2(str1, adjs, 1);
  for (r = 0; r < adj; r++)
    if (adjacent_strings(adjs[r], str2)
        && !snapback(adjs[r])) {
      findlib(adjs[r], MAXLIBS, libs);
      add_array(moves, libs[0]);
      adjadj = chainlinks2(adjs[r], adjadjs, 1);
      for (s = 0; s < adjadj; s++) {
	findlib(adjadjs[s], MAXLIBS, libs);
	add_array(moves, libs[0]);
      }
      return WIN;
    }
  
  return 0;
}

/* Returns WIN if str1 and str2 are connected in at most
 * one move even if the opponent plays first.
 * Verify that the strings are connectable in one move
 * and find the only possible moves that can prevent
 * using prevent_connection_one_move. If none of these
 * moves works, the two strings are connected.
 *
 * This is the g1 game function.
 */

static int
connected_one_move(int str1, int str2)
{
  int r, res = 0;
  int moves[MAX_MOVES];
  SGFTree *save_sgf_dumptree = sgf_dumptree;

  /* turn off the sgf traces
   */
  sgf_dumptree = NULL;
  
  moves[0] = 0;
  if (prevent_connection_one_move(moves, str1, str2)) {
    order_connection_moves(moves, str1, str2, OTHER_COLOR(board[str1]),
			   "connected_one_move");
    res = WIN;
    for (r = 1; ((r < moves[0] + 1) && res); r++) {
      if (trymove(moves[r], OTHER_COLOR(board[str1]),
		  "connected_one_move", str1)) {
	if (!connection_one_move(str1, str2))
	  res = 0;
	popgo();
      }
    }
  }

  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  
  return res;
}

/* Find the moves that might be able to connect in less than three plies.
 * That is moves that can connect the strings if another move of the same
 * color is played just after:
 * - common liberties of the two strings;
 * - moves on the liberties of an opponent string with less than two
 *   liberties adjacent to both strings, or adjacent to one string and
 *   that has a common liberty with the second string;
 * - liberties of one string that are second order liberties of the
 *   other string.
 *
 * Returns WIN if a direct connection has been found. Returns 0
 * otherwise.
 */

static int
moves_to_connect_in_two_moves(int *moves, int str1, int str2)
{
  int r, s, common_adj_liberty;
  int liberties, libs[MAXLIBS];
  int adj, adjs[MAXCHAIN];
  int adjadj, adjadjs[MAXCHAIN];
  int k;
  int color = board[str1];
  int move;
  
  /* Common liberties. */
  if (have_common_lib(str1, str2, libs)) {
    add_array(moves, libs[0]);
    return 1;
  }
  
  /* Capture a common adjacent string or an adjacent liberty of str1
   * that has a common liberty with str2...
   */
  adj = chainlinks3(str1, adjs, 2);
  for (r = 0; r < adj; r++) {
    liberties = findlib(adjs[r], MAXLIBS, libs);
    common_adj_liberty = 0;
    for (s = 0; s < liberties; s++)
      if (liberty_of_string(libs[s], str2))
	common_adj_liberty = 1;
    if (common_adj_liberty || adjacent_strings(adjs[r], str2)) {
      for (s = 0; s < liberties; s++)
	add_array(moves, libs[s]);
      adjadj = chainlinks2(adjs[r], adjadjs, 1);
      for (s = 0; s < adjadj; s++) {
	findlib(adjadjs[s], MAXLIBS, libs);
	add_array(moves, libs[0]);
      }
    }
  }

  /* ...and vice versa. */
  adj = chainlinks3(str2, adjs, 2);
  for (r = 0; r < adj; r++) {
    liberties = findlib(adjs[r], MAXLIBS, libs);
    common_adj_liberty = 0;
    for (s = 0; s < liberties; s++)
      if (liberty_of_string(libs[s], str1))
	common_adj_liberty = 1;
    if (common_adj_liberty || adjacent_strings(adjs[r], str1)) {
      for (s = 0; s < liberties; s++)
	add_array(moves, libs[s]);
      adjadj = chainlinks2(adjs[r], adjadjs, 1);
      for (s = 0; s < adjadj; s++) {
	findlib(adjadjs[s], MAXLIBS, libs);
	add_array(moves, libs[0]);
      }
    }
  }
  
  /* Liberties of str1 that are second order liberties of str2 and
   * vice versa.
   */
  liberties = findlib(str1, MAXLIBS, libs);
  for (r = 0; r < liberties; r++) {
    if (board[SOUTH(libs[r])] == EMPTY) {
      if (liberty_of_string(SOUTH(libs[r]), str2)) {
	add_array(moves, libs[r]);
	add_array(moves, SOUTH(libs[r]));
      }
    }
    
    if (board[WEST(libs[r])] == EMPTY) {
      if (liberty_of_string(WEST(libs[r]), str2)) {
	add_array(moves, libs[r]);
	add_array(moves, WEST(libs[r]));
      }
    }

    if (board[NORTH(libs[r])] == EMPTY) {
      if (liberty_of_string(NORTH(libs[r]), str2)) {
	add_array(moves, libs[r]);
	add_array(moves, NORTH(libs[r]));
      }
    }

    if (board[EAST(libs[r])] == EMPTY) {
      if (liberty_of_string(EAST(libs[r]), str2)) {
	add_array(moves, libs[r]);
	add_array(moves, EAST(libs[r]));
      }
    }    
  }

  /* Liberties of str1 which are adjacent to a friendly string with
   * common liberty with str2.
   */
  liberties = findlib(str1, MAXLIBS, libs);
  for (r = 0; r < liberties; r++) {
    for (k = 0; k < 4; k++) {
      int pos = libs[r] + delta[k];
      if (board[pos] == color
	  && !same_string(pos, str1)
	  && quiescence_connect(pos, str2, &move)) {
	add_array(moves, libs[r]);
	add_array(moves, move);
      }
    }
  }

  /* And vice versa. */
  liberties = findlib(str2, MAXLIBS, libs);
  for (r = 0; r < liberties; r++) {
    for (k = 0; k < 4; k++) {
      int pos = libs[r] + delta[k];
      if (board[pos] == color
	  && !same_string(pos, str2)
	  && quiescence_connect(pos, str1, &move)) {
	add_array(moves, libs[r]);
	add_array(moves, move);
      }
    }
  }

  return 0;
}
  
/* Tests if the strings can be connected in three plies starts
 * with finding the possible moves that can connect.  If two
 * moves in a row are played, then try them and stops at the
 * first working move.  The strings are connected in two moves
 * if the function connected_one_move is verified after a move.
 *
 * This is the gi2 game function.
 */

static int
connection_two_moves(int str1, int str2)
{
  int r, res = 0, moves[MAX_MOVES];
  SGFTree *save_sgf_dumptree = sgf_dumptree;
 
  /* If one string is missing we have already failed. */
  if (board[str1] == EMPTY || board[str2] == EMPTY)
    return 0;
  
  moves[0] = 0;
  if (moves_to_connect_in_two_moves(moves, str1, str2))
    return WIN;
  order_connection_moves(moves, str1, str2, board[str1],
			 "connection_two_moves");
  
  /* turn off the sgf traces
   */
  sgf_dumptree = NULL;
  
  for (r = 1; ((r < moves[0] + 1) && !res); r++) {
    if (trymove(moves[r], board[str1], "connection_two_moves", str1)) {
      if (connected_one_move(str1, str2))
	res = WIN;
      popgo();
    }
  }
  
  sgf_dumptree = save_sgf_dumptree;
  
  return res;
}

/* Find the complete set of possible moves that can prevent
 * a connection in three plies.
 *
 * The function is not yet written, but moves_to_connect_in_two_moves does
 * a similar job, so it is called temporarly.
 */

static int
moves_to_prevent_connection_in_two_moves(int *moves, int str1, int str2)
{
  if (moves_to_connect_in_two_moves(moves, str1, str2))
    return 1;
  return 0;
}

/* Find all the moves that prevent to connect in a three plies
 * deep search and put them in the moves array.  Returns 0 if
 * there is no three plies connection, or else it tries all the
 * possible preventing moves.  If after a possible preventing
 * moves, there no connection in one move and no connection in
 * two moves, then the moves prevents a three plies deep
 * connection, and it is added to the moves array.
 *
 * this is the ip2 game function */

static int
prevent_connection_two_moves(int *moves, int str1, int str2)
{
  int r, res = 0;
  int possible_moves[MAX_MOVES];
  SGFTree *save_sgf_dumptree = sgf_dumptree;

  /* turn off the sgf traces
   */
  sgf_dumptree = NULL;
    
  if (connection_two_moves(str1, str2)) {
    res = WIN;
    possible_moves[0] = 0;
    moves_to_prevent_connection_in_two_moves(possible_moves, str1, str2);
    order_connection_moves(possible_moves, str1, str2,
			   OTHER_COLOR(board[str1]),
			   "prevent_connection_two_moves");
    for (r = 1; r < possible_moves[0] + 1; r++) {
      if (trymove(possible_moves[r], OTHER_COLOR(board[str1]), 
		  "prevent_connection_two_moves", str1)) {
	if (!connection_one_move(str1, str2))
	  if (!connection_two_moves(str1, str2))
	    add_array(moves, possible_moves[r]);
	popgo();
      }
    }
  }

  sgf_dumptree = save_sgf_dumptree;  

  return res;
}

/* Only partially written.
 *
 * Find all the moves than can connect if two subsequent
 * moves of the same color are played after
 * - common liberties;
 * - liberties of common adjacent strings with 3 liberties or less;
 * - liberties of adjacent strings with 2 liberties or less that have
 *   liberties that are second order liberties of the other string;
 * - liberties of one string that are second order liberties of the
 *   other string;
 * - second order liberties of the first string that are second order 
 *   liberties of the other string;
 *
 * A function that computes the second order liberties of a string is
 * needed as well as a function that checks efficiently if an
 * intersection is a second order liberty of a given string.
 *
 * If does_connect is 1, generate moves to connect, otherwise generate
 * moves to disconnect.
 */

static int
moves_to_connect_in_three_moves(int *moves, int str1, int str2,
				int does_connect)
{
  int r, s;
  int liberties, libs[MAXLIBS];
  int liberties2, libs2[MAXLIBS];
  int adj, adjs[MAXCHAIN];
  int adjadj, adjadjs[MAXCHAIN];
  int move;
  int k;
  int pos;
  int secondlib1[BOARDMAX];
  int secondlib2[BOARDMAX];
  
  if (moves_to_connect_in_two_moves(moves, str1, str2))
    return 1;

  /* Find second order liberties of str1. */
  memset(secondlib1, 0, sizeof(secondlib1));
  liberties = findlib(str1, MAXLIBS, libs);
  for (r = 0; r < liberties; r++)
    for (k = 0; k < 4; k++) {
      pos = libs[r] + delta[k];
      if (board[pos] == EMPTY)
	secondlib1[pos] = 1;
      else if (board[pos] == board[str1]) {
	liberties2 = findlib(pos, MAXLIBS, libs2);
	for (s = 0; s < liberties2; s++)
	  secondlib1[libs2[s]] = 1;
      }
    }
  
  /* Find second order liberties of str2.
   */
  memset(secondlib2, 0, sizeof(secondlib2));
  liberties = findlib(str2, MAXLIBS, libs);
  for (r = 0; r < liberties; r++)
    for (k = 0; k < 4; k++) {
      pos = libs[r] + delta[k];
      if (board[pos] == EMPTY)
	secondlib2[pos] = 1;
      else if (board[pos] == board[str2]) {
	liberties2 = findlib(pos, MAXLIBS, libs2);
	for (s = 0; s < liberties2; s++)
	  secondlib2[libs2[s]] = 1;
      }
    }
  
  /* Second order liberties of str1 that are second order liberties
   * of str2 and vice versa.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (secondlib1[pos] && secondlib2[pos])
      add_array(moves, pos);
  }

  /* Capture a neighbor of str1 which is in atari. The captured string
   * must in turn have a neighbor which can connect to str2 easily.
   */
  adj = chainlinks2(str1, adjs, 1);
  for (r = 0; r < adj; r++) {
    adjadj = chainlinks(adjs[r], adjadjs);
    for (s = 0; s < adjadj; s++) {
      if (!same_string(adjadjs[s], str1)
	  && quiescence_connect(adjadjs[s], str2, &move)) {
	findlib(adjs[r], 1, libs);
	add_array(moves, libs[0]);
	add_array(moves, move);
      }
    }
  }

  /* And vice versa. */
  adj = chainlinks2(str2, adjs, 1);
  for (r = 0; r < adj; r++) {
    adjadj = chainlinks(adjs[r], adjadjs);
    for (s = 0; s < adjadj; s++) {
      if (!same_string(adjadjs[s], str2)
	  && quiescence_connect(adjadjs[s], str1, &move)) {
	findlib(adjs[r], 1, libs);
	add_array(moves, libs[0]);
	add_array(moves, move);
      }
    }
  }

  /* Liberties of neighbor of str1 with at most two liberties, which
   * are second order liberties of str2.
   */
  adj = chainlinks3(str1, adjs, 2);
  for (r = 0; r < adj; r++) {
    liberties = findlib(adjs[r], 2, libs);
    for (s = 0; s < liberties; s++)
      if (second_order_liberty_of_string(libs[s], str2))
	add_array(moves, libs[s]);
  }

  /* And vice versa. */
  adj = chainlinks3(str2, adjs, 2);
  for (r = 0; r < adj; r++) {
    liberties = findlib(adjs[r], 2, libs);
    for (s = 0; s < liberties; s++)
      if (second_order_liberty_of_string(libs[s], str1))
	add_array(moves, libs[s]);
  }
  
  /* Move in on a three liberty opponent string which is adjacent to
   * str1 and has a liberty in common with str2.
   */
  adj = chainlinks2(str1, adjs, 3);
  for (r = 0; r < adj; r++) {
    if (have_common_lib(adjs[r], str2, NULL)) {
      liberties = findlib(adjs[r], 3, libs);
      for (s = 0; s < liberties; s++) {
	/* If generating a connecting move, require the liberty to be
         * no further than diagonal to a second order liberty of one
         * of the strings.
	 */
	for (k = 0; k < 8; k++) {
	  if (!does_connect
	      || (ON_BOARD(libs[s] + delta[k])
		  && (secondlib1[libs[s] + delta[k]]
		      || secondlib2[libs[s] + delta[k]]))) {
	    add_array(moves, libs[s]);
	    break;
	  }
	}
      }
    }
  }

  /* And vice versa. */
  adj = chainlinks2(str2, adjs, 3);
  for (r = 0; r < adj; r++) {
    if (have_common_lib(adjs[r], str1, NULL)) {
      liberties = findlib(adjs[r], 3, libs);
      for (s = 0; s < liberties; s++) {
	/* If generating a connecting move, require the liberty to be
         * no further than diagonal to a second order liberty of one
         * of the strings.
	 */
	for (k = 0; k < 8; k++) {
	  if (!does_connect
             || (ON_BOARD(libs[s] + delta[k])
                 && (secondlib1[libs[s] + delta[k]]
                     || secondlib2[libs[s] + delta[k]]))) {
	    add_array(moves, libs[s]);
	    break;
	  }
	}
      }
    }
  }
  
  return 0;
}


/* Not yet written.
 *
 * Find the complete set of possible moves that can prevent
 * a connection in 5 plies.
 */

static int
moves_to_prevent_connection_in_three_moves(int *moves, int str1, int str2)
{
  if (moves_to_connect_in_three_moves(moves, str1, str2, 0))
    return 1;
  return 0;
}

/* 
 * The simplest depth 4 connection:
 *
 * If there are forced moves to prevent connection in one move,
 * try them, and verify that they all lead to a depth 1 or
 * depth 3 connection.
 * 
 * This is the g211 game function.
 */

static int
simply_connected_two_moves(int str1, int str2)
{
  int r, res = 0;
  int moves[MAX_MOVES];
  SGFTree *save_sgf_dumptree = sgf_dumptree;

  /* turn off the sgf traces
   */
  sgf_dumptree = NULL;
  
  
  /* If one string is missing we have already failed. */
  if (board[str1] == EMPTY || board[str2] == EMPTY)
    return 0;
  
  moves[0] = 0;
  if (prevent_connection_one_move(moves, str1, str2)) {
    res = WIN;
    order_connection_moves(moves, str1, str2, OTHER_COLOR(board[str1]),
			   "simply_connected_two_moves");
    for (r = 1; ((r < moves[0] + 1) && res); r++) {
      if (trymove(moves[r], OTHER_COLOR(board[str1]),
		  "simply_connected_two_moves", str1)) {
	if (!connection_one_move(str1, str2))
	  if (!connection_two_moves(str1, str2))
	    res = 0;
	popgo();
      }
    }
  }
  
  sgf_dumptree = save_sgf_dumptree;
  
  return res;
}

/* Test if a move is a simple depth 5 connection.
 *
 * This is the gi311 game function.
 */

static int
simple_connection_three_moves(int str1, int str2)
{
  int r, res = 0, moves[MAX_MOVES];
  SGFTree *save_sgf_dumptree = sgf_dumptree;

  /* turn off the sgf traces
   */
  sgf_dumptree = NULL;
    
  
  moves[0] = 0;
  if (moves_to_connect_in_two_moves(moves, str1, str2))
    return WIN;
  order_connection_moves(moves, str1, str2, board[str1],
			 "simple_connection_three_moves");
  for (r = 1; ((r < moves[0] + 1) && !res); r++) {
    if (trymove(moves[r], board[str1],
		"simple_connection_three_moves", str1)) {
      if (simply_connected_two_moves(str1, str2))
	res = WIN;
      popgo();
    }
  }
  
  sgf_dumptree = save_sgf_dumptree;
  
  return res;
}

/* Find the forced moves that prevent a simple depth 5 connection.
 * Fills the array moves with the forced moves.
 *
 * This is the ip311 game function.
 *
 * It finds moves in very important situations such as:
 *
 * + + + O + +
 * + @ @ O + +
 * + @ O @ @ +
 * + @ O + + +
 * + + + + + +
 * - - - - - -
 *
 * and enables recursive_disconnect to prove the two black
 * strings are connected in these situations.
 */

static int
prevent_simple_connection_three_moves(int *moves, int str1, int str2)
{
  int r, res = 0;
  int possible_moves[MAX_MOVES];
  SGFTree *save_sgf_dumptree = sgf_dumptree;

  /* turn off the sgf traces
   */
  sgf_dumptree = NULL;
    
  
  if (simple_connection_three_moves(str1, str2)) {
    res = WIN;
    possible_moves[0] = 0;
    moves_to_prevent_connection_in_three_moves(possible_moves, str1, str2);
    order_connection_moves(moves, str1, str2, OTHER_COLOR(board[str1]),
			   "prevent_simple_connection_three_moves");
    for (r = 1; r < possible_moves[0] + 1; r++) {
      if (trymove(possible_moves[r], OTHER_COLOR(board[str1]), 
		  "prevent_simple_connection_three_moves", str1)) {
	if (!connection_one_move(str1, str2))
	  if (!connection_two_moves(str1, str2))
	    if (!simple_connection_three_moves(str1, str2))
	      add_array(moves, possible_moves[r]);
	popgo();
      }
    }
  }
  
  sgf_dumptree = save_sgf_dumptree;
  
  return res;
}

/* Find simple connections by looking at common liberties
 * or directly capturing a common adjacent string without a snapback
 * or looking at a ladder for a common adjacent string.
 */

static int
quiescence_connect(int str1, int str2, int *move)
{
  int r;
  int lib;
  int adj, adjs[MAXCHAIN];

  /* Common liberties. */
  if (have_common_lib(str1, str2, &lib)) {
    *move = lib;
    return WIN;
  }

  /* Common adjacent string in atari, more than one stone, no snapback. */
  adj = chainlinks2(str1, adjs, 1);
  for (r = 0; r < adj; r++)
    if (adjacent_strings(adjs[r], str2)
        && !snapback(adjs[r])) {
      findlib(adjs[r], 1, move);
      return WIN;
    }
  
  /* Common adjacent string two liberties, read ladder. */
  adj = chainlinks2(str1, adjs, 2);
  for (r = 0; r < adj; r++)
    if (adjacent_strings(adjs[r], str2))
      if (quiescence_capture(adjs[r], move))
	return WIN;
  
  return 0;
}


/* A persistent connection cache has been implemented, but currently
 * (3.3.15) it does not have much impact on performance. Possible
 * explanations for this include:
 * 1. The active area is too often unnecessarily large.
 * 2. Between the persistent caches of tactical reading and owl
 *    reading, there is not much to gain from also caching the
 *    connection results.
 * 3. There is some bug in the implementation.
 *
 * In order to simplify testing of code modifications, the caching
 * code has been made conditional. Setting
 * USE_PERSISTENT_CONNECTION_CACHE to 0, 1, or 2 has the following
 * effects.
 * 0 - Completely turned off.
 * 1 - Results are stored in the cache but retrieved results are only
 *     compared to the non-cached result. Deviations are reported.
 * 2 - Fully turned on.
 */
#define USE_PERSISTENT_CONNECTION_CACHE 0


/* Externally callable frontend to recursive_connect().
 * Returns WIN if str1 and str2 can be connected. 
 */
int
string_connect(int str1, int str2, int *move)
{
  int dummy_move;
  int save_verbose;
  int result;

  if (move == NULL)
    move = &dummy_move;
  
  nodes_connect = 0;
  *move = PASS_MOVE;
  
  if (alternate_connections) {
    int reading_nodes_when_called = get_reading_node_counter();
    double start = 0;
    int tactical_nodes;
    int save_connection_node_limit = connection_node_limit;
#if USE_PERSISTENT_CONNECTION_CACHE == 1
    int result2 = -1;
    int move2;
#endif
    if (board[str1] == EMPTY || board[str2] == EMPTY)
      return 0;
    str1 = find_origin(str1);
    str2 = find_origin(str2);
    if (str1 > str2) {
      int tmp = str1;
      str1 = str2;
      str2 = tmp;
    }


#if USE_PERSISTENT_CONNECTION_CACHE == 1
    if (!search_persistent_connection_cache(CONNECT, str1, str2,
					    &result2, &move2))
      result2 = -1;
    else if (0)
      gprintf("Persistent cache found connect %1m %1m: %d %1m\n",
	      str1, str2, result2, move2);
#endif
#if USE_PERSISTENT_CONNECTION_CACHE == 2
    if (search_persistent_connection_cache(CONNECT, str1, str2, &result, move))
      return result;
#endif

    connection_node_limit *= pow(1.45, -stackp + get_depth_modification());
    save_verbose = verbose;
    if (verbose > 0)
      verbose--;
    start = gg_cputime();
    memset(connection_shadow, 0, sizeof(connection_shadow));
    result = recursive_connect2(str1, str2, move, 0);
    verbose = save_verbose;
    tactical_nodes = get_reading_node_counter() - reading_nodes_when_called;
    connection_node_limit = save_connection_node_limit;

#if USE_PERSISTENT_CONNECTION_CACHE == 1
    if (result2 != -1
	&& result2 != result
	&& *move != move2)
      gprintf("Persistent cache failure connect %1m %1m: %d %1m != %d %1m\n",
	      str1, str2, result, *move, result2, move2);
#endif

    if (0) {
      gprintf("%oconnect    %1M %1M, result %d %1M (%d, %d nodes, %f seconds)\n",
	      str1, str2, result, *move,
	      nodes_connect, tactical_nodes, gg_cputime() - start);
      dump_stack();
    }
    if (0) {
      gprintf("%oconnect %1m %1m %d %1m ", str1, str2, result, *move);
      dump_stack();
    }

#if USE_PERSISTENT_CONNECTION_CACHE > 0
    store_persistent_connection_cache(CONNECT, str1, str2, result, *move,
				      tactical_nodes, connection_shadow);
#endif
  
    return result;
  }

  return recursive_connect(str1, str2, move);
}


/* returns WIN if str1 and str2 can be connected. */

static int
recursive_connect(int str1, int str2, int *move)
{
  int i, res = 0, Moves[MAX_MOVES], ForcedMoves[MAX_MOVES];
  SETUP_TRACE_INFO2("recursive_connect", str1, str2);
  
  if (board[str1] == EMPTY || board[str2] == EMPTY) {
    SGFTRACE2(PASS_MOVE, 0, "one string already captured");
    return 0;
  }
  
  if (same_string(str1, str2)) {
    SGFTRACE2(PASS_MOVE, WIN, "already connected");
    return WIN;
  }

  if (nodes_connect > connection_node_limit) {
    SGFTRACE2(PASS_MOVE, 0, "connection node limit reached");
    return 0;
  }
  
  if (stackp == connect_depth) {
    SGFTRACE2(PASS_MOVE, 0, "connection depth limit reached");
    return 0;
  }

  nodes_connect++;
  global_connection_node_counter++;
  
  if (quiescence_connect (str1, str2, move)) {
    SGFTRACE2(*move, WIN, "quiescence_connect");
    return WIN;
  }

  ForcedMoves[0] = 0;
  Moves[0] = 0;
  /* if one of the strings to connect can be captured
   * and there are forced moves to prevent the capture
   * then the only moves to try are the moves that
   * defend the string: all the other moves will
   * lead to the capture of the string
   */
  if (!prevent_capture_one_move(ForcedMoves, str1))
    prevent_capture_one_move(ForcedMoves, str2);
#if 0
  else if (prevent_capture_two_moves(ForcedMoves, str1))
     ; 
  else if (prevent_capture_two_moves(ForcedMoves, str2))
     ; 
#endif
  
  /* We are at a max node, so any move we can find
   * is ok. Try moves that can connect in three moves
   * because the function that prevent connection in one
   * and two moves are called at AND nodes.
   */
  moves_to_connect_in_three_moves(Moves, str1, str2, 1);

  /* if there are some forced moves to prevent the capture
   * of one of the two strings, then we only look at
   * the moves that prevent capture and that might also
   * connect
   */
  if (ForcedMoves[0] != 0 && Moves[0] != 0)
    intersection_array(Moves, ForcedMoves);

  order_connection_moves(Moves, str1, str2, board[str1],
			 "recursive_connect");
  for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++) {
    if (trymove(Moves[i], board[str1], "recursive_connect", str1)) {
      if (!recursive_disconnect(str1, str2, move)) {
	*move = Moves[i];
	res = WIN;
      }
      popgo();
    }
  }

  if (res == WIN) {
    SGFTRACE2(*move, WIN, "success");
  }
  else {
    SGFTRACE2(PASS_MOVE, 0, "failure");
  }
  
  return res;
}
  

/* Externally callable frontend to recursive_disconnect().
 * Returns WIN if str1 and str2 can be disconnected. 
 */
int
disconnect(int str1, int str2, int *move)
{
  int i;
  int res = WIN;
  int Moves[MAX_MOVES];
  int dummy_move;
  int result;
  int save_verbose;
  
  if (move == NULL)
    move = &dummy_move;
  
  nodes_connect = 0;
  *move = PASS_MOVE;
  
  if (alternate_connections) {
    int reading_nodes_when_called = get_reading_node_counter();
    int save_connection_node_limit = connection_node_limit;
    double start = 0;
    int tactical_nodes;
#if USE_PERSISTENT_CONNECTION_CACHE == 1
    int result2 = -1;
    int move2;
#endif
    if (board[str1] == EMPTY || board[str2] == EMPTY)
      return WIN;
    str1 = find_origin(str1);
    str2 = find_origin(str2);
    if (str1 > str2) {
      int tmp = str1;
      str1 = str2;
      str2 = tmp;
    }

#if USE_PERSISTENT_CONNECTION_CACHE == 1
    if (!search_persistent_connection_cache(DISCONNECT, str1, str2,
					    &result2, &move2))
      result2 = -1;
    else if (0)
      gprintf("Persistent cache found disconnect %1m %1m: %d %1m\n",
	      str1, str2, result2, move2);
#endif
#if USE_PERSISTENT_CONNECTION_CACHE == 2
    if (search_persistent_connection_cache(DISCONNECT, str1, str2,
					   &result, move))
      return result;
#endif

    connection_node_limit *= pow(1.5, -stackp + get_depth_modification());
    save_verbose = verbose;
    if (verbose > 0)
      verbose--;
    start = gg_cputime();
    memset(connection_shadow, 0, sizeof(connection_shadow));
    result = recursive_disconnect2(str1, str2, move, 0);
    verbose = save_verbose;
    tactical_nodes = get_reading_node_counter() - reading_nodes_when_called;
    connection_node_limit = save_connection_node_limit;

#if USE_PERSISTENT_CONNECTION_CACHE == 1
    if (result2 != -1
	&& result2 != result
	&& *move != move2)
      gprintf("Persistent cache failure disconnect %1m %1m: %d %1m != %d %1m\n",
	      str1, str2, result, *move, result2, move2);
#endif

    if (0) {
      gprintf("%odisconnect %1m %1m, result %d %1m (%d, %d nodes, %f seconds)\n",
	      str1, str2, result, *move,
	      nodes_connect, tactical_nodes, gg_cputime() - start);
      dump_stack();
    }
    if (0) {
      gprintf("%odisconnect %1m %1m %d %1m ", str1, str2, result, *move);
      dump_stack();
    }

#if USE_PERSISTENT_CONNECTION_CACHE > 0
    store_persistent_connection_cache(DISCONNECT, str1, str2, result, *move,
				      tactical_nodes, connection_shadow);
#endif

    return result;
  }

  Moves[0] = 0;
  moves_to_prevent_connection_in_three_moves(Moves, str1, str2);
  if (Moves[0] > 0)
    res = 0;
  order_connection_moves(Moves, str1, str2, OTHER_COLOR(board[str1]),
			 "disconnect");
  for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++)
    if (trymove(Moves[i], OTHER_COLOR(board[str1]),
		"disconnect", str1)) {
      if (!recursive_connect(str1, str2, move)) {
	*move = Moves[i];
	res = WIN;
      }
      popgo();
    }
  return res;
}

/* Externally callable frontend to recursive_disconnect().
 * Returns WIN if str1 and str2 can be disconnected. 
 *
 * Uses much lower node and depths limits.
 */
int
fast_disconnect(int str1, int str2, int *move)
{
  int result;
  int save_limit = connection_node_limit;
  int save_verbose = verbose;

  if (board[str1] == EMPTY || board[str2] == EMPTY)
    return WIN;
  str1 = find_origin(str1);
  str2 = find_origin(str2);
  if (str1 > str2) {
    int tmp = str1;
    str1 = str2;
    str2 = tmp;
  }

  modify_depth_values(-3);
  connection_node_limit /= 4;

  if (verbose > 0)
    verbose--;
  result = recursive_disconnect2(str1, str2, move, 0);
  verbose = save_verbose;

  connection_node_limit = save_limit;
  modify_depth_values(3);

  return result;
}



/* Returns WIN if str1 and str2 can be disconnected. */

static int
recursive_disconnect(int str1, int str2, int *move)
{
  int i, res = WIN, Moves[MAX_MOVES];
  
  SETUP_TRACE_INFO2("recursive_disconnect", str1, str2);
  
  if (board[str1] == EMPTY || board[str2] == EMPTY) {
    SGFTRACE2(PASS_MOVE, WIN, "one string already captured");
    return WIN;
  }

  if (quiescence_capture(str1, move)) {
    SGFTRACE2(*move, WIN, "first string capturable");
    return WIN;
  }
  if (quiescence_capture(str2, move)) {
    SGFTRACE2(*move, WIN, "second string capturable");
    return WIN;
  }

  if (same_string(str1, str2)) {
    SGFTRACE2(PASS_MOVE, 0, "already connected");
    return 0;
  }
  
  if (nodes_connect > connection_node_limit) {
    SGFTRACE2(PASS_MOVE, WIN, "connection node limit reached");
    return WIN;
  }
  
  if (stackp == connect_depth) {
    SGFTRACE2(PASS_MOVE, WIN, "connection depth limit reached");
    return WIN;
  }
  
  nodes_connect++;
  global_connection_node_counter++;
  
  /* we are at an and node
   * only look at forced moves here,
   * it ensures that the result of recursive_disconnect
   * is proved if it returns 0 (that is connections are proved)
   */
  Moves[0] = 0;

  if (prevent_connection_one_move(Moves, str1, str2))
    res = 0;
  else if (prevent_connection_two_moves(Moves, str1, str2))
    res = 0;
  else if (prevent_simple_connection_three_moves(Moves, str1, str2))
    res = 0;
  
  if (res == 0)
    order_connection_moves(Moves, str1, str2, OTHER_COLOR(board[str1]),
			   "recursive_disconnect");
    for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++)
      if (trymove(Moves[i], OTHER_COLOR(board[str1]),
		  "recursive_disconnect", str1)) {
	if (!recursive_connect(str1, str2, move)) {
	  *move = Moves[i];
	  res = WIN;
	}
	popgo();
      }

  if (res == WIN) {
    SGFTRACE2(*move, WIN, "success");
  }
  else {
    SGFTRACE2(PASS_MOVE, 0, "failure");
  }
  
  return res;
}
 
/* Reads simple ladders.
 */

static int
quiescence_capture(int str, int *move)
{
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;
  int result = 0;

  /* We turn off the sgf traces here to avoid cluttering them up with
   * naive_ladder moves.
   */
  sgf_dumptree = NULL;
  count_variations = 0;

  if (countlib(str) == 1) {
    findlib(str, 1, move);
    result = WIN;
  }
  else if (countlib(str) == 2)
    result = simple_ladder(str, move);

  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;

  return result;
}

#if 0
static int
capture_one_move(int str) 
{
  if (countlib(str) == 1)
    return 1;
  return 0;
}
#endif

/* Find all the possible moves that can prevent the capture
 * of a string in atari.
 *
 * The ip1 game function.
 */

static int
prevent_capture_one_move(int *moves, int str1)
{
  int r, res = 0;
  int liberties, libs[MAXLIBS];
  int adj, adjs[MAXCHAIN];
  
  liberties = findlib(str1, MAXLIBS, libs);
  if (liberties == 1) {
    add_array(moves, libs[0]);
    res = WIN;
    adj = chainlinks2(str1, adjs, 1);
    for (r = 0; r < adj; r++) {
      findlib(adjs[r], 1, libs);
      add_array(moves, libs[0]);
    }
  }
  return res;
}


/* Returns WIN if str1, str2 and str3 can be connected. */

static int
recursive_transitivity(int str1, int str2, int str3, int *move)
{
  int i, res = 0, Moves[MAX_MOVES], ForcedMoves[MAX_MOVES];

  SETUP_TRACE_INFO2("recursive_transitivity", str1, str3);
  
  if (board[str1] == EMPTY || board[str2] == EMPTY || board[str3] == EMPTY) {
    SGFTRACE2(PASS_MOVE, 0, "one string already captured");
    return 0;
  }
  
  if (same_string(str1, str2) && same_string(str1, str3)) {
    SGFTRACE2(PASS_MOVE, WIN, "already connected");
    return WIN;
  }

  if (nodes_connect > connection_node_limit) {
    SGFTRACE2(PASS_MOVE, 0, "connection node limit reached");
    return 0;
  }
  
  if (stackp == connect_depth) {
    SGFTRACE2(PASS_MOVE, 0, "connection depth limit reached");
    return 0;
  }

  nodes_connect++;
  global_connection_node_counter++;
  
  if (same_string(str1, str2))
    if (quiescence_connect (str1, str3, move)) {
      SGFTRACE2(*move, WIN, "quiescence_connect");
      return WIN;
    }

  if (same_string(str2, str3))
    if (quiescence_connect (str1, str2, move)) {
      SGFTRACE2(*move, WIN, "quiescence_connect");
      return WIN;
    }

  ForcedMoves[0] = 0;
  Moves[0] = 0;
  /* If one of the strings to connect can be captured
   * and there are forced moves to prevent the capture
   * then the only moves to try are the moves that
   * defend the string. All the other moves will
   * lead to the capture of the string.
   */
  if (!prevent_capture_one_move(ForcedMoves, str1))
    if (!prevent_capture_one_move(ForcedMoves, str2))
      prevent_capture_one_move(ForcedMoves, str3);
  
  /* We are at a max node, so any move we can find
   * is ok. Try moves that can connect in two moves
   * because the function that prevents connection in one
   * move is called at and nodes.
   */
  moves_to_connect_in_two_moves(Moves, str1, str2);
  moves_to_connect_in_two_moves(Moves, str2, str3);

  /* If there are some forced moves to prevent the capture
   * of one of the two strings, then we only look at
   * the moves that prevent capture and that might also
   * connect.
   */
  if ((ForcedMoves[0] != 0) && (Moves[0] != 0))
    intersection_array(Moves, ForcedMoves);

  order_connection_moves(Moves, str1, str2, board[str1],
			 "recursive_transitivity");
  for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++) {
    if (trymove(Moves[i], board[str1], "recursive_transitivity", str1)) {
      if (!recursive_non_transitivity(str1, str2, str3, move)) {
	*move = Moves[i];
	res = WIN;
      }
      popgo();
    }
  }

  if (res == WIN) {
    SGFTRACE2(*move, WIN, "success");
  }
  else {
    SGFTRACE2(PASS_MOVE, 0, "failure");
  }
  
  return res;
}
  
/* It is often assumed that if str1 connects to str2 and str2 
 * connects to str3 then str1 connects to str3. This is called
 * TRANSITIVITY. However there are exceptions such as this
 * situation:
 * 
 *     XXXXX            XXXXX
 *     OO.OO            AA*CC
 *     ..O..            ..B..
 *     XXXXX            XXXXX
 *
 * Although strings A and B are connected, and strings B and C
 * are connected, a move at * disconnects strings A and C.
 *
 * This function is a public frontend to recursive_non_transitivity(). 
 * Returns WIN if str1, str2 and str3 can be disconnected.
*/

int
non_transitivity(int str1, int str2, int str3, int *move)
{
  int i, res = WIN, Moves[MAX_MOVES];
  
  nodes_connect = 0;
  *move = PASS_MOVE;
  moves_to_prevent_connection_in_three_moves(Moves, str1, str3);
  if (Moves[0] > 0)
    res = 0;
  order_connection_moves(Moves, str1, str2, OTHER_COLOR(board[str1]),
			 "non_transitivity");
  for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++)
    if (trymove(Moves[i], OTHER_COLOR(board[str1]),
		"non_transitivity", str1)) {
      if (!recursive_transitivity(str1, str2, str3, move)) {
	*move = Moves[i];
	res = WIN;
      }
      popgo();
    }
  return res;
}

/* Returns WIN if str1, str2 and str3 can be disconnected. */

static int
recursive_non_transitivity(int str1, int str2, int str3, int *move)
{
  int i, res = WIN, Moves[MAX_MOVES];
  
  SETUP_TRACE_INFO2("recursive_non_transitivity", str1, str3);
  
  if (board[str1] == EMPTY || board[str2] == EMPTY
      || board[str3] == EMPTY) {
    SGFTRACE2(PASS_MOVE, WIN, "one string already captured");
    return WIN;
  }

  if (quiescence_capture(str1, move)) {
    SGFTRACE2(*move, WIN, "first string capturable");
    return WIN;
  }
  if (quiescence_capture(str2, move)) {
    SGFTRACE2(*move, WIN, "second string capturable");
    return WIN;
  }
  if (quiescence_capture(str3, move)) {
    SGFTRACE2(*move, WIN, "third string capturable");
    return WIN;
  }

  if (same_string(str1, str2) && same_string(str1, str3)) {
    SGFTRACE2(PASS_MOVE, 0, "already connected");
    return 0;
  }
  
  if (nodes_connect > connection_node_limit) {
    SGFTRACE2(PASS_MOVE, WIN, "connection node limit reached");
    return WIN;
  }
  
  if (stackp == connect_depth) {
    SGFTRACE2(PASS_MOVE, WIN, "connection depth limit reached");
    return WIN;
  }
  
  nodes_connect++;
  global_connection_node_counter++;
  
  /* We are at an and node. Only look at forced moves. */
  Moves[0] = 0;
  if (prevent_connection_one_move(Moves, str1, str3))
    res = 0;
  else if (prevent_connection_two_moves(Moves, str1, str3))
    res = 0;
  else if (prevent_simple_connection_three_moves(Moves, str1, str3))
    res = 0;
  
  if (res == 0)
    order_connection_moves(Moves, str1, str2, OTHER_COLOR(board[str1]),
			   "recursive_non_transitivity");
    for (i = 1; ((i < Moves[0] + 1) && (res == 0)); i++)
      if (trymove(Moves[i], OTHER_COLOR(board[str1]),
		  "recursive_non_transitivity", str1)) {
	if (!recursive_transitivity(str1, str2, str3, move)) {
	  *move = Moves[i];
	  res = WIN;
	}
	popgo();
      }

  if (res == WIN) {
    SGFTRACE2(*move, WIN, "success");
  }
  else {
    SGFTRACE2(PASS_MOVE, 0, "failure");
  }
  
  return res;
}


/* Order the moves so that we try the ones likely to succeed early. */
static void
order_connection_moves(int *moves, int str1, int str2, int color_to_move,
		       const char *funcname)
{
  int scores[MAX_MOVES];
  int r;
  int i, j;
  UNUSED(str2);
  UNUSED(color_to_move);

  for (r = 1; r <= moves[0]; r++) {
    int move = moves[r];

    /* Look at the neighbors of this move and count the things we
     * find. Friendly and opponent stones are related to color, i.e.
     * the player to move, not to the color of the string.
     *
     * We don't use all these values. They are only here so we can
     * reuse incremental_order_moves() which was developed for the
     * tactical reading.
     */
    int number_edges       = 0; /* outside board */
    int number_same_string = 0; /* the string being attacked */
    int number_own         = 0; /* friendly stone */
    int number_opponent    = 0; /* opponent stone */
    int captured_stones    = 0; /* number of stones captured by this move */
    int threatened_stones  = 0; /* number of stones threatened by this move */
    int saved_stones       = 0; /* number of stones in atari saved */
    int number_open        = 0; /* empty intersection */
    int libs;

    /* We let the incremental board code do the heavy work. */
    incremental_order_moves(move, color_to_move, str1, &number_edges,
			    &number_same_string, &number_own,
			    &number_opponent, &captured_stones,
			    &threatened_stones, &saved_stones, &number_open);

    if (0)
      gprintf("%o %1m values: %d %d %d %d %d %d %d %d\n", move, number_edges,
	      number_same_string, number_own, number_opponent,
	      captured_stones, threatened_stones, saved_stones, number_open);

    scores[r] = 0;
    libs = approxlib(move, color_to_move, 10, NULL);

    /* Avoid self atari. */
    if (libs == 1 && captured_stones == 0)
      scores[r] -= 10;

    /* Good to get many liberties. */
    if (libs < 4)
      scores[r] += libs;
    else
      scores[r] += 4;

    /* Very good to capture opponent stones. */
    if (captured_stones > 0)
      scores[r] += 5 + captured_stones;

    /* Good to threaten opponent stones. */
    if (threatened_stones > 0)
      scores[r] += 3;

    /* Extremely good to save own stones. */
    if (saved_stones > 0)
      scores[r] += 10 + saved_stones;
  }
  
  /* Now sort the moves.  We use selection sort since this array will
   * probably never be more than 10 moves long.  In this case, the
   * overhead imposed by quicksort will probably overshadow the gains
   * given by the O(n*log(n)) behaviour over the O(n^2) behaviour of
   * selection sort.
   */
  for (i = 1; i <= moves[0]; i++) {
    /* Find the move with the biggest score. */
    int maxscore = scores[i];
    int max_at = i;
    for (j = i+1; j <= moves[0]; j++) {
      if (scores[j] > maxscore) {
	maxscore = scores[j];
	max_at = j;
      }
    }

    /* Now exchange the move at i with the move at max_at.
     * Don't forget to exchange the scores as well.
     */
    if (max_at != i) {
      int temp = moves[i];
      int tempmax = scores[i];

      moves[i] = moves[max_at];
      scores[i] = scores[max_at];

      moves[max_at] = temp;
      scores[max_at] = tempmax;
    }
  }

  if (0) {
    gprintf("%oVariation %d:\n", count_variations);
    for (i = 1; i <= moves[0]; i++)
      gprintf("%o  %1M %d\n", moves[i], scores[i]);
  }

  if (sgf_dumptree) {
    char buf[500];
    char *pos;
    int chars;
    sprintf(buf, "Move order for %s: %n", funcname, &chars);
    pos = buf + chars;
    for (i = 1; i <= moves[0]; i++) {
      sprintf(pos, "%c%d (%d) %n", J(moves[i]) + 'A' + (J(moves[i]) >= 8),
	      board_size - I(moves[i]), scores[i], &chars);
      pos += chars;
    }
    sgftreeAddComment(sgf_dumptree, buf);
  }
}

/* Clear statistics. */
void
reset_connection_node_counter()
{
  global_connection_node_counter = 0;
}


/* Retrieve statistics. */
int
get_connection_node_counter()
{
  return global_connection_node_counter;
}


/*********************************************************
 *
 * Alternate connection reading algorithm.
 *
 * This code is enabled with the --enable-alternate-connections
 * configure flag at build time or toggled with the
 * --alternate-connections option at run time.
 *
 *********************************************************/

/* This has been copied from reading.c and modified.
 */

#define ADD_CANDIDATE_MOVE(move, distance, moves, distances, num_moves)\
  do {\
    int l;\
    for (l = 0; l < num_moves; l++)\
      if (moves[l] == (move)) {\
        if (distances[l] > distance)\
          distances[l] = distance;\
	break;\
      }\
    if ((l == num_moves) && (num_moves < MAX_MOVES)) {\
      moves[num_moves] = move;\
      distances[num_moves] = distance;\
      (num_moves)++;\
    }\
  } while (0)


static int find_string_connection_moves(int str1, int str2, int color_to_move,
				        int moves[MAX_MOVES],
					int *total_distance);
static void clear_connection_data(struct connection_data *conn);
static int trivial_connection(int str1, int str2, int *move);
static int does_secure_through_ladder(int color, int move, int pos);
static int ladder_capture(int str, int *move);
static int ladder_capturable(int pos, int color);
static int no_escape_from_atari(int str);
static int no_escape_from_ladder(int str);
static int check_self_atari(int pos, int color_to_move);
static int common_vulnerabilities(int a1, int a2, int b1, int b2, int color);
static int common_vulnerability(int apos, int bpos, int color);

/* Try to connect two strings. This function is called in a mutual
 * recursion with recursive_disconnect2(). Return codes is identical to
 * the tactical reading functions. For the has_passed parameter, see the
 * documentation of recursive_disconnect2().
 *
 * The algorithm is
 * 1. Check if the strings are trivially connected or disconnected or
 *    the result is already cached.
 * 2. Find connection moves.
 * 3. Try one move at a time and call recursive_disconnect2() to see
 *    whether we were successful.
 * 4. If no move was found we assume success if the connection
 *    distance was small and failure otherwise.
 */
static int
recursive_connect2(int str1, int str2, int *move, int has_passed)
{
  int color = board[str1];
  int moves[MAX_MOVES];
  int num_moves;
  int distance = FP(0.0);
  int k;
  int xpos;
  int savemove = NO_MOVE;
  int savecode = 0;
  int tried_moves = 0;
  int value;
  
  SETUP_TRACE_INFO2("recursive_connect2", str1, str2);

  if (move)
    *move = NO_MOVE;

  nodes_connect++;
  global_connection_node_counter++;
  
  if (board[str1] == EMPTY || board[str2] == EMPTY) {
    SGFTRACE2(PASS_MOVE, 0, "one string already captured");
    return 0;
  }
  
  if (same_string(str1, str2)) {
    SGFTRACE2(PASS_MOVE, WIN, "already connected");
    return WIN;
  }

  if (nodes_connect > connection_node_limit) {
    SGFTRACE2(PASS_MOVE, 0, "connection node limit reached");
    return 0;
  }
  
  if (stackp > connect_depth2) {
    SGFTRACE2(PASS_MOVE, 0, "connection depth limit reached");
    return 0;
  }

  str1 = find_origin(str1);
  str2 = find_origin(str2);

  if (stackp <= depth && !has_passed
      && tt_get(&ttable, CONNECT, str1, str2, depth - stackp, NULL,
		&value, NULL, &xpos) == 2) {
    TRACE_CACHED_RESULT2(value, value, xpos);
    if (value != 0)
      if (move)
	*move = xpos;

    SGFTRACE2(xpos, value, "cached");
    return value;
  }
  
  if (trivial_connection(str1, str2, &xpos) == WIN) {
    SGFTRACE2(xpos, WIN, "trivial connection");
    READ_RETURN_CONN(CONNECT, str1, str2, depth - stackp, move, xpos, WIN);
  }
  
  num_moves = find_string_connection_moves(str1, str2, color,
      					   moves, &distance);
  
  for (k = 0; k < num_moves; k++) {
    int ko_move;
    xpos = moves[k];
    
    if (komaster_trymove(xpos, color, "recursive_connect2", str1,
			 &ko_move, stackp <= ko_depth && savecode == 0)) {
      tried_moves++;
      if (!ko_move) {
	int acode = recursive_disconnect2(str1, str2, NULL,
					 
					  has_passed);
	popgo();
	if (acode == 0) {
	  SGFTRACE2(xpos, WIN, "connection effective");
	  READ_RETURN_CONN(CONNECT, str1, str2, depth - stackp,
			   move, xpos, WIN);
	}
	/* if the move works with ko we save it, then look for something
	 * better.
	 */
	UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, xpos);
      }
      else {
	if (recursive_disconnect2(str1, str2, NULL,
				 
				  has_passed) != WIN) {
	  savemove = xpos;
	  savecode = KO_B;
	}
	popgo();
      }
    }
  }

  if (tried_moves == 0 && distance < FP(1.0)) {
    SGFTRACE2(NO_MOVE, WIN, "no move, probably connected");
    READ_RETURN_CONN(CONNECT, str1, str2, depth - stackp, move, NO_MOVE, WIN);
  }
  
  if (savecode != 0) {
    SGFTRACE2(savemove, savecode, "saved move");
    READ_RETURN_CONN(CONNECT, str1, str2, depth - stackp,
		     move, savemove, savecode);
  }

  SGFTRACE2(0, 0, NULL);
  READ_RETURN_CONN(CONNECT, str1, str2, depth - stackp, move, NO_MOVE, 0);
}


/* Try to disconnect two strings. This function is called in a mutual
 * recursion with recursive_connect2(). Return codes is identical to
 * the tactical reading functions.
 *
 * The algorithm is
 * 1. Check if the strings are trivially connected or disconnected or
 *    the result is already cached.
 * 2. Find disconnection moves.
 * 3. Try one move at a time and call recursive_connect2() to see
 *    whether we were successful.
 * 4. If no move was found we assume failure if the connection
 *    distance was small. Otherwise we pass and let
 *    recursive_connect2() try to connect. However, if we already have
 *    passed once we just declare success. Whether a pass already has
 *    been made is indicated by the has_passed parameter.
 */
static int
recursive_disconnect2(int str1, int str2, int *move, int has_passed)
{
  int color = board[str1];
  int other = OTHER_COLOR(color);
  int moves[MAX_MOVES];
  int num_moves;
  int distance = FP(0.0);
  int k;
  int xpos;
  int savemove = NO_MOVE;
  int savecode = 0;
  int tried_moves = 0;
  int attack_code1;
  int attack_pos1;
  int attack_code2;
  int attack_pos2;
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;
  int value;

  SETUP_TRACE_INFO2("recursive_disconnect2", str1, str2);
  
  nodes_connect++;
  global_connection_node_counter++;

  if (move)
    *move = NO_MOVE;
  
  if (board[str1] == EMPTY || board[str2] == EMPTY) {
    SGFTRACE2(PASS_MOVE, WIN, "one string already captured");
    return WIN;
  }

  if (same_string(str1, str2)) {
    SGFTRACE2(PASS_MOVE, 0, "already connected");
    return 0;
  }
  
  if (nodes_connect > connection_node_limit) {
    SGFTRACE2(PASS_MOVE, WIN, "connection node limit reached");
    return WIN;
  }
  
  if (stackp > connect_depth2) {
    SGFTRACE2(PASS_MOVE, WIN, "connection depth limit reached");
    return WIN;
  }

  sgf_dumptree = NULL;
  count_variations = 0;

  str1 = find_origin(str1);
  str2 = find_origin(str2);

  attack_code1 = attack(str1, &attack_pos1);
  if (attack_code1 == WIN) {
    sgf_dumptree = save_sgf_dumptree;
    count_variations = save_count_variations;

    SGFTRACE2(attack_pos1, WIN, "one string is capturable");
    if (move)
      *move = attack_pos1;

    return WIN;
  }

  attack_code2 = attack(str2, &attack_pos2);
  if (attack_code2 == WIN) {
    sgf_dumptree = save_sgf_dumptree;
    count_variations = save_count_variations;

    SGFTRACE2(attack_pos2, WIN, "one string is capturable");
    if (move)
      *move = attack_pos2;

    return WIN;
  }

  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;

  if (stackp <= depth
      && tt_get(&ttable, DISCONNECT, str1, str2,
		depth - stackp, NULL,
		&value, NULL, &xpos) == 2) {
    TRACE_CACHED_RESULT2(value, value, xpos);
    if (value != 0)
      if (move)
	*move = xpos;

    SGFTRACE2(xpos, value, "cached");
    return value;
  }

  if (ladder_capture(str1, &xpos) == WIN) {
    SGFTRACE2(xpos, WIN, "first string capturable");
    READ_RETURN_CONN(DISCONNECT, str1, str2, depth - stackp, move, xpos, WIN);
  }
  
  if (ladder_capture(str2, &xpos) == WIN) {
    SGFTRACE2(xpos, WIN, "second string capturable");
    READ_RETURN_CONN(DISCONNECT, str1, str2, depth - stackp, move, xpos, WIN);
  }

  num_moves = find_string_connection_moves(str1, str2, other,
      					   moves, &distance);

  if (attack_code1 != 0 && num_moves < MAX_MOVES) {
    for (k = 0; k < num_moves; k++) {
      if (moves[k] == attack_pos1)
	break;
    }

    if (k == num_moves)
      moves[num_moves++] = attack_pos1;
  }

  if (attack_code2 != 0 && num_moves < MAX_MOVES) {
    for (k = 0; k < num_moves; k++) {
      if (moves[k] == attack_pos2)
	break;
    }

    if (k == num_moves)
      moves[num_moves++] = attack_pos2;
  }

  for (k = 0; k < num_moves; k++) {
    int ko_move;
    xpos = moves[k];
    
    if (komaster_trymove(xpos, other, "recursive_disconnect2", str1,
			 &ko_move, stackp <= ko_depth && savecode == 0)) {
      tried_moves++;
      if (!ko_move) {
	int dcode = recursive_connect2(str1, str2, NULL,
				       has_passed);
	popgo();
	if (dcode == 0) {
	  SGFTRACE2(xpos, WIN, "disconnection effective");
	  READ_RETURN_CONN(DISCONNECT, str1, str2, depth - stackp,
			   move, xpos, WIN);
	}
	/* if the move works with ko we save it, then look for something
	 * better.
	 */
	UPDATE_SAVED_KO_RESULT(savecode, savemove, dcode, xpos);
      }
      else {
	if (recursive_connect2(str1, str2, NULL,
			      
			       has_passed) != WIN) {
	  savemove = xpos;
	  savecode = KO_B;
	}
	popgo();
      }
    }
  }

  if (tried_moves == 0
      && distance >= FP(1.0)
      && (has_passed
	  || !recursive_connect2(str1, str2, NULL, 1))) {
    SGFTRACE2(NO_MOVE, WIN, "no move, probably disconnected");
    READ_RETURN_CONN(DISCONNECT, str1, str2, depth - stackp,
		     move, NO_MOVE, WIN);
  }
  
  if (savecode != 0) {
    SGFTRACE2(savemove, savecode, "saved move");
    READ_RETURN_CONN(DISCONNECT, str1, str2, depth - stackp, 
		     move, savemove, savecode);
  }

  SGFTRACE2(0, 0, NULL);
  READ_RETURN_CONN(DISCONNECT, str1, str2, depth - stackp, move, NO_MOVE, 0);
}


/* Find moves to connect or disconnect the two strings str1 and str2.
 * If color_to_move equals the color of the strings we search for
 * connecting moves and otherwise disconnecting moves. The moves are
 * returned in the moves[] array and the number of moves is the return
 * value of the function. The parameter *total_distance is set to the
 * approximated connection distance between the two strings. This is
 * most useful when no moves are found. If *total_distance is small
 * they are probably already effectively connected and if it is huge
 * they are probably disconnected.
 *
 * The algorithm is to compute connection distances around each string
 * and find points where the sum of the distances is small, or more
 * exactly where the sum of the distances after the move would be
 * small. This can be done with help of delta values returned together
 * with distance values from the function
 * compute_connection_distances(). This "distance after move" measure
 * is modified with various bonuses and then used to order the found
 * moves.
 */
static int
find_connection_moves(int str1, int str2, int color_to_move,
    		      struct connection_data *conn1,
    		      struct connection_data *conn2,
		      int max_dist1, int max_dist2,
		      int moves[MAX_MOVES], int total_distance,
		      int cutoff)
{
  int color = board[str1];
  int other = OTHER_COLOR(color);
  int connect_move = (color_to_move == color);
  int r;
  int distances[MAX_MOVES];
  int num_moves = 0;
  int acode = 0;
  int attack_move = NO_MOVE;
  int dcode = 0;
  int defense_move = NO_MOVE;
  int k;
  int i, j;
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;
  int distance_limit;

  /* We turn off the sgf traces here to avoid cluttering them up with
   * tactical reading moves.
   */
  sgf_dumptree = NULL;
  count_variations = 0;

  /* Loop through the points with smallish distance from str1 and look
   * for ones also having a small distance to str2.
   */
  for (r = 0; r < conn1->queue_end; r++) {
    int pos = conn1->queue[r];
    int dist1 = conn1->distances[pos];
    int deltadist1 = conn1->deltas[pos];
    int dist2 = conn2->distances[pos];
    int deltadist2 = conn2->deltas[pos];
    int d1;
    int d2;
    int distance;
    
    if (dist1 - deltadist1 + dist2 - deltadist2 > FP(2.5)
	|| dist1 > max_dist1 + FP(0.2)
	|| dist2 > max_dist2 + FP(0.2))
      continue;

    if (verbose > 0)
      gprintf("%oMove %1m, (%f, %f, %f, %f)\n", pos, 
	      FIXED_TO_FLOAT(dist1), FIXED_TO_FLOAT(deltadist1), 
	      FIXED_TO_FLOAT(dist2), FIXED_TO_FLOAT(deltadist2));

    /* The basic quality of the move is the sum of the distances to
     * each string minus the two delta values. This distance value
     * will subsequently be modified to take other factors into
     * account.
     */
    d1 = dist1 - deltadist1;
    d2 = dist2 - deltadist2;
    distance = d1 + d2;
    if (verbose > 0)
      gprintf("%o  %f, primary distance\n", FIXED_TO_FLOAT(distance));
    
    /* Bonus if d1 and d2 are well balanced. */
    if ((3 * d1) / 2 > d2 && (3 * d2) / 2 > d1) {
      distance -= FP(0.1);
      if (verbose > 0)
	gprintf("%o  -0.1, well balanced\n");
    }

    /* Check whether the move is "between" the two strings. */
    if (conn1->coming_from[pos] != NO_MOVE
	&& conn1->coming_from[pos] == conn2->coming_from[pos]) {
      if (verbose > 0)
	gprintf("%o  discarded, not between strings\n");
      continue;
    }
    
    if (board[pos] == EMPTY) {
      if (check_self_atari(pos, color_to_move)) {
	ADD_CANDIDATE_MOVE(pos, distance, moves, distances, num_moves);
      }
      else {
	if (verbose > 0)
	  gprintf("%o  discarded, self atari\n");
      }
    }
    else if (board[pos] == other) {
      attack_and_defend(pos, &acode, &attack_move, &dcode, &defense_move);
      if (verbose > 0)
	gprintf("%o  attack with code %d at %1m, defense with code %d at %1m\n",
		acode, attack_move, dcode, defense_move);
      
      if (connect_move && acode != 0) {
	if (dcode == 0) {
	  distance += FP(0.5);
	  if (verbose > 0)
	    gprintf("%o  +0.5, no defense\n");
	}
	else {
	  if (conn1->distances[attack_move]
	      + conn2->distances[attack_move] > dist1 + dist2) {
	    distance += FP(0.5);
	    if (verbose > 0)
	      gprintf("%o  +0.5, attack point not on shortest path\n");
	  }
	}
	ADD_CANDIDATE_MOVE(attack_move, distance - FP(0.15), moves, distances,
			   num_moves);
	if (verbose > 0)
	  gprintf("%o  -0.15 at %1m, capturing a string\n", attack_move);
      }
      else if (!connect_move && acode != 0 && dcode != 0) {
	ADD_CANDIDATE_MOVE(defense_move, distance - FP(0.5), moves, distances,
			   num_moves);
	if (verbose > 0)
	  gprintf("%o  -0.5 at %1m, defending a string\n", defense_move);
      }
    }
    else if (board[pos] == color) {
      /* Check whether there are common vulnerable points. */
      for (k = 0; k < 4; k++) {
	int apos, bpos;
	if (k & 1)
	  apos = conn1->vulnerable1[pos];
	else 
	  apos = conn1->vulnerable2[pos];

	if (k & 2)
	  bpos = conn2->vulnerable1[pos];
	else 
	  bpos = conn2->vulnerable2[pos];

	if (common_vulnerability(apos, bpos, color)) {
	  if (check_self_atari(apos, color_to_move)) {
	    ADD_CANDIDATE_MOVE(apos, distance, moves, distances, num_moves);
	    if (verbose > 0)
	      gprintf("%o  +0.0 at %1m, vulnerability\n", apos);
	  }

	  if (bpos != apos
	      && check_self_atari(bpos, color_to_move)) {
	    ADD_CANDIDATE_MOVE(bpos, distance, moves, distances, num_moves);
	    if (verbose > 0)
	      gprintf("%o  +0.0 at %1m, vulnerability\n", bpos);
	  }
	}
      } 
    }
  }

  /* Modify the distance values for the moves with various bonuses. */
  for (r = 0; r < num_moves; r++) {
    int move = moves[r];
    int adjacent_to_attacker = 0;
    int bonus_given = 0;

    for (k = 0; k < 4; k++) {
      int pos = move + delta[k];
      if (board[pos] == other) {
	adjacent_to_attacker = 1;
	distances[r] -= FP(0.15);
	if (verbose > 0)
	  gprintf("%o%1M -0.15, adjacent to attacker string\n", move);
	if (countlib(pos) <= 2) {
	  distances[r] -= FP(0.2);
	  if (verbose > 0)
	    gprintf("%o%1M -0.2, adjacent to attacker string with at most two liberties\n", move);
	  if ((connect_move || !bonus_given)
	      && (conn1->distances[move] - conn1->deltas[move] <= FP(0.5)
		  || conn1->distances[pos] - conn1->deltas[pos] <= FP(0.5))
	      && (conn2->distances[move] - conn2->deltas[move] <= FP(0.5)
		  || conn2->distances[pos] - conn2->deltas[pos] <= FP(0.5))
	      && conn1->distances[pos] < total_distance
	      && conn2->distances[pos] < total_distance) {
	    bonus_given = 1;
	    distances[r] -= FP(0.7);
	    if (verbose > 0)
	      gprintf("%o%1M -0.7, capture or atari of immediately connecting string\n", move);
	  }
	}
      }
      else if (board[pos] == color) {
	if (countlib(pos) <= 2) {
	  distances[r] -= FP(0.2);
	  if (verbose > 0)
	    gprintf("%o%1M -0.2, adjacent to defender string with at most two liberties\n", move);
	}
	/* The code above (in the 'board[pos] == other' branch) makes
	 * perfect sense for the defender, but has a tendency to
	 * overestimate solid connection defenses when the attacker's
	 * stones happen to be in atari, specially when capturing some
	 * defender stones instead would help just as well, if not better.
	 * The following code compensates in such kind of situations.
	 * See connection:111 and gunnar:53 for example.
	 */
	if (!connect_move && countlib(pos) == 1
	    /* let's avoid ko and snapbacks */
	    && accuratelib(move, other, 2, NULL) > 1) {
	  int adjs[MAXCHAIN];
	  int bonus;
	  bonus = FP(0.1) * chainlinks2(pos, adjs, 2);
	  bonus += FP(0.5) * chainlinks2(pos, adjs, 1);
	  distances[r] -= bonus;
	  if (verbose > 0)
	    gprintf("%o%1M -%f, capture of defender string\n",
		    move, FIXED_TO_FLOAT(bonus));
	}
      }
    }
    if (adjacent_to_attacker
	&& !connect_move
	&& is_edge_vertex(move)) {
      distances[r] -= FP(0.1);
      if (verbose > 0)
	gprintf("%o%1M -0.1, disconnect move on edge\n", move);
    }

    if (ladder_capturable(move, color_to_move)) {
      distances[r] += FP(0.3);
      if (verbose > 0)
	gprintf("%o%1M +0.3, can be captured in a ladder\n", move);
    }

    /* Bonus for moves adjacent to endpoint strings with 3 liberties.
     * Neighbor strings with less than 3 liberties have already
     * generated a bonus above.
     */
    if ((liberty_of_string(move, str1)
	 && countlib(str1) == 3)
	|| (ON_BOARD(str2) && liberty_of_string(move, str2)
	    && countlib(str2) == 3)) {
      distances[r] -= FP(0.1);
      if (verbose > 0)
	gprintf("%o%1M -0.1, liberty of endpoint string with 3 libs\n", move);
    }
  }

  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;

  /* Now sort the moves.  We use selection sort since this array will
   * probably never be more than 10 moves long.  In this case, the
   * overhead imposed by quicksort will probably overshadow the gains
   * given by the O(n*log(n)) behaviour over the O(n^2) behaviour of
   * selection sort.
   */
  for (i = 0; i < num_moves; i++) {
    /* Find the move with the smallest distance. */
    int mindistance = distances[i];
    int min_at = i;
    for (j = i + 1; j < num_moves; j++) {
      if (distances[j] < mindistance) {
	mindistance = distances[j];
	min_at = j;
      }
    }

    /* Now exchange the move at i with the move at min_at.
     * Don't forget to exchange the distances as well.
     */
    if (min_at != i) {
      int temp = moves[i];
      int tempmin = distances[i];

      moves[i] = moves[min_at];
      distances[i] = distances[min_at];

      moves[min_at] = temp;
      distances[min_at] = tempmin;
    }
  }

  if (verbose > 0) {
    gprintf("%oSorted moves:\n");
    for (i = 0; i < num_moves; i++)
      gprintf("%o%1M %f\n", moves[i], FIXED_TO_FLOAT(distances[i]));
  }

  if (sgf_dumptree) {
    char buf[500];
    char *pos;
    int chars;
    sprintf(buf, "Move order for %sconnect: %n",
	    connect_move ? "" : "dis", &chars);
    pos = buf + chars;
    for (i = 0; i < num_moves; i++) {
      sprintf(pos, "%c%d (%4.2f) %n", J(moves[i]) + 'A' + (J(moves[i]) >= 8),
	      board_size - I(moves[i]), FIXED_TO_FLOAT(distances[i]),
	      &chars);
      pos += chars;
    }
    if (cutoff < HUGE_CONNECTION_DISTANCE) {
      sprintf(pos, "(cutoff %f)%n", FIXED_TO_FLOAT(cutoff), &chars);
      pos += chars;
    }
    sgftreeAddComment(sgf_dumptree, buf);
  }

  if (num_moves == 0)
    return num_moves;

  /* Filter out moves with distance at least 1.5 more than the best
   * move, or with distance higher than the cutoff specified.
   *
   * In order to further reduce the branching factor, a decreasing
   * cutoff is applied between candidates. For instance, in this case
   *   1. d    2. d+0.5   3. d+1.0   4. d+1.5
   * the 4th candidate will be tested, while in following one
   *   1. d    2. d+0.1   3. d+0.2   4. d+1.5
   * it will be discarded.
   */
  if (num_moves <= 1 || !is_ko(moves[0], color_to_move, NULL))
    distance_limit = distances[0] + FP(1.5);
  else
    distance_limit = distances[1] + FP(1.5);

  /* Special case: If the second best move has a distance less than 1,
   * include it if even if the best move has a very low distance.
   */
  if (num_moves > 1
      && distances[1] < FP(1.0)
      && distances[1] > distance_limit)
    distance_limit = distances[1];
  
  for (r = 0; r < num_moves; r++) {
    if (r > 1
	&& distances[r] > distances[r-1]
	&& distances[r] - distances[r-1] > (8 - r) * FP(0.2))
      break;
    if (distances[r] > distance_limit
	|| distances[r] > cutoff)
      break;
  }
  num_moves = r;

  return num_moves;
}

static int
find_string_connection_moves(int str1, int str2, int color_to_move,
		             int moves[MAX_MOVES], int *total_distance)
{
  struct connection_data conn1;
  struct connection_data conn2;
  int max_dist1;
  int max_dist2;
  int num_moves;
  int lib;
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;

  /* We turn off the sgf traces here to avoid cluttering them up with
   * tactical reading moves.
   */
  sgf_dumptree = NULL;
  count_variations = 0;

  compute_connection_distances(str1, str2, FP(3.051), &conn1, 1);
  compute_connection_distances(str2, str1, FP(3.051), &conn2, 1);

  if (findlib(str1, 1, &lib) == 1) {
    conn1.distances[lib] = 0;
    conn1.coming_from[lib] = NO_MOVE;
    conn2.distances[lib] = conn2.distances[str1];
    conn2.coming_from[lib] = conn1.coming_from[str1];
  }

  if (findlib(str2, 1, &lib) == 1) {
    conn2.distances[lib] = 0;
    conn1.distances[lib] = conn1.distances[str2];
  }

  max_dist1 = conn1.distances[str2];
  max_dist2 = conn2.distances[str1];

  *total_distance = gg_min(max_dist1, max_dist2);

  if (verbose > 0) {
    gprintf("%oVariation %d\n", save_count_variations);
    dump_stack();
    showboard(0);
    print_connection_distances(&conn1);
    print_connection_distances(&conn2);
  }

  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;

  num_moves = find_connection_moves(str1, str2, color_to_move,
      				    &conn1, &conn2, max_dist1, max_dist2,
      			   	    moves, *total_distance,
				    HUGE_CONNECTION_DISTANCE);
  return num_moves;
}


static void
add_to_start_queue(int pos, int dist, struct connection_data *conn)
{
  conn->queue[conn->queue_end++] = pos;
  conn->distances[pos] = dist;
  conn->deltas[pos] = dist;
  conn->coming_from[pos] = NO_MOVE;
  conn->vulnerable1[pos] = NO_MOVE;
  conn->vulnerable2[pos] = NO_MOVE;
}


void
init_connection_data(int color, const signed char goal[BOARDMAX],
    		     int target, int cutoff,
		     struct connection_data *conn, int speculative)
{
  int pos;
  signed char mark[BOARDMAX];

  memset(mark, 0, BOARDMAX);
  VALGRIND_MAKE_WRITABLE(conn, sizeof(conn));
  clear_connection_data(conn);

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (goal[pos]) {
      if (board[pos] == color) {
	int origin = find_origin(pos);

	if (!mark[origin]) {
	  add_to_start_queue(origin, FP(0.0), conn);
	  mark[origin] = 1;
	}
      }
      else if (board[pos] == EMPTY)
        add_to_start_queue(pos, FP(1.0), conn);
    }
  }

  conn->target = target;
  conn->cutoff_distance = cutoff;
  conn->speculative = speculative;
}

static int
find_break_moves(int str, const signed char goal[BOARDMAX], int color_to_move,
		 int moves[MAX_MOVES], int *total_distance)
{
  struct connection_data conn1;
  struct connection_data conn2;
  int max_dist1 = HUGE_CONNECTION_DISTANCE;
  int max_dist2;
  int num_moves;
  int str2 = NO_MOVE;
  int color = board[str];
  int lib;
  int k;
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;

  /* We turn off the sgf traces here to avoid cluttering them up with
   * tactical reading moves.
   */
  sgf_dumptree = NULL;
  count_variations = 0;

  compute_connection_distances(str, NO_MOVE, FP(2.501), &conn1, 1);
  for (k = 0; k < conn1.queue_end; k++)
    if (board[conn1.queue[k]] == color) {
      int stones[MAX_BOARD * MAX_BOARD];
      int num_stones = findstones(conn1.queue[k],
	  			  MAX_BOARD * MAX_BOARD, stones);
      int i;
      for (i = 0; i < num_stones; i++) {
	if (goal[stones[i]]) {
	  str2 = find_origin(stones[i]);
	  TRACE("%oUsing %1m as secondary target.\n", str2);
	  mark_string(str2, breakin_shadow, 1);
	  break;
	}
      }
      if (i < num_stones)
	break;
    }

  /* Add all stones in the goal to the queue. */
  init_connection_data(color, goal, str, FP(2.501), &conn2, 1);

  for (k = 0; k < conn2.queue_end; k++) {
    if (max_dist1 > conn1.distances[conn2.queue[k]])
      max_dist1 = conn1.distances[conn2.queue[k]];
  }

  spread_connection_distances(color, &conn2);

  if (findlib(str, 1, &lib) == 1) {
    conn1.distances[lib] = 0;
    conn1.coming_from[lib] = NO_MOVE;
    conn2.distances[lib] = conn2.distances[str];
    conn2.coming_from[lib] = conn1.coming_from[str];
  }

  max_dist2 = conn2.distances[str];
  *total_distance = gg_min(max_dist1, max_dist2);

  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;

  if (verbose > 0) {
    gprintf("%oVariation %d\n", save_count_variations);
    dump_stack();
    showboard(0);
    print_connection_distances(&conn1);
    print_connection_distances(&conn2);
  }

  {
    int cutoff = HUGE_CONNECTION_DISTANCE;
    if (breakin_depth - stackp <= 5)
      cutoff = FP(1.101) + (breakin_depth - stackp) * FP(0.15);
    num_moves = find_connection_moves(str, str2, color_to_move,
				      &conn1, &conn2, max_dist1, max_dist2,
				      moves, *total_distance, cutoff);
  }

  if (color_to_move != board[str]) {
    int move;
    if (num_moves < MAX_MOVES
	&& ON_BOARD(str2)
	&& ladder_capture(str2, &move)) {
      moves[num_moves++] = move;
    }
  }

  for (k = 0; k < num_moves; k++)
    breakin_shadow[moves[k]] = 1;

  return num_moves;
}


/* Can (str) connect to goal[] if the other color moves first? */
static int
recursive_break(int str, const signed char goal[BOARDMAX], int *move,
    		  int has_passed,
		Hash_data *goal_hash)
{
  int color = board[str];
  int moves[MAX_MOVES];
  int num_moves;
  int distance = FP(0.0);
  int k;
  int xpos;
  int savemove = NO_MOVE;
  int savecode = 0;
  int tried_moves = 0;
  int retval;
  
  SETUP_TRACE_INFO("recursive_break", str);

  if (move)
    *move = NO_MOVE;

  nodes_connect++;
  global_connection_node_counter++;
  
  if (board[str] == EMPTY) {
    SGFTRACE(PASS_MOVE, 0, "one string already captured");
    return 0;
  }
  
  if (nodes_connect > breakin_node_limit) {
    SGFTRACE(PASS_MOVE, 0, "connection node limit reached");
    return 0;
  }
  
  if (stackp > breakin_depth) {
    SGFTRACE(PASS_MOVE, 0, "connection depth limit reached");
    return 0;
  }

  str = find_origin(str);
  if (stackp <= depth && !has_passed
      && tt_get(&ttable, BREAK_IN, str, NO_MOVE, depth - stackp, goal_hash,
		&retval, NULL, &xpos) == 2) {
    /* FIXME: Use move for move ordering if tt_get() returned 1 */
    TRACE_CACHED_RESULT(retval, xpos);
    SGFTRACE(xpos, retval, "cached");
    if (move)
      *move = xpos;
    return retval;
  }
  
#if 0
  if (trivial_connection(str1, str2, &xpos) == WIN) {
    SGFTRACE2(xpos, WIN, "trivial connection");
    READ_RETURN_HASH(BREAK_IN, str, depth - stackp, goal_hash,
		     move, xpos, WIN);
  }
#endif
  
  num_moves = find_break_moves(str, goal, color, moves, &distance);
  
  for (k = 0; k < num_moves; k++) {
    int ko_move;
    xpos = moves[k];
    
    if (komaster_trymove(xpos, color, "recursive_break", str,
			 &ko_move, stackp <= ko_depth && savecode == 0)) {
      tried_moves++;
      if (!ko_move) {
	int acode = recursive_block(str, goal, NULL, has_passed, goal_hash);
	popgo();
	if (acode == 0) {
	  SGFTRACE(xpos, WIN, "break effective");
	  READ_RETURN_HASH(BREAK_IN, str, depth - stackp, goal_hash,
			   move, xpos, WIN);
	}
	/* if the move works with ko we save it, then look for something
	 * better.
	 */
	UPDATE_SAVED_KO_RESULT(savecode, savemove, acode, xpos);
      }
      else {
	if (recursive_block(str, goal, NULL, has_passed, goal_hash) != WIN) {
	  savemove = xpos;
	  savecode = KO_B;
	}
	popgo();
      }
    }
  }

  /* Because of a couple differences between the break-in and the
   * connection reading code, we can't afford to be as optimistic
   * as in recursive_connect2() here. See nando:32
   */
  if (tried_moves == 0 && distance < FP(0.89)) {
    SGFTRACE(NO_MOVE, WIN, "no move, probably connected");
    READ_RETURN_HASH(BREAK_IN, str, depth - stackp, goal_hash,
		     move, NO_MOVE, WIN);
  }
  
  if (savecode != 0) {
    SGFTRACE(savemove, savecode, "saved move");
    READ_RETURN_HASH(BREAK_IN, str, depth - stackp, goal_hash,
		     move, savemove, savecode);
  }

  SGFTRACE(0, 0, NULL);
  READ_RETURN_HASH(BREAK_IN, str, depth - stackp, goal_hash, move, NO_MOVE, 0);
}


/* Can (str) connect to goal[] if the other color moves first? */
static int
recursive_block(int str, const signed char goal[BOARDMAX], int *move,
		int has_passed,	Hash_data *goal_hash)
{
  int color = board[str];
  int other = OTHER_COLOR(color);
  int moves[MAX_MOVES];
  int num_moves;
  int distance = FP(0.0);
  int k;
  int xpos;
  int savemove = NO_MOVE;
  int savecode = 0;
  int tried_moves = 0;
  int retval;
  SETUP_TRACE_INFO("recursive_block", str);
  
  nodes_connect++;
  global_connection_node_counter++;

  if (move)
    *move = NO_MOVE;
  
  if (board[str] == EMPTY) {
    SGFTRACE(PASS_MOVE, WIN, "string already captured");
    return WIN;
  }

#if 0
  if (same_string(str1, str2)) {
    SGFTRACE(PASS_MOVE, 0, "already connected");
    return 0;
  }
#endif
  
  if (nodes_connect > breakin_node_limit) {
    SGFTRACE(PASS_MOVE, WIN, "connection node limit reached");
    return WIN;
  }
  
  if (stackp > breakin_depth) {
    SGFTRACE(PASS_MOVE, WIN, "connection depth limit reached");
    return WIN;
  }
  
  str = find_origin(str);
  if (stackp <= depth
      && tt_get(&ttable, BLOCK_OFF, str, NO_MOVE,
		depth - stackp, goal_hash, &retval, NULL, &xpos) == 2) {
    TRACE_CACHED_RESULT(retval, xpos);
    SGFTRACE(xpos, retval, "cached");
    if (move)
      *move = xpos;
    return retval;
  }

  if (ladder_capture(str, &xpos) == WIN) {
    SGFTRACE(xpos, WIN, "string capturable");
    READ_RETURN_HASH(BLOCK_OFF, str, depth - stackp, goal_hash,
		     move, xpos, WIN);
  }
  
  num_moves = find_break_moves(str, goal, other, moves, &distance);
  
  for (k = 0; k < num_moves; k++) {
    int ko_move;
    xpos = moves[k];
    
    if (komaster_trymove(xpos, other, "recursive_block", str,
			 &ko_move, stackp <= ko_depth && savecode == 0)) {
      tried_moves++;
      if (!ko_move) {
	int dcode = recursive_break(str, goal, NULL, has_passed, goal_hash);
	popgo();
	if (dcode == 0) {
	  SGFTRACE(xpos, WIN, "block effective");
	  READ_RETURN_HASH(BLOCK_OFF, str, depth - stackp, goal_hash,
			   move, xpos, WIN);
	}
	/* if the move works with ko we save it, then look for something
	 * better.
	 */
	UPDATE_SAVED_KO_RESULT(savecode, savemove, dcode, xpos);
      }
      else {
	if (recursive_break(str, goal, NULL,
			    has_passed, goal_hash) != WIN) {
	  savemove = xpos;
	  savecode = KO_B;
	}
	popgo();
      }
    }
  }

  if (tried_moves == 0
      && distance >= FP(1.0)
      && (has_passed
	  || !recursive_break(str, goal, NULL, 1,
	                      goal_hash))) {
    SGFTRACE(NO_MOVE, WIN, "no move, probably disconnected");
    READ_RETURN_HASH(BLOCK_OFF, str, depth - stackp, goal_hash,
		     move, NO_MOVE, WIN);
  }
  
  if (savecode != 0) {
    SGFTRACE(savemove, savecode, "saved move");
    READ_RETURN_HASH(BLOCK_OFF, str, depth - stackp, goal_hash,
		     move, savemove, savecode);
  }

  SGFTRACE(0, 0, NULL);
  READ_RETURN_HASH(BLOCK_OFF, str, depth - stackp, goal_hash,
		   move, NO_MOVE, 0);
}



/* Externally callable frontend to recursive_break.
 * Returns WIN if (str) can connect to the area goal[] (which may or may
 * not contain stones), if he gets the first move.
 */
int
break_in(int str, const signed char goal[BOARDMAX], int *move)
{
  int dummy_move;
  int save_verbose;
  int result;
  int reading_nodes_when_called = get_reading_node_counter();
  double start = 0;
  int tactical_nodes;
  Hash_data goal_hash = goal_to_hashvalue(goal);

  if (move == NULL)
    move = &dummy_move;
  
  nodes_connect = 0;
  *move = PASS_MOVE;
  
  if (board[str] == EMPTY)
    return 0;
  str = find_origin(str);

  if (search_persistent_breakin_cache(BREAK_IN, str, &goal_hash,
				      breakin_node_limit, &result, move)) {
    if (debug & DEBUG_BREAKIN) {
      gprintf("Break-in from %1m to:\n", str);
      goaldump(goal);
      gprintf("Result cached: %s %1m\n", result_to_string(result), *move);
    }
    return result;
  }

  save_verbose = verbose;
  if (verbose > 0)
    verbose--;
  start = gg_cputime();
  memcpy(breakin_shadow, goal, sizeof(breakin_shadow));
  result = recursive_break(str, goal, move, 0, &goal_hash);
  verbose = save_verbose;
  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called;
  if (debug & DEBUG_BREAKIN) {
    gprintf("%obreak_in    %1M, result %s %1M (%d, %d nodes, %f seconds)\n",
	    str, result_to_string(result), *move,
	    nodes_connect, tactical_nodes, gg_cputime() - start);
    goaldump(goal);
    dump_stack();
  }
  if (0) {
    gprintf("%obreak_in %1m %d %1m ", str, result, *move);
    dump_stack();
    goaldump(goal);
  }
  store_persistent_breakin_cache(BREAK_IN, str, &goal_hash, result, *move,
      				 tactical_nodes, breakin_node_limit,
				 breakin_shadow);

  return result;
}


/* Externably callable frontend to recursive_block_off.
 * Returns WIN if (str) cannot connect to the area goal[] (which may or may
 * not contain stones), if the other color moves first.
 */
int
block_off(int str, const signed char goal[BOARDMAX], int *move)
{
  int dummy_move;
  int result;
  int save_verbose;
  int reading_nodes_when_called = get_reading_node_counter();
  double start = 0;
  int tactical_nodes;
  Hash_data goal_hash = goal_to_hashvalue(goal);
  
  if (move == NULL)
    move = &dummy_move;
  
  nodes_connect = 0;
  *move = PASS_MOVE;
  
  str = find_origin(str);
  if (search_persistent_breakin_cache(BLOCK_OFF, str, &goal_hash,
				      breakin_node_limit, &result, move)) {
    if (debug & DEBUG_BREAKIN) {
      gprintf("Blocking off %1m from:\n", str);
      goaldump(goal);
      gprintf("Result cached: %s %1m\n", result_to_string(result), *move);
    }
    return result;
  }

  save_verbose = verbose;
  if (verbose > 0)
    verbose--;
  start = gg_cputime();
  memcpy(breakin_shadow, goal, sizeof(breakin_shadow));
  result = recursive_block(str, goal, move, 0, &goal_hash);
  verbose = save_verbose;
  tactical_nodes = get_reading_node_counter() - reading_nodes_when_called;

  if (debug & DEBUG_BREAKIN) {
    gprintf("%oblock_off %1m, result %s %1m (%d, %d nodes, %f seconds)\n",
	    str, result_to_string(result), *move,
	    nodes_connect, tactical_nodes, gg_cputime() - start);
    goaldump(goal);
    dump_stack();
  }
  if (0) {
    gprintf("%oblock_off %1m %d %1m ", str, result, *move);
    goaldump(goal);
    dump_stack();
  }
  store_persistent_breakin_cache(BLOCK_OFF, str, &goal_hash, result, *move,
				 tactical_nodes, breakin_node_limit,
				 breakin_shadow);

  return result;
}


/* Store a possibly expensive decision for later evaluation. The
 * data getting stored should be self-explanatory.
 * The job of the helper function is to
 * - decide whether the spreading step will be allowed (typically
 *   depending on a latter)
 * - add the relevant positions to the connection queue in case the test
 *   was successful.
 *
 * Elements in the heap are kept sorted according to smallest distance.
 */
static void
push_connection_heap_entry(struct connection_data *conn, int distance,
			   int coming_from, int target,
			   connection_helper_fn_ptr helper)
{
  int k;
  int parent;
  struct heap_entry *new_entry = &conn->heap_data[conn->heap_data_size];

  gg_assert(conn->heap_data_size < 4 * BOARDMAX);
  gg_assert(conn->heap_size < BOARDMAX);

  /* Create new heap entry. */
  new_entry->distance	 = distance;
  new_entry->coming_from = coming_from;
  new_entry->target	 = target;
  new_entry->helper	 = helper;

  /* And insert it into the heap. */
  conn->heap_data_size++;

  for (k = conn->heap_size++; k > 0; k = parent) {
    parent = (k - 1) / 2;
    if (conn->heap[parent]->distance <= distance)
      break;

    conn->heap[k] = conn->heap[parent];
  }

  conn->heap[k] = new_entry;
}


/* Delete the first entry from the heap. */
static void
pop_connection_heap_entry(struct connection_data *conn)
{
  int k;
  int child;

  conn->heap_size--;
  for (k = 0; 2 * k + 1 < conn->heap_size; k = child) {
    child = 2 * k + 1;
    if (conn->heap[child]->distance > conn->heap[child + 1]->distance)
      child++;
    if (conn->heap[child]->distance >= conn->heap[conn->heap_size]->distance)
      break;

    conn->heap[k] = conn->heap[child];
  }

  conn->heap[k] = conn->heap[conn->heap_size];
}


#define ENQUEUE(conn, from, pos, dist, delta, v1, v2)			\
  do {									\
    if (dist < conn->distances[pos]) {					\
      if (conn->distances[pos] == HUGE_CONNECTION_DISTANCE)		\
	conn->queue[conn->queue_end++] = pos;				\
      conn->distances[pos]   = dist;					\
      conn->deltas[pos]      = delta;					\
      conn->coming_from[pos] = from;					\
      conn->vulnerable1[pos] = v1;					\
      conn->vulnerable2[pos] = v2;					\
    }									\
  } while(0)

#define ENQUEUE_STONE(conn, from, pos, dist, delta, v1, v2)		\
  do {									\
    int origin = find_origin(pos);					\
    if (dist < conn->distances[origin]) {				\
      if (conn->distances[origin] == HUGE_CONNECTION_DISTANCE)		\
	conn->queue[conn->queue_end++] = origin;			\
      conn->distances[origin]	= dist;					\
      conn->deltas[origin]	= delta;				\
      conn->coming_from[origin] = from;					\
      conn->vulnerable1[origin] = v1;					\
      conn->vulnerable2[origin] = v2;					\
      if (origin == conn->target && dist < conn->cutoff_distance)	\
	conn->cutoff_distance = dist - FP(0.0001);			\
    }									\
  } while(0)


static void
case_6_7_helper(struct connection_data *conn, int color)
{
  struct heap_entry *data = conn->heap[0];
  int pos = data->coming_from;
  int apos = data->target;
  int other = OTHER_COLOR(color);

  if (ladder_capturable(apos, other))
    ENQUEUE(conn, pos, apos, data->distance, FP(0.6), apos, NO_MOVE);
  else {
    int this_delta 
      = FP(0.85) + FP(0.05) * gg_min(approxlib(apos, other, 5, NULL), 5);
    ENQUEUE(conn, pos, apos, data->distance + this_delta - FP(0.6), this_delta,
	    NO_MOVE, NO_MOVE);
  }
}


static void
case_9_10_helper(struct connection_data *conn, int color)
{
  struct heap_entry *data = conn->heap[0];
  int pos = data->coming_from;
  int apos = data->target;

  UNUSED(color);

  if (no_escape_from_ladder(apos))
    ENQUEUE_STONE(conn, pos, apos, data->distance, FP(0.3), NO_MOVE, NO_MOVE);
  else {
    if (conn->speculative) {
      ENQUEUE_STONE(conn, pos, apos, data->distance + FP(0.7), FP(1.0),
		    NO_MOVE, NO_MOVE);
    }
    else {
      ENQUEUE_STONE(conn, pos, apos, data->distance + FP(0.8), FP(1.1),
		    NO_MOVE, NO_MOVE);
    }
  }
}


static void
case_16_17_18_helper(struct connection_data *conn, int color)
{
  struct heap_entry *data = conn->heap[0];
  int pos = data->coming_from;
  int bpos = data->target;
  int apos = SOUTH(gg_min(pos, bpos));
  int gpos = NORTH(gg_max(pos, bpos));
  int other = OTHER_COLOR(color);

  if (board[apos] == EMPTY
      && does_secure_through_ladder(color, bpos, apos))
    ENQUEUE(conn, pos, bpos, data->distance, FP(1.0), apos, NO_MOVE);
  else if (board[gpos] == EMPTY
	   && does_secure_through_ladder(color, bpos, gpos))
    ENQUEUE(conn, pos, bpos, data->distance, FP(1.0), gpos, NO_MOVE);
  else if (conn->distances[bpos] > data->distance + FP(0.3)) {
    if (board[apos] == EMPTY
	&& board[gpos] == other
	&& countlib(gpos) <= 3)
      ENQUEUE(conn, pos, bpos, data->distance + FP(0.3), FP(1.0),
	      apos, NO_MOVE);
    else if (board[gpos] == EMPTY
	     && board[apos] == other
	     && countlib(apos) <= 3)
      ENQUEUE(conn, pos, bpos, data->distance + FP(0.3), FP(1.0),
	      gpos, NO_MOVE);
    else
      ENQUEUE(conn, pos, bpos, data->distance + FP(0.6), FP(0.9),
	      NO_MOVE, NO_MOVE);
  }
}


/* Do the real work of computing connection distances.
 * This is a rough approximation of the number of moves required to secure
 * a connection. We also compute delta values which are intended to tell how
 * big difference a particular move locally has on the connection
 * distance. However, remember that this is only a heuristic with the
 * sole purpose of helping to find relevant moves for connection
 * problems.
 *
 * The algorithm is to propagate connection values outwards using a
 * breadth-first searching strategy, implemented through an implicitly
 * sorted queue. The propagation to new vertices depends on
 * geometrical features with significance for connections. E.g. a
 * bamboo joint is recognized and the distance added when passing
 * through it is small. New points are added to the queue through the
 * ENQUEUE macro above. This checks whether the point has already been
 * entered on the queue and updates the distance and delta values if
 * the previous ones were worse. When a stone is entered, all stones
 * of the string are added to the queue simultaneously.
 *
 * (target) is the other string when called from find_connection_moves().
 * (It can be set to NO_MOVE otherwise.)
 *
 * The propagation is inhibited when the distance becomes too large,
 * or larger than the shortest path found to the target so far.
 *
 *
 * The purpose of the fields called vulnerable is to keep track of
 * points where the attacker can threaten an individual
 * connection. For example the diagonal formation
 *
 * .O
 * O.
 *
 * is considered a small distance link but both the empty vertices are
 * marked as vulnerable. Thus if we are computing connection distance
 * from the lower left O in this diagram,
 *
 * XXX     XXX
 * .O.     .O.
 * O.O     OaO
 * .X.     .X.
 *
 * the distance to the middle O is small but the second diagonal link
 * to the lower right O stone is not given a small distance since a
 * had already been marked as vulnerable.
 *
 * It should also be pointed out that this reasoning is not relevant
 * in this position where X has no cutting potential,
 *
 * XXX     XXX
 * .O.     .O.
 * O.O     OaO
 * ...     ...
 *
 * That is because there is a pattern directly recognizing the safe
 * link between the two lower stones, without taking the longer road
 * over the two diagonal links.
 *
 * (color) is the color for which we are computing connection distances,
 * (target) the position we want to reach (can be set to NO_MOVE),
 * (*conn) has to have the queue initialized with the positions
 * from which we want to know the distances,
 * (cutoff_distance) is the highest distance before we give up,
 * (speculative) controls some special cases in the propagation rules
 * below.
 *
 * As an optimization, new points are either added directly via the ENQUEUE
 * macro if the necessary test is an immediate (usually purely geometric)
 * check, or if the decision is more expensive (usually depending on a
 * ladder), it gets postponed and stored via push_connection_heap_entry()
 * for later evaluation.
 */

void
spread_connection_distances(int color, struct connection_data *conn)
{
  int other = OTHER_COLOR(color);
  int stones[MAX_BOARD * MAX_BOARD];
  int num_stones = 0;
  int stone = 0;

  /* Loop until we reach the end of the queue. */
  while (conn->queue_start < conn->queue_end || conn->heap_size > 0) {
    int k;
    int pos;
    int distance;

    /* Delete heap entries for positions that have already been reached
     * with smaller distance.
     */
    while (conn->heap_size > 0
	   && conn->heap[0]->distance >= conn->distances[conn->heap[0]->target])
      pop_connection_heap_entry(conn);

    if (stone == num_stones) {
      int best_index = -1;
      int smallest_dist = HUGE_CONNECTION_DISTANCE;

      if (conn->queue_start == conn->queue_end) {
	if (conn->heap_size > 0) {
	  conn->heap[0]->helper(conn, color);
	  pop_connection_heap_entry(conn);
	}

	continue;
      }

      gg_assert(conn->queue_end <= MAX_BOARD * MAX_BOARD);

      /* Find the smallest distance among the queued points. */
      for (k = conn->queue_start; k < conn->queue_end; k++) {
	if (conn->distances[conn->queue[k]] < smallest_dist) {
	  smallest_dist = conn->distances[conn->queue[k]];
	  best_index = k;
	}
      }

      /* Exchange the best point with the first element in the queue. */
      if (best_index != conn->queue_start) {
	int temp = conn->queue[conn->queue_start];
	conn->queue[conn->queue_start] = conn->queue[best_index];
	conn->queue[best_index] = temp;
      }

      /* If the first element in heap has smaller distance than the
       * smallest we have found so far, call the relevant helper function
       * now, and delete the heap entry.
       */
      if (conn->heap_size > 0 && conn->heap[0]->distance < smallest_dist) {
	conn->heap[0]->helper(conn, color);
	pop_connection_heap_entry(conn);
	continue;
      }

      /* Now we are ready to pick the first element in the queue and
       * process it.
       */
      pos = conn->queue[conn->queue_start++];
      if (board[pos] != EMPTY) {
	num_stones = findstones(pos, MAX_BOARD * MAX_BOARD, stones);
	pos = stones[0];
	stone = 1;
      }
    }
    else {
      pos = stones[stone++];
      conn->distances[pos]   = conn->distances[stones[0]];
      conn->deltas[pos]      = conn->deltas[stones[0]];
      conn->coming_from[pos] = conn->coming_from[stones[0]];
      conn->vulnerable1[pos] = conn->vulnerable1[stones[0]];
      conn->vulnerable2[pos] = conn->vulnerable2[stones[0]];
    }

    /* No further propagation if the distance is too large. */
    distance = conn->distances[pos];
    if (distance > conn->cutoff_distance)
      break;

    /* Search for new vertices to propagate to. */
    if (board[pos] == color) {
      for (k = 0; k < 4; k++) {
	/* List of relative coordinates. (pos) is marked by *.
	 *
	 *  jef.
	 *  igb.
	 * kh*ac
	 *  ....
	 *
	 */
	int right = delta[k];
	int up = delta[(k+1)%4];

	/* FIXME: Compactify this list. */
	int apos = pos + right;
	int bpos = pos + right + up;
	int cpos = pos + 2 * right;
	int epos = pos + 2*up;
	int fpos = pos + right + 2*up;
	int gpos = pos + up;
	int hpos = pos - right;
	int ipos = pos - right + up;
	int jpos = pos - right + 2 * up;
	int kpos = pos - 2 * right;
	
	/* Case 1. "a" is empty and would be suicide for the opponent. */
	if (board[apos] == EMPTY && is_suicide(apos, other))
	  ENQUEUE(conn, pos, apos, distance, FP(0.0), apos, NO_MOVE);
	
	/* Case 2. "a" is empty and would be self atari for the opponent. */
	if (board[apos] == EMPTY
	    && conn->distances[apos] > distance + FP(0.1)
	    && is_self_atari(apos, other)) {
	  int lib;
	  int vulnerable1 = NO_MOVE;
	  int vulnerable2 = NO_MOVE;
	  if (approxlib(apos, other, 1, &lib) >= 1) {
	    if (approxlib(lib, other, 2, NULL) > 2)
	      vulnerable1 = lib;
	    if (countlib(pos) == 2) {
	      int i;
	      for (i = 0; i < 4; i++) {
		if (board[lib + delta[i]] == EMPTY
		    && lib + delta[i] != apos
		    && trymove(lib + delta[i], other,
			       "compute_connection_distances", pos)) {
		  if (ladder_capture(pos, NULL)) {
		    vulnerable2 = lib + delta[i];
		    popgo();
		    break;
		  }
		  popgo();
		}
	      }
	    }
	  }
	  
	  if (!common_vulnerabilities(conn->vulnerable1[pos],
				      conn->vulnerable2[pos],
				      vulnerable1, vulnerable2, color)) {
	    ENQUEUE(conn, pos, apos, distance + FP(0.1), FP(0.1),
		    vulnerable1, vulnerable2);
	  }
	}
	
	/* Case 3. Bamboo joint of "*" + "a" to "e" + "f" through "b" and "g".
	 * Notice that the order of these tests is significant. We must
	 * check bpos before fpos and epos to avoid accessing memory
	 * outside the board array. (Notice that fpos is two steps away
	 * from pos, which we know is on the board.)
	 */
	if (board[apos] == color && board[bpos] == EMPTY
	    && board[fpos] == color && board[epos] == color
	    && board[gpos] == EMPTY) {
	  ENQUEUE(conn, pos, bpos, distance + FP(0.1), FP(0.1),
		  NO_MOVE, NO_MOVE);
	  ENQUEUE(conn, pos, gpos, distance + FP(0.1), FP(0.1),
		  NO_MOVE, NO_MOVE);
	}
	   
	/* Case 4. Diagonal connection to another stone "b" through
	 * empty vertices "a" and "g".
	 */
	if (board[bpos] == color
	    && board[apos] == EMPTY
	    && board[gpos] == EMPTY
	    && !common_vulnerabilities(conn->vulnerable1[pos],
				       conn->vulnerable2[pos],
				       apos, gpos, color)
	    && conn->distances[bpos] > distance + FP(0.1)) {
#if 0
	  ENQUEUE(conn, pos, apos, distance + FP(0.2), FP(0.2),
		  NO_MOVE, NO_MOVE);
	  ENQUEUE(conn, pos, gpos, distance + FP(0.2), FP(0.2),
		  NO_MOVE, NO_MOVE);
#endif
	  ENQUEUE_STONE(conn, pos, bpos, distance + FP(0.1), FP(0.1),
			apos, gpos);
	}

	/* Case 5. Almost bamboo joint.
	 * 
	 */
	if (board[gpos] == EMPTY
	    && board[epos] == color
            && conn->distances[epos] > distance + FP(0.2)
	    && approxlib(gpos, other, 3, NULL) <= 2) {
	  if (board[bpos] == EMPTY
	      && approxlib(bpos, color, 3, NULL) >= 3
	      && (board[apos] == color
		  || (board[apos] == EMPTY
		      && countlib(pos) > 2
		      && !common_vulnerabilities(conn->vulnerable1[pos],
						 conn->vulnerable2[pos],
						 apos, gpos, color)
		      && approxlib(apos, other, 3, NULL) <= 2))
	      && (board[fpos] == color
		  || (board[fpos] == EMPTY
		      && countlib(epos) > 2
		      && !common_vulnerabilities(conn->vulnerable1[pos],
						 conn->vulnerable2[pos],
						 fpos, gpos, color)
		      && approxlib(fpos, other, 3, NULL) <= 2))) {
	    if (board[apos] == EMPTY && board[fpos] == EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    apos, fpos);
	    }
	    else if (board[apos] == EMPTY && board[fpos] != EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    apos, NO_MOVE);
	    }
	    else if (board[apos] != EMPTY && board[fpos] == EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    fpos, NO_MOVE);
	    }
	    else if (board[apos] != EMPTY && board[fpos] != EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    NO_MOVE, NO_MOVE);
	    }
	  }

	  if (board[ipos] == EMPTY
	      && approxlib(ipos, color, 3, NULL) >= 3
	      && (board[hpos] == color
		  || (board[hpos] == EMPTY
		      && countlib(pos) > 2
		      && !common_vulnerabilities(conn->vulnerable1[pos],
						 conn->vulnerable2[pos],
						 hpos, gpos, color)
		      && approxlib(hpos, other, 3, NULL) <= 2))
	      && (board[jpos] == color
		  || (board[jpos] == EMPTY
		      && countlib(epos) > 2
		      && !common_vulnerabilities(conn->vulnerable1[pos],
						 conn->vulnerable2[pos],
						 jpos, gpos, color)
		      && approxlib(jpos, other, 3, NULL) <= 2))) {
	    if (board[hpos] == EMPTY && board[jpos] == EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    hpos, jpos);
	    }
	    else if (board[hpos] == EMPTY && board[jpos] != EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    hpos, NO_MOVE);
	    }
	    else if (board[hpos] != EMPTY && board[jpos] == EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    jpos, NO_MOVE);
	    }
	    else if (board[hpos] != EMPTY && board[jpos] != EMPTY) {
	      ENQUEUE_STONE(conn, pos, epos, distance + FP(0.2), FP(0.2),
			    NO_MOVE, NO_MOVE);
	    }
	  }
	}

	/* Case 6. "a" is empty and an opponent move can be captured
	 * in a ladder.
	 *
	 * Case 7. "a" is empty.
	 */
	if (board[apos] == EMPTY && conn->distances[apos] > distance + FP(0.6)) {
	  push_connection_heap_entry(conn, distance + FP(0.6), pos, apos,
				     case_6_7_helper);
	}

	/* Case 8. Adjacent opponent stone at "a" which can't avoid atari.
	 */
	if (board[apos] == other
	    && conn->distances[apos] > distance + FP(0.1)
	    && no_escape_from_atari(apos)) {
	  ENQUEUE_STONE(conn, pos, apos, distance + FP(0.1), FP(0.1),
			NO_MOVE, NO_MOVE);
	}

	/* Case 9. Adjacent opponent stone at "a" which can't avoid
	 * ladder capture.
	 *
	 * Case 10. "a" is occupied by opponent.
	 */
	if (board[apos] == other && conn->distances[apos] > distance + FP(0.3)) {
	  push_connection_heap_entry(conn, distance + FP(0.3), pos, apos,
				     case_9_10_helper);
	}

	/* Case 11. Diagonal connection to empty vertex "b" through
	 * empty vertex "a" or "g", which makes "a" or "g" self-atari
	 * for opponent.
	 */
	if (board[bpos] == EMPTY
	    && board[apos] == EMPTY
	    && conn->distances[bpos] > distance + FP(1.1)
	    && does_secure(color, bpos, apos)) {
	  ENQUEUE(conn, pos, bpos, distance + FP(1.1), FP(1.0), apos, NO_MOVE);
	}

	if (board[bpos] == EMPTY
	    && board[gpos] == EMPTY
	    && conn->distances[bpos] > distance + FP(1.1)
	    && does_secure(color, bpos, gpos)) {
	  ENQUEUE(conn, pos, bpos, distance + FP(1.1), FP(1.0), gpos, NO_MOVE);
	}

	/* Case 12. One-space jump to empty vertex "e" through empty
	 * vertex "g", which makes "g" self-atari for opponent.
	 */
	if (board[gpos] == EMPTY
	    && board[epos] == EMPTY
	    && conn->distances[epos] > distance + FP(1.1)
	    && does_secure(color, epos, gpos)) {
	  ENQUEUE(conn, pos, epos, distance + FP(1.1), FP(1.0), gpos, NO_MOVE);
	}

	/* Case 13. One-space jump to empty vertex "e" through empty
	 * vertex "g", making a bamboo joint.
	 */
	if (board[gpos] == EMPTY
	    && board[epos] == EMPTY
	    && conn->distances[epos] > distance + FP(1.1)
	    && ((board[apos] == color && board[fpos] == color
		 && board[bpos] == EMPTY)
		|| (board[hpos] == color && board[jpos] == color
		    && board[ipos] == EMPTY))) {
	  ENQUEUE(conn, pos, epos, distance + FP(1.1), FP(1.0), gpos, NO_MOVE);
	}

	/* Case 14. Diagonal connection to empty vertex "b" through
	 * empty vertices "a" and "g".
	 */
	if (board[bpos] == EMPTY
	    && board[apos] == EMPTY && board[gpos] == EMPTY
            && conn->distances[bpos] > distance + FP(1.3)) {
	  ENQUEUE(conn, pos, bpos, distance + FP(1.3), FP(1.0), apos, gpos);
	}

	/* Case 15. Keima to "f" or "j" on edge. and one space jump on
	 * first or second line.
	 */
	if (board[apos] == EMPTY
	    && board[bpos] == EMPTY
	    && board[gpos] == EMPTY
	    && board[epos] == EMPTY
	    && board[fpos] == EMPTY
	    && (conn->distances[fpos] > distance + FP(1.3)
		|| conn->distances[epos] > distance + FP(1.3))
	    && countlib(pos) >= 3
	    && (!ON_BOARD(cpos) || !ON_BOARD(hpos))) {
	  ENQUEUE(conn, pos, fpos, distance + FP(1.3), FP(1.0),
		  NO_MOVE, NO_MOVE);
	  ENQUEUE(conn, pos, epos, distance + FP(1.3), FP(1.0),
		  NO_MOVE, NO_MOVE);
	}

	if (board[hpos] == EMPTY
	    && board[ipos] == EMPTY
	    && board[gpos] == EMPTY
	    && board[epos] == EMPTY
	    && board[jpos] == EMPTY
	    && (conn->distances[jpos] > distance + FP(1.3)
		|| conn->distances[epos] > distance + FP(1.3))
	    && countlib(pos) >= 3
	    && (!ON_BOARD(apos) || !ON_BOARD(kpos))) {
	  ENQUEUE(conn, pos, jpos, distance + FP(1.3), FP(1.0),
		  NO_MOVE, NO_MOVE);
	  ENQUEUE(conn, pos, epos, distance + FP(1.3), FP(1.0),
		  NO_MOVE, NO_MOVE);
	}

	/* Case 16. Diagonal connection to empty vertex "b" through
	 * empty vertex "a" or "g", which allows opponent move at "a"
	 * or "g" to be captured in a ladder.
	 *
	 * Case 17. Diagonal connection to empty vertex "b" through
	 * one empty and one opponent vertex "a" and "g", where
	 * the opponent stone is short of liberties.
	 *
	 * Case 18. Diagonal connection to empty vertex "b" through
	 * empty vertex "a" or "g", with no particular properties.
	 */
	if (board[bpos] == EMPTY
	    && (board[apos] == EMPTY || board[gpos] == EMPTY)
	    && conn->distances[bpos] > distance + FP(1.2)) {
	  push_connection_heap_entry(conn, distance + FP(1.2), pos, bpos,
				     case_16_17_18_helper);
	}

	/* Case 19. Clamp at "e" of single stone at "g". */
	if (board[gpos] == other
	    && board[epos] == EMPTY
	    && conn->distances[epos] > distance + FP(2.0)
	    && countstones(gpos) == 1) {
	  ENQUEUE(conn, pos, epos, distance + FP(2.0), FP(1.0),
		  NO_MOVE, NO_MOVE);
	}

	/* Case 20. Diagonal connection to empty vertex "b" through
	 * opponent stones "a" or "g" with few liberties.
	 */
	if (board[bpos] == EMPTY
	    && board[apos] == other
	    && board[gpos] == other
	    && conn->distances[bpos] > distance + FP(2.0)
	    && (countlib(apos) + countlib(gpos) <= 6)) {
	  ENQUEUE(conn, pos, bpos, distance + FP(2.0), FP(1.0),
		  NO_MOVE, NO_MOVE);
	}

	/* Case 21. Diagonal connection to own stone "b" through
	 * opponent stones "a" or "g" with few liberties.
	 */
	if (board[bpos] == color
	    && board[apos] == other
	    && board[gpos] == other
	    && conn->distances[bpos] > distance + FP(2.0)
	    && (countlib(apos) + countlib(gpos) <= 5)) {
	  ENQUEUE_STONE(conn, pos, bpos, distance + FP(2.0), FP(1.0),
			NO_MOVE, NO_MOVE);
	}
      }
    }
    else if (board[pos] == EMPTY
	     || (board[pos] == other
		 && countlib(pos) <= 2
		 && no_escape_from_ladder(pos))) {
      for (k = 0; k < 4; k++) {
	/* List of relative coordinates. (pos) is marked by *.
	 *
	 *  jef.
	 *  igb.
	 * kh*ac
	 *   .d.
	 *
	 */
	int right = delta[k];
	int up = delta[(k+1)%4];

	/* FIXME: Compactify this list. */
	int apos = pos + right;
	int bpos = pos + right + up;
#if 0
	int cpos = pos + 2 * right;
	int epos = pos + 2*up;
	int fpos = pos + right + 2*up;
#endif
	int gpos = pos + up;
#if 0
	int hpos = pos - right;
	int ipos = pos - right + up;
	int jpos = pos - right + 2 * up;
	int kpos = pos - 2 * right;
#endif
	
	if (board[apos] == color) {
	  ENQUEUE_STONE(conn, pos, apos, distance, FP(0.0),
			conn->vulnerable1[pos], conn->vulnerable2[pos]);
	}
	else if (board[apos] == EMPTY) {
	  int this_delta
	    = FP(0.8) + FP(0.05) * gg_min(approxlib(apos, other, 6, NULL), 6);
	  ENQUEUE(conn, pos, apos, distance + this_delta, this_delta,
		  NO_MOVE, NO_MOVE);
	}
	else if (board[apos] == other) {
	  ENQUEUE_STONE(conn, pos, apos, distance + FP(1.0), FP(1.0),
			NO_MOVE, NO_MOVE);
	}

	/* Case 1. Diagonal connection to empty vertex "b" through
	 * empty vertices "a" and "g".
	 */
	if (board[bpos] == EMPTY
	    && board[apos] == EMPTY
	    && board[gpos] == EMPTY
            && conn->distances[bpos] > distance + FP(1.5)) {
	  ENQUEUE(conn, pos, bpos, distance + FP(1.5), FP(1.0),
		  NO_MOVE, NO_MOVE);
	}
	
	/* Case 2. Diagonal connection to friendly stone at "b" through
	 * empty vertices "a" and "g".
	 */
	if (board[bpos] == color
	    && board[apos] == EMPTY
	    && board[gpos] == EMPTY
	    && conn->distances[bpos] > distance + FP(1.3)) {
	  ENQUEUE_STONE(conn, pos, bpos, distance + FP(1.3), FP(1.0),
			NO_MOVE, NO_MOVE);
	}
      }
    }
  }
}


void
sort_connection_queue_tail(struct connection_data *conn)
{
  int k;

  for (k = conn->queue_start; k < conn->queue_end - 1; k++) {
    int i;
    int best_index = k;
    int smallest_dist = conn->distances[conn->queue[k]];

    for (i = k + 1; i < conn->queue_end; i++) {
      if (conn->distances[conn->queue[i]] < smallest_dist) {
	best_index = i;
	smallest_dist = conn->distances[conn->queue[i]];
      }
    }

    if (best_index != k) {
      int temp = conn->queue[k];
      conn->queue[k] = conn->queue[best_index];
      conn->queue[best_index] = temp;
    }
  }
}


/* Replace string origins in a connection queue with complete sets of
 * corresponding string stones.
 */
void
expand_connection_queue(struct connection_data *conn)
{
  int k;
  int full_queue[BOARDMAX];
  int full_queue_position = 0;
  int full_queue_start = 0;

  for (k = 0; k < conn->queue_end; k++) {
    if (k == conn->queue_start)
      full_queue_start = full_queue_position;

    if (board[conn->queue[k]] == EMPTY)
      full_queue[full_queue_position++] = conn->queue[k];
    else {
      full_queue_position += findstones(conn->queue[k],
					MAX_BOARD * MAX_BOARD,
					full_queue + full_queue_position);
    }
  }

  conn->queue_start = full_queue_start;
  conn->queue_end   = full_queue_position;
  memcpy(conn->queue, full_queue, conn->queue_end * sizeof(int));
}


/* Initialize distance and delta values so that the former are
 * everywhere huge and the latter everywhere zero.
 */
static void
clear_connection_data(struct connection_data *conn)
{
  int pos;

  conn->queue_start = 0;
  conn->queue_end = 0;
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    conn->distances[pos] = HUGE_CONNECTION_DISTANCE;
    conn->deltas[pos] = FP(0.0);
    conn->coming_from[pos] = NO_MOVE;
    conn->vulnerable1[pos] = NO_MOVE;
    conn->vulnerable2[pos] = NO_MOVE;
  }

  conn->heap_data_size = 0;
  conn->heap_size = 0;
}


/* Compute the connection distances from string (str) to nearby
 * vertices, until we reach target or the distance gets too high.
 */
void
compute_connection_distances(int str, int target, int cutoff,
			     struct connection_data *conn,
			     int speculative)
{
  int color = board[str];
  
  clear_connection_data(conn);

  /* Add the origin of the initial string to the queue. */
  add_to_start_queue(find_origin(str), FP(0.0), conn);

  conn->target = target;
  conn->cutoff_distance = cutoff;
  conn->speculative = speculative;

  spread_connection_distances(color, conn);
}


/* Print the connection distances in a struct connection_data. */
void
print_connection_distances(struct connection_data *conn)
{
  int i, j;
  int ch;
  int pos;
  
  fprintf(stderr, "  ");
  for (j = 0, ch = 'A'; j < board_size; j++, ch++) {
    if (ch == 'I')
      ch++;
    fprintf(stderr, "  %c ", ch);
  }
  fprintf(stderr, "\n");

  for (i = 0; i < board_size; i++) {
    fprintf(stderr, "%2d ", board_size - i);
    for (j = 0; j < board_size; j++) {
      pos = POS(i, j);
      if (conn->distances[pos] == HUGE_CONNECTION_DISTANCE) {
	if (board[pos] == WHITE)
	  fprintf(stderr, " O  ");
	if (board[pos] == BLACK)
	  fprintf(stderr, " X  ");
	if (board[pos] == EMPTY)
	  fprintf(stderr, " .  ");
      }
      else {
	fprintf(stderr, "%3.1f ", FIXED_TO_FLOAT(conn->distances[pos]));
      }
    }
    fprintf(stderr, "\n");
  }
  fprintf(stderr, "\n");

  fprintf(stderr, "Vulnerable:\n");
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (conn->distances[pos] < HUGE_CONNECTION_DISTANCE
	&& (conn->vulnerable1[pos] != NO_MOVE
	    || conn->vulnerable2[pos] != NO_MOVE)) {
      gprintf(" %1m:", pos);
      if (conn->vulnerable1[pos] != NO_MOVE)
	gprintf(" %1m", conn->vulnerable1[pos]);
      if (conn->vulnerable2[pos] != NO_MOVE)
	gprintf(" %1m", conn->vulnerable2[pos]);
      gprintf("\n", pos);
    }
}


/* Test whether there is a trivial connection between str1 and str2
 * and if so return the connecting move in *move. By trivial
 * connection we mean that they either have a common liberty or a
 * common neighbor which can be tactically attacked.
 */
static int
trivial_connection(int str1, int str2, int *move)
{
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;
  int adj, adjs[MAXCHAIN];
  int r;
  int result = 0;
  
  if (have_common_lib(str1, str2, move))
    return WIN;

  adj = chainlinks(str1, adjs);

  /* We turn off the sgf traces here to avoid cluttering them up with
   * tactical reading moves.
   */
  sgf_dumptree = NULL;
  count_variations = 0;
  
  for (r = 0; r < adj; r++)
    if (adjacent_strings(adjs[r], str2) && attack(adjs[r], move) == WIN) {
      result = WIN;
      break;
    }

  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;
  
  return result;
}


/* True if a move by color makes an opponent move at pos a self atari
 * or possible to capture in a ladder.
 */
static int
does_secure_through_ladder(int color, int move, int pos)
{
  int result = 0;
  
  if (trymove(move, color, NULL, NO_MOVE)) {
    if (ladder_capturable(pos, OTHER_COLOR(color)))
      result = 1;
    popgo();
  }
  
  return result;
}

/* Test whether the string str can be immediately taken off the board
 * or captured in a ladder. If so the capturing move is returned in
 * *move.
 */
static int
ladder_capture(int str, int *move)
{
  int result;
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;
  int liberties = countlib(str);
  
  /* We turn off the sgf traces here to avoid cluttering them up with
   * tactical reading moves.
   */
  sgf_dumptree = NULL;
  count_variations = 0;

  if (liberties == 1)
    result = attack(str, move);
  else if (liberties == 2)
    result = simple_ladder(str, move);
  else
    result = 0;
  
  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;

  return result;
}

/* Test whether a move at pos by color can be captured in a ladder. */
static int
ladder_capturable(int pos, int color)
{
  int result = 0;
  
  if (trymove(pos, color, NULL, NO_MOVE)) {
    int liberties = countlib(pos);
    if (liberties == 1 && attack(pos, NULL) == WIN)
      result = 1;
    else if (liberties == 2 && simple_ladder(pos, NULL) == WIN)
      result = 1;
    popgo();
  }
  else
    result = 1;
  
  return result;
}


/* Test whether the string str with one liberty is stuck with at most
 * one liberty. This function trivially returns false if the string
 * has more than one liberty to start with.
 */
static int
no_escape_from_atari(int str)
{
  int lib;
  int adj[MAXCHAIN];

  if (findlib(str, 1, &lib) > 1)
    return 0;

  if (accuratelib(lib, board[str], 2, NULL) > 1)
    return 0;

  /* FIXME: Should exclude snapback. */
  if (chainlinks2(str, adj, 1) > 0)
    return 0;

  return 1;
}


/* Test whether the string str with one liberty is captured in a
 * ladder. This function trivially returns false if the string has
 * more than one liberty to start with, except for one special case.
 * FIXME: Needs a simple_ladder_defense().
 */
static int
no_escape_from_ladder(int str)
{
  int result = 0;
  SGFTree *save_sgf_dumptree = sgf_dumptree;
  int save_count_variations = count_variations;
  int adj[MAXCHAIN];
  int libs[2];
  
  /* We turn off the sgf traces here to avoid cluttering them up with
   * tactical reading moves.
   */
  sgf_dumptree = NULL;
  count_variations = 0;
  
  if (countlib(str) == 1 && find_defense(str, NULL) == 0)
    result = 1;

  if (countlib(str) == 2
      && chainlinks2(str, adj, 1) == 0
      && findlib(str, 2, libs) == 2
      && approxlib(libs[0], board[str], 2, NULL) == 1
      && approxlib(libs[1], board[str], 2, NULL) == 1
      && ladder_capture(str, NULL)
      && !find_defense(str, NULL))
    result = 1;
      
  
  /* Turn the sgf traces back on. */
  sgf_dumptree = save_sgf_dumptree;
  count_variations = save_count_variations;

  return result;
}

/* We usually don't want to spend time with moves which are
 * self-atari, unless the stone is involved in a ko.
 */
static int
check_self_atari(int pos, int color_to_move)
{
#if 1
  int lib;
#endif
  
  if (!is_self_atari(pos, color_to_move))
    return 1;

  if (is_ko(pos, color_to_move, NULL))
    return 1;

#if 1
  /* FIXME: At some time I added this exceptional case but I can no
   * longer see how it would be useful. It might still be, however, so
   * I leave the code in for a while. /gf
   *
   * Code reactivated, see nando:31. /nn
   *
   * Added requirement that no additional stones are sacrificed in the
   * self atari. /gf
   *
   * FIXME: Add a function in board.c to check how big the string
   *        becomes when playing a move and use for the isolated stone
   *        test below.
   */
  if (approxlib(pos, color_to_move, 1, &lib) >= 1
      && approxlib(lib, OTHER_COLOR(color_to_move), 3, NULL) <= 2
      && ladder_capturable(lib, OTHER_COLOR(color_to_move))) {
    int k;
    for (k = 0; k < 4; k++) {
      if (board[pos + delta[k]] == color_to_move)
	break;
    }
    if (k == 4)
      return 1;
  }
#endif

  return 0;
}

/* Check for overlap between (a1, a2) and (b1, b2). */
static int
common_vulnerabilities(int a1, int a2, int b1, int b2, int color)
{
  return (common_vulnerability(a1, b1, color)
	  || common_vulnerability(a1, b2, color)
	  || common_vulnerability(a2, b1, color)
	  || common_vulnerability(a2, b2, color));
}

/* Check if apos and bpos are the same or if they are both liberties
 * of a string of the given color with at most three liberties.
 */
static int
common_vulnerability(int apos, int bpos, int color)
{
  int k;
  
  if (apos == NO_MOVE || bpos == NO_MOVE)
    return 0;
  
  if (apos == bpos)
    return 1;

  for (k = 0; k < 4; k++)
    if (board[apos + delta[k]] == color
	&& countlib(apos + delta[k]) <= 3
	&& liberty_of_string(bpos, apos + delta[k]))
      return 1;

  return 0;
}

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