Two player mode now works with basic functionality.
[sgk-go] / engine / interface.c
CommitLineData
7eeb782e
AT
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
2 * This is GNU Go, a Go program. Contact gnugo@gnu.org, or see *
3 * http://www.gnu.org/software/gnugo/ for more information. *
4 * *
5 * Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, *
6 * 2008 and 2009 by the Free Software Foundation. *
7 * *
8 * This program is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU General Public License as *
10 * published by the Free Software Foundation - version 3 or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License in file COPYING for more details. *
17 * *
18 * You should have received a copy of the GNU General Public *
19 * License along with this program; if not, write to the Free *
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, *
21 * Boston, MA 02111, USA. *
22\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
23
24#include "gnugo.h"
25
26#include <stdlib.h>
27#include <string.h>
28
29#include "sgftree.h"
30#include "liberty.h"
31#include "clock.h"
32
33#include "gg_utils.h"
34
35/*
36 * Initialize the gnugo engine. This needs to be called
37 * once only.
38 */
39
40void
41init_gnugo(float memory, unsigned int seed)
42{
43 /* We need a fixed seed when initializing the Zobrist hashing to get
44 * reproducable results.
45 * FIXME: Test the quality of the seed.
46 */
47 set_random_seed(HASH_RANDOM_SEED);
48 reading_cache_init(memory * 1024 * 1024);
49 set_random_seed(seed);
50 persistent_cache_init();
51 clear_board();
52
53 transformation_init();
54 dfa_match_init();
55 choose_mc_patterns(NULL);
56
57 clear_approxlib_cache();
58 clear_accuratelib_cache();
59}
60
61
62/* ---------------------------------------------------------------- */
63
64/* Check whether we can accept a certain boardsize. Set out to NULL to
65 * suppress informative messages. Return 1 for an acceptable
66 * boardsize, 0 otherwise.
67 */
68int check_boardsize(int boardsize, FILE *out)
69{
70 int max_board = MAX_BOARD;
71 if (use_monte_carlo_genmove && max_board > 9)
72 max_board = 9;
73
74 if (boardsize < MIN_BOARD || boardsize > max_board) {
75 if (out) {
76 fprintf(out, "Unsupported board size: %d. ", boardsize);
77 if (boardsize < MIN_BOARD)
78 fprintf(out, "Min size is %d.\n", MIN_BOARD);
79 else {
80 fprintf(out, "Max size is %d", max_board);
81 if (max_board < MAX_BOARD)
82 fprintf(out, " (%d without --monte-carlo)", MAX_BOARD);
83 fprintf(out, ".\n");
84 }
85 fprintf(out, "Try `gnugo --help' for more information.\n");
86 }
87 return 0;
88 }
89
90 return 1;
91}
92
93/*
94 * Clear the board.
95 */
96void
97gnugo_clear_board(int boardsize)
98{
99 board_size = boardsize;
100 clear_board();
101 init_timers();
102#if 0
103 if (metamachine && oracle_exists)
104 oracle_clear_board(boardsize);
105#endif
106}
107
108/* Play a move and start the clock */
109
110void
111gnugo_play_move(int move, int color)
112{
113#if ORACLE
114 if (oracle_exists)
115 oracle_play_move(move, color);
116 else
117 play_move(move, color);
118#else
119 play_move(move, color);
120#endif
121 clock_push_button(color);
122}
123
124
125/*
126 * Perform the moves and place the stones from the SGF node on the
127 * board. Return the color of the player whose turn it is to move.
128 */
129
130int
131gnugo_play_sgfnode(SGFNode *node, int to_move)
132{
133 SGFProperty *prop;
134
135 for (prop = node->props; prop; prop = prop->next) {
136 switch (prop->name) {
137 case SGFAB:
138 /* A black stone. */
139 add_stone(get_sgfmove(prop), BLACK);
140 break;
141
142 case SGFAW:
143 /* A white stone. */
144 add_stone(get_sgfmove(prop), WHITE);
145 break;
146
147 case SGFPL:
148 /* Player property - who is next to move? */
149 if (prop->value[0] == 'w' || prop->value[0] == 'W')
150 to_move = WHITE;
151 else
152 to_move = BLACK;
153 break;
154
155 case SGFW:
156 case SGFB:
157 /* An ordinary move. */
158 to_move = (prop->name == SGFW) ? WHITE : BLACK;
159 gnugo_play_move(get_sgfmove(prop), to_move);
160 to_move = OTHER_COLOR(to_move);
161 break;
162 }
163 }
164
165 return to_move;
166}
167
168
169/* Interface to place_fixed_handicap. Sets up handicap stones and
170 * updates the sgf file.
171 */
172int
173gnugo_sethand(int desired_handicap, SGFNode *node)
174{
175 place_fixed_handicap(desired_handicap);
176 sgffile_recordboard(node);
177 return handicap;
178}
179
180
181/* Put upper and lower score estimates into *upper, *lower and
182 * return the average. A positive score favors white. In computing
183 * the upper bound, CRITICAL dragons are awarded to white; in
184 * computing the lower bound, they are awarded to black.
185 */
186
187float
188gnugo_estimate_score(float *upper, float *lower)
189{
190 silent_examine_position(EXAMINE_DRAGONS);
191 if (upper != NULL)
192 *upper = white_score;
193 if (lower != NULL)
194 *lower = black_score;
195 return ((white_score + black_score) / 2.0);
196}
197
198
199/* ================================================================ */
200/* Gameinfo */
201/* ================================================================ */
202
203
204/*
205 * Initialize the structure.
206 */
207
208void
209gameinfo_clear(Gameinfo *gameinfo)
210{
211 gnugo_clear_board(board_size);
212 gameinfo->handicap = 0;
213 gameinfo->to_move = BLACK;
214 sgftree_clear(&gameinfo->game_record);
215
216 /* Info relevant to the computer player. */
217 gameinfo->computer_player = WHITE; /* Make an assumption. */
218}
219
220
221/*
222 * Print a gameinfo.
223 */
224
225void
226gameinfo_print(Gameinfo *gameinfo)
227{
228 printf("Board Size: %d\n", board_size);
229 printf("Handicap %d\n", gameinfo->handicap);
230 printf("Komi: %.1f\n", komi);
231 printf("Move Number: %d\n", movenum);
232 printf("To Move: %s\n", color_to_string(gameinfo->to_move));
233
0cb00f5b 234 printf("Opponent plays: ");
7eeb782e
AT
235 if (gameinfo->computer_player == WHITE)
236 printf("White\n");
237 else if (gameinfo->computer_player == BLACK)
238 printf("Black\n");
239 else if (gameinfo->computer_player == EMPTY)
240 printf("Both (solo)\n");
241 else
242 printf("Nobody\n");
243}
244
245/*
246 * Play the moves in an SGF tree. Walk the main variation, actioning
247 * the properties into the playing board.
248 *
249 * Returns the color of the next move to be made. The returned color
250 * being EMPTY signals a failure to load the file.
251 *
252 * Head is an sgf tree.
253 * Untilstr is an optional string of the form either 'L12' or '120'
254 * which tells it to stop playing at that move or move-number.
255 * When debugging, this is the location of the move being examined.
256 */
257
258int
259gameinfo_play_sgftree_rot(Gameinfo *gameinfo, SGFTree *tree,
260 const char *untilstr, int orientation)
261{
262 int bs;
263 int next = BLACK;
264 int untilmove = -1; /* Neither a valid move nor pass. */
265 int until = 9999;
266
267 if (!sgfGetIntProperty(tree->root, "SZ", &bs))
268 bs = 19;
269
270 if (!check_boardsize(bs, stderr))
271 return EMPTY;
272
273 handicap = 0;
274 if (sgfGetIntProperty(tree->root, "HA", &handicap) && handicap > 1)
275 next = WHITE;
276 gameinfo->handicap = handicap;
277
278 if (handicap > bs * bs - 1 || handicap < 0) {
279 gprintf(" Handicap HA[%d] is unreasonable.\n Modify SGF file.\n",
280 handicap);
281 return EMPTY;
282 }
283
284 gnugo_clear_board(bs);
285
286 if (!sgfGetFloatProperty(tree->root, "KM", &komi)) {
287 if (gameinfo->handicap == 0)
288 komi = 5.5;
289 else
290 komi = 0.5;
291 }
292
293 /* Now we can safely parse the until string (which depends on board size). */
294 if (untilstr) {
295 if (*untilstr > '0' && *untilstr <= '9') {
296 until = atoi(untilstr);
297 DEBUG(DEBUG_LOADSGF, "Loading until move %d\n", until);
298 }
299 else {
300 untilmove = string_to_location(board_size, untilstr);
301 DEBUG(DEBUG_LOADSGF, "Loading until move at %1m\n", untilmove);
302 }
303 }
304
305 /* Finally, we iterate over all the properties of all the
306 * nodes, actioning them. We follow only the 'child' pointers,
307 * as we have no interest in variations.
308 *
309 * The sgf routines map AB[aa][bb][cc] into AB[aa]AB[bb]AB[cc]
310 */
311 for (tree->lastnode = NULL; sgftreeForward(tree);) {
312 SGFProperty *prop;
313 int move;
314
315 for (prop = tree->lastnode->props; prop; prop = prop->next) {
316 DEBUG(DEBUG_LOADSGF, "%c%c[%s]\n",
317 prop->name & 0xff, (prop->name >> 8), prop->value);
318 switch (prop->name) {
319 case SGFAB:
320 case SGFAW:
321 /* Generally the last move is unknown when the AB or AW
322 * properties are encountered. These are used to set up
323 * a board position (diagram) or to place handicap stones
324 * without reference to the order in which the stones are
325 * placed on the board.
326 */
327 move = rotate1(get_sgfmove(prop), orientation);
328 if (board[move] != EMPTY)
329 gprintf("Illegal SGF! attempt to add a stone at occupied point %1m\n",
330 move);
331 else
332 add_stone(move, prop->name == SGFAB ? BLACK : WHITE);
333 break;
334
335 case SGFPL:
336 /* Due to a bad comment in the SGF FF3 definition (in the
337 * "Alphabetical list of properties" section) some
338 * applications encode the colors with 1 for black and 2 for
339 * white.
340 */
341 if (prop->value[0] == 'w'
342 || prop->value[0] == 'W'
343 || prop->value[0] == '2')
344 next = WHITE;
345 else
346 next = BLACK;
347 /* following really should not be needed for proper sgf file */
348 if (stones_on_board(GRAY) == 0 && next == WHITE) {
349 place_fixed_handicap(gameinfo->handicap);
350 sgfOverwritePropertyInt(tree->root, "HA", handicap);
351 }
352 break;
353
354 case SGFW:
355 case SGFB:
356 next = prop->name == SGFW ? WHITE : BLACK;
357 /* following really should not be needed for proper sgf file */
358 if (stones_on_board(GRAY) == 0 && next == WHITE) {
359 place_fixed_handicap(gameinfo->handicap);
360 sgfOverwritePropertyInt(tree->root, "HA", handicap);
361 }
362
363 move = get_sgfmove(prop);
364 if (move == untilmove || movenum == until - 1) {
365 gameinfo->to_move = next;
366 /* go back so that variant will be added to the proper node */
367 sgftreeBack(tree);
368 return next;
369 }
370
371 move = rotate1(move, orientation);
372 if (move == PASS_MOVE || board[move] == EMPTY) {
373 gnugo_play_move(move, next);
374 next = OTHER_COLOR(next);
375 }
376 else {
377 gprintf("WARNING: Move off board or on occupied position found in sgf-file.\n");
378 gprintf("Move at %1m ignored, trying to proceed.\n", move);
379 gameinfo->to_move = next;
380 return next;
381 }
382
383 break;
384
385 case SGFIL:
386 /* The IL property is not a standard SGF property but
387 * is used by GNU Go to mark illegal moves. If a move
388 * is found marked with the IL property which is a ko
389 * capture then that ko capture is deemed illegal and
390 * (board_ko_i, board_ko_j) is set to the location of
391 * the ko.
392 */
393 move = rotate1(get_sgfmove(prop), orientation);
394
395 if (board_size > 1)
396 {
397 int move_color;
398
399 if (ON_BOARD(NORTH(move)))
400 move_color = OTHER_COLOR(board[NORTH(move)]);
401 else
402 move_color = OTHER_COLOR(board[SOUTH(move)]);
403 if (is_ko(move, move_color, NULL))
404 board_ko_pos = move;
405 }
406 break;
407 }
408 }
409 }
410
411 gameinfo->to_move = next;
412 return next;
413}
414
415/* Same as previous function, using standard orientation */
416
417int
418gameinfo_play_sgftree(Gameinfo *gameinfo, SGFTree *tree, const char *untilstr)
419{
420 return gameinfo_play_sgftree_rot(gameinfo, tree, untilstr, 0);
421}
422
423
424
425/*
426 * Local Variables:
427 * tab-width: 8
428 * c-basic-offset: 2
429 * End:
430 */