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