Added ANSI mode with separate flags for `ansi` and `strictansi`.
[sgk-go] / interface / play_ansi.c
CommitLineData
3ebb0002
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 <ctype.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32#if READLINE
33#include <readline/history.h>
34#include <readline/readline.h>
35#endif
36
37#include "gg_utils.h"
38#include "interface.h"
39#include "liberty.h"
40#include "sgftree.h"
41
42#define DEBUG_COMMANDS "\
43capture <pos> try to capture indicated group\n\
44dead Toggle display of dead stones\n\
45defend <pos> try to defend indicated group\n\
46listdragons print dragon info \n\
47showarea display area\n\
48showdragons display dragons\n\
49showmoyo display moyo\n\
50showterri display territory\n\
51"
52
8c1c3011
AT
53/* ANSI color codes */
54#define RESETCOLOR "\x1B[0m"
55#define WHITECOLOR "\x1B[41m"
56#define BLACKCOLOR "\x1B[44m"
57#define TILE1COLOR "\x1B[47m"
58#define TILE2COLOR "\x1B[107m"
59#define TILE3COLOR "\x1B[43m"
60
61/* some options for the ansi interface */
3ebb0002
AT
62static int opt_showboard = 1;
63static int showdead = 0;
64static SGFTree sgftree;
65static int resignation_allowed;
66
67static int clock_on = 0;
68
69/* Unreasonable score used to detect missing information. */
70#define NO_SCORE 4711
71/* Keep track of the score estimated before the last computer move. */
72static int current_score_estimate = NO_SCORE;
73
74static void do_play_ansi(Gameinfo* gameinfo, bool strict);
8c1c3011
AT
75static int ansi_endgame(Gameinfo* gameinfo, int reason, bool strict);
76static void ansi_count(Gameinfo* gameinfo, bool strict);
3ebb0002
AT
77static void showcapture(char* line);
78static void showdefense(char* line);
8c1c3011
AT
79static void ansi_goto(Gameinfo* gameinfo, char* line);
80static void ansi_free_handicap(Gameinfo* gameinfo, char* handicap_string, bool strict);
3ebb0002
AT
81
82/* If sgf game info is written can't reset parameters like handicap, etc. */
83static int sgf_initialized;
84
85/*
8c1c3011 86 * Create letterbar for the top and bottom of the ANSI board.
3ebb0002
AT
87 */
88
89static void
90make_letterbar(int boardsize, char* letterbar)
91{
92 int i, letteroffset;
93 char spaces[64];
94 char letter[64];
95
96 if (boardsize <= 25)
97 strcpy(spaces, " ");
98 strcpy(letterbar, " ");
99
100 for (i = 0; i < boardsize; i++) {
101 letteroffset = 'A';
102 if (i + letteroffset >= 'I')
103 letteroffset++;
104 strcat(letterbar, spaces);
105 sprintf(letter, "%c", i + letteroffset);
106 strcat(letterbar, letter);
107 }
108}
109
110/* This array contains +'s and -'s for the empty board positions.
111 * hspot_size contains the board size that the grid has been
112 * initialized to.
113 */
114
115static int hspot_size;
116static char hspots[MAX_BOARD][MAX_BOARD];
117
118/*
119 * Mark the handicap spots on the board.
120 */
121
122static void
123set_handicap_spots(int boardsize)
124{
125 if (hspot_size == boardsize)
126 return;
127
128 hspot_size = boardsize;
129
130 memset(hspots, '.', sizeof(hspots));
131
132 if (boardsize == 5) {
133 /* place the outer 4 */
134 hspots[1][1] = '+';
135 hspots[boardsize - 2][1] = '+';
136 hspots[1][boardsize - 2] = '+';
137 hspots[boardsize - 2][boardsize - 2] = '+';
138 /* and the middle one */
139 hspots[boardsize / 2][boardsize / 2] = '+';
140 return;
141 }
142
143 if (!(boardsize % 2)) {
144 /* If the board size is even, no center handicap spots. */
145 if (boardsize > 2 && boardsize < 12) {
146 /* Place the outer 4 only. */
147 hspots[2][2] = '+';
148 hspots[boardsize - 3][2] = '+';
149 hspots[2][boardsize - 3] = '+';
150 hspots[boardsize - 3][boardsize - 3] = '+';
151 } else {
152 /* Place the outer 4 only. */
153 hspots[3][3] = '+';
154 hspots[boardsize - 4][3] = '+';
155 hspots[3][boardsize - 4] = '+';
156 hspots[boardsize - 4][boardsize - 4] = '+';
157 }
158 } else {
159 /* Uneven board size */
160 if (boardsize > 2 && boardsize < 12) {
161 /* Place the outer 4... */
162 hspots[2][2] = '+';
163 hspots[boardsize - 3][2] = '+';
164 hspots[2][boardsize - 3] = '+';
165 hspots[boardsize - 3][boardsize - 3] = '+';
166
167 /* ...and the middle one. */
168 hspots[boardsize / 2][boardsize / 2] = '+';
169 } else if (boardsize > 12) {
170 /* Place the outer 4... */
171 hspots[3][3] = '+';
172 hspots[boardsize - 4][3] = '+';
173 hspots[3][boardsize - 4] = '+';
174 hspots[boardsize - 4][boardsize - 4] = '+';
175
176 /* ...and the inner 4... */
177 hspots[3][boardsize / 2] = '+';
178 hspots[boardsize / 2][3] = '+';
179 hspots[boardsize / 2][boardsize - 4] = '+';
180 hspots[boardsize - 4][boardsize / 2] = '+';
181
182 /* ...and the middle one. */
183 hspots[boardsize / 2][boardsize / 2] = '+';
184 }
185 }
186
187 return;
188}
189
3ebb0002 190/*
8c1c3011 191 * Display the board position when playing in ANSI.
3ebb0002
AT
192 */
193
194static void
8c1c3011 195ansi_showboard(bool strict)
3ebb0002
AT
196{
197 int i, j;
198 char letterbar[64];
199 int last_pos_was_move;
200 int pos_is_move;
201 int dead;
202 int last_move = get_last_move();
203
204 make_letterbar(board_size, letterbar);
205 set_handicap_spots(board_size);
206
207 printf("\n");
208 printf(" White (" WHITECOLOR " " RESETCOLOR ") has captured %d pieces\n", black_captured);
209 printf(" Black (" BLACKCOLOR " " RESETCOLOR ") has captured %d pieces\n", white_captured);
210 if (showscore) {
211 if (current_score_estimate == NO_SCORE)
212 printf(" No score estimate is available yet.\n");
213 else if (current_score_estimate < 0)
214 printf(" Estimated score: Black is ahead by %d\n",
215 -current_score_estimate);
216 else if (current_score_estimate > 0)
217 printf(" Estimated score: White is ahead by %d\n",
218 current_score_estimate);
219 else
220 printf(" Estimated score: Even!\n");
221 }
222
223 printf("\n");
224
225 fflush(stdout);
226 printf("%s", letterbar);
227
228 if (get_last_player() != EMPTY) {
229 gfprintf(stdout, " Last move: %s %1m",
230 get_last_player() == WHITE ? "White" : "Black",
231 last_move);
232 }
233
234 printf("\n");
235 fflush(stdout);
236
237 for (i = 0; i < board_size; i++) {
238 printf(" %2d", board_size - i);
239 last_pos_was_move = 0;
240 for (j = 0; j < board_size; j++) {
241 if (POS(i, j) == last_move)
242 pos_is_move = 128;
243 else
244 pos_is_move = 0;
245 dead = (dragon_status(POS(i, j)) == DEAD) && showdead;
246 switch (BOARD(i, j) + pos_is_move + last_pos_was_move) {
3ebb0002 247 case BLACK:
8c1c3011 248 case BLACK + 256:
3ebb0002
AT
249 printf(BLACKCOLOR " " RESETCOLOR);
250 last_pos_was_move = 0;
251 break;
252 case WHITE:
8c1c3011 253 case WHITE + 256:
3ebb0002
AT
254 printf(WHITECOLOR " " RESETCOLOR);
255 last_pos_was_move = 0;
256 break;
257 case BLACK + 128:
258 printf(BLACKCOLOR "()" RESETCOLOR);
259 last_pos_was_move = 0;
260 break;
261 case WHITE + 128:
262 printf(WHITECOLOR "()" RESETCOLOR);
263 last_pos_was_move = 0;
264 break;
8c1c3011
AT
265 case EMPTY:
266 case EMPTY + 128:
3ebb0002
AT
267 case EMPTY + 256:
268 if (i % 2 == 0) {
269 if (j % 2 == 0) {
8c1c3011 270 printf(TILE1COLOR " " RESETCOLOR);
3ebb0002 271 } else {
8c1c3011
AT
272 if (strict) {
273 printf(TILE3COLOR " " RESETCOLOR);
274 } else {
275 printf(TILE2COLOR " " RESETCOLOR);
276 }
3ebb0002
AT
277 }
278 } else {
279 if (j % 2 == 0) {
8c1c3011
AT
280 if (strict) {
281 printf(TILE3COLOR " " RESETCOLOR);
282 } else {
283 printf(TILE2COLOR " " RESETCOLOR);
284 }
3ebb0002 285 } else {
8c1c3011 286 printf(TILE1COLOR " " RESETCOLOR);
3ebb0002
AT
287 }
288 }
289 last_pos_was_move = 0;
290 break;
3ebb0002
AT
291 default:
292 fprintf(stderr, "Illegal board value %d\n", (int)BOARD(i, j));
293 exit(EXIT_FAILURE);
294 break;
295 }
296 }
297
298 if (last_pos_was_move == 0) {
299 if (board_size > 10)
300 printf(" %2d", board_size - i);
301 else
302 printf(" %1d", board_size - i);
303 } else {
304 if (board_size > 10)
305 printf("%2d", board_size - i);
306 else
307 printf("%1d", board_size - i);
308 }
309 printf("\n");
310 }
311
312 fflush(stdout);
313 printf("%s\n\n", letterbar);
314 fflush(stdout);
315
316 if (clock_on) {
317 clock_print(WHITE);
318 clock_print(BLACK);
319 }
320
8c1c3011 321} /* end ansi_showboard */
3ebb0002
AT
322
323/*
324 * command help
325 */
326
327static void
328show_commands(void)
329{
330 printf("\nCommands:\n");
331 printf(" back Take back your last move\n");
332 printf(" boardsize Set boardsize (on move 1 only)\n");
333 printf(" comment Write a comment to outputfile\n");
334 printf(" depth <num> Set depth for reading\n");
335 printf(" display Display game board\n");
336 printf(" exit Exit GNU Go\n");
337 printf(" force <move> Force a move for current color\n");
338 printf(" forward Go to next node in game tree\n");
339 printf(" goto <movenum> Go to movenum in game tree\n");
340 printf(" level <amount> Playing level (default = 10)\n");
341 printf(" handicap <num> Set fixed handicap (on move 1 only)\n");
342 printf(" freehandicap <num> Place free handicap (on move 1 only)\n");
343 printf(" Omit <num> to place handicap yourself\n");
344 printf(" help Display this help menu\n");
345 printf(" helpdebug Display debug help menu\n");
346 printf(" info Display program settings\n");
347 printf(" komi Set komi (on move 1 only)\n");
348 printf(" last Goto last node in game tree\n");
349 printf(" pass Pass on your move\n");
350 printf(" play <num> Play <num> moves\n");
351 printf(" playblack Play as Black (switch if White)\n");
352 printf(" playwhite Play as White (switch if Black)\n");
353 printf(" quit Exit GNU Go\n");
354 printf(" resign Resign the current game\n");
355 printf(" save <file> Save the current game\n");
356 printf(" load <file> Load a game from file\n");
357 printf(" score Toggle display of score On/Off\n");
358 printf(" showboard Toggle display of board On/Off\n");
359 printf(" switch Switch the color you are playing\n");
360 printf(" undo Take the last move back (same as back)\n");
361 printf(" <move> A move of the format <letter><number>");
362 printf("\n");
363}
364
365enum commands { INVALID = -1,
366 END,
367 EXIT,
368 QUIT,
369 RESIGN,
370 PASS,
371 MOVE,
372 FORCE,
373 SWITCH,
374 PLAY,
375 PLAYBLACK,
376 PLAYWHITE,
377 SETHANDICAP,
378 FREEHANDICAP,
379 SETBOARDSIZE,
380 SETKOMI,
381 SETDEPTH,
382 INFO,
383 DISPLAY,
384 SHOWBOARD,
385 HELP,
386 UNDO,
387 COMMENT,
388 SCORE,
389 CMD_DEAD,
390 CMD_BACK,
391 CMD_FORWARD,
392 CMD_LAST,
393 CMD_CAPTURE,
394 CMD_DEFEND,
395 CMD_HELPDEBUG,
396 CMD_SHOWAREA,
397 CMD_SHOWMOYO,
398 CMD_SHOWTERRI,
399 CMD_GOTO,
400 CMD_SAVE,
401 CMD_LOAD,
402 CMD_SHOWDRAGONS,
403 CMD_LISTDRAGONS,
404 SETLEVEL,
405 NEW,
406 COUNT,
407 CONTINUE
408};
409
410/*
411 * Check if we have a valid command.
412 */
413
414static int
415get_command(char* command)
416{
417 char c;
418 int d;
419
420 /* Check to see if a move was input. */
421 if (!((sscanf(command, "%c%d", &c, &d) != 2)
422 || ((c = toupper((int)c)) < 'A')
423 || ((c = toupper((int)c)) > 'Z')
424 || (c == 'I')))
425 return MOVE;
426
427 /* help shortcut */
428 if (command[0] == '?')
429 return HELP;
430
431 /* Kill leading spaces. */
432 while (command[0] == ' ')
433 command++;
434
435 if (!strncmp(command, "playblack", 9))
436 return PLAYBLACK;
437 if (!strncmp(command, "playwhite", 9))
438 return PLAYWHITE;
439 if (!strncmp(command, "showboard", 9))
440 return SHOWBOARD;
441 if (!strncmp(command, "showdragons", 9))
442 return CMD_SHOWDRAGONS;
443 if (!strncmp(command, "listdragons", 9))
444 return CMD_LISTDRAGONS;
445 if (!strncmp(command, "boardsize", 9))
446 return SETBOARDSIZE;
447 if (!strncmp(command, "freehandicap", 9))
448 return FREEHANDICAP;
449 if (!strncmp(command, "handicap", 5))
450 return SETHANDICAP;
451 if (!strncmp(command, "display", 7))
452 return DISPLAY;
453 if (!strncmp(command, "helpdebug", 7))
454 return CMD_HELPDEBUG;
455 if (!strncmp(command, "resign", 6))
456 return RESIGN;
457 if (!strncmp(command, "showmoyo", 6))
458 return CMD_SHOWMOYO;
459 if (!strncmp(command, "showterri", 6))
460 return CMD_SHOWTERRI;
461 if (!strncmp(command, "showarea", 6))
462 return CMD_SHOWAREA;
463 if (!strncmp(command, "depth", 5))
464 return SETDEPTH;
465 if (!strncmp(command, "switch", 5))
466 return SWITCH;
467 if (!strncmp(command, "komi", 4))
468 return SETKOMI;
469 if (!strncmp(command, "play", 4))
470 return PLAY;
471 if (!strncmp(command, "info", 4))
472 return INFO;
473 if (!strncmp(command, "force", 4))
474 return FORCE;
475 if (!strncmp(command, "level", 5))
476 return SETLEVEL;
477 if (!strncmp(command, "pass", 4))
478 return PASS;
479 if (!strncmp(command, "save", 3))
480 return CMD_SAVE;
481 if (!strncmp(command, "load", 3))
482 return CMD_LOAD;
483 if (!strncmp(command, "end", 3))
484 return END;
485 if (!strncmp(command, "move", 3))
486 return MOVE;
487 if (!strncmp(command, "undo", 3))
488 return UNDO;
489 if (!strncmp(command, "comment", 3))
490 return COMMENT;
491 if (!strncmp(command, "score", 3))
492 return SCORE;
493 if (!strncmp(command, "dead", 3))
494 return CMD_DEAD;
495 if (!strncmp(command, "capture", 3))
496 return CMD_CAPTURE;
497 if (!strncmp(command, "defend", 3))
498 return CMD_DEFEND;
499 if (!strncmp(command, "exit", 4))
500 return EXIT;
501 if (!strncmp(command, "quit", 4))
502 return QUIT;
503 if (!strncmp(command, "help", 1))
504 return HELP;
505 if (!strncmp(command, "back", 2))
506 return CMD_BACK;
507 if (!strncmp(command, "forward", 2))
508 return CMD_FORWARD;
509 if (!strncmp(command, "last", 2))
510 return CMD_LAST;
511 if (!strncmp(command, "goto", 2))
512 return CMD_GOTO;
513 if (!strncmp(command, "game", 2))
514 return NEW;
515 if (!strncmp(command, "count", 3))
516 return COUNT;
517 if (!strncmp(command, "continue", 4))
518 return CONTINUE;
519
520 /* No valid command was found. */
521 return INVALID;
522}
523
524/*
525 * Write sgf root node.
526 */
527
528static void
529init_sgf(Gameinfo* ginfo)
530{
531 if (sgf_initialized)
532 return;
533 sgf_initialized = 1;
534
535 sgf_write_header(sgftree.root, 1, get_random_seed(), komi,
536 ginfo->handicap, get_level(), chinese_rules);
537 if (ginfo->handicap > 0)
538 sgffile_recordboard(sgftree.root);
539}
540
541/*
542 * Generate the computer move.
543 */
544
545static int
8c1c3011 546computer_move(Gameinfo* gameinfo, int* passes, bool strict)
3ebb0002
AT
547{
548 int move;
549 float move_value;
550 int resign;
551 int resignation_declined = 0;
552 float upper_bound, lower_bound;
553
554 init_sgf(gameinfo);
555
556 /* Generate computer move. */
557 if (autolevel_on)
558 adjust_level_offset(gameinfo->to_move);
559 move = genmove(gameinfo->to_move, &move_value, &resign);
560 if (resignation_allowed && resign) {
8c1c3011 561 int state = ansi_endgame(gameinfo, 2, strict);
3ebb0002
AT
562 if (state != -1)
563 return state;
564
565 /* The opponent declined resignation. Remember not to resign again. */
566 resignation_allowed = 0;
567 resignation_declined = 1;
568 }
569
570 if (showscore) {
571 gnugo_estimate_score(&upper_bound, &lower_bound);
572 current_score_estimate = (int)((lower_bound + upper_bound) / 2.0);
573 }
574
575 mprintf("%s(%d): %1m\n", color_to_string(gameinfo->to_move),
576 movenum + 1, move);
577 if (is_pass(move))
578 (*passes)++;
579 else
580 *passes = 0;
581
582 gnugo_play_move(move, gameinfo->to_move);
583 sgffile_add_debuginfo(sgftree.lastnode, move_value);
584 sgftreeAddPlay(&sgftree, gameinfo->to_move, I(move), J(move));
585 if (resignation_declined)
586 sgftreeAddComment(&sgftree, "GNU Go resignation was declined");
587 sgffile_output(&sgftree);
588
589 gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
590 return 0;
591}
592
593/*
594 * Make a move.
595 */
596
597static int
8c1c3011 598do_move(Gameinfo* gameinfo, char* command, int* passes, int force, bool strict)
3ebb0002
AT
599{
600 int move = string_to_location(board_size, command);
601
602 if (move == NO_MOVE) {
603 printf("\nInvalid move: %s\n", command);
604 return 0;
605 }
606
607 if (!is_allowed_move(move, gameinfo->to_move)) {
608 printf("\nIllegal move: %s", command);
609 return 0;
610 }
611
612 *passes = 0;
613 TRACE("\nyour move: %1m\n\n", move);
614 init_sgf(gameinfo);
615 gnugo_play_move(move, gameinfo->to_move);
616 sgffile_add_debuginfo(sgftree.lastnode, 0.0);
617 sgftreeAddPlay(&sgftree, gameinfo->to_move, I(move), J(move));
618 sgffile_output(&sgftree);
619
620 if (opt_showboard) {
8c1c3011 621 ansi_showboard(strict);
3ebb0002
AT
622 printf("GNU Go is thinking...\n");
623 }
624
625 if (force) {
626 gameinfo->computer_player = OTHER_COLOR(gameinfo->computer_player);
627 gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
628 sgftreeAddComment(&sgftree, "forced");
629 return 0;
630 }
631
632 gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
8c1c3011 633 return computer_move(gameinfo, passes, strict);
3ebb0002
AT
634}
635
636/*
637 * Make a pass.
638 */
639
640static int
8c1c3011 641do_pass(Gameinfo* gameinfo, int* passes, int force, bool strict)
3ebb0002
AT
642{
643 (*passes)++;
644 init_sgf(gameinfo);
645 gnugo_play_move(PASS_MOVE, gameinfo->to_move);
646 sgffile_add_debuginfo(sgftree.lastnode, 0.0);
647 sgftreeAddPlay(&sgftree, gameinfo->to_move, -1, -1);
648 sgffile_output(&sgftree);
649
650 gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
651 if (force) {
652 gameinfo->computer_player = OTHER_COLOR(gameinfo->computer_player);
653 sgftreeAddComment(&sgftree, "forced");
654 return 0;
655 }
656
8c1c3011 657 return computer_move(gameinfo, passes, strict);
3ebb0002
AT
658}
659
660/*
661 * Play a game using ANSI client.
662 */
663
664void play_ansi(SGFTree* tree, Gameinfo* gameinfo, char* filename, char* until, bool strict)
665{
666 int sz;
667
668 setvbuf(stdout, (char*)NULL, _IONBF, 0); /* No buffering. */
669
670 sgftree = *tree;
671
672 if (filename) {
673 /* No need to check for failure here since that was already done
674 * when it was loaded in main().
675 *
676 * FIXME: Why do we load the game again?
677 */
678 gameinfo_play_sgftree(gameinfo, &sgftree, until);
679 sgf_initialized = 1;
680 } else {
681 if (sgfGetIntProperty(sgftree.root, "SZ", &sz))
682 gnugo_clear_board(sz);
683 if (gameinfo->handicap == 0)
684 gameinfo->to_move = BLACK;
685 else {
686 gameinfo->handicap = place_fixed_handicap(gameinfo->handicap);
687 gameinfo->to_move = WHITE;
688 }
689 sgf_initialized = 0;
690 }
691
692 do_play_ansi(gameinfo, strict);
693 printf("\nThanks! for playing GNU Go.\n\n");
694
695 /* main() frees the tree and we might have changed it. */
696 *tree = sgftree;
697}
698
699void do_play_ansi(Gameinfo* gameinfo, bool strict)
700{
701 int m, num;
702 float fnum;
703 int passes = 0; /* two passes and its over */
704 int tmp;
705 char line[80];
706 char* line_ptr = line;
707 char* command;
708 char* tmpstring;
709 int state = 1;
710
711 if (have_time_settings())
712 clock_on = 1;
713
714 while (state == 1) {
715 state = 0;
716
717 /* No score is estimated yet. */
718 current_score_estimate = NO_SCORE;
719
720 /* Allow resignation at interface level (the engine may still be not
721 * allowed to resign.
722 */
723 resignation_allowed = 1;
724
725 if (strict) {
726 printf("\nBeginning strict-ANSI mode game.\n\n");
727 } else {
728 printf("\nBeginning ANSI mode game.\n\n");
729 }
730 gameinfo_print(gameinfo);
731
732 /* Does the computer play first? If so, make a move. */
733 if (gameinfo->computer_player == gameinfo->to_move)
8c1c3011 734 state = computer_move(gameinfo, &passes, strict);
3ebb0002 735
8c1c3011 736 /* main ANSI Play loop */
3ebb0002
AT
737 while (state == 0) {
738 /* Display game board. */
739 if (opt_showboard)
8c1c3011 740 ansi_showboard(strict);
3ebb0002
AT
741
742#if !READLINE
743 /* Print the prompt */
744 mprintf("%s(%d): ", color_to_string(gameinfo->to_move), movenum + 1);
745
746 /* Read a line of input. */
747 line_ptr = line;
748 if (!fgets(line, 80, stdin))
749 return;
750#else
751 snprintf(line, 79, "%s(%d): ",
752 color_to_string(gameinfo->to_move), movenum + 1);
753 if (!(line_ptr = readline(line)))
754 return;
755
756 add_history(line_ptr);
757#endif
758
759 while (state == 0
760 && (command = strtok(line_ptr, ";"), line_ptr = 0, command)) {
761 /* Get the command or move. */
762 switch (get_command(command)) {
763 case RESIGN:
8c1c3011 764 state = ansi_endgame(gameinfo, 1, strict);
3ebb0002
AT
765 break;
766
767 case END:
768 case EXIT:
769 case QUIT:
770 return;
771
772 case HELP:
773 show_commands();
774 break;
775
776 case CMD_HELPDEBUG:
777 printf(DEBUG_COMMANDS);
778 break;
779
780 case SHOWBOARD:
781 opt_showboard = !opt_showboard;
782 break;
783
784 case INFO:
785 printf("\n");
786 gameinfo_print(gameinfo);
787 break;
788
789 case SETBOARDSIZE:
790 if (sgf_initialized) {
791 printf("Boardsize cannot be changed after record is started!\n");
792 break;
793 }
794 command += 10;
795 if (sscanf(command, "%d", &num) != 1) {
796 printf("\nInvalid command syntax!\n");
797 break;
798 }
799 if (!check_boardsize(num, stdout))
800 break;
801 /* Init board. */
802 board_size = num;
803 clear_board();
804 /* In case max handicap changes on smaller board. */
805 gameinfo->handicap = place_fixed_handicap(gameinfo->handicap);
806 sgfOverwritePropertyInt(sgftree.root, "SZ", board_size);
807 sgfOverwritePropertyInt(sgftree.root, "HA", gameinfo->handicap);
808 break;
809
810 case SETHANDICAP:
811 if (sgf_initialized) {
812 printf("Handicap cannot be changed after game is started!\n");
813 break;
814 }
815 command += 9;
816 if (sscanf(command, "%d", &num) != 1) {
817 printf("\nInvalid command syntax!\n");
818 break;
819 }
820 if (num < 0 || num > MAX_HANDICAP) {
821 printf("\nInvalid handicap: %d\n", num);
822 break;
823 }
824 /* Init board. */
825 clear_board();
826 /* Place stones on board but don't record sgf
827 * in case we change more info. */
828 gameinfo->handicap = place_fixed_handicap(num);
829 printf("\nSet handicap to %d\n", gameinfo->handicap);
830 gameinfo->to_move = (gameinfo->handicap ? WHITE : BLACK);
831 break;
832
833 case FREEHANDICAP:
834 if (sgf_initialized) {
835 printf("Handicap cannot be changed after game is started!\n");
836 break;
837 }
838 while (*command && *command != ' ')
839 command++;
8c1c3011 840 ansi_free_handicap(gameinfo, command, strict);
3ebb0002
AT
841 break;
842
843 case SETKOMI:
844 if (sgf_initialized) {
845 printf("Komi cannot be modified after game record is started!\n");
846 break;
847 }
848 command += 5;
849 if (sscanf(command, "%f", &fnum) != 1) {
850 printf("\nInvalid command syntax!\n");
851 break;
852 }
853 komi = fnum;
854 printf("\nSet Komi to %.1f\n", komi);
855 break;
856
857 case SETDEPTH:
858 command += 6;
859 if (sscanf(command, "%d", &num) != 1) {
860 printf("\nInvalid command syntax!\n");
861 break;
862 }
863 mandated_depth = num;
864 printf("\nSet depth to %d\n", mandated_depth);
865 break;
866
867 case SETLEVEL:
868 command += 6;
869 if (sscanf(command, "%d", &num) != 1) {
870 printf("\nInvalid command syntax!\n");
871 break;
872 }
873 set_level(num);
874 printf("\nSet level to %d\n", num);
875 break;
876
877 case DISPLAY:
878 if (!opt_showboard)
8c1c3011 879 ansi_showboard(strict);
3ebb0002
AT
880 break;
881
882 case FORCE:
883 command += 6; /* skip the force part... */
884 switch (get_command(command)) {
885 case MOVE:
8c1c3011 886 state = do_move(gameinfo, command, &passes, 1, strict);
3ebb0002
AT
887 break;
888 case PASS:
8c1c3011 889 state = do_pass(gameinfo, &passes, 1, strict);
3ebb0002
AT
890 break;
891 default:
892 printf("Illegal forced move: %s %d\n", command,
893 get_command(command));
894 break;
895 }
896 break;
897
898 case MOVE:
8c1c3011 899 state = do_move(gameinfo, command, &passes, 0, strict);
3ebb0002
AT
900 break;
901
902 case PASS:
8c1c3011 903 state = do_pass(gameinfo, &passes, 0, strict);
3ebb0002
AT
904 break;
905
906 case PLAY:
907 command += 5;
908 if (sscanf(command, "%d", &num) != 1) {
909 printf("\nInvalid command syntax!\n");
910 break;
911 }
912 if (num >= 0)
913 for (m = 0; m < num; m++) {
914 gameinfo->computer_player
915 = OTHER_COLOR(gameinfo->computer_player);
8c1c3011 916 state = computer_move(gameinfo, &passes, strict);
3ebb0002
AT
917 if (state)
918 break;
919 if (passes >= 2)
920 break;
921 }
922 else {
923 printf("\nInvalid number of moves specified: %d\n", num);
924 break;
925 }
926 break;
927
928 case PLAYBLACK:
929 if (gameinfo->computer_player == WHITE)
930 gameinfo->computer_player = BLACK;
931 if (gameinfo->computer_player == gameinfo->to_move)
8c1c3011 932 state = computer_move(gameinfo, &passes, strict);
3ebb0002
AT
933 break;
934
935 case PLAYWHITE:
936 if (gameinfo->computer_player == BLACK)
937 gameinfo->computer_player = WHITE;
938 if (gameinfo->computer_player == gameinfo->to_move)
8c1c3011 939 state = computer_move(gameinfo, &passes, strict);
3ebb0002
AT
940 break;
941
942 case SWITCH:
943 gameinfo->computer_player = OTHER_COLOR(gameinfo->computer_player);
8c1c3011 944 state = computer_move(gameinfo, &passes, strict);
3ebb0002
AT
945 break;
946
947 case UNDO:
948 case CMD_BACK:
949 if (undo_move(1)) {
950 sgftreeAddComment(&sgftree, "undone");
951 sgftreeBack(&sgftree);
952 gameinfo->to_move = OTHER_COLOR(gameinfo->to_move);
953 } else
954 printf("\nCan't undo.\n");
955 break;
956
957 case CMD_FORWARD:
958 if (sgftreeForward(&sgftree))
959 gameinfo->to_move = gnugo_play_sgfnode(sgftree.lastnode,
960 gameinfo->to_move);
961 else
962 printf("\nEnd of game tree.\n");
963 break;
964
965 case CMD_LAST:
966 while (sgftreeForward(&sgftree))
967 gameinfo->to_move = gnugo_play_sgfnode(sgftree.lastnode,
968 gameinfo->to_move);
969 break;
970
971 case COMMENT:
972 printf("\nEnter comment. Press ENTER when ready.\n");
973 fgets(line, 80, stdin);
974 sgftreeAddComment(&sgftree, line);
975 break;
976
977 case SCORE:
978 showscore = !showscore;
979 if (!showscore)
980 current_score_estimate = NO_SCORE;
981 break;
982
983 case CMD_DEAD:
984 silent_examine_position(FULL_EXAMINE_DRAGONS);
985 showdead = !showdead;
986 break;
987
988 case CMD_CAPTURE:
989 strtok(command, " ");
990 showcapture(strtok(NULL, " "));
991 break;
992
993 case CMD_DEFEND:
994 strtok(command, " ");
995 showdefense(strtok(NULL, " "));
996 break;
997
998 case CMD_SHOWMOYO:
999 tmp = printmoyo;
1000 printmoyo = PRINTMOYO_MOYO;
1001 silent_examine_position(EXAMINE_DRAGONS);
1002 printmoyo = tmp;
1003 break;
1004
1005 case CMD_SHOWTERRI:
1006 tmp = printmoyo;
1007 printmoyo = PRINTMOYO_TERRITORY;
1008 silent_examine_position(EXAMINE_DRAGONS);
1009 printmoyo = tmp;
1010 break;
1011
1012 case CMD_SHOWAREA:
1013 tmp = printmoyo;
1014 printmoyo = PRINTMOYO_AREA;
1015 silent_examine_position(EXAMINE_DRAGONS);
1016 printmoyo = tmp;
1017 break;
1018
1019 case CMD_SHOWDRAGONS:
1020 silent_examine_position(EXAMINE_DRAGONS);
1021 showboard(1);
1022 break;
1023
1024 case CMD_GOTO:
1025 strtok(command, " ");
8c1c3011 1026 ansi_goto(gameinfo, strtok(NULL, " "));
3ebb0002
AT
1027 break;
1028
1029 case CMD_SAVE:
1030 strtok(command, " ");
1031 tmpstring = strtok(NULL, " ");
1032 if (tmpstring) {
1033 /* discard newline */
1034 tmpstring[strlen(tmpstring) - 1] = 0;
1035 /* make sure we are saving proper handicap */
1036 init_sgf(gameinfo);
1037 writesgf(sgftree.root, tmpstring);
1038 printf("You may resume the game");
8c1c3011 1039 printf(" with -l %s --mode ansi\n", tmpstring);
3ebb0002
AT
1040 printf("or load %s\n", tmpstring);
1041 } else
1042 printf("Please specify filename\n");
1043 break;
1044
1045 case CMD_LOAD:
1046 strtok(command, " ");
1047 tmpstring = strtok(NULL, " ");
1048 if (tmpstring) {
1049 /* discard newline */
1050 tmpstring[strlen(tmpstring) - 1] = 0;
1051 if (!sgftree_readfile(&sgftree, tmpstring)) {
1052 fprintf(stderr, "Cannot open or parse '%s'\n", tmpstring);
1053 break;
1054 }
1055 /* to avoid changing handicap etc. */
1056 if (gameinfo_play_sgftree(gameinfo, &sgftree, NULL) == EMPTY)
1057 fprintf(stderr, "Cannot load '%s'\n", tmpstring);
1058 else {
1059 sgf_initialized = 1;
1060 sgfOverwritePropertyInt(sgftree.root, "HA", gameinfo->handicap);
1061 }
1062 } else
1063 printf("Please specify a filename\n");
1064 break;
1065
1066 case CMD_LISTDRAGONS:
1067 silent_examine_position(EXAMINE_DRAGONS);
1068 show_dragons();
1069 break;
1070
1071 default:
1072 printf("\nInvalid command: %s", command);
1073 break;
1074 }
1075
1076 if (passes >= 2)
8c1c3011 1077 state = ansi_endgame(gameinfo, 0, strict);
3ebb0002
AT
1078 }
1079#if READLINE
1080 free(line_ptr);
1081#endif
1082 }
1083
1084 sgffile_output(&sgftree);
1085 passes = 0;
1086
1087 /* Play a different game next time. */
1088 update_random_seed();
1089
1090 /* Free the sgf tree and prepare for a new game. */
1091 sgfFreeNode(sgftree.root);
1092 sgftree_clear(&sgftree);
1093 sgftreeCreateHeaderNode(&sgftree, board_size, komi, gameinfo->handicap);
1094 sgf_initialized = 0;
1095
1096 gameinfo_clear(gameinfo);
1097 }
1098}
1099
1100/* Communicates with user after a game has ended. */
1101static int
8c1c3011 1102ansi_endgame(Gameinfo* gameinfo, int reason, bool strict)
3ebb0002
AT
1103{
1104 char line[80];
1105 char* line_ptr;
1106 char* command;
1107 char* tmpstring;
1108 int state = 0;
1109
1110 if (reason == 0) { /* Two passes, game is over. */
1111 who_wins(gameinfo->computer_player, stdout);
1112 printf("\nIf you disagree, we may count the game together.\n");
1113
1114 sgftreeWriteResult(&sgftree, (white_score + black_score) / 2.0, 1);
1115 } else {
1116 int color = OTHER_COLOR(gameinfo->to_move);
1117
1118 if (reason == 1) /* Our opponent has resigned. */
1119 printf("GNU Go wins by resignation.\n");
1120 else /* We have resigned. */
1121 printf("You win by resignation.\n");
1122
1123 printf("Result: %c+Resign\n\n", color == WHITE ? 'W' : 'B');
1124 sgftreeWriteResult(&sgftree, color == WHITE ? 1000.0 : -1000.0, 1);
1125 }
1126
1127 while (state == 0) {
1128 printf("You may optionally save the game as an SGF file.\n\n");
1129 printf("Type \"save <filename>\" to save,\n");
1130 if (reason == 0)
1131 printf(" \"count\" to recount,\n");
1132 else if (reason == 2)
1133 printf(" \"continue\" to decline resignation and continue the game,\n");
1134 printf(" \"quit\" to quit\n");
1135 printf(" or \"game\" to play again\n");
1136
1137 line_ptr = line;
1138 if (!fgets(line, 80, stdin))
1139 break;
1140
1141 command = strtok(line_ptr, "");
1142 switch (get_command(command)) {
1143 case CMD_SAVE:
1144 strtok(command, " ");
1145 tmpstring = strtok(NULL, " ");
1146 if (tmpstring) {
1147 /* discard newline */
1148 tmpstring[strlen(tmpstring) - 1] = 0;
1149 init_sgf(gameinfo);
1150 writesgf(sgftree.root, tmpstring);
1151 } else
1152 printf("Please specify filename\n");
1153 break;
1154
1155 case COUNT:
1156 if (reason == 0)
8c1c3011 1157 ansi_count(gameinfo, strict);
3ebb0002
AT
1158 break;
1159
1160 case CONTINUE:
1161 state = -1;
1162 break;
1163
1164 case NEW:
1165 state = 1;
1166 break;
1167
1168 case QUIT:
1169 state = 2;
1170 break;
1171
1172 default:
1173 state = 0;
1174 }
1175 }
1176
1177 return state;
1178}
1179
8c1c3011 1180/* ansi_count() scores the game.
3ebb0002
AT
1181 */
1182static void
8c1c3011 1183ansi_count(Gameinfo* gameinfo, bool strict)
3ebb0002
AT
1184{
1185 char line[12];
1186 int done = 0;
1187 int i;
1188 int xyzzy = 1;
1189
1190 printf("\nGame over. Let's count!.\n");
1191 showdead = 1;
8c1c3011 1192 ansi_showboard(strict);
3ebb0002
AT
1193 while (!done) {
1194 printf("Dead stones are marked with small letters (x,o).\n");
1195 printf("\nIf you don't agree, enter the location of a group\n");
1196 printf("to toggle its state or \"done\".\n");
1197
1198 if (!fgets(line, 12, stdin))
1199 return; /* EOF or some error */
1200
1201 for (i = 0; i < 12; i++)
1202 line[i] = tolower((int)line[i]);
1203 if (!strncmp(line, "done", 4))
1204 done = 1;
1205 else if (!strncmp(line, "quit", 4))
1206 return;
1207 else if (!strncmp(line, "xyzzy", 5)) {
1208 if (xyzzy) {
1209 printf("\nYou are in a debris room filled with stuff washed in from the\n");
1210 printf("surface. A low wide passage with cobbles becomes plugged\n");
1211 printf("with mud and debris here, but an awkward canyon leads\n");
1212 printf("upward and west. A note on the wall says:\n");
1213 printf(" Magic Word \"XYZZY\"\n\n");
1214 xyzzy = 0;
1215 } else {
1216 printf("You are inside a building, a well house for a large spring.\n\n");
1217 xyzzy = 1;
1218 }
1219 } else if (!strncmp(line, "help", 4)) {
1220 printf("\nIf there are dead stones on the board I will remove them.\n");
1221 printf("You must tell me where they are. Type the coordinates of a\n");
1222 printf("dead group, or type \"done\"\n");
1223 } else if (!strncmp(line, "undo", 4)) {
1224 printf("UNDO not allowed anymore. The status of the stones now\n");
1225 printf("toggles after entering the location of it.\n");
8c1c3011 1226 ansi_showboard(strict);
3ebb0002
AT
1227 } else {
1228 int pos = string_to_location(board_size, line);
1229 if (pos == NO_MOVE || board[pos] == EMPTY)
1230 printf("\ninvalid!\n");
1231 else {
1232 enum dragon_status status = dragon_status(pos);
1233 status = (status == DEAD) ? ALIVE : DEAD;
1234 change_dragon_status(pos, status);
8c1c3011 1235 ansi_showboard(strict);
3ebb0002
AT
1236 }
1237 }
1238 }
1239 who_wins(gameinfo->computer_player, stdout);
1240}
1241
1242static void
1243showcapture(char* line)
1244{
1245 int str;
1246 int move;
1247
1248 gg_assert(line);
1249 str = string_to_location(board_size, line);
1250 if (str == NO_MOVE || board[str] == EMPTY) {
1251 printf("\ninvalid point!\n");
1252 return;
1253 }
1254
1255 if (attack(str, &move))
1256 mprintf("\nSuccessful attack of %1m at %1m\n", str, move);
1257 else
1258 mprintf("\n%1m cannot be attacked\n", str);
1259}
1260
1261static void
1262showdefense(char* line)
1263{
1264 int str;
1265 int move;
1266
1267 gg_assert(line);
1268 str = string_to_location(board_size, line);
1269 if (str == NO_MOVE || board[str] == EMPTY) {
1270 printf("\ninvalid point!\n");
1271 return;
1272 }
1273
1274 if (attack(str, NULL)) {
1275 if (find_defense(str, &move))
1276 mprintf("\nSuccessful defense of %1m at %1m\n", str, move);
1277 else
1278 mprintf("\n%1m cannot be defended\n", str);
1279 } else
1280 mprintf("\nThere is no need to defend %1m\n", str);
1281}
1282
1283static void
8c1c3011 1284ansi_goto(Gameinfo* gameinfo, char* line)
3ebb0002
AT
1285{
1286 const char* movenumber = line;
1287
1288 if (!line)
1289 return;
1290
1291 if (!strncmp(line, "last", 4))
1292 movenumber = "9999";
1293 else if (!strncmp(line, "first", 4))
1294 movenumber = "1";
1295
1296 printf("goto %s\n", movenumber);
1297 gameinfo_play_sgftree(gameinfo, &sgftree, movenumber);
1298}
1299
1300static void
8c1c3011 1301ansi_free_handicap(Gameinfo* gameinfo, char* handicap_string, bool strict)
3ebb0002
AT
1302{
1303 int handi;
1304 int i;
1305 char line[80];
1306 int stones[MAX_BOARD * MAX_BOARD];
1307
1308 if (sscanf(handicap_string, "%d", &handi) == 1) {
1309 /* GNU Go is to place handicap */
1310 if (handi < 0 || handi == 1) {
1311 printf("\nInvalid command syntax!\n");
1312 return;
1313 }
1314
1315 clear_board();
1316 handi = place_free_handicap(handi);
1317 printf("\nPlaced %d stones of free handicap.\n", handi);
1318 } else { /* User is to place handicap */
1319 clear_board();
1320 handi = 0;
1321
1322 while (1) {
8c1c3011 1323 ansi_showboard(strict);
3ebb0002
AT
1324 printf("\nType in coordinates of next handicap stone, or one of the following commands:\n");
1325 printf(" undo take back the last stone placed\n");
1326 printf(" clear remove all the stones placed so far\n");
1327 printf(" done finish placing handicap\n\n");
1328 printf("You have placed %d handicap stone(s) so far.\n\n", handi);
1329
1330 if (!fgets(line, 80, stdin))
1331 return; /* EOF or some error */
1332 for (i = 0; i < 80; i++)
1333 line[i] = tolower((int)line[i]);
1334
1335 if (!strncmp(line, "undo", 4)) {
1336 if (!handi)
1337 printf("\nNothing to undo.\n");
1338 else {
1339 remove_stone(stones[--handi]);
1340 gprintf("\nRemoved the stone at %m.\n", I(stones[handi]),
1341 J(stones[handi]));
1342 }
1343 } else if (!strncmp(line, "clear", 5)) {
1344 clear_board();
1345 handi = 0;
1346 } else if (!strncmp(line, "done", 4)) {
1347 if (handi == 1) /* Don't bother with checks later */
1348 printf("\nHandicap cannot be one stone. Either add "
1349 "some more, or delete the only stone.\n");
1350 else
1351 break;
1352 } else {
1353 int pos = string_to_location(board_size, line);
1354 if (pos != NO_MOVE) {
1355 if (board[pos] != EMPTY)
1356 printf("\nThere's already a stone there.\n");
1357 else {
1358 add_stone(pos, BLACK);
1359 stones[handi++] = pos;
1360 }
1361 } else
1362 printf("\nInvalid command: %s", line);
1363 }
1364 }
1365 }
1366 gameinfo->handicap = handi;
1367 gameinfo->to_move = (handi ? WHITE : BLACK);
1368}
1369
1370/*
1371 * Local Variables:
1372 * tab-width: 4
1373 * c-basic-offset: 4
1374 * End:
1375 */