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


/* The functions in this file implements a go board with incremental
 * update of strings and liberties.
 * 
 * See the Texinfo documentation (Utility Functions: Incremental Board)
 * for an introduction.
 */

#include "board.h"
#include "hash.h"
#include "sgftree.h"
#include "gg_utils.h"

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


/* This can be used for internal checks w/in board.c that should
 * typically not be necessary (for speed).
 */
#if 1
#define PARANOID1(x, pos) ASSERT1(x, pos)
#else
#define PARANOID1(x, pos)
#endif


/* ================================================================ */
/*                          data structures                         */
/* ================================================================ */


/* Incremental string data. */
struct string_data {
  int color;                       /* Color of string, BLACK or WHITE */
  int size;                        /* Number of stones in string. */
  int origin;                      /* Coordinates of "origin", i.e. */
                                   /* "upper left" stone. */
  int liberties;                   /* Number of liberties. */
  int neighbors;                   /* Number of neighbor strings */
  int mark;                        /* General purpose mark. */
};

struct string_liberties_data {
  int list[MAX_LIBERTIES];         /* Coordinates of liberties. */
};

struct string_neighbors_data {
  int list[MAXCHAIN];              /* List of neighbor string numbers. */
};

/* we keep the address and the old value */
struct change_stack_entry {
  int *address;
  int value;
};

/* we keep the address and the old value */
struct vertex_stack_entry {
  Intersection *address;
  int value;
};


/* Experimental results show that the average number of change stack
 * entries per move usually is in the 20-30 range and very seldom
 * exceeds 40. But since we have no way to recover from running out of
 * stack space, we allocate with a substantial safety margin.
 */
#define STACK_SIZE 80 * MAXSTACK


#define CLEAR_STACKS() do { \
  change_stack_pointer = change_stack; \
  vertex_stack_pointer = vertex_stack; \
  VALGRIND_MAKE_WRITABLE(change_stack, sizeof(change_stack)); \
  VALGRIND_MAKE_WRITABLE(vertex_stack, sizeof(vertex_stack)); \
} while (0)

/* Begin a record : address == NULL */
#define BEGIN_CHANGE_RECORD()\
((change_stack_pointer++)->address = NULL,\
 (vertex_stack_pointer++)->address = NULL)

/* Save a value : store the address and the value in the stack */
#define PUSH_VALUE(v)\
(change_stack_pointer->address = &(v),\
 (change_stack_pointer++)->value = (v))

/* Save a board value : store the address and the value in the stack */
#define PUSH_VERTEX(v)\
(vertex_stack_pointer->address = &(v),\
 (vertex_stack_pointer++)->value = (v))

#define POP_MOVE()\
  while ((--change_stack_pointer)->address)\
  *(change_stack_pointer->address) =\
  change_stack_pointer->value


#define POP_VERTICES()\
  while ((--vertex_stack_pointer)->address)\
  *(vertex_stack_pointer->address) =\
  vertex_stack_pointer->value


/* ================================================================ */
/*                      static data structures                      */
/* ================================================================ */


/* Main array of string information. */
static struct string_data string[MAX_STRINGS];
static struct string_liberties_data string_libs[MAX_STRINGS];
static struct string_neighbors_data string_neighbors[MAX_STRINGS];

/* Stacks and stack pointers. */
static struct change_stack_entry change_stack[STACK_SIZE];
static struct change_stack_entry *change_stack_pointer;

static struct vertex_stack_entry vertex_stack[STACK_SIZE];
static struct vertex_stack_entry *vertex_stack_pointer;


/* Index into list of strings. The index is only valid if there is a
 * stone at the vertex.
 */
static int string_number[BOARDMAX];


/* The stones in a string are linked together in a cyclic list. 
 * These are the coordinates to the next stone in the string.
 */
static int next_stone[BOARDMAX];


/* ---------------------------------------------------------------- */


/* Macros to traverse the stones of a string.
 *
 * Usage:
 * int s, pos;
 * s = find_the_string()
 * pos = FIRST_STONE(s);
 *   do {
 *    use_stone(pos);
 *    pos = NEXT_STONE(pos);
 *  } while (!BACK_TO_FIRST_STONE(s, pos));
 */
#define FIRST_STONE(s) \
  (string[s].origin)

#define NEXT_STONE(pos) \
  (next_stone[pos])

#define BACK_TO_FIRST_STONE(s, pos) \
  ((pos) == string[s].origin)


/* Assorted useful macros.
 *
 * Some of them could have been functions but are implemented as
 * macros for speed.
 */

#define LIBERTY(pos) \
  (board[pos] == EMPTY)

#define UNMARKED_LIBERTY(pos) \
  (board[pos] == EMPTY && ml[pos] != liberty_mark)

#define MARK_LIBERTY(pos) \
  ml[pos] = liberty_mark

#define UNMARKED_STRING(pos) \
  (string[string_number[pos]].mark != string_mark)

/* Note that these two macros are not complementary. Both return
 * false if board[pos] != color.
 */
#define UNMARKED_COLOR_STRING(pos, color)\
  (board[pos] == color\
   && string[string_number[pos]].mark != string_mark)

#define MARKED_COLOR_STRING(pos, color)\
  (board[pos] == color\
   && string[string_number[pos]].mark == string_mark)

#define MARK_STRING(pos) string[string_number[pos]].mark = string_mark

#define STRING_AT_VERTEX(pos, s, color)\
  ((board[pos] == color) && string_number[pos] == (s))

#define NEIGHBOR_OF_STRING(pos, s, color)\
  (STRING_AT_VERTEX(SOUTH(pos), s, color)\
   || STRING_AT_VERTEX(WEST(pos), s, color)\
   || STRING_AT_VERTEX(NORTH(pos), s, color)\
   || STRING_AT_VERTEX(EAST(pos), s, color))

/* These four macros have rather confusing names. It should be read as:
 * "(pos) is a neighbor of string (s) of (color) in any direction except
 * the specified one".
 */
#define NON_SOUTH_NEIGHBOR_OF_STRING(pos, s, color)\
  (STRING_AT_VERTEX(SOUTH(pos), s, color)\
   || STRING_AT_VERTEX(WEST(pos), s, color)\
   || STRING_AT_VERTEX(EAST(pos), s, color))
  
#define NON_WEST_NEIGHBOR_OF_STRING(pos, s, color)\
  (STRING_AT_VERTEX(WEST(pos), s, color)\
   || STRING_AT_VERTEX(NORTH(pos), s, color)\
   || STRING_AT_VERTEX(SOUTH(pos), s, color))
  
#define NON_NORTH_NEIGHBOR_OF_STRING(pos, s, color)\
  (STRING_AT_VERTEX(NORTH(pos), s, color)\
   || STRING_AT_VERTEX(EAST(pos), s, color)\
   || STRING_AT_VERTEX(WEST(pos), s, color))
  
#define NON_EAST_NEIGHBOR_OF_STRING(pos, s, color)\
  (STRING_AT_VERTEX(EAST(pos), s, color)\
   || STRING_AT_VERTEX(SOUTH(pos), s, color)\
   || STRING_AT_VERTEX(NORTH(pos), s, color))
  
#define LIBERTIES(pos)\
  string[string_number[pos]].liberties

#define COUNTSTONES(pos) \
  string[string_number[pos]].size

#define ADD_LIBERTY(s, pos)\
  do {\
    if (string[s].liberties < MAX_LIBERTIES)\
      string_libs[s].list[string[s].liberties] = pos;\
    string[s].liberties++;\
  } while (0)

#define ADD_AND_MARK_LIBERTY(s, pos)\
  do {\
    if (string[s].liberties < MAX_LIBERTIES)\
      string_libs[s].list[string[s].liberties] = pos;\
    string[s].liberties++;\
    ml[pos] = liberty_mark;\
  } while (0)

#define ADD_NEIGHBOR(s, pos)\
  string_neighbors[s].list[string[s].neighbors++] = string_number[pos]

#define DO_ADD_STONE(pos, color)\
  do {\
    PUSH_VERTEX(board[pos]);\
    board[pos] = color;\
    hashdata_invert_stone(&board_hash, pos, color);\
  } while (0)

#define DO_REMOVE_STONE(pos)\
  do {\
    PUSH_VERTEX(board[pos]);\
    hashdata_invert_stone(&board_hash, pos, board[pos]);\
    board[pos] = EMPTY;\
  } while (0)


/* ---------------------------------------------------------------- */



/* Number of the next free string. */
static int next_string;


/* For marking purposes. */
static int ml[BOARDMAX];
static int liberty_mark;
static int string_mark;


/* Forward declarations. */
static void really_do_trymove(int pos, int color);
static int do_trymove(int pos, int color, int ignore_ko);
static void undo_trymove(void);

static int do_approxlib(int pos, int color, int maxlib, int *libs);
static int slow_approxlib(int pos, int color, int maxlib, int *libs);
static int do_accuratelib(int pos, int color, int maxlib, int *libs);

static int is_superko_violation(int pos, int color, enum ko_rules type);

static void new_position(void);
static int propagate_string(int stone, int str);
static void find_liberties_and_neighbors(int s);
static int do_remove_string(int s);
static void do_commit_suicide(int pos, int color);
static void do_play_move(int pos, int color);

static int komaster, kom_pos;


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

/* Coordinates for the eight directions, ordered
 * south, west, north, east, southwest, northwest, northeast, southeast.
 */
int deltai[8] = { 1,  0, -1,  0,  1, -1, -1, 1};
int deltaj[8] = { 0, -1,  0,  1, -1, -1,  1, 1};
int delta[8]  = { NS, -1, -NS, 1, NS-1, -NS-1, -NS+1, NS+1};


/* ================================================================ */
/*                    Board initialization                          */
/* ================================================================ */

/*
 * Save board state.
 */

void
store_board(struct board_state *state)
{
  int k;

  gg_assert(stackp == 0);

  state->board_size = board_size;

  memcpy(state->board, board, sizeof(board));
  memcpy(state->initial_board, initial_board, sizeof(initial_board));

  state->board_ko_pos = board_ko_pos;
  state->white_captured = white_captured;
  state->black_captured = black_captured;
  
  state->initial_board_ko_pos = initial_board_ko_pos;
  state->initial_white_captured = initial_white_captured;
  state->initial_black_captured = initial_black_captured;
  
  state->move_history_pointer = move_history_pointer;
  for (k = 0; k < move_history_pointer; k++) {
    state->move_history_color[k] = move_history_color[k];
    state->move_history_pos[k] = move_history_pos[k];
    state->move_history_hash[k] = move_history_hash[k];
  }

  state->komi = komi;
  state->handicap = handicap;
  state->move_number = movenum;
}


/*
 * Restore a saved board state.
 */

void
restore_board(struct board_state *state)
{
  int k;

  gg_assert(stackp == 0);

  board_size = state->board_size;

  memcpy(board, state->board, sizeof(board));
  memcpy(initial_board, state->initial_board, sizeof(initial_board));

  board_ko_pos = state->board_ko_pos;
  white_captured = state->white_captured;
  black_captured = state->black_captured;
  
  initial_board_ko_pos = state->initial_board_ko_pos;
  initial_white_captured = state->initial_white_captured;
  initial_black_captured = state->initial_black_captured;
  
  move_history_pointer = state->move_history_pointer;
  for (k = 0; k < move_history_pointer; k++) {
    move_history_color[k] = state->move_history_color[k];
    move_history_pos[k] = state->move_history_pos[k];
    move_history_hash[k] = state->move_history_hash[k];
  }

  komi = state->komi;
  handicap = state->handicap;
  movenum = state->move_number;
  
  hashdata_recalc(&board_hash, board, board_ko_pos);
  new_position();
}


/*
 * Clear the internal board.
 */

void
clear_board(void)
{
  int k;

  gg_assert(board_size > 0 && board_size <= MAX_BOARD);
  
  memset(board, EMPTY, sizeof(board));
  memset(initial_board, EMPTY, sizeof(initial_board));
  for (k = 0; k < BOARDSIZE; k++) {
    if (!ON_BOARD2(I(k), J(k))) {
      board[k] = GRAY;
      initial_board[k] = GRAY;
    }
  }

  board_ko_pos = NO_MOVE;
  white_captured = 0;
  black_captured = 0;

  komaster = EMPTY;
  kom_pos = NO_MOVE;

  initial_board_ko_pos = NO_MOVE;
  initial_white_captured = 0;
  initial_black_captured = 0;

  move_history_pointer = 0;
  movenum = 0;

  handicap = 0;
  
  hashdata_recalc(&board_hash, board, board_ko_pos);
  new_position();
}

/* Test the integrity of the gray border. */
int
test_gray_border(void)
{
  int k;

  gg_assert(board_size > 0 && board_size <= MAX_BOARD);
  
  for (k = 0; k < BOARDSIZE; k++)
    if (!ON_BOARD2(I(k), J(k)))
      if (board[k] != GRAY)
      	return k;
  
  return -1;
}


/* ================================================================ */
/*                      Temporary moves                             */
/* ================================================================ */


/* Stack of trial moves to get to current
 * position and which color made them. Perhaps 
 * this should be one array of a structure 
 */
static int stack[MAXSTACK];
static int move_color[MAXSTACK];

static Hash_data board_hash_stack[MAXSTACK];

/*
 * trymove pushes the position onto the stack, and makes a move
 * at pos of color. Returns one if the move is legal. The
 * stack pointer is only incremented if the move is legal.
 *
 * The way to use this is:
 *
 *   if (trymove(...)) {
 *      ...
 *      popgo();
 *   }   
 *
 * The message can be written as a comment to an sgf file using 
 * sgfdump(). str can be NO_MOVE if it is not needed but otherwise  
 * the location of str is included in the comment.
 */

int 
trymove(int pos, int color, const char *message, int str)
{
  UNUSED(str);
  /* Do the real work elsewhere. */
  if (!do_trymove(pos, color, 0))
    return 0;

  /* Store the move in an sgf tree if one is available. */
  if (sgf_dumptree) {
    char buf[100];

    if (message == NULL)
      message = "UNKNOWN";

    if (pos == NO_MOVE) {
      if (komaster != EMPTY)
	gg_snprintf(buf, 100, "%s (variation %d, hash %s, komaster %s:%s)", 
		    message, count_variations, hashdata_to_string(&board_hash),
		    color_to_string(komaster), location_to_string(kom_pos));
      else
	gg_snprintf(buf, 100, "%s (variation %d, hash %s)", message,
		    count_variations, hashdata_to_string(&board_hash));
    }
    else {
      if (komaster != EMPTY)
	gg_snprintf(buf, 100, 
		    "%s at %s (variation %d, hash %s, komaster %s:%s)", 
		    message, location_to_string(pos), count_variations,
		    hashdata_to_string(&board_hash),
		    color_to_string(komaster),
		    location_to_string(kom_pos));
      else
	gg_snprintf(buf, 100, "%s at %s (variation %d, hash %s)", 
		    message, location_to_string(pos), count_variations,
		    hashdata_to_string(&board_hash));
    }
    sgftreeAddPlayLast(sgf_dumptree, color, I(pos), J(pos));
    sgftreeAddComment(sgf_dumptree, buf);
  }
  
  if (count_variations)
    count_variations++;
  stats.nodes++;

  return 1;
}


/*
 * tryko pushes the position onto the stack, and makes a move
 * at (pos) of (color). The move is allowed even if it is an
 * illegal ko capture. It is to be imagined that (color) has
 * made an intervening ko threat which was answered and now
 * the continuation is to be explored.
 *
 * Return 1 if the move is legal with the above caveat. Returns
 * zero if it is not legal because of suicide.
 */

