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

#include "gnugo.h"

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

#include "liberty.h"
#include "gg_utils.h"
#include "random.h"
#include "move_reasons.h"


/* All these data structures are declared in move_reasons.h */

struct move_data move[BOARDMAX];
struct move_reason move_reasons[MAX_MOVE_REASONS];
int next_reason;

/* Connections */
int conn_worm1[MAX_CONNECTIONS];
int conn_worm2[MAX_CONNECTIONS];
int next_connection;

/* Potential semeai moves. */
int semeai_target1[MAX_POTENTIAL_SEMEAI];
int semeai_target2[MAX_POTENTIAL_SEMEAI];
static int next_semeai;

/* Unordered sets (currently pairs) of move reasons / targets */
Reason_set either_data[MAX_EITHER];
int next_either;
Reason_set all_data[MAX_ALL];
int next_all;

/* Eye shapes */
int eyes[MAX_EYES];
int eyecolor[MAX_EYES];
int next_eye;

/* Lunches */
int lunch_dragon[MAX_LUNCHES]; /* eater */
int lunch_worm[MAX_LUNCHES];   /* food */
int next_lunch;

/* Point redistribution */
int replacement_map[BOARDMAX];

/* The color for which we are evaluating moves. */
int current_color;

/* Attack threats that are known to be sente locally. */
static int known_good_attack_threats[BOARDMAX][MAX_ATTACK_THREATS];

/* Moves that are known to be safe (in the sense that played stones can
 * be captured, but opponent loses much more when attempting to do so)
 */
static int known_safe_moves[BOARDMAX];

/* Helper functions to check conditions in discard rules. */
typedef int (*discard_condition_fn_ptr)(int pos, int what);

struct discard_rule {
  int reason_type[MAX_REASONS];
  discard_condition_fn_ptr condition;
  int flags;
  char trace_message[MAX_TRACE_LENGTH];
};


/* Initialize move reason data structures. */
void
clear_move_reasons(void)
{
  int pos;
  int k;
  next_reason = 0;
  next_connection = 0;
  next_semeai = 0;
  next_either = 0;
  next_all = 0;
  next_eye = 0;
  next_lunch = 0;
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (ON_BOARD(pos)) {
      move[pos].value                    = 0.0;
      move[pos].final_value              = 0.0;
      move[pos].additional_ko_value      = 0.0;
      move[pos].territorial_value        = 0.0;
      move[pos].strategical_value        = 0.0;
      move[pos].maxpos_shape             = 0.0;
      move[pos].numpos_shape             = 0;
      move[pos].maxneg_shape             = 0.0;
      move[pos].numneg_shape             = 0;
      move[pos].followup_value           = 0.0;
      move[pos].influence_followup_value = 0.0;
      move[pos].reverse_followup_value   = 0.0;
      move[pos].secondary_value          = 0.0;
      move[pos].min_value                = 0.0;
      move[pos].max_value                = HUGE_MOVE_VALUE;
      move[pos].min_territory            = 0.0;
      move[pos].max_territory            = HUGE_MOVE_VALUE;
      for (k = 0; k < MAX_REASONS; k++)     
	move[pos].reason[k]              = -1;
      move[pos].move_safety              = 0;
      move[pos].worthwhile_threat        = 0;
      move[pos].randomness_scaling       = 1.0;
      /* The reason we assign a random number to each move immediately
       * is to avoid dependence on which moves are evaluated when it
       * comes to choosing between multiple moves of the same value.
       * In this way we can get consistent results for use in the
       * regression tests.
       */
      move[pos].random_number            = gg_drand();

      /* Do not send away the points (yet). */
      replacement_map[pos] = NO_MOVE;
    }
  }

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    known_safe_moves[pos] = 0;
    for (k = 0; k < MAX_ATTACK_THREATS; k++)
      known_good_attack_threats[pos][k] = NO_MOVE;
  }
}


/*
 * Find the index of a connection in the list of connections.
 * If necessary, add a new entry.
 */
int
find_connection(int worm1, int worm2)
{
  int k;
  
  if (worm1 > worm2) {
    /* Swap to canonical order. */
    int tmp = worm1;
    worm1 = worm2;
    worm2 = tmp;
  }
  
  for (k = 0; k < next_connection; k++)
    if (conn_worm1[k] == worm1 && conn_worm2[k] == worm2)
      return k;
  
  /* Add a new entry. */
  gg_assert(next_connection < MAX_CONNECTIONS);
  conn_worm1[next_connection] = worm1;
  conn_worm2[next_connection] = worm2;
  next_connection++;
  return next_connection - 1;
}


static int
find_either_data(int reason1, int what1, int reason2, int what2)
{
  int k;
  
  /* Make sure the worms are ordered canonically. */
  if (what1 > what2) {
    int tmp = what1;
    what1 = what2;
    what2 = tmp;
  }

  for (k = 0; k < next_either; k++)
    if (either_data[k].reason1    == reason1
	&& either_data[k].what1   == what1
	&& either_data[k].reason2 == reason2
	&& either_data[k].what2   == what2)
      return k;
  
  /* Add a new entry. */
  gg_assert(next_either < MAX_EITHER);
  either_data[next_either].reason1 = reason1;
  either_data[next_either].what1   = what1;
  either_data[next_either].reason2 = reason2;
  either_data[next_either].what2   = what2;
  next_either++;
  return next_either - 1;
}

static int
find_all_data(int reason1, int what1, int reason2, int what2)
{
  int k;
  
  /* Make sure the worms are ordered canonically. */
  if (what1 > what2) {
    int tmp = what1;
    what1 = what2;
    what2 = tmp;
  }

  for (k = 0; k < next_all; k++)
    if (all_data[k].reason1    == reason1
	&& all_data[k].what1   == what1
	&& all_data[k].reason2 == reason2
	&& all_data[k].what2   == what2)
      return k;
  
  /* Add a new entry. */
  gg_assert(next_all < MAX_ALL);
  all_data[next_all].reason1 = reason1;
  all_data[next_all].what1   = what1;
  all_data[next_all].reason2 = reason2;
  all_data[next_all].what2   = what2;
  next_all++;
  return next_all - 1;
}

static int
find_pair_data(int what1, int what2)
{
  int k;
  
  for (k = 0; k < next_either; k++)
    if (either_data[k].what1 == what1
	&& either_data[k].what2 == what2)
      return k;
  
  /* Add a new entry. */
  gg_assert(next_either < MAX_EITHER);
  either_data[next_either].what1   = what1;
  either_data[next_either].what2   = what2;
  next_either++;
  return next_either - 1;
}


/* Interprets the object of a reason and returns its position.
 * If the object is a pair (of worms or dragons), the position of the first
 * object is returned. (This is only used for trace outputs.) Returns
 * NO_MOVE if move does not point to a location.
 * FIXME: This new function produces some code duplication with other
 * trace output function. Do some code cleanup here.
 */
