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