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