static int
get_pos(int reason, int what)
{
  switch (reason) {
  case ATTACK_MOVE:
  case DEFEND_MOVE:
  case ATTACK_THREAT:
  case DEFEND_THREAT:
  case ATTACK_MOVE_GOOD_KO:
  case ATTACK_MOVE_BAD_KO:
  case DEFEND_MOVE_GOOD_KO:
  case DEFEND_MOVE_BAD_KO:
    return what;

  case SEMEAI_MOVE:
  case SEMEAI_THREAT:
  case STRATEGIC_ATTACK_MOVE:
  case STRATEGIC_DEFEND_MOVE:
  case OWL_ATTACK_MOVE:
  case OWL_DEFEND_MOVE:
  case OWL_ATTACK_THREAT:
  case OWL_DEFEND_THREAT:
  case OWL_PREVENT_THREAT:
  case UNCERTAIN_OWL_ATTACK:
  case UNCERTAIN_OWL_DEFENSE:
  case OWL_ATTACK_MOVE_GOOD_KO:
  case OWL_ATTACK_MOVE_BAD_KO:
  case OWL_DEFEND_MOVE_GOOD_KO:
  case OWL_DEFEND_MOVE_BAD_KO:
    return what;

  case EITHER_MOVE:
    /* FIXME: What should we return here? */
    return either_data[what].what1;

  case ALL_MOVE:
    /* FIXME: What should we return here? */
    return all_data[what].what1;

  case CONNECT_MOVE:
  case CUT_MOVE:
    return conn_worm1[what];

  case ANTISUJI_MOVE:
  case EXPAND_TERRITORY_MOVE:
  case EXPAND_MOYO_MOVE:
  case INVASION_MOVE:
  case MY_ATARI_ATARI_MOVE:
  case YOUR_ATARI_ATARI_MOVE:
    return NO_MOVE;

  case OWL_ATTACK_MOVE_GAIN:
  case OWL_DEFEND_MOVE_LOSS:
    /* FIXME: What should we return here? */
    return either_data[what].what1;

  default:
    /* We should never get here: */
    gg_assert(0);
    return 0; /* To keep gcc happy. */
  }
}

/*
 * See if a lunch is already in the list of lunches, otherwise add a new
 * entry. A lunch is in this context a pair of eater (a dragon) and food
 * (a worm).
 */
void
add_lunch(int eater, int food)
{
  int k;
  int dragon1 = dragon[eater].origin;
  int worm1   = worm[food].origin;
  ASSERT_ON_BOARD1(eater);
  ASSERT_ON_BOARD1(food);
  
  for (k = 0; k < next_lunch; k++)
    if ((lunch_dragon[k] == dragon1) && (lunch_worm[k] == worm1))
      return;
  
  /* Add a new entry. */
  gg_assert(next_lunch < MAX_LUNCHES);
  lunch_dragon[next_lunch] = dragon1;
  lunch_worm[next_lunch] = worm1;
  next_lunch++;
  return;
}

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


/*
 * Add a move reason for (pos) if it's not already there or the
 * table is full.
 */ 
static void
add_move_reason(int pos, int type, int what)
{
  int k;

  ASSERT_ON_BOARD1(pos);
  if (stackp == 0) {
    ASSERT1(board[pos] == EMPTY, pos);
  }

  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    if (r < 0)
      break;
    if (move_reasons[r].type == type
	&& move_reasons[r].what == what)
      return;  /* Reason already listed. */
  }

  /* Reason not found, add it if there is place left in both lists.
   * Otherwise drop it.
   */
  if (k >= MAX_REASONS) {
    DEBUG(DEBUG_MOVE_REASONS,
	  "Move reason at %1m (type=%d, what=%d) dropped because list full.\n",
	  pos, type, what);
    return;
  }

  if (next_reason >= MAX_MOVE_REASONS) {
    DEBUG(DEBUG_MOVE_REASONS,
	  "Move reason at %1m (type=%d, what=%d) dropped because global list full.\n",
	  pos, type, what);
    return;
  }

  /* Add a new entry. */
  move[pos].reason[k] = next_reason;
  move_reasons[next_reason].type = type;
  move_reasons[next_reason].what = what;
  move_reasons[next_reason].status = ACTIVE;
  next_reason++;
}

/*
 * Remove a move reason for (pos). Ignore silently if the reason
 * wasn't there.
 */ 
static void
remove_move_reason(int pos, int type, int what)
{
  int k;
  int n = -1; /* Position of the move reason to be deleted. */

  ASSERT_ON_BOARD1(pos);
  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    if (r < 0)
      break;
    if (move_reasons[r].type == type
	&& move_reasons[r].what == what)
      n = k;
  }
  
  if (n == -1)
    return; /* Move reason wasn't there. */
  
  /* Now move the last move reason to position n, thereby removing the
   * one we were looking for.
   */
  k--;
  move[pos].reason[n] = move[pos].reason[k];
  move[pos].reason[k] = -1;
}


/*
 * Check whether a move reason already is recorded for a move.
 * A negative value for 'what' means only match 'type'.
 */
int
move_reason_known(int pos, int type, int what)
{
  int k;
  int r;

  ASSERT_ON_BOARD1(pos);
  for (k = 0; k < MAX_REASONS; k++) {
    r = move[pos].reason[k];
    if (r < 0)
      break;
    if (move_reasons[r].type == type
	&& (what < 0
	    || move_reasons[r].what == what))
      return 1;
  }
  return 0;
}

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

/* Functions used in discard_rules follow below. */

/*
 * Check whether an attack move reason already is recorded for a move.
 * A negative value for 'what' means only match 'type'.
 */
int
attack_move_reason_known(int pos, int what)
{
  ASSERT1(what < 0 || IS_STONE(board[what]), what);
  what = worm[what].origin;
  if (move_reason_known(pos, ATTACK_MOVE, what))
    return WIN;
  if (move_reason_known(pos, ATTACK_MOVE_GOOD_KO, what))
    return KO_A;
  if (move_reason_known(pos, ATTACK_MOVE_BAD_KO, what))
    return KO_B;
  return 0;
}

/*
 * Check whether a defense move reason already is recorded for a move.
 * A negative value for 'what' means only match 'type'.
 */
int
defense_move_reason_known(int pos, int what)
{
  ASSERT1(what < 0 || IS_STONE(board[what]), what);
  what = worm[what].origin;
  if (move_reason_known(pos, DEFEND_MOVE, what))
    return WIN;
  if (move_reason_known(pos, DEFEND_MOVE_GOOD_KO, what))
    return KO_A;
  if (move_reason_known(pos, DEFEND_MOVE_BAD_KO, what))
    return KO_B;
  return 0;
}

/* Check whether a dragon consists of only one worm. If so, check
 * whether we know of a tactical attack or defense move.
 */
static int
tactical_move_vs_whole_dragon_known(int pos, int what)
{
  return ((worm[what].size == dragon[what].size)
	  && (attack_move_reason_known(pos, what)
	      || defense_move_reason_known(pos, what)));
}

/*
 * Check whether an owl attack move reason already is recorded for a move.
 * A negative value for 'what' means only match 'type'.
 */
int
owl_attack_move_reason_known(int pos, int what)
{
  if (move_reason_known(pos, OWL_ATTACK_MOVE, what))
    return WIN;
  if (move_reason_known(pos, OWL_ATTACK_MOVE_GOOD_KO, what))
    return KO_A;
  if (move_reason_known(pos, OWL_ATTACK_MOVE_BAD_KO, what))
    return KO_B;
  return 0;
}

/*
 * Check whether an owl defense move reason already is recorded for a move.
 * A negative value for 'what' means only match 'type'.
 */
int
owl_defense_move_reason_known(int pos, int what)
{
  if (move_reason_known(pos, OWL_DEFEND_MOVE, what))
    return WIN;
  if (move_reason_known(pos, OWL_DEFEND_MOVE_GOOD_KO, what))
    return KO_A;
  if (move_reason_known(pos, OWL_DEFEND_MOVE_BAD_KO, what))
    return KO_B;
  return 0;
}

/*
 * Check whether an owl attack/defense move reason is recorded for a move.
 * A negative value for 'what' means only match 'type'.
 */
int
owl_move_reason_known(int pos, int what)
{
  return (owl_attack_move_reason_known(pos, what)
          || owl_defense_move_reason_known(pos, what));
}

/*
 * Check whether we have an owl attack/defense reason for a move that
 * involves a specific worm.
 */
