File: interface.c

package info (click to toggle)
gnugo 3.8-4
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 17,312 kB
  • ctags: 4,228
  • sloc: ansic: 56,439; perl: 3,771; lisp: 2,789; sh: 730; makefile: 700; python: 682; awk: 113; sed: 22
file content (430 lines) | stat: -rw-r--r-- 11,615 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
 * 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("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:
 */