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