static int
owl_move_vs_worm_known(int pos, int what)
{
  return owl_move_reason_known(pos, dragon[what].origin);
}

int
semeai_move_reason_known(int pos, int what)
{
  return move_reason_known(pos, SEMEAI_MOVE, what);
}

/* Check whether a worm is inessential */
static int
concerns_inessential_worm(int pos, int what)
{
  UNUSED(pos);
  return DRAGON2(what).safety == INESSENTIAL
        || worm[what].inessential;
}

/* Check whether a dragon is inessential */
static int
concerns_inessential_dragon(int pos, int what)
{
  UNUSED(pos);
  return DRAGON2(what).safety == INESSENTIAL; 
}

static int
move_is_marked_unsafe(int pos, int what)
{
  UNUSED(what);
  return (!move[pos].move_safety
	  && !adjacent_to_nondead_stone(pos, current_color));
}

/* Check whether a dragon is non-critical. */
static int
concerns_noncritical_dragon(int pos, int what)
{
  UNUSED(pos);
  return (dragon[what].status != CRITICAL
	  && worm[what].attack_codes[0] == 0);
}


/* (what) points to two worms listed in either_data. Returns true if
 * this is a "attack either" move reason, and one of the worms attackable.
 * FIXME: Ko?
 */
static int
either_worm_attackable(int pos, int what)
{
  UNUSED(pos);
  return (either_data[what].reason1 == ATTACK_STRING
      	  && either_data[what].reason2 == ATTACK_STRING
          && (worm[either_data[what].what1].attack_codes[0] != 0
              || worm[either_data[what].what2].attack_codes[0] != 0));
}

/* (what) points to two worms via all_data. Returns true if this is
 * a "defend both" move reason, and one of the worms is attackable.
 * FIXME: Ko?
 */
static int
one_of_both_attackable(int pos, int what)
{
  UNUSED(pos);
  return (all_data[what].reason1 == DEFEND_STRING
      	  && all_data[what].reason2 == DEFEND_STRING
   	  && (worm[all_data[what].what1].attack_codes[0] != 0
              || worm[all_data[what].what2].attack_codes[0] != 0));
}


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


/*
 * Add to the reasons for the move at (pos) that it attacks the worm
 * at (ww).
 */
void
add_attack_move(int pos, int ww, int code)
{
  ASSERT_ON_BOARD1(ww);
  ww = worm[ww].origin;

  if (code == WIN)
    add_move_reason(pos, ATTACK_MOVE, ww);
  else if (code == KO_A)
    add_move_reason(pos, ATTACK_MOVE_GOOD_KO, ww);
  else if (code == KO_B)
    add_move_reason(pos, ATTACK_MOVE_BAD_KO, ww);
}

/*
 * Add to the reasons for the move at (pos) that it defends the worm
 * at (ww).
 */
void
add_defense_move(int pos, int ww, int code)
{
  ASSERT_ON_BOARD1(ww);
  ww = worm[ww].origin;

  if (code == WIN)
    add_move_reason(pos, DEFEND_MOVE, ww);
  else if (code == KO_A)
    add_move_reason(pos, DEFEND_MOVE_GOOD_KO, ww);
  else if (code == KO_B)
    add_move_reason(pos, DEFEND_MOVE_BAD_KO, ww);
}

/*
 * Add to the reasons for the move at (pos) that it threatens to
 * attack the worm at (ww). 
 */
void
add_attack_threat_move(int pos, int ww, int code)
{
  UNUSED(code);
  
  ASSERT_ON_BOARD1(ww);
  add_move_reason(pos, ATTACK_THREAT, worm[ww].origin);
}

/* Remove an attack threat move reason. */

void
remove_attack_threat_move(int pos, int ww)
{
  ASSERT_ON_BOARD1(ww);
  remove_move_reason(pos, ATTACK_THREAT, worm[ww].origin);
}

/*
 * Add to the reasons for the move at (pos) that it defends the worm
 * at (ww).
 */
void
add_defense_threat_move(int pos, int ww, int code)
{
  UNUSED(code);

  ASSERT_ON_BOARD1(ww);
  add_move_reason(pos, DEFEND_THREAT, worm[ww].origin);
}


/* Report all, or up to max_strings, strings that are threatened 
 * at (pos).
 */
int
get_attack_threats(int pos, int max_strings, int strings[])
{
  int k;
  int num_strings;

  num_strings = 0;
  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    if (r < 0)
      break;

    if (move_reasons[r].type == ATTACK_THREAT)
      strings[num_strings++] = move_reasons[r].what;

    if (num_strings == max_strings)
      break;
  }

  return num_strings;
}

/* Report all, or up to max_strings, strings that might be defended 
 * at (pos).
 */
int
get_defense_threats(int pos, int max_strings, int strings[])
{
  int k;
  int num_strings;

  num_strings = 0;
  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    if (r < 0)
      break;

    if (move_reasons[r].type == DEFEND_THREAT)
      strings[num_strings++] = move_reasons[r].what;

    if (num_strings == max_strings)
      break;
  }

  return num_strings;
}

/* Report the biggest dragon that is owl-affected (possibily with ko)
 * by a move at (pos).
 */
int
get_biggest_owl_target(int pos)
{
  int k;
  int biggest_target = -1;
  float target_size = 0.0;
  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    if (r < 0)
      break;

    switch (move_reasons[r].type) {
    case OWL_ATTACK_MOVE:
    case OWL_ATTACK_MOVE_GOOD_KO:
    case OWL_ATTACK_MOVE_BAD_KO:
    case OWL_ATTACK_THREAT:
    case OWL_DEFEND_MOVE:
    case OWL_DEFEND_MOVE_GOOD_KO:
    case OWL_DEFEND_MOVE_BAD_KO:
    case OWL_DEFEND_THREAT:
    case OWL_PREVENT_THREAT:
      if (dragon[move_reasons[r].what].effective_size > target_size) {
        biggest_target = move_reasons[r].what;
        target_size = dragon[move_reasons[r].what].effective_size;
      }
      break;
    }
  }
  return biggest_target;
}

/*
 * Add to the reasons for the move at (pos) that it connects the
 * dragons at (dr1) and (dr2). Require that the dragons are
 * distinct.
 */
void
add_connection_move(int pos, int w1, int w2)
{
  int connection;

  ASSERT_ON_BOARD1(w1);
  ASSERT_ON_BOARD1(w2);
  ASSERT1(worm[w1].color == worm[w2].color, w1);
  if (worm[w1].origin == worm[w2].origin)
    return;
  
  connection = find_connection(worm[w1].origin, worm[w2].origin);
  add_move_reason(pos, CONNECT_MOVE, connection);
}

/*
 * Add to the reasons for the move at (pos) that it cuts the
 * dragons at (dr1) and (dr2). Require that the dragons are
 * distinct.
 */
void
add_cut_move(int pos, int w1, int w2)
{
  int connection;

  ASSERT_ON_BOARD1(w1);
  ASSERT_ON_BOARD1(w2);
  ASSERT1(worm[w1].color == worm[w2].color, w1);
  if (worm[w1].origin == worm[w2].origin)
    return;
  connection = find_connection(worm[w1].origin, worm[w2].origin);
  
  /*
   * Ignore the cut or connection if either (w1) or (w2)
   * points to a tactically captured worm.
   */
  if ((worm[w1].attack_codes[0] != 0 && worm[w1].defense_codes[0] == 0)
      || (worm[w2].attack_codes[0] != 0 && worm[w2].defense_codes[0] == 0))
    return;
  
  add_move_reason(pos, CUT_MOVE, connection);

}

