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

#include "gnugo.h"

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

#include "sgftree.h"
#include "liberty.h"
#include "clock.h"

#include "gg_utils.h"

/*
 * Initialize the gnugo engine. This needs to be called 
 * once only.
 */

void
init_gnugo(float memory, unsigned int seed)
{
  /* We need a fixed seed when initializing the Zobrist hashing to get
   * reproducable results.
   * FIXME: Test the quality of the seed.
   */
  set_random_seed(HASH_RANDOM_SEED);
  reading_cache_init(memory * 1024 * 1024);
  set_random_seed(seed);
  persistent_cache_init();
  clear_board();

  transformation_init();
  dfa_match_init();
  choose_mc_patterns(NULL);

  clear_approxlib_cache();
  clear_accuratelib_cache();
}


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

/* Check whether we can accept a certain boardsize. Set out to NULL to
 * suppress informative messages. Return 1 for an acceptable
 * boardsize, 0 otherwise.
 */
int check_boardsize(int boardsize, FILE *out)
{
  int max_board = MAX_BOARD;
  if (use_monte_carlo_genmove && max_board > 9)
    max_board = 9;
  
  if (boardsize < MIN_BOARD || boardsize > max_board) {
    if (out) {
      fprintf(out, "Unsupported board size: %d. ", boardsize);
      if (boardsize < MIN_BOARD)
	fprintf(out, "Min size is %d.\n", MIN_BOARD);
      else {
	fprintf(out, "Max size is %d", max_board);
	if (max_board < MAX_BOARD)
	  fprintf(out, " (%d without --monte-carlo)", MAX_BOARD);
	fprintf(out, ".\n");
      }
      fprintf(out, "Try `gnugo --help' for more information.\n");
    }
    return 0;
  }

  return 1;
}

/*
 * Clear the board.
 */
void
gnugo_clear_board(int boardsize)
{
  board_size = boardsize;
  clear_board();
  init_timers();
#if 0
  if (metamachine && oracle_exists)
    oracle_clear_board(boardsize);
#endif
}

/* Play a move and start the clock */

void
gnugo_play_move(int move, int color)
{
#if ORACLE
  if (oracle_exists)
    oracle_play_move(move, color);
  else
    play_move(move, color);
#else
  play_move(move, color);
#endif
  clock_push_button(color);
}


/*
 * Perform the moves and place the stones from the SGF node on the 
 * board. Return the color of the player whose turn it is to move.
 */

int
gnugo_play_sgfnode(SGFNode *node, int to_move)
{
  SGFProperty *prop;

  for (prop = node->props; prop; prop = prop->next) {
    switch (prop->name) {
    case SGFAB:
      /* A black stone. */
      add_stone(get_sgfmove(prop), BLACK);
      break;

    case SGFAW:
      /* A white stone. */
      add_stone(get_sgfmove(prop), WHITE);
      break;

    case SGFPL:
      /* Player property - who is next to move? */
      if (prop->value[0] == 'w' || prop->value[0] == 'W')
	to_move = WHITE;
      else
	to_move = BLACK;
      break;

    case SGFW:
    case SGFB:
      /* An ordinary move. */
      to_move = (prop->name == SGFW) ? WHITE : BLACK;
      gnugo_play_move(get_sgfmove(prop), to_move);
      to_move = OTHER_COLOR(to_move);
      break;
    }
  }

  return to_move;
}


/* Interface to place_fixed_handicap. Sets up handicap stones and
 * updates the sgf file.
 */
int
gnugo_sethand(int desired_handicap, SGFNode *node)
{
  place_fixed_handicap(desired_handicap);
  sgffile_recordboard(node);
  return handicap;
}


/* Put upper and lower score estimates into *upper, *lower and
 * return the average. A positive score favors white. In computing
 * the upper bound, CRITICAL dragons are awarded to white; in
 * computing the lower bound, they are awarded to black.
 */

float
gnugo_estimate_score(float *upper, float *lower)
{
  silent_examine_position(EXAMINE_DRAGONS);
  if (upper != NULL)
    *upper = white_score;
  if (lower != NULL)
    *lower = black_score;
  return ((white_score + black_score) / 2.0);
}


/* ================================================================ */
/*                             Gameinfo                             */
/* ================================================================ */


/*
 * Initialize the structure.
 */

void
gameinfo_clear(Gameinfo *gameinfo)
{
  gnugo_clear_board(board_size);
  gameinfo->handicap = 0;
  gameinfo->to_move = BLACK;
  sgftree_clear(&gameinfo->game_record);

  /* Info relevant to the computer player. */
  gameinfo->computer_player = WHITE; /* Make an assumption. */
}


/*
 * Print a gameinfo.
 */

void
gameinfo_print(Gameinfo *gameinfo)
{
  printf("Board Size:   %d\n", board_size);
  printf("Handicap      %d\n", gameinfo->handicap);
  printf("Komi:         %.1f\n", komi);
  printf("Move Number:  %d\n", movenum);
  printf("To Move:      %s\n", color_to_string(gameinfo->to_move));

  printf("Computer player: ");
  if (gameinfo->computer_player == WHITE)
    printf("White\n");
  else if (gameinfo->computer_player == BLACK)
    printf("Black\n");
  else if (gameinfo->computer_player == EMPTY)
    printf("Both (solo)\n");
  else
    printf("Nobody\n");
}

/*
 * Play the moves in an SGF tree. Walk the main variation, actioning
 * the properties into the playing board.
 *
 * Returns the color of the next move to be made. The returned color
 * being EMPTY signals a failure to load the file.
 *
 * Head is an sgf tree. 
 * Untilstr is an optional string of the form either 'L12' or '120'
 * which tells it to stop playing at that move or move-number.
 * When debugging, this is the location of the move being examined.
 */

