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