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