int
gameinfo_play_sgftree_rot(Gameinfo *gameinfo, SGFTree *tree,
			  const char *untilstr, int orientation)
{
  int bs;
  int next = BLACK;
  int untilmove = -1; /* Neither a valid move nor pass. */
  int until = 9999;
  
  if (!sgfGetIntProperty(tree->root, "SZ", &bs))
    bs = 19;

  if (!check_boardsize(bs, stderr))
    return EMPTY;
  
  handicap = 0;
  if (sgfGetIntProperty(tree->root, "HA", &handicap) && handicap > 1)
    next = WHITE;
  gameinfo->handicap = handicap;
  
  if (handicap > bs * bs - 1 || handicap < 0) {
    gprintf(" Handicap HA[%d] is unreasonable.\n Modify SGF file.\n",
	    handicap);
    return EMPTY;
  }
  
  gnugo_clear_board(bs);

  if (!sgfGetFloatProperty(tree->root, "KM", &komi)) {
    if (gameinfo->handicap == 0)
      komi = 5.5;
    else
      komi = 0.5;
  }

  /* Now we can safely parse the until string (which depends on board size). */
  if (untilstr) {
    if (*untilstr > '0' && *untilstr <= '9') {
      until = atoi(untilstr);
      DEBUG(DEBUG_LOADSGF, "Loading until move %d\n", until);
    }
    else {
      untilmove = string_to_location(board_size, untilstr);
      DEBUG(DEBUG_LOADSGF, "Loading until move at %1m\n", untilmove);
    }
  }
  
  /* Finally, we iterate over all the properties of all the
   * nodes, actioning them. We follow only the 'child' pointers,
   * as we have no interest in variations.
   *
   * The sgf routines map AB[aa][bb][cc] into AB[aa]AB[bb]AB[cc]
   */
  for (tree->lastnode = NULL; sgftreeForward(tree);) {
    SGFProperty *prop;
    int move;
      
    for (prop = tree->lastnode->props; prop; prop = prop->next) {
      DEBUG(DEBUG_LOADSGF, "%c%c[%s]\n", 
	    prop->name & 0xff, (prop->name >> 8), prop->value);
      switch (prop->name) {
      case SGFAB:
      case SGFAW:
	/* Generally the last move is unknown when the AB or AW
	 * properties are encountered. These are used to set up
	 * a board position (diagram) or to place handicap stones
	 * without reference to the order in which the stones are
	 * placed on the board.
	 */
	move = rotate1(get_sgfmove(prop), orientation);
	if (board[move] != EMPTY)
	  gprintf("Illegal SGF! attempt to add a stone at occupied point %1m\n",
		  move);
	else
	  add_stone(move, prop->name == SGFAB ? BLACK : WHITE);
	break;
	      
      case SGFPL:
	/* Due to a bad comment in the SGF FF3 definition (in the
         * "Alphabetical list of properties" section) some
         * applications encode the colors with 1 for black and 2 for
         * white.
	 */
	if (prop->value[0] == 'w'
	    || prop->value[0] == 'W'
	    || prop->value[0] == '2')
	  next = WHITE;
	else
	  next = BLACK;
	/* following really should not be needed for proper sgf file */
	if (stones_on_board(GRAY) == 0 && next == WHITE) {
	  place_fixed_handicap(gameinfo->handicap);
	  sgfOverwritePropertyInt(tree->root, "HA", handicap);
	}
	break;
	      
      case SGFW:
      case SGFB:
	next = prop->name == SGFW ? WHITE : BLACK;
	/* following really should not be needed for proper sgf file */
	if (stones_on_board(GRAY) == 0 && next == WHITE) {
	  place_fixed_handicap(gameinfo->handicap);
	  sgfOverwritePropertyInt(tree->root, "HA", handicap);
	}

	move = get_sgfmove(prop);
	if (move == untilmove || movenum == until - 1) {
	  gameinfo->to_move = next;
	  /* go back so that variant will be added to the proper node */
	  sgftreeBack(tree);
	  return next;
	}

	move = rotate1(move, orientation);
	if (move == PASS_MOVE || board[move] == EMPTY) {
	  gnugo_play_move(move, next);
	  next = OTHER_COLOR(next);
	}
	else {
	  gprintf("WARNING: Move off board or on occupied position found in sgf-file.\n");
	  gprintf("Move at %1m ignored, trying to proceed.\n", move);
	  gameinfo->to_move = next;
	  return next;
	}

	break;

      case SGFIL:
	/* The IL property is not a standard SGF property but
	 * is used by GNU Go to mark illegal moves. If a move
	 * is found marked with the IL property which is a ko
	 * capture then that ko capture is deemed illegal and
	 * (board_ko_i, board_ko_j) is set to the location of
	 * the ko.
	 */
	move = rotate1(get_sgfmove(prop), orientation);

	if (board_size > 1)
	{
	  int move_color;

	  if (ON_BOARD(NORTH(move)))
	    move_color = OTHER_COLOR(board[NORTH(move)]);
	  else 
	    move_color = OTHER_COLOR(board[SOUTH(move)]);
	  if (is_ko(move, move_color, NULL))
	    board_ko_pos = move;
	}
	break;
      }
    }
  }

  gameinfo->to_move = next;
  return next;
}

/* Same as previous function, using standard orientation */

int
gameinfo_play_sgftree(Gameinfo *gameinfo, SGFTree *tree, const char *untilstr)
{
  return gameinfo_play_sgftree_rot(gameinfo, tree, untilstr, 0);
}



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