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