int 
tryko(int pos, int color, const char *message)
{
  /* Do the real work elsewhere. */
  if (!do_trymove(pos, color, 1))
    return 0;

  if (sgf_dumptree) {
    char buf[100];
    if (message == NULL)
      message = "UNKNOWN";
    if (komaster != EMPTY)
      gg_snprintf(buf, 100, "tryko: %s (variation %d, %s, komaster %s:%s)", 
		  message, count_variations, hashdata_to_string(&board_hash),
		  color_to_string(komaster), location_to_string(kom_pos));
    else
      gg_snprintf(buf, 100, "tryko: %s (variation %d, %s)", message,
		  count_variations, hashdata_to_string(&board_hash));

    /* Add two pass moves to the SGF output to simulate the ko threat
     * and the answer.
     *
     * The reason we add these is that certain SGF viewers, including
     * Cgoban 1, won't properly display variations with illegal ko
     * captures. SGF FF[4] compliant browsers should have no problem
     * with this, though.
     */
    sgftreeAddPlayLast(sgf_dumptree, color, -1, -1);
    sgftreeAddComment(sgf_dumptree, "tenuki (ko threat)");
    sgftreeAddPlayLast(sgf_dumptree, OTHER_COLOR(color), -1, -1);
    sgftreeAddComment(sgf_dumptree, "tenuki (answers ko threat)");

    sgftreeAddPlayLast(sgf_dumptree, color, I(pos), J(pos));
    sgftreeAddComment(sgf_dumptree, buf);
  }
  
  if (count_variations)
    count_variations++;
  stats.nodes++;

  return 1;
}

/* Really, really make a temporary move. It is assumed that all
 * necessary checks have already been made and likewise that various
 * administrative bookkeeping outside of the actual board logic has
 * either been done or is not needed.
 */
static void
really_do_trymove(int pos, int color)
{
  BEGIN_CHANGE_RECORD();
  PUSH_VALUE(board_ko_pos);

  /*
   * FIXME: Do we really have to store board_hash in a stack?
   *
   * Answer: No, we don't.  But for every stone that we add
   *         or remove, we must call hashdata_invert_stone(). This is
   *         not difficult per se, but the whole board.c 
   *         will have to be checked, and there is lots of room
   *         for mistakes.
   *
   *         At the same time, profiling shows that storing the
   *         hashdata in a stack doesn't take a lot of time, so
   *         this is not an urgent FIXME.
   */
  memcpy(&board_hash_stack[stackp], &board_hash, sizeof(board_hash));

  if (board_ko_pos != NO_MOVE)
    hashdata_invert_ko(&board_hash, board_ko_pos);

  board_ko_pos = NO_MOVE;
  
  stackp++;

  if (pos != PASS_MOVE) {
    PUSH_VALUE(black_captured);
    PUSH_VALUE(white_captured);
    do_play_move(pos, color);
  }
}

/*
 * Do the main work of trymove() and tryko(), i.e. the common parts.
 * The ignore_ko flag tells whether an illegal ko capture may be done.
 * Return 1 if the move was valid, otherwise 0.
 */

static int 
do_trymove(int pos, int color, int ignore_ko)
{
  /* 1. The color must be BLACK or WHITE. */
  gg_assert(color == BLACK || color == WHITE);
 
  if (pos != PASS_MOVE) {
    /* 2. Unless pass, the move must be inside the board. */
    ASSERT_ON_BOARD1(pos);
    
    /* Update the reading tree shadow. */
    shadow[pos] = 1;

    /* 3. The location must be empty. */
    if (board[pos] != EMPTY)
      return 0;
    
    /* 4. The location must not be the ko point, unless ignore_ko == 1. */
    if (!ignore_ko && pos == board_ko_pos) {
      if (board[WEST(pos)] == OTHER_COLOR(color)
	  || board[EAST(pos)] == OTHER_COLOR(color)) {
	return 0;
      }
    }

    /* 5. Test for suicide. */
    if (is_suicide(pos, color))
      return 0;
  }
  
  /* Check for stack overflow. */
  if (stackp >= MAXSTACK-2) {
    fprintf(stderr, 
	    "gnugo: Truncating search. This is beyond my reading ability!\n");
    /* FIXME: Perhaps it's best to just assert here and be done with it? */
    if (0) {
      ASSERT1(0 && "trymove stack overflow", pos);
    }
#if 0
    if (verbose > 0) {
      showboard(0);
      dump_stack();
    }
#endif
    fflush(stderr);
    return 0;
  }


  /* Only count trymove when we do create a new position. */
  trymove_counter++;
  
  /* So far, so good. Now push the move on the move stack. These are
   * needed for dump_stack().
   */
  stack[stackp] = pos;
  move_color[stackp] = color;

  really_do_trymove(pos, color);

  return 1;
}


/*
 * popgo pops the position from the stack.
 */

void
popgo()
{
  undo_trymove();
  
  if (sgf_dumptree) {
    char buf[100];
    int is_tryko = 0;
    char *sgf_comment;

    /* FIXME: Change the sgfGet*Property() interface so that either
     * "C" instead of "C " works or the SGFXX symbols are used.
     */
    if (sgfGetCharProperty(sgf_dumptree->lastnode, "C ", &sgf_comment)
	&& strncmp(sgf_comment, "tryko:", 6) == 0)
      is_tryko = 1;
    
    gg_snprintf(buf, 100, "(next variation: %d)", count_variations);
    sgftreeAddComment(sgf_dumptree, buf);
    sgf_dumptree->lastnode = sgf_dumptree->lastnode->parent;

    /* After tryko() we need to undo two pass nodes too. */
    if (is_tryko)
      sgf_dumptree->lastnode = sgf_dumptree->lastnode->parent->parent;
  }
}


/* Restore board state to the position before the last move. This is
 * accomplished by popping everything that was stored on the stacks
 * since the last BEGIN_CHANGE_RECORD(). Also stackp is decreased and
 * board hash is restored from stack.
 *
 * This undoes the effects of do_trymove() or really_do_trymove() and
 * is appropriate to call instead of popgo() if you have not passed
 * through trymove() or tryko().
 */

static void
undo_trymove()
{
  gg_assert(change_stack_pointer - change_stack <= STACK_SIZE);

  if (0) {
    gprintf("Change stack size = %d\n", change_stack_pointer - change_stack);
    gprintf("Vertex stack size = %d\n", vertex_stack_pointer - vertex_stack);
  }

  POP_MOVE();
  POP_VERTICES();
  
  stackp--;
  memcpy(&board_hash, &(board_hash_stack[stackp]), sizeof(board_hash));
}



/*
 * dump_stack() for use under gdb prints the move stack. 
 */

void
dump_stack(void)
{
  do_dump_stack();

#if !TRACE_READ_RESULTS
  if (count_variations)
    gprintf("%o (variation %d)", count_variations-1);
#else
  gprintf("%o (%s)", hashdata_to_string(&board_hash));
#endif

  gprintf("%o\n");
  fflush(stderr);
}

/* Bare bones of dump_stack(). */
void
do_dump_stack(void)
{
  int n;

  for (n = 0; n < stackp; n++)
    gprintf("%o%s:%1m ", move_color[n] == BLACK ? "B" : "W", stack[n]);
}

/* ================================================================ */
/*                     Permanent moves                              */
/* ================================================================ */


static void
reset_move_history(void)
{
  memcpy(initial_board, board, sizeof(board));
  initial_board_ko_pos = board_ko_pos;
  initial_white_captured = white_captured;
  initial_black_captured = black_captured;
  move_history_pointer = 0;
}

/* Place a stone on the board and update the board_hash. This operation
 * destroys all move history.
 */

void
add_stone(int pos, int color)
{
  ASSERT1(stackp == 0, pos);
  ASSERT_ON_BOARD1(pos);
  ASSERT1(board[pos] == EMPTY, pos);

  board[pos] = color;
  hashdata_invert_stone(&board_hash, pos, color);
  reset_move_history();
  new_position();
}


/* Remove a stone from the board and update the board_hash. This
 * operation destroys the move history.
 */

void
remove_stone(int pos)
{
  ASSERT1(stackp == 0, pos);
  ASSERT_ON_BOARD1(pos);
  ASSERT1(IS_STONE(board[pos]), pos);

  hashdata_invert_stone(&board_hash, pos, board[pos]);
  board[pos] = EMPTY;
  reset_move_history();
  new_position();
}


/* Play a move. Basically the same as play_move() below, but doesn't store
 * the move in history list.
 *
 * Set `update_internals' to zero if you want to play several moves in a
 * row to avoid overhead caused by new_position(). Don't forget to call
 * it yourself after all the moves have been played.
 */
static void
play_move_no_history(int pos, int color, int update_internals)
{
#if CHECK_HASHING
  Hash_data oldkey;

  /* Check the hash table to see if it corresponds to the cumulative one. */
  hashdata_recalc(&oldkey, board, board_ko_pos);
  gg_assert(hashdata_is_equal(oldkey, board_hash));
#endif

  if (board_ko_pos != NO_MOVE)
    hashdata_invert_ko(&board_hash, board_ko_pos);
  board_ko_pos = NO_MOVE;

  /* If the move is a pass, we can skip some steps. */
  if (pos != PASS_MOVE) {
    ASSERT_ON_BOARD1(pos);
    ASSERT1(board[pos] == EMPTY, pos);

    /* Do play the move. */
    if (!is_suicide(pos, color))
      do_play_move(pos, color);
    else
      do_commit_suicide(pos, color);

#if CHECK_HASHING
    /* Check the hash table to see if it equals the previous one. */
    hashdata_recalc(&oldkey, board, board_ko_pos);
    gg_assert(hashdata_is_equal(oldkey, board_hash));
#endif
  }

  if (update_internals || next_string == MAX_STRINGS)
    new_position();
  else
    CLEAR_STACKS();
}

/* Load the initial position and replay the first n moves. */
static void
replay_move_history(int n)
{
  int k;
  
  memcpy(board, initial_board, sizeof(board));
  board_ko_pos = initial_board_ko_pos;
  white_captured = initial_white_captured;
  black_captured = initial_black_captured;
  new_position();

  for (k = 0; k < n; k++)
    play_move_no_history(move_history_pos[k], move_history_color[k], 0);

  new_position();
}

/* Play a move. If you want to test for legality you should first call
 * is_legal(). This function strictly follows the algorithm: 
 * 1. Place a stone of given color on the board.
 * 2. If there are any adjacent opponent strings without liberties,
 *    remove them and increase the prisoner count. 
 * 3. If the newly placed stone is part of a string without liberties,
 *    remove it and increase the prisoner count.
 *
 * In spite of the name "permanent move", this move can (usually) be
 * unplayed by undo_move(), but it is significantly more costly than
 * unplaying a temporary move. There are limitations on the available
 * move history, so under certain circumstances the move may not be
 * possible to unplay at a later time.
 */
void
play_move(int pos, int color)
{
  ASSERT1(stackp == 0, pos);
  ASSERT1(color == WHITE || color == BLACK, pos);
  ASSERT1(pos == PASS_MOVE || ON_BOARD1(pos), pos);
  ASSERT1(pos == PASS_MOVE || board[pos] == EMPTY, pos);
  ASSERT1(komaster == EMPTY && kom_pos == NO_MOVE, pos);

  if (move_history_pointer >= MAX_MOVE_HISTORY) {
    /* The move history is full. We resolve this by collapsing the
     * first about 10% of the moves into the initial position.
     */
    int number_collapsed_moves = 1 + MAX_MOVE_HISTORY / 10;
    int k;
    Intersection saved_board[BOARDSIZE];
    int saved_board_ko_pos = board_ko_pos;
    int saved_white_captured = white_captured;
    int saved_black_captured = black_captured;
    memcpy(saved_board, board, sizeof(board));

    replay_move_history(number_collapsed_moves);

    memcpy(initial_board, board, sizeof(board));
    initial_board_ko_pos = board_ko_pos;
    initial_white_captured = white_captured;
    initial_black_captured = black_captured;

    for (k = number_collapsed_moves; k < move_history_pointer; k++) {
      move_history_color[k - number_collapsed_moves] = move_history_color[k];
      move_history_pos[k - number_collapsed_moves] = move_history_pos[k];
      move_history_hash[k - number_collapsed_moves] = move_history_hash[k];
    }
    move_history_pointer -= number_collapsed_moves;

    memcpy(board, saved_board, sizeof(board));
    board_ko_pos = saved_board_ko_pos;
    white_captured = saved_white_captured;
    black_captured = saved_black_captured;
    new_position();
  }

  move_history_color[move_history_pointer] = color;
  move_history_pos[move_history_pointer] = pos;
  move_history_hash[move_history_pointer] = board_hash;
  if (board_ko_pos != NO_MOVE)
    hashdata_invert_ko(&move_history_hash[move_history_pointer], board_ko_pos);
  move_history_pointer++;
  
  play_move_no_history(pos, color, 1);
  
  movenum++;
}


/* Undo n permanent moves. Returns 1 if successful and 0 if it fails.
 * If n moves cannot be undone, no move is undone.
 */
int
undo_move(int n)
{
  gg_assert(stackp == 0);
  
  /* Fail if and only if the move history is too short. */
  if (move_history_pointer < n)
    return 0;

  replay_move_history(move_history_pointer - n);
  move_history_pointer -= n;
  movenum -= n;

  return 1;
}


/* Return the last move done by the opponent to color. Both if no move
 * was found or if the last move was a pass, PASS_MOVE is returned.
 */
int
get_last_opponent_move(int color)
{
  int k;
  
  for (k = move_history_pointer - 1; k >= 0; k--)
    if (move_history_color[k] == OTHER_COLOR(color))
      return move_history_pos[k];

  return PASS_MOVE;
}

/* Return the last move done by anyone. Both if no move was found or
 * if the last move was a pass, PASS_MOVE is returned.
 */
int
get_last_move()
{
  if (move_history_pointer == 0)
    return PASS_MOVE;

  return move_history_pos[move_history_pointer - 1];
}

/* Return the color of the player doing the last move. If no move was
 * found, EMPTY is returned.
 */
int
get_last_player()
{
  if (move_history_pointer == 0)
    return EMPTY;

  return move_history_color[move_history_pointer - 1];
}


/* ================================================================ */
/*                        Utility functions                         */
/* ================================================================ */


/*
 * Test if the move is a pass or not.  Return 1 if it is.
 */

int
is_pass(int pos)
{
  return pos == 0;
}


/*
 * is_legal(pos, color) determines whether the move (color) at pos is
 * legal. This is for internal use in the engine and always assumes
 * that suicide is allowed and only simple ko restrictions, no
 * superko, regardless of the rules actually used in the game.
 *
 * Use is_allowed_move() if you want to take alternative suicide and
 * ko rules into account.
 */

int 
is_legal(int pos, int color)
{
  /* 0. A pass move is always legal. */
  if (pos == PASS_MOVE)
    return 1;

  /* 1. The move must be inside the board. */
  ASSERT_ON_BOARD1(pos);

  /* 2. The location must be empty. */
  if (board[pos] != EMPTY) 
    return 0;

  /* 3. The location must not be the ko point. */
  if (pos == board_ko_pos) {
    /*    The ko position is guaranteed to have all neighbors of the
     *    same color, or off board. If that color is the same as the
     *    move the ko is being filled, which is always allowed. This
     *    could be tested with has_neighbor() but here a faster test
     *    suffices.
     */
    if (board[WEST(pos)] == OTHER_COLOR(color)
	|| board[EAST(pos)] == OTHER_COLOR(color)) {
      return 0;
    }
  }

  /* Check for stack overflow. */
  if (stackp >= MAXSTACK-2) {
    fprintf(stderr, 
	    "gnugo: Truncating search. This is beyond my reading ability!\n");
    /* FIXME: Perhaps it's best to just assert here and be done with it? */
    if (0) {
      ASSERT1(0 && "is_legal stack overflow", pos);
    }
    return 0;
  }

  /* Check for suicide. */
  if (is_suicide(pos, color))
    return 0;
  
  return 1;
}


