1 /* cmd.cc
2
3 GNU Chess frontend
4
5 Copyright (C) 2001-2021 Free Software Foundation, Inc.
6
7 GNU Chess is based on the two research programs
8 Cobalt by Chua Kong-Sian and Gazebo by Stuart Cracraft.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22
23 Contact Info:
24 bug-gnu-chess@gnu.org
25 cracraft@ai.mit.edu, cracraft@stanfordalumni.org, cracraft@earthlink.net
26 */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <ctype.h>
33 #include <errno.h>
34
35 #include "version.h"
36 #include "common.h"
37 #include "gettext.h"
38
39 #define _(str) gettext (str)
40
41 static char logfile[MAXSTR];
42 static char gamefile[MAXSTR];
43
44 /*
45 * Splitting input is actually not neccessary, but we find
46 * tokens separated by whitespace and put pointers on them.
47 * How many do we need? We just take 3 for now. Check if the
48 * fact that tokens are not 0-terminated but whitespace-terminated
49 * generates bugs. (Already killed one bug in move.c)
50 * We also kill trailing whitespace. (The trailing '\n' might
51 * be really annoying otherwise.)
52 */
53
54 #define TOKENS 3
55
56 static char *token[TOKENS];
57 char *endptr;
58
59 static int hardFlag=0;
60 static int postFlag=0;
61
62 static const char setboard_cmd[] = "setboard ";
63
split_input(void)64 static void split_input(void)
65 {
66 /* r points to the last non-space character */
67 char *s, *r;
68 int k;
69
70 for (k = 0, s = r = inputstr; k < TOKENS; ++k) {
71 /* Skip leading whitespace */
72 while (isspace(*s)) s++;
73 token[k] = s;
74 /* Skip token */
75 while (*s && !isspace(*s)) r = s++;
76 }
77 while (*s) {
78 while (isspace(*s)) s++;
79 while (*s && !isspace(*s)) r = s++;
80 }
81 r[1] = '\0';
82 }
83
84 /*
85 * Compares two tokens, returns 1 on equality. Tokens
86 * are separated by whitespace.
87 */
tokeneq(const char * s,const char * t)88 static int tokeneq(const char *s, const char *t)
89 {
90 while (*s && *t && !isspace(*s) && !isspace(*t)) {
91 if (*s++ != *t++) return 0;
92 }
93 return (!*s || isspace(*s)) && (!*t || isspace(*t));
94 }
95
96 /*
97 * Remove a trailing \n and return NULL if last character is not \n.
98 */
trim_newline(char * line)99 static char *trim_newline(char *line)
100 {
101 if (line == NULL) {
102 return NULL;
103 }
104 const size_t line_len = strlen(line);
105 const size_t last_char_index = strlen(line) - 1;
106 if (line_len <= 0 || line[last_char_index] != '\n') {
107 return NULL;
108 }
109 line[last_char_index] = '\0';
110 return line;
111 }
112
113 /*
114 * Takes an EPD filename as input and returns the contents as a
115 * 'setboard <epd-position>' command.
116 */
build_setboard_cmd_from_epd_file(char * data,const char * epd_filename,unsigned int data_len)117 static char *build_setboard_cmd_from_epd_file(char *data, const char *epd_filename, unsigned int data_len)
118 {
119 char *result = NULL;
120 char *epdline = (char *)calloc(data_len, sizeof(char));
121
122 if (epdline == NULL) {
123 return NULL;
124 }
125 FILE *epdfile = fopen(epd_filename, "r");
126 if (epdfile == NULL) {
127 return NULL;
128 }
129 if (fgets(epdline, data_len, epdfile) && trim_newline(epdline) && strlen(setboard_cmd) + strlen(epdline) < data_len) {
130 strcpy(data, setboard_cmd);
131 strcat(data, epdline);
132 result = data;
133 }
134 fclose(epdfile);
135 free(epdline);
136
137 return result;
138 }
139
140 /*
141 * Takes a PGN filename as input and returns the contents as a
142 * 'setboard <epd-position>' command.
143 */
build_setboard_cmd_from_pgn_file(char * data,const char * pgn_filename,unsigned int data_len)144 static char *build_setboard_cmd_from_pgn_file(char *data, const char *pgn_filename, unsigned int data_len)
145 {
146 char *result = NULL;
147 char *epdline = (char *)calloc(data_len, sizeof(char));
148
149 if (epdline == NULL) {
150 return NULL;
151 }
152 PGNReadFromFile (pgn_filename, 0);
153 EPD2str(epdline);
154 if (strlen(setboard_cmd) + strlen(epdline) < data_len) {
155 strcpy(data, setboard_cmd);
156 strcat(data, epdline);
157 result = data;
158 }
159 free(epdline);
160
161 return result;
162 }
163
164 /*
165 * Loads a PGN file. Returns 1 on success, 0 on error.
166 */
pgnload(const char * pgn_filename)167 static int pgnload(const char *pgn_filename)
168 {
169 int success;
170 char data[MAXSTR]="";
171
172 if (build_setboard_cmd_from_pgn_file(data, pgn_filename, sizeof(data))) {
173 SetDataToEngine( data );
174 SetAutoGo( true );
175 success = 1;
176 } else {
177 printf( _("Error loading PGN file '%s'.\n"), pgn_filename );
178 success = 0;
179 }
180 return success;
181 }
182
cmd_accepted(void)183 void cmd_accepted(void)
184 {
185 SetDataToEngine( token[0] );
186 }
187
cmd_activate(void)188 void cmd_activate(void)
189 {
190 printf( _("Command 'activate' is currently not supported.\n") );
191 }
192
cmd_analyze(void)193 void cmd_analyze(void)
194 {
195 /*
196 * "analyze" mode is similar to force, hard and post together
197 * in that it produces a text output like post, but must
198 * think indefinitely like ponder.
199 *
200 * Some additional output is expected in command ".\n" but if ignored
201 * this should not be sent any more
202 */
203
204 /* TODO correct output, add fail high low */
205
206 SET (flags, ANALYZE);
207 SetDataToEngine( "hard\npost\nanalyze" );
208 }
209
cmd_bk(void)210 void cmd_bk(void)
211 {
212 SetDataToEngine( "bk" );
213 }
214
cmd_black(void)215 void cmd_black(void)
216 {
217 /*
218 * No longer used by Xboard but requested as a feature
219 */
220 printf( _("Command 'black' is currently not supported.\n") );
221 }
222
cmd_book(void)223 void cmd_book(void)
224 {
225 char data[MAXSTR]="";
226 strcpy( data, "book " );
227 if (tokeneq(token[1], "add")) {
228 if (access(token[2], F_OK) < 0) {
229 printf(_("The syntax to add a new book is:\n\n\tbook add file.pgn\n"));
230 } else {
231 strcat( data, "add " );
232 strcat( data, token[2] );
233 }
234 } else if (tokeneq (token[1], "on") || tokeneq(token[1], "prefer")) {
235 strcpy( data, "book on" );
236 printf(_("Book is now on.\n"));
237 } else if (tokeneq (token[1], "off")) {
238 strcpy( data, "book off" );
239 printf(_("Book is now off.\n"));
240 } else if (tokeneq (token[1], "best")) {
241 strcpy( data, "book best" );
242 printf(_("Book is now best.\n"));
243 } else if (tokeneq (token[1], "worst")) {
244 strcpy( data, "book worst" );
245 printf(_("Book is now worst.\n"));
246 } else if (tokeneq (token[1], "random")) {
247 strcpy( data, "book random" );
248 printf(_("Book is now random.\n"));
249 } else {
250 printf( _("Incorrect book option: '%s'.\n"), token[1] );
251 return;
252 }
253 SetDataToEngine( data );
254 }
255
cmd_depth(void)256 void cmd_depth(void)
257 {
258 char data[MAXSTR]="";
259 int searchDepth=0;
260 searchDepth = atoi( token[1] );
261 sprintf( data, "sd %d", searchDepth );
262 SetDataToEngine( data );
263 printf(_("Search to a depth of %d.\n"),searchDepth);
264 }
265
cmd_easy(void)266 void cmd_easy(void)
267 {
268 SetDataToEngine( token[0] );
269 }
270
271 /* Predecessor to setboard */
cmd_edit(void)272 void cmd_edit(void)
273 {
274 if ( flags & XBOARD ) {
275 printf("tellusererror command 'edit' not implemented\n");
276 fflush(stdout);
277 }
278 }
279
cmd_exit(void)280 void cmd_exit(void)
281 {
282 /*
283 * "exit" is a synonym for quit except in engine mode
284 * when it means leave analyze mode
285 */
286
287 if ( flags & ANALYZE ){
288 flags = preanalyze_flags ; /* this implicitly clears ANALYZE flag */
289 SetDataToEngine( token[0] );
290 } else {
291 cmd_quit();
292 }
293 }
294
cmd_force(void)295 void cmd_force(void)
296 {
297 SET (flags, MANUAL);
298 SetDataToEngine( token[0] );
299 }
300
cmd_go(void)301 void cmd_go(void)
302 {
303 SET (flags, THINK);
304 CLEAR (flags, MANUAL);
305 CLEAR (flags, TIMEOUT);
306 CLEAR (flags, ENDED);
307 computer = board.side;
308 ExpectAnswerFromEngine( true );
309 ChangeColor( true );
310 SetDataToEngine( token[0] );
311 pgnloaded = 0;
312 }
313
cmd_hard(void)314 void cmd_hard(void)
315 {
316 SetDataToEngine( token[0] );
317 }
318
cmd_hash(void)319 void cmd_hash(void)
320 {
321 if (tokeneq (token[1], "off")) {
322 CLEAR (flags, USEHASH);
323 SetDataToEngine( "hashoff" );
324 } else if (tokeneq (token[1], "on")) {
325 SET (flags, USEHASH);
326 SetDataToEngine( "hashon" );
327 }
328 if ( flags & USEHASH ) {
329 printf( _("Hashing is on.\n") );
330 } else {
331 printf( _("Hashing is off.\n") );
332 }
333 }
334
335 /* Give a possible move for the player to play */
cmd_hint(void)336 void cmd_hint(void)
337 {
338 /* An answer is received only if book on - TODO change this in adapter */
339 SetDataToEngine( token[0] );
340 /* TODO if no hint, inform on stdout */
341 }
342
cmd_ics(void)343 void cmd_ics(void)
344 {
345 SetDataToEngine( token[0] );
346 }
347
cmd_level(void)348 void cmd_level(void)
349 {
350 SetDataToEngine( token[0] );
351 sscanf (token[1], "%d %f %d", &TCMove, &TCTime, &TCinc);
352 if (TCMove == 0) {
353 TCMove = 35 /* MIN((5*(GameCnt+1)/2)+1,60) */;
354 printf("TCMove = %d\n",TCMove);
355 }
356 if (TCTime == 0) {
357 SearchTime = TCinc / 2.0f ;
358 printf(_("Fischer increment of %d seconds.\n"),TCinc);
359 } else {
360 MoveLimit[white] = MoveLimit[black] = TCMove - (GameCnt+1)/2;
361 TimeLimit[white] = TimeLimit[black] = TCTime * 60;
362 if (!(flags & XBOARD)) {
363 /* TRANSLATORS: Please be aware that the word 'move' is sometimes
364 used as a synonym of 'ply', and sometimes in the sense of a
365 full 2-ply move. */
366 printf (_("Time control: %d moves in %.2f secs.\n"),
367 MoveLimit[white], TimeLimit[white]);
368 printf(_("Fischer increment of %d seconds.\n"),TCinc);
369 }
370 }
371 }
372
cmd_list(void)373 void cmd_list(void)
374 {
375 if (token[1][0] == '?') {
376 printf(_("name - list known players alphabetically\n"));
377 printf(_("score - list by GNU best result first\n"));
378 printf(_("reverse - list by GNU worst result first\n"));
379 } else {
380 if (token[1][0] == '\0') DBListPlayer("rscore");
381 else DBListPlayer(token[1]);
382 }
383 }
384
cmd_load(void)385 void cmd_load(void)
386 {
387 char *epd_filename = token[1];
388 char data[MAXSTR]="";
389 LoadEPD (epd_filename);
390 pgnloaded = 0;
391 check_board();
392 if (!ValidateBoard()) {
393 SET (flags, ENDED);
394 printf (_("Board is wrong!\n"));
395 } else {
396 /* Read EPD file and send contents to engine */
397 if (build_setboard_cmd_from_epd_file(data, epd_filename, strlen(data))) {
398 SetDataToEngine( data );
399 SetAutoGo( true );
400 } else {
401 printf( _("Error loading EPD file '%s'.\n"), epd_filename );
402 }
403 }
404 }
405
cmd_manual(void)406 void cmd_manual(void)
407 {
408 SET (flags, MANUAL);
409 ExpectAnswerFromEngine( false );
410 SetDataToEngine( "force" );
411 }
412
cmd_memory(void)413 void cmd_memory(void)
414 {
415 if (token[1][0] == 0) {
416 ExpectAnswerFromEngine( true );
417 SetDataToEngine( "memory" );
418 } else {
419 unsigned int memory;
420 if ( sscanf( token[1], "%d", &memory ) == 1 ) {
421 char data[MAXSTR]="";
422 sprintf( data, "memory %d\nmemory", memory );
423 SetDataToEngine( data );
424 }
425 /* TODO Handle error */
426 }
427 }
428
429 /* Move now, not applicable */
cmd_movenow(void)430 void cmd_movenow(void)
431 {
432 SetDataToEngine( "?" );
433 }
434
435 /*
436 * TODO: Add a logpath variable or macro, not always dump into current
437 * dir. Again, how does one handle paths portably across Unix/Windows?
438 * -- Lukas
439 */
cmd_name(void)440 void cmd_name(void)
441 {
442 SetDataToEngine( token[0] );
443 int suffix = 0;
444
445 /* name[sizeof name - 1] should always be 0 */
446 strncpy(name, token[1], sizeof name - 1);
447 for (suffix = 0; suffix < 1000; suffix++) {
448 sprintf(logfile,"log.%03d",suffix);
449 sprintf(gamefile,"game.%03d",suffix);
450 /*
451 * There is an obvious race condition here but who cares, we just
452 * bail out in case of failure... --Lukas
453 */
454 if (access(logfile,F_OK) < 0) {
455 ofp = fopen(logfile,"w");
456 if (ofp == NULL) {
457 ofp = stdout;
458 fprintf(stderr, _("Failed to open %s for writing: %s\n"),
459 logfile, strerror(errno));
460 }
461 return;
462 }
463 }
464 fprintf(stderr, _("Could not create logfile, all slots occupied.\n"));
465 fprintf(stderr, _("You may consider deleting or renaming your existing logfiles.\n"));
466 }
467
cmd_new(void)468 void cmd_new(void)
469 {
470 InitVars ();
471 NewPosition ();
472 /* Protocol specification for ANALYZE says "new" does not end analysis */
473 if (!(flags & ANALYZE))
474 CLEAR (flags, MANUAL);
475 CLEAR (flags, THINK);
476 myrating = opprating = 0;
477 SetDataToEngine( token[0] );
478 }
479
cmd_nopost(void)480 void cmd_nopost(void)
481 {
482 CLEAR (flags, POST);
483 postFlag = 0;
484 ExpectAnswerFromEngine( false );
485 SetDataToEngine( token[0] );
486 }
487
cmd_null(void)488 void cmd_null(void)
489 {
490 if (tokeneq (token[1], "off")) {
491 CLEAR (flags, USENULL);
492 SetDataToEngine( "nulloff" );
493 } else if (tokeneq (token[1], "on")) {
494 SET (flags, USENULL);
495 SetDataToEngine( "nullon" );
496 }
497 if ( flags & USENULL ) {
498 printf( _("Null-move heuristic is on.\n") );
499 } else {
500 printf( _("Null-move heuristic is off.\n") );
501 }
502 }
503
cmd_otim(void)504 void cmd_otim(void)
505 {
506 SetDataToEngine( token[0] );
507 }
508
509 /*
510 * Load a file containing a game in PGN format.
511 *
512 * The file contents will be passed on to the adapter
513 * in EPD notation (the adapter expectes FEN actually,
514 * but EPD and FEN are similar (possible issue here?),
515 * hence a PGN -> EPD conversion in done first.
516 */
cmd_pgnload(void)517 void cmd_pgnload(void)
518 {
519 pgnload(token[1]);
520 }
521
522 /* See comment above in cmd_pgnload about PGN -> EPD conversion. */
cmd_pgnreplay(void)523 void cmd_pgnreplay(void)
524 {
525 if (!pgnload(token[1])) {
526 return;
527 }
528 pgnloaded = 1;
529 pgncnt = GameCnt;
530
531 while (GameCnt >= 0) {
532 if (GameCnt >= 0) {
533 CLEAR (flags, ENDED);
534 CLEAR (flags, TIMEOUT);
535 ChangeColor( true );
536 SetAutoGo( true );
537 UnmakeMove (board.side, &Game[GameCnt].move);
538 if (GameCnt >= 0) {
539 UnmakeMove (board.side, &Game[GameCnt].move);
540 }
541 }
542 }
543
544 cmd_first();
545 }
546
cmd_next(void)547 void cmd_next(void)
548 {
549 if (!pgnloaded) {
550 printf(_("Error: PGN file not loaded!\n"));
551 return;
552 }
553
554 if ((GameCnt+1) <= pgncnt) {
555 ChangeColor( true );
556 SetAutoGo( true );
557 MakeMove (board.side, &Game[GameCnt+1].move);
558 } else {
559 printf(_("No more moves. Game reached the end.\n"));
560 return;
561 }
562
563 printf("%d. ",GameCnt/2+1);
564 printf("%s\n", Game[GameCnt].SANmv);
565 ShowBoard ();
566 }
567
cmd_previous(void)568 void cmd_previous(void)
569 {
570 if (!pgnloaded) {
571 printf(_("Error: PGN file not loaded!\n"));
572 return;
573 }
574
575 if (GameCnt >= 0) {
576 ChangeColor( true );
577 SetAutoGo( true );
578 UnmakeMove (board.side, &Game[GameCnt].move);
579 }
580 else {
581 printf(_("Initial position reached. There are no earlier moves.\n"));
582 return;
583 }
584
585 printf("%d. ",GameCnt/2+1);
586 printf("%s\n", Game[GameCnt].SANmv);
587 ShowBoard ();
588 }
589
cmd_last(void)590 void cmd_last(void)
591 {
592 if (!pgnloaded) {
593 printf(_("Error: PGN file not loaded!\n"));
594 return;
595 }
596
597 while (GameCnt+1 <= pgncnt) {
598 ChangeColor( true );
599 SetAutoGo( true );
600 MakeMove (board.side, &Game[GameCnt+1].move);
601 }
602
603 printf("%d. ",GameCnt/2+1);
604 printf("%s\n", Game[GameCnt].SANmv);
605 ShowBoard ();
606 }
607
cmd_first(void)608 void cmd_first(void)
609 {
610 if (!pgnloaded) {
611 printf(_("Error: PGN file not loaded!\n"));
612 return;
613 }
614
615 while (GameCnt >= 0) {
616 if (GameCnt >= 0) {
617 CLEAR (flags, ENDED);
618 CLEAR (flags, TIMEOUT);
619 ChangeColor( true );
620 SetAutoGo( true );
621 UnmakeMove (board.side, &Game[GameCnt].move);
622 if (GameCnt >= 0) {
623 UnmakeMove (board.side, &Game[GameCnt].move);
624 }
625 }
626 }
627
628 ShowBoard ();
629 }
630
631 /*
632 * XXX - Filenames with spaces will break here,
633 * do we want to support them? I vote for "no"
634 * - Lukas
635 */
cmd_pgnsave(void)636 void cmd_pgnsave(void)
637 {
638 if ( strlen(token[1]) > 0 )
639 PGNSaveToFile (token[1], "");
640 else
641 printf(_("Invalid filename.\n"));
642 }
643
cmd_graphic(void)644 void cmd_graphic(void)
645 {
646 graphicmodeoutput = 1;
647 printf(_("Graphic mode is enabled.\n"));
648 }
649
cmd_nographic(void)650 void cmd_nographic(void)
651 {
652 graphicmodeoutput = 0;
653 printf(_("Graphic mode is disabled.\n"));
654 }
655
cmd_ping(void)656 void cmd_ping(void)
657 {
658 /* TODO cf. 5.08 */
659 SetDataToEngine( token[0] );
660 /* If ping is received when we are on move, we are supposed to
661 reply only after moving. In this version of GNU Chess, we
662 never read commands while we are on move, so we don't have to
663 worry about that here. */
664 printf("pong %s\n", token[1]);
665 fflush(stdout);
666 }
667
cmd_post(void)668 void cmd_post(void)
669 {
670 /* TODO State makes no sense */
671 SET (flags, POST);
672 postFlag = 1;
673 if ( hardFlag && postFlag )
674 ExpectAnswerFromEngine( true );
675 if ( flags & XBOARD )
676 ExpectAnswerFromEngine( true );
677 else
678 ExpectAnswerFromEngine( false );
679 SetDataToEngine( token[0] );
680 }
681
cmd_protover(void)682 void cmd_protover(void)
683 {
684 SetDataToEngine( token[0] );
685 return;
686 if (flags & XBOARD) {
687 /* Note: change this if "draw" command is added, etc. */
688 printf("feature setboard=1 analyze=1 ping=1 draw=0 sigint=0\
689 variants=\"normal\" myname=\"%s %s\" done=1\n",
690 PROGRAM, VERSION);
691 fflush(stdout);
692 }
693 }
694
cmd_quit(void)695 void cmd_quit(void) { SET (flags, QUIT); }
696
cmd_random(void)697 void cmd_random(void)
698 {
699 printf( _("Command 'random' is currently not supported.\n") );
700 //SetDataToEngine( token[0] );
701 }
702
cmd_rating(void)703 void cmd_rating(void)
704 {
705 myrating = atoi(token[1]);
706 opprating = atoi(token[2]);
707 fprintf(ofp,_("my rating = %d, opponent rating = %d\n"),myrating,opprating);
708 /* Change randomness of book based on opponent rating. */
709 /* Basically we play narrower book the higher the opponent */
710 if (opprating >= 1700) bookfirstlast = 2;
711 else if (opprating >= 1700) bookfirstlast = 2;
712 else bookfirstlast = 2;
713 }
714
cmd_rejected(void)715 void cmd_rejected(void) {}
716
cmd_remove(void)717 void cmd_remove(void)
718 {
719 SetDataToEngine( token[0] );
720 if (GameCnt >= 0) {
721 CLEAR (flags, ENDED);
722 CLEAR (flags, TIMEOUT);
723 UnmakeMove (board.side, &Game[GameCnt].move);
724 if (GameCnt >= 0) {
725 UnmakeMove (board.side, &Game[GameCnt].move);
726 if (!(flags & XBOARD))
727 ShowBoard ();
728 }
729 PGNSaveToFile ("game.log","");
730 } else
731 printf (_("No moves to undo!\n"));
732 }
733
cmd_result(void)734 void cmd_result(void)
735 {
736 /* TODO Do not send to engine */
737 SetDataToEngine( token[0] );
738 if (ofp != stdout) {
739 fprintf(ofp, "result: %s\n",token[1]);
740 fclose(ofp);
741 ofp = stdout;
742 printf(_("Save to %s\n"),gamefile);
743 PGNSaveToFile (gamefile, token[1]);
744 DBUpdatePlayer (name, token[1]);
745 }
746 }
747
cmd_save(void)748 void cmd_save(void)
749 {
750 if ( strlen(token[1]) > 0 )
751 SaveEPD (token[1]);
752 else
753 printf(_("Invalid filename.\n"));
754 }
755
cmd_setboard(void)756 void cmd_setboard(void)
757 {
758 /* setboard uses FEN, not EPD, but ParseEPD will accept FEN too */
759 char data[MAXSTR]="";
760 ParseEPD (token[1]);
761 NewPosition();
762 check_board();
763 snprintf(data, sizeof(data), "setboard %s", token[1]);
764 SetDataToEngine(data);
765 }
766
cmd_solve(void)767 void cmd_solve(void)
768 {
769 Solve (token[1]);
770 }
771
772 /* Set total time for move to be N seconds is "st N" */
cmd_st(void)773 void cmd_st(void)
774 {
775 char data[MAXSTR]="";
776 /* Approximately level 1 0 N */
777 sscanf(token[1],"%d",&TCinc);
778 /* Allow a little fussiness for failing low etc */
779 SearchTime = TCinc * 0.90f ;
780 sprintf( data, "st %d", atoi( token[1] ) );
781 SetDataToEngine( data );
782 }
783
cmd_switch(void)784 void cmd_switch(void)
785 {
786 printf( _("Command 'switch' is currently not supported.\n") );
787 }
788
cmd_time(void)789 void cmd_time(void)
790 {
791 /* TODO send what? */
792 SetDataToEngine( token[0] );
793 TimeLimit[1^board.side] = atoi(token[1]) / 100.0f ;
794 }
795
cmd_undo(void)796 void cmd_undo(void)
797 {
798 SetDataToEngine( "force\nundo" );
799 ChangeColor( true );
800 SetAutoGo( !(flags & MANUAL) );
801 if (GameCnt >= 0)
802 UnmakeMove (board.side, &Game[GameCnt].move);
803 else
804 printf (_("No moves to undo!\n"));
805 MoveLimit[board.side]++;
806 TimeLimit[board.side] += Game[GameCnt+1].et;
807 if (!(flags & XBOARD)) ShowBoard ();
808 }
809
cmd_usage(void)810 void cmd_usage(void)
811 {
812 printf ( "\n" );
813 printf ( _("\
814 Usage: %s [OPTION]...\n\n"), progname );
815 fputs( _("\
816 Play the game of chess.\n\n"), stdout );
817 fputs( _("Options:\n"), stdout );
818 fputs( _("\
819 -h, --help display this help and exit\n"), stdout );
820 fputs( _("\
821 -v, --version display version information and exit\n"), stdout );
822 fputs( _("\
823 -q, --quiet make the program silent on startup\n"), stdout );
824 fputs( _("\
825 --silent same as -q\n"), stdout );
826 fputs( _("\
827 \n"), stdout );
828 fputs( _("\
829 -x, --xboard start in engine mode\n"), stdout );
830 fputs( _("\
831 -p, --post start up showing thinking\n"), stdout );
832 fputs( _("\
833 -e, --easy disable thinking in opponents time\n"), stdout );
834 fputs( _("\
835 -m, --manual enable manual mode\n"), stdout );
836 fputs( _("\
837 -u, --uci enable UCI protocol (externally behave as UCI engine)\n"), stdout );
838 fputs( _("\
839 -M size, --memory=size specify memory usage in MB for hashtable\n"), stdout );
840 fputs( _("\
841 -a filename, --addbook=filename compile book.bin from pgn book 'filename'\n"), stdout );
842 fputs( _("\
843 -g, --graphic enable graphic mode\n"), stdout );
844 fputs( _("\
845 \n"), stdout );
846 fputs( _("\
847 Options xboard and post are accepted without leading dashes\n\
848 for backward compatibility.\n\
849 \n"), stdout );
850 fputs( _("\
851 Moves are accepted either in standard algebraic notation (SAN) or\n\
852 in coordinate algebraic notation.\n\
853 \n"), stdout );
854 fputs( _("\
855 The file 'gnuchess.ini' allows setting config options if --uci is not\n\
856 used. See 'info gnuchess' for details. The file is looked for in three\n\
857 locations according to this precedence: current directory, the\n\
858 directory pointed to by environment variable GNUCHESS_PKGDATADIR,\n\
859 or the package data directory stated at configure time.\n\
860 \n"), stdout );
861 fputs( _("\
862 Report bugs to <bug-gnu-chess@gnu.org>.\n\
863 \n"), stdout );
864 }
865
866
867 /* Play variant, we instruct interface in protover we play normal */
cmd_variant(void)868 void cmd_variant(void) {}
869
870 /* TODO Not in 5.08 */
cmd_usermove(void)871 void cmd_usermove(void)
872 {
873 /* TODO: Remove the first SetDataToEngine */
874 /*SetDataToEngine( token[0] );*/
875 leaf *ptr;
876 ptr = ValidateMove (token[1]);
877 if (ptr != NULL) {
878 /* Since the user entered a move:
879 * 1. The move must be sent to the engine.
880 * 2. A reply is expected from the engine.
881 */
882 SetUserInputValidMove( 1 );
883 SetDataToEngine( token[0] );
884 pgnloaded = 0;
885 ExpectAnswerFromEngine( true );
886 SANMove (ptr->move, 1);
887 MakeMove (board.side, &ptr->move);
888 strcpy (Game[GameCnt].SANmv, SANmv);
889 printf("%d. ",GameCnt/2+1);
890 printf("%s",token[1]);
891 if (ofp != stdout) {
892 fprintf(ofp,"%d. ",GameCnt/2+1);
893 fprintf(ofp,"%s",token[1]);
894 }
895 putchar('\n');
896 fflush(stdout);
897 if (ofp != stdout) {
898 fputc('\n',ofp);
899 fflush(ofp);
900 }
901 if (!(flags & XBOARD)) ShowBoard ();
902 SET (flags, THINK);
903 }
904 else {
905 /*
906 * Must Output Illegal move to prevent Xboard accepting illegal
907 * en passant captures and other subtle mistakes
908 */
909 printf(_("Invalid move: %s\n"),token[1]);
910 fflush(stdout);
911 }
912 }
913
cmd_version(void)914 void cmd_version(void)
915 {
916 if (!(flags & XBOARD))
917 printf ("%s %s\n", PROGRAM, VERSION);
918 else
919 printf ("Chess\n");
920 }
921
cmd_coords(void)922 void cmd_coords(void) {
923 printf(_("Coordinate display enabled.\n"));
924 coords = 1;
925 }
926
cmd_nocoords(void)927 void cmd_nocoords(void) {
928 printf(_("Coordinate display disabled.\n"));
929 coords = 0;
930 }
931
cmd_white(void)932 void cmd_white(void)
933 {
934 /*
935 * No longer used by Xboard but requested as a feature
936 */
937 printf( _("Command 'white' is currently not supported.\n") );
938 }
939
cmd_xboard(void)940 void cmd_xboard(void)
941 {
942 SetDataToEngine( "xboard" );
943 if (tokeneq (token[1], "off"))
944 CLEAR (flags, XBOARD);
945 else if (tokeneq (token[1], "on"))
946 SET (flags, XBOARD);
947 else if (!(flags & XBOARD)) { /* set if unset and only xboard called */
948 SET (flags, XBOARD); /* like in xboard/winboard usage */
949 }
950 }
951
952 /*
953 * Command with subcommands, could write secondary method
954 * tables here
955 */
956
cmd_show(void)957 void cmd_show (void)
958 /************************************************************************
959 *
960 * The show command driver section.
961 *
962 ************************************************************************/
963 {
964 /* TODO Remove gettext support */
965 if (tokeneq (token[1], "board"))
966 ShowBoard ();
967 else if (tokeneq (token[1], "rating"))
968 {
969 printf(_("My rating = %d\n"),myrating);
970 printf(_("Opponent rating = %d\n"),opprating);
971 }
972 else if (tokeneq (token[1], _("time")))
973 ShowTime ();
974 else if (tokeneq (token[1], _("moves"))) {
975 GenCnt = 0;
976 TreePtr[2] = TreePtr[1];
977 GenMoves (1);
978 ShowMoveList (1);
979 printf (_("No. of moves generated = %ld\n"), GenCnt);
980 }
981 else if (tokeneq (token[1], "escape")) {
982 GenCnt = 0;
983 TreePtr[2] = TreePtr[1];
984 GenCheckEscapes (1);
985 ShowMoveList (1);
986 printf (_("No. of moves generated = %ld\n"), GenCnt);
987 }
988 else if (tokeneq (token[1], "noncapture"))
989 {
990 GenCnt = 0;
991 TreePtr[2] = TreePtr[1];
992 GenNonCaptures (1);
993 FilterIllegalMoves (1);
994 ShowMoveList (1);
995 printf (_("No. of moves generated = %ld\n"), GenCnt);
996 }
997 else if (tokeneq (token[1], "capture"))
998 {
999 GenCnt = 0;
1000 TreePtr[2] = TreePtr[1];
1001 GenCaptures (1);
1002 FilterIllegalMoves (1);
1003 ShowMoveList (1);
1004 printf (_("No. of moves generated = %ld\n"), GenCnt);
1005 }
1006 else if (tokeneq (token[1], "eval") || tokeneq (token[1], "score"))
1007 {
1008 printf( _("Command 'show eval/score' is currently not supported.\n") );
1009 return;
1010 }
1011 else if (tokeneq (token[1], "game"))
1012 ShowGame ();
1013 else if (tokeneq (token[1], "pin"))
1014 {
1015 printf( _("Command 'show pin' is currently not supported.\n") );
1016 return;
1017 }
1018 }
1019
cmd_test(void)1020 void cmd_test (void)
1021 /*************************************************************************
1022 *
1023 * The test command driver section.
1024 *
1025 *************************************************************************/
1026 {
1027 printf( _("Command 'test' is currently not supported.\n") );
1028 }
1029
1030 /*
1031 * This is more or less copied from the readme, and the
1032 * parser is not very clever, so the lines containing
1033 * command names should not be indented, the lines with
1034 * explanations following them should be indented. Do not
1035 * use tabs for indentation, only spaces. CAPITALS are
1036 * reserved for parameters in the command names. The
1037 * array must be terminated by two NULLs.
1038 *
1039 * This one should be integrated in the method table.
1040 * (Very much like docstrings in Lisp.)
1041 */
1042
1043 static const char * const helpstr[] = {
1044 "quit",
1045 gettext_noop(" Quits the program."),
1046 "exit",
1047 gettext_noop(" In analysis mode this stops analysis, otherwise it quits the program."),
1048 "help",
1049 gettext_noop(" Produces a help blurb corresponding to this list of commands."),
1050 "book",
1051 gettext_noop(" add - compiles book.bin from a pgn book file"),
1052 gettext_noop(" on - enables use of book (default)"),
1053 gettext_noop(" off - disables use of book"),
1054 gettext_noop(" worst - plays worst move from book"),
1055 gettext_noop(" best - plays best move from book"),
1056 gettext_noop(" prefer - same as 'book on' (default)"),
1057 gettext_noop(" random - plays any move from book"),
1058 "version",
1059 gettext_noop(" Prints out the version of this program."),
1060 "previous",
1061 "p",
1062 gettext_noop(" Backs up one move in pgn loaded game."),
1063 "pgnsave FILENAME",
1064 gettext_noop(" Saves the game so far from memory to the file."),
1065 "pgnload FILENAME",
1066 gettext_noop(" Loads the game in the file into memory."),
1067 "pgnreplay FILENAME",
1068 gettext_noop(" Loads the game in the file into memory, and enables\n"
1069 " the commands 'first', 'last', 'next', 'previous'."),
1070 "next",
1071 "n",
1072 gettext_noop(" Advances one move in pgn loaded game."),
1073 "first",
1074 gettext_noop(" Goes to begin position of pgn loaded game."),
1075 "last",
1076 gettext_noop(" Goes to end position of pgn loaded game."),
1077 "force",
1078 "manual",
1079 gettext_noop(" Makes the program stop moving. You may now enter moves\n"
1080 " to reach some position in the future."),
1081 " ",
1082 "white",
1083 gettext_noop(" Program plays white."),
1084 "black",
1085 gettext_noop(" Program plays black."),
1086 "go",
1087 gettext_noop(" Computer takes whichever side is on move and begins its\n"
1088 " thinking immediately."),
1089 "post",
1090 gettext_noop(" Arranges for verbose thinking output showing variation, score,\n"
1091 " time, depth, etc."),
1092 "nopost",
1093 gettext_noop(" Turns off verbose thinking output."),
1094 "name NAME",
1095 gettext_noop(" Lets you input your name. Also writes the log.nnn and\n"
1096 " corresponding game.nnn files. For details please see\n"
1097 " the auxiliary file format sections."),
1098 "result",
1099 gettext_noop(" Mostly used by Internet Chess Server."),
1100 "activate",
1101 gettext_noop(" This command reactivates a game that has been terminated automatically\n"
1102 " due to checkmate or no more time on the clock. However, it does not\n"
1103 " alter those conditions. So you would have to undo a move or two, or\n"
1104 " add time to the clock with 'level' or 'time'."),
1105 "rating COMPUTERRATING OPPONENTRATING",
1106 gettext_noop(" Inputs the estimated rating for computer and for its opponent."),
1107 "new",
1108 gettext_noop(" Sets up a new game (i.e. pieces in original positions)."),
1109 "time",
1110 gettext_noop(" Inputs time left in game for computer in hundredths of a second.\n"
1111 " Mostly used by Internet Chess Server."),
1112 "hash",
1113 gettext_noop(" on - enables using the memory hash table to speed up search"),
1114 gettext_noop(" off - disables the memory hash table"),
1115 "memory N",
1116 gettext_noop(" Sets the hash table to permit storage of N MB."),
1117 "null",
1118 gettext_noop(" on - enables using the null-move heuristic to speed up search"),
1119 gettext_noop(" off - disables using the null-move heuristic"),
1120 "xboard",
1121 gettext_noop(" on - enables use of xboard/winboard"),
1122 gettext_noop(" off - disables use of xboard/winboard"),
1123 "depth N",
1124 gettext_noop(" Sets the program to look N ply (half-moves) deep for every\n"
1125 " search it performs. If there is a checkmate or other condition\n"
1126 " that does not allow that depth, then it will not be."),
1127 "level MOVES MINUTES INCREMENT",
1128 gettext_noop(" Sets time control to be MOVES in MINUTES, with each move giving\n"
1129 " an INCREMENT (in seconds, i.e. a Fischer-style clock)."),
1130 "load",
1131 "epdload",
1132 gettext_noop(" Loads a position in EPD format from disk into memory."),
1133 "save",
1134 "epdsave",
1135 gettext_noop(" Saves game position into EPD format from memory to disk."),
1136 "switch",
1137 gettext_noop(" Switches side to move."),
1138 "solve FILENAME",
1139 "solveepd FILENAME",
1140 gettext_noop(" Solves the positions in FILENAME."),
1141 "remove",
1142 gettext_noop(" Backs up two moves in game history."),
1143 "undo",
1144 gettext_noop(" Backs up one move in game history."),
1145 "usage",
1146 gettext_noop(" Displays command line syntax."),
1147 "show",
1148 gettext_noop(" board - displays the current board"),
1149 gettext_noop(" time - displays the time settings"),
1150 gettext_noop(" moves - shows all moves using one call to routine"),
1151 gettext_noop(" escape - shows moves that escape from check using one call to routine"),
1152 gettext_noop(" noncapture - shows non-capture moves"),
1153 gettext_noop(" capture - shows capture moves"),
1154 gettext_noop(" eval [or score] - shows the evaluation per piece and overall"),
1155 gettext_noop(" game - shows moves in game history"),
1156 gettext_noop(" pin - shows pinned pieces"),
1157 "test",
1158 gettext_noop(" movelist - reads in an epd file and shows legal moves for its entries"),
1159 gettext_noop(" capture - reads in an epd file and shows legal captures for its entries"),
1160 gettext_noop(" movegenspeed - tests speed of move generator"),
1161 gettext_noop(" capturespeed - tests speed of capture move generator"),
1162 gettext_noop(" eval - reads in an epd file and shows evaluation for its entries"),
1163 gettext_noop(" evalspeed - tests speed of the evaluator"),
1164 "bk",
1165 gettext_noop(" Shows moves from opening book."),
1166 "graphic",
1167 gettext_noop(" Enables display board in graphic mode."),
1168 "nographic",
1169 gettext_noop(" Disables graphic mode and display classical view."),
1170 "coords",
1171 gettext_noop(" Displays the chessboard rank and file in both graphic and classical views."),
1172 "nocoords",
1173 gettext_noop(" Does not display the chessboard rank nor file in either mode (graphic nor classical)."),
1174 NULL,
1175 NULL
1176 };
1177
cmd_help(void)1178 void cmd_help (void)
1179 /**************************************************************************
1180 *
1181 * Display all the help commands.
1182 *
1183 **************************************************************************/
1184 {
1185 const char * const *p;
1186 int count, len;
1187
1188 if (strlen(token[1])>0) {
1189 for (p=helpstr, count=0; *p; p++) {
1190 if (strncmp(*p, token[1], strlen(token[1])) == 0) {
1191 puts(*p);
1192 while (*++p && **p != ' ') /* Skip aliases */ ;
1193 for (; *p && **p == ' '; p++) {
1194 puts(_(*p));
1195 }
1196 return;
1197 }
1198 }
1199 printf(_("Help for command '%s' not found.\n\n"), token[1]);
1200 }
1201 printf(_("List of commands: (help COMMAND to get more help)\n"));
1202 for (p=helpstr, count=0; *p; p++) {
1203 len = strcspn(*p, " ");
1204 if (len > 0) {
1205 count += printf("%.*s ", len, *p);
1206 if (count > 60) {
1207 count = 0;
1208 puts("");
1209 }
1210 }
1211 }
1212 puts("");
1213 }
1214
1215 /*
1216 * Try a method table, one could also include the documentation
1217 * strings here
1218 */
1219
1220 struct methodtable {
1221 const char *name;
1222 void (*method) (void);
1223 };
1224
1225 /* Last entry contains two NULL pointers */
1226
1227 /* List commands we don't implement to avoid illegal moving them */
1228
1229 const struct methodtable commands[] = {
1230 { "?", cmd_movenow },
1231 { "accepted", cmd_accepted },
1232 { "activate", cmd_activate },
1233 { "analyze", cmd_analyze },
1234 { "bk", cmd_bk },
1235 { "black", cmd_black },
1236 { "book", cmd_book },
1237 { "depth", cmd_depth },
1238 { "easy", cmd_easy },
1239 { "edit", cmd_edit },
1240 { "epdload", cmd_load },
1241 { "epdsave", cmd_save },
1242 { "exit", cmd_exit },
1243 { "force", cmd_force },
1244 { "go", cmd_go },
1245 { "graphic", cmd_graphic },
1246 { "hard", cmd_hard },
1247 { "hash", cmd_hash },
1248 { "help", cmd_help },
1249 { "hint", cmd_hint },
1250 { "ics", cmd_ics },
1251 { "last", cmd_last },
1252 { "level", cmd_level },
1253 { "list", cmd_list },
1254 { "load", cmd_load },
1255 { "manual", cmd_manual },
1256 { "memory", cmd_memory },
1257 { "name", cmd_name },
1258 { "new", cmd_new },
1259 { "next", cmd_next },
1260 { "n", cmd_next },
1261 { "nographic", cmd_nographic },
1262 { "nopost", cmd_nopost },
1263 { "null", cmd_null },
1264 { "otim", cmd_otim },
1265 { "pgnload", cmd_pgnload },
1266 { "pgnreplay", cmd_pgnreplay },
1267 { "pgnsave", cmd_pgnsave },
1268 { "ping", cmd_ping },
1269 { "post", cmd_post },
1270 { "previous", cmd_previous },
1271 { "p", cmd_previous },
1272 { "first", cmd_first },
1273 { "protover", cmd_protover },
1274 { "quit", cmd_quit },
1275 { "random", cmd_random },
1276 { "rating", cmd_rating },
1277 { "rejected", cmd_rejected },
1278 { "remove", cmd_remove },
1279 { "result", cmd_result },
1280 { "save", cmd_save },
1281 { "setboard", cmd_setboard },
1282 { "show", cmd_show },
1283 { "solve", cmd_solve },
1284 { "solveepd", cmd_solve },
1285 { "st", cmd_st },
1286 { "switch", cmd_switch },
1287 { "test", cmd_test },
1288 { "time", cmd_time },
1289 { "undo", cmd_undo },
1290 { "usage", cmd_usage },
1291 { "usermove", cmd_usermove },
1292 { "variant", cmd_variant },
1293 { "version", cmd_version },
1294 { "white", cmd_white },
1295 { "xboard", cmd_xboard },
1296 { "coords", cmd_coords},
1297 { "nocoords", cmd_nocoords},
1298 { NULL, NULL }
1299 };
1300
parse_input(void)1301 void parse_input(void)
1302 /*************************************************************************
1303 *
1304 * This is the main user command interface driver.
1305 *
1306 *************************************************************************/
1307 {
1308 leaf *ptr;
1309 const struct methodtable * meth;
1310
1311 dbg_printf("parse_input() called, inputstr = *%s*\n", inputstr);
1312
1313 /* Initialize variables used to send messages to the engine */
1314 SetDataToEngine( "" );
1315 ExpectAnswerFromEngine( false );
1316 SetUserInputValidMove( 0 );
1317 ChangeColor( false );
1318
1319 split_input();
1320
1321 for (meth = commands; meth->name != NULL; meth++) {
1322 if (tokeneq(token[0], meth->name)) {
1323 meth->method();
1324 return;
1325 }
1326 }
1327
1328 /* OK, no known command, this should be a move */
1329 char cleanMove[MAXSTR]="";
1330 ptr = ValidateMove (token[0], cleanMove);
1331 if (ptr != NULL) {
1332 /* Since the user entered a move:
1333 * 1. The move must be sent to the engine.
1334 * 2. A reply is expected from the engine.
1335 */
1336 SetUserInputValidMove( 1 );
1337 SetDataToEngine( cleanMove );
1338 pgnloaded = 0;
1339 ExpectAnswerFromEngine( true );
1340 SANMove (ptr->move, 1);
1341 MakeMove (board.side, &ptr->move);
1342 strcpy (Game[GameCnt].SANmv, SANmv);
1343 printf("%d. ",GameCnt/2+1);
1344 printf("%s",token[0]);
1345 if (ofp != stdout) {
1346 fprintf(ofp,"%d. ",GameCnt/2+1);
1347 fprintf(ofp,"%s",token[0]);
1348 }
1349 putchar('\n');
1350 fflush(stdout);
1351 if (ofp != stdout) {
1352 fputc('\n',ofp);
1353 fflush(ofp);
1354 }
1355 if (!(flags & XBOARD)) ShowBoard ();
1356 SET (flags, THINK);
1357 }
1358 else {
1359 /*
1360 * Must Output Illegal move to prevent Xboard accepting illegal
1361 * en passant captures and other subtle mistakes
1362 */
1363 printf(_("Invalid move: %s\n"),token[0]);
1364 fflush(stdout);
1365 }
1366 }
1367
check_board()1368 void check_board()
1369 /*************************************************************************
1370 *
1371 * When the board is changed by commands, call the Validation
1372 * routine, and if it fails set flags to prevent the analysis of
1373 * illegal positions, as the code is not robust against the
1374 * analysis of such positions (To Do).
1375 *
1376 *************************************************************************/
1377 {
1378 if (!ValidateBoard()) {
1379 SET (flags, ENDED);
1380 if (flags & XBOARD) {
1381 printf ("telluser Board is wrong!\n");
1382 fflush(stdout);
1383 } else {
1384 printf (_("Board is wrong!\n"));
1385 }
1386 }
1387 }
1388
1389