/*
 * Add to the reasons for the move at (pos) that it is an anti-suji.
 * This means that it's a locally inferior move or for some other reason
 * must *not* be played.
 */
void
add_antisuji_move(int pos)
{
  add_move_reason(pos, ANTISUJI_MOVE, 0);
}

/*
 * Add to the reasons for the move at (pos) that it wins the
 * dragon (friendly or not) at (dr) in semeai. Since it is
 * possible that in some semeai one player can kill but the
 * other can only make seki, it is possible that one dragon
 * is already alive in seki. Therefore separate move reasons
 * must be added for the two dragons.
 */
void
add_semeai_move(int pos, int dr)
{
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, SEMEAI_MOVE, dragon[dr].origin);
}

/*
 * Add to the reasons for the move at (pos) that it might
 * kill/save the dragon at (dr1) in the semeai against (dr2).
 */
static void
add_potential_semeai_move(int pos, int type, int dr1, int dr2)
{
  ASSERT1(ON_BOARD(dr1), pos);
  ASSERT1(ON_BOARD(dr2), pos);
  if (next_semeai >= MAX_POTENTIAL_SEMEAI)
    DEBUG(DEBUG_MOVE_REASONS,
	  "Potential semeai move at %1m dropped as list was full\n", pos);
  else {
    semeai_target1[next_semeai] = dr1;
    semeai_target2[next_semeai] = dr2;
    add_move_reason(pos, type, next_semeai);
    next_semeai++;
  }
}

/*
 * Add to the reasons for the move at (pos) that it might
 * kill the dragon at (dr1) in the semeai against (dr2).
 */
void
add_potential_semeai_attack(int pos, int dr1, int dr2)
{
  add_potential_semeai_move(pos, POTENTIAL_SEMEAI_ATTACK, dr1, dr2);
}

/*
 * Add to the reasons for the move at (pos) that it might
 * save the dragon at (dr1) in the semeai against (dr2).
 */
void
add_potential_semeai_defense(int pos, int dr1, int dr2)
{
  add_potential_semeai_move(pos, POTENTIAL_SEMEAI_DEFENSE, dr1, dr2);
}

/*
 * Add to the reasons for the move at (pos) that given two
 * moves in a row a move here can win the dragon (friendly or
 * not) at (dr) in semeai. Such a move can be used as a 
 * ko threat, and it is also given some value due to uncertainty
 * in the counting of liberties.
 */
void
add_semeai_threat(int pos, int dr)
{
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, SEMEAI_THREAT, dragon[dr].origin);
}

/*
 * Add to the reasons for the move at (pos) that it will accomplish
 * one of two things: either (reason1) on (target1) or (reason2) on 
 * (target2).  
 *
 * At this time, (reason) can only be ATTACK_STRING.
 * However, more reasons will be implemented in the future.
 *
 * FIXME: Implement at least ATTACK_MOVE_GOOD_KO, ATTACK_MOVE_BAD_KO,
 *         DEFEND_MOVE and associates, CONNECT_MOVE, OWL_ATTACK_MOVE,
 *         OWL_DEFEND_MOVE, and possibly more.
 *
 * FIXME: Generalize to more than 2 parameters.
 *        When that is done, this will be a good way to add 
 *        atari_atari moves.
 */
void
add_either_move(int pos, int reason1, int target1, int reason2, int target2)
{
  int what1 = 0;
  int what2 = 0;
  int index;

  ASSERT_ON_BOARD1(target1);
  ASSERT_ON_BOARD1(target2);
  if (reason1 == reason2 && target1 == target2)
    return;
  
  /* For now. */
  gg_assert(reason1 == ATTACK_STRING);
  gg_assert(reason2 == ATTACK_STRING);

  switch (reason1) {
  case ATTACK_STRING:
    {
      what1 = worm[target1].origin;

      /* If this string is already attacked, and with no defense, then
       * there is no additional value of this move reason. */
      if (worm[target1].attack_codes[0] != 0
	  && worm[target1].defense_codes[0] == 0)
	return;
    }
    break;

  default:
    break;
  }

  switch (reason2) {
  case ATTACK_STRING:
    {
      what2 = worm[target2].origin;

      /* If this string is already attacked, and with no defense, then
       * there is no additional value of this move reason. */
      if (worm[target2].attack_codes[0] != 0 
	  && worm[target2].defense_codes[0] == 0)
	return;
    }
    break;

  default:
    break;
  }

  index = find_either_data(reason1, what1, reason2, what2);
  add_move_reason(pos, EITHER_MOVE, index);
}


/*
 * Add to the reasons for the move at (pos) that it will accomplish
 * both of two things: (reason1) on (target1) and (reason2) on 
 * (target2).  
 *
 * At this time, (reason) can only be DEFEND_STRING.
 * However, more reasons will be implemented in the future.
 *
 * FIXME: Implement at least ATTACK_MOVE_GOOD_KO, ATTACK_MOVE_BAD_KO,
 *         DEFEND_MOVE and associates, CONNECT_MOVE, OWL_ATTACK_MOVE,
 *         OWL_DEFEND_MOVE, and possibly more.
 *
 * FIXME: Generalize to more than 2 parameters.
 *        When that is done, this will be a good way to add 
 *        atari_atari moves.
 */
void
add_all_move(int pos, int reason1, int target1, int reason2, int target2)
{
  int what1 = 0;
  int what2 = 0;
  int index;

  ASSERT_ON_BOARD1(target1);
  ASSERT_ON_BOARD1(target2);
  if (reason1 == reason2 && target1 == target2)
    return;
  
  /* For now. */
  gg_assert(reason1 == DEFEND_STRING);
  gg_assert(reason2 == DEFEND_STRING);

  switch (reason1) {
  case DEFEND_STRING:
    what1 = worm[target1].origin;
    break;

  default:
    break;
  }

  switch (reason2) {
  case DEFEND_STRING:
    what2 = worm[target2].origin;
    break;

  default:
    break;
  }

  index = find_all_data(reason1, what1, reason2, what2);
  add_move_reason(pos, ALL_MOVE, index);
}


void
add_loss_move(int pos, int target1, int target2)
{
  int what1 = dragon[target1].origin;
  int what2 = worm[target2].origin;
  int index = find_pair_data(what1, what2);
  ASSERT1(target2 != NO_MOVE, pos);
  add_move_reason(pos, OWL_DEFEND_MOVE_LOSS, index);
}

/*
 * Add to the reasons for the move at (pos) that it expands
 * territory.
 */
void
add_expand_territory_move(int pos)
{
  add_move_reason(pos, EXPAND_TERRITORY_MOVE, 0);
}

/*
 * Add to the reasons for the move at (pos) that it expands
 * moyo.
 */
void
add_expand_moyo_move(int pos)
{
  add_move_reason(pos, EXPAND_MOYO_MOVE, 0);
}

/*
 * Add to the reasons for the move at (pos) that it is an invasion.
 */
void
add_invasion_move(int pos)
{
  add_move_reason(pos, INVASION_MOVE, 0);
}

/*
 * This function is called when a shape value for the move at (pos)
 * is found. 
 * 
 * We keep track of the largest positive shape value found, and the
 * total number of positive contributions, as well as the largest
 * negative shape value found, and the total number of negative
 * shape contributions.
 */
void
add_shape_value(int pos, float value)
{
  ASSERT_ON_BOARD1(pos);
  if (value > 0.0) {
    if (value > move[pos].maxpos_shape)
      move[pos].maxpos_shape = value;
    move[pos].numpos_shape += 1;
  }
  else if (value < 0.0) {
    value = -value;
    if (value > move[pos].maxneg_shape)
      move[pos].maxneg_shape = value;
    move[pos].numneg_shape += 1;
  }
}