/*
 * is_suicide(pos, color) determines whether the move (color) at
 * (pos) would be a suicide.
 *
 * This is the case if
 * 1. There is no neighboring empty intersection.
 * 2. There is no neighboring opponent string with exactly one liberty.
 * 3. There is no neighboring friendly string with more than one liberty.
 */
int 
is_suicide(int pos, int color)
{
  ASSERT_ON_BOARD1(pos);
  ASSERT1(board[pos] == EMPTY, pos);

  /* Check for suicide. */
  if (LIBERTY(SOUTH(pos))
      || (ON_BOARD(SOUTH(pos))
	  && ((board[SOUTH(pos)] == color) ^ (LIBERTIES(SOUTH(pos)) == 1))))
    return 0;

  if (LIBERTY(WEST(pos))
      || (ON_BOARD(WEST(pos))
	  && ((board[WEST(pos)] == color) ^ (LIBERTIES(WEST(pos)) == 1))))
    return 0;

  if (LIBERTY(NORTH(pos))
      || (ON_BOARD(NORTH(pos))
	  && ((board[NORTH(pos)] == color) ^ (LIBERTIES(NORTH(pos)) == 1))))
    return 0;

  if (LIBERTY(EAST(pos))
      || (ON_BOARD(EAST(pos))
	  && ((board[EAST(pos)] == color) ^ (LIBERTIES(EAST(pos)) == 1))))
    return 0;

  return 1;
}


/*
 * is_illegal_ko_capture(pos, color) determines whether the move
 * (color) at (pos) would be an illegal ko capture.
 */
int 
is_illegal_ko_capture(int pos, int color)
{
  ASSERT_ON_BOARD1(pos);
  ASSERT1(board[pos] == EMPTY, pos);

  return (pos == board_ko_pos
	  && ((board[WEST(pos)] == OTHER_COLOR(color))
	      || (board[EAST(pos)] == OTHER_COLOR(color))));
}

/*
 * is_allowed_move(int pos, int color) determines whether a move is
 * legal with respect to the suicide and ko rules in play.
 *
 * This function is only valid when stackp == 0 since there is no
 * tracking of superko for trymoves.
 */
int
is_allowed_move(int pos, int color)
{
  gg_assert(stackp == 0);

  /* 1. A pass move is always legal, no matter what. */
  if (pos == PASS_MOVE)
    return 1;

  /* 2. The move must be inside the board. */
  ASSERT_ON_BOARD1(pos);

  /* 3. The location must be empty. */
  if (board[pos] != EMPTY) 
    return 0;

  /* 4. Simple ko repetition is only allowed if no ko rule is in use.
   *    For superko rules this check is redundant.
   *
   *    The ko position is guaranteed to have all neighbors of the
   *    same color, or off board. If that color is the same as the
   *    move the ko is being filled, which is always allowed. This
   *    could be tested with has_neighbor() but here a faster test
   *    suffices.
   */
  if (ko_rule != NONE
      && pos == board_ko_pos
      && (board[WEST(pos)] == OTHER_COLOR(color)
	  || board[EAST(pos)] == OTHER_COLOR(color)))
    return 0;

  /* 5. Check for suicide. Suicide rule options:
   *    FORBIDDEN   - No suicides allowed.
   *    ALLOWED     - Suicide of more than one stone allowed.
   *    ALL_ALLOWED - All suicides allowed.
   */
  if (is_suicide(pos, color))
    if (suicide_rule == FORBIDDEN
	|| (suicide_rule == ALLOWED
	    && !has_neighbor(pos, color)))
      return 0;

  /* 6. Check for whole board repetitions. The superko options are
   *    SIMPLE, NONE - No superko restrictions.
   *    PSK          - Repetition of a previous position forbidden.
   *    SSK          - Repetition of a previous position with the same
   *                   player to move forbidden.
   */
  if (is_superko_violation(pos, color, ko_rule))
    return 0;
  
  return 1;
}

/* Necessary work to set the new komaster state. */
static void
set_new_komaster(int new_komaster)
{
  PUSH_VALUE(komaster);
  hashdata_invert_komaster(&board_hash, komaster);
  komaster = new_komaster;
  hashdata_invert_komaster(&board_hash, komaster);
}

/* Necessary work to set the new komaster position. */
static void
set_new_kom_pos(int new_kom_pos)
{
  PUSH_VALUE(kom_pos);
  hashdata_invert_kom_pos(&board_hash, kom_pos);
  kom_pos = new_kom_pos;
  hashdata_invert_kom_pos(&board_hash, kom_pos);
}

/* Variation of trymove()/tryko() where ko captures (both conditional
 * and unconditional) must follow a komaster scheme.
 *
 * Historical note: Up to GNU Go 3.4 five different komaster schemes
 * were implemented and could easily be switched between. In GNU Go
 * 3.5.1 four of them were removed to simplify the code and because it
 * no longer seemed interesting to be able to switch. The remaining
 * komaster scheme was previously known as komaster scheme 5 (or V).
 *
 * FIXME: This function could be optimized by integrating the
 * trymove()/tryko() code.
 */

/* V. Complex scheme, O to move.
 * 
 * 1. Komaster is EMPTY.
 * 1a) Unconditional ko capture is allowed.
 *       Komaster remains EMPTY if previous move was not a ko capture.
 *       Komaster is set to WEAK_KO if previous move was a ko capture
 *       and kom_pos is set to the old value of board_ko_pos.
 * 1b) Conditional ko capture is allowed. Komaster is set to O and
 *     kom_pos to the location of the ko, where a stone was
 *     just removed.
 * 
 * 2. Komaster is O:
 * 2a) Only nested ko captures are allowed. Kom_pos is moved to the
 *     new removed stone.
 * 2b) If komaster fills the ko at kom_pos then komaster reverts to
 *     EMPTY.
 * 
 * 3. Komaster is X:
 *    Play at kom_pos is not allowed. Any other ko capture
 *    is allowed. If O takes another ko, komaster becomes GRAY_X.
 * 
 * 4. Komaster is GRAY_O or GRAY_X:
 *    Ko captures are not allowed. If the ko at kom_pos is
 *    filled then the komaster reverts to EMPTY.
 *
 * 5. Komaster is WEAK_KO:
 * 5a) After a non-ko move komaster reverts to EMPTY.
 * 5b) Unconditional ko capture is only allowed if it is nested ko capture.
 *     Komaster is changed to WEAK_X and kom_pos to the old value of
 *     board_ko_pos.
 * 5c) Conditional ko capture is allowed according to the rules of 1b.
 */
int
komaster_trymove(int pos, int color, const char *message, int str,
		 int *is_conditional_ko, int consider_conditional_ko)
{
  int other = OTHER_COLOR(color);
  int ko_move;
  int kpos;
  int previous_board_ko_pos = board_ko_pos;

  *is_conditional_ko = 0;
  ko_move = is_ko(pos, color, &kpos);

  if (ko_move) {
    /* If opponent is komaster we may not capture his ko. */
    if (komaster == other && pos == kom_pos)
      return 0;

    /* If komaster is gray we may not capture ko at all. */
    if (komaster == GRAY_WHITE || komaster == GRAY_BLACK)
      return 0;

    /* If we are komaster, we may only do nested captures. */
    if (komaster == color && !DIAGONAL_NEIGHBORS(kpos, kom_pos))
      return 0;

    /* If komaster is WEAK_KO, we may only do nested ko capture or
     * conditional ko capture.
     */
    if (komaster == WEAK_KO) {
      if (pos != board_ko_pos && !DIAGONAL_NEIGHBORS(kpos, kom_pos))
	return 0;
    }
  }

  if (!trymove(pos, color, message, str)) {
    if (!consider_conditional_ko)
      return 0;

    if (!tryko(pos, color, message))
      return 0; /* Suicide. */
      
    *is_conditional_ko = 1;

    /* Conditional ko capture, set komaster parameters. */
    if (komaster == EMPTY || komaster == WEAK_KO) {
      set_new_komaster(color);
      set_new_kom_pos(kpos);
      return 1;
    }
  }

  if (!ko_move) {
    /* If we are komaster, check whether the ko was resolved by the
     * current move. If that is the case, revert komaster to EMPTY.
     *
     * The ko has been resolved in favor of the komaster if it has
     * been filled, or if it is no longer a ko and an opponent move
     * there is suicide.
     */
    if (((komaster == color
	  || (komaster == GRAY_WHITE && color == WHITE)
	  || (komaster == GRAY_BLACK && color == BLACK))
	 && (IS_STONE(board[kom_pos])
	     || (!is_ko(kom_pos, other, NULL)
		 && is_suicide(kom_pos, other))))) {
      set_new_komaster(EMPTY);
      set_new_kom_pos(NO_MOVE);
    }

    if (komaster == WEAK_KO) {
      set_new_komaster(EMPTY);
      set_new_kom_pos(NO_MOVE);
    }
    
    return 1;
  }

  if (komaster == other) {
    if (color == WHITE)
      set_new_komaster(GRAY_BLACK);
    else
      set_new_komaster(GRAY_WHITE);
  }
  else if (komaster == color) {
    /* This is where we update kom_pos after a nested capture. */
    set_new_kom_pos(kpos);
  }
  else {
    /* We can reach here when komaster is EMPTY or WEAK_KO. If previous
     * move was also a ko capture, we now set komaster to WEAK_KO.
     */
    if (previous_board_ko_pos != NO_MOVE) {
      set_new_komaster(WEAK_KO);
      set_new_kom_pos(previous_board_ko_pos);
    }
  }
  
  return 1;
}

int
get_komaster()
{
  return komaster;
}

int
get_kom_pos()
{
  return kom_pos;
}


/* Determine whether vertex is on the edge. */
int
is_edge_vertex(int pos)
{
  ASSERT_ON_BOARD1(pos);
  if (!ON_BOARD(SW(pos))
      || !ON_BOARD(NE(pos)))
    return 1;

  return 0;
}

/* Distance to the edge. */
int
edge_distance(int pos)
{
  int i = I(pos);
  int j = J(pos);
  ASSERT_ON_BOARD1(pos);
  return gg_min(gg_min(i, board_size-1 - i), gg_min(j, board_size-1 - j));
}


/* Determine whether vertex is a corner. */
int
is_corner_vertex(int pos)
{
  ASSERT_ON_BOARD1(pos);
  if ((!ON_BOARD(WEST(pos)) || !ON_BOARD(EAST(pos)))
      && (!ON_BOARD(SOUTH(pos)) || !ON_BOARD(NORTH(pos))))
    return 1;
  
  return 0;
}


/* Reorientation of point pos. This function could have been
 * implemented using the rotate() function in utils/gg_utils.c but we
 * don't want to make libboard dependent on utils.
 */
int
rotate1(int pos, int rot)
{
  int bs = board_size - 1;
  int i = I(pos);
  int j = J(pos);
  gg_assert(rot >= 0 && rot < 8);

  if (pos == PASS_MOVE)
    return PASS_MOVE;

  if (rot == 0)
    return pos;                 /* identity map */
  if (rot == 1)
    return POS(bs - j, i);      /* rotation over 90 degrees */
  if (rot == 2)
    return POS(bs - i, bs - j); /* rotation over 180 degrees */
  if (rot == 3)
    return POS(j, bs - i);      /* rotation over 270 degrees */
  if (rot == 4)
    return POS(j, i);           /* flip along diagonal */
  if (rot == 5)
    return POS(bs - i, j);      /* flip */
  if (rot == 6)
    return POS(bs - j, bs - i); /* flip along diagonal */
  if (rot == 7)
    return POS(i, bs - j);      /* flip */

  return PASS_MOVE;             /* unreachable */
}


/* Returns true if the empty vertex respectively the string at pos1 is
 * adjacent to the empty vertex respectively the string at pos2.
 */
int
are_neighbors(int pos1, int pos2)
{
  if (board[pos1] == EMPTY) {
    if (board[pos2] == EMPTY)
      return (gg_abs(pos1 - pos2) == NS || gg_abs(pos1 - pos2) == WE);
    else
      return neighbor_of_string(pos1, pos2);
  }
  else {
    if (board[pos2] == EMPTY)
      return neighbor_of_string(pos2, pos1);
    else
      return adjacent_strings(pos1, pos2);
  }
}


/* Count the number of liberties of the string at pos. pos must not be
 * empty.
 */
int
countlib(int str)
{
  ASSERT1(IS_STONE(board[str]), str);
  
  /* We already know the number of liberties. Just look it up. */
  return string[string_number[str]].liberties;
}


/* Find the liberties of the string at str. str must not be
 * empty. The locations of up to maxlib liberties are written into
 * libs[]. The full number of liberties is returned.
 *
 * If you want the locations of all liberties, whatever their number,
 * you should pass MAXLIBS as the value for maxlib and allocate space
 * for libs[] accordingly.
 */

int
findlib(int str, int maxlib, int *libs)
{
  int k;
  int liberties;
  int s;
  
  ASSERT1(IS_STONE(board[str]), str);
  ASSERT1(libs != NULL, str);
  
  /* We already have the list of liberties and only need to copy it to
   * libs[].
   *
   * However, if the string has more than MAX_LIBERTIES liberties the
   * list is truncated and if maxlib is also larger than MAX_LIBERTIES
   * we have to traverse the stones in the string in order to find
   * where the liberties are.
   */
  s = string_number[str];
  liberties = string[s].liberties;

  if (liberties <= MAX_LIBERTIES || maxlib <= MAX_LIBERTIES) {
    /* The easy case, it suffices to copy liberty locations from the
     * incrementally updated list.
     */
    for (k = 0; k < maxlib && k < liberties; k++)
      libs[k] = string_libs[s].list[k];
  }
  else {
    /* The harder case, where we have to traverse the stones in the
     * string. We don't have to check explicitly if we are back to
     * the start of the chain since we will run out of liberties
     * before that happens.
     */
    int pos;
    liberty_mark++;
    for (k = 0, pos = FIRST_STONE(s);
	 k < maxlib && k < liberties;
	 pos = NEXT_STONE(pos)) {
      if (UNMARKED_LIBERTY(SOUTH(pos))) {
	libs[k++] = SOUTH(pos);
	MARK_LIBERTY(SOUTH(pos));
	if (k >= maxlib)
	  break;
      }
      
      if (UNMARKED_LIBERTY(WEST(pos))) {
	libs[k++] = WEST(pos);
	MARK_LIBERTY(WEST(pos));
	if (k >= maxlib)
	  break;
      }
      
      if (UNMARKED_LIBERTY(NORTH(pos))) {
	libs[k++] = NORTH(pos);
	MARK_LIBERTY(NORTH(pos));
	if (k >= maxlib)
	  break;
      }
      
      if (UNMARKED_LIBERTY(EAST(pos))) {
	libs[k++] = EAST(pos);
	MARK_LIBERTY(EAST(pos));
	if (k >= maxlib)
	  break;
      }
    }
  }
      
  return liberties;
}

/* Count the liberties a stone of the given color would get if played
 * at (pos). The location (pos) must be empty.
 *
 * The intent of this function is to be as fast as possible, not
 * necessarily complete. But if it returns a positive value (meaning
 * it has succeeded), the value is guaranteed to be correct.
 *
 * Captures are ignored based on the ignore_capture flag.  The function
 * fails if there are more than two neighbor strings of the same
 * color.  In this case, the return value is -1.  Captures are handled
 * in a very limited way, so if ignore_capture is 0, and a capture is
 * required, it will often return -1.
 *
 * Note well, that it relies on incremental data.
 */

