1 #include <ctype.h>
2 #include <signal.h>
3 #include "chess.h"
4 #include "data.h"
5 #if defined(UNIX)
6 # include <unistd.h>
7 # include <signal.h>
8 #endif
9 #include "epdglue.h"
10 #include "tbprobe.h"
11 /* last modified 08/03/16 */
12 /*
13 *******************************************************************************
14 * *
15 * Option() is used to handle user input necessary to control/customize the *
16 * program. It performs all functions excepting chess move input which is *
17 * handled by main(). *
18 * *
19 *******************************************************************************
20 */
Option(TREE * RESTRICT tree)21 int Option(TREE * RESTRICT tree) {
22 int v;
23
24 /*
25 ************************************************************
26 * *
27 * parse the input. If it looks like a FEN string, don't *
28 * parse using "/" as a separator, otherwise do. *
29 * *
30 ************************************************************
31 */
32 if (StrCnt(buffer, '/') >= 7)
33 nargs = ReadParse(buffer, args, " \t;=");
34 else
35 nargs = ReadParse(buffer, args, " \t;=/");
36 if (!nargs)
37 return 1;
38 if (args[0][0] == '#')
39 return 1;
40 /*
41 ************************************************************
42 * *
43 * EPD implementation interface code. EPD commands can *
44 * not be handled if the program is actually searching in *
45 * a real game, and if Crafty is "pondering" this has to *
46 * be stopped. *
47 * *
48 ************************************************************
49 */
50 #if defined(EPD)
51 if (initialized) {
52 if (EGCommandCheck(buffer)) {
53 if (thinking || pondering)
54 return 2;
55 else {
56 EGCommand(buffer);
57 return 1;
58 }
59 }
60 }
61 #endif
62 /*
63 ************************************************************
64 * *
65 * "!" character is a 'shell escape' that passes the rest *
66 * of the command to a shell for execution. Note that it *
67 * is ignored if in xboard mode because one could use your *
68 * zippy2password to execute commands on your local *
69 * machine, probably something that is not wanted. *
70 * *
71 ************************************************************
72 */
73 if (buffer[0] == '!') {
74 if (!xboard) {
75 v = system(strchr(buffer, '!') + 1);
76 if (v != 0)
77 perror("Option() system() error: ");
78 }
79 }
80 /*
81 ************************************************************
82 * *
83 * "." ignores "." if it happens to get to this point, if *
84 * xboard is running. *
85 * *
86 ************************************************************
87 */
88 else if (OptionMatch(".", *args)) {
89 if (xboard) {
90 printf("stat01: 0 0 0 0 0\n");
91 fflush(stdout);
92 return 1;
93 } else
94 return 0;
95 }
96 /*
97 ************************************************************
98 * *
99 * "accepted" handles the new xboard protocol version 2 *
100 * accepted command. *
101 * *
102 ************************************************************
103 */
104 else if (OptionMatch("accepted", *args)) {
105 }
106 /*
107 ************************************************************
108 * *
109 * "adaptive" sets the new adaptive hash algorithm *
110 * parameters. It requires five parameters. The first is *
111 * an estimated NPS, the second is the minimum hash size, *
112 * and the third is the maximum hash size. The adaptive *
113 * algorithm will look at the time control, and try to *
114 * adjust the hash sizes to an optimal value without *
115 * dropping below the minimum or exceeding the maximum *
116 * memory size given. The min/max sizes can be given *
117 * using the same syntax as the hash= command, ie xxx, *
118 * xxxK or xxxM will all work. The fourth and fifth *
119 * parameters are used to limit hashp in the same way. *
120 * *
121 ************************************************************
122 */
123 else if (OptionMatch("adaptive", *args)) {
124 if (nargs != 6) {
125 printf("usage: adaptive NPS hmin hmax pmin pmax\n");
126 return 1;
127 }
128 if (nargs > 1) {
129 adaptive_hash = atoiKMB(args[1]);
130 adaptive_hash_min = atoiKMB(args[2]);
131 adaptive_hash_max = atoiKMB(args[3]);
132 adaptive_hashp_min = atoiKMB(args[4]);
133 adaptive_hashp_max = atoiKMB(args[5]);
134 }
135 Print(32, "adaptive estimated NPS = %s\n", DisplayKMB(adaptive_hash, 1));
136 Print(32, "adaptive minimum hsize = %s\n", DisplayKMB(adaptive_hash_min,
137 1));
138 Print(32, "adaptive maximum hsize = %s\n", DisplayKMB(adaptive_hash_max,
139 1));
140 Print(32, "adaptive minimum psize = %s\n", DisplayKMB(adaptive_hashp_min,
141 1));
142 Print(32, "adaptive maximum psize = %s\n", DisplayKMB(adaptive_hashp_max,
143 1));
144 }
145 /*
146 ************************************************************
147 * *
148 * "alarm" command turns audible move warning on/off. *
149 * *
150 ************************************************************
151 */
152 else if (OptionMatch("alarm", *args)) {
153 if (!strcmp(args[1], "on"))
154 audible_alarm = 0x07;
155 else if (!strcmp(args[1], "off"))
156 audible_alarm = 0x00;
157 else
158 printf("usage: alarm on|off\n");
159 }
160 /*
161 ************************************************************
162 * *
163 * "analyze" puts Crafty in analyze mode, where it reads *
164 * moves in and between moves, computes as though it is *
165 * trying to find the best move to make. When another *
166 * move is entered, it switches sides and continues. It *
167 * will never make a move on its own, rather, it will *
168 * continue to analyze until an "exit" command is given. *
169 * *
170 ************************************************************
171 */
172 else if (OptionMatch("analyze", *args)) {
173 if (thinking || pondering)
174 return 2;
175 Analyze();
176 }
177 /*
178 ************************************************************
179 * *
180 * "annotate" command is used to read a series of moves *
181 * and analyze the resulting game, producing comments as *
182 * requested by the user. This also handles the annotateh *
183 * (html) and annotatet (LaTex) output forms of the *
184 * command. *
185 * *
186 ************************************************************
187 */
188 else if (OptionMatch("annotate", *args) || OptionMatch("annotateh", *args)
189 || OptionMatch("annotatet", *args)) {
190 if (thinking || pondering)
191 return 2;
192 Annotate();
193 }
194 /*
195 ************************************************************
196 * *
197 * "autotune" command is used to automatically tune the *
198 * SMP search parameters that affect search efficiency. *
199 * *
200 ************************************************************
201 */
202 else if (OptionMatch("autotune", *args)) {
203 if (thinking || pondering)
204 return 2;
205 AutoTune(nargs, args);
206 }
207 /*
208 ************************************************************
209 * *
210 * "batch" command disables asynchronous I/O so that a *
211 * stream of commands can be put into a file and they are *
212 * not executed instantly. *
213 * *
214 ************************************************************
215 */
216 else if (OptionMatch("batch", *args)) {
217 if (!strcmp(args[1], "on"))
218 batch_mode = 1;
219 else if (!strcmp(args[1], "off"))
220 batch_mode = 0;
221 else
222 printf("usage: batch on|off\n");
223 }
224 /*
225 ************************************************************
226 * *
227 * "beep" command is ignored. [xboard compatibility] *
228 * *
229 ************************************************************
230 */
231 else if (OptionMatch("beep", *args)) {
232 return xboard;
233 }
234 /*
235 ************************************************************
236 * *
237 * "bench" runs internal performance benchmark. An *
238 * optional second argument can increase or decrease the *
239 * time it takes. "bench 1" increases the default depth *
240 * by one ply, and "bench -1" reduces the depth to speed *
241 * it up. *
242 * *
243 ************************************************************
244 */
245 else if (OptionMatch("bench", *args)) {
246 int mod = 0;
247
248 if (nargs > 1)
249 mod = atoi(args[1]);
250 (void) Bench(mod, 0);
251 }
252 /*
253 ************************************************************
254 * *
255 * "bk" book command from xboard sends the suggested book *
256 * moves. *
257 * *
258 ************************************************************
259 */
260 else if (OptionMatch("bk", *args)) {
261 printf("\t%s\n\n", book_hint);
262 fflush(stdout);
263 return xboard;
264 }
265 /*
266 ************************************************************
267 * *
268 * "black" command sets black to move (Flip(wtm)). *
269 * *
270 ************************************************************
271 */
272 else if (OptionMatch("white", *args)) {
273 if (thinking || pondering)
274 return 2;
275 game_wtm = 1;
276 ponder_move = 0;
277 last_pv.pathd = 0;
278 last_pv.pathl = 0;
279 if (!game_wtm)
280 Pass();
281 force = 0;
282 } else if (OptionMatch("black", *args)) {
283 if (thinking || pondering)
284 return 2;
285 game_wtm = 0;
286 ponder_move = 0;
287 last_pv.pathd = 0;
288 last_pv.pathl = 0;
289 if (game_wtm)
290 Pass();
291 force = 0;
292 }
293 /*
294 ************************************************************
295 * *
296 * "bogus" command is ignored. [xboard compatibility] *
297 * *
298 ************************************************************
299 */
300 else if (OptionMatch("bogus", *args)) {
301 return xboard;
302 }
303 /*
304 ************************************************************
305 * *
306 * "book" command updates/creates the opening book file. *
307 * *
308 ************************************************************
309 */
310 else if (OptionMatch("book", *args)) {
311 nargs = ReadParse(buffer, args, " \t;");
312 Bookup(tree, nargs, args);
313 } else if (!strcmp("create", *(args + 1))) {
314 nargs = ReadParse(buffer, args, " \t;");
315 Bookup(tree, nargs, args);
316 }
317 /*
318 ************************************************************
319 * *
320 * "bookw" command updates the book selection weights. *
321 * *
322 ************************************************************
323 */
324 else if (OptionMatch("bookw", *args)) {
325 if (nargs > 1) {
326 if (OptionMatch("frequency", args[1]))
327 book_weight_freq = atof(args[2]);
328 else if (OptionMatch("evaluation", args[1]))
329 book_weight_eval = atof(args[2]);
330 else if (OptionMatch("learning", args[1]))
331 book_weight_learn = atof(args[2]);
332 } else {
333 Print(32, "frequency (freq)..............%4.2f\n", book_weight_freq);
334 Print(32, "static evaluation (eval)......%4.2f\n", book_weight_eval);
335 Print(32, "learning (learn)..............%4.2f\n", book_weight_learn);
336 }
337 }
338 /*
339 ************************************************************
340 * *
341 * "clock" command displays chess clock. *
342 * *
343 ************************************************************
344 */
345 else if (OptionMatch("clock", *args)) {
346 int side;
347
348 for (side = white; side >= black; side--) {
349 Print(32, "time remaining (%s): %s", (side) ? "white" : "black",
350 DisplayHHMMSS(tc_time_remaining[side]));
351 if (tc_sudden_death != 1)
352 Print(32, " (%d more moves)", tc_moves_remaining[side]);
353 Print(32, "\n");
354 }
355 Print(32, "\n");
356 if (tc_sudden_death == 1)
357 Print(32, "Sudden-death time control in effect\n");
358 }
359 /*
360 ************************************************************
361 * *
362 * "computer" lets Crafty know it is playing a computer. *
363 * *
364 ************************************************************
365 */
366 else if (OptionMatch("computer", *args)) {
367 Print(32, "playing a computer!\n");
368 accept_draws = 1;
369 if (resign)
370 resign = 10;
371 resign_count = 4;
372 usage_level = 0;
373 books_file = (computer_bs_file) ? computer_bs_file : normal_bs_file;
374 }
375 /*
376 ************************************************************
377 * *
378 * "display" command displays the chess board. *
379 * *
380 * "display" command sets specific display options which *
381 * control how "chatty" the program is. In the variable *
382 * display_options, the following bits are set/cleared *
383 * based on the option chosen. *
384 * *
385 * 1 -> display move/time/results/etc. *
386 * 2 -> display PV. *
387 * 4 -> display fail high / fail low moves *
388 * 8 -> display search statistics. *
389 * 16 -> display root moves as they are searched. *
390 * 32 -> display general informational messages. *
391 * 64 -> display ply-1 move list / flags after each *
392 * iteration. *
393 * 128 -> display root moves and scores before search *
394 * begins. *
395 * 2048 -> error messages (can not be disabled). *
396 * *
397 ************************************************************
398 */
399 else if (OptionMatch("display", *args)) {
400 int i, set, old_display_options = display_options;
401 char *doptions[8] = { "moveinfo", "pv", "fail", "stats", "moves", "info",
402 "ply1", "movelist"
403 };
404 char *descriptions[8] = { "display move time/results/etc",
405 "principal variation", "fail highs/lows", "search statistics",
406 "root moves as they are searched", "general information",
407 "ply1 move list after each iteration",
408 "root move list and scores prior to search"
409 };
410
411 if (nargs > 1) {
412 if (!strcmp(args[1], "all"))
413 old_display_options = ~display_options;
414 for (i = 0; i < 8; i++) {
415 if (strstr(args[1], doptions[i])) {
416 if (strstr(args[1], "no"))
417 set = 0;
418 else
419 set = 1;
420 display_options &= ~(1 << i);
421 display_options |= set << i;
422 break;
423 }
424 }
425 for (i = 0; i < 8; i++) {
426 if ((old_display_options & (1 << i)) != (display_options & (1 << i))) {
427 Print(32, "display ");
428 if (!(display_options & (1 << i)))
429 Print(32, "no");
430 Print(32, "%s (%s)\n", doptions[i], descriptions[i]);
431 }
432 }
433 } else
434 DisplayChessBoard(stdout, display);
435 }
436 /*
437 ************************************************************
438 * *
439 * "debug" handles the new debug command that is often *
440 * modified to test some modified code function. *
441 * *
442 ************************************************************
443 */
444 else if (OptionMatch("debug", *args)) {
445 Print(32, "ERROR: no debug code included\n");
446 }
447 /*
448 ************************************************************
449 * *
450 * "depth" command sets a specific search depth to *
451 * control the tree search depth. [xboard compatibility]. *
452 * *
453 ************************************************************
454 */
455 else if (OptionMatch("depth", *args)) {
456 if (nargs < 2) {
457 printf("usage: depth <n>\n");
458 return 1;
459 }
460 search_depth = atoi(args[1]);
461 Print(32, "search depth set to %d.\n", search_depth);
462 }
463 /*
464 ************************************************************
465 * *
466 * "draw" is used to offer Crafty a draw, or to control *
467 * whether Crafty will offer and/or accept draw offers. *
468 * *
469 ************************************************************
470 */
471 else if (OptionMatch("draw", *args)) {
472 if (nargs == 1) {
473 draw_offer_pending = 1;
474 if (draw_offered) {
475 Print(4095, "1/2-1/2 {Draw agreed}\n");
476 strcpy(pgn_result, "1/2-1/2");
477 }
478 } else {
479 if (!strcmp(args[1], "accept")) {
480 accept_draws = 1;
481 Print(32, "accept draw offers\n");
482 } else if (!strcmp(args[1], "decline")) {
483 accept_draws = 0;
484 Print(32, "decline draw offers\n");
485 } else if (!strcmp(args[1], "dynamic")) {
486 if (nargs > 2)
487 dynamic_draw_score = atoi(args[2]);
488 Print(32, "dynamic draw scores %s\n",
489 (dynamic_draw_score) ? "enabled" : "disabled");
490 } else if (!strcmp(args[1], "offer")) {
491 offer_draws = 1;
492 Print(32, "offer draws\n");
493 } else if (!strcmp(args[1], "nooffer")) {
494 offer_draws = 0;
495 Print(32, "do not offer draws\n");
496 } else
497 Print(32, "usage: draw accept|decline|offer|nooffer\n");
498 }
499 }
500 /*
501 ************************************************************
502 * *
503 * "easy" command disables thinking on opponent's time. *
504 * *
505 ************************************************************
506 */
507 else if (OptionMatch("easy", *args)) {
508 if (thinking || pondering)
509 return 2;
510 ponder = 0;
511 Print(32, "pondering disabled.\n");
512 }
513 /*
514 ************************************************************
515 * *
516 * "echo" command displays messages from command file. *
517 * *
518 ************************************************************
519 */
520 else if (OptionMatch("echo", *args) || OptionMatch("title", *args)) {
521 }
522 /*
523 ************************************************************
524 * *
525 * "edit" command modifies the board position. *
526 * *
527 ************************************************************
528 */
529 else if (OptionMatch("edit", *args)) {
530 if (thinking || pondering)
531 return 2;
532 Edit();
533 move_number = 1; /* discard history */
534 if (!game_wtm) {
535 game_wtm = 1;
536 Pass();
537 }
538 ponder_move = 0;
539 last_pv.pathd = 0;
540 last_pv.pathl = 0;
541 strcpy(buffer, "savepos *");
542 Option(tree);
543 }
544 /*
545 ************************************************************
546 * *
547 * "egtb" command enables syzygy endgame database tables. *
548 * *
549 ************************************************************
550 */
551 else if (OptionMatch("egtb", *args)) {
552 #if defined(SYZYGY)
553 if (!strcmp(args[1], "off"))
554 EGTBlimit = 0;
555 else {
556 if (!EGTB_setup) {
557 tb_init(tb_path);
558 EGTB_setup = 1;
559 }
560 EGTBlimit = TB_LARGEST;
561 }
562 if (EGTBlimit)
563 Print(32, "SYZYGY EGTB access enabled, %d piece TBs found\n",
564 TB_LARGEST);
565 else
566 Print(32, "SYZYGY EGTB access disabled.\n");
567 #else
568 Print(32, "SYZYGY support not included (no -DSYZYGY)\n");
569 #endif
570 }
571 /*
572 ************************************************************
573 * *
574 * "egtbd" command sets the probe depth limit. If the *
575 * remaining depth is < this limit, probes are not done to *
576 * avoid slowing the search unnecessarily. *
577 * *
578 ************************************************************
579 */
580 #if defined(SYZYGY)
581 else if (OptionMatch("egtbd", *args)) {
582 if (nargs > 1)
583 EGTB_depth = atoi(args[1]);
584 Print(32, "EGTB probe depth set to %d\n", EGTB_depth);
585 }
586 #endif
587 /*
588 ************************************************************
589 * *
590 * "end" (or "quit") command terminates the program. *
591 * *
592 ************************************************************
593 */
594 else if (OptionMatch("end", *args) || OptionMatch("quit", *args)) {
595 abort_search = 1;
596 quit = 1;
597 last_search_value =
598 (crafty_is_white) ? last_search_value : -last_search_value;
599 if (moves_out_of_book)
600 LearnBook();
601 if (thinking || pondering)
602 return 1;
603 CraftyExit(0);
604 }
605 /*
606 ************************************************************
607 * *
608 * "evtest" command runs a test suite of problems and *
609 * prints evaluations only. *
610 * *
611 ************************************************************
612 */
613 else if (OptionMatch("evtest", *args)) {
614 if (thinking || pondering)
615 return 2;
616 if (nargs < 2) {
617 printf("usage: evtest <filename>\n");
618 return 1;
619 }
620 EVTest(args[1]);
621 ponder_move = 0;
622 last_pv.pathd = 0;
623 last_pv.pathl = 0;
624 }
625 /*
626 ************************************************************
627 * *
628 * "exit" command resets input device to STDIN. *
629 * *
630 ************************************************************
631 */
632 else if (OptionMatch("exit", *args)) {
633 if (analyze_mode)
634 return 0;
635 if (input_stream != stdin)
636 fclose(input_stream);
637 input_stream = stdin;
638 ReadClear();
639 Print(32, "\n");
640 }
641 /*
642 ************************************************************
643 * *
644 * "flag" command controls whether Crafty will call the *
645 * flag in xboard/winboard games (to end the game.) *
646 * *
647 ************************************************************
648 */
649 else if (OptionMatch("flag", *args)) {
650 if (nargs < 2) {
651 printf("usage: flag on|off\n");
652 return 1;
653 }
654 if (!strcmp(args[1], "on"))
655 call_flag = 1;
656 else if (!strcmp(args[1], "off"))
657 call_flag = 0;
658 if (call_flag)
659 Print(32, "end game on time forfeits\n");
660 else
661 Print(32, "ignore time forfeits\n");
662 }
663 /*
664 ************************************************************
665 * *
666 * "flip" command flips the board, interchanging each *
667 * rank with the corresponding rank on the other half of *
668 * the board, and also reverses the color of all pieces. *
669 * *
670 ************************************************************
671 */
672 else if (OptionMatch("flip", *args)) {
673 int file, rank, piece, temp;
674
675 if (thinking || pondering)
676 return 2;
677 for (rank = 0; rank < 4; rank++) {
678 for (file = 0; file < 8; file++) {
679 piece = -PcOnSq((rank << 3) + file);
680 PcOnSq((rank << 3) + file) = -PcOnSq(((7 - rank) << 3) + file);
681 PcOnSq(((7 - rank) << 3) + file) = piece;
682 }
683 }
684 game_wtm = Flip(game_wtm);
685 temp = Castle(0, white);
686 Castle(0, white) = Castle(0, black);
687 Castle(0, black) = temp;
688 SetChessBitBoards(tree);
689 #if defined(DEBUG)
690 ValidatePosition(tree, 0, game_wtm, "Option().flip");
691 #endif
692 }
693 /*
694 ************************************************************
695 * *
696 * "flop" command flops the board, interchanging each *
697 * file with the corresponding file on the other half of *
698 * the board. *
699 * *
700 ************************************************************
701 */
702 else if (OptionMatch("flop", *args)) {
703 int file, rank, piece;
704
705 if (thinking || pondering)
706 return 2;
707 for (rank = 0; rank < 8; rank++) {
708 for (file = 0; file < 4; file++) {
709 piece = PcOnSq((rank << 3) + file);
710 PcOnSq((rank << 3) + file) = PcOnSq((rank << 3) + 7 - file);
711 PcOnSq((rank << 3) + 7 - file) = piece;
712 }
713 }
714 SetChessBitBoards(tree);
715 #if defined(DEBUG)
716 ValidatePosition(tree, 0, game_wtm, "Option().flop");
717 #endif
718 }
719 /*
720 ************************************************************
721 * *
722 * "force" command forces the program to make a specific *
723 * move instead of its last chosen move. *
724 * *
725 ************************************************************
726 */
727 else if (OptionMatch("force", *args)) {
728 int move, movenum, save_move_number;
729 char text[16];
730
731 if (thinking || pondering)
732 return 3;
733 if (xboard) {
734 force = 1;
735 return 3;
736 }
737 if (nargs < 2) {
738 printf("usage: force <move>\n");
739 return 1;
740 }
741 ponder_move = 0;
742 presult = 0;
743 last_pv.pathd = 0;
744 last_pv.pathl = 0;
745 save_move_number = move_number;
746 movenum = move_number;
747 if (game_wtm)
748 movenum--;
749 strcpy(text, args[1]);
750 sprintf(buffer, "reset %d", movenum);
751 game_wtm = Flip(game_wtm);
752 Option(tree);
753 move = InputMove(tree, 0, game_wtm, 0, 0, text);
754 if (move) {
755 if (input_stream != stdin)
756 printf("%s\n", OutputMove(tree, 0, game_wtm, move));
757 if (history_file) {
758 fseek(history_file, ((movenum - 1) * 2 + 1 - game_wtm) * 10,
759 SEEK_SET);
760 fprintf(history_file, "%9s\n", OutputMove(tree, 0, game_wtm, move));
761 }
762 MakeMoveRoot(tree, game_wtm, move);
763 last_pv.pathd = 0;
764 last_pv.pathl = 0;
765 } else if (input_stream == stdin)
766 printf("illegal move.\n");
767 game_wtm = Flip(game_wtm);
768 move_number = save_move_number;
769 strcpy(ponder_text, "none");
770 }
771 /*
772 ************************************************************
773 * *
774 * "go" command does nothing, except force main() to start *
775 * a search. ("move" is an alias for go). *
776 * *
777 ************************************************************
778 */
779 else if (OptionMatch("go", *args) || OptionMatch("move", *args)) {
780 int t;
781 char temp[128];
782
783 if (thinking || pondering)
784 return 2;
785 if (game_wtm) {
786 if (strncmp(pgn_white, "Crafty", 6)) {
787 strcpy(temp, pgn_white);
788 strcpy(pgn_white, pgn_black);
789 strcpy(pgn_black, temp);
790 }
791 } else {
792 if (strncmp(pgn_black, "Crafty", 6)) {
793 strcpy(temp, pgn_white);
794 strcpy(pgn_white, pgn_black);
795 strcpy(pgn_black, temp);
796 }
797 }
798 t = tc_time_remaining[white];
799 tc_time_remaining[white] = tc_time_remaining[black];
800 tc_time_remaining[black] = t;
801 t = tc_moves_remaining[white];
802 tc_moves_remaining[white] = tc_moves_remaining[black];
803 tc_moves_remaining[black] = t;
804 force = 0;
805 return -1;
806 }
807 /*
808 ************************************************************
809 * *
810 * "history" command displays game history (moves). *
811 * *
812 ************************************************************
813 */
814 else if (OptionMatch("history", *args)) {
815 int i;
816 char buffer[128];
817
818 if (history_file) {
819 printf(" white black\n");
820 for (i = 0; i < (move_number - 1) * 2 - game_wtm + 1; i++) {
821 fseek(history_file, i * 10, SEEK_SET);
822 v = fscanf(history_file, "%s", buffer);
823 if (v <= 0)
824 perror("Option() fscanf error: ");
825 if (!(i % 2))
826 printf("%3d", i / 2 + 1);
827 printf(" %-10s", buffer);
828 if (i % 2 == 1)
829 printf("\n");
830 }
831 if (Flip(game_wtm))
832 printf(" ...\n");
833 }
834 }
835 /*
836 ************************************************************
837 * *
838 * "hard" command enables thinking on opponent's time. *
839 * *
840 ************************************************************
841 */
842 else if (OptionMatch("hard", *args)) {
843 ponder = 1;
844 Print(32, "pondering enabled.\n");
845 }
846 /*
847 ************************************************************
848 * *
849 * "hash" command controls the transposition table size. *
850 * The size can be entered in one of four ways: *
851 * *
852 * hash=nnn where nnn is in bytes. *
853 * hash=nnnK where nnn is in K bytes. *
854 * hash=nnnM where nnn is in M bytes. *
855 * hash=nnnG where nnn is in G bytes. *
856 * *
857 * the only restriction is that the hash table is computed *
858 * as a perfect power of 2. Any value that is not a *
859 * perfect power of 2 is rounded down so that it is, in *
860 * order to avoid breaking the addressing scheme. *
861 * *
862 ************************************************************
863 */
864 else if (OptionMatch("hash", *args)) {
865 size_t old_hash_size = hash_table_size, new_hash_size;
866
867 if (thinking || pondering)
868 return 2;
869 if (nargs > 1) {
870 allow_memory = 0;
871 if (xboard)
872 Print(4095, "Warning-- xboard 'memory' option disabled\n");
873 new_hash_size = atoiKMB(args[1]);
874 if (new_hash_size < 64 * 1024) {
875 printf("ERROR. Minimum hash table size is 64K bytes.\n");
876 return 1;
877 }
878 hash_table_size = ((1ull) << MSB(new_hash_size)) / 16;
879 AlignedRemalloc((void *) &hash_table, 64,
880 hash_table_size * sizeof(HASH_ENTRY));
881 if (!hash_table) {
882 printf("AlignedRemalloc() failed, not enough memory.\n");
883 exit(1);
884 }
885 hash_mask = (hash_table_size - 1) & ~3;
886 }
887 Print(32, "hash table memory = %s bytes",
888 DisplayKMB(hash_table_size * sizeof(HASH_ENTRY), 1));
889 Print(32, " (%s entries).\n", DisplayKMB(hash_table_size, 1));
890 InitializeHashTables(old_hash_size != hash_table_size);
891 }
892 /*
893 ************************************************************
894 * *
895 * "phash" command controls the path hash table size. The *
896 * size can be entered in one of four ways: *
897 * *
898 * phash=nnn where nnn is in bytes. *
899 * phash=nnnK where nnn is in K bytes. *
900 * phash=nnnM where nnn is in M bytes. *
901 * phash=nnnG where nnn is in G bytes. *
902 * *
903 * the only restriction is that the path hash table must *
904 * have a perfect power of 2 entries. The value entered *
905 * will be rounded down to meet that requirement. *
906 * *
907 ************************************************************
908 */
909 else if (OptionMatch("phash", *args)) {
910 size_t old_hash_size = hash_path_size, new_hash_size;
911
912 if (thinking || pondering)
913 return 2;
914 if (nargs > 1) {
915 new_hash_size = atoiKMB(args[1]);
916 if (new_hash_size < 64 * 1024) {
917 printf("ERROR. Minimum phash table size is 64K bytes.\n");
918 return 1;
919 }
920 hash_path_size = ((1ull) << MSB(new_hash_size / sizeof(HPATH_ENTRY)));
921 AlignedRemalloc((void *) &hash_path, 64,
922 sizeof(HPATH_ENTRY) * hash_path_size);
923 if (!hash_path) {
924 printf("AlignedRemalloc() failed, not enough memory.\n");
925 hash_path_size = 0;
926 hash_path = 0;
927 }
928 hash_path_mask = (hash_path_size - 1) & ~15;
929 }
930 Print(32, "hash path table memory = %s bytes",
931 DisplayKMB(hash_path_size * sizeof(HPATH_ENTRY), 1));
932 Print(32, " (%s entries).\n", DisplayKMB(hash_path_size, 1));
933 InitializeHashTables(old_hash_size != hash_path_size);
934 }
935 /*
936 ************************************************************
937 * *
938 * "hashp" command controls the pawn hash table size. *
939 * *
940 ************************************************************
941 */
942 else if (OptionMatch("hashp", *args)) {
943 size_t old_hash_size = pawn_hash_table_size, new_hash_size;
944
945 if (thinking || pondering)
946 return 2;
947 if (nargs > 1) {
948 allow_memory = 0;
949 if (xboard)
950 Print(4095, "Warning-- xboard 'memory' option disabled\n");
951 new_hash_size = atoiKMB(args[1]);
952 if (new_hash_size < 16 * 1024) {
953 printf("ERROR. Minimum pawn hash table size is 16K bytes.\n");
954 return 1;
955 }
956 pawn_hash_table_size =
957 1ull << MSB(new_hash_size / sizeof(PAWN_HASH_ENTRY));
958 AlignedRemalloc((void *) &pawn_hash_table, 64,
959 sizeof(PAWN_HASH_ENTRY) * pawn_hash_table_size);
960 if (!pawn_hash_table) {
961 printf("AlignedRemalloc() failed, not enough memory.\n");
962 exit(1);
963 }
964 pawn_hash_mask = pawn_hash_table_size - 1;
965 }
966 Print(32, "pawn hash table memory = %s bytes",
967 DisplayKMB(pawn_hash_table_size * sizeof(PAWN_HASH_ENTRY), 1));
968 Print(32, " (%s entries).\n", DisplayKMB(pawn_hash_table_size, 1));
969 InitializeHashTables(old_hash_size != pawn_hash_table_size);
970 }
971 /*
972 ************************************************************
973 * *
974 * "help" command lists commands/options. *
975 * *
976 ************************************************************
977 */
978 else if (OptionMatch("help", *args)) {
979 FILE *helpfile;
980 char *readstat = (char *) -1;
981 char fname[128];
982 int lines = 0;
983
984 sprintf(fname, "%s/crafty.hlp", book_path);
985 helpfile = fopen(fname, "r");
986 if (!helpfile) {
987 printf("ERROR. Unable to open \"crafty.hlp\" -- help unavailable\n");
988 return 1;
989 }
990 if (nargs > 1) {
991 while (1) {
992 readstat = fgets(buffer, 128, helpfile);
993 if (!readstat) {
994 printf("Sorry, no help available for \"%s\"\n", args[1]);
995 fclose(helpfile);
996 return 1;
997 }
998 if (buffer[0] == '<') {
999 if (strstr(buffer, args[1]))
1000 break;
1001 }
1002 }
1003 }
1004 while (1) {
1005 readstat = fgets(buffer, 128, helpfile);
1006 if (!readstat)
1007 break;
1008 if (strchr(buffer, '\n'))
1009 *strchr(buffer, '\n') = 0;
1010 if (!strcmp(buffer, "<end>"))
1011 break;
1012 printf("%s\n", buffer);
1013 lines++;
1014 if (lines > 22) {
1015 lines = 0;
1016 printf("<return> for more...");
1017 fflush(stdout);
1018 Read(1, buffer);
1019 }
1020 }
1021 fclose(helpfile);
1022 }
1023 /*
1024 ************************************************************
1025 * *
1026 * "hint" displays the expected move based on the last *
1027 * search done. [xboard compatibility] *
1028 * *
1029 ************************************************************
1030 */
1031 else if (OptionMatch("hint", *args)) {
1032 if (strlen(ponder_text)) {
1033 printf("Hint: %s\n", ponder_text);
1034 fflush(stdout);
1035 }
1036 }
1037 /*
1038 ************************************************************
1039 * *
1040 * "input" command directs the program to read input from *
1041 * a file until eof is reached or an "exit" command is *
1042 * encountered while reading the file. *
1043 * *
1044 ************************************************************
1045 */
1046 else if (OptionMatch("input", *args)) {
1047 if (thinking || pondering)
1048 return 2;
1049 nargs = ReadParse(buffer, args, " \t=");
1050 if (nargs < 2) {
1051 printf("usage: input <filename>\n");
1052 return 1;
1053 }
1054 if (!(input_stream = fopen(args[1], "r"))) {
1055 printf("file does not exist.\n");
1056 input_stream = stdin;
1057 }
1058 }
1059 /*
1060 ************************************************************
1061 * *
1062 * "info" command gives some information about Crafty. *
1063 * *
1064 ************************************************************
1065 */
1066 else if (OptionMatch("info", *args)) {
1067 Print(32, "Crafty version %s\n", version);
1068 Print(32, "number of threads = %2d\n", smp_max_threads);
1069 Print(32, "hash table memory = %s bytes", DisplayKMB(hash_table_size * 64,
1070 1));
1071 Print(32, " (%s entries).\n", DisplayKMB(hash_table_size * 5, 0));
1072 Print(32, "pawn hash table memory = %5s\n",
1073 DisplayKMB(pawn_hash_table_size * sizeof(PAWN_HASH_ENTRY), 1));
1074 if (!tc_sudden_death) {
1075 Print(32, "%d moves/%d minutes %d seconds primary time control\n",
1076 tc_moves, tc_time / 6000, (tc_time / 100) % 60);
1077 Print(32, "%d moves/%d minutes %d seconds secondary time control\n",
1078 tc_secondary_moves, tc_secondary_time / 6000,
1079 (tc_secondary_time / 100) % 60);
1080 if (tc_increment)
1081 Print(32, "increment %d seconds.\n", tc_increment / 100);
1082 } else if (tc_sudden_death == 1) {
1083 Print(32, " game/%d minutes primary time control\n", tc_time / 6000);
1084 if (tc_increment)
1085 Print(32, "increment %d seconds.\n", (tc_increment / 100) % 60);
1086 } else if (tc_sudden_death == 2) {
1087 Print(32, "%d moves/%d minutes primary time control\n", tc_moves,
1088 tc_time / 6000);
1089 Print(32, "game/%d minutes secondary time control\n",
1090 tc_secondary_time / 6000);
1091 if (tc_increment)
1092 Print(32, "increment %d seconds.\n", tc_increment / 100);
1093 }
1094 Print(32, "book frequency (freq)..............%4.2f\n", book_weight_freq);
1095 Print(32, "book static evaluation (eval)......%4.2f\n", book_weight_eval);
1096 Print(32, "book learning (learn)..............%4.2f\n",
1097 book_weight_learn);
1098 }
1099 /*
1100 ************************************************************
1101 * *
1102 * "kibitz" command sets kibitz mode for ICS. =1 will *
1103 * kibitz mate announcements, =2 will kibitz scores and *
1104 * other info, =3 will kibitz scores and PV, =4 adds the *
1105 * list of book moves, =5 displays the PV after each *
1106 * iteration completes. *
1107 * *
1108 ************************************************************
1109 */
1110 else if (OptionMatch("kibitz", *args)) {
1111 if (nargs < 2) {
1112 printf("usage: kibitz <level>\n");
1113 return 1;
1114 }
1115 kibitz = Min(5, atoi(args[1]));
1116 }
1117 /*
1118 ************************************************************
1119 * *
1120 * "learn" command enables/disables the learning *
1121 * algorithm used in Crafty. this is controlled by a *
1122 * single variable that is either 0 or 1 (disabled or *
1123 * enabled). *
1124 * *
1125 * "learn clear" clears all learning data in the opening *
1126 * book, returning it to a state identical to when the *
1127 * book was originally created. *
1128 * *
1129 ************************************************************
1130 */
1131 else if (OptionMatch("learn", *args)) {
1132 if (nargs == 2) {
1133 if (OptionMatch("clear", *(args + 1))) {
1134 int index[32768], i, j, cluster;
1135 unsigned char buf32[4];
1136
1137 fseek(book_file, 0, SEEK_SET);
1138 for (i = 0; i < 32768; i++) {
1139 v = fread(buf32, 4, 1, book_file);
1140 if (v <= 0)
1141 perror("Option() fread error: ");
1142 index[i] = BookIn32(buf32);
1143 }
1144 for (i = 0; i < 32768; i++)
1145 if (index[i] > 0) {
1146 fseek(book_file, index[i], SEEK_SET);
1147 v = fread(buf32, 4, 1, book_file);
1148 if (v <= 0)
1149 perror("Option() fread error: ");
1150 cluster = BookIn32(buf32);
1151 if (cluster)
1152 BookClusterIn(book_file, cluster, book_buffer);
1153 for (j = 0; j < cluster; j++)
1154 book_buffer[j].learn = 0.0;
1155 fseek(book_file, index[i] + sizeof(int), SEEK_SET);
1156 if (cluster)
1157 BookClusterOut(book_file, cluster, book_buffer);
1158 }
1159 } else {
1160 learning = atoi(args[1]);
1161 learn = (learning > 0) ? 1 : 0;
1162 if (learning)
1163 Print(32, "book learning enabled {-%d,+%d}\n", learning, learning);
1164 else
1165 Print(32, "book learning disabled\n");
1166 }
1167 }
1168 }
1169 /*
1170 ************************************************************
1171 * *
1172 * "level" command sets time controls [xboard compati- *
1173 * bility.] *
1174 * *
1175 ************************************************************
1176 */
1177 else if (OptionMatch("level", *args)) {
1178 if (nargs < 4) {
1179 printf("usage: level <nmoves> <stime> <inc>\n");
1180 return 1;
1181 }
1182 tc_moves = atoi(args[1]);
1183 tc_time = atoi(args[2]) * 60;
1184 if (strchr(args[2], ':'))
1185 tc_time = tc_time + atoi(strchr(args[2], ':') + 1);
1186 tc_time *= 100;
1187 tc_increment = atoi(args[3]) * 100;
1188 tc_time_remaining[white] = tc_time;
1189 tc_time_remaining[black] = tc_time;
1190 if (!tc_moves) {
1191 tc_sudden_death = 1;
1192 tc_moves = 1000;
1193 tc_moves_remaining[white] = 1000;
1194 tc_moves_remaining[black] = 1000;
1195 } else
1196 tc_sudden_death = 0;
1197 if (tc_moves) {
1198 tc_secondary_moves = tc_moves;
1199 tc_secondary_time = tc_time;
1200 tc_moves_remaining[white] = tc_moves;
1201 tc_moves_remaining[black] = tc_moves;
1202 }
1203 if (!tc_sudden_death) {
1204 Print(32, "%d moves/%d seconds primary time control\n", tc_moves,
1205 tc_time / 100);
1206 Print(32, "%d moves/%d seconds secondary time control\n",
1207 tc_secondary_moves, tc_secondary_time / 100);
1208 if (tc_increment)
1209 Print(32, "increment %d seconds.\n", tc_increment / 100);
1210 } else if (tc_sudden_death == 1) {
1211 Print(32, " game/%d seconds primary time control\n", tc_time / 100);
1212 if (tc_increment)
1213 Print(32, "increment %d seconds.\n", tc_increment / 100);
1214 }
1215 if (adaptive_hash) {
1216 uint64_t positions_per_move;
1217 float percent;
1218 int optimal_hash_size;
1219
1220 TimeSet(think);
1221 time_limit /= 100;
1222 positions_per_move = time_limit * adaptive_hash / 16;
1223 optimal_hash_size = positions_per_move * 16;
1224 printf("optimal=%d\n", optimal_hash_size);
1225 optimal_hash_size = Max(optimal_hash_size, adaptive_hash_min);
1226 optimal_hash_size = Min(optimal_hash_size, adaptive_hash_max);
1227 sprintf(buffer, "hash=%d\n", optimal_hash_size);
1228 Option(tree);
1229 percent =
1230 (float) (optimal_hash_size -
1231 adaptive_hash_min) / (float) (adaptive_hash_max -
1232 adaptive_hash_min);
1233 optimal_hash_size =
1234 adaptive_hashp_min + percent * (adaptive_hashp_max -
1235 adaptive_hashp_min);
1236 optimal_hash_size = Max(optimal_hash_size, adaptive_hashp_min);
1237 sprintf(buffer, "hashp=%d\n", optimal_hash_size);
1238 Option(tree);
1239 }
1240 }
1241 /*
1242 ************************************************************
1243 * *
1244 * "linelength" sets the default line length to something *
1245 * other than 80, if desired. Setting this to a huge *
1246 * number makes a PV print on one line for easier parsing *
1247 * by automated scripts. *
1248 * *
1249 ************************************************************
1250 */
1251 else if (OptionMatch("linelength", *args)) {
1252 if (nargs > 2) {
1253 printf("usage: linelength <n>\n");
1254 return 1;
1255 }
1256 if (nargs == 2)
1257 line_length = atoi(args[1]);
1258 printf("line length set to %d.\n", line_length);
1259 }
1260 /*
1261 ************************************************************
1262 * *
1263 * "list" command allows the operator to add or remove *
1264 * names from the various lists Crafty uses to recognize *
1265 * and adapt to particular opponents. *
1266 * *
1267 * list <listname> <player> *
1268 * *
1269 * <listname> is one of AK, B, C, GM, IM, SP. *
1270 * *
1271 * The final parameter is a name to add or remove. If *
1272 * you prepend a + to the name, that asks that the name be *
1273 * added to the list. If you prepend a - to the name, *
1274 * that asks that the name be removed from the list. If *
1275 * no name is given, the list is displayed. *
1276 * *
1277 * AK is the "auto-kibitz" list. Crafty will kibitz info *
1278 * on a chess server when playing any opponent in this *
1279 * list. This should only have computer names as humans *
1280 * don't approve of kibitzes while they are playing. *
1281 * *
1282 * B identifies "blocker" players, those that try to *
1283 * block the position and go for easy draws. This makes *
1284 * Crafty try much harder to prevent this from happening, *
1285 * even at the expense of positional compensation. *
1286 * *
1287 * GM and IM identify titled players. This affects how *
1288 * and when Crafty resigns or offers/accepts draws. For *
1289 * GM players it will do so fairly early after the right *
1290 * circumstances have been seen, for IM it delays a bit *
1291 * longer as they are more prone to making a small error *
1292 * that avoids the loss or draw. *
1293 * *
1294 * SP is the "special player" option. This is an extended *
1295 * version of the "list" command that allows you to *
1296 * specify a special "start book" for a particular *
1297 * opponent to make Crafty play specific openings against *
1298 * that opponent, as well as allowing you to specify a *
1299 * personality file to use against that specific opponent *
1300 * when he is identified by the correct "name" command. *
1301 * *
1302 * For the SP list, the command is extended to use *
1303 * *
1304 * "list SP +player book=filename personality=filename" *
1305 * *
1306 * For the SP list, the files specified must exist in the *
1307 * current directory unless the bookpath and perspath *
1308 * commands direct Crafty to look elsewhere. *
1309 * *
1310 ************************************************************
1311 */
1312 else if (OptionMatch("list", *args)) {
1313 char **targs;
1314 char listname[5][3] = { "AK", "B", "GM", "IM", "SP" };
1315 char **listaddr[] = { AK_list, B_list, GM_list,
1316 IM_list, SP_list
1317 };
1318 int i, list, lastent = -1;
1319
1320 targs = args;
1321 for (list = 0; list < 5; list++) {
1322 if (!strcmp(listname[list], args[1]))
1323 break;
1324 }
1325 if (list > 4) {
1326 printf("usage: list AK|B|GM|IM|P|SP +name1 -name2 etc\n");
1327 return 1;
1328 }
1329 nargs -= 2;
1330 targs += 2;
1331 if (nargs) {
1332 while (nargs) {
1333 if (targs[0][0] == '-') {
1334 for (i = 0; i < 128; i++)
1335 if (listaddr[list][i]) {
1336 if (!strcmp(listaddr[list][i], targs[0] + 1)) {
1337 free(listaddr[list][i]);
1338 listaddr[list][i] = NULL;
1339 Print(32, "%s removed from %s list.\n", targs[0] + 1,
1340 listname[list]);
1341 break;
1342 }
1343 }
1344 } else if (targs[0][0] == '+') {
1345 for (i = 0; i < 128; i++)
1346 if (listaddr[list][i]) {
1347 if (!strcmp(listaddr[list][i], targs[0] + 1)) {
1348 Print(32, "Warning: %s is already in %s list.\n",
1349 targs[0] + 1, listname[list]);
1350 break;
1351 }
1352 }
1353 for (i = 0; i < 128; i++)
1354 if (listaddr[list][i] == NULL)
1355 break;
1356 if (i >= 128)
1357 Print(32, "ERROR! %s list is full at 128 entries\n",
1358 listname[list]);
1359 else {
1360 listaddr[list][i] = malloc(strlen(targs[0]));
1361 strcpy(listaddr[list][i], targs[0] + 1);
1362 Print(32, "%s added to %s list.\n", targs[0] + 1, listname[list]);
1363 if (list == 5)
1364 lastent = i;
1365 }
1366 } else if (!strcmp(targs[0], "clear")) {
1367 for (i = 0; i < 128; i++) {
1368 free(listaddr[list][i]);
1369 listaddr[list][i] = NULL;
1370 }
1371 } else if (!strcmp(targs[0], "book") && lastent != -1) {
1372 char filename[256];
1373 FILE *file;
1374
1375 strcpy(filename, book_path);
1376 strcat(filename, "/");
1377 strcat(filename, targs[1]);
1378 if (!strstr(args[2], ".bin"))
1379 strcat(filename, ".bin");
1380 file = fopen(filename, "r");
1381 if (!file) {
1382 Print(4095, "ERROR book file %s can not be opened\n", filename);
1383 break;
1384 }
1385 fclose(file);
1386 SP_opening_filename[lastent] = malloc(strlen(filename) + 1);
1387 strcpy(SP_opening_filename[lastent], filename);
1388 nargs--;
1389 targs++;
1390 } else if (!strcmp(targs[0], "personality") && lastent != -1) {
1391 char filename[256];
1392 FILE *file;
1393
1394 strcat(filename, targs[1]);
1395 if (!strstr(args[2], ".cpf"))
1396 strcat(filename, ".cpf");
1397 file = fopen(filename, "r");
1398 if (!file) {
1399 Print(4095, "ERROR personality file %s can not be opened\n",
1400 filename);
1401 break;
1402 }
1403 fclose(file);
1404 SP_personality_filename[lastent] = malloc(strlen(filename) + 1);
1405 strcpy(SP_personality_filename[lastent], filename);
1406 nargs--;
1407 targs++;
1408 } else
1409 printf("error, name must be preceeded by +/- flag.\n");
1410 nargs--;
1411 targs++;
1412 }
1413 } else {
1414 Print(32, "%s List:\n", listname[list]);
1415 for (i = 0; i < 128; i++) {
1416 if (listaddr[list][i]) {
1417 Print(32, "%s", listaddr[list][i]);
1418 if (list == 5) {
1419 if (SP_opening_filename[i])
1420 Print(32, " book=%s", SP_opening_filename[i]);
1421 if (SP_personality_filename[i])
1422 Print(32, " personality=%s", SP_personality_filename[i]);
1423 }
1424 Print(32, "\n");
1425 }
1426 }
1427 }
1428 }
1429 /*
1430 ************************************************************
1431 * *
1432 * "lmp" command sets the formla parameters that produce *
1433 * LMP pruning bounds array. *
1434 * *
1435 * lmp <maxdepth> <base> <scale> *
1436 * *
1437 * <maxdepth> is the max depth at which LMP is done. *
1438 * *
1439 * <base> is the base pruning move count. The function is *
1440 * an exponential of the form x = base + f(y). The *
1441 * default is currently 3. *
1442 * *
1443 * <scale> is the exponent of the exponential function. *
1444 * larger numbers produce more conservative (larger) move *
1445 * counts. Smaller values are more aggressive. The *
1446 * default is currently 1.9. *
1447 * *
1448 ************************************************************
1449 */
1450 else if (OptionMatch("lmp", *args)) {
1451 int i;
1452
1453 if ((nargs > 1 && nargs < 4) || nargs > 4) {
1454 printf("usage: lmp <maxdepth> <base> <scale>\n");
1455 return 1;
1456 }
1457 if (nargs > 1) {
1458 LMP_depth = atoi(args[1]);
1459 LMP_base = atoi(args[2]);
1460 LMP_scale = atof(args[3]);
1461 InitializeLMP();
1462 }
1463 Print(32, "LMP depth=%d base=%d scale=%f\n", LMP_depth, LMP_base,
1464 LMP_scale);
1465 Print(32, "depth: ");
1466 for (i = 1; i < 16; i++)
1467 Print(32, "%4d", i);
1468 Print(32, "\n");
1469 Print(32, "movcnt: ");
1470 for (i = 1; i < 16; i++)
1471 Print(32, "%4d", LMP[i]);
1472 Print(32, "\n");
1473 }
1474 /*
1475 ************************************************************
1476 * *
1477 * "lmr" command sets the formla parameters that produce *
1478 * LMR reduction matrix. The format is: *
1479 * *
1480 * lmr <min> <max> <depth bias> <moves bias> <scale> *
1481 * *
1482 * <min> is the minimum LMR reduction. This probably *
1483 * should not be changed from 1, the default. *
1484 * *
1485 * <max> is the maximum LMR reduction. If you adjust the *
1486 * following values, you might need to increase this as it *
1487 * is an absolute clamp and no value can exceed this no *
1488 * matter what the formula produces. *
1489 * *
1490 * <depth_bias> is simply a multiplier that causes depth *
1491 * to influence the reduction amount more or less (as the *
1492 * value drops below the value used for <moves bias> below *
1493 * or as it is increased above <moves bias>. The default *
1494 * is 2.0. *
1495 * *
1496 * <moves bias> is simply a multiplier that causes the *
1497 * number of moves already searched to become more or less *
1498 * important than the remaining depth as above. The *
1499 * default is 1.0. *
1500 * *
1501 * <scale> is used to scale the formula back since it uses *
1502 * a logarithmic expression. The basic idea is to adjust *
1503 * the above two values to produce the desired "shape" of *
1504 * the reduction matrix, then adjust this to change the *
1505 * reduction amounts overall. The default is 2.9. *
1506 * *
1507 ************************************************************
1508 */
1509 else if (OptionMatch("lmr", *args)) {
1510 int i, j;
1511
1512 if ((nargs > 1 && nargs < 6) || nargs > 7) {
1513 printf("usage: lmr <min> <max> <depth bias> <move bias> <scale>\n");
1514 return 1;
1515 }
1516 if (nargs > 1) {
1517 LMR_min = atoi(args[1]);
1518 LMR_max = Min(atoi(args[2]), 15);
1519 LMR_db = atof(args[3]);
1520 LMR_mb = atof(args[4]);
1521 LMR_s = atof(args[5]);
1522 InitializeLMR();
1523 }
1524 if (nargs > 6) {
1525 char *axis = "|||||||||||depth left|||||||||||";
1526
1527 Print(32,
1528 "LMR values: %d(min) %d(max) %.2f(depth) %.2f(moves) %.2f(scale).\n",
1529 LMR_min, LMR_max, LMR_db, LMR_mb, LMR_s);
1530 Print(32, "\n LMR reductions[depth][moves]\n");
1531 Print(32, " ----------------------moves searched-----------------\n");
1532 Print(32, " | ");
1533 for (i = 0; i < 64; i += 1)
1534 Print(32, "%3d", i);
1535 Print(32, "\n");
1536 for (i = 0; i < 32; i += 1) {
1537 Print(32, " %c %3d: ", axis[i], i);
1538 for (j = 0; j < 64; j += 1)
1539 Print(32, " %2d", LMR[i][j]);
1540 Print(32, "\n");
1541 }
1542 } else {
1543 char *axis = "||depth left|||";
1544
1545 Print(32,
1546 "LMR values: %d(min) %d(max) %.2f(depth) %.2f(moves) %.2f(scale).\n",
1547 LMR_min, LMR_max, LMR_db, LMR_mb, LMR_s);
1548 Print(32, "\n LMR reductions[depth][moves]\n");
1549 Print(32, " ----------------------moves searched------------------\n");
1550 Print(32, " | ");
1551 for (i = 2; i < 64; i += 4)
1552 Print(32, "%3d", i);
1553 Print(32, "\n");
1554 for (i = 3; i < 32; i += 2) {
1555 Print(32, " %c %3d: ", axis[(i - 3) / 2], i);
1556 for (j = 2; j < 64; j += 4)
1557 Print(32, " %2d", LMR[i][j]);
1558 Print(32, "\n");
1559 }
1560 Print(32, " note: table is shown compressed, each index is in\n");
1561 Print(32, " units of 1, all rows/columns are not shown above\n");
1562 }
1563 }
1564 /*
1565 ************************************************************
1566 * *
1567 * "load" command directs the program to read input from *
1568 * a file until a "setboard" command is found this *
1569 * command is then executed, setting up the position for *
1570 * a search. *
1571 * *
1572 ************************************************************
1573 */
1574 else if (OptionMatch("load", *args)) {
1575 char title[64];
1576 char *readstat;
1577 FILE *prob_file;
1578
1579 if (thinking || pondering)
1580 return 2;
1581 nargs = ReadParse(buffer, args, " \t=");
1582 if (nargs < 3) {
1583 printf("usage: input <filename> title\n");
1584 return 1;
1585 }
1586 if (!(prob_file = fopen(args[1], "r"))) {
1587 printf("file does not exist.\n");
1588 return 1;
1589 }
1590 strcpy(title, args[2]);
1591 while (!feof(prob_file)) {
1592 readstat = fgets(buffer, 128, prob_file);
1593 if (readstat) {
1594 char *delim;
1595
1596 delim = strchr(buffer, '\n');
1597 if (delim)
1598 *delim = 0;
1599 delim = strchr(buffer, '\r');
1600 if (delim)
1601 *delim = ' ';
1602 }
1603 if (readstat == NULL)
1604 break;
1605 nargs = ReadParse(buffer, args, " \t;\n");
1606 if (!strcmp(args[0], "title") && strstr(buffer, title))
1607 break;
1608 }
1609 while (!feof(prob_file)) {
1610 readstat = fgets(buffer, 128, prob_file);
1611 if (readstat) {
1612 char *delim;
1613
1614 delim = strchr(buffer, '\n');
1615 if (delim)
1616 *delim = 0;
1617 delim = strchr(buffer, '\r');
1618 if (delim)
1619 *delim = ' ';
1620 }
1621 if (readstat == NULL)
1622 break;
1623 nargs = ReadParse(buffer, args, " \t;\n");
1624 if (!strcmp(args[0], "setboard")) {
1625 Option(tree);
1626 break;
1627 }
1628 }
1629 fclose(prob_file);
1630 }
1631 /*
1632 ************************************************************
1633 * *
1634 * "log" command turns log on/off, and also lets you view *
1635 * the end of the log or copy it to disk as needed. To *
1636 * view the end, simply type "log <n>" where n is the # *
1637 * of lines you'd like to see (the last <n> lines). You *
1638 * can add a filename to the end and the output will go *
1639 * to this file instead. *
1640 * *
1641 ************************************************************
1642 */
1643 else if (OptionMatch("log", *args)) {
1644 char filename[64];
1645
1646 if (nargs < 2) {
1647 printf("usage: log on|off|n [filename]\n");
1648 return 1;
1649 }
1650 if (!strcmp(args[1], "on")) {
1651 int id;
1652
1653 id = InitializeGetLogID();
1654 sprintf(log_filename, "%s/log.%03d", log_path, id);
1655 sprintf(history_filename, "%s/game.%03d", log_path, id);
1656 log_file = fopen(log_filename, "w");
1657 history_file = fopen(history_filename, "w+");
1658 } else if (!strcmp(args[1], "off")) {
1659 if (log_file)
1660 fclose(log_file);
1661 log_file = 0;
1662 sprintf(filename, "%s/log.%03d", log_path, log_id - 1);
1663 remove(filename);
1664 sprintf(filename, "%s/game.%03d", log_path, log_id - 1);
1665 remove(filename);
1666 } else if (args[1][0] >= '0' && args[1][0] <= '9')
1667 log_id = atoi(args[1]);
1668 }
1669 /*
1670 ************************************************************
1671 * *
1672 * "memory" command is used to set the max memory to use *
1673 * for hash and hashp combined. This is an xboard *
1674 * compatibility command, not normally used by players. *
1675 * *
1676 ************************************************************
1677 */
1678 else if (OptionMatch("memory", *args)) {
1679 uint64_t size;
1680 size_t hmemory, pmemory;
1681 if (nargs < 2) {
1682 printf("usage: memory <size>\n");
1683 return 1;
1684 }
1685 if (allow_memory) {
1686 size = (uint64_t) atoi(args[1]);
1687 if (size == 0) {
1688 Print(4095, "ERROR - memory size can not be zero\n");
1689 return 1;
1690 }
1691 hmemory = (1ull) << MSB(size);
1692 size &= ~hmemory;
1693 pmemory = (1ull) << MSB(size);
1694 sprintf(buffer, "hash %" PRIu64 "M\n", (uint64_t) hmemory);
1695 Option(tree);
1696 if (pmemory) {
1697 sprintf(buffer, "hashp %" PRIu64 "M\n", (uint64_t) pmemory);
1698 Option(tree);
1699 }
1700 } else
1701 Print(4095, "WARNING - memory command ignored.\n");
1702 }
1703 /*
1704 ************************************************************
1705 * *
1706 * "mode" command sets tournament mode or normal mode. *
1707 * Tournament mode is used when Crafty is in a "real" *
1708 * tournament. It forces draw_score to 0, and makes *
1709 * Crafty display the chess clock after each move. *
1710 * *
1711 ************************************************************
1712 */
1713 else if (OptionMatch("mode", *args)) {
1714 if (nargs > 1) {
1715 if (!strcmp(args[1], "tournament")) {
1716 mode = tournament_mode;
1717 printf("use 'settc' command if a game is restarted after Crafty\n");
1718 printf("has been terminated for any reason.\n");
1719 } else if (!strcmp(args[1], "normal")) {
1720 mode = normal_mode;
1721 book_weight_learn = 1.0;
1722 book_weight_freq = 1.0;
1723 book_weight_eval = 0.5;
1724 } else if (!strcmp(args[1], "match")) {
1725 mode = normal_mode;
1726 book_weight_learn = 1.0;
1727 book_weight_freq = 0.2;
1728 book_weight_eval = 0.1;
1729 } else {
1730 printf("usage: mode normal|tournament|match\n");
1731 mode = normal_mode;
1732 book_weight_learn = 1.0;
1733 book_weight_freq = 1.0;
1734 book_weight_eval = 0.5;
1735 }
1736 }
1737 if (mode == tournament_mode)
1738 printf("tournament mode.\n");
1739 else if (mode == normal_mode)
1740 printf("normal mode.\n");
1741 }
1742 /*
1743 ************************************************************
1744 * *
1745 * "name" command saves opponents name and writes it into *
1746 * logfile along with the date/time. It also scans the *
1747 * list of known computers and adjusts its opening book *
1748 * to play less "risky" if it matches. If the opponent *
1749 * is in the GM list, it tunes the resignation controls *
1750 * to resign earlier. Ditto for other lists that are *
1751 * used to recognize specific opponents and adjust things *
1752 * accordingly. *
1753 * *
1754 ************************************************************
1755 */
1756 else if (OptionMatch("name", *args)) {
1757 char *next;
1758 int i;
1759
1760 if (nargs < 2) {
1761 printf("usage: name <name>\n");
1762 return 1;
1763 }
1764 if (game_wtm) {
1765 strcpy(pgn_white, args[1]);
1766 sprintf(pgn_black, "Crafty %s", version);
1767 } else {
1768 strcpy(pgn_black, args[1]);
1769 sprintf(pgn_white, "Crafty %s", version);
1770 }
1771 Print(32, "Crafty %s vs %s\n", version, args[1]);
1772 next = args[1];
1773 while (*next) {
1774 *next = tolower(*next);
1775 next++;
1776 }
1777 if (mode != tournament_mode) {
1778 for (i = 0; i < 128; i++)
1779 if (AK_list[i] && !strcmp(AK_list[i], args[1])) {
1780 kibitz = 4;
1781 break;
1782 }
1783 for (i = 0; i < 128; i++)
1784 if (GM_list[i] && !strcmp(GM_list[i], args[1])) {
1785 Print(32, "playing a GM!\n");
1786 book_selection_width = 3;
1787 resign = Min(6, resign);
1788 resign_count = 4;
1789 draw_count = 4;
1790 accept_draws = 1;
1791 kibitz = 0;
1792 break;
1793 }
1794 for (i = 0; i < 128; i++)
1795 if (IM_list[i] && !strcmp(IM_list[i], args[1])) {
1796 Print(32, "playing an IM!\n");
1797 book_selection_width = 4;
1798 resign = Min(9, resign);
1799 resign_count = 5;
1800 draw_count = 4;
1801 accept_draws = 1;
1802 kibitz = 0;
1803 break;
1804 }
1805 for (i = 0; i < 128; i++)
1806 if (SP_list[i] && !strcmp(SP_list[i], args[1])) {
1807 FILE *normal_bs_file = books_file;
1808
1809 Print(32, "playing a special player!\n");
1810 if (SP_opening_filename[i]) {
1811 books_file = fopen(SP_opening_filename[i], "rb");
1812 if (!books_file) {
1813 Print(4095, "Error! unable to open %s for player %s.\n",
1814 SP_opening_filename[i], SP_list[i]);
1815 books_file = normal_bs_file;
1816 }
1817 }
1818 if (SP_personality_filename[i]) {
1819 sprintf(buffer, "personality load %s\n",
1820 SP_personality_filename[i]);
1821 Option(tree);
1822 }
1823 break;
1824 }
1825 }
1826 printf("tellicsnoalias kibitz Hello from Crafty v%s! (%d cpus)\n",
1827 version, Max(1, smp_max_threads));
1828 }
1829 /*
1830 ************************************************************
1831 * *
1832 * "new" command initializes for a new game. *
1833 * *
1834 ************************************************************
1835 */
1836 else if (OptionMatch("new", *args)) {
1837 Print(4095, "NOTICE: ""new"" command not implemented, please exit and\n");
1838 Print(4095, "restart crafty to re-initialize everything for a new game\n");
1839 return 1;
1840 }
1841 /*
1842 ************************************************************
1843 * *
1844 * "noise" command sets a minimum limit on time searched *
1845 * before we start to display the normal search output. *
1846 * With today's hardware and deep searches, it is easy to *
1847 * get "swamped" with output. Using "noise" you can say *
1848 * "hold the output until you have searched for <x> time *
1849 * (where time can be x.xx seconds or just x seconds.) *
1850 * *
1851 ************************************************************
1852 */
1853 else if (OptionMatch("noise", *args)) {
1854 if (nargs < 2) {
1855 printf("usage: noise <n>\n");
1856 return 1;
1857 }
1858 noise_level = atof(args[1]) * 100;
1859 Print(32, "noise level set to %.2f seconds.\n",
1860 (float) noise_level / 100.0);
1861 }
1862 /*
1863 ************************************************************
1864 * *
1865 * "null" command sets the minimum null-move reduction and *
1866 * a value that is used to compute the max reduction. *
1867 * *
1868 * null <min> <divisor> *
1869 * *
1870 * <min> is the minimum null move reduction. The default *
1871 * is 3 which is pretty reliable. *
1872 * *
1873 * <divisor> increases the null move by the following *
1874 * simple formula: *
1875 * *
1876 * null_reduction = min + depth / divisor *
1877 * *
1878 * The default value is currently 10, which will increase *
1879 * R (null-move reduction) by one at any position where *
1880 * depth >= 10 and < 20. Or by two when depth > 20. Etc. *
1881 * *
1882 ************************************************************
1883 */
1884 else if (OptionMatch("null", *args)) {
1885
1886 if (nargs > 3) {
1887 printf("usage: null <min> <divisor>\n");
1888 return 1;
1889 }
1890 if (nargs > 1) {
1891 null_depth = atoi(args[1]);
1892 null_divisor = atoi(args[2]);
1893 }
1894 Print(32, "null move: R = %d + depth / %d\n", null_depth, null_divisor);
1895 }
1896 /*
1897 ************************************************************
1898 * *
1899 * "otim" command sets the opponent's time remaining. *
1900 * This is used to determine if the opponent is in time *
1901 * trouble, and is factored into the draw score if he is. *
1902 * *
1903 ************************************************************
1904 */
1905 else if (OptionMatch("otim", *args)) {
1906 if (nargs < 2) {
1907 printf("usage: otime <time(unit=.01 secs))>\n");
1908 return 1;
1909 }
1910 tc_time_remaining[game_wtm] = atoi(args[1]);
1911 if (log_file && time_limit > 99)
1912 fprintf(log_file, "time remaining: %s (opponent).\n",
1913 DisplayTime(tc_time_remaining[game_wtm]));
1914 if (call_flag && xboard && tc_time_remaining[game_wtm] < 1) {
1915 if (crafty_is_white)
1916 Print(32, "1-0 {Black ran out of time}\n");
1917 else
1918 Print(32, "0-1 {White ran out of time}\n");
1919 }
1920 }
1921 /*
1922 ************************************************************
1923 * *
1924 * "output" command sets long or short algebraic output. *
1925 * Long is Ng1f3, while short is simply Nf3. *
1926 * *
1927 ************************************************************
1928 */
1929 else if (OptionMatch("output", *args)) {
1930 if (nargs < 2) {
1931 printf("usage: output long|short\n");
1932 return 1;
1933 }
1934 if (!strcmp(args[1], "long"))
1935 output_format = 1;
1936 else if (!strcmp(args[1], "short"))
1937 output_format = 0;
1938 else
1939 printf("usage: output long|short\n");
1940 if (output_format == 1)
1941 Print(32, "output moves in long algebraic format\n");
1942 else if (output_format == 0)
1943 Print(32, "output moves in short algebraic format\n");
1944 }
1945 /*
1946 ************************************************************
1947 * *
1948 * "personality" command is used to adjust the eval terms *
1949 * and search options to modify the way Crafty plays. *
1950 * *
1951 ************************************************************
1952 */
1953 else if (OptionMatch("personality", *args)) {
1954 int i, j, param, index, value;
1955
1956 /*
1957 ************************************************************
1958 * *
1959 * Handle the "personality list" command and dump every- *
1960 * thing the user can modify. *
1961 * *
1962 ************************************************************
1963 */
1964 if (nargs == 2 && !strcmp(args[1], "list")) {
1965 printf("\n");
1966 for (i = 0; i < 256; i++) {
1967 if (!personality_packet[i].description)
1968 continue;
1969 if (personality_packet[i].value) {
1970 switch (personality_packet[i].type) {
1971 case 1:
1972 printf("%3d %s %7d\n", i, personality_packet[i].description,
1973 *(int *) personality_packet[i].value);
1974 break;
1975 case 2:
1976 printf("%3d %s %7d (mg) %7d (eg)\n", i,
1977 personality_packet[i].description,
1978 ((int *) personality_packet[i].value)[mg],
1979 ((int *) personality_packet[i].value)[eg]);
1980 break;
1981 case 3:
1982 printf("%3d %s %7.2f\n", i, personality_packet[i].description,
1983 *(double *) personality_packet[i].value);
1984 break;
1985 case 4:
1986 printf("%3d %s ", i, personality_packet[i].description);
1987 for (j = 0; j < personality_packet[i].size; j++)
1988 printf("%4d", ((int *) personality_packet[i].value)[j]);
1989 printf("\n");
1990 break;
1991 }
1992 } else {
1993 printf("==================================================\n");
1994 printf("= %s =\n", personality_packet[i].description);
1995 printf("==================================================\n");
1996 }
1997 }
1998 printf("\n");
1999 return 1;
2000 }
2001 /*
2002 ************************************************************
2003 * *
2004 * Handle the "personality load" command and read in the *
2005 * specified *.cpf file. *
2006 * *
2007 ************************************************************
2008 */
2009 if (!strcmp(args[1], "load")) {
2010 FILE *file;
2011 char filename[256];
2012
2013 strcpy(filename, args[2]);
2014 if (!strstr(filename, ".cpf"))
2015 strcat(filename, ".cpf");
2016 Print(32, "Loading personality file %s\n", filename);
2017 if ((file = fopen(filename, "r+"))) {
2018 while (fgets(buffer, 4096, file)) {
2019 char *delim;
2020
2021 delim = strchr(buffer, '\n');
2022 if (delim)
2023 *delim = 0;
2024 delim = strstr(buffer, "->");
2025 if (delim)
2026 *delim = 0;
2027 delim = strchr(buffer, '\r');
2028 if (delim)
2029 *delim = ' ';
2030 Option(tree);
2031 }
2032 fclose(file);
2033 }
2034 return 1;
2035 }
2036 /*
2037 ************************************************************
2038 * *
2039 * Handle the "personality save" command and dump every- *
2040 * thing that can be modified to a file. *
2041 * *
2042 ************************************************************
2043 */
2044 if (nargs == 3 && !strcmp(args[1], "save")) {
2045 char filename[256];
2046 FILE *file;
2047
2048 strcpy(filename, args[2]);
2049 if (!strstr(filename, ".cpf"))
2050 strcat(filename, ".cpf");
2051 file = fopen(filename, "w");
2052 if (!file) {
2053 printf("ERROR. Unable to open %s for writing\n", args[2]);
2054 return 1;
2055 }
2056 printf("saving to file \"%s\"\n", filename);
2057 fprintf(file, "# Crafty v%s personality file\n", version);
2058 for (i = 0; i < 256; i++) {
2059 if (!personality_packet[i].description)
2060 continue;
2061 if (personality_packet[i].value) {
2062 if (personality_packet[i].size <= 1)
2063 fprintf(file, "personality %3d %7d\n", i,
2064 *((int *) personality_packet[i].value));
2065 else if (personality_packet[i].size > 1) {
2066 fprintf(file, "personality %3d ", i);
2067 for (j = 0; j < personality_packet[i].size; j++)
2068 fprintf(file, "%d ", ((int *) personality_packet[i].value)[j]);
2069 fprintf(file, "\n");
2070 }
2071 }
2072 }
2073 fprintf(file, "exit\n");
2074 fclose(file);
2075 return 1;
2076 }
2077 /*
2078 ************************************************************
2079 * *
2080 * Handle the "personality index val" command that changes *
2081 * only those personality terms that are scalars. *
2082 * *
2083 ************************************************************
2084 */
2085 param = atoi(args[1]);
2086 value = atoi(args[2]);
2087 if (!personality_packet[param].value) {
2088 Print(4095, "ERROR. evaluation term %d is not defined\n", param);
2089 return 1;
2090 }
2091 if (personality_packet[param].size == 0) {
2092 if (nargs > 3) {
2093 printf("this eval term requires exactly 1 value.\n");
2094 return 1;
2095 }
2096 *(int *) personality_packet[param].value = value;
2097 }
2098 /*
2099 ************************************************************
2100 * *
2101 * Handle the "personality index v1 v2 .. vn" command that *
2102 * changes eval terms that are vectors. *
2103 * *
2104 ************************************************************
2105 */
2106 else {
2107 index = nargs - 2;
2108 if (index != personality_packet[param].size) {
2109 printf
2110 ("this eval term (%s [%d]) requires exactly %d values, found %d.\n",
2111 personality_packet[param].description, param,
2112 Abs(personality_packet[param].size), index);
2113 return 1;
2114 }
2115 for (i = 0; i < index; i++)
2116 ((int *) personality_packet[param].value)[i] = atoi(args[i + 2]);
2117 }
2118 InitializeKingSafety();
2119 }
2120 /*
2121 ************************************************************
2122 * *
2123 * "bookpath", "logpath" and "tbpath" set the default *
2124 * paths to locate or save these files. *
2125 * *
2126 ************************************************************
2127 */
2128 else if (OptionMatch("logpath", *args) || OptionMatch("bookpath", *args)
2129 || OptionMatch("tbpath", *args)) {
2130 if (OptionMatch("logpath", *args) || OptionMatch("bookpath", *args)) {
2131 if (log_file)
2132 Print(4095, "ERROR -- this must be used on command line only\n");
2133 }
2134 nargs = ReadParse(buffer, args, " \t=");
2135 if (nargs < 2) {
2136 printf("usage: bookpath|perspath|logpath|tbpath <path>\n");
2137 return 1;
2138 }
2139 if (!strchr(args[1], '(')) {
2140 if (strstr(args[0], "bookpath"))
2141 strcpy(book_path, args[1]);
2142 else if (strstr(args[0], "logpath"))
2143 strcpy(log_path, args[1]);
2144 #if defined(SYZYGY)
2145 else if (strstr(args[0], "tbpath"))
2146 strcpy(tb_path, args[1]);
2147 #endif
2148
2149 } else {
2150 if (strchr(args[1], ')')) {
2151 *strchr(args[1], ')') = 0;
2152 if (strstr(args[0], "bookpath"))
2153 strcpy(book_path, args[1] + 1);
2154 else if (strstr(args[0], "logpath"))
2155 strcpy(log_path, args[1] + 1);
2156 #if defined(SYZYGY)
2157 else if (strstr(args[0], "tbpath"))
2158 strcpy(tb_path, args[1] + 1);
2159 #endif
2160 } else
2161 Print(4095, "ERROR multiple paths must be enclosed in ( and )\n");
2162 }
2163 }
2164 /*
2165 ************************************************************
2166 * *
2167 * "perf" command turns times move generator/make_move. *
2168 * *
2169 ************************************************************
2170 */
2171 #define PERF_CYCLES 4000000
2172 else if (OptionMatch("perf", *args)) {
2173 int i, clock_before, clock_after;
2174 unsigned *mv;
2175 float time_used;
2176
2177 if (thinking || pondering)
2178 return 2;
2179 clock_before = clock();
2180 while (clock() == clock_before);
2181 clock_before = clock();
2182 for (i = 0; i < PERF_CYCLES; i++) {
2183 tree->last[1] = GenerateCaptures(tree, 0, game_wtm, tree->last[0]);
2184 tree->last[1] = GenerateNoncaptures(tree, 0, game_wtm, tree->last[1]);
2185 }
2186 clock_after = clock();
2187 time_used =
2188 ((float) clock_after - (float) clock_before) / (float) CLOCKS_PER_SEC;
2189 printf("generated %d moves, time=%.2f seconds\n",
2190 (int) (tree->last[1] - tree->last[0]) * PERF_CYCLES, time_used);
2191 printf("generated %d moves per second\n",
2192 (int) (((float) (PERF_CYCLES * (tree->last[1] -
2193 tree->last[0]))) / time_used));
2194 clock_before = clock();
2195 while (clock() == clock_before);
2196 clock_before = clock();
2197 for (i = 0; i < PERF_CYCLES; i++) {
2198 tree->last[1] = GenerateCaptures(tree, 0, game_wtm, tree->last[0]);
2199 tree->last[1] = GenerateNoncaptures(tree, 0, game_wtm, tree->last[1]);
2200 for (mv = tree->last[0]; mv < tree->last[1]; mv++) {
2201 MakeMove(tree, 0, game_wtm, *mv);
2202 UnmakeMove(tree, 0, game_wtm, *mv);
2203 }
2204 }
2205 clock_after = clock();
2206 time_used =
2207 ((float) clock_after - (float) clock_before) / (float) CLOCKS_PER_SEC;
2208 printf("generated/made/unmade %d moves, time=%.2f seconds\n",
2209 (int) (tree->last[1] - tree->last[0]) * PERF_CYCLES, time_used);
2210 printf("generated/made/unmade %d moves per second\n",
2211 (int) (((float) (PERF_CYCLES * (tree->last[1] -
2212 tree->last[0]))) / time_used));
2213 }
2214 /*
2215 ************************************************************
2216 * *
2217 * "perft" command turns tests move generator/make_move. *
2218 * *
2219 ************************************************************
2220 */
2221 else if (OptionMatch("perft", *args)) {
2222 float time_used;
2223 int i, clock_before, clock_after;
2224
2225 if (thinking || pondering)
2226 return 2;
2227 clock_before = clock();
2228 while (clock() == clock_before);
2229 clock_before = clock();
2230 if (nargs < 2) {
2231 printf("usage: perft <depth>\n");
2232 return 1;
2233 }
2234 tree->status[1] = tree->status[0];
2235 tree->last[0] = tree->move_list;
2236 i = atoi(args[1]);
2237 if (i <= 0) {
2238 Print(32, "usage: perft <maxply>\n");
2239 return 1;
2240 }
2241 total_moves = 0;
2242 OptionPerft(tree, 1, i, game_wtm);
2243 clock_after = clock();
2244 time_used =
2245 ((float) clock_after - (float) clock_before) / (float) CLOCKS_PER_SEC;
2246 printf("total moves=%" PRIu64 " time=%.2f\n", total_moves, time_used);
2247 }
2248 /*
2249 ************************************************************
2250 * *
2251 * "pgn" command sets the various PGN header files. *
2252 * *
2253 ************************************************************
2254 */
2255 else if (OptionMatch("pgn", *args)) {
2256 int i;
2257
2258 if (nargs < 3) {
2259 printf("usage: pgn <tag> <value>\n");
2260 return 1;
2261 }
2262 if (!strcmp(args[1], "Event")) {
2263 pgn_event[0] = 0;
2264 for (i = 2; i < nargs; i++) {
2265 strcpy(pgn_event + strlen(pgn_event), args[i]);
2266 strcpy(pgn_event + strlen(pgn_event), " ");
2267 }
2268 } else if (!strcmp(args[1], "Site")) {
2269 pgn_site[0] = 0;
2270 for (i = 2; i < nargs; i++) {
2271 strcpy(pgn_site + strlen(pgn_site), args[i]);
2272 strcpy(pgn_site + strlen(pgn_site), " ");
2273 }
2274 } else if (!strcmp(args[1], "Round")) {
2275 pgn_round[0] = 0;
2276 strcpy(pgn_round, args[2]);
2277 } else if (!strcmp(args[1], "White")) {
2278 pgn_white[0] = 0;
2279 for (i = 2; i < nargs; i++) {
2280 strcpy(pgn_white + strlen(pgn_white), args[i]);
2281 strcpy(pgn_white + strlen(pgn_white), " ");
2282 }
2283 } else if (!strcmp(args[1], "WhiteElo")) {
2284 pgn_white_elo[0] = 0;
2285 strcpy(pgn_white_elo, args[2]);
2286 } else if (!strcmp(args[1], "Black")) {
2287 pgn_black[0] = 0;
2288 for (i = 2; i < nargs; i++) {
2289 strcpy(pgn_black + strlen(pgn_black), args[i]);
2290 strcpy(pgn_black + strlen(pgn_black), " ");
2291 }
2292 } else if (!strcmp(args[1], "BlackElo")) {
2293 pgn_black_elo[0] = 0;
2294 strcpy(pgn_black_elo, args[2]);
2295 }
2296 }
2297 /*
2298 ************************************************************
2299 * *
2300 * "ping" command simply echos the argument back to xboard *
2301 * to let it know all previous commands have been executed *
2302 * and we are ready for whatever is next. *
2303 * *
2304 ************************************************************
2305 */
2306 else if (OptionMatch("ping", *args)) {
2307 if (pondering)
2308 Print(-1, "pong %s\n", args[1]);
2309 else
2310 pong = atoi(args[1]);
2311 }
2312 /*
2313 ************************************************************
2314 * *
2315 * "playother" command says "position is set up, we are *
2316 * waiting on the opponent to move, ponder if you want to *
2317 * do so. *
2318 * *
2319 ************************************************************
2320 */
2321 else if (OptionMatch("playother", *args)) {
2322 force = 0;
2323 }
2324 /*
2325 ************************************************************
2326 * *
2327 * "ponder" command toggles pondering off/on or sets a *
2328 * move to ponder. *
2329 * *
2330 ************************************************************
2331 */
2332 else if (OptionMatch("ponder", *args)) {
2333 if (thinking || pondering)
2334 return 2;
2335 if (nargs < 2) {
2336 printf("usage: ponder off|on|<move>\n");
2337 return 1;
2338 }
2339 if (!strcmp(args[1], "on")) {
2340 ponder = 1;
2341 Print(32, "pondering enabled.\n");
2342 } else if (!strcmp(args[1], "off")) {
2343 ponder = 0;
2344 Print(32, "pondering disabled.\n");
2345 } else {
2346 ponder_move = InputMove(tree, 0, game_wtm, 0, 0, args[1]);
2347 last_pv.pathd = 0;
2348 last_pv.pathl = 0;
2349 }
2350 }
2351 /*
2352 ************************************************************
2353 * *
2354 * "post/nopost" command sets/resets "show thinking" mode *
2355 * for xboard compatibility. *
2356 * *
2357 ************************************************************
2358 */
2359 else if (OptionMatch("post", *args)) {
2360 post = 1;
2361 } else if (OptionMatch("nopost", *args)) {
2362 post = 0;
2363 }
2364 /*
2365 ************************************************************
2366 * *
2367 * "protover" command is sent by xboard to identify the *
2368 * xboard protocol version and discover what the engine *
2369 * can handle. *
2370 * *
2371 ************************************************************
2372 */
2373 else if (OptionMatch("protover", *args)) {
2374 int pversion = atoi(args[1]);
2375
2376 if (pversion >= 1 && pversion <= 3) {
2377 if (pversion >= 2) {
2378 Print(-1, "feature ping=1 setboard=1 san=1 time=1 draw=1\n");
2379 Print(-1, "feature sigint=0 sigterm=0 reuse=0 analyze=1\n");
2380 Print(-1, "feature myname=\"Crafty-%s\" name=1\n", version);
2381 Print(-1, "feature playother=1 colors=0 memory=%d\n", allow_memory);
2382 #if (CPUS > 1)
2383 Print(-1, "feature smp=%d\n", allow_cores);
2384 #endif
2385 Print(-1, "feature variants=\"normal,nocastle\"\n");
2386 Print(-1, "feature done=1\n");
2387 xboard_done = 1;
2388 }
2389 } else
2390 Print(4095, "ERROR, bogus xboard protocol version received.\n");
2391 }
2392 /*
2393 ************************************************************
2394 * *
2395 * "random" command is ignored. [xboard compatibility] *
2396 * *
2397 ************************************************************
2398 */
2399 else if (OptionMatch("random", *args)) {
2400 return xboard;
2401 }
2402 /*
2403 ************************************************************
2404 * *
2405 * "rating" is used by xboard to set Crafty's rating and *
2406 * the opponent's rating, which is used by the learning *
2407 * functions. *
2408 * *
2409 ************************************************************
2410 */
2411 else if (OptionMatch("rating", *args)) {
2412 int rd;
2413
2414 if (nargs < 3) {
2415 printf("usage: rating <Crafty> <opponent>\n");
2416 return 1;
2417 }
2418 crafty_rating = atoi(args[1]);
2419 opponent_rating = atoi(args[2]);
2420 if (crafty_rating == 0 && opponent_rating == 0) {
2421 crafty_rating = 2500;
2422 opponent_rating = 2300;
2423 }
2424 if (dynamic_draw_score) {
2425 rd = opponent_rating - crafty_rating;
2426 rd = Max(Min(rd, 300), -300);
2427 abs_draw_score = rd / 8;
2428 if (log_file) {
2429 fprintf(log_file, "Crafty's rating: %d.\n", crafty_rating);
2430 fprintf(log_file, "opponent's rating: %d.\n", opponent_rating);
2431 fprintf(log_file, "draw score: %d.\n", abs_draw_score);
2432 }
2433 }
2434 }
2435 /*
2436 ************************************************************
2437 * *
2438 * "remove" command backs up the game one whole move, *
2439 * leaving the opponent still on move. It's intended for *
2440 * xboard compatibility, but works in any mode. *
2441 * *
2442 ************************************************************
2443 */
2444 else if (OptionMatch("remove", *args)) {
2445 if (thinking || pondering)
2446 return 2;
2447 move_number--;
2448 sprintf(buffer, "reset %d", move_number);
2449 Option(tree);
2450 }
2451 /*
2452 ************************************************************
2453 * *
2454 * "read" reads game moves in and makes them. This can *
2455 * be used in two ways: (1) type "read" and then start *
2456 * entering moves; type "exit" when done; (2) type *
2457 * "read <filename>" to read moves in from <filename>. *
2458 * Note that read will attempt to skip over "non-move" *
2459 * text and try to extract moves if it can. *
2460 * *
2461 * Note that "reada" appends to the existing position, *
2462 * while "read" resets the board to the start position *
2463 * before reading moves. *
2464 * *
2465 ************************************************************
2466 */
2467 else if (OptionMatch("read", *args) || OptionMatch("reada", *args)) {
2468 FILE *read_input = 0;
2469 int append, move, readstat;
2470
2471 if (thinking || pondering)
2472 return 2;
2473 nargs = ReadParse(buffer, args, " \t=");
2474 if (!strcmp("reada", *args))
2475 append = 1;
2476 else
2477 append = 0;
2478 ponder_move = 0;
2479 last_pv.pathd = 0;
2480 last_pv.pathl = 0;
2481 if (nargs > 1) {
2482 if (!(read_input = fopen(args[1], "r"))) {
2483 printf("file %s does not exist.\n", args[1]);
2484 return 1;
2485 }
2486 } else {
2487 printf("type \"exit\" to terminate.\n");
2488 read_input = stdin;
2489 }
2490 if (!append) {
2491 InitializeChessBoard(tree);
2492 game_wtm = 1;
2493 move_number = 1;
2494 tc_moves_remaining[white] = tc_moves;
2495 tc_moves_remaining[black] = tc_moves;
2496 }
2497 /*
2498 step 1: read in the PGN tags.
2499 */
2500 readstat = ReadPGN(0, 0);
2501 do {
2502 if (read_input == stdin) {
2503 if (game_wtm)
2504 printf("read.White(%d): ", move_number);
2505 else
2506 printf("read.Black(%d): ", move_number);
2507 fflush(stdout);
2508 }
2509 readstat = ReadPGN(read_input, 0);
2510 } while (readstat == 1);
2511 if (readstat < 0)
2512 return 1;
2513 /*
2514 step 2: read in the moves.
2515 */
2516 do {
2517 move = 0;
2518 move = ReadNextMove(tree, buffer, 0, game_wtm);
2519 if (move) {
2520 if (read_input != stdin) {
2521 printf("%s ", OutputMove(tree, 0, game_wtm, move));
2522 if (!(move_number % 8) && Flip(game_wtm))
2523 printf("\n");
2524 }
2525 fseek(history_file, ((move_number - 1) * 2 + 1 - game_wtm) * 10,
2526 SEEK_SET);
2527 fprintf(history_file, "%9s\n", OutputMove(tree, 0, game_wtm, move));
2528 MakeMoveRoot(tree, game_wtm, move);
2529 TimeAdjust(game_wtm, 0);
2530 #if defined(DEBUG)
2531 ValidatePosition(tree, 1, move, "Option()");
2532 #endif
2533 } else if (!read_input)
2534 printf("illegal move.\n");
2535 if (move) {
2536 game_wtm = Flip(game_wtm);
2537 if (game_wtm)
2538 move_number++;
2539 }
2540 if (read_input == stdin) {
2541 if (game_wtm)
2542 printf("read.White(%d): ", move_number);
2543 else
2544 printf("read.Black(%d): ", move_number);
2545 fflush(stdout);
2546 }
2547 readstat = ReadPGN(read_input, 0);
2548 if (readstat < 0)
2549 break;
2550 if (!strcmp(buffer, "exit"))
2551 break;
2552 } while (1);
2553 moves_out_of_book = 0;
2554 printf("NOTICE: %d moves to next time control\n",
2555 tc_moves_remaining[root_wtm]);
2556 root_wtm = !game_wtm;
2557 if (read_input != stdin) {
2558 printf("\n");
2559 fclose(read_input);
2560 }
2561 }
2562 /*
2563 ************************************************************
2564 * *
2565 * "rejected" handles the new xboard protocol version 2 *
2566 * rejected command. *
2567 * *
2568 ************************************************************
2569 */
2570 else if (OptionMatch("rejected", *args)) {
2571 Print(4095, "ERROR. feature %s rejected by xboard\n", args[1]);
2572 }
2573 /*
2574 ************************************************************
2575 * *
2576 * "reset" restores (backs up) a game to a prior position *
2577 * with the same side on move. Reset 17 would reset the *
2578 * position to what it was at move 17 with the current *
2579 * still on move (you can use white/black commands to *
2580 * change the side to move first, if needed.) *
2581 * *
2582 ************************************************************
2583 */
2584 else if (OptionMatch("reset", *args)) {
2585 int i, move, nmoves;
2586
2587 if (!history_file)
2588 return 1;
2589 if (thinking || pondering)
2590 return 2;
2591 if (nargs < 2) {
2592 printf("usage: reset <movenumber>\n");
2593 return 1;
2594 }
2595 ponder_move = 0;
2596 last_mate_score = 0;
2597 last_pv.pathd = 0;
2598 last_pv.pathl = 0;
2599 if (thinking || pondering)
2600 return 2;
2601 over = 0;
2602 move_number = atoi(args[1]);
2603 if (!move_number) {
2604 move_number = 1;
2605 return 1;
2606 }
2607 nmoves = (move_number - 1) * 2 + 1 - game_wtm;
2608 root_wtm = Flip(game_wtm);
2609 InitializeChessBoard(tree);
2610 game_wtm = 1;
2611 move_number = 1;
2612 tc_moves_remaining[white] = tc_moves;
2613 tc_moves_remaining[black] = tc_moves;
2614 for (i = 0; i < nmoves; i++) {
2615 fseek(history_file, i * 10, SEEK_SET);
2616 v = fscanf(history_file, "%s", buffer);
2617 if (v <= 0)
2618 perror("Option() fscanf error: ");
2619 /*
2620 If the move is "pass", that means that the side on move passed.
2621 This includes the case where the game started from a black-to-move
2622 position; then white's first move is recorded as a pass.
2623 */
2624 if (strcmp(buffer, "pass") == 0) {
2625 game_wtm = Flip(game_wtm);
2626 if (game_wtm)
2627 move_number++;
2628 continue;
2629 }
2630 move = InputMove(tree, 0, game_wtm, 0, 0, buffer);
2631 if (move)
2632 MakeMoveRoot(tree, game_wtm, move);
2633 else {
2634 printf("ERROR! move %s is illegal\n", buffer);
2635 break;
2636 }
2637 TimeAdjust(game_wtm, 0);
2638 game_wtm = Flip(game_wtm);
2639 if (game_wtm)
2640 move_number++;
2641 }
2642 moves_out_of_book = 0;
2643 printf("NOTICE: %d moves to next time control\n",
2644 tc_moves_remaining[root_wtm]);
2645 }
2646 /*
2647 ************************************************************
2648 * *
2649 * "resign" command sets the resignation threshold to the *
2650 * number of pawns the program must be behind before *
2651 * resigning (0 -> disable resignations). Resign with no *
2652 * arguments will mark the pgn result as lost by the *
2653 * opponent. *
2654 * *
2655 ************************************************************
2656 */
2657 else if (OptionMatch("resign", *args)) {
2658 if (nargs < 2) {
2659 if (crafty_is_white) {
2660 Print(4095, "result 1-0\n");
2661 strcpy(pgn_result, "1-0");
2662 } else {
2663 Print(4095, "result 0-1\n");
2664 strcpy(pgn_result, "0-1");
2665 }
2666 learn_value = 300;
2667 LearnBook();
2668 return 1;
2669 }
2670 resign = atoi(args[1]);
2671 if (nargs == 3)
2672 resign_count = atoi(args[2]);
2673 if (resign)
2674 Print(32, "resign after %d consecutive moves with score < %d.\n",
2675 resign_count, -resign);
2676 else
2677 Print(32, "disabled resignations.\n");
2678 }
2679 /*
2680 ************************************************************
2681 * *
2682 * "result" command comes from xboard/winboard and gives *
2683 * the result of the current game. If learning routines *
2684 * have not yet been activated, this will do it. *
2685 * *
2686 ************************************************************
2687 */
2688 else if (OptionMatch("result", *args)) {
2689 if (nargs > 1) {
2690 if (!strcmp(args[1], "1-0")) {
2691 strcpy(pgn_result, "1-0");
2692 if (crafty_is_white)
2693 learn_value = 300;
2694 else
2695 learn_value = -300;
2696 } else if (!strcmp(args[1], "0-1")) {
2697 strcpy(pgn_result, "0-1");
2698 if (crafty_is_white)
2699 learn_value = -300;
2700 else
2701 learn_value = 300;
2702 } else if (!strcmp(args[1], "1/2-1/2")) {
2703 strcpy(pgn_result, "1/2-1/2");
2704 learn_value = 1;
2705 }
2706 LearnBook();
2707 return 1;
2708 }
2709 }
2710 /*
2711 ************************************************************
2712 * *
2713 * "safety" command sets a specific time safety margin *
2714 * target for normal timed games. This can generally be *
2715 * left at the default value unless Crafty is being *
2716 * manually operated. *
2717 * *
2718 ************************************************************
2719 */
2720 else if (OptionMatch("safety", *args)) {
2721 if (nargs == 2)
2722 tc_safety_margin = atoi(args[1]) * 100;
2723 Print(32, "safety margin set to %s.\n", DisplayTime(tc_safety_margin));
2724 }
2725 /*
2726 ************************************************************
2727 * *
2728 * "savegame" command saves the game in a file in PGN *
2729 * format. Command has an optional filename. *
2730 * *
2731 ************************************************************
2732 */
2733 else if (OptionMatch("savegame", *args)) {
2734 struct tm *timestruct;
2735 FILE *output_file;
2736 time_t secs;
2737 int i, more, swtm;
2738 char input[128], text[128], *next;
2739
2740 output_file = stdout;
2741 secs = time(0);
2742 timestruct = localtime((time_t *) & secs);
2743 if (nargs > 1) {
2744 if (!(output_file = fopen(args[1], "w"))) {
2745 printf("unable to open %s for write.\n", args[1]);
2746 return 1;
2747 }
2748 }
2749 fprintf(output_file, "[Event \"%s\"]\n", pgn_event);
2750 fprintf(output_file, "[Site \"%s\"]\n", pgn_site);
2751 fprintf(output_file, "[Date \"%4d.%02d.%02d\"]\n",
2752 timestruct->tm_year + 1900, timestruct->tm_mon + 1,
2753 timestruct->tm_mday);
2754 fprintf(output_file, "[Round \"%s\"]\n", pgn_round);
2755 fprintf(output_file, "[White \"%s\"]\n", pgn_white);
2756 fprintf(output_file, "[WhiteElo \"%s\"]\n", pgn_white_elo);
2757 fprintf(output_file, "[Black \"%s\"]\n", pgn_black);
2758 fprintf(output_file, "[BlackElo \"%s\"]\n", pgn_black_elo);
2759 fprintf(output_file, "[Result \"%s\"]\n", pgn_result);
2760 /* Handle setup positions and initial pass by white */
2761 swtm = 1;
2762 if (move_number > 1 || !game_wtm) {
2763 fseek(history_file, 0, SEEK_SET);
2764 if (fscanf(history_file, "%s", input) == 1 &&
2765 strcmp(input, "pass") == 0)
2766 swtm = 0;
2767 }
2768 if (initial_position[0])
2769 fprintf(output_file, "[FEN \"%s\"]\n[SetUp \"1\"]\n", initial_position);
2770 else if (!swtm) {
2771 fprintf(output_file,
2772 "[FEN \"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR b KQkq - 0 1\"\n"
2773 "[SetUp \"1\"]\n");
2774 }
2775 fprintf(output_file, "\n");
2776 next = text;
2777 if (!swtm) {
2778 strcpy(next, "1... ");
2779 next = text + strlen(text);
2780 }
2781 /* Output the moves */
2782 more = 0;
2783 for (i = (swtm ? 0 : 1); i < (move_number - 1) * 2 - game_wtm + 1; i++) {
2784 fseek(history_file, i * 10, SEEK_SET);
2785 v = fscanf(history_file, "%s", input);
2786 if (v <= 0)
2787 perror("Option() fscanf error: ");
2788 if (!(i % 2)) {
2789 sprintf(next, "%d. ", i / 2 + 1);
2790 next = text + strlen(text);
2791 }
2792 sprintf(next, "%s ", input);
2793 next = text + strlen(text);
2794 more = 1;
2795 if (next - text >= 60) {
2796 fprintf(output_file, "%s\n", text);
2797 more = 0;
2798 next = text;
2799 }
2800 }
2801 if (more)
2802 fprintf(output_file, "%s", text);
2803 fprintf(output_file, "%s\n", pgn_result);
2804 if (output_file != stdout)
2805 fclose(output_file);
2806 printf("PGN save complete.\n");
2807 }
2808 /*
2809 ************************************************************
2810 * *
2811 * "savepos" command saves the current position in a FEN *
2812 * (Forsythe-Edwards Notation) string that can be later *
2813 * used to recreate this exact position. *
2814 * *
2815 ************************************************************
2816 */
2817 else if (OptionMatch("savepos", *args)) {
2818 FILE *output_file;
2819 int rank, file, nempty;
2820
2821 output_file = stdout;
2822 if (nargs > 1) {
2823 if (!strcmp(args[1], "*")) {
2824 output_file = 0;
2825 strcpy(initial_position, "");
2826 } else if (!(output_file = fopen(args[1], "w"))) {
2827 printf("unable to open %s for write.\n", args[1]);
2828 return 1;
2829 }
2830 }
2831 if (output_file)
2832 fprintf(output_file, "setboard ");
2833 for (rank = RANK8; rank >= RANK1; rank--) {
2834 nempty = 0;
2835 for (file = FILEA; file <= FILEH; file++) {
2836 if (PcOnSq((rank << 3) + file)) {
2837 if (nempty) {
2838 if (output_file)
2839 fprintf(output_file, "%c", empty_sqs[nempty]);
2840 else
2841 sprintf(initial_position + strlen(initial_position), "%c",
2842 empty_sqs[nempty]);
2843 nempty = 0;
2844 }
2845 if (output_file)
2846 fprintf(output_file, "%c",
2847 translate[PcOnSq((rank << 3) + file) + 6]);
2848 else
2849 sprintf(initial_position + strlen(initial_position), "%c",
2850 translate[PcOnSq((rank << 3) + file) + 6]);
2851 } else
2852 nempty++;
2853 }
2854 if (empty_sqs[nempty]) {
2855 if (output_file)
2856 fprintf(output_file, "%c", empty_sqs[nempty]);
2857 else
2858 sprintf(initial_position + strlen(initial_position), "%c",
2859 empty_sqs[nempty]);
2860 }
2861 if (rank != RANK1) {
2862 if (output_file)
2863 fprintf(output_file, "/");
2864 else
2865 sprintf(initial_position + strlen(initial_position), "/");
2866 }
2867 }
2868 if (output_file)
2869 fprintf(output_file, " %c ", (game_wtm) ? 'w' : 'b');
2870 else
2871 sprintf(initial_position + strlen(initial_position), " %c ",
2872 (game_wtm) ? 'w' : 'b');
2873 if (Castle(0, white) & 1) {
2874 if (output_file)
2875 fprintf(output_file, "K");
2876 else
2877 sprintf(initial_position + strlen(initial_position), "K");
2878 }
2879 if (Castle(0, white) & 2) {
2880 if (output_file)
2881 fprintf(output_file, "Q");
2882 else
2883 sprintf(initial_position + strlen(initial_position), "Q");
2884 }
2885 if (Castle(0, black) & 1) {
2886 if (output_file)
2887 fprintf(output_file, "k");
2888 else
2889 sprintf(initial_position + strlen(initial_position), "k");
2890 }
2891 if (Castle(0, black) & 2) {
2892 if (output_file)
2893 fprintf(output_file, "q");
2894 else
2895 sprintf(initial_position + strlen(initial_position), "q");
2896 }
2897 if (!Castle(0, white) && !Castle(0, black)) {
2898 if (output_file)
2899 fprintf(output_file, " -");
2900 else
2901 sprintf(initial_position + strlen(initial_position), " -");
2902 }
2903 if (EnPassant(0)) {
2904 if (output_file)
2905 fprintf(output_file, " %c%c", File(EnPassant(0)) + 'a',
2906 Rank(EnPassant(0)) + '1');
2907 else
2908 sprintf(initial_position + strlen(initial_position), " %c%c",
2909 File(EnPassant(0)) + 'a', Rank(EnPassant(0)) + '1');
2910 } else {
2911 if (output_file)
2912 fprintf(output_file, " -");
2913 else
2914 sprintf(initial_position + strlen(initial_position), " -");
2915 }
2916 if (output_file)
2917 fprintf(output_file, "\n");
2918 if (output_file && output_file != stdout) {
2919 fprintf(output_file, "exit\n");
2920 fclose(output_file);
2921 }
2922 if (output_file)
2923 printf("FEN save complete.\n");
2924 }
2925 /*
2926 ************************************************************
2927 * *
2928 * "scale" command is used for tuning. We modify this to *
2929 * scale some scoring value(s) by a percentage that can *
2930 * be either positive or negative. *
2931 * *
2932 ************************************************************
2933 */
2934 else if (!strcmp("scale", *args)) {
2935 scale = atoi(args[1]);
2936 }
2937 /*
2938 ************************************************************
2939 * *
2940 * "score" command displays static evaluation of the *
2941 * current board position. *
2942 * *
2943 ************************************************************
2944 */
2945 else if (OptionMatch("score", *args)) {
2946 int phase, s, tw, tb, mgb, mgw, egb, egw, trop[2];
2947
2948 if (thinking || pondering)
2949 return 2;
2950 memset((void *) &(tree->pawn_score), 0, sizeof(tree->pawn_score));
2951 Print(32, "note: scores are for the white side\n");
2952 Print(32, " ");
2953 Print(32, " +-----------white----------+");
2954 Print(32, "-----------black----------+\n");
2955 tree->score_mg = 0;
2956 tree->score_eg = 0;
2957 mgb = tree->score_mg;
2958 EvaluateMaterial(tree, game_wtm);
2959 mgb = tree->score_mg - mgb;
2960 Print(32, "material.......%s", DisplayEvaluation(mgb, 1));
2961 Print(32, " | comp mg eg |");
2962 Print(32, " comp mg eg |\n");
2963 root_wtm = Flip(game_wtm);
2964 tree->status[1] = tree->status[0];
2965 s = Evaluate(tree, 1, game_wtm, -99999, 99999);
2966 trop[black] = tree->tropism[black];
2967 trop[white] = tree->tropism[white];
2968 if (!game_wtm)
2969 s = -s;
2970 tree->score_mg = 0;
2971 tree->score_eg = 0;
2972 phase =
2973 Min(62, TotalPieces(white, occupied) + TotalPieces(black, occupied));
2974 tree->pawn_score.score_mg = 0;
2975 tree->pawn_score.score_eg = 0;
2976 mgb = tree->pawn_score.score_mg;
2977 egb = tree->pawn_score.score_eg;
2978 EvaluatePawns(tree, black);
2979 mgb = tree->pawn_score.score_mg - mgb;
2980 egb = tree->pawn_score.score_eg - egb;
2981 mgw = tree->pawn_score.score_mg;
2982 egw = tree->pawn_score.score_eg;
2983 EvaluatePawns(tree, white);
2984 mgw = tree->pawn_score.score_mg - mgw;
2985 egw = tree->pawn_score.score_eg - egw;
2986 tb = (mgb * phase + egb * (62 - phase)) / 62;
2987 tw = (mgw * phase + egw * (62 - phase)) / 62;
2988 Print(32, "pawns..........%s |", DisplayEvaluation(tb + tw, 1));
2989 Print(32, " %s", DisplayEvaluation(tw, 1));
2990 Print(32, " %s", DisplayEvaluation(mgw, 1));
2991 Print(32, " %s |", DisplayEvaluation(egw, 1));
2992 Print(32, " %s", DisplayEvaluation(tb, 1));
2993 Print(32, " %s", DisplayEvaluation(mgb, 1));
2994 Print(32, " %s |\n", DisplayEvaluation(egb, 1));
2995 mgb = tree->score_mg;
2996 egb = tree->score_eg;
2997 EvaluatePassedPawns(tree, black, game_wtm);
2998 mgb = tree->score_mg - mgb;
2999 egb = tree->score_eg - egb;
3000 mgw = tree->score_mg;
3001 egw = tree->score_eg;
3002 EvaluatePassedPawns(tree, white, game_wtm);
3003 mgw = tree->score_mg - mgw;
3004 egw = tree->score_eg - egw;
3005 tb = (mgb * phase + egb * (62 - phase)) / 62;
3006 tw = (mgw * phase + egw * (62 - phase)) / 62;
3007 Print(32, "passed pawns...%s |", DisplayEvaluation(tb + tw, 1));
3008 Print(32, " %s", DisplayEvaluation(tw, 1));
3009 Print(32, " %s", DisplayEvaluation(mgw, 1));
3010 Print(32, " %s |", DisplayEvaluation(egw, 1));
3011 Print(32, " %s", DisplayEvaluation(tb, 1));
3012 Print(32, " %s", DisplayEvaluation(mgb, 1));
3013 Print(32, " %s |\n", DisplayEvaluation(egb, 1));
3014 mgb = tree->score_mg;
3015 egb = tree->score_eg;
3016 EvaluateKnights(tree, black);
3017 mgb = tree->score_mg - mgb;
3018 egb = tree->score_eg - egb;
3019 mgw = tree->score_mg;
3020 egw = tree->score_eg;
3021 EvaluateKnights(tree, white);
3022 mgw = tree->score_mg - mgw;
3023 egw = tree->score_eg - egw;
3024 tb = (mgb * phase + egb * (62 - phase)) / 62;
3025 tw = (mgw * phase + egw * (62 - phase)) / 62;
3026 Print(32, "knights........%s |", DisplayEvaluation(tb + tw, 1));
3027 Print(32, " %s", DisplayEvaluation(tw, 1));
3028 Print(32, " %s", DisplayEvaluation(mgw, 1));
3029 Print(32, " %s |", DisplayEvaluation(egw, 1));
3030 Print(32, " %s", DisplayEvaluation(tb, 1));
3031 Print(32, " %s", DisplayEvaluation(mgb, 1));
3032 Print(32, " %s |\n", DisplayEvaluation(egb, 1));
3033 mgb = tree->score_mg;
3034 egb = tree->score_eg;
3035 EvaluateBishops(tree, black);
3036 mgb = tree->score_mg - mgb;
3037 egb = tree->score_eg - egb;
3038 mgw = tree->score_mg;
3039 egw = tree->score_eg;
3040 EvaluateBishops(tree, white);
3041 mgw = tree->score_mg - mgw;
3042 egw = tree->score_eg - egw;
3043 tb = (mgb * phase + egb * (62 - phase)) / 62;
3044 tw = (mgw * phase + egw * (62 - phase)) / 62;
3045 Print(32, "bishops........%s |", DisplayEvaluation(tb + tw, 1));
3046 Print(32, " %s", DisplayEvaluation(tw, 1));
3047 Print(32, " %s", DisplayEvaluation(mgw, 1));
3048 Print(32, " %s |", DisplayEvaluation(egw, 1));
3049 Print(32, " %s", DisplayEvaluation(tb, 1));
3050 Print(32, " %s", DisplayEvaluation(mgb, 1));
3051 Print(32, " %s |\n", DisplayEvaluation(egb, 1));
3052 mgb = tree->score_mg;
3053 egb = tree->score_eg;
3054 EvaluateRooks(tree, black);
3055 mgb = tree->score_mg - mgb;
3056 egb = tree->score_eg - egb;
3057 mgw = tree->score_mg;
3058 egw = tree->score_eg;
3059 EvaluateRooks(tree, white);
3060 mgw = tree->score_mg - mgw;
3061 egw = tree->score_eg - egw;
3062 tb = (mgb * phase + egb * (62 - phase)) / 62;
3063 tw = (mgw * phase + egw * (62 - phase)) / 62;
3064 Print(32, "rooks..........%s |", DisplayEvaluation(tb + tw, 1));
3065 Print(32, " %s", DisplayEvaluation(tw, 1));
3066 Print(32, " %s", DisplayEvaluation(mgw, 1));
3067 Print(32, " %s |", DisplayEvaluation(egw, 1));
3068 Print(32, " %s", DisplayEvaluation(tb, 1));
3069 Print(32, " %s", DisplayEvaluation(mgb, 1));
3070 Print(32, " %s |\n", DisplayEvaluation(egb, 1));
3071 mgb = tree->score_mg;
3072 egb = tree->score_eg;
3073 EvaluateQueens(tree, black);
3074 mgb = tree->score_mg - mgb;
3075 egb = tree->score_eg - egb;
3076 mgw = tree->score_mg;
3077 egw = tree->score_eg;
3078 EvaluateQueens(tree, white);
3079 mgw = tree->score_mg - mgw;
3080 egw = tree->score_eg - egw;
3081 tb = (mgb * phase + egb * (62 - phase)) / 62;
3082 tw = (mgw * phase + egw * (62 - phase)) / 62;
3083 Print(32, "queens.........%s |", DisplayEvaluation(tb + tw, 1));
3084 Print(32, " %s", DisplayEvaluation(tw, 1));
3085 Print(32, " %s", DisplayEvaluation(mgw, 1));
3086 Print(32, " %s |", DisplayEvaluation(egw, 1));
3087 Print(32, " %s", DisplayEvaluation(tb, 1));
3088 Print(32, " %s", DisplayEvaluation(mgb, 1));
3089 Print(32, " %s |\n", DisplayEvaluation(egb, 1));
3090 tree->tropism[black] = trop[black];
3091 tree->tropism[white] = trop[white];
3092 mgb = tree->score_mg;
3093 egb = tree->score_eg;
3094 EvaluateKing(tree, 1, black);
3095 mgb = tree->score_mg - mgb;
3096 egb = tree->score_eg - egb;
3097 mgw = tree->score_mg;
3098 egw = tree->score_eg;
3099 EvaluateKing(tree, 1, white);
3100 mgw = tree->score_mg - mgw;
3101 egw = tree->score_eg - egw;
3102 tb = (mgb * phase + egb * (62 - phase)) / 62;
3103 tw = (mgw * phase + egw * (62 - phase)) / 62;
3104 Print(32, "kings..........%s |", DisplayEvaluation(tb + tw, 1));
3105 Print(32, " %s", DisplayEvaluation(tw, 1));
3106 Print(32, " %s", DisplayEvaluation(mgw, 1));
3107 Print(32, " %s |", DisplayEvaluation(egw, 1));
3108 Print(32, " %s", DisplayEvaluation(tb, 1));
3109 Print(32, " %s", DisplayEvaluation(mgb, 1));
3110 Print(32, " %s |\n", DisplayEvaluation(egb, 1));
3111 egb = tree->score_eg;
3112 if ((TotalPieces(white, occupied) == 0 && tree->pawn_score.passed[black])
3113 || (TotalPieces(black, occupied) == 0 &&
3114 tree->pawn_score.passed[white]))
3115 EvaluatePassedPawnRaces(tree, game_wtm);
3116 egb = tree->score_eg - egb;
3117 Print(32, "pawn races.....%s", DisplayEvaluation(egb, 1));
3118 Print(32, " +--------------------------+--------------------------+\n");
3119 Print(32, "total..........%s\n", DisplayEvaluation(s, 1));
3120 }
3121 /*
3122 ************************************************************
3123 * *
3124 * "screen" command runs runs through a test suite of *
3125 * positions and culls any where a search returns a value *
3126 * outside the margin given to the screen command. *
3127 * *
3128 ************************************************************
3129 */
3130 else if (OptionMatch("screen", *args)) {
3131 int margin = 9999999, save_noise, save_display;
3132
3133 nargs = ReadParse(buffer, args, " \t;=");
3134 if (thinking || pondering)
3135 return 2;
3136 if (nargs < 3) {
3137 printf("usage: screen <filename> score-margin\n");
3138 return 1;
3139 }
3140 save_noise = noise_level;
3141 save_display = display_options;
3142 early_exit = 99;
3143 margin = atoi(args[2]);
3144 noise_level = 99999999;
3145 display_options = 2048;
3146 Test(args[1], 0, 1, margin);
3147 noise_level = save_noise;
3148 display_options = save_display;
3149 ponder_move = 0;
3150 last_pv.pathd = 0;
3151 last_pv.pathl = 0;
3152 }
3153 /*
3154 ************************************************************
3155 * *
3156 * "sd" command sets a specific search depth to control *
3157 * the tree search depth. *
3158 * *
3159 ************************************************************
3160 */
3161 else if (OptionMatch("sd", *args)) {
3162 if (nargs < 2) {
3163 printf("usage: sd <depth>\n");
3164 return 1;
3165 }
3166 search_depth = atoi(args[1]);
3167 Print(32, "search depth set to %d.\n", search_depth);
3168 }
3169 /*
3170 ************************************************************
3171 * *
3172 * "search" command sets a specific move for the search *
3173 * to analyze, ignoring all others completely. *
3174 * *
3175 ************************************************************
3176 */
3177 else if (OptionMatch("search", *args)) {
3178 if (thinking || pondering)
3179 return 2;
3180 if (nargs < 2) {
3181 printf("usage: search <move>\n");
3182 return 1;
3183 }
3184 search_move = InputMove(tree, 0, game_wtm, 0, 0, args[1]);
3185 if (!search_move)
3186 search_move = InputMove(tree, 0, Flip(game_wtm), 0, 0, args[1]);
3187 if (!search_move)
3188 printf("illegal move.\n");
3189 }
3190 /*
3191 ************************************************************
3192 * *
3193 * "setboard" command sets the board to a specific *
3194 * position for analysis by the program. *
3195 * *
3196 ************************************************************
3197 */
3198 else if (OptionMatch("setboard", *args)) {
3199 if (thinking || pondering)
3200 return 2;
3201 nargs = ReadParse(buffer, args, " \t;=");
3202 if (nargs < 3) {
3203 printf("usage: setboard <fen>\n");
3204 return 1;
3205 }
3206 SetBoard(tree, nargs - 1, args + 1, 0);
3207 move_number = 1;
3208 if (!game_wtm) {
3209 game_wtm = 1;
3210 Pass();
3211 }
3212 ponder_move = 0;
3213 last_pv.pathd = 0;
3214 last_pv.pathl = 0;
3215 over = 0;
3216 strcpy(buffer, "savepos *");
3217 Option(tree);
3218 } else if (StrCnt(*args, '/') > 3) {
3219 if (thinking || pondering)
3220 return 2;
3221 nargs = ReadParse(buffer, args, " \t;=");
3222 SetBoard(tree, nargs, args, 0);
3223 move_number = 1;
3224 if (!game_wtm) {
3225 game_wtm = 1;
3226 Pass();
3227 }
3228 ponder_move = 0;
3229 last_pv.pathd = 0;
3230 last_pv.pathl = 0;
3231 over = 0;
3232 strcpy(buffer, "savepos *");
3233 Option(tree);
3234 }
3235 /*
3236 ************************************************************
3237 * *
3238 * "settc" command is used to reset the time controls *
3239 * after a complete restart. *
3240 * *
3241 ************************************************************
3242 */
3243 else if (OptionMatch("settc", *args)) {
3244 if (thinking || pondering)
3245 return 2;
3246 if (nargs < 4) {
3247 printf("usage: settc <wmoves> <wtime> <bmoves> <btime>\n");
3248 return 1;
3249 }
3250 tc_moves_remaining[white] = atoi(args[1]);
3251 tc_time_remaining[white] = ParseTime(args[2]) * 6000;
3252 tc_moves_remaining[black] = atoi(args[3]);
3253 tc_time_remaining[black] = ParseTime(args[4]) * 6000;
3254 Print(32, "time remaining: %s (white).\n",
3255 DisplayTime(tc_time_remaining[white]));
3256 Print(32, "time remaining: %s (black).\n",
3257 DisplayTime(tc_time_remaining[black]));
3258 if (tc_sudden_death != 1) {
3259 Print(32, "%d moves to next time control (white)\n",
3260 tc_moves_remaining[white]);
3261 Print(32, "%d moves to next time control (black)\n",
3262 tc_moves_remaining[black]);
3263 } else
3264 Print(32, "Sudden-death time control in effect\n");
3265 TimeSet(999);
3266 }
3267 /*
3268 ************************************************************
3269 * *
3270 * "show" command enables/disables whether or not we want *
3271 * show book information as the game is played. *
3272 * *
3273 ************************************************************
3274 */
3275 else if (OptionMatch("show", *args)) {
3276 if (nargs < 2) {
3277 printf("usage: show book\n");
3278 return 1;
3279 }
3280 if (OptionMatch("book", args[1])) {
3281 show_book = !show_book;
3282 if (show_book)
3283 Print(32, "show book statistics\n");
3284 else
3285 Print(32, "don't show book statistics\n");
3286 }
3287 }
3288 /*
3289 ************************************************************
3290 * *
3291 * "skill" command sets a value from 1-100 that affects *
3292 * Crafty's playing skill level. 100 => max skill, 1 => *
3293 * minimal skill. This is used to slow the search speed *
3294 * (and depth) significantly. *
3295 * *
3296 ************************************************************
3297 */
3298 #if defined(SKILL)
3299 else if (OptionMatch("skill", *args)) {
3300 if (nargs < 2) {
3301 printf("usage: skill <1-100>\n");
3302 return 1;
3303 }
3304 if (skill != 100)
3305 printf("ERROR: skill can only be changed one time in a game\n");
3306 else {
3307 skill = atoi(args[1]);
3308 if (skill < 1 || skill > 100) {
3309 printf("ERROR: skill range is 1-100 only\n");
3310 skill = 100;
3311 }
3312 Print(32, "skill level set to %d%%\n", skill);
3313 }
3314 }
3315 #endif
3316 /*
3317 ************************************************************
3318 * *
3319 * "smp" command is used to tune the various SMP search *
3320 * parameters. *
3321 * *
3322 * "smpaffinity" command is used to enable (>= 0) and to *
3323 * disable smp processor affinity (off). If you try to *
3324 * run two instances of Crafty on the same machine, ONE *
3325 * them (if not both) need to have processor affinity *
3326 * disabled or else you can use the smpaffinity=<n> to *
3327 * prevent processor conflicts. If you use a 32 core *
3328 * machine, and you want to run two instances of Crafty, *
3329 * use smpaffinity=0 on one, and smpaffinity=16 on the *
3330 * other. The first will bind to processors 0-15, and *
3331 * the second will bind to processors 16-31. *
3332 * *
3333 * "smpgroup" command is used to control how many threads *
3334 * may work together at any point in the tree. The *
3335 * usual default is 6, but this might be reduced on a *
3336 * machine with a large number of processors. It should *
3337 * be tested, of course. *
3338 * *
3339 * "smpmin" sets the minimum depth the search can split *
3340 * at to keep it from splitting too near the leaves. *
3341 * *
3342 * "smpmt" command is used to set the maximum number of *
3343 * parallel threads to use, assuming that Crafty was *
3344 * compiled with -DSMP. This value can not be set *
3345 * larger than the compiled-in -DCPUS=n value. *
3346 * *
3347 * "smpnice" command turns on "nice" mode where idle *
3348 * processors are terminated between searches to avoid *
3349 * burning CPU time in the idle loop. *
3350 * *
3351 * "smpnuma" command enables NUMA mode which distributes *
3352 * hash tables across all NUMA nodes evenly. If your *
3353 * machine is not NUMA, or only has one socket (node) you *
3354 * should set this to zero as it will be slightly more *
3355 * efficient when you change hash sizes. *
3356 * *
3357 * "smproot" command is used to enable (1) or disable (0) *
3358 * splitting the tree at the root (ply=1). Splitting at *
3359 * the root is more efficient, but might slow finding the *
3360 * move in some test positions. *
3361 * *
3362 * "smpgsd" sets the minimum depth remaining at which a *
3363 * gratuitous split can be done. *
3364 * *
3365 * "smpgsl" sets the maximum number of gratuitous splits *
3366 * per thread. This only counts splits that have not yet *
3367 * been joined. *
3368 * *
3369 ************************************************************
3370 */
3371 else if (OptionMatch("smpaffinity", *args)) {
3372 if (nargs < 2) {
3373 printf("usage: smpaffinity <0/1>\n");
3374 return 1;
3375 }
3376 if (!strcmp(args[1], "off"))
3377 smp_affinity = -1;
3378 else
3379 smp_affinity = atoi(args[1]);
3380 if (smp_affinity >= 0)
3381 Print(32, "smp processor affinity enabled.\n");
3382 else
3383 Print(32, "smp processor affinity disabled.\n");
3384 } else if (OptionMatch("smpmin", *args)) {
3385 if (nargs < 2) {
3386 printf("usage: smpmin <depth>\n");
3387 return 1;
3388 }
3389 smp_min_split_depth = atoi(args[1]);
3390 Print(32, "minimum thread depth set to %d.\n", smp_min_split_depth);
3391 } else if (OptionMatch("smpgroup", *args)) {
3392 if (nargs < 2) {
3393 printf("usage: smpgroup <threads>\n");
3394 return 1;
3395 }
3396 smp_split_group = atoi(args[1]);
3397 Print(32, "maximum thread group size set to %d.\n", smp_split_group);
3398 } else if (OptionMatch("smpmt", *args) || OptionMatch("mt", *args)
3399 || OptionMatch("cores", *args)) {
3400 int proc;
3401
3402 if (nargs < 2) {
3403 printf("usage: smpmt=<threads>\n");
3404 return 1;
3405 }
3406 if (thinking || pondering)
3407 return 3;
3408 allow_cores = 0;
3409 if (xboard)
3410 Print(4095, "Warning-- xboard 'cores' option disabled\n");
3411 smp_max_threads = atoi(args[1]);
3412 if (smp_max_threads > hardware_processors) {
3413 Print(4095, "ERROR - machine has %d processors.\n",
3414 hardware_processors);
3415 Print(4095, "ERROR - max threads can not exceed this limit.\n");
3416 smp_max_threads = hardware_processors;
3417 }
3418 if (smp_max_threads > CPUS) {
3419 Print(4095, "ERROR - Crafty was compiled with CPUS=%d.", CPUS);
3420 Print(4095, " mt can not exceed this value.\n");
3421 smp_max_threads = CPUS;
3422 }
3423 if (smp_max_threads == 1) {
3424 Print(4095, "ERROR - max threads can be set to zero (0) to");
3425 Print(4095, " disable parallel search, otherwise it must be > 1.\n");
3426 smp_max_threads = 0;
3427 }
3428 if (smp_max_threads)
3429 Print(32, "max threads set to %d.\n", smp_max_threads);
3430 else
3431 Print(32, "parallel threads disabled.\n");
3432 for (proc = 1; proc < CPUS; proc++)
3433 if (proc >= smp_max_threads)
3434 thread[proc].terminate = 1;
3435 } else if (OptionMatch("smpnice", *args)) {
3436 if (nargs < 2) {
3437 printf("usage: smpnice 0|1\n");
3438 return 1;
3439 }
3440 smp_nice = atoi(args[1]);
3441 if (smp_nice)
3442 Print(32, "SMP terminate extra threads when idle.\n");
3443 else
3444 Print(32, "SMP keep extra threads spinning when idle.\n");
3445 } else if (OptionMatch("smpnuma", *args)) {
3446 if (nargs < 2) {
3447 printf("usage: smpnuma 0|1\n");
3448 return 1;
3449 }
3450 smp_numa = atoi(args[1]);
3451 if (smp_numa)
3452 Print(32, "SMP NUMA mode enabled.\n");
3453 else
3454 Print(32, "SMP NUMA mode disabled.\n");
3455 } else if (OptionMatch("smproot", *args)) {
3456 if (nargs < 2) {
3457 printf("usage: smproot 0|1\n");
3458 return 1;
3459 }
3460 smp_split_at_root = atoi(args[1]);
3461 if (smp_split_at_root)
3462 Print(32, "SMP search split at ply >= 1.\n");
3463 else
3464 Print(32, "SMP search split at ply > 1.\n");
3465 } else if (OptionMatch("smpgsl", *args)) {
3466 if (nargs < 2) {
3467 printf("usage: smpgsl <n>\n");
3468 return 1;
3469 }
3470 smp_gratuitous_limit = atoi(args[1]);
3471 Print(32, "maximum gratuitous splits allowed %d.\n",
3472 smp_gratuitous_limit);
3473 } else if (OptionMatch("smpgsd", *args)) {
3474 if (nargs < 2) {
3475 printf("usage: smpgsd <nodes>\n");
3476 return 1;
3477 }
3478 smp_gratuitous_depth = atoi(args[1]);
3479 Print(32, "gratuitous split min depth %d.\n", smp_gratuitous_depth);
3480 }
3481 /*
3482 ************************************************************
3483 * *
3484 * "sn" command sets a specific number of nodes to search *
3485 * before stopping. Note: this requires -DNODES as an *
3486 * option when building Crafty. *
3487 * *
3488 ************************************************************
3489 */
3490 else if (OptionMatch("sn", *args)) {
3491 if (nargs < 2) {
3492 printf("usage: sn <nodes>\n");
3493 return 1;
3494 }
3495 search_nodes = atoi(args[1]);
3496 Print(32, "search nodes set to %" PRIu64 ".\n", search_nodes);
3497 ponder = 0;
3498 }
3499 /*
3500 ************************************************************
3501 * *
3502 * "speech" command turns speech on/off. *
3503 * *
3504 ************************************************************
3505 */
3506 else if (OptionMatch("speech", *args)) {
3507 if (nargs < 2) {
3508 printf("usage: speech on|off\n");
3509 return 1;
3510 }
3511 if (!strcmp(args[1], "on"))
3512 speech = 1;
3513 else if (!strcmp(args[1], "off"))
3514 speech = 0;
3515 if (speech)
3516 Print(4095, "Audio output enabled\n");
3517 else
3518 Print(4095, "Audio output disabled\n");
3519 }
3520 /*
3521 ************************************************************
3522 * *
3523 * "st" command sets a specific search time to control the *
3524 * tree search time. *
3525 * *
3526 ************************************************************
3527 */
3528 else if (OptionMatch("st", *args)) {
3529 if (nargs < 2) {
3530 printf("usage: st <time>\n");
3531 return 1;
3532 }
3533 search_time_limit = atof(args[1]) * 100;
3534 Print(32, "search time set to %.2f.\n",
3535 (float) search_time_limit / 100.0);
3536 }
3537 /*
3538 ************************************************************
3539 * *
3540 * "swindle" command turns swindle mode off/on. *
3541 * *
3542 ************************************************************
3543 */
3544 else if (OptionMatch("swindle", *args)) {
3545 if (!strcmp(args[1], "on"))
3546 swindle_mode = 1;
3547 else if (!strcmp(args[1], "off"))
3548 swindle_mode = 0;
3549 else
3550 printf("usage: swindle on|off\n");
3551 }
3552 /*
3553 ************************************************************
3554 * *
3555 * "tags" command lists the current PGN header tags. *
3556 * *
3557 ************************************************************
3558 */
3559 else if (OptionMatch("tags", *args)) {
3560 struct tm *timestruct;
3561 uint64_t secs;
3562
3563 secs = time(0);
3564 timestruct = localtime((time_t *) & secs);
3565 printf("[Event \"%s\"]\n", pgn_event);
3566 printf("[Site \"%s\"]\n", pgn_site);
3567 printf("[Date \"%4d.%02d.%02d\"]\n", timestruct->tm_year + 1900,
3568 timestruct->tm_mon + 1, timestruct->tm_mday);
3569 printf("[Round \"%s\"]\n", pgn_round);
3570 printf("[White \"%s\"]\n", pgn_white);
3571 printf("[WhiteElo \"%s\"]\n", pgn_white_elo);
3572 printf("[Black \"%s\"]\n", pgn_black);
3573 printf("[BlackElo \"%s\"]\n", pgn_black_elo);
3574 printf("[Result \"%s\"]\n", pgn_result);
3575 }
3576 /*
3577 ************************************************************
3578 * *
3579 * "test" command runs a test suite of problems and *
3580 * displays results. *
3581 * *
3582 ************************************************************
3583 */
3584 else if (OptionMatch("test", *args)) {
3585 FILE *unsolved = NULL;
3586 int save_noise, save_display;
3587
3588 if (thinking || pondering)
3589 return 2;
3590 nargs = ReadParse(buffer, args, " \t;=");
3591 if (nargs < 2) {
3592 printf("usage: test <filename> [exitcnt]\n");
3593 return 1;
3594 }
3595 save_noise = noise_level;
3596 save_display = display_options;
3597 if (nargs > 2)
3598 early_exit = atoi(args[2]);
3599 if (nargs > 3)
3600 unsolved = fopen(args[3], "w+");
3601 Test(args[1], unsolved, 0, 0);
3602 noise_level = save_noise;
3603 display_options = save_display;
3604 ponder_move = 0;
3605 last_pv.pathd = 0;
3606 last_pv.pathl = 0;
3607 if (unsolved)
3608 fclose(unsolved);
3609 }
3610 /*
3611 ************************************************************
3612 * *
3613 * "time" is used to set the basic search timing controls. *
3614 * The general form of the command is as follows: *
3615 * *
3616 * time nmoves/ntime/[nmoves/ntime]/[increment] *
3617 * *
3618 * nmoves/ntime represents a traditional first time *
3619 * control when nmoves is an integer representing the *
3620 * number of moves and ntime is the total time allowed for *
3621 * these moves. The [optional] nmoves/ntime is a *
3622 * traditional secondary time control. Increment is a *
3623 * feature related to ics play and emulates the fischer *
3624 * clock where "increment" is added to the time left after *
3625 * each move is made. *
3626 * *
3627 * As an alternative, nmoves can be "sd" which represents *
3628 * a "sudden death" time control of the remainder of the *
3629 * game played in ntime. The optional secondary time *
3630 * control can be a sudden-death time control, as in the *
3631 * following example: *
3632 * *
3633 * time 60/30/sd/30 *
3634 * *
3635 * This sets 60 moves in 30 minutes, then game in 30 *
3636 * additional minutes. An increment can be added if *
3637 * desired. *
3638 * *
3639 ************************************************************
3640 */
3641 else if (OptionMatch("time", *args)) {
3642 if (xboard) {
3643 tc_time_remaining[Flip(game_wtm)] = atoi(args[1]);
3644 if (log_file && time_limit > 99)
3645 fprintf(log_file, "time remaining: %s (Crafty).\n",
3646 DisplayTime(tc_time_remaining[Flip(game_wtm)]));
3647 } else {
3648 if (thinking || pondering)
3649 return 2;
3650 tc_moves = 60;
3651 tc_time = 180000;
3652 tc_moves_remaining[white] = 60;
3653 tc_moves_remaining[black] = 60;
3654 tc_time_remaining[white] = 180000;
3655 tc_time_remaining[black] = 180000;
3656 tc_secondary_moves = 60;
3657 tc_secondary_time = 180000;
3658 tc_increment = 0;
3659 tc_sudden_death = 0;
3660 /*
3661 first let's pick off the basic time control (moves/minutes)
3662 */
3663 if (nargs > 1)
3664 if (!strcmp(args[1], "sd")) {
3665 tc_sudden_death = 1;
3666 tc_moves = 1000;
3667 }
3668 if (nargs > 2) {
3669 tc_moves = atoi(args[1]);
3670 tc_time = atoi(args[2]) * 100;
3671 }
3672 /*
3673 now let's pick off the secondary time control (moves/minutes)
3674 */
3675 tc_secondary_time = tc_time;
3676 tc_secondary_moves = tc_moves;
3677 if (nargs > 4) {
3678 if (!strcmp(args[3], "sd")) {
3679 tc_sudden_death = 2;
3680 tc_secondary_moves = 1000;
3681 } else
3682 tc_secondary_moves = atoi(args[3]);
3683 tc_secondary_time = atoi(args[4]) * 100;
3684 }
3685 if (nargs > 5)
3686 tc_increment = atoi(args[5]) * 100;
3687 tc_time_remaining[white] = tc_time;
3688 tc_time_remaining[black] = tc_time;
3689 tc_moves_remaining[white] = tc_moves;
3690 tc_moves_remaining[black] = tc_moves;
3691 if (!tc_sudden_death) {
3692 Print(32, "%d moves/%d minutes primary time control\n", tc_moves,
3693 tc_time / 100);
3694 Print(32, "%d moves/%d minutes secondary time control\n",
3695 tc_secondary_moves, tc_secondary_time / 100);
3696 if (tc_increment)
3697 Print(32, "increment %d seconds.\n", tc_increment / 100);
3698 } else if (tc_sudden_death == 1) {
3699 Print(32, " game/%d minutes primary time control\n", tc_time / 100);
3700 if (tc_increment)
3701 Print(32, "increment %d seconds.\n", tc_increment / 100);
3702 } else if (tc_sudden_death == 2) {
3703 Print(32, "%d moves/%d minutes primary time control\n", tc_moves,
3704 tc_time / 100);
3705 Print(32, "game/%d minutes secondary time control\n",
3706 tc_secondary_time / 100);
3707 if (tc_increment)
3708 Print(32, "increment %d seconds.\n", tc_increment / 100);
3709 }
3710 tc_time *= 60;
3711 tc_time_remaining[white] *= 60;
3712 tc_time_remaining[black] *= 60;
3713 tc_secondary_time *= 60;
3714 tc_safety_margin = tc_time / 6;
3715 }
3716 }
3717 /*
3718 ************************************************************
3719 * *
3720 * "timebook" command is used to adjust Crafty's time *
3721 * usage after it leaves the opening book. The first *
3722 * value specifies the multiplier for the time added to *
3723 * the first move out of book expressed as a percentage *
3724 * (100 is 100% for example). The second value specifies *
3725 * the "span" (number of moves) that this multiplier *
3726 * decays over. For example, "timebook 100 10" says to *
3727 * add 100% of the normal search time for the first move *
3728 * out of book, then 90% for the next, until after 10 *
3729 * non-book moves have been played, the percentage has *
3730 * dropped back to 0 where it will stay for the rest of *
3731 * the game. *
3732 * *
3733 ************************************************************
3734 */
3735 else if (OptionMatch("timebook", *args)) {
3736 if (nargs < 3) {
3737 printf("usage: timebook <percentage> <move span>\n");
3738 return 1;
3739 }
3740 first_nonbook_factor = atoi(args[1]);
3741 first_nonbook_span = atoi(args[2]);
3742 if (first_nonbook_factor < 0 || first_nonbook_factor > 500) {
3743 Print(4095, "ERROR, factor must be >= 0 and <= 500\n");
3744 first_nonbook_factor = 0;
3745 }
3746 if (first_nonbook_span < 0 || first_nonbook_span > 30) {
3747 Print(4095, "ERROR, span must be >= 0 and <= 30\n");
3748 first_nonbook_span = 0;
3749 }
3750 }
3751 /*
3752 ************************************************************
3753 * *
3754 * "trace" command sets the search trace level which will *
3755 * dump the tree as it is searched. *
3756 * *
3757 ************************************************************
3758 */
3759 else if (OptionMatch("trace", *args)) {
3760 #if !defined(TRACE)
3761 printf
3762 ("Sorry, but I can't display traces unless compiled with -DTRACE\n");
3763 #endif
3764 if (nargs < 2) {
3765 printf("usage: trace <depth>\n");
3766 return 1;
3767 }
3768 trace_level = atoi(args[1]);
3769 printf("trace=%d\n", trace_level);
3770 }
3771 /*
3772 ************************************************************
3773 * *
3774 * "undo" command backs up 1/2 move, which leaves the *
3775 * opposite side on move. [xboard compatibility] *
3776 * *
3777 ************************************************************
3778 */
3779 else if (OptionMatch("undo", *args)) {
3780 if (thinking || pondering)
3781 return 2;
3782 if (!game_wtm || move_number != 1) {
3783 game_wtm = Flip(game_wtm);
3784 if (Flip(game_wtm))
3785 move_number--;
3786 sprintf(buffer, "reset %d", move_number);
3787 Option(tree);
3788 }
3789 }
3790 /*
3791 ************************************************************
3792 * *
3793 * "usage" command controls the time usage multiplier *
3794 * factors used in the game - percentage increase or *
3795 * decrease in time used up front. Enter a number between *
3796 * 1 to 100 for the % decrease (negative value) or to *
3797 * increase (positive value) although other time *
3798 * limitation controls may kick in. This more commonly *
3799 * used in the .craftyrc/crafty.rc file. *
3800 * *
3801 ************************************************************
3802 */
3803 else if (OptionMatch("usage", *args)) {
3804 if (nargs < 2) {
3805 printf("usage: usage <percentage>\n");
3806 return 1;
3807 }
3808 usage_level = atoi(args[1]);
3809 if (usage_level > 50)
3810 usage_level = 50;
3811 else if (usage_level < -50)
3812 usage_level = -50;
3813 Print(32, "time usage up front set to %d percent increase/(-)decrease.\n",
3814 usage_level);
3815 }
3816 /*
3817 ************************************************************
3818 * *
3819 * "variant" command sets the wild variant being played *
3820 * on a chess server. [xboard compatibility]. *
3821 * *
3822 ************************************************************
3823 */
3824 else if (OptionMatch("variant", *args)) {
3825 if (thinking || pondering)
3826 return 2;
3827 printf("command=[%s]\n", buffer);
3828 return -1;
3829 }
3830 /*
3831 ************************************************************
3832 * *
3833 * "whisper" command sets whisper mode for ICS. =1 will *
3834 * whisper mate announcements, =2 will whisper scores and *
3835 * other info, =3 will whisper scores and PV, =4 adds the *
3836 * list of book moves, =5 displays the PV after each *
3837 * iteration completes, and =6 displays the PV each time *
3838 * it changes in an iteration. *
3839 * *
3840 ************************************************************
3841 */
3842 else if (OptionMatch("whisper", *args)) {
3843 if (nargs < 2) {
3844 printf("usage: whisper <level>\n");
3845 return 1;
3846 }
3847 kibitz = 16 + Min(0, atoi(args[1]));
3848 }
3849 /*
3850 ************************************************************
3851 * *
3852 * "white" command sets white to move (wtm). *
3853 * *
3854 ************************************************************
3855 */
3856 else if (OptionMatch("white", *args)) {
3857 if (thinking || pondering)
3858 return 2;
3859 ponder_move = 0;
3860 last_pv.pathd = 0;
3861 last_pv.pathl = 0;
3862 if (!game_wtm)
3863 Pass();
3864 force = 0;
3865 }
3866 /*
3867 ************************************************************
3868 * *
3869 * "wild" command sets up an ICS wild position (only 7 at *
3870 * present, but any can be added easily, except for those *
3871 * that Crafty simply can't play (two kings, invisible *
3872 * pieces, etc.) *
3873 * *
3874 ************************************************************
3875 */
3876 else if (OptionMatch("wild", *args)) {
3877 int i;
3878
3879 if (nargs < 2) {
3880 printf("usage: wild <value>\n");
3881 return 1;
3882 }
3883 i = atoi(args[1]);
3884 switch (i) {
3885 case 7:
3886 strcpy(buffer, "setboard 4k/5ppp/////PPP/3K/ w");
3887 Option(tree);
3888 break;
3889 default:
3890 printf("sorry, only wild7 implemented at present\n");
3891 break;
3892 }
3893 }
3894 /*
3895 ************************************************************
3896 * *
3897 * "xboard" command is normally invoked from main() via *
3898 * the xboard command-line option. It sets proper *
3899 * defaults for Xboard interface requirements. *
3900 * *
3901 ************************************************************
3902 */
3903 else if (OptionMatch("xboard", *args) || OptionMatch("winboard", *args)) {
3904 if (!xboard) {
3905 signal(SIGINT, SIG_IGN);
3906 xboard = 1;
3907 display_options = 2048;
3908 Print(-1, "\n");
3909 Print(-1, "tellicsnoalias set 1 Crafty v%s (%d cpus)\n", version, Max(1,
3910 smp_max_threads));
3911 Print(-1, "tellicsnoalias kibitz Hello from Crafty v%s! (%d cpus)\n",
3912 version, Max(1, smp_max_threads));
3913 }
3914 }
3915 /*
3916 ************************************************************
3917 * *
3918 * "?" command does nothing, but since this is the "move *
3919 * now" keystroke, if Crafty is not searching, this will *
3920 * simply "wave it off" rather than produce an error. *
3921 * *
3922 ************************************************************
3923 */
3924 else if (OptionMatch("?", *args)) {
3925 }
3926 /*
3927 ************************************************************
3928 * *
3929 * unknown command, it must be a move. *
3930 * *
3931 ************************************************************
3932 */
3933 else
3934 return 0;
3935 /*
3936 ************************************************************
3937 * *
3938 * command executed, return for another. *
3939 * *
3940 ************************************************************
3941 */
3942 return 1;
3943 }
3944
3945 /*
3946 *******************************************************************************
3947 * *
3948 * OptionMatch() is used to recognize user commands. It requires that the *
3949 * command (text input which is the *2nd parameter* conform to the following *
3950 * simple rules: *
3951 * *
3952 * 1. The input must match the command, starting at the left-most *
3953 * character. *
3954 * 2. If the command starts with a sequence of characters that could *
3955 * be interpreted as a chess move as well (re for reset and/or rook *
3956 * to the e-file) then the input must match enough of the command *
3957 * to get past the ambiguity (res would be minimum we will accept *
3958 * for the reset command.) *
3959 * *
3960 *******************************************************************************
3961 */
OptionMatch(char * command,char * input)3962 int OptionMatch(char *command, char *input) {
3963 /*
3964 ************************************************************
3965 * *
3966 * check for the obvious exact match first. *
3967 * *
3968 ************************************************************
3969 */
3970 if (!strcmp(command, input))
3971 return 1;
3972 /*
3973 ************************************************************
3974 * *
3975 * now use strstr() to see if "input" is in "command." the *
3976 * first requirement is that input matches command *
3977 * starting at the very left-most character. *
3978 * *
3979 ************************************************************
3980 */
3981 if (strstr(command, input) == command)
3982 return 1;
3983 return 0;
3984 }
3985
OptionPerft(TREE * RESTRICT tree,int ply,int depth,int wtm)3986 void OptionPerft(TREE * RESTRICT tree, int ply, int depth, int wtm) {
3987 unsigned *mv;
3988 #if defined(TRACE)
3989 static char line[256];
3990 static char move[16], *p[64];
3991 #endif
3992
3993 tree->last[ply] = GenerateCaptures(tree, ply, wtm, tree->last[ply - 1]);
3994 for (mv = tree->last[ply - 1]; mv < tree->last[ply]; mv++)
3995 if (Captured(*mv) == king)
3996 return;
3997 tree->last[ply] = GenerateNoncaptures(tree, ply, wtm, tree->last[ply]);
3998 #if defined(TRACE)
3999 p[1] = line;
4000 #endif
4001 for (mv = tree->last[ply - 1]; mv < tree->last[ply]; mv++) {
4002 #if defined(TRACE)
4003 strcpy(move, OutputMove(tree, ply, wtm, *mv));
4004 #endif
4005 MakeMove(tree, ply, wtm, *mv);
4006 #if defined(TRACE)
4007 if (ply <= trace_level) {
4008 strcpy(p[ply], move);
4009 strcpy(line + strlen(line), " ");
4010 p[ply + 1] = line + strlen(line);
4011 if (ply == trace_level)
4012 printf("%s\n", line);
4013 }
4014 #endif
4015 if (depth - 1)
4016 OptionPerft(tree, ply + 1, depth - 1, Flip(wtm));
4017 else if (!Check(wtm))
4018 total_moves++;
4019 UnmakeMove(tree, ply, wtm, *mv);
4020 }
4021 }
4022