+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <sys/time.h>
+#include <ctype.h>
+#include <math.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <signal.h>
+
+#include "common.h"
+#include "hash.h"
+#include "evaluator.h"
+
+FILE *ucilog;
+
+unsigned total_nodes, min_depth;
+unsigned move_num;
+
+struct killer_move killer_moves[64][2]; // two for each depth
+unsigned killer_currdepth; // easier to have as a global :-/
+unsigned max_depth, max_selective_depth; // my oh my
+bool abort_search;
+
+int find_best_move(const struct position * const pos, unsigned long long phash, unsigned *ret_from, unsigned *ret_to, unsigned depth, unsigned seldepth, int alpha, int beta, bool search_castling);
+
+void uciprintf(const char *format, ...)
+{
+ va_list ap;
+ char buf[4096];
+
+ va_start(ap, format);
+ vsnprintf(buf, 4096, format, ap);
+ va_end(ap);
+
+ fputs(buf, stdout);
+ fputs("=> ", ucilog);
+ fputs(buf, ucilog);
+
+ fflush(stdout);
+ fflush(ucilog);
+}
+
+int estimate_score(struct position *pos, unsigned depth)
+{
+ unsigned long long phash, bucket;
+ struct hash_entry *he;
+
+ // hack -- looking up the hash table is too expensive just to get
+ // a good estimate, so we don't do it except at a certain level
+ if (depth > 3) {
+ bool found_any = false;
+ unsigned best_depth = 10000;
+ int best_depth_score;
+
+ // see if we have something in the hash -- we don't really care at what
+ // depth, _anything_ is better than the static evaluation, really
+ phash = hash_position(pos);
+ bucket = phash % HASH_BUCKETS;
+ he = transposition_table[bucket];
+
+ while (he != NULL) {
+ if (he->phash == phash)
+ return he->score;
+
+ if (he->phash == phash && he->depth < best_depth) {
+ found_any = true;
+ best_depth = he->depth;
+ best_depth_score = he->score;
+ }
+ he = he->next;
+ }
+
+ if (found_any)
+ return best_depth_score;
+ }
+
+ return (pos->white_to_move) ? evaluate(pos) : -evaluate(pos);
+}
+
+#define ADD_MOVE(newsquare) \
+ do { \
+ assert(IS_WHITE(pos->board[square]) == pos->white_to_move); \
+ moves[num_moves].pos = *pos; \
+ moves[num_moves].from = square; \
+ moves[num_moves].to = newsquare; \
+ if (pos->white_to_move) \
+ real_make_move(&moves[num_moves].pos, square, newsquare, &moves[num_moves].pos.white_pieces[i], moves[num_moves].pos.black_pieces); \
+ else \
+ real_make_move(&moves[num_moves].pos, square, newsquare, &moves[num_moves].pos.black_pieces[i], moves[num_moves].pos.white_pieces); \
+ moves[num_moves].estimate_score = -estimate_score(&moves[num_moves].pos, depth); \
+ indexes[num_moves] = num_moves; \
+ heapify(moves, indexes, num_moves); \
+ ++num_moves; \
+ } while (0)
+
+struct binheap_node {
+ int estimate_score;
+ struct position pos;
+ unsigned char from, to;
+};
+
+bool bh_greaterthan(struct binheap_node *moves, unsigned *indexes, unsigned i1, unsigned i2)
+{
+ bool killer1 = (killer_moves[killer_currdepth][0].from == moves[indexes[i1]].from &&
+ killer_moves[killer_currdepth][0].to == moves[indexes[i1]].to) ||
+ (killer_moves[killer_currdepth][1].from == moves[indexes[i1]].from &&
+ killer_moves[killer_currdepth][1].to == moves[indexes[i1]].to);
+ bool killer2 = (killer_moves[killer_currdepth][0].from == moves[indexes[i2]].from &&
+ killer_moves[killer_currdepth][0].to == moves[indexes[i2]].to) ||
+ (killer_moves[killer_currdepth][1].from == moves[indexes[i2]].from &&
+ killer_moves[killer_currdepth][1].to == moves[indexes[i2]].to);
+
+ if (killer1 && !killer2)
+ return true;
+ if (killer2 && !killer1)
+ return false;
+
+ return (moves[indexes[i1]].estimate_score > moves[indexes[i2]].estimate_score);
+}
+
+void heapify(struct binheap_node *moves, unsigned *indexes, unsigned i)
+{
+ unsigned parent;
+
+ if (i == 0)
+ return;
+
+ parent = (i-1)/2;
+ if (bh_greaterthan(moves, indexes, i, parent)) {
+ unsigned tmp = indexes[i];
+ indexes[i] = indexes[parent];
+ indexes[parent] = tmp;
+
+ heapify(moves, indexes, parent);
+ }
+}
+
+void downheapify(struct binheap_node *moves, unsigned *indexes, unsigned i, unsigned num_elem)
+{
+ unsigned child1 = 2 * i + 1;
+ unsigned child2 = 2 * i + 2;
+ unsigned compare_ind;
+
+ // no children
+ if (child1 >= num_elem)
+ return;
+
+ if (child2 >= num_elem) {
+ // one child
+ compare_ind = child1;
+ } else {
+ // two children -- find the largest one
+ if (bh_greaterthan(moves, indexes, child1, child2)) {
+ compare_ind = child1;
+ } else {
+ compare_ind = child2;
+ }
+ }
+
+ // see if we should swap
+ if (bh_greaterthan(moves, indexes, compare_ind, i)) {
+ unsigned tmp = indexes[i];
+ indexes[i] = indexes[compare_ind];
+ indexes[compare_ind] = tmp;
+
+ downheapify(moves, indexes, compare_ind, num_elem);
+ }
+}
+
+// generate all possible moves, stick them in a binary heap based on estimated score, and then try
+// them in the estimated order
+int find_best_move(const struct position * const pos, unsigned long long phash, unsigned *ret_from, unsigned *ret_to, unsigned depth, unsigned seldepth, int alpha, int beta, bool search_castling)
+{
+ unsigned i;
+ const struct piece * const ptr = (pos->white_to_move) ? pos->white_pieces : pos->black_pieces;
+ int score;
+ unsigned best_from = 21, best_to = 21; // a1-a1 (easy to pick out visually)
+
+ struct binheap_node moves[128]; // should be plenty?
+ unsigned indexes[128]; // much cheaper to move around
+ unsigned num_moves = 0;
+
+ assert(depth > 0);
+
+ if (abort_search)
+ return 0;
+ if (depth + seldepth < min_depth)
+ min_depth = depth + seldepth;
+
+ // see if we can find this position in our hash tables
+ if (lookup_hash(pos, phash, depth, seldepth, ret_from, ret_to, &score)) {
+ return score;
+ }
+
+ killer_currdepth = depth + seldepth;
+
+ for (i = 0; i < 16; ++i) {
+ unsigned piece, type, square;
+
+ type = ptr[i].type; // without color
+ if (type == PIECE_EMPTY)
+ continue;
+
+ square = ptr[i].square;
+ piece = pos->board[square]; // with color
+
+ if (type == PIECE_PAWN) {
+ int pawn_step = IS_WHITE(piece) ? 10 : -10;
+
+ // could it move forwards one step?
+ if (pos->board[ptr[i].square + pawn_step] == PIECE_EMPTY) {
+ ADD_MOVE(square + pawn_step);
+
+ // so that's OK, what about two?
+ if (pos->board[square + pawn_step * 2] == PIECE_EMPTY &&
+ ((IS_WHITE(piece) && NUM_TO_RANK(square) == 1) ||
+ (IS_BLACK(piece) && NUM_TO_RANK(square) == 6))) {
+ ADD_MOVE(square + pawn_step * 2);
+ }
+ }
+
+ // could it capture left?
+ if ((pos->board[square + pawn_step - 1] == PIECE_EMPTY && (square + pawn_step - 1 == pos->ep_square)) ||
+ (pos->board[square + pawn_step - 1] != PIECE_EMPTY && NUM_TO_FILE(square) != 0 && OPPOSITE_COLOR(piece, pos->board[square + pawn_step - 1]))) {
+ ADD_MOVE(square + pawn_step - 1);
+ }
+
+ // could it capture right?
+ if ((pos->board[square + pawn_step + 1] == PIECE_EMPTY && (square + pawn_step + 1 == pos->ep_square)) ||
+ (pos->board[square + pawn_step + 1] != PIECE_EMPTY && NUM_TO_FILE(square) != 7 && OPPOSITE_COLOR(piece, pos->board[square + pawn_step + 1]))) {
+ ADD_MOVE(square + pawn_step + 1);
+ }
+
+ continue;
+ }
+
+ if (type == PIECE_KNIGHT) {
+ // these are all the possible knight moves
+ if (CAN_GOTO(piece, pos->board[square + 12]))
+ ADD_MOVE(square + 12);
+ if (CAN_GOTO(piece, pos->board[square - 12]))
+ ADD_MOVE(square - 12);
+ if (CAN_GOTO(piece, pos->board[square - 21]))
+ ADD_MOVE(square - 21);
+ if (CAN_GOTO(piece, pos->board[square + 21]))
+ ADD_MOVE(square + 21);
+ if (CAN_GOTO(piece, pos->board[square + 19]))
+ ADD_MOVE(square + 19);
+ if (CAN_GOTO(piece, pos->board[square - 19]))
+ ADD_MOVE(square - 19);
+ if (CAN_GOTO(piece, pos->board[square + 8]))
+ ADD_MOVE(square + 8);
+ if (CAN_GOTO(piece, pos->board[square - 8]))
+ ADD_MOVE(square - 8);
+
+ continue;
+ }
+
+ if (type == PIECE_KING) {
+ // these are all the possible king moves
+ if (CAN_GOTO(piece, pos->board[square + 1]))
+ ADD_MOVE(square + 1);
+ if (CAN_GOTO(piece, pos->board[square - 1]))
+ ADD_MOVE(square - 1);
+ if (CAN_GOTO(piece, pos->board[square + 11]))
+ ADD_MOVE(square + 11);
+ if (CAN_GOTO(piece, pos->board[square - 11]))
+ ADD_MOVE(square - 11);
+ if (CAN_GOTO(piece, pos->board[square + 10]))
+ ADD_MOVE(square + 10);
+ if (CAN_GOTO(piece, pos->board[square - 10]))
+ ADD_MOVE(square - 10);
+ if (CAN_GOTO(piece, pos->board[square + 9]))
+ ADD_MOVE(square + 9);
+ if (CAN_GOTO(piece, pos->board[square - 9]))
+ ADD_MOVE(square - 9);
+
+ // evaluate if white can castle or not; quite ugly, but OK
+ if (search_castling) {
+ bool c_short, c_long;
+ if (pos->white_to_move) {
+ c_short = pos->wc_short;
+ c_long = pos->wc_long;
+ } else {
+ c_short = pos->bc_short;
+ c_long = pos->bc_long;
+ }
+
+ if (pos->board[square + 1] != PIECE_EMPTY ||
+ pos->board[square + 2] != PIECE_EMPTY)
+ c_short = false;
+ if (pos->board[square - 1] != PIECE_EMPTY ||
+ pos->board[square - 2] != PIECE_EMPTY ||
+ pos->board[square - 3] != PIECE_EMPTY ||
+ pos->board[square - 4] != PIECE_EMPTY)
+ c_long = false;
+
+ if (c_short || c_long) {
+ struct position npos = *pos;
+ npos.white_to_move = !npos.white_to_move;
+ npos.ep_square = 0;
+
+ // score -20000 => our king is in check
+ score = find_best_move(&npos, hash_position(&npos), NULL, NULL, 1, 0, -40000, 40000, false);
+
+ if (score >= 14500)
+ continue;
+
+ if (c_short) {
+ // check if the middle square is in check
+ npos = *pos;
+ if (pos->white_to_move)
+ real_make_move(&npos, square, square + 1, &npos.white_pieces[i], npos.black_pieces);
+ else
+ real_make_move(&npos, square, square + 1, &npos.black_pieces[i], npos.white_pieces);
+ score = find_best_move(&npos, hash_position(&npos), NULL, NULL, 1, 0, -40000, 40000, false);
+
+ if (score < 14500)
+ ADD_MOVE(square + 2);
+ }
+ if (c_long) {
+ // check if the middle square is in check
+ npos = *pos;
+ if (pos->white_to_move)
+ real_make_move(&npos, square, square - 1, &npos.white_pieces[i], npos.black_pieces);
+ else
+ real_make_move(&npos, square, square - 1, &npos.black_pieces[i], npos.white_pieces);
+ score = find_best_move(&npos, hash_position(&npos), NULL, NULL, 1, 0, -40000, 40000, false);
+
+ if (score < 14500)
+ ADD_MOVE(square - 2);
+ }
+ }
+ }
+
+ continue;
+ }
+
+ if (SLIDES_STRAIGHT(type)) {
+ // slide to the right for as long we can
+ int nsq = square + 1;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ ++nsq;
+ }
+
+ // to the left
+ nsq = square - 1;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ --nsq;
+ }
+
+ // up
+ nsq = square + 10;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ nsq += 10;
+ }
+
+ // and down
+ nsq = square - 10;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ nsq -= 10;
+ }
+ }
+
+ if (SLIDES_DIAGONALLY(type)) {
+ // slide to the upper-right for as long as we can
+ int nsq = square + 11;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ nsq += 11;
+ }
+
+ // to the upper-left
+ nsq = square + 9;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ nsq += 9;
+ }
+
+ // lower-right
+ nsq = square - 9;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ nsq -= 9;
+ }
+
+ // and lower-left
+ nsq = square - 11;
+ for ( ;; ) {
+ if (CAN_GOTO(piece, pos->board[nsq]))
+ ADD_MOVE(nsq);
+ if (pos->board[nsq] != PIECE_EMPTY)
+ break;
+ nsq -= 11;
+ }
+ }
+ }
+
+ // null-move heuristic
+ if (depth > 6) {
+ struct position npos = *pos;
+ unsigned long long nphash;
+ npos.white_to_move = !npos.white_to_move;
+ npos.ep_square = 0;
+
+ nphash = hash_position(&npos);
+ score = -find_best_move(&npos, nphash, NULL, NULL, depth - 1, seldepth, alpha, beta, true);
+
+ if (score > alpha) {
+ alpha = score;
+ best_from = moves[indexes[0]].from;
+ best_to = moves[indexes[0]].to;
+
+ if (alpha >= beta)
+ goto early_exit;
+ }
+ }
+
+ // now go through all the moves
+ while (num_moves > 0) {
+ unsigned tmp;
+ const struct position * const npos = &moves[indexes[0]].pos;
+ unsigned long long nphash;
+
+ /*
+ * If this move estimated way too good score, it means we just captured
+ * the king, and this position is definitely not legal. Stop the search,
+ * since there's no point of searching illegal positions.
+ */
+ if (moves[indexes[0]].estimate_score < -15000) {
+ alpha = -15000 + (max_depth + max_selective_depth - depth - seldepth);
+ best_from = moves[indexes[0]].from;
+ best_to = moves[indexes[0]].to;
+
+ killer_moves[depth][phash & 1].from = best_from;
+ killer_moves[depth][phash & 1].to = best_to;
+
+ goto early_exit;
+ }
+ if (moves[indexes[0]].estimate_score > 15000) {
+ alpha = 15000 - (max_depth + max_selective_depth - depth - seldepth);
+ best_from = moves[indexes[0]].from;
+ best_to = moves[indexes[0]].to;
+
+ killer_moves[depth][phash & 1].from = best_from;
+ killer_moves[depth][phash & 1].to = best_to;
+
+ goto early_exit;
+ }
+
+ nphash = hash_position(&moves[indexes[0]].pos);
+ if (lookup_record(nphash) >= 1) {
+ // ouch, move repetition, it's going to be a draw
+ score = -50;
+ } else if (depth == 1) {
+ // terminal node: static evaluation
+ score = (pos->white_to_move) ? evaluate(npos) : -evaluate(npos);
+ } else {
+ // non-terminal node: consider the opponent's best move
+
+ // if we're close to the end, and this is a capture, make a move extension
+ if (depth <= 1 && seldepth > 0 && pos->board[moves[indexes[0]].to] != PIECE_EMPTY)
+ score = -find_best_move(npos, nphash, NULL, NULL, depth, seldepth - 1, -beta, -alpha, true);
+ else
+ score = -find_best_move(npos, nphash, NULL, NULL, depth - 1, seldepth, -beta, -alpha, true);
+ }
+
+ if (score > alpha) {
+ alpha = score;
+ best_from = moves[indexes[0]].from;
+ best_to = moves[indexes[0]].to;
+
+ if (alpha >= beta) {
+ // replace a random killer move
+ killer_moves[depth][phash & 1].from = best_from;
+ killer_moves[depth][phash & 1].to = best_to;
+
+ goto early_exit;
+ }
+ }
+
+ // swap this element with the last one
+ tmp = indexes[num_moves - 1];
+ indexes[num_moves - 1] = indexes[0];
+ indexes[0] = tmp;
+ --num_moves;
+
+ downheapify(moves, indexes, 0, num_moves);
+ }
+
+ if ((best_from != 21 || best_to != 21) && search_castling)
+ insert_hash(pos, phash, depth, seldepth, best_from, best_to, alpha);
+
+early_exit:
+ if (ret_from)
+ *ret_from = best_from;
+ if (ret_to)
+ *ret_to = best_to;
+ return alpha;
+}
+
+void alarm_sounded(int signal)
+{
+ abort_search = true;
+ fprintf(ucilog, "Aborting search due to signal %u\n", signal);
+}
+
+void ai_move(struct position *pos, unsigned think_time)
+{
+ unsigned from, to, best_from, best_to;
+ int score, depth, seldepth;
+ struct timeval think_start, now;
+
+ total_nodes = 0;
+ gettimeofday(&think_start, NULL);
+
+ depth = 2;
+ seldepth = depth;
+ clear_hash();
+
+ signal(SIGALRM, alarm_sounded);
+ alarm(think_time);
+ abort_search = false;
+
+ for ( ;; ) {
+ struct position npos;
+ int pdepth = depth;
+ double time_spent;
+
+ // set the globals
+ max_depth = depth;
+ max_selective_depth = seldepth;
+
+ //fprintf(stderr, "\n\n\nThinking at depth %u:\n\n", depth);
+ min_depth = depth + seldepth;
+ score = find_best_move(pos, hash_position(pos), &from, &to, depth, seldepth, -40000, 40000, true);
+
+ if (abort_search)
+ break;
+
+ best_from = from;
+ best_to = to;
+
+ gettimeofday(&now, NULL);
+ time_spent = (now.tv_sec - think_start.tv_sec) +
+ 1e-6 * (now.tv_usec - think_start.tv_usec);
+
+ uciprintf("info depth %u seldepth %u score cp %d time %u hashfull %u nodes %u nps %u pv",
+ depth, depth + seldepth - min_depth + 1, score,
+ (unsigned)(time_spent * 1000.0),
+ 100 * hash_used / HASH_MAX_ELEMS,
+ total_nodes, (unsigned)(total_nodes / time_spent + 0.5));
+ uciprintf(" %c%u%c%u",
+ "abcdefgh"[NUM_TO_FILE(from)], NUM_TO_RANK(from) + 1,
+ "abcdefgh"[NUM_TO_FILE(to)], NUM_TO_RANK(to) + 1);
+
+ // reconstruct the pv (hopefully from the hash table!)
+ npos = *pos;
+ make_move(&npos, from, to);
+
+ while (pdepth > 1) {
+ unsigned pvf, pvt;
+
+ --pdepth;
+ find_best_move(&npos, hash_position(&npos), &pvf, &pvt, pdepth, seldepth, -40000, 40000, true);
+
+ uciprintf(" %c%u%c%u",
+ "abcdefgh"[NUM_TO_FILE(pvf)], NUM_TO_RANK(pvf) + 1,
+ "abcdefgh"[NUM_TO_FILE(pvt)], NUM_TO_RANK(pvt) + 1);
+
+ //print_position(&npos);
+ make_move(&npos, pvf, pvt);
+ }
+
+ uciprintf("\n");
+ fflush(stdout);
+
+ // this was a much better idea before we had selective search :-)
+#if 0
+ if (score > 15000 || score < -15000)
+ break;
+#endif
+
+ if (seldepth < depth * 2)
+ seldepth += (depth+1)/2;
+ else {
+ ++depth;
+ seldepth = depth;
+ }
+ }
+
+ make_move(pos, best_from, best_to);
+
+ uciprintf("bestmove %c%u%c%u\n",
+ "abcdefgh"[NUM_TO_FILE(best_from)], NUM_TO_RANK(best_from) + 1,
+ "abcdefgh"[NUM_TO_FILE(best_to)], NUM_TO_RANK(best_to) + 1);
+ fflush(stdout);
+}
+
+int main(void)
+{
+ struct position curr_pos;
+ ucilog = fopen("ucilog.txt", "w");
+
+ move_num = 0;
+
+ init_evaluator();
+ init_hash();
+
+ for ( ;; ) {
+ char buf[4096], *ptr;
+ fgets(buf, 4096, stdin);
+
+ // strip newlines
+ ptr = strchr(buf, '\n');
+ if (ptr != NULL)
+ *ptr = 0;
+
+ ptr = strchr(buf, '\r');
+ if (ptr != NULL)
+ *ptr = 0;
+
+ fprintf(ucilog, "<= %s\n", buf);
+ fflush(ucilog);
+
+ if (strcmp(buf, "uci") == 0) {
+ uciprintf("id name stupid\n");
+ uciprintf("id author Steinar H. Gunderson\n");
+ uciprintf("uciok\n");
+ fflush(stdout);
+ continue;
+ }
+ if (strcmp(buf, "isready") == 0) {
+ uciprintf("readyok\n");
+ fflush(stdout);
+ continue;
+ }
+ if (strcmp(buf, "ucinewgame") == 0) {
+ // TODO: clear history and possibly pawn structure stuff?
+ continue;
+ }
+ if (strncmp(buf, "position", 8) == 0) {
+ ptr = buf + 9;
+ if (strncmp(ptr, "startpos", 8) == 0) {
+ init_position(&curr_pos);
+ ptr += 9;
+ }
+ if (strncmp(ptr, "fen", 3) == 0) {
+ // no fen handling yet
+ ptr = strchr(ptr, ' ') + 1;
+ }
+ if (strncmp(ptr, "moves", 5) == 0) {
+ ptr = strchr(ptr, ' ');
+ while (ptr != NULL) {
+ unsigned from_file, from_rank, to_file, to_rank;
+ from_file = tolower(ptr[1]) - 'a';
+ from_rank = ptr[2] - '1';
+ to_file = tolower(ptr[3]) - 'a';
+ to_rank = ptr[4] - '1';
+
+ make_move(&curr_pos, SQUARE_TO_NUM(from_file, from_rank), SQUARE_TO_NUM(to_file, to_rank));
+ record_move(&curr_pos);
+ ++move_num;
+ ptr = strchr(ptr + 1, ' ');
+ }
+ }
+ continue;
+ }
+ if (strncmp(buf, "go", 2) == 0) {
+ // see if there's a clock for us -- if not, use ten seconds
+ unsigned thinking_time = 10;
+
+ if (curr_pos.white_to_move)
+ ptr = strstr(buf, "wtime ");
+ else
+ ptr = strstr(buf, "btime ");
+
+ if (ptr != NULL) {
+ unsigned total_ms = atoi(ptr + 6);
+ unsigned moves = 40;
+ ptr = strstr(buf, "movestogo ");
+ if (ptr != NULL)
+ moves = atoi(ptr + 10);
+
+ thinking_time = ((total_ms - 5000) / (moves + 2) + 500) / 1000;
+
+ // also allocate 90% of the increment, if there's any
+ if (curr_pos.white_to_move)
+ ptr = strstr(buf, "winc ");
+ else
+ ptr = strstr(buf, "binc ");
+ if (ptr != NULL)
+ thinking_time += atoi(ptr + 5) * 9 / 10000;
+
+ if (thinking_time < 1)
+ thinking_time = 1;
+
+ fprintf(ucilog, "Allocating %u seconds to think for this move.\n", thinking_time);
+ }
+
+ ai_move(&curr_pos, thinking_time);
+ record_move(&curr_pos);
+ ++move_num;
+ continue;
+ }
+
+ fprintf(ucilog, "Unknown UCI command '%s'!\n", buf);
+ fflush(ucilog);
+ }
+
+ exit(0);
+}