Two player mode now works with basic functionality.
[sgk-go] / engine / interface.c
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* 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 <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("Opponent plays: ");
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:
*/