/*
 * Flag that this move is worthwhile to play as a pure threat move.
 */
void
add_worthwhile_threat_move(int pos)
{
  move[pos].worthwhile_threat = 1;
}

/*
 * Add to the reasons for the move at (pos) that it attacks
 * the dragon (dr) on a strategical level.
 */
void
add_strategical_attack_move(int pos, int dr)
{
  dr = dragon[dr].origin;
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, STRATEGIC_ATTACK_MOVE, dr);
}

/*
 * Add to the reasons for the move at (pos) that it defends
 * the dragon (dr) on a strategical level.
 */
void
add_strategical_defense_move(int pos, int dr)
{
  dr = dragon[dr].origin;
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, STRATEGIC_DEFEND_MOVE, dr);
}

/*
 * Add to the reasons for the move at (pos) that the owl
 * code reports an attack on the dragon (dr).
 */
void
add_owl_attack_move(int pos, int dr, int kworm, int code)
{
  dr = dragon[dr].origin;

  ASSERT_ON_BOARD1(dr);
  if (code == WIN)
    add_move_reason(pos, OWL_ATTACK_MOVE, dr);
  else if (code == KO_A)
    add_move_reason(pos, OWL_ATTACK_MOVE_GOOD_KO, dr);
  else if (code == KO_B)
    add_move_reason(pos, OWL_ATTACK_MOVE_BAD_KO, dr);
  else if (code == GAIN) {
    ASSERT_ON_BOARD1(kworm);
    add_move_reason(pos, OWL_ATTACK_MOVE_GAIN, find_pair_data(dr, kworm));
  }
}

/*
 * Add to the reasons for the move at (pos) that the owl
 * code reports a defense of the dragon (dr).
 */
void
add_owl_defense_move(int pos, int dr, int code)
{
  dr = dragon[dr].origin;

  ASSERT_ON_BOARD1(dr);
  if (code == WIN)
    add_move_reason(pos, OWL_DEFEND_MOVE, dr);
  else if (code == KO_A)
    add_move_reason(pos, OWL_DEFEND_MOVE_GOOD_KO, dr);
  else if (code == KO_B)
    add_move_reason(pos, OWL_DEFEND_MOVE_BAD_KO, dr);
}

/*
 * Add to the reasons for the move at (pos) that the owl
 * code reports a move threatening to attack the dragon enemy (dr).
 * That is, if the attacker is given two moves in a row, (pos)
 * can be the first move.
 */
void
add_owl_attack_threat_move(int pos, int dr, int code)
{
  UNUSED(code);
  dr = dragon[dr].origin;
  
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, OWL_ATTACK_THREAT, dragon[dr].origin);
  add_worthwhile_threat_move(pos);
}

/* The owl code found the friendly dragon alive, or the unfriendly dragon
 * dead, and an extra point of attack or defense was found, so this might be a
 * good place to play.  
 */
void
add_owl_uncertain_defense_move(int pos, int dr)
{
  dr = dragon[dr].origin;
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, UNCERTAIN_OWL_DEFENSE, dragon[dr].origin);
}

/* The owl code found the opponent dragon alive, or the friendly
 * dragon dead, but was uncertain, and this move reason propose
 * an attack or defense which is expected to fail but might succeed.
 */
void
add_owl_uncertain_attack_move(int pos, int dr)
{
  dr = dragon[dr].origin;
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, UNCERTAIN_OWL_ATTACK, dragon[dr].origin);
}

/*
 * Add to the reasons for the move at (pos) that the owl
 * code reports a move threatening to rescue the dragon (dr).
 * That is, if the defender is given two moves in a row, (pos)
 * can be the first move.
 */
void
add_owl_defense_threat_move(int pos, int dr, int code)
{
  UNUSED(code);
  dr = dragon[dr].origin;

  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, OWL_DEFEND_THREAT, dragon[dr].origin);
  add_worthwhile_threat_move(pos);
}

/* Add to the reasons for the move at (pos) that it captures
 * at least one of a set of worms which individually are tactically
 * safe (such as a double atari). Only one such move reason is
 * permitted per move.
 */
void
add_my_atari_atari_move(int pos, int size)
{
  add_move_reason(pos, MY_ATARI_ATARI_MOVE, size);
}

/* Add to the reasons for the move at (pos) that it stops a
 * combination attack for the opponent.
 */
void
add_your_atari_atari_move(int pos, int size)
{
  add_move_reason(pos, YOUR_ATARI_ATARI_MOVE, size);
}


/*
 * Add to the reasons for the move at (pos) that the owl
 * code reports a move threatening to defend the dragon enemy (dr),
 * and that (pos) is a move which attacks the dragon. 
 * That is, if the defender is given two moves in a row, (pos)
 * can be the first move. Hopefully playing at (pos) makes it harder 
 * for the dragon to live.
 */
void
add_owl_prevent_threat_move(int pos, int dr)
{
  ASSERT_ON_BOARD1(dr);
  add_move_reason(pos, OWL_PREVENT_THREAT, dragon[dr].origin);
}

/*
 * Add value of followup moves. 
 */
void
add_followup_value(int pos, float value)
{
  ASSERT_ON_BOARD1(pos);
  if (value > move[pos].followup_value)
    move[pos].followup_value = value;
}

/*
 * Add value of reverse followup moves. 
 */
void
add_reverse_followup_value(int pos, float value)
{
  ASSERT_ON_BOARD1(pos);
  if (value > move[pos].reverse_followup_value)
    move[pos].reverse_followup_value = value;
}

/*
 * Set a minimum allowed value for the move.
 */
int
set_minimum_move_value(int pos, float value)
{
  ASSERT_ON_BOARD1(pos);
  if (value > move[pos].min_value) {
    move[pos].min_value = value;
    return 1;
  }
  return 0;
}

/*
 * Set a maximum allowed value for the move.
 */
void
set_maximum_move_value(int pos, float value)
{
  ASSERT_ON_BOARD1(pos);
  if (value < move[pos].max_value)
    move[pos].max_value = value;
}

/*
 * Set a minimum allowed territorial value for the move.
 */
void
set_minimum_territorial_value(int pos, float value)
{
  ASSERT_ON_BOARD1(pos);
  if (value > move[pos].min_territory)
    move[pos].min_territory = value;
}

/*
 * Set a maximum allowed territorial value for the move.
 */
void
set_maximum_territorial_value(int pos, float value)
{
  ASSERT_ON_BOARD1(pos);
  if (value < move[pos].max_territory)
    move[pos].max_territory = value;
}

/* 
 * Add a point redistribution rule, sending the points from (from)
 * to (to). 
 */
void
add_replacement_move(int from, int to, int color)
{
  int cc;
  int pos;
  int dummy;

  ASSERT_ON_BOARD1(from);
  ASSERT_ON_BOARD1(to);

  if (board[from] != EMPTY)
    return;
  ASSERT1(board[to] == EMPTY, to);

  cc = replacement_map[to];
  if (unconditionally_meaningless_move(to, color, &dummy)) {
    /* Silently ignore replacement patterns which conflict with the
     * unconditional analysis since the latter is always correct and
     * it's difficult to anticipate such situations for the patterns.
     */
    return;
  }

  /* First check for an incompatible redistribution rule. */
  if (replacement_map[from] != NO_MOVE) {
    int dd = replacement_map[from];
    /* Abort if the old rule isn't compatible with the new one.
     * (But not in the stable release.)
     */
    if (0) {
      ASSERT1(dd == to || to == replacement_map[dd], from);
    }
    /* There already is a redistribution in effect so we
     * have nothing more to do.
     */
    return;
  }

  TRACE("Move at %1m is replaced by %1m.\n", from, to);    

  /* Verify that we don't introduce a cyclic redistribution. */
  if (cc == from) {
    gprintf("Cyclic point redistribution detected.\n");
    ASSERT1(0, from);
  }

  /* Update the replacement map. Make sure that all replacements
   * always are directed immediately to the final destination.
   */
  if (cc != NO_MOVE)
    replacement_map[from] = cc;
  else
    replacement_map[from] = to;
  
  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (ON_BOARD(pos) && replacement_map[pos] == from)
      replacement_map[pos] = replacement_map[from];
  }
}


