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