1 /* DreamChess
2 **
3 ** DreamChess is the legal property of its developers, whose names are too
4 ** numerous to list here. Please refer to the AUTHORS.txt file distributed
5 ** with this source distribution.
6 **
7 ** This program is free software: you can redistribute it and/or modify
8 ** it under the terms of the GNU General Public License as published by
9 ** the Free Software Foundation, either version 3 of the License, or
10 ** (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include "commands.h"
27 #include "dreamer.h"
28 #include "e_comm.h"
29 #include "git_rev.h"
30 #include "history.h"
31 #include "move.h"
32 #include "repetition.h"
33 #include "san.h"
34 #include "search.h"
35 #include "timer.h"
36 #include "transposition.h"
37
is_coord_move(char * ms)38 static int is_coord_move(char *ms) {
39 int len = strlen(ms);
40 /* Check format. */
41 if ((len < 4) || (len > 5))
42 return 0;
43 if ((ms[0] < 'a') || (ms[0] > 'h') || (ms[1] < '1') || (ms[1] > '8') || (ms[2] < 'a') || (ms[2] > 'h') ||
44 (ms[3] < '1') || (ms[3] > '8'))
45 return 0;
46
47 if (len == 5)
48 switch (ms[4]) {
49 case 'q':
50 case 'r':
51 case 'n':
52 case 'b':
53 return 1;
54 default:
55 return 0;
56 }
57
58 return 1;
59 }
60
convert_piece(int san_piece)61 static int convert_piece(int san_piece) {
62 switch (san_piece) {
63 case SAN_KING:
64 return KING;
65 case SAN_QUEEN:
66 return QUEEN;
67 case SAN_ROOK:
68 return ROOK;
69 case SAN_KNIGHT:
70 return KNIGHT;
71 case SAN_BISHOP:
72 return BISHOP;
73 case SAN_PAWN:
74 return PAWN;
75 }
76
77 /* We should never get here */
78 assert(0);
79 }
80
san_piece(int piece)81 static int san_piece(int piece) {
82 switch (piece) {
83 case KING:
84 return SAN_KING;
85 case QUEEN:
86 return SAN_QUEEN;
87 case ROOK:
88 return SAN_ROOK;
89 case KNIGHT:
90 return SAN_KNIGHT;
91 case BISHOP:
92 return SAN_BISHOP;
93 case PAWN:
94 return SAN_PAWN;
95 }
96
97 /* We should never get here */
98 assert(0);
99 }
100
get_san_move(board_t * board,int ply,san_move_t * san)101 static move_t get_san_move(board_t *board, int ply, san_move_t *san) {
102 move_t move;
103 int piece;
104 bitboard_t en_passant = board->en_passant;
105 int castle_flags = board->castle_flags;
106 int fifty_moves = board->fifty_moves;
107 int found = 0;
108 move_t found_move;
109
110 if (san->type == SAN_QUEENSIDE_CASTLE) {
111 san->source_file = 4;
112 san->source_rank = (board->current_player == SIDE_WHITE ? 0 : 7);
113 san->destination = (board->current_player == SIDE_WHITE ? 2 : 58);
114 piece = KING + board->current_player;
115 } else if (san->type == SAN_KINGSIDE_CASTLE) {
116 san->source_file = 4;
117 san->source_rank = (board->current_player == SIDE_WHITE ? 0 : 7);
118 san->destination = (board->current_player == SIDE_WHITE ? 6 : 62);
119 piece = KING + board->current_player;
120 } else
121 piece = convert_piece(san->piece) + board->current_player;
122
123 compute_legal_moves(board, ply);
124
125 /* Look for move in list. */
126 while ((move = move_next(board, ply)) != NO_MOVE) {
127 int move_piece;
128
129 if (MOVE_GET(move, DEST) != san->destination)
130 continue;
131
132 if (board->current_player == SIDE_WHITE)
133 move_piece = find_white_piece(board, MOVE_GET(move, SOURCE));
134 else
135 move_piece = find_black_piece(board, MOVE_GET(move, SOURCE));
136
137 if (move_piece != piece)
138 continue;
139
140 if (san->source_file != SAN_NOT_SPECIFIED)
141 if (san->source_file != MOVE_GET(move, SOURCE) % 8)
142 continue;
143
144 if (san->source_rank != SAN_NOT_SPECIFIED)
145 if (san->source_rank != MOVE_GET(move, SOURCE) / 8)
146 continue;
147
148 if (san->type == SAN_CAPTURE) {
149 /* TODO verify en passant capture? */
150 if (!(move & (CAPTURE_MOVE_EN_PASSANT | CAPTURE_MOVE)))
151 continue;
152 }
153
154 if ((move & PROMOTION_MOVE_QUEEN) && (san->promotion_piece != SAN_QUEEN))
155 continue;
156 if ((move & PROMOTION_MOVE_ROOK) && (san->promotion_piece != SAN_ROOK))
157 continue;
158 if ((move & PROMOTION_MOVE_BISHOP) && (san->promotion_piece != SAN_BISHOP))
159 continue;
160 if ((move & PROMOTION_MOVE_KNIGHT) && (san->promotion_piece != SAN_KNIGHT))
161 continue;
162 if (!(move & MOVE_PROMOTION_MASK) && (san->promotion_piece != SAN_NOT_SPECIFIED))
163 continue;
164
165 /* TODO verify check and checkmate flags? */
166
167 execute_move(board, move);
168 board->current_player = OPPONENT(board->current_player);
169 if (!is_check(board, ply + 1)) {
170 found++;
171 found_move = move;
172 }
173
174 board->current_player = OPPONENT(board->current_player);
175 unmake_move(board, move, en_passant, castle_flags, fifty_moves);
176 }
177
178 if (found != 1)
179 return NO_MOVE;
180
181 return found_move;
182 }
183
get_coord_move(board_t * board,int ply,char * ms)184 static move_t get_coord_move(board_t *board, int ply, char *ms) {
185 int source, dest;
186 move_t move;
187
188 source = (ms[0] - 'a') + 8 * (ms[1] - '1');
189 dest = (ms[2] - 'a') + 8 * (ms[3] - '1');
190
191 compute_legal_moves(board, ply);
192
193 /* Look for move in list. */
194 while ((move = move_next(board, ply)) != NO_MOVE) {
195 if ((MOVE_GET(move, SOURCE) == source) && (MOVE_GET(move, DEST) == dest)) {
196 bitboard_t en_passant = board->en_passant;
197 int castle_flags = board->castle_flags;
198 int fifty_moves = board->fifty_moves;
199
200 /* Move found. */
201 execute_move(board, move);
202 board->current_player = OPPONENT(board->current_player);
203 if (!is_check(board, ply + 1)) {
204 board->current_player = OPPONENT(board->current_player);
205 unmake_move(board, move, en_passant, castle_flags, fifty_moves);
206 break;
207 }
208 board->current_player = OPPONENT(board->current_player);
209 unmake_move(board, move, en_passant, castle_flags, fifty_moves);
210 }
211 }
212 if (move != NO_MOVE) {
213 if (move & MOVE_PROMOTION_MASK) {
214 /* Set correct promotion piece. */
215 move &= ~MOVE_PROMOTION_MASK;
216 if (strlen(ms) == 5)
217 switch (ms[4]) {
218 case 'q':
219 move |= PROMOTION_MOVE_QUEEN;
220 break;
221 case 'r':
222 move |= PROMOTION_MOVE_ROOK;
223 break;
224 case 'n':
225 move |= PROMOTION_MOVE_KNIGHT;
226 break;
227 case 'b':
228 move |= PROMOTION_MOVE_BISHOP;
229 }
230 else {
231 /* No promotion piece specified. */
232 return NO_MOVE;
233 }
234 return move;
235 }
236 if (strlen(ms) == 4)
237 return move;
238 }
239 return NO_MOVE;
240 }
241
coord_move_str(move_t move)242 char *coord_move_str(move_t move) {
243 char *ret = malloc(6);
244 ret[5] = '\0';
245 ret[0] = 'a' + MOVE_GET(move, SOURCE) % 8;
246 ret[1] = '1' + MOVE_GET(move, SOURCE) / 8;
247 ret[2] = 'a' + MOVE_GET(move, DEST) % 8;
248 ret[3] = '1' + MOVE_GET(move, DEST) / 8;
249 switch (move & MOVE_PROMOTION_MASK) {
250 case PROMOTION_MOVE_QUEEN:
251 ret[4] = 'q';
252 break;
253 case PROMOTION_MOVE_BISHOP:
254 ret[4] = 'b';
255 break;
256 case PROMOTION_MOVE_KNIGHT:
257 ret[4] = 'n';
258 break;
259 case PROMOTION_MOVE_ROOK:
260 ret[4] = 'r';
261 break;
262 default:
263 ret[4] = '\0';
264 }
265 return ret;
266 }
267
san_move_str(board_t * board,int ply,move_t move)268 char *san_move_str(board_t *board, int ply, move_t move) {
269 san_move_t san_move;
270 int state;
271 int move_piece;
272 bitboard_t en_passant = board->en_passant;
273 int castle_flags = board->castle_flags;
274 int fifty_moves = board->fifty_moves;
275
276 execute_move(board, move);
277 state = check_game_state(board, ply);
278 unmake_move(board, move, en_passant, castle_flags, fifty_moves);
279
280 switch (state) {
281 case STATE_CHECK:
282 san_move.state = SAN_STATE_CHECK;
283 break;
284 case STATE_MATE:
285 san_move.state = SAN_STATE_CHECKMATE;
286 break;
287 default:
288 san_move.state = SAN_STATE_NORMAL;
289 }
290
291 switch (MOVE_GET(move, TYPE)) {
292 case CASTLING_MOVE_QUEENSIDE:
293 san_move.type = SAN_QUEENSIDE_CASTLE;
294 return san_string(&san_move);
295 case CASTLING_MOVE_KINGSIDE:
296 san_move.type = SAN_KINGSIDE_CASTLE;
297 return san_string(&san_move);
298 case CAPTURE_MOVE:
299 case CAPTURE_MOVE_EN_PASSANT:
300 san_move.type = SAN_CAPTURE;
301 break;
302 default:
303 san_move.type = SAN_NORMAL;
304 }
305
306 if (board->current_player == SIDE_WHITE)
307 move_piece = find_white_piece(board, MOVE_GET(move, SOURCE)) & PIECE_MASK;
308 else
309 move_piece = find_black_piece(board, MOVE_GET(move, SOURCE)) & PIECE_MASK;
310
311 san_move.piece = san_piece(move_piece);
312
313 if (MOVE_GET(move, TYPE) & MOVE_PROMOTION_MASK)
314 san_move.promotion_piece = san_piece(MOVE_GET(move, CAPTURED) & PIECE_MASK);
315 else
316 san_move.promotion_piece = SAN_NOT_SPECIFIED;
317
318 san_move.source_file = SAN_NOT_SPECIFIED;
319 san_move.source_rank = SAN_NOT_SPECIFIED;
320 san_move.destination = MOVE_GET(move, DEST);
321
322 if (san_move.piece == SAN_PAWN) {
323 if (MOVE_GET(move, SOURCE) % 8 != MOVE_GET(move, DEST) % 8)
324 san_move.source_file = MOVE_GET(move, SOURCE) % 8;
325 } else {
326 move_t u_move;
327 u_move = get_san_move(board, ply, &san_move);
328
329 if (u_move == NO_MOVE) {
330 san_move.source_file = MOVE_GET(move, SOURCE) % 8;
331 u_move = get_san_move(board, ply, &san_move);
332 if (u_move == NO_MOVE) {
333 san_move.source_file = SAN_NOT_SPECIFIED;
334 san_move.source_rank = MOVE_GET(move, SOURCE) / 8;
335 u_move = get_san_move(board, ply, &san_move);
336 if (u_move == NO_MOVE) {
337 san_move.source_file = MOVE_GET(move, SOURCE) % 8;
338 u_move = get_san_move(board, ply, &san_move);
339 if (!u_move) {
340 char *move_s = coord_move_str(move);
341
342 e_comm_send("failed to convert move %s to SAN notation", move_s);
343
344 free(move_s);
345 return NULL;
346 }
347 }
348 }
349 }
350 }
351
352 return san_string(&san_move);
353 }
354
error(char * type,char * command)355 static void error(char *type, char *command) {
356 e_comm_send("Error (%s): %s\n", type, command);
357 }
358
359 #define NOT_NOW(c) error("command not legal now", c)
360 #define UNKNOWN(c) error("unknown command", c)
361 #define BADPARAM(c) error("invalid or missing parameter(s)", c)
362
parse_time_control(state_t * state,char * s)363 static int parse_time_control(state_t *state, char *s) {
364 struct time_control t;
365 char *end;
366 char *semi;
367
368 errno = 0;
369 t.mps = strtol(s, &end, 10);
370
371 if (errno || *end != ' ')
372 return 1;
373
374 semi = strchr(end + 1, ':');
375
376 if (semi) {
377 *semi = ' ';
378 t.base = strtol(end + 1, &end, 10) * 60 * 100;
379 *semi = ':';
380
381 if (errno || end != semi)
382 return 1;
383
384 t.base += strtol(end + 1, &end, 10) * 100;
385
386 if (errno || *end != ' ')
387 return 1;
388 } else {
389 t.base = strtol(end + 1, &end, 10) * 60 * 100;
390
391 if (errno || *end != ' ')
392 return 1;
393 }
394
395 t.inc = strtol(end + 1, &end, 10) * 100;
396
397 if (errno || *end != 0)
398 return 1;
399
400 /* Time control with both mps and inc is invalid */
401 if (t.mps != 0 && t.inc != 0)
402 return 1;
403
404 state->time = t;
405 timer_set(&state->engine_time, t.base);
406 return 0;
407 }
408
command_always(state_t * state,char * command)409 static int command_always(state_t *state, char *command) {
410 if (!strcmp(command, "post")) {
411 set_option(OPTION_POST, 1);
412 return 1;
413 }
414
415 if (!strcmp(command, "nopost")) {
416 set_option(OPTION_POST, 0);
417 return 1;
418 }
419
420 if (!strncmp(command, "time ", 5)) {
421 int time;
422 char *end;
423 errno = 0;
424 time = strtol(command + 5, &end, 10);
425 if (errno || *end != 0)
426 BADPARAM(command);
427 else
428 timer_set(&state->engine_time, time);
429 return 1;
430 }
431
432 if (!strncmp(command, "otim ", 5))
433 return 1;
434
435 return 0;
436 }
437
parse_move(board_t * board,int ply,char * command,move_t * move)438 int parse_move(board_t *board, int ply, char *command, move_t *move) {
439 san_move_t *san;
440
441 san = san_parse(command);
442
443 if (san) {
444 *move = get_san_move(board, ply, san);
445 free(san);
446 return 0;
447 } else if (is_coord_move(command)) {
448 *move = get_coord_move(board, ply, command);
449 return 0;
450 }
451
452 return 1;
453 }
454
command_usermove(state_t * state,char * command)455 int command_usermove(state_t *state, char *command) {
456 move_t move;
457
458 if (!parse_move(&state->board, 0, command, &move)) {
459 if (move == NO_MOVE) {
460 e_comm_send("Illegal move: %s\n", command);
461 return 0;
462 }
463
464 if (my_turn(state)) {
465 NOT_NOW(command);
466 return 0;
467 }
468
469 if (state->mode == MODE_WHITE || state->mode == MODE_BLACK)
470 timer_start(&state->engine_time);
471
472 do_move(state, move);
473 check_game_end(state);
474
475 if (state->mode == MODE_IDLE)
476 state->mode = (state->board.current_player == SIDE_WHITE ? MODE_WHITE : MODE_BLACK);
477
478 if (state->ponder_my_move != NO_MOVE) {
479 /* We already have a possible answer to this move from pondering. */
480 if (move == state->ponder_opp_move && MOVE_IS_REGULAR(state->ponder_my_move)) {
481 /* User made the expected move. */
482 send_move(state, state->ponder_my_move);
483 }
484
485 state->ponder_my_move = NO_MOVE;
486 }
487
488 return 0;
489 }
490
491 return 1;
492 }
493
command_handle(state_t * state,char * command)494 void command_handle(state_t *state, char *command) {
495 if (command_always(state, command))
496 return;
497
498 if (!strcmp(command, "xboard")) {
499 /* xboard mode is default. */
500 return;
501 }
502
503 if (!strncmp(command, "protover ", 9)) {
504 char *endptr;
505 errno = 0;
506 strtol(command + 9, &endptr, 10);
507
508 if (errno || (*endptr != 0))
509 BADPARAM(command);
510
511 e_comm_send("feature myname=\"Dreamer %s\"\n", g_version);
512 e_comm_send("feature setboard=1\n");
513 e_comm_send("feature colors=0\n");
514 e_comm_send("feature done=1\n");
515 return;
516 }
517
518 if (!strncmp(command, "level ", 6)) {
519 if (parse_time_control(state, command + 6))
520 BADPARAM(command);
521
522 return;
523 }
524
525 if (!strncmp(command, "accepted ", 9)) {
526 if (!strcmp(command + 9, "setboard") || !strcmp(command + 9, "done") || !strcmp(command + 9, "myname") ||
527 !strcmp(command + 9, "colors"))
528 return;
529
530 BADPARAM(command);
531 return;
532 }
533
534 if (!strcmp(command, "new")) {
535 setup_board(&state->board);
536 forget_history();
537 clear_table();
538 pv_clear();
539 repetition_init(&state->board);
540 state->done = 0;
541 state->mode = MODE_BLACK;
542 state->flags = 0;
543 state->depth = MAX_DEPTH;
544
545 if (state->undo_data != NULL)
546 free(state->undo_data);
547 state->undo_data = NULL;
548
549 state->moves = 0;
550 timer_init(&state->engine_time, 1);
551 timer_set(&state->engine_time, state->time.base * 60 * 100);
552 timer_init(&state->move_time, 1);
553
554 state->hint = NO_MOVE;
555 state->ponder_opp_move = NO_MOVE;
556 state->ponder_my_move = NO_MOVE;
557 state->ponder_actual_move = NO_MOVE;
558 return;
559 }
560
561 if (!strcmp(command, "quit")) {
562 state->mode = MODE_QUIT;
563 return;
564 }
565
566 if (!strcmp(command, "force")) {
567 state->mode = MODE_FORCE;
568 return;
569 }
570
571 if (!strcmp(command, "white")) {
572 if (state->board.current_player != SIDE_WHITE) {
573 /* FIXME, we should support this, but right now it would cause
574 ** severe problems with the undo system.
575 */
576 NOT_NOW(command);
577 return;
578 }
579 state->mode = MODE_BLACK;
580 return;
581 }
582
583 if (!strcmp(command, "black")) {
584 if (state->board.current_player != SIDE_BLACK) {
585 /* FIXME, see above. */
586 NOT_NOW(command);
587 return;
588 }
589 state->mode = MODE_WHITE;
590 return;
591 }
592
593 if (!strcmp(command, "playother")) {
594 if (state->mode != MODE_FORCE) {
595 NOT_NOW(command);
596 return;
597 }
598
599 if (state->board.current_player == SIDE_WHITE)
600 state->mode = MODE_BLACK;
601 else
602 state->mode = MODE_WHITE;
603 return;
604 }
605
606 if (!strncmp(command, "sd", 2)) {
607 char *number = strchr(command, ' ');
608 char *end;
609 if (number && (*(number + 1) != '\0')) {
610 long int val = strtol(number + 1, &end, 10);
611 if ((*end == '\0') && (val > 0))
612 state->depth = val;
613 else
614 BADPARAM(command);
615 } else
616 BADPARAM(command);
617 return;
618 }
619
620 if (!strcmp(command, "go")) {
621 if (state->board.current_player == SIDE_WHITE)
622 state->mode = MODE_WHITE;
623 else
624 state->mode = MODE_BLACK;
625 return;
626 }
627
628 if (!strcmp(command, "remove")) {
629 switch (state->mode) {
630 case MODE_WHITE:
631 if (state->board.current_player == SIDE_WHITE) {
632 NOT_NOW(command);
633 return;
634 }
635 break;
636 case MODE_BLACK:
637 if (state->board.current_player == SIDE_BLACK) {
638 NOT_NOW(command);
639 return;
640 }
641 break;
642 case MODE_FORCE:
643 break;
644 default:
645 NOT_NOW(command);
646 return;
647 }
648
649 if (state->moves < 2)
650 NOT_NOW(command);
651
652 undo_move(state);
653 undo_move(state);
654 state->done = 0;
655 return;
656 }
657
658 if (!strcmp(command, "?")) {
659 NOT_NOW(command);
660 return;
661 }
662
663 if (!strncmp(command, "setboard ", 9)) {
664 board_t board;
665
666 if (state->mode != MODE_FORCE) {
667 NOT_NOW(command);
668 return;
669 }
670
671 if (setup_board_fen(&board, command + 9)) {
672 BADPARAM(command);
673 return;
674 }
675
676 state->board = board;
677 forget_history();
678 clear_table();
679 repetition_init(&state->board);
680 state->done = 0;
681 return;
682 }
683
684 if (!strcmp(command, "noquiesce")) {
685 set_option(OPTION_QUIESCE, 0);
686 return;
687 }
688
689 if (!strcmp(command, "easy")) {
690 set_option(OPTION_PONDER, 0);
691 state->ponder_my_move = NO_MOVE;
692 return;
693 }
694
695 if (!strcmp(command, "hard")) {
696 set_option(OPTION_PONDER, 1);
697 if (state->moves != 0)
698 state->flags |= FLAG_PONDER;
699 return;
700 }
701
702 if (!strcmp(command, "hint")) {
703 if (state->hint != NO_MOVE) {
704 char *str = coord_move_str(state->hint);
705 e_comm_send("Hint: %s\n", str);
706 free(str);
707 }
708 return;
709 }
710
711 if (!command_usermove(state, command))
712 return;
713
714 UNKNOWN(command);
715 }
716
command_check_abort(state_t * state,int ply,char * command)717 int command_check_abort(state_t *state, int ply, char *command) {
718 if (!strcmp(command, "?")) {
719 if (my_turn(state))
720 return 1;
721 }
722
723 if (command_always(state, command))
724 return 0;
725
726 if (!strcmp(command, "new")) {
727 state->flags = FLAG_NEW_GAME | FLAG_IGNORE_MOVE;
728 return 1;
729 }
730
731 if (!strcmp(command, "quit") || !strcmp(command, "force")) {
732 state->flags = FLAG_IGNORE_MOVE;
733 command_handle(state, command);
734 return 1;
735 }
736
737 if (!strcmp(command, "easy")) {
738 if (state->flags & FLAG_PONDER) {
739 state->flags = FLAG_IGNORE_MOVE;
740 state->ponder_actual_move = NO_MOVE;
741 }
742 command_handle(state, command);
743 return 1;
744 }
745
746 if (!strcmp(command, "hint")) {
747 if (state->flags & FLAG_PONDER) {
748 char *str = coord_move_str(state->ponder_opp_move);
749 e_comm_send("Hint: %s\n", str);
750 free(str);
751 return 0;
752 }
753 }
754
755 if (state->flags & FLAG_PONDER) {
756 move_t move;
757
758 if (!parse_move(&state->root_board, ply, command, &move)) {
759 if (move == NO_MOVE) {
760 e_comm_send("Illegal move: %s\n", command);
761 return 0;
762 }
763
764 /* Start our timer */
765 timer_start(&state->engine_time);
766
767 if (move == state->ponder_opp_move) {
768 /* User made the expected move. */
769 state->flags = 0;
770 return 0;
771 } else {
772 /* User made a different move, abort and restart search. */
773 state->flags = FLAG_IGNORE_MOVE;
774 state->ponder_actual_move = move;
775 return 1;
776 }
777 }
778 }
779
780 NOT_NOW(command);
781 return 0;
782 }
783