/* Find worms rescued by a move at (pos). */
void
get_saved_worms(int pos, signed char saved[BOARDMAX])
{
  int k;
  memset(saved, 0, sizeof(saved[0]) * BOARDMAX);
  
  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    int what;

    if (r < 0)
      break;
    
    what = move_reasons[r].what;
    /* We exclude the ko contingent defenses, to avoid that the
     * confirm_safety routines spot an attack with ko and thinks the
     * move is unsafe.
     */
    if (move_reasons[r].type == DEFEND_MOVE)
      mark_string(worm[what].origin, saved, 1);
    else if (move_reasons[r].type == OWL_DEFEND_MOVE_LOSS) {
      int origin = dragon[what].origin;
      int kworm = worm[what].origin;
      int ii;
      for (ii = BOARDMIN; ii < BOARDMAX; ii++)
	if (IS_STONE(board[ii]) && dragon[ii].origin == origin
	    && worm[ii].origin != kworm)
	  mark_string(worm[ii].origin, saved, 1);
    }
  }    
}

/* This function marks all stones whose status is changed by an owl move
 * reason according to the following rules:
 * 1. For an owl attack, all stones belonging to the attacked dragon are
 *    marked as INFLUENCE_CAPTURED_STONE
 * 2. For an owl defense, all stones belonging to the defended dragon are
 *    markes as INFLUENCE_SAVED_STONE if they are also sufficiently
 *    tactically stable.
 *
 * In effective_size, the sum of the effective size of the changed worms
 * is returned (unless it is a NULL pointer).
 */
void
mark_changed_dragon(int pos, int color, int affected, int affected2,
    		    int move_reason_type, signed char safe_stones[BOARDMAX],
		    float strength[BOARDMAX], float *effective_size)
{
  int ii;
  signed char new_status = INFLUENCE_SAVED_STONE;
  int result_to_beat = 0;

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

  if (effective_size)
    *effective_size = 0.0;

  /* For attack moves, we immediately can set the effective size.
   * For defense moves, it will be calculated in the course of
   * updating the worms' status.
   */
  switch (move_reason_type) {
    case OWL_ATTACK_MOVE:
    case OWL_ATTACK_MOVE_GOOD_KO:
    case OWL_ATTACK_MOVE_BAD_KO:
      ASSERT1(board[affected] == OTHER_COLOR(color), pos);
      new_status = 0;
      if (effective_size)
	*effective_size = dragon[affected].effective_size;
      break;
    case OWL_DEFEND_MOVE:
      ASSERT1(board[affected] == color, pos);
      result_to_beat = WIN;
      break;
    case OWL_DEFEND_MOVE_GOOD_KO:
      ASSERT1(board[affected] == color, pos);
      result_to_beat = KO_A;
      break;
    case OWL_DEFEND_MOVE_BAD_KO:
      ASSERT1(board[affected] == color, pos);
      result_to_beat = KO_B;
      break;
    case OWL_ATTACK_MOVE_GAIN:
      ASSERT1(board[affected] == OTHER_COLOR(color), pos);
      new_status = 0;
      if (effective_size)
	*effective_size = worm[affected2].effective_size;
      break;
    case OWL_DEFEND_MOVE_LOSS:
      ASSERT1(board[affected] == color, pos);
      if (effective_size)
	*effective_size = dragon[affected].effective_size
			  - worm[affected2].effective_size;
      result_to_beat = WIN;
      break;
    case SEMEAI_MOVE:
      ASSERT1(IS_STONE(board[affected]), pos);
      if (board[affected] == color)
	result_to_beat = WIN;
      else {
	new_status = 0;
	if (effective_size)
	  *effective_size = dragon[affected].effective_size;
      }
      break;

    default:
      /* mark_changed_dragon() called with invalid move reason. */
      ASSERT1(0, pos);
  }

  if (move_reason_type == OWL_ATTACK_MOVE_GAIN)
    mark_changed_string(affected2, safe_stones, strength, new_status);
  else {
    for (ii = first_worm_in_dragon(affected); ii != NO_MOVE; 
	 ii = next_worm_in_dragon(ii))
      if (new_status == 0)
	mark_changed_string(ii, safe_stones, strength, new_status);
      else {
	int worm_is_safe = 0;
	if (worm[ii].attack_codes[0] == NO_MOVE
	    || defense_move_reason_known(pos, ii))
	  worm_is_safe = 1;
	else if (trymove(pos, color, "mark-changed-dragon", ii)) {
	    if (REVERSE_RESULT(attack(ii, NULL)) >= result_to_beat)
	      worm_is_safe = 1;
	    popgo();
	}
	if (worm_is_safe || move_reason_type == SEMEAI_MOVE) {
	  /* This string can now be considered safe. Hence we mark the
	   * whole string as such:
	   */
	  mark_changed_string(ii, safe_stones, strength, new_status);
	  if (effective_size)
	    *effective_size += worm[ii].effective_size;
	}
      }
    if (move_reason_type == OWL_DEFEND_MOVE_LOSS) {
      new_status = 0;
      mark_changed_string(affected2, safe_stones, strength, new_status);
    }
  }
}

/* Marks the string at (affected) with the new status and accordingly
 * with the new strength.
 */
void
mark_changed_string(int affected, signed char safe_stones[BOARDMAX],
    		    float strength[BOARDMAX], signed char new_status)
{
  float new_strength;
  int ii;

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

  if (new_status == 0)
    new_strength = 0.0;
  else {
    gg_assert(new_status == INFLUENCE_SAVED_STONE);
    new_strength = DEFAULT_STRENGTH;
  }
  for (ii = BOARDMIN; ii < BOARDMAX; ii++)
    if (board[ii] == board[affected]
	&& same_string(ii, affected)) {
      strength[ii] = new_strength;
      safe_stones[ii] = new_status;
    }
}


/* Find dragons rescued by a move at (pos). */
void
get_saved_dragons(int pos, signed char saved[BOARDMAX])
{
  int k;
  memset(saved, 0, sizeof(saved[0]) * BOARDMAX);
  
  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    int what;

    if (r < 0)
      break;
    
    what = move_reasons[r].what;
    /* We exclude the ko contingent defenses, to avoid that the
     * confirm_safety routines spot an attack with ko and thinks the
     * move is unsafe.
     */
    if (move_reasons[r].type == OWL_DEFEND_MOVE)
      mark_dragon(what, saved, 1);
  }    
}


/* If a move has saved the dragons in saved_dragons[] and worms in
 * saved_worms[], this functions writes the stones now supposedly safe
 * in the array safe_stones[].
 *
 * The safety of the played move itself is set according to
 * move[pos].move_safety.
 */
