Reverted changes to play_ascii() interface since they now reside under play_ansi().
[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
c150f57c 26#include <ctype.h>
7eeb782e
AT
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
7eeb782e
AT
30
31#if READLINE
7eeb782e 32#include <readline/history.h>
c150f57c 33#include <readline/readline.h>
7eeb782e
AT
34#endif
35
c150f57c 36#include "gg_utils.h"
7eeb782e 37#include "interface.h"
c150f57c 38#include "liberty.h"
7eeb782e 39#include "sgftree.h"
7eeb782e
AT
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
c150f57c
AT
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);
7eeb782e
AT
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
c150f57c 81make_letterbar(int boardsize, char* letterbar)
7eeb782e 82{
c150f57c
AT
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 }
7eeb782e
AT
99}
100
7eeb782e
AT
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
7eeb782e
AT
109/*
110 * Mark the handicap spots on the board.
111 */
112
113static void
114set_handicap_spots(int boardsize)
115{
c150f57c
AT
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;
7eeb782e 132 }
c150f57c
AT
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 }
7eeb782e 176 }
7eeb782e 177
c150f57c 178 return;
7eeb782e
AT
179}
180
59cca85c
AT
181#define WHITECOLOR "\x1B[41m"
182#define BLACKCOLOR "\x1B[44m"
183#define RESETCOLOR "\x1B[0m"
184
1068c50c
AT
185#define BACK1COLOR "\x1B[43m"
186#define BACK2COLOR "\x1B[47m"
59cca85c 187
7eeb782e
AT
188/*
189 * Display the board position when playing in ASCII.
190 */
191
192static void
193ascii_showboard(void)
194{
c150f57c
AT
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");
31736a7b
AT
206 printf(" White (O) has captured %d pieces\n", black_captured);
207 printf(" Black (X) has captured %d pieces\n", white_captured);
c150f57c
AT
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
59cca85c
AT
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++) {
c150f57c
AT
239 if (POS(i, j) == last_move)
240 pos_is_move = 128;
241 else
242 pos_is_move = 0;
59cca85c
AT
243 dead = (dragon_status(POS(i, j)) == DEAD) && showdead;
244 switch (BOARD(i, j) + pos_is_move + last_pos_was_move) {
c150f57c
AT
245 case EMPTY + 128:
246 case EMPTY:
31736a7b 247 printf(" %c", hspots[i][j]);
c150f57c
AT
248 last_pos_was_move = 0;
249 break;
250 case BLACK:
31736a7b 251 printf(" %c", dead ? 'x' : 'X');
c150f57c
AT
252 last_pos_was_move = 0;
253 break;
254 case WHITE:
31736a7b 255 printf(" %c", dead ? 'o' : 'O');
c150f57c
AT
256 last_pos_was_move = 0;
257 break;
258 case BLACK + 128:
31736a7b
AT
259 printf("(%c)", 'X');
260 last_pos_was_move = 256;
c150f57c
AT
261 break;
262 case WHITE + 128:
31736a7b
AT
263 printf("(%c)", 'O');
264 last_pos_was_move = 256;
c150f57c
AT
265 break;
266 case EMPTY + 256:
31736a7b 267 printf("%c", hspots[i][j]);
c150f57c
AT
268 last_pos_was_move = 0;
269 break;
270 case BLACK + 256:
31736a7b 271 printf("%c", dead ? 'x' : 'X');
c150f57c
AT
272 last_pos_was_move = 0;
273 break;
274 case WHITE + 256:
31736a7b 275 printf("%c", dead ? 'o' : 'O');
c150f57c
AT
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;
59cca85c
AT
282 }
283 }
c150f57c
AT
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");
7eeb782e 297 }
c150f57c
AT
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);
7eeb782e 306 }
c150f57c
AT
307
308} /* end ascii_showboard */
7eeb782e
AT
309
310/*
311 * command help
312 */
313
314static void
315show_commands(void)
316{
c150f57c
AT
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");
7eeb782e
AT
350}
351
c150f57c
AT
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
7eeb782e
AT
395};
396
7eeb782e
AT
397/*
398 * Check if we have a valid command.
399 */
400
401static int
c150f57c 402get_command(char* command)
7eeb782e 403{
c150f57c
AT
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;
7eeb782e
AT
509}
510
7eeb782e
AT
511/*
512 * Write sgf root node.
513 */
514
515static void
c150f57c 516init_sgf(Gameinfo* ginfo)
7eeb782e 517{
c150f57c
AT
518 if (sgf_initialized)
519 return;
520 sgf_initialized = 1;
7eeb782e 521
c150f57c
AT
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);
7eeb782e
AT
526}
527
7eeb782e
AT
528/*
529 * Generate the computer move.
530 */
531
532static int
c150f57c 533computer_move(Gameinfo* gameinfo, int* passes)
7eeb782e 534{
c150f57c
AT
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 }
7eeb782e 556
c150f57c
AT
557 if (showscore) {
558 gnugo_estimate_score(&upper_bound, &lower_bound);
559 current_score_estimate = (int)((lower_bound + upper_bound) / 2.0);
560 }
7eeb782e 561
c150f57c
AT
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;
7eeb782e 568
c150f57c
AT
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}
7eeb782e
AT
579
580/*
581 * Make a move.
582 */
583
584static int
c150f57c 585do_move(Gameinfo* gameinfo, char* command, int* passes, int force)
7eeb782e 586{
c150f57c 587 int move = string_to_location(board_size, command);
7eeb782e 588
c150f57c
AT
589 if (move == NO_MOVE) {
590 printf("\nInvalid move: %s\n", command);
591 return 0;
592 }
7eeb782e 593
c150f57c
AT
594 if (!is_allowed_move(move, gameinfo->to_move)) {
595 printf("\nIllegal move: %s", command);
596 return 0;
597 }
7eeb782e 598
c150f57c
AT
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 }
7eeb782e 611
c150f57c
AT
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}
7eeb782e
AT
622
623/*
624 * Make a pass.
625 */
626
627static int
c150f57c 628do_pass(Gameinfo* gameinfo, int* passes, int force)
7eeb782e 629{
c150f57c
AT
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);
7eeb782e 636
c150f57c
AT
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 }
7eeb782e 643
c150f57c
AT
644 return computer_move(gameinfo, passes);
645}
7eeb782e
AT
646
647/*
648 * Play a game against an ascii client.
649 */
650
c150f57c 651void play_ascii(SGFTree* tree, Gameinfo* gameinfo, char* filename, char* until)
7eeb782e 652{
c150f57c
AT
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
7eeb782e
AT
661 * when it was loaded in main().
662 *
663 * FIXME: Why do we load the game again?
664 */
c150f57c
AT
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;
7eeb782e 677 }
7eeb782e 678
c150f57c
AT
679 do_play_ascii(gameinfo);
680 printf("\nThanks! for playing GNU Go.\n\n");
7eeb782e 681
c150f57c
AT
682 /* main() frees the tree and we might have changed it. */
683 *tree = sgftree;
7eeb782e
AT
684}
685
c150f57c 686void do_play_ascii(Gameinfo* gameinfo)
7eeb782e 687{
c150f57c
AT
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
7eeb782e
AT
708 * allowed to resign.
709 */
c150f57c 710 resignation_allowed = 1;
7eeb782e 711
c150f57c
AT
712 printf("\nBeginning ASCII mode game.\n\n");
713 gameinfo_print(gameinfo);
7eeb782e 714
c150f57c
AT
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);
7eeb782e 718
c150f57c
AT
719 /* main ASCII Play loop */
720 while (state == 0) {
721 /* Display game board. */
722 if (opt_showboard)
723 ascii_showboard();
7eeb782e
AT
724
725#if !READLINE
c150f57c
AT
726 /* Print the prompt */
727 mprintf("%s(%d): ", color_to_string(gameinfo->to_move), movenum + 1);
7eeb782e 728
c150f57c
AT
729 /* Read a line of input. */
730 line_ptr = line;
731 if (!fgets(line, 80, stdin))
732 return;
7eeb782e 733#else
c150f57c
AT
734 snprintf(line, 79, "%s(%d): ",
735 color_to_string(gameinfo->to_move), movenum + 1);
736 if (!(line_ptr = readline(line)))
737 return;
7eeb782e 738
c150f57c 739 add_history(line_ptr);
7eeb782e
AT
740#endif
741
c150f57c
AT
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
7eeb782e 810 * in case we change more info. */
c150f57c
AT
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 }
7eeb782e 1062#if READLINE
c150f57c 1063 free(line_ptr);
7eeb782e 1064#endif
c150f57c 1065 }
7eeb782e 1066
c150f57c
AT
1067 sgffile_output(&sgftree);
1068 passes = 0;
7eeb782e 1069
c150f57c
AT
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;
7eeb782e 1078
c150f57c
AT
1079 gameinfo_clear(gameinfo);
1080 }
1081}
7eeb782e
AT
1082
1083/* Communicates with user after a game has ended. */
1084static int
c150f57c 1085ascii_endgame(Gameinfo* gameinfo, int reason)
7eeb782e 1086{
c150f57c
AT
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 }
7eeb782e 1109
c150f57c
AT
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 }
7eeb782e 1158 }
7eeb782e 1159
c150f57c 1160 return state;
7eeb782e
AT
1161}
1162
7eeb782e
AT
1163/* ascii_count() scores the game.
1164 */
1165static void
c150f57c 1166ascii_count(Gameinfo* gameinfo)
7eeb782e 1167{
c150f57c
AT
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 }
7eeb782e 1221 }
c150f57c 1222 who_wins(gameinfo->computer_player, stdout);
7eeb782e
AT
1223}
1224
7eeb782e 1225static void
c150f57c 1226showcapture(char* line)
7eeb782e 1227{
c150f57c
AT
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 }
7eeb782e 1237
c150f57c
AT
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);
7eeb782e
AT
1242}
1243
7eeb782e 1244static void
c150f57c 1245showdefense(char* line)
7eeb782e 1246{
c150f57c
AT
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;
7eeb782e 1255 }
7eeb782e 1256
c150f57c
AT
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}
7eeb782e
AT
1265
1266static void
c150f57c 1267ascii_goto(Gameinfo* gameinfo, char* line)
7eeb782e 1268{
c150f57c 1269 const char* movenumber = line;
7eeb782e 1270
c150f57c
AT
1271 if (!line)
1272 return;
7eeb782e 1273
c150f57c
AT
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}
7eeb782e
AT
1282
1283static void
c150f57c 1284ascii_free_handicap(Gameinfo* gameinfo, char* handicap_string)
7eeb782e 1285{
c150f57c
AT
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 }
7eeb782e 1297
c150f57c
AT
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 */
7eeb782e
AT
1302 clear_board();
1303 handi = 0;
c150f57c
AT
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 }
7eeb782e 1348 }
c150f57c
AT
1349 gameinfo->handicap = handi;
1350 gameinfo->to_move = (handi ? WHITE : BLACK);
7eeb782e
AT
1351}
1352
7eeb782e
AT
1353/*
1354 * Local Variables:
c150f57c
AT
1355 * tab-width: 4
1356 * c-basic-offset: 4
7eeb782e
AT
1357 * End:
1358 */