int
fastlib(int pos, int color, int ignore_captures)
{
  int ally1 = -1;
  int ally2 = -1;
  int fast_liberties = 0;

  ASSERT1(board[pos] == EMPTY, pos);
  ASSERT1(IS_STONE(color), pos);

  /* Find neighboring strings of the same color. If there are more than two of
   * them, we give up (it's too difficult to count their common liberties).
   */
  if (board[SOUTH(pos)] == color) {
    ally1 = string_number[SOUTH(pos)];

    if (board[WEST(pos)] == color
	&& string_number[WEST(pos)] != ally1) {
      ally2 = string_number[WEST(pos)];

      if (board[NORTH(pos)] == color
	  && string_number[NORTH(pos)] != ally1
	  && string_number[NORTH(pos)] != ally2)
	return -1;
    }
    else if (board[NORTH(pos)] == color
	     && string_number[NORTH(pos)] != ally1)
      ally2 = string_number[NORTH(pos)];

    if (board[EAST(pos)] == color
	&& string_number[EAST(pos)] != ally1) {
      if (ally2 < 0)
	ally2 = string_number[EAST(pos)];
      else if (string_number[EAST(pos)] != ally2)
	return -1;
    }
  }
  else if (board[WEST(pos)] == color) {
    ally1 = string_number[WEST(pos)];

    if (board[NORTH(pos)] == color
	&& string_number[NORTH(pos)] != ally1) {
      ally2 = string_number[NORTH(pos)];

      if (board[EAST(pos)] == color
	  && string_number[EAST(pos)] != ally1
	  && string_number[EAST(pos)] != ally2)
	return -1;
    }
    else if (board[EAST(pos)] == color
	     && string_number[EAST(pos)] != ally1)
      ally2 = string_number[EAST(pos)];
  }
  else if (board[NORTH(pos)] == color) {
    ally1 = string_number[NORTH(pos)];
    
    if (board[EAST(pos)] == color
	&& string_number[EAST(pos)] != ally1)
      ally2 = string_number[EAST(pos)];
  }
  else if (board[EAST(pos)] == color)
    ally1 = string_number[EAST(pos)];

  /* If we are to ignore captures, the things are very easy. */
  if (ignore_captures) {
    if (ally1 < 0) {			/* No allies */
      if (LIBERTY(SOUTH(pos)))
	fast_liberties++;
      if (LIBERTY(WEST(pos)))
	fast_liberties++;
      if (LIBERTY(NORTH(pos)))
	fast_liberties++;
      if (LIBERTY(EAST(pos)))
	fast_liberties++;
    }
    else if (ally2 < 0) {		/* One ally */
      if (LIBERTY(SOUTH(pos))
	  && !NON_SOUTH_NEIGHBOR_OF_STRING(SOUTH(pos), ally1, color))
	fast_liberties++;
      if (LIBERTY(WEST(pos))
	  && !NON_WEST_NEIGHBOR_OF_STRING(WEST(pos), ally1, color))
	fast_liberties++;
      if (LIBERTY(NORTH(pos))
	  && !NON_NORTH_NEIGHBOR_OF_STRING(NORTH(pos), ally1, color))
	fast_liberties++;
      if (LIBERTY(EAST(pos))
	  && !NON_EAST_NEIGHBOR_OF_STRING(EAST(pos), ally1, color))
	fast_liberties++;

      fast_liberties += string[ally1].liberties - 1;
    }
    else {				/* Two allies */
      if (LIBERTY(SOUTH(pos))
	  && !NON_SOUTH_NEIGHBOR_OF_STRING(SOUTH(pos), ally1, color)
	  && !NON_SOUTH_NEIGHBOR_OF_STRING(SOUTH(pos), ally2, color))
	fast_liberties++;
      if (LIBERTY(WEST(pos))
	  && !NON_WEST_NEIGHBOR_OF_STRING(WEST(pos), ally1, color)
	  && !NON_WEST_NEIGHBOR_OF_STRING(WEST(pos), ally2, color))
	fast_liberties++;
      if (LIBERTY(NORTH(pos))
	  && !NON_NORTH_NEIGHBOR_OF_STRING(NORTH(pos), ally1, color)
	  && !NON_NORTH_NEIGHBOR_OF_STRING(NORTH(pos), ally2, color))
	fast_liberties++;
      if (LIBERTY(EAST(pos))
	  && !NON_EAST_NEIGHBOR_OF_STRING(EAST(pos), ally1, color)
	  && !NON_EAST_NEIGHBOR_OF_STRING(EAST(pos), ally2, color))
	fast_liberties++;

      fast_liberties += string[ally1].liberties + string[ally2].liberties
	- count_common_libs(string[ally1].origin, string[ally2].origin) - 1;
    }
  }
  /* We are to take captures into account. This case is much more rare, so
   * it is not optimized much.
   */
  else {
    int k;

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

      if (LIBERTY(neighbor)
	  && (ally1 < 0 || !NEIGHBOR_OF_STRING(neighbor, ally1, color))
	  && (ally2 < 0 || !NEIGHBOR_OF_STRING(neighbor, ally2, color)))
	fast_liberties++;
      else if (board[neighbor] == OTHER_COLOR(color)	/* A capture */
	       && LIBERTIES(neighbor) == 1) {
	int neighbor_size = COUNTSTONES(neighbor);

	if (neighbor_size == 1 || (neighbor_size == 2 && ally1 < 0))
	  fast_liberties++;
	else
	  return -1;
      }
    }

    if (ally1 >= 0) {
      fast_liberties += string[ally1].liberties - 1;
      if (ally2 >= 0)
	fast_liberties += string[ally2].liberties
	  - count_common_libs(string[ally1].origin, string[ally2].origin);
    }
  }

  return fast_liberties;
}


/* Effectively true unless we store full position in hash. */
#define USE_BOARD_CACHES	(NUM_HASHVALUES <= 4)

struct board_cache_entry {
  int threshold;
  int liberties;
  Hash_data position_hash;
};


/* approxlib() cache. */
static struct board_cache_entry approxlib_cache[BOARDMAX][2];


/* Clears approxlib() cache. This function should be called only once
 * during engine initialization. Sets thresholds to zero.
 */
void
clear_approxlib_cache(void)
{
  int pos;

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    approxlib_cache[pos][0].threshold = 0;
    approxlib_cache[pos][1].threshold = 0;
  }
}


/* Find the liberties a stone of the given color would get if played
 * at (pos), ignoring possible captures of opponent stones. (pos)
 * must be empty. If libs != NULL, the locations of up to maxlib
 * liberties are written into libs[]. The counting of liberties may
 * or may not be halted when maxlib is reached. The number of liberties
 * found is returned.
 *
 * If you want the number or the locations of all liberties, however
 * many they are, you should pass MAXLIBS as the value for maxlib and
 * allocate space for libs[] accordingly.
 */
int
approxlib(int pos, int color, int maxlib, int *libs)
{
  int liberties;

#ifdef USE_BOARD_CACHES

  struct board_cache_entry *entry = &approxlib_cache[pos][color - 1];

  ASSERT1(board[pos] == EMPTY, pos);
  ASSERT1(IS_STONE(color), pos);

  if (!libs) {
    /* First see if this result is cached. */
    if (hashdata_is_equal(board_hash, entry->position_hash)
	&& maxlib <= entry->threshold) {
      return entry->liberties;
    }

    liberties = fastlib(pos, color, 1);
    if (liberties >= 0) {
      /* Since fastlib() always returns precise result and doesn't take
       * `maxlib' into account, we set threshold to MAXLIBS so that this
       * result is used regardless of any `maxlib' passed.
       */
      entry->threshold = MAXLIBS;
      entry->liberties = liberties;
      entry->position_hash = board_hash;

      return liberties;
    }
  }

  /* We initialize the cache entry threshold to `maxlib'. If do_approxlib()
   * or slow_approxlib() finds all the liberties (that is, they don't use
   * `maxlib' value for an early return), they will set threshold to
   * MAXLIBS themselves.
   */
  entry->threshold = maxlib;

  if (maxlib <= MAX_LIBERTIES)
    liberties = do_approxlib(pos, color, maxlib, libs);
  else
    liberties = slow_approxlib(pos, color, maxlib, libs);

  entry->liberties = liberties;
  entry->position_hash = board_hash;

#else /* not USE_BOARD_CACHES */

  ASSERT1(board[pos] == EMPTY, pos);
  ASSERT1(IS_STONE(color), pos);

  if (!libs) {
    liberties = fastlib(pos, color, 1);
    if (liberties >= 0)
      return liberties;
  }

  if (maxlib <= MAX_LIBERTIES)
    liberties = do_approxlib(pos, color, maxlib, libs);
  else
    liberties = slow_approxlib(pos, color, maxlib, libs);

#endif /* not USE_BOARD_CACHES */

  return liberties;
}


/* Does the real work of approxlib(). */
static int
do_approxlib(int pos, int color, int maxlib, int *libs)
{
  int k;
  int liberties = 0;

  /* Look for empty neighbors and the liberties of the adjacent
   * strings of the given color. The algorithm below won't work
   * correctly if any of the adjacent strings have more than
   * MAX_LIBERTIES liberties AND maxlib is larger than MAX_LIBERTIES.
   * therefore approxlib() calls more robust slow_approxlib() if
   * this might be the case.
   */

  /* Start by marking pos itself so it isn't counted among its own
   * liberties.
   */
  liberty_mark++;
  MARK_LIBERTY(pos);

  if (UNMARKED_LIBERTY(SOUTH(pos))) {
    if (libs != NULL)
      libs[liberties] = SOUTH(pos);
    liberties++;
    /* Stop counting if we reach maxlib. */
    if (liberties >= maxlib)
      return liberties;
    MARK_LIBERTY(SOUTH(pos));
  }
  else if (board[SOUTH(pos)] == color) {
    int s = string_number[SOUTH(pos)];
    for (k = 0; k < string[s].liberties; k++) {
      int lib = string_libs[s].list[k];
      if (UNMARKED_LIBERTY(lib)) {
	if (libs != NULL)
	  libs[liberties] = lib;
	liberties++;
	if (liberties >= maxlib)
	  return liberties;
	MARK_LIBERTY(lib);
      }
    }
  }
  
  if (UNMARKED_LIBERTY(WEST(pos))) {
    if (libs != NULL)
      libs[liberties] = WEST(pos);
    liberties++;
    /* Stop counting if we reach maxlib. */
    if (liberties >= maxlib)
      return liberties;
    MARK_LIBERTY(WEST(pos));
  }
  else if (board[WEST(pos)] == color) {
    int s = string_number[WEST(pos)];
    for (k = 0; k < string[s].liberties; k++) {
      int lib = string_libs[s].list[k];
      if (UNMARKED_LIBERTY(lib)) {
	if (libs != NULL)
	  libs[liberties] = lib;
	liberties++;
	if (liberties >= maxlib)
	  return liberties;
	MARK_LIBERTY(lib);
      }
    }
  }
  
  if (UNMARKED_LIBERTY(NORTH(pos))) {
    if (libs != NULL)
      libs[liberties] = NORTH(pos);
    liberties++;
    /* Stop counting if we reach maxlib. */
    if (liberties >= maxlib)
      return liberties;
    MARK_LIBERTY(NORTH(pos));
  }
  else if (board[NORTH(pos)] == color) {
    int s = string_number[NORTH(pos)];
    for (k = 0; k < string[s].liberties; k++) {
      int lib = string_libs[s].list[k];
      if (UNMARKED_LIBERTY(lib)) {
	if (libs != NULL)
	  libs[liberties] = lib;
	liberties++;
	if (liberties >= maxlib)
	  return liberties;
	MARK_LIBERTY(lib);
      }
    }
  }
  
  if (UNMARKED_LIBERTY(EAST(pos))) {
    if (libs != NULL)
      libs[liberties] = EAST(pos);
    liberties++;
    /* Unneeded since we're about to leave. */
#if 0
    if (liberties >= maxlib)
      return liberties;
    MARK_LIBERTY(EAST(pos));
#endif
  }
  else if (board[EAST(pos)] == color) {
    int s = string_number[EAST(pos)];
    for (k = 0; k < string[s].liberties; k++) {
      int lib = string_libs[s].list[k];
      if (UNMARKED_LIBERTY(lib)) {
	if (libs != NULL)
	  libs[liberties] = lib;
	liberties++;
	if (liberties >= maxlib)
	  return liberties;
	MARK_LIBERTY(lib);
      }
    }
  }  

#if USE_BOARD_CACHES
  /* If we reach here, then we have counted _all_ the liberties, so
   * we set threshold to MAXLIBS (the result is the same regardless
   * of `maxlib' value).
   */
  if (!libs)
    approxlib_cache[pos][color - 1].threshold = MAXLIBS;
#endif
  return liberties;
}


/* Find the liberties a move of the given color at pos would have,
 * excluding possible captures, by traversing all adjacent friendly
 * strings. This is a fallback used by approxlib() when a faster
 * algorithm can't be used.
 */
static int
slow_approxlib(int pos, int color, int maxlib, int *libs)
{
  int k;
  int liberties = 0;

  liberty_mark++;
  MARK_LIBERTY(pos);
  string_mark++;
  for (k = 0; k < 4; k++) {
    int d = delta[k];
    if (UNMARKED_LIBERTY(pos + d)) {
      if (libs)
	libs[liberties] = pos + d;
      liberties++;
      if (liberties == maxlib)
	return liberties;
      MARK_LIBERTY(pos + d);
    }
    else if (board[pos + d] == color
	     && UNMARKED_STRING(pos + d)) {
      int s = string_number[pos + d];
      int pos2;
      pos2 = FIRST_STONE(s);
      do {
	int l;
	for (l = 0; l < 4; l++) {
	  int d2 = delta[l];
	  if (UNMARKED_LIBERTY(pos2 + d2)) {
	    if (libs)
	      libs[liberties] = pos2 + d2;
	    liberties++;
	    if (liberties == maxlib)
	      return liberties;
	    MARK_LIBERTY(pos2 + d2);
	  }
	}

	pos2 = NEXT_STONE(pos2);
      } while (!BACK_TO_FIRST_STONE(s, pos2));
      MARK_STRING(pos + d);
    }
  }

#if USE_BOARD_CACHES
  /* If we reach here, then we have counted _all_ the liberties, so
   * we set threshold to MAXLIBS (the result is the same regardless
   * of `maxlib' value).
   */
  if (!libs)
    approxlib_cache[pos][color - 1].threshold = MAXLIBS;
#endif
  return liberties;
}


/* accuratelib() cache. */
static struct board_cache_entry accuratelib_cache[BOARDMAX][2];


/* Clears accuratelib() cache. This function should be called only once
 * during engine initialization. Sets thresholds to zero.
 */
void
clear_accuratelib_cache(void)
{
  int pos;

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    accuratelib_cache[pos][0].threshold = 0;
    accuratelib_cache[pos][1].threshold = 0;
  }
}


/* Find the liberties a stone of the given color would get if played
 * at (pos). This function takes into consideration all captures. Its
 * return value is exact in that sense it counts all the liberties,
 * unless (maxlib) allows it to stop earlier. (pos) must be empty. If
 * libs != NULL, the locations of up to maxlib liberties are written
 * into libs[]. The counting of liberties may or may not be halted
 * when maxlib is reached. The number of found liberties is returned.
 *
 * This function guarantees that liberties which are not results of
 * captures come first in libs[] array. To find whether all the 
 * liberties starting from a given one are results of captures, one
 * may use  if (board[libs[k]] != EMPTY)  construction.
 *
 * If you want the number or the locations of all liberties, however
 * many they are, you should pass MAXLIBS as the value for maxlib and
 * allocate space for libs[] accordingly.
 */