void
mark_safe_stones(int color, int move_pos,
		 const signed char saved_dragons[BOARDMAX],
    		 const signed char saved_worms[BOARDMAX],
		 signed char safe_stones[BOARDMAX])
{
  int pos; 

  for (pos = BOARDMIN; pos < BOARDMAX; pos++) {
    if (board[pos] == OTHER_COLOR(color)) {
      if (dragon[pos].status == DEAD
	  || (worm[pos].attack_codes[0] != 0
	      && worm[pos].defense_codes[0] == 0))
	safe_stones[pos] = 0;
      else
	safe_stones[pos] = SAFE_STONE;
    }
    else if (board[pos] == color) {
      if ((worm[pos].attack_codes[0] != 0
	   && (worm[pos].defense_codes[0] == 0 || !saved_worms[pos]))
	  || dragon[pos].status == DEAD)
	safe_stones[pos] = 0;
      else if (saved_dragons[pos])
	safe_stones[pos] = OWL_SAVED_STONE;
      else if (dragon[pos].status == CRITICAL)
	safe_stones[pos] = 0;
      else
	safe_stones[pos] = SAFE_STONE;
    }
    else
      safe_stones[pos] = 0;
  }
  safe_stones[move_pos]
    = move[move_pos].move_safety && safe_move(move_pos, color) == WIN;
}


/* List the move reasons for (color)'s move at (pos). Return the
 * number of move reasons.
 */
int
list_move_reasons(FILE *out, int move_pos)
{
  int m;
  int n;
  int pos;
  int k;
  int reason1;
  int reason2;
  int aa = NO_MOVE;
  int bb = NO_MOVE;
  int worm1 = -1;
  int worm2 = -1;
  int num_move_reasons = 0;

  gprintf("\nMove reasons:\n");
  
  for (n = 0; n < board_size; n++)
    for (m = board_size-1; m >= 0; m--) {
      pos = POS(m, n);

      if (move_pos != NO_MOVE && move_pos != pos)
	continue;
      
      for (k = 0; k < MAX_REASONS; k++) {
	int r = move[pos].reason[k];

	if (r < 0)
	  break;

	num_move_reasons++;

	switch (move_reasons[r].type) {
	case ATTACK_MOVE:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m attacks %1m%s\n", pos, aa,
		   (worm[aa].defense_codes[0] == 0) ? " (defenseless)" : "");
	  break;
	case ATTACK_MOVE_GOOD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m attacks %1m%s with good ko\n", pos, aa,
		   (worm[aa].defense_codes[0] == 0) ? " (defenseless)" : "");
	  break;
	case ATTACK_MOVE_BAD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m attacks %1m%s with bad ko\n", pos, aa,
		   (worm[aa].defense_codes[0] == 0) ? " (defenseless)" : "");
	  break;
	  
	case DEFEND_MOVE:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m defends %1m\n", pos, aa);
	  break;
	case DEFEND_MOVE_GOOD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m defends %1m with good ko\n", pos, aa);
	  break;
	case DEFEND_MOVE_BAD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m defends %1m with bad ko\n", pos, aa);
	  break;
	  
	case ATTACK_THREAT:
	case DEFEND_THREAT:
	  aa = move_reasons[r].what;
	  
	  if (move_reasons[r].type == ATTACK_THREAT)
	    gfprintf(out, "Move at %1m threatens to attack %1m\n", pos, aa);
	  else if (move_reasons[r].type == DEFEND_THREAT)
	    gfprintf(out, "Move at %1m threatens to defend %1m\n", pos, aa);
	  break;

	case UNCERTAIN_OWL_DEFENSE:
	  aa = move_reasons[r].what;
	  if (board[aa] == current_color)
	    gfprintf(out, "%1m found alive but not certainly, %1m defends it again\n",
		     aa, pos);
	  else
	    gfprintf(out, "%1m found dead but not certainly, %1m attacks it again\n",
		     aa, pos);
	  break;	  

	case CONNECT_MOVE:
	case CUT_MOVE:
	  worm1 = conn_worm1[move_reasons[r].what];
	  worm2 = conn_worm2[move_reasons[r].what];
	  if (move_reasons[r].type == CONNECT_MOVE)
	    gfprintf(out, "Move at %1m connects %1m and %1m\n",
		     pos, worm1, worm2);
	  else
	    gfprintf(out, "Move at %1m cuts %1m and %1m\n", pos, worm1, worm2);
	  break;
	  
	case ANTISUJI_MOVE:
	  gfprintf(out, "Move at %1m is an antisuji\n", pos);
	  break;
	  
	case SEMEAI_MOVE:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m wins semeai for %1m\n", pos, aa);
	  break;
	  
	case SEMEAI_THREAT:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m threatens to win semeai for %1m\n",
		   pos, aa);
	  break;
	  
	case EITHER_MOVE:
	  reason1 = either_data[move_reasons[r].what].reason1;
	  reason2 = either_data[move_reasons[r].what].reason2;
	  worm1 = either_data[move_reasons[r].what].what1;
	  worm2 = either_data[move_reasons[r].what].what2;
	  gfprintf(out, "Move at %1m either %s %1m or %s %1m\n", pos, 
		   reason1 == ATTACK_STRING ? "attacks" : "defends", worm1, 
		   reason2 == ATTACK_STRING ? "attacks" : "defends", worm2);
	  break;

	case ALL_MOVE:
	  reason1 = all_data[move_reasons[r].what].reason1;
	  reason2 = all_data[move_reasons[r].what].reason2;
	  worm1 = all_data[move_reasons[r].what].what1;
	  worm2 = all_data[move_reasons[r].what].what2;
	  gfprintf(out, "Move at %1m both %s %1m and %s %1m\n", pos, 
		   reason1 == ATTACK_STRING ? "attacks" : "defends", worm1, 
		   reason2 == ATTACK_STRING ? "attacks" : "defends", worm2);
	  break;

	case OWL_ATTACK_MOVE:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-attacks %1m\n", pos, aa);
	  break;
	case OWL_ATTACK_MOVE_GOOD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-attacks %1m with good ko\n", pos, aa);
	  break;
	case OWL_ATTACK_MOVE_BAD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-attacks %1m with bad ko\n", pos, aa);
	  break;
	case OWL_ATTACK_MOVE_GAIN:
	  aa = either_data[move_reasons[r].what].what1;
	  bb = either_data[move_reasons[r].what].what2;
	  gfprintf(out, "Move at %1m owl-attacks %1m (captures %1m)\n",
		   pos, aa, bb);
	  break;
	  
	case OWL_DEFEND_MOVE:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-defends %1m\n", pos, aa);
	  break;
	case OWL_DEFEND_MOVE_GOOD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-defends %1m with good ko\n", pos, aa);
	  break;
	case OWL_DEFEND_MOVE_BAD_KO:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-defends %1m with bad ko\n", pos, aa);
	  break;
	case OWL_DEFEND_MOVE_LOSS:
	  aa = either_data[move_reasons[r].what].what1;
	  bb = either_data[move_reasons[r].what].what2;
	  gfprintf(out, "Move at %1m owl-defends %1m (loses %1m)\n",
		   pos, aa, bb);
	  break;
	  
	case OWL_ATTACK_THREAT:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-threatens to attack %1m\n", pos, aa);
	  break;
	  
	case OWL_DEFEND_THREAT:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-threatens to defend %1m\n", pos, aa);
	  break;
	  
	case OWL_PREVENT_THREAT:
	  aa = move_reasons[r].what;
	  gfprintf(out, "Move at %1m owl-prevents a threat to attack or defend %1m\n", 
		   pos, aa);
	  break;

	case EXPAND_TERRITORY_MOVE:
	  gfprintf(out, "Move at %1m expands territory\n", pos);
	  break;
	  
	case EXPAND_MOYO_MOVE:
	  gfprintf(out, "Move at %1m expands moyo\n", pos);
	  break;
	  
	case INVASION_MOVE:
	  gfprintf(out, "Move at %1m is an invasion\n", pos);
	  break;
	  
	case STRATEGIC_ATTACK_MOVE:
	case STRATEGIC_DEFEND_MOVE:
	  aa = move_reasons[r].what;
	  
	  if (move_reasons[r].type == STRATEGIC_ATTACK_MOVE)
	    gfprintf(out, "Move at %1m strategically attacks %1m\n", pos, aa);
	  else
	    gfprintf(out, "Move at %1m strategically defends %1m\n", pos, aa);
	  break;
	  
	case MY_ATARI_ATARI_MOVE:
	  gfprintf(out, "Move at %1m captures something\n", pos);

	case YOUR_ATARI_ATARI_MOVE:
	  gfprintf(out, "Move at %1m defends against combination attack\n",
		   pos);
	}
      }
      if (k > 0 && move[pos].move_safety == 0)
	gfprintf(out, "Move at %1m strategically or tactically unsafe\n", pos);
    }
  
  return num_move_reasons;
}