int
accuratelib(int pos, int color, int maxlib, int *libs)
{
  int liberties;

#ifdef USE_BOARD_CACHES

  struct board_cache_entry *entry = &accuratelib_cache[pos][color - 1];

  ASSERT1(board[pos] == EMPTY, pos);
  ASSERT1(IS_STONE(color), pos);

  if (!libs) {
    /* First see if this result is cached. */
    if (hashdata_is_equal(board_hash, entry->position_hash)
	&& maxlib <= entry->threshold) {
      return entry->liberties;
    }

    liberties = fastlib(pos, color, 0);
    if (liberties >= 0) {
      /* Since fastlib() always returns precise result and doesn't take
       * `maxlib' into account, we set threshold to MAXLIBS so that this
       * result is used regardless of any `maxlib' passed.
       */
      entry->threshold = MAXLIBS;
      entry->liberties = liberties;
      entry->position_hash = board_hash;

      return liberties;
    }
  }

  liberties = do_accuratelib(pos, color, maxlib, libs);

  /* If accuratelib() found less than `maxlib' liberties, then its
   * result is certainly independent of `maxlib' and we set threshold
   * to MAXLIBS.
   */
  entry->threshold = liberties < maxlib ? MAXLIBS : maxlib;
  entry->liberties = liberties;
  entry->position_hash = board_hash;

#else /* not USE_BOARD_CACHES */

  ASSERT1(board[pos] == EMPTY, pos);
  ASSERT1(IS_STONE(color), pos);

  if (!libs) {
    liberties = fastlib(pos, color, 0);
    if (liberties >= 0)
      return liberties;
  }

  liberties = do_accuratelib(pos, color, maxlib, libs);

#endif /* not USE_BOARD_CACHES */

  return liberties;
}


/* Does the real work of accuratelib(). */
static int
do_accuratelib(int pos, int color, int maxlib, int *libs)
{
  int k, l;
  int liberties = 0;
  int lib;
  int captured[4];
  int captures = 0;

  string_mark++;
  liberty_mark++;
  MARK_LIBERTY(pos);

  for (k = 0; k < 4; k++) {
    int pos2 = pos + delta[k];
    if (UNMARKED_LIBERTY(pos2)) {
      /* A trivial liberty */
      if (libs)
	libs[liberties] = pos2;
      liberties++;
      if (liberties >= maxlib)
	return liberties;

      MARK_LIBERTY(pos2);
    }
    else if (UNMARKED_COLOR_STRING(pos2, color)) {
      /* An own neighbor string */
      struct string_data *s = &string[string_number[pos2]];
      struct string_liberties_data *sl = &string_libs[string_number[pos2]];

      if (s->liberties <= MAX_LIBERTIES || maxlib <= MAX_LIBERTIES - 1) {
	/* The easy case - we already have all (necessary) liberties of
	 * the string listed
	 */
	for (l = 0; l < s->liberties; l++) {
	  lib = sl->list[l];
	  if (UNMARKED_LIBERTY(lib)) {
	    if (libs)
	      libs[liberties] = lib;
	    liberties++;
	    if (liberties >= maxlib)
	      return liberties;

	    MARK_LIBERTY(lib);
	  }
	}
      }
      else {
	/* The harder case - we need to find all the liberties of the
	 * string by traversing its stones. We stop as soon as we have
	 * traversed all the stones or have reached maxlib. Unfortunately,
	 * we cannot use the trick from findlib() since some of the
	 * liberties may already have been marked.
	 */
	int stone = pos2;
	do {
	  if (UNMARKED_LIBERTY(SOUTH(stone))) {
	    if (libs)
	      libs[liberties] = SOUTH(stone);
	    liberties++;
	    if (liberties >= maxlib)
	      return liberties;

	    MARK_LIBERTY(SOUTH(stone));
	  }

	  if (UNMARKED_LIBERTY(WEST(stone))) {
	    if (libs)
	      libs[liberties] = WEST(stone);
	    liberties++;
	    if (liberties >= maxlib)
	      return liberties;

	    MARK_LIBERTY(WEST(stone));
	  }

	  if (UNMARKED_LIBERTY(NORTH(stone))) {
	    if (libs)
	      libs[liberties] = NORTH(stone);
	    liberties++;
	    if (liberties >= maxlib)
	      return liberties;

	    MARK_LIBERTY(NORTH(stone));
	  }

	  if (UNMARKED_LIBERTY(EAST(stone))) {
	    if (libs)
	      libs[liberties] = EAST(stone);
	    liberties++;
	    if (liberties >= maxlib)
	      return liberties;

	    MARK_LIBERTY(EAST(stone));
	  }

	  stone = NEXT_STONE(stone);
	} while (stone != pos2);
      }

      MARK_STRING(pos2);
    }
    else if (board[pos2] == OTHER_COLOR(color)
	     && string[string_number[pos2]].liberties == 1) {
      /* A capture. */
      captured[captures++] = pos2;
    }
  }

  /* Now we look at all the captures found in the previous step */
  for (k = 0; k < captures; k++) {
    lib = captured[k];

    /* Add the stone adjacent to (pos) to the list of liberties if
     * it is not also adjacent to an own marked string (otherwise,
     * it will be added later).
     */
    if (!MARKED_COLOR_STRING(SOUTH(lib), color)
	&& !MARKED_COLOR_STRING(WEST(lib), color)
	&& !MARKED_COLOR_STRING(NORTH(lib), color)
	&& !MARKED_COLOR_STRING(EAST(lib), color)) {
      if (libs)
	libs[liberties] = lib;
      liberties++;
      if (liberties >= maxlib)
	return liberties;
    }

    /* Check if we already know of this capture. */
    for (l = 0; l < k; l++)
      if (string_number[captured[l]] == string_number[lib])
	break;

    if (l == k) {
      /* Traverse all the stones of the capture and add to the list
       * of liberties those, which are adjacent to at least one own
       * marked string.
       */
      do {
	if (MARKED_COLOR_STRING(SOUTH(lib), color)
	    || MARKED_COLOR_STRING(WEST(lib), color)
	    || MARKED_COLOR_STRING(NORTH(lib), color)
	    || MARKED_COLOR_STRING(EAST(lib), color)) {
	  if (libs)
	    libs[liberties] = lib;
	  liberties++;
	  if (liberties >= maxlib)
	    return liberties;
	}

	lib = NEXT_STONE(lib);
      } while (lib != captured[k]);
    }
  }

  return liberties;
}


/* Find the number of common liberties of the two strings at str1 and str2.
 */

int
count_common_libs(int str1, int str2)
{
  int all_libs1[MAXLIBS], *libs1;
  int liberties1, liberties2;
  int commonlibs = 0;
  int k, n, tmp;
  
  ASSERT_ON_BOARD1(str1);
  ASSERT_ON_BOARD1(str2);
  ASSERT1(IS_STONE(board[str1]), str1);
  ASSERT1(IS_STONE(board[str2]), str2);
  
  n = string_number[str1];
  liberties1 = string[n].liberties;
  
  if (liberties1 > string[string_number[str2]].liberties) {
    n = string_number[str2];
    liberties1 = string[n].liberties;
    tmp = str1;
    str1 = str2;
    str2 = tmp;
  }

  if (liberties1 <= MAX_LIBERTIES) {
    /* Speed optimization: don't copy liberties with findlib */
    libs1 = string_libs[n].list;
    n = string_number[str2];
    liberties2 = string[n].liberties;
    
    if (liberties2 <= MAX_LIBERTIES) {
      /* Speed optimization: NEIGHBOR_OF_STRING is quite expensive */
      liberty_mark++;
      
      for (k = 0; k < liberties1; k++)
	MARK_LIBERTY(libs1[k]);

      libs1 = string_libs[n].list;
      for (k = 0; k < liberties2; k++)
	if (!UNMARKED_LIBERTY(libs1[k]))
	  commonlibs++;

      return commonlibs;
    }
  }
  else {
    findlib(str1, MAXLIBS, all_libs1);
    libs1 = all_libs1;
  }
  
  for (k = 0; k < liberties1; k++)
    if (NEIGHBOR_OF_STRING(libs1[k], string_number[str2], board[str2]))
      commonlibs++;
  
  return commonlibs;
}


/* Find the common liberties of the two strings at str1 and str2. The
 * locations of up to maxlib common liberties are written into libs[].
 * The full number of common liberties is returned.
 *
 * If you want the locations of all common liberties, whatever their
 * number, you should pass MAXLIBS as the value for maxlib and
 * allocate space for libs[] accordingly.
 */

int
find_common_libs(int str1, int str2, int maxlib, int *libs)
{
  int all_libs1[MAXLIBS], *libs1;
  int liberties1, liberties2;
  int commonlibs = 0;
  int k, n, tmp;
  
  ASSERT_ON_BOARD1(str1);
  ASSERT_ON_BOARD1(str2);
  ASSERT1(IS_STONE(board[str1]), str1);
  ASSERT1(IS_STONE(board[str2]), str2);
  ASSERT1(libs != NULL, str1);
  
  n = string_number[str1];
  liberties1 = string[n].liberties;
  
  if (liberties1 > string[string_number[str2]].liberties) {
    n = string_number[str2];
    liberties1 = string[n].liberties;
    tmp = str1;
    str1 = str2;
    str2 = tmp;
  }
  
  if (liberties1 <= MAX_LIBERTIES) {
    /* Speed optimization: don't copy liberties with findlib */
    libs1 = string_libs[n].list;
    n = string_number[str2];
    liberties2 = string[n].liberties;

    if (liberties2 <= MAX_LIBERTIES) {
      /* Speed optimization: NEIGHBOR_OF_STRING is quite expensive */
      liberty_mark++;

      for (k = 0; k < liberties1; k++)
	MARK_LIBERTY(libs1[k]);
      
      libs1 = string_libs[n].list;
      for (k = 0; k < liberties2; k++)
	if (!UNMARKED_LIBERTY(libs1[k])) {
          if (commonlibs < maxlib)
	    libs[commonlibs] = libs1[k];
	  commonlibs++;
	}
      
      return commonlibs;
    }
  }
  else {
    findlib(str1, MAXLIBS, all_libs1);
    libs1 = all_libs1;
  }
  
  for (k = 0; k < liberties1; k++)
    if (NEIGHBOR_OF_STRING(libs1[k], string_number[str2], board[str2])) {
      if (commonlibs < maxlib)
	libs[commonlibs] = libs1[k];
      commonlibs++;
    }
  
  return commonlibs;
}


/* Determine whether two strings have at least one common liberty.
 * If they do and lib != NULL, one common liberty is returned in *lib.
 */
int
have_common_lib(int str1, int str2, int *lib)
{
  int all_libs1[MAXLIBS], *libs1;
  int liberties1;
  int k, n, tmp;
  
  ASSERT_ON_BOARD1(str1);
  ASSERT_ON_BOARD1(str2);
  ASSERT1(IS_STONE(board[str1]), str1);
  ASSERT1(IS_STONE(board[str2]), str2);
  
  n = string_number[str1];
  liberties1 = string[n].liberties;
  
  if (liberties1 > string[string_number[str2]].liberties) {
    n = string_number[str2];
    liberties1 = string[n].liberties;
    tmp = str1;
    str1 = str2;
    str2 = tmp;
  }
  
  if (liberties1 <= MAX_LIBERTIES)
    /* Speed optimization: don't copy liberties with findlib */
    libs1 = string_libs[n].list;
  else {
    findlib(str1, MAXLIBS, all_libs1);
    libs1 = all_libs1;
  }

  for (k = 0; k < liberties1; k++) {
    if (NEIGHBOR_OF_STRING(libs1[k], string_number[str2], board[str2])) {
      if (lib)
	*lib = libs1[k];
      return 1;
    }
  }
  
  return 0;
}



/*
 * Report the number of stones in a string.
 */

int
countstones(int str)
{
  ASSERT_ON_BOARD1(str);
  ASSERT1(IS_STONE(board[str]), str);

  return COUNTSTONES(str);
}


/* Find the stones of the string at str. str must not be
 * empty. The locations of up to maxstones stones are written into
 * stones[]. The full number of stones is returned.
 */

int
findstones(int str, int maxstones, int *stones)
{
  int s;
  int size;
  int pos;
  int k;
  
  ASSERT_ON_BOARD1(str);
  ASSERT1(IS_STONE(board[str]), str);

  s = string_number[str];
  size = string[s].size;
  
  /* Traverse the stones of the string, by following the cyclic chain. */
  pos = FIRST_STONE(s);
  for (k = 0; k < maxstones && k < size; k++) {
    stones[k] = pos;
    pos = NEXT_STONE(pos);
  }

  return size;
}


/* Counts how many stones in str1 are directly adjacent to str2.
 * A limit can be given in the maxstones parameter so that the
 * function returns immediately. See fast_defense() in reading.c
 */

int
count_adjacent_stones(int str1, int str2, int maxstones)
{
  int s1, s2;
  int size;
  int pos;
  int k;
  int count = 0;

  ASSERT_ON_BOARD1(str1);
  ASSERT1(IS_STONE(board[str1]), str1);
  ASSERT_ON_BOARD1(str2);
  ASSERT1(IS_STONE(board[str2]), str2);

  s1 = string_number[str1];
  s2 = string_number[str2];
  size = string[s1].size;

  /* Traverse the stones of the string, by following the cyclic chain. */
  pos = FIRST_STONE(s1);
  for (k = 0; k < size && count < maxstones; k++) {
    if (NEIGHBOR_OF_STRING(pos, s2, board[str2]))
      count++;
    pos = NEXT_STONE(pos);
  }

  return count;
}


/* chainlinks returns (in the (adj) array) the chains surrounding
 * the string at (str). The number of chains is returned.
 */

int 
chainlinks(int str, int adj[MAXCHAIN])
{
  struct string_data *s;
  struct string_neighbors_data *sn;
  int k;

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

  /* We already have the list ready, just copy it and fill in the
   * desired information.
   */
  s = &string[string_number[str]];
  sn = &string_neighbors[string_number[str]];
  for (k = 0; k < s->neighbors; k++)
    adj[k] = string[sn->list[k]].origin;

  return s->neighbors;
}


/* chainlinks2 returns (in adj array) those chains surrounding
 * the string at str which have exactly lib liberties. The number
 * of such chains is returned.
 */

int
chainlinks2(int str, int adj[MAXCHAIN], int lib)
{
  struct string_data *s, *t;
  struct string_neighbors_data *sn;
  int k;
  int neighbors;

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

  /* We already have the list ready, just copy the strings with the
   * right number of liberties.
   */
  neighbors = 0;
  s = &string[string_number[str]];
  sn = &string_neighbors[string_number[str]];
  for (k = 0; k < s->neighbors; k++) {
    t = &string[sn->list[k]];
    if (t->liberties == lib)
      adj[neighbors++] = t->origin;
  }
  return neighbors;
}


/* chainlinks3 returns (in adj array) those chains surrounding
 * the string at str, which have less or equal lib liberties.
 * The number of such chains is returned.
 */

int
chainlinks3(int str, int adj[MAXCHAIN], int lib)
{
  struct string_data *s, *t;
  struct string_neighbors_data *sn;
  int k;
  int neighbors;

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

  /* We already have the list ready, just copy the strings with the
   * right number of liberties.
   */
  neighbors = 0;
  s = &string[string_number[str]];
  sn = &string_neighbors[string_number[str]];
  for (k = 0; k < s->neighbors; k++) {
    t = &string[sn->list[k]];
    if (t->liberties <= lib)
      adj[neighbors++] = t->origin;
  }
  return neighbors;
}


/* extended_chainlinks() returns (in the (adj) array) the opponent
 * strings being directly adjacent to (str) or having a common liberty
 * with (str). The number of such strings is returned.
 *
 * If the both_colors parameter is true, also own strings sharing a
 * liberty are returned.
 */