/* This array lists rules according to which we set the status
 * flags of a move reasons.
 * The format is:
 * { List of reasons to which the rule applies, condition of the rule,
 * flags to be set, trace message }
 * The condition must be of type discard_condition_fn_ptr, that is a pointer
 * to a function with parameters (pos, what).
 *
 * FIXME: Add handling of ALL and EITHER moves for inessential worms.
 */

static struct discard_rule discard_rules[] =
{
  { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO,
      ATTACK_MOVE_BAD_KO, ATTACK_THREAT,
      DEFEND_MOVE, DEFEND_MOVE_GOOD_KO,
      DEFEND_MOVE_BAD_KO, DEFEND_THREAT, -1 },
    owl_move_vs_worm_known, TERRITORY_REDUNDANT,
    "  %1m: 0.0 - (threat of) attack/defense of %1m (owl attack/defense as well)\n" },
  { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
    owl_move_reason_known, REDUNDANT,
    "  %1m: 0.0 - (threat to) win semeai involving %1m (owl move as well)\n"},
  { { SEMEAI_MOVE, SEMEAI_THREAT, -1 },
    tactical_move_vs_whole_dragon_known, REDUNDANT,
    "  %1m: 0.0 - (threat to) win semeai involving %1m (tactical move as well)\n"},
  { { EITHER_MOVE, -1 },
    either_worm_attackable, REDUNDANT,
    "  %1m: 0.0 - 'attack either' is redundant at %1m (direct att./def. as well)\n"},
  { { ALL_MOVE, -1 },
    one_of_both_attackable, REDUNDANT,
    "  %1m: 0.0 - 'defend both' is redundant at %1m (direct att./def. as well)\n"},
  { { ATTACK_THREAT, DEFEND_THREAT, -1 },
    concerns_inessential_worm, TERRITORY_REDUNDANT,
    "  %1m: 0.0 - attack/defense threat of %1m (inessential)\n"},
  { { OWL_ATTACK_THREAT, UNCERTAIN_OWL_DEFENSE, -1 },
    concerns_inessential_dragon, REDUNDANT,
    "  %1m: 0.0 - (uncertain) owl attack/defense of %1m (inessential)\n"},
  { { ATTACK_MOVE, ATTACK_MOVE_GOOD_KO, ATTACK_MOVE_BAD_KO,
      DEFEND_MOVE, DEFEND_MOVE_GOOD_KO, DEFEND_MOVE_BAD_KO, -1},
    move_is_marked_unsafe, REDUNDANT,
    "  %1m: 0.0 - tactical move vs %1m (unsafe move)\n"},
  { { OWL_ATTACK_MOVE, OWL_ATTACK_MOVE_GOOD_KO, OWL_ATTACK_MOVE_BAD_KO,
      OWL_DEFEND_MOVE, OWL_DEFEND_MOVE_GOOD_KO, OWL_DEFEND_MOVE_BAD_KO, -1},
    concerns_noncritical_dragon, REDUNDANT,
    "  %1m: 0.0 - owl move vs %1m (non-critical)\n"},
  { { -1 }, NULL, 0, ""}  /* Keep this entry at end of the list. */
};

/* This function checks the list of move reasons for redundant move
 * reasons and marks them accordingly in their status field.
 */
void
discard_redundant_move_reasons(int pos)
{
  int k1, k2;
  int l;
  for (k1 = 0; !(discard_rules[k1].reason_type[0] == -1); k1++) {
    for (k2 = 0; !(discard_rules[k1].reason_type[k2] == -1); k2++) {
      for (l = 0; l < MAX_REASONS; l++) {

        int r = move[pos].reason[l];
        if (r < 0)
          break;
        if ((move_reasons[r].type == discard_rules[k1].reason_type[k2])
            && (discard_rules[k1].condition(pos, move_reasons[r].what))) {
          DEBUG(DEBUG_MOVE_REASONS, discard_rules[k1].trace_message,
                pos, get_pos(move_reasons[r].type, move_reasons[r].what)); 
          move_reasons[r].status |= discard_rules[k1].flags;
        }
      } 
    }
  }
}


/* Look through the move reasons to see whether (pos) is an antisuji move. */
int
is_antisuji_move(int pos)
{
  int k;
  for (k = 0; k < MAX_REASONS; k++) {
    int r = move[pos].reason[k];
    if (r < 0)
      break;
    if (move_reasons[r].type == ANTISUJI_MOVE)
      return 1; /* This move must not be played. End of story. */
  }

  return 0;
}

/* Increase the randomness scaling factor.
 * This causes the move value to be more random.
 */

void
scale_randomness(int pos, float scaling)
{
  if (scaling > move[pos].randomness_scaling)
    move[pos].randomness_scaling = scaling;
}


/* Register the given `move' as a good attack threat against `target'. By
 * "good" we mean a threat which is effectively a sente for the player.
 * E.g. in this position the threat is good, because it results in four
 * sente moves locally (trevord:950):
 *
 *  ..OX..
 *  .O.*..
 *  .OXX..
 *  .OOX..
 *  ------
 *
 * We use this list of good threats for performance reasons so that
 * estimate_territorial_value() in valuemoves.c doesn't have to read
 * through all the moves. Such threats are found with patterns.
 */
void
register_good_attack_threat(int move, int target)
{
  int k;
  ASSERT_ON_BOARD1(move);
  ASSERT_ON_BOARD1(target);
  ASSERT1(IS_STONE(worm[target].color), move);

  target = worm[target].origin;
  for (k = 0; k < MAX_ATTACK_THREATS; k++) {
    if (known_good_attack_threats[move][k] == target)
      break;
    if (known_good_attack_threats[move][k] == NO_MOVE) {
      known_good_attack_threats[move][k] = target;
      break;
    }
  }
}


/* Determine if an attack threat is registered as good (see above). */
int
is_known_good_attack_threat(int move, int target)
{
  int k;
  ASSERT_ON_BOARD1(move);
  ASSERT_ON_BOARD1(target);
  ASSERT1(IS_STONE(worm[target].color), move);

  target = worm[target].origin;
  for (k = 0; k < MAX_ATTACK_THREATS; k++) {
    if (known_good_attack_threats[move][k] == target)
      return 1;
    if (known_good_attack_threats[move][k] == NO_MOVE)
      break;
  }

  return 0;
}

/* Like documented in endgame:980, there are also moves which aren't
 * safe by themselves, but attempting to capture these stones would
 * result in a loss for the opponent (typically, by damezumari).
 * Simple examples include snapbacks, but more complicated ones do
 * exist. Following functions are helpers for the valuation processing
 * which deal with such special cases.
 */
void
register_known_safe_move(int move)
{
  ASSERT_ON_BOARD1(move);
  
  known_safe_moves[move] = 1;
}


int
is_known_safe_move(int move)
{
  ASSERT_ON_BOARD1(move);
  
  return known_safe_moves[move];
}


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