int 
extended_chainlinks(int str, int adj[MAXCHAIN], int both_colors)
{
  struct string_data *s;
  struct string_neighbors_data *sn;
  int n;
  int k;
  int r;
  int libs[MAXLIBS];
  int liberties;

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

  /* We already have the list of directly adjacent strings ready, just
   * copy it and mark the strings.
   */
  s = &string[string_number[str]];
  sn = &string_neighbors[string_number[str]];
  string_mark++;
  for (n = 0; n < s->neighbors; n++) {
    adj[n] = string[sn->list[n]].origin;
    MARK_STRING(adj[n]);
  }

  /* Get the liberties. */
  liberties = findlib(str, MAXLIBS, libs);

  /* Look for unmarked opponent strings next to a liberty and add the
   * ones which are found to the output.
   */
  for (r = 0; r < liberties; r++) {
    for (k = 0; k < 4; k++) {
      if ((board[libs[r] + delta[k]] == OTHER_COLOR(board[str])
	   || (both_colors && board[libs[r] + delta[k]] == board[str]))
	  && UNMARKED_STRING(libs[r] + delta[k])) {
	adj[n] = string[string_number[libs[r] + delta[k]]].origin;
	MARK_STRING(adj[n]);
	n++;
      }
    }
  }
  
  return n;
}


/* Returns true if a move by (color) fits a shape like:
 *
 *  -----
 *  O.O*X        (O=color)
 *  OOXXX
 *
 * More specifically the move should have the following properties:
 * - The move is a self-atari
 * - The move forms a string of exactly two stones
 * - When the opponent captures, the capturing stone becomes a single
 *   stone in atari
 * - When capturing back the original position is repeated
 */

int
send_two_return_one(int move, int color)
{
  int other = OTHER_COLOR(color);
  int lib = NO_MOVE;
  int friendly_neighbor = NO_MOVE;
  int k;
  
  ASSERT1(board[move] == EMPTY, move);

  for (k = 0; k < 4; k++) {
    int pos = move + delta[k];
    if (board[pos] == EMPTY)
      return 0;
    if (board[pos] == color) {
      int s;
      if (friendly_neighbor != NO_MOVE)
	return 0;
      friendly_neighbor = pos;
      s = string_number[pos];
      if (string[s].size != 1 || string[s].liberties != 2)
	return 0;
      lib = string_libs[s].list[0] + string_libs[s].list[1] - move;
    }
    else if (board[pos] == other
	     && string[string_number[pos]].liberties == 1)
      return 0;
  }
  
  if (friendly_neighbor == NO_MOVE)
    return 0;

  for (k = 0; k < 4; k++) {
    int pos = lib + delta[k];
    if (board[pos] == EMPTY || board[pos] == other)
      return 0;
    if (board[pos] == color &&
	string[string_number[pos]].liberties < 2)
      return 0;
  }
  
  return 1;
}


/*
 * Find the origin of a worm, i.e. the point with the
 * smallest 1D board coordinate. The idea is to have a canonical
 * reference point for a string.
 */

int
find_origin(int str)
{
  ASSERT1(IS_STONE(board[str]), str);

  return string[string_number[str]].origin;
}


/* Determine whether a move by color at (pos) would be a self atari,
 * i.e. whether it would get more than one liberty. This function
 * returns true also for the case of a suicide move.
 */

int
is_self_atari(int pos, int color)
{
  int other = OTHER_COLOR(color);
  /* number of empty neighbors */
  int trivial_liberties = 0;
  /* number of captured opponent strings */
  int captures = 0;
  /* Whether there is a friendly neighbor with a spare liberty. If it
   * has more than one spare liberty we immediately return 0.
   */
  int far_liberties = 0;
  
  ASSERT_ON_BOARD1(pos);
  ASSERT1(board[pos] == EMPTY, pos);
  ASSERT1(IS_STONE(color), pos);

  /* 1. Try first to solve the problem without much work. */
  string_mark++;
  
  if (LIBERTY(SOUTH(pos)))
    trivial_liberties++;
  else if (board[SOUTH(pos)] == color) {
    if (LIBERTIES(SOUTH(pos)) > 2)
      return 0;
    if (LIBERTIES(SOUTH(pos)) == 2)
      far_liberties++;
  }
  else if (board[SOUTH(pos)] == other
          && LIBERTIES(SOUTH(pos)) == 1 && UNMARKED_STRING(SOUTH(pos))) {
    captures++;
    MARK_STRING(SOUTH(pos));
  }

  if (LIBERTY(WEST(pos)))
    trivial_liberties++;
  else if (board[WEST(pos)] == color) {
    if (LIBERTIES(WEST(pos)) > 2)
      return 0;
    if (LIBERTIES(WEST(pos)) == 2)
      far_liberties++;
  }
  else if (board[WEST(pos)] == other
          && LIBERTIES(WEST(pos)) == 1 && UNMARKED_STRING(WEST(pos))) {
    captures++;
    MARK_STRING(WEST(pos));
  }

  if (LIBERTY(NORTH(pos)))
    trivial_liberties++;
  else if (board[NORTH(pos)] == color) {
    if (LIBERTIES(NORTH(pos)) > 2)
      return 0;
    if (LIBERTIES(NORTH(pos)) == 2)
      far_liberties++;
  }
  else if (board[NORTH(pos)] == other
          && LIBERTIES(NORTH(pos)) == 1 && UNMARKED_STRING(NORTH(pos))) {
    captures++;
    MARK_STRING(NORTH(pos));
  }

  if (LIBERTY(EAST(pos)))
    trivial_liberties++;
  else if (board[EAST(pos)] == color) {
    if (LIBERTIES(EAST(pos)) > 2)
      return 0;
    if (LIBERTIES(EAST(pos)) == 2)
      far_liberties++;
  }
  else if (board[EAST(pos)] == other
          && LIBERTIES(EAST(pos)) == 1 && UNMARKED_STRING(EAST(pos))) {
    captures++;
#if 0
    MARK_STRING(EAST(pos));
#endif
  }

  /* Each captured string is guaranteed to produce at least one
   * liberty. These are disjoint from both trivial liberties and far
   * liberties. The two latter may however coincide.
   */
  if (trivial_liberties + captures >= 2)
    return 0;

  if ((far_liberties > 0) + captures >= 2)
    return 0;

  if (captures == 0 && far_liberties + trivial_liberties <= 1)
    return 1;

  /* 2. It was not so easy.  We use accuratelib() in this case. */
  return accuratelib(pos, color, 2, NULL) <= 1;
}


/*
 * Returns true if pos is a liberty of the string at str.
 */

int
liberty_of_string(int pos, int str)
{
  ASSERT_ON_BOARD1(pos);
  ASSERT_ON_BOARD1(str);
  if (IS_STONE(board[pos]))
    return 0;

  return NEIGHBOR_OF_STRING(pos, string_number[str], board[str]);
}


/*
 * Returns true if pos is a second order liberty of the string at str.
 */
int
second_order_liberty_of_string(int pos, int str)
{
  int k;
  ASSERT_ON_BOARD1(pos);
  ASSERT_ON_BOARD1(str);

  for (k = 0; k < 4; k++)
    if (board[pos + delta[k]] == EMPTY
	&& NEIGHBOR_OF_STRING(pos + delta[k], string_number[str], board[str]))
      return 1;

  return 0;
}


/*
 * Returns true if pos is adjacent to the string at str.
 */

int
neighbor_of_string(int pos, int str)
{
  int color = board[str];

  ASSERT1(IS_STONE(color), str);
  ASSERT_ON_BOARD1(pos);

  return NEIGHBOR_OF_STRING(pos, string_number[str], color);
}

/*
 * Returns true if (pos) has a neighbor of color (color).
 */

int
has_neighbor(int pos, int color)
{
  ASSERT_ON_BOARD1(pos);
  ASSERT1(IS_STONE(color), pos);

  return (board[SOUTH(pos)] == color
          || board[WEST(pos)] == color
          || board[NORTH(pos)] == color
          || board[EAST(pos)] == color);
}

/*
 * Returns true if str1 and str2 belong to the same string.
 */

int
same_string(int str1, int str2)
{
  ASSERT_ON_BOARD1(str1);
  ASSERT_ON_BOARD1(str2);
  ASSERT1(IS_STONE(board[str1]), str1);
  ASSERT1(IS_STONE(board[str2]), str2);

  return string_number[str1] == string_number[str2];
}


/*
 * Returns true if the strings at str1 and str2 are adjacent.
 */

int
adjacent_strings(int str1, int str2)
{
  int s1, s2;
  int k;
  
  ASSERT_ON_BOARD1(str1);
  ASSERT_ON_BOARD1(str2);
  ASSERT1(IS_STONE(board[str1]), str1);
  ASSERT1(IS_STONE(board[str2]), str2);

  s1 = string_number[str1];
  s2 = string_number[str2];

  for (k = 0; k < string[s1].neighbors; k++)
    if (string_neighbors[s1].list[k] == s2)
      return 1;

  return 0;
}


/*
 * Return true if the move (pos) by (color) is a ko capture
 * (whether capture is legal on this move or not). If so,
 * and if ko_pos is not a NULL pointer, then
 * *ko_pos returns the location of the captured ko stone.
 * If the move is not a ko capture, *ko_pos is set to 0.
 *
 * A move is a ko capture if and only if
 *    1. All neighbors are opponent stones.
 *    2. The number of captured stones is exactly one.
 */

int
is_ko(int pos, int color, int *ko_pos)
{
  int other = OTHER_COLOR(color);
  int captures = 0;
  int kpos = 0;
  
  ASSERT_ON_BOARD1(pos);
  ASSERT1(color == WHITE || color == BLACK, pos);

  if (ON_BOARD(SOUTH(pos))) {
    if (board[SOUTH(pos)] != other)
      return 0;
    else if (LIBERTIES(SOUTH(pos)) == 1) {
      kpos = SOUTH(pos);
      captures += string[string_number[SOUTH(pos)]].size;
      if (captures > 1)
	return 0;
    }
  }
  
  if (ON_BOARD(WEST(pos))) {
    if (board[WEST(pos)] != other)
      return 0;
    else if (LIBERTIES(WEST(pos)) == 1) {
      kpos = WEST(pos);
      captures += string[string_number[WEST(pos)]].size;
      if (captures > 1)
	return 0;
    }
  }
  
  if (ON_BOARD(NORTH(pos))) {
    if (board[NORTH(pos)] != other)
      return 0;
    else if (LIBERTIES(NORTH(pos)) == 1) {
      kpos = NORTH(pos);
      captures += string[string_number[NORTH(pos)]].size;
      if (captures > 1)
	return 0;
    }
  }
  
  if (ON_BOARD(EAST(pos))) {
    if (board[EAST(pos)] != other)
      return 0;
    else if (LIBERTIES(EAST(pos)) == 1) {
      kpos = EAST(pos);
      captures += string[string_number[EAST(pos)]].size;
      if (captures > 1)
	return 0;
    }
  }
  
  if (captures == 1) {
    if (ko_pos)
      *ko_pos = kpos;
    return 1;
  }
  return 0;
}


/* Return true if pos is either a stone, which if captured would give
 * ko, or if pos is an empty intersection adjacent to a ko stone.
 */
int
is_ko_point(int pos)
{
  ASSERT_ON_BOARD1(pos);

  if (board[pos] == EMPTY) {
    int color;
    if (ON_BOARD(SOUTH(pos)))
      color = board[SOUTH(pos)];
    else
      color = board[NORTH(pos)];
    if (IS_STONE(color) && is_ko(pos, OTHER_COLOR(color), NULL))
      return 1;
  }
  else {
    struct string_data *s = &string[string_number[pos]];
    struct string_liberties_data *sl = &string_libs[string_number[pos]];
    if (s->liberties == 1 && s->size == 1
	&& is_ko(sl->list[0], OTHER_COLOR(s->color), NULL))
      return 1;
  }

  return 0;
}


/* Return true if a move by color at pos is a superko violation
 * according to the specified type of ko rules. This function does not
 * detect simple ko unless it's also a superko violation.
 *
 * The superko detection is done by comparing board hashes from
 * previous positions. For this to work correctly it's necessary to
 * remove the contribution to the hash from the simple ko position.
 * The move_history_hash array contains board hashes for previous
 * positions, also without simple ko position contributions.
 */
static int
is_superko_violation(int pos, int color, enum ko_rules type)
{
  Hash_data this_board_hash = board_hash;
  Hash_data new_board_hash;
  int k;

  /* No superko violations if the ko rule is not a superko rule. */
  if (type == NONE || type == SIMPLE)
    return 0;

  if (board_ko_pos != NO_MOVE)
    hashdata_invert_ko(&this_board_hash, board_ko_pos);

  really_do_trymove(pos, color);
  new_board_hash = board_hash;
  if (board_ko_pos != NO_MOVE)
    hashdata_invert_ko(&new_board_hash, board_ko_pos);
  undo_trymove();

  /* The current position is only a problem with positional superko
   * and a single stone suicide.
   */
  if (type == PSK && hashdata_is_equal(this_board_hash, new_board_hash))
    return 1;

  for (k = move_history_pointer - 1; k >= 0; k--)
    if (hashdata_is_equal(move_history_hash[k], new_board_hash)
	&& (type == PSK
	    || move_history_color[k] == OTHER_COLOR(color)))
      return 1;

  return 0;
}

/* Returns 1 if at least one string is captured when color plays at pos.
 */
int
does_capture_something(int pos, int color)
{
  int other = OTHER_COLOR(color);

  ASSERT1(board[pos] == EMPTY, pos);

  if (board[SOUTH(pos)] == other && LIBERTIES(SOUTH(pos)) == 1)
    return 1;
  
  if (board[WEST(pos)] == other && LIBERTIES(WEST(pos)) == 1)
    return 1;
  
  if (board[NORTH(pos)] == other && LIBERTIES(NORTH(pos)) == 1)
    return 1;
  
  if (board[EAST(pos)] == other && LIBERTIES(EAST(pos)) == 1)
    return 1;

  return 0;
}


/* For each stone in the string at pos, set mx to value mark. */
void
mark_string(int str, signed char mx[BOARDMAX], signed char mark)
{
  int pos = str;

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

  do {
    mx[pos] = mark;
    pos = NEXT_STONE(pos);
  } while (pos != str);
}


/* Returns true if at least one move has been played at pos
 * at deeper than level 'cutoff' in the reading tree.
 */
int
move_in_stack(int pos, int cutoff)
{
  int k;
  for (k = cutoff; k < stackp; k++)
    if (stack[k] == pos)
      return 1;
  
  return 0;
}


/* Retrieve a move from the move stack. */
void
get_move_from_stack(int k, int *move, int *color)
{
  gg_assert(k < stackp);
  *move = stack[k];
  *color = move_color[k];
}

/* Return the number of stones of the indicated color(s) on the board.
 * This only counts stones in the permanent position, not stones placed
 * by trymove() or tryko(). Use stones_on_board(BLACK | WHITE) to get
 * the total number of stones on the board.
 *
 * FIXME: This seems wrong, it uses the modified board, not the permanent
 * one. /ab
 */
int
stones_on_board(int color)
{
  static int stone_count_for_position = -1;
  static int white_stones = 0;
  static int black_stones = 0;

  gg_assert(stackp == 0);

  if (stone_count_for_position != position_number) {
    int pos;
    white_stones = 0;
    black_stones = 0;
    for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
      if (board[pos] == WHITE)
	white_stones++;
      else if (board[pos] == BLACK)
	black_stones++;
    }
    
    stone_count_for_position = position_number;
  }

  return ((color & BLACK ? black_stones : 0) +
	  (color & WHITE ? white_stones : 0));
}


/* ===================== Statistics  ============================= */


/* Clear statistics. */
void
reset_trymove_counter()
{
  trymove_counter = 0;
}


/* Retrieve statistics. */
int
get_trymove_counter()
{
  return trymove_counter;
}


/* ================================================================ */
/*                      Lower level functions                       */
/* ================================================================ */


/* This function should be called if the board is modified by other
 * means than do_play_move() or undo_trymove().
 *
 * We have reached a new position. Increase the position counter and
 * re-initialize the incremental strings.
 *
 * Set up incremental board structures and populate them with the
 * strings available in the position given by board[]. Clear the stacks
 * and start the mark numbers from zero. All undo information is lost
 * by calling this function.
 */

static void
new_position(void)
{
  int pos;
  int s;

  position_number++;
  next_string = 0;
  liberty_mark = 0;
  string_mark = 0;
  CLEAR_STACKS();

  memset(string, 0, sizeof(string));
  memset(string_libs, 0, sizeof(string_libs));
  memset(string_neighbors, 0, sizeof(string_neighbors));
  memset(ml, 0, sizeof(ml));
  VALGRIND_MAKE_WRITABLE(next_stone, sizeof(next_stone));

  /* propagate_string relies on non-assigned stones to have
   * string_number -1.
   */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++)
    if (ON_BOARD(pos))
      string_number[pos] = -1;

  /* Find the existing strings. */
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (IS_STONE(board[pos]) && string_number[pos] == -1) {
      string_number[pos] = next_string;
      string[next_string].size = propagate_string(pos, pos);
      string[next_string].color = board[pos];
      string[next_string].origin = pos;
      string[next_string].mark = 0;
      next_string++;
      PARANOID1(next_string < MAX_STRINGS, pos);
    }
  }
  
  /* Fill in liberty and neighbor info. */
  for (s = 0; s < next_string; s++) {
    find_liberties_and_neighbors(s);
  }
}


#if 0

/*
 * Debug function. Dump all string information.
 */

static void
dump_incremental_board(void)
{
  int pos;
  int s;
  int i;
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (!ON_BOARD(pos))
      continue;
    if (board[pos] == EMPTY)
      fprintf(stderr, " . ");
    else
      fprintf(stderr, "%2d ", string_number[pos]);
    fprintf(stderr, "\n");
  }

  for (s = 0; s < next_string; s++) {
    if (board[string[s].origin] == EMPTY)
      continue;
    
    gprintf("%o%d %s %1m size %d, %d liberties, %d neighbors\n", s,
	    color_to_string(string[s].color),
	    string[s].origin, string[s].size,
	    string[s].liberties, string[s].neighbors);
    gprintf("%ostones:");

    pos = FIRST_STONE(s);
    do {
      gprintf("%o %1m", pos);
      pos = NEXT_STONE(pos);
    } while (!BACK_TO_FIRST_STONE(s, pos));
    
    gprintf("%o\nliberties:");
    for (i = 0; i < string[s].liberties; i++)
      gprintf("%o %1m", string[s].libs[i]);
    
    gprintf("%o\nneighbors:");
    for (i = 0; i < string[s].neighbors; i++)
      gprintf("%o %d(%1m)", string[s].neighborlist[i],
	      string[string[s].neighborlist[i]].origin);
    gprintf("%o\n\n");
  }
}
#endif


/* Build a string and its cyclic list representation from scratch.
 * propagate_string(stone, str) adds the stone (stone) to the string
 * (str) and recursively continues with not already included friendly
 * neighbors. To start a new string at (stone), use
 * propagate_string(stone, stone). The size of the string is returned.
 */

static int
propagate_string(int stone, int str)
{
  int size = 1;
  int k;
  
  if (stone == str) {
    /* Start a new string. */
    next_stone[stone] = stone;
  }
  else {
    /* Link the stone at (stone) to the string including (str) */
    string_number[stone] = string_number[str];
    next_stone[stone] = next_stone[str];
    next_stone[str] = stone;
  }

  /* Look in all four directions for more stones to add. */
  for (k = 0; k < 4; k++) {
    int d = delta[k];
    if (ON_BOARD(stone + d)
	&& board[stone + d] == board[stone]
	&& string_number[stone + d] == -1)
      size += propagate_string(stone + d, str);
  }
  
  return size;
}


/* Build the lists of liberties and neighbors of a string from
 * scratch. No information is pushed onto the stack by this function.
 */

static void
find_liberties_and_neighbors(int s)
{
  int pos;
  int other = OTHER_COLOR(string[s].color);

  /* Clear the marks. */
  liberty_mark++;
  string_mark++;

  /* Traverse the stones of the string, by following the cyclic chain. */
  pos = FIRST_STONE(s);
  do {
    /* Look in each direction for new liberties or new neighbors. Mark
     * already visited liberties and neighbors.
     */
    if (UNMARKED_LIBERTY(SOUTH(pos))) {
      ADD_AND_MARK_LIBERTY(s, SOUTH(pos));
    }
    else if (UNMARKED_COLOR_STRING(SOUTH(pos), other)) {
      ADD_NEIGHBOR(s, SOUTH(pos));
      MARK_STRING(SOUTH(pos));
    }
    
    if (UNMARKED_LIBERTY(WEST(pos))) {
      ADD_AND_MARK_LIBERTY(s, WEST(pos));
    }
    else if (UNMARKED_COLOR_STRING(WEST(pos), other)) {
      ADD_NEIGHBOR(s, WEST(pos));
      MARK_STRING(WEST(pos));
    }
    
    if (UNMARKED_LIBERTY(NORTH(pos))) {
      ADD_AND_MARK_LIBERTY(s, NORTH(pos));
    }
    else if (UNMARKED_COLOR_STRING(NORTH(pos), other)) {
      ADD_NEIGHBOR(s, NORTH(pos));
      MARK_STRING(NORTH(pos));
    }
    
    if (UNMARKED_LIBERTY(EAST(pos))) {
      ADD_AND_MARK_LIBERTY(s, EAST(pos));
    }
    else if (UNMARKED_COLOR_STRING(EAST(pos), other)) {
      ADD_NEIGHBOR(s, EAST(pos));
      MARK_STRING(EAST(pos));
    }
    
    pos = NEXT_STONE(pos);
  } while (!BACK_TO_FIRST_STONE(s, pos));
}


/* Update the liberties of a string from scratch, first pushing the
 * old information.
 */

static void
update_liberties(int s)
{
  int pos;
  int k;

  /* Push the old information. */
  PUSH_VALUE(string[s].liberties);
  for (k = 0; k < string[s].liberties && k < MAX_LIBERTIES; k++) {
    PUSH_VALUE(string_libs[s].list[k]);
  }
  string[s].liberties = 0;

  /* Clear the liberty mark. */
  liberty_mark++;

  /* Traverse the stones of the string, by following the cyclic chain. */
  pos = FIRST_STONE(s);
  do {
    /* Look in each direction for new liberties. Mark already visited
     * liberties. 
     */
    if (UNMARKED_LIBERTY(SOUTH(pos))) {
      ADD_AND_MARK_LIBERTY(s, SOUTH(pos));
    }
    
    if (UNMARKED_LIBERTY(WEST(pos))) {
      ADD_AND_MARK_LIBERTY(s, WEST(pos));
    }
    
    if (UNMARKED_LIBERTY(NORTH(pos))) {
      ADD_AND_MARK_LIBERTY(s, NORTH(pos));
    }
    
    if (UNMARKED_LIBERTY(EAST(pos))) {
      ADD_AND_MARK_LIBERTY(s, EAST(pos));
    }
    
    pos = NEXT_STONE(pos);
  } while (!BACK_TO_FIRST_STONE(s, pos));
}


/* Remove a string from the list of neighbors and push the changed
 * information.
 */

static void
remove_neighbor(int str_number, int n)
{
  int k;
  int done = 0;
  struct string_data *s = &string[str_number];
  struct string_neighbors_data *sn = &string_neighbors[str_number];
  for (k = 0; k < s->neighbors; k++)
    if (sn->list[k] == n) {
      /* We need to push the last entry too because it may become
       * destroyed later.
       */
      PUSH_VALUE(sn->list[s->neighbors - 1]);
      PUSH_VALUE(sn->list[k]);
      PUSH_VALUE(s->neighbors);
      sn->list[k] = sn->list[s->neighbors - 1];
      s->neighbors--;
      done = 1;
      break;
    }
  gg_assert(done);
}


/* Remove one liberty from the list of liberties, pushing changed
 * information. If the string had more liberties than the size of the
 * list, rebuild the list from scratch.
 */

static void
remove_liberty(int str_number, int pos)
{
  int k;
  struct string_data *s = &string[str_number];
  struct string_liberties_data *sl = &string_libs[str_number];
  
  if (s->liberties > MAX_LIBERTIES)
    update_liberties(str_number);
  else {
    for (k = 0; k < s->liberties; k++)
      if (sl->list[k] == pos) {
	/* We need to push the last entry too because it may become
	 * destroyed later.
	 */
	PUSH_VALUE(sl->list[s->liberties - 1]);
	PUSH_VALUE(sl->list[k]);
	PUSH_VALUE(s->liberties);
	sl->list[k] = sl->list[s->liberties - 1];
	s->liberties--;
	break;
      }
  }
}


/* Remove a string from the board, pushing necessary information to
 * restore it. Return the number of removed stones.
 */

static int
do_remove_string(int s)
{
  int pos;
  int k;
  int size = string[s].size;

  /* Traverse the stones of the string, by following the cyclic chain. */
  pos = FIRST_STONE(s);
  do {
    /* Push color, string number and cyclic chain pointers. */
    PUSH_VALUE(string_number[pos]);
    PUSH_VALUE(next_stone[pos]);
    DO_REMOVE_STONE(pos);
    pos = NEXT_STONE(pos);
  } while (!BACK_TO_FIRST_STONE(s, pos));

  /* The neighboring strings have obtained some new liberties and lost
   * a neighbor.  For speed reasons we handle two most common cases
   * when string size is 1 or 2 stones here instead of calling
   * update_liberties().
   */
  if (size == 1) {
    for (k = 0; k < string[s].neighbors; k++) {
      int neighbor = string_neighbors[s].list[k];

      remove_neighbor(neighbor, s);
      PUSH_VALUE(string[neighbor].liberties);

      if (string[neighbor].liberties < MAX_LIBERTIES)
	string_libs[neighbor].list[string[neighbor].liberties] = pos;
      string[neighbor].liberties++;
    }
  }
  else if (size == 2) {
    int other = OTHER_COLOR(string[s].color);
    int pos2 = NEXT_STONE(pos);

    for (k = 0; k < string[s].neighbors; k++) {
      int neighbor = string_neighbors[s].list[k];      

      remove_neighbor(neighbor, s);
      PUSH_VALUE(string[neighbor].liberties);

      if (NEIGHBOR_OF_STRING(pos, neighbor, other)) {
	if (string[neighbor].liberties < MAX_LIBERTIES)
	  string_libs[neighbor].list[string[neighbor].liberties] = pos;
	string[neighbor].liberties++;
      }

      if (NEIGHBOR_OF_STRING(pos2, neighbor, other)) {
	if (string[neighbor].liberties < MAX_LIBERTIES)
	  string_libs[neighbor].list[string[neighbor].liberties] = pos2;
	string[neighbor].liberties++;
      }
    }
  }
  else {
    for (k = 0; k < string[s].neighbors; k++) {
      remove_neighbor(string_neighbors[s].list[k], s);
      update_liberties(string_neighbors[s].list[k]);
    }
  }

  /* Update the number of captured stones. These are assumed to
   * already have been pushed.
   */
  if (string[s].color == WHITE)
    white_captured += size;
  else
    black_captured += size;

  return size;
}


/* We have played an isolated new stone and need to create a new
 * string for it.
 */
static void
create_new_string(int pos)
{
  int s;
  int color = board[pos];
  int other = OTHER_COLOR(color);

  /* Get the next free string number. */
  PUSH_VALUE(next_string);
  s = next_string++;
  PARANOID1(s < MAX_STRINGS, pos);
  string_number[pos] = s;
  /* Set up a size one cycle for the string. */
  next_stone[pos] = pos;

  /* Set trivially known values and initialize the rest to zero. */
  string[s].color = color;
  string[s].size = 1;
  string[s].origin = pos;
  string[s].liberties = 0;
  string[s].neighbors = 0;
  string[s].mark = 0;

  /* Clear the string mark. */
  string_mark++;

  /* In each direction, look for a liberty or a nonmarked opponent
   * neighbor. Mark visited neighbors. There is no need to mark the
   * liberties since we can't find them twice. */
  if (LIBERTY(SOUTH(pos))) {
    ADD_LIBERTY(s, SOUTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(SOUTH(pos), other)) {
    int s2 = string_number[SOUTH(pos)];
    /* Add the neighbor to our list. */
    ADD_NEIGHBOR(s, SOUTH(pos));
    /* Add us to our neighbor's list. */
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
    MARK_STRING(SOUTH(pos));
  }
  
  if (LIBERTY(WEST(pos))) {
    ADD_LIBERTY(s, WEST(pos));
  }
  else if (UNMARKED_COLOR_STRING(WEST(pos), other)) {
    int s2 = string_number[WEST(pos)];
    /* Add the neighbor to our list. */
    ADD_NEIGHBOR(s, WEST(pos));
    /* Add us to our neighbor's list. */
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
    MARK_STRING(WEST(pos));
  }
  
  if (LIBERTY(NORTH(pos))) {
    ADD_LIBERTY(s, NORTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(NORTH(pos), other)) {
    int s2 = string_number[NORTH(pos)];
    /* Add the neighbor to our list. */
    ADD_NEIGHBOR(s, NORTH(pos));
    /* Add us to our neighbor's list. */
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
    MARK_STRING(NORTH(pos));
  }
  
  if (LIBERTY(EAST(pos))) {
    ADD_LIBERTY(s, EAST(pos));
  }
  else if (UNMARKED_COLOR_STRING(EAST(pos), other)) {
    int s2 = string_number[EAST(pos)];
    /* Add the neighbor to our list. */
    ADD_NEIGHBOR(s, EAST(pos));
    /* Add us to our neighbor's list. */
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
    /* No need to mark since no visits left. */
#if 0
    MARK_STRING(EAST(pos));
#endif
  }
}


/* We have played a stone with exactly one friendly neighbor. Add the
 * new stone to that string.
 */
static void
extend_neighbor_string(int pos, int s)
{
  int k;
  int liberties_updated = 0;
  int color = board[pos];
  int other = OTHER_COLOR(color);

  /* Link in the stone in the cyclic list. */
  int pos2 = string[s].origin;
  next_stone[pos] = next_stone[pos2];
  PUSH_VALUE(next_stone[pos2]);
  next_stone[pos2] = pos;
  
  /* Do we need to update the origin? */
  if (pos < pos2) {
    PUSH_VALUE(string[s].origin);
    string[s].origin = pos;
  }
  
  string_number[pos] = s;

  /* The size of the string has increased by one. */
  PUSH_VALUE(string[s].size);
  string[s].size++;

  /* If s has too many liberties, we don't know where they all are and
   * can't update the liberties with the algorithm we otherwise
   * use. In that case we can only recompute the liberties from
   * scratch.
   */
  if (string[s].liberties > MAX_LIBERTIES) {
    update_liberties(s);
    liberties_updated = 1;
  }
  else {
    /* The place of the new stone is no longer a liberty. */
    remove_liberty(s, pos);
  }

  /* Mark old neighbors of the string. */
  string_mark++;
  for (k = 0; k < string[s].neighbors; k++)
    string[string_neighbors[s].list[k]].mark = string_mark;

  /* Look at the neighbor locations of pos for new liberties and/or
   * neighbor strings.
   */

  /* If we find a liberty, look two steps away to determine whether
   * this already is a liberty of s.
   */
  if (LIBERTY(SOUTH(pos))) {
    if (!liberties_updated
	&& !NON_SOUTH_NEIGHBOR_OF_STRING(SOUTH(pos), s, color))
      ADD_LIBERTY(s, SOUTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(SOUTH(pos), other)) {
    int s2 = string_number[SOUTH(pos)];
    PUSH_VALUE(string[s].neighbors);
    ADD_NEIGHBOR(s, SOUTH(pos));
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
    MARK_STRING(SOUTH(pos));
  }
  
  if (LIBERTY(WEST(pos))) {
    if (!liberties_updated
	&& !NON_WEST_NEIGHBOR_OF_STRING(WEST(pos), s, color))
      ADD_LIBERTY(s, WEST(pos));
  }
  else if (UNMARKED_COLOR_STRING(WEST(pos), other)) {
    int s2 = string_number[WEST(pos)];
    PUSH_VALUE(string[s].neighbors);
    ADD_NEIGHBOR(s, WEST(pos));
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
    MARK_STRING(WEST(pos));
  }
  
  if (LIBERTY(NORTH(pos))) {
    if (!liberties_updated
	&& !NON_NORTH_NEIGHBOR_OF_STRING(NORTH(pos), s, color))
      ADD_LIBERTY(s, NORTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(NORTH(pos), other)) {
    int s2 = string_number[NORTH(pos)];
    PUSH_VALUE(string[s].neighbors);
    ADD_NEIGHBOR(s, NORTH(pos));
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
    MARK_STRING(NORTH(pos));
  }
  
  if (LIBERTY(EAST(pos))) {
    if (!liberties_updated
	&& !NON_EAST_NEIGHBOR_OF_STRING(EAST(pos), s, color))
      ADD_LIBERTY(s, EAST(pos));
  }
  else if (UNMARKED_COLOR_STRING(EAST(pos), other)) {
    int s2 = string_number[EAST(pos)];
    PUSH_VALUE(string[s].neighbors);
    ADD_NEIGHBOR(s, EAST(pos));
    PUSH_VALUE(string[s2].neighbors);
    ADD_NEIGHBOR(s2, pos);
#if 0
    MARK_STRING(EAST(pos));
#endif
  }
  
}


/* Incorporate the string at pos with the string s.
 */

static void
assimilate_string(int s, int pos)
{
  int k;
  int last;
  int s2 = string_number[pos];
  string[s].size += string[s2].size;

  /* Walk through the s2 stones and change string number. Also pick up
   * the last stone in the cycle for later use.
   */
  pos = FIRST_STONE(s2);
  do {
    PUSH_VALUE(string_number[pos]);
    string_number[pos] = s;
    last = pos;
    pos = NEXT_STONE(pos);
  } while (!BACK_TO_FIRST_STONE(s2, pos));

  /* Link the two cycles together. */
  {
    int pos2 = string[s].origin;
    PUSH_VALUE(next_stone[last]);
    PUSH_VALUE(next_stone[pos2]);
    next_stone[last] = next_stone[pos2];
    next_stone[pos2] = string[s2].origin;
    
    /* Do we need to update the origin? */
    if (string[s2].origin < pos2)
      string[s].origin = string[s2].origin;
  }

  /* Pick up the liberties of s2 that we don't already have.
   * It is assumed that the liberties of s have been marked before
   * this function is called.
   */
  if (string[s2].liberties <= MAX_LIBERTIES) {
    for (k = 0; k < string[s2].liberties; k++) {
      int pos2 = string_libs[s2].list[k];
      if (UNMARKED_LIBERTY(pos2)) {
	ADD_AND_MARK_LIBERTY(s, pos2);
      }
    }
  }
  else {
    /* If s2 had too many liberties the above strategy wouldn't be
     * effective, since not all liberties are listed in
     * libs[] the chain of stones for s2 is no
     * longer available (it has already been merged with s) so we
     * can't reconstruct the s2 liberties. Instead we capitulate and
     * rebuild the list of liberties for s (including the neighbor
     * strings assimilated so far) from scratch.
     */
    liberty_mark++;          /* Reset the mark. */
    string[s].liberties = 0; /* To avoid pushing the current list. */
    update_liberties(s);
  }

  /* Remove s2 as neighbor to the neighbors of s2 and instead add s if
   * they don't already have added it. Also add the neighbors of s2 as
   * neighbors of s, unless they already have been added. The already
   * known neighbors of s are assumed to have been marked before this
   * function is called.
   */
  for (k = 0; k < string[s2].neighbors; k++) {
    int t = string_neighbors[s2].list[k];
    remove_neighbor(t, s2);
    if (string[t].mark != string_mark) {
      PUSH_VALUE(string[t].neighbors);
      string_neighbors[t].list[string[t].neighbors++] = s;
      string_neighbors[s].list[string[s].neighbors++] = t;
      string[t].mark = string_mark;
    }
  }
}


/* Create a new string for the stone at pos and assimilate all
 * friendly neighbor strings.
 */

static void
assimilate_neighbor_strings(int pos)
{
  int s;
  int color = board[pos];
  int other = OTHER_COLOR(color);

  /* Get the next free string number. */
  PUSH_VALUE(next_string);
  s = next_string++;
  PARANOID1(s < MAX_STRINGS, pos); 
  string_number[pos] = s;
  /* Set up a size one cycle for the string. */
  next_stone[pos] = pos;
  
  /* Set trivially known values and initialize the rest to zero. */
  string[s].color = color;
  string[s].size = 1;
  string[s].origin = pos;
  string[s].liberties = 0;
  string[s].neighbors = 0;

  /* Clear the marks. */
  liberty_mark++;
  string_mark++;

  /* Mark ourselves. */
  string[s].mark = string_mark;

  /* Look in each direction for
   *
   * 1. liberty: Add if not already visited.
   * 2. opponent string: Add it among our neighbors and us among its
   *    neighbors, unless already visited.
   * 3. friendly string: Assimilate.
   */
  if (UNMARKED_LIBERTY(SOUTH(pos))) {
    ADD_AND_MARK_LIBERTY(s, SOUTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(SOUTH(pos), other)) {
    ADD_NEIGHBOR(s, SOUTH(pos));
    PUSH_VALUE(string[string_number[SOUTH(pos)]].neighbors);
    ADD_NEIGHBOR(string_number[SOUTH(pos)], pos);
    MARK_STRING(SOUTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(SOUTH(pos), color)) {
    assimilate_string(s, SOUTH(pos));
  }

  if (UNMARKED_LIBERTY(WEST(pos))) {
    ADD_AND_MARK_LIBERTY(s, WEST(pos));
  }
  else if (UNMARKED_COLOR_STRING(WEST(pos), other)) {
    ADD_NEIGHBOR(s, WEST(pos));
    PUSH_VALUE(string[string_number[WEST(pos)]].neighbors);
    ADD_NEIGHBOR(string_number[WEST(pos)], pos);
    MARK_STRING(WEST(pos));
  }
  else if (UNMARKED_COLOR_STRING(WEST(pos), color)) {
    assimilate_string(s, WEST(pos));
  }
  
  if (UNMARKED_LIBERTY(NORTH(pos))) {
    ADD_AND_MARK_LIBERTY(s, NORTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(NORTH(pos), other)) {
    ADD_NEIGHBOR(s, NORTH(pos));
    PUSH_VALUE(string[string_number[NORTH(pos)]].neighbors);
    ADD_NEIGHBOR(string_number[NORTH(pos)], pos);
    MARK_STRING(NORTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(NORTH(pos), color)) {
    assimilate_string(s, NORTH(pos));
  }
  
  if (UNMARKED_LIBERTY(EAST(pos))) {
#if 0
    ADD_AND_MARK_LIBERTY(s, EAST(pos));
#else
    ADD_LIBERTY(s, EAST(pos));
#endif
  }
  else if (UNMARKED_COLOR_STRING(EAST(pos), other)) {
    ADD_NEIGHBOR(s, EAST(pos));
    PUSH_VALUE(string[string_number[EAST(pos)]].neighbors);
    ADD_NEIGHBOR(string_number[EAST(pos)], pos);
#if 0
    MARK_STRING(EAST(pos));
#endif
  }
  else if (UNMARKED_COLOR_STRING(EAST(pos), color)) {
    assimilate_string(s, EAST(pos));
  }
}


/* Suicide at `pos' (the function assumes that the move is indeed suicidal).
 * Remove the neighboring friendly strings.
 */

static void
do_commit_suicide(int pos, int color)
{
  if (board[SOUTH(pos)] == color)
    do_remove_string(string_number[SOUTH(pos)]);

  if (board[WEST(pos)] == color)
    do_remove_string(string_number[WEST(pos)]);

  if (board[NORTH(pos)] == color)
    do_remove_string(string_number[NORTH(pos)]);

  if (board[EAST(pos)] == color)
    do_remove_string(string_number[EAST(pos)]);

  /* Count the stone we "played" as captured. */
  if (color == WHITE)
    white_captured++;
  else
    black_captured++;
}


/* Play a move without legality checking. This is a low-level function,
 * it assumes that the move is not a suicide. Such cases must be handled
 * where the function is called.
 */

static void
do_play_move(int pos, int color)
{
  int other = OTHER_COLOR(color);
  int captured_stones = 0;
  int neighbor_allies = 0;
  int s = -1;

  /* Clear string mark. */
  string_mark++;

  /* Put down the stone.  We also set its string number to -1 for a while
   * so that NEIGHBOR_OF_STRING() and friends don't get confused with the
   * stone.
   */
  DO_ADD_STONE(pos, color);
  string_number[pos] = -1;

  /* Look in all directions. Count the number of neighbor strings of the same
   * color, remove captured strings and remove `pos' as liberty for opponent
   * strings that are not captured.
   */
  if (board[SOUTH(pos)] == color) {
    neighbor_allies++;
    s = string_number[SOUTH(pos)];
    MARK_STRING(SOUTH(pos));
  }
  else if (board[SOUTH(pos)] == other) {
    if (LIBERTIES(SOUTH(pos)) > 1) {
      remove_liberty(string_number[SOUTH(pos)], pos);
      MARK_STRING(SOUTH(pos));
    }
    else
      captured_stones += do_remove_string(string_number[SOUTH(pos)]);
  }

  if (UNMARKED_COLOR_STRING(WEST(pos), color)) {
    neighbor_allies++;
    s = string_number[WEST(pos)];
    MARK_STRING(WEST(pos));
  }
  else if (UNMARKED_COLOR_STRING(WEST(pos), other)) {
    if (LIBERTIES(WEST(pos)) > 1) {
      remove_liberty(string_number[WEST(pos)], pos);
      MARK_STRING(WEST(pos));
    }
    else
      captured_stones += do_remove_string(string_number[WEST(pos)]);
  }

  if (UNMARKED_COLOR_STRING(NORTH(pos), color)) {
    neighbor_allies++;
    s = string_number[NORTH(pos)];
    MARK_STRING(NORTH(pos));
  }
  else if (UNMARKED_COLOR_STRING(NORTH(pos), other)) {
    if (LIBERTIES(NORTH(pos)) > 1) {
      remove_liberty(string_number[NORTH(pos)], pos);
      MARK_STRING(NORTH(pos));
    }
    else
      captured_stones += do_remove_string(string_number[NORTH(pos)]);
  }

  if (UNMARKED_COLOR_STRING(EAST(pos), color)) {
    neighbor_allies++;
    s = string_number[EAST(pos)];
#if 0
    MARK_STRING(EAST(pos));
#endif
  }
  else if (UNMARKED_COLOR_STRING(EAST(pos), other)) {
    if (LIBERTIES(EAST(pos)) > 1) {
      remove_liberty(string_number[EAST(pos)], pos);
#if 0
      MARK_STRING(EAST(pos));
#endif
    }
    else
      captured_stones += do_remove_string(string_number[EAST(pos)]);
  }

  /* Choose strategy depending on the number of friendly neighbors. */
  if (neighbor_allies == 0)
    create_new_string(pos);
  else if (neighbor_allies == 1) {
    gg_assert(s >= 0);
    extend_neighbor_string(pos, s);
    return; /* can't be a ko, we're done */
  }
  else {
    assimilate_neighbor_strings(pos);
    return; /* can't be a ko, we're done */
  }

  /* Check whether this move was a ko capture and if so set 
   * board_ko_pos.
   *
   * No need to push board_ko_pos on the stack, 
   * because this has been done earlier.
   */
  s = string_number[pos];
  if (string[s].liberties == 1
      && string[s].size == 1
      && captured_stones == 1) {
    /* In case of a double ko: clear old ko position first. */
    if (board_ko_pos != NO_MOVE)
      hashdata_invert_ko(&board_hash, board_ko_pos);
    board_ko_pos = string_libs[s].list[0];
    hashdata_invert_ko(&board_hash, board_ko_pos);
  }
}



/* ================================================================ *
 * The following functions don't actually belong here. They are  
 * only here because they are faster here where they have access to
 * the incremental data structures. 
 * ================================================================ */


/* Help collect the data needed by order_moves() in reading.c.
 * It's the caller's responsibility to initialize the result parameters.
 */
#define NO_UNROLL 0
void
incremental_order_moves(int move, int color, int str,
			int *number_edges, int *number_same_string,
			int *number_own, int *number_opponent,
			int *captured_stones, int *threatened_stones,
			int *saved_stones, int *number_open)
{
#if NO_UNROLL == 1
  int pos;
  int k;

  /* Clear the string mark. */
  string_mark++;

  for (k = 0; k < 4; k++) {
    pos = move + delta[k];
    if (!ON_BOARD(pos))
      (*number_edges)++;
    else if (board[pos] == EMPTY)
      (*number_open)++;
    else {
      int s = string_number[pos];
      if (string_number[str] == s)
	(*number_same_string)++;
      
      if (board[pos] == color) {
	(*number_own)++;
	if (string[s].liberties == 1)
	  (*saved_stones) += string[s].size;
      }
      else {
	(*number_opponent)++;
	if (string[s].liberties == 1) {
	  int r;
	  struct string_data *t;
	  (*captured_stones) += string[s].size;
	  for (r = 0; r < string[s].neighbors; r++) {
	    t = &string[string[s].neighborlist[r]];
	    if (t->liberties == 1)
	      (*saved_stones) += t->size;
	  }
	}
	else if (string[s].liberties == 2 && UNMARKED_STRING(pos)) {
	  (*threatened_stones) += string[s].size;
	  MARK_STRING(pos);
	}
      }
    }
  }
  
#else
#define code1(arg) \
  if (!ON_BOARD(arg)) \
    (*number_edges)++; \
  else if (board[arg] == EMPTY) \
    (*number_open)++; \
  else { \
    int s = string_number[arg]; \
    if (string_number[str] == s) \
      (*number_same_string)++; \
    if (board[arg] == color) { \
      (*number_own)++; \
      if (string[s].liberties == 1) \
	(*saved_stones) += string[s].size; \
    } \
    else { \
      (*number_opponent)++; \
      if (string[s].liberties == 1) { \
	int r; \
	struct string_data *t; \
	(*captured_stones) += string[s].size; \
	for (r = 0; r < string[s].neighbors; r++) { \
	  t = &string[string_neighbors[s].list[r]]; \
	  if (t->liberties == 1) \
	    (*saved_stones) += t->size; \
	} \
      } \
      else if (string[s].liberties == 2 && UNMARKED_STRING(arg)) { \
	(*threatened_stones) += string[s].size; \
        MARK_STRING(arg); \
      } \
    } \
  }

  /* Clear the string mark. */
  string_mark++;

  code1(SOUTH(move));
  code1(WEST(move));
  code1(NORTH(move));
  code1(EAST(move));
#endif
}


int 
square_dist(int pos1, int pos2)
{
  int idist = I(pos1) - I(pos2);
  int jdist = J(pos1) - J(pos2);
  return idist*idist + jdist*jdist;
}


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