home / fornax / fornax-v4-0 / src / uci.cpp

uci.cpp



//
//  uci.cpp
//  fornax3
//
//  Created by Anders on 24/04/2019.
//

#ifndef GIT_DESC
#define GIT_DESC ""
#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <inttypes.h>
#include "uci.hpp"
#include "libs/require.h"
#include "libs/strings.h"
#include "libs/iolog.hpp"
#include "board.h"
#include "moving/makemove.hpp"
#include "moving/movegen.hpp"
#include "moving/movepick.hpp"
#include "search/search.hpp"
#include "eval/eval.hpp"
#include "parsing.hpp"
#include "tt/transpositions.hpp"
#include "supplementary/time_management.hpp"
#include "supplementary/opening_book.hpp"
#include "supplementary/testsuite.hpp"
#include "supplementary/perft.hpp"

static Board* curr_board;
static char** curr_tokens;

static std::future<move> currmove;

move get_currmove() {
  return currmove.valid() ? currmove.get() : 0;
}


void uci_printversion() {
  const char* mainVersion = "4.0";
  if (!string_equals(GIT_DESC, "")) {
    printf("%s %s", mainVersion, GIT_DESC);
  }
  else {
    printf("%s", mainVersion);
  }
}


void uci_init(void) {
  curr_board = (Board*) malloc(sizeof(Board));
  curr_tokens = (char**) malloc(sizeof(*curr_tokens) * 512);
}

void uci_destroy(void) {
  free(curr_board);
  free(curr_tokens);
}

static void uci_test(char** tokens, int length) {
  if (string_starts_with(tokens[0], "all")) {
    testsuite_perft();
    testsuite_search(1);
  }
  else if (string_starts_with(tokens[0], "p")) {
    testsuite_perft();
  }
  else if (string_starts_with(tokens[0], "s")) {
    if (length > 1) {
      int number = (int) string_to_long(tokens[1]);
      testsuite_search(number);
    }
    else {
      testsuite_search(1);
    }
  }
  else if (string_starts_with(tokens[0], "e")) {
    testuite_eval();
  }
}

static void uci_tt(char** tokens, int length) {
  if (length == 0) {
    IO_PRINT("ttkey = %" PRIu64 "\n", board_get_hash(curr_board));
    IO_FLUSH();
  }
  else if (string_starts_with(tokens[0], "clear")) {
    transpositions_clear();
  }
  else if (string_starts_with(tokens[0], "clean")) {
    transpositions_maintenance();
  }
  else if (string_starts_with(tokens[0], "status")) {
    transpositions_status();
  }
  else if (string_starts_with(tokens[0], "print")) {
    long size = 0;
    if (length > 1) {
      size = string_to_long(tokens[1]);
    }
    transpositions_print(size);
  }
  else if (string_starts_with(tokens[0], "row")) {
    long size = 0;
    if (length > 1) {
      size = string_to_long(tokens[1]);
    }
    transpositions_print_bucket(board_get_hash(curr_board), size);
  }
}

static void uci_play_moves(char** tokens, int length) {
  int moves_to_play = length;
  int i = 0;
  
  while (moves_to_play > 0) {
    moves_to_play--;
    move m = parsing_move_from_uci(tokens[i]);
    i++;
    makemove_makef(curr_board, m);
  }
}

static void uci_position(char** tokens, int length) {
  if (length == 0) {
    return;
  }
  if (string_equals("fen", tokens[0])) {
    int fenEnd = 1;
    int moveStart = -1;
    for (; fenEnd < length - 1; ++fenEnd) {
      if (string_equals(tokens[fenEnd], "moves")) {
        moveStart = fenEnd;
        break;
      }
      else if (fenEnd == 6) break;
    }
    Board b = parsing_from_fen_parts(tokens + 1, fenEnd - 1);
    *curr_board = b;
    if (moveStart >= 0) {
      uci_play_moves(tokens + moveStart + 1, length - moveStart - 1);
    }
    
  }
  else if (string_starts_with(tokens[0], "startpos")) {
    Board b = parsing_from_fen(STARTPOS_FEN);
    *curr_board = b;
    if (length <= 2) return;
    if (string_starts_with(tokens[1], "moves")) {
      uci_play_moves(tokens + 2, length - 2);
    }
  }
}

static void uci_perft(char** tokens, int length) {
  int depth = 1;
  if (length > 0) {
    require(isdigit(*tokens[0]), "'%s' was not a digit\n", tokens[0]);
    depth = (int)string_to_long(tokens[0]);
  }
  perft(curr_board, depth, true);
}

static inline move uci_find_book_ponder(Board* board, move m) {
  makemove_makef(board, m);
  move bookmove = opening_book_consult(board);
  makemove_takebackf(board, m);
  return bookmove;
}

static void uci_go(char** tokens, int length) {
  move bookmove = opening_book_consult(curr_board);
  if (bookmove) {
#if DEBUGLOG
  IO_PRINT("info depth 1 nodes 1 string - found book move\n");
  IO_FLUSH();
#endif
    currmove = move_now(bookmove);
    parsing_printbestmove(bookmove, uci_find_book_ponder(curr_board, bookmove));
    return;
  }
  
  if (length == 0) {
    currmove = search_async_time(curr_board, 1000);
    return;
  }
  else if (!strcmp(tokens[0], "depth")
           && length == 2
           && isdigit(*tokens[1])) {
    currmove = search_async_depth(curr_board, (uint8_t) string_to_long(tokens[1]));
  }
  else if (string_starts_with(tokens[0], "infinite")) {
    currmove = search_async_infinite(curr_board);
  }
  else if ((string_starts_with(tokens[0], "movetime")
           || string_starts_with(tokens[0], "time"))
           && length == 2
           && isdigit(*tokens[1])) {
    currmove = search_async_time(curr_board, string_to_long(tokens[1]));
  }
  else {
    currmove = time_management_parse_uci(curr_board, tokens, length);
  }
}

static void uci_setoption(char** tokens, int length) {
  if (length < 4 || !string_equals("name", tokens[0])) return;
  
  if (length == 5
      && string_equals("Move", tokens[1])
      && string_equals("Overhead", tokens[2])) {
    long overhead = string_to_long(tokens[4]);
    time_management_set_overhead(overhead);
  }
  else if (length == 4 && string_equals("Contempt", tokens[1])) {
    long contempt = string_to_long(tokens[3]);
    eval_set_contempt_factor(contempt);
  }
  else if (length == 4 && string_equals("Hash", tokens[1])) {
    long mb = string_to_long(tokens[3]);
    transpositions_set_hash_size(mb);
  }
  else if (length == 4 && string_equals("Ponder", tokens[1])) {
    bool enabled = string_equals("true", tokens[3]);
    time_management_set_ponder_enabled(enabled);
  }
  else if (length == 4 && string_equals("OwnBook", tokens[1])) {
    bool enabled = string_equals("true", tokens[3]);
    opening_book_set_enabled(enabled);
  }
  else {
    IO_PRINT("unknown option '%s'\n", tokens[1]);
    IO_FLUSH();
  }
}

static void uci_do(char** tokens, int length) {
  if (length == 0 && currmove.valid()) {
    makemove_makef(curr_board, currmove.get());
  }
  else {
    uci_play_moves(tokens, length);
  }
}

static bool uci_handleToken(char** tokens, int length) {
  if (length == 0) {
    return true;
  }
  
  char* token = tokens[0];
  
  if (string_equals(token, "go")) {
    uci_go(tokens + 1, length - 1);
  }
  else if (string_equals(token, "stop")) {
    search_async_stop();
  }
  else if (string_equals(token, "ponderhit")) {
    time_management_ponderhit(curr_board);
  }
  else if (string_equals(token, "position")) {
    uci_position(tokens + 1, length - 1);
  }
  else if (string_equals(token, "ucinewgame")) {
    transpositions_clear();
  }
  else if (string_equals(token, "uci")) {
    IO_PRINT("id name fornax v");
    uci_printversion();
    IO_PRINT("\nid author Anders Fuglseth\n\n");
    IO_PRINT("option name Move Overhead type spin default %d min %d max %d\n",
                 TIME_MANAGEMENT_OVERHEAD_DEFAULT, TIME_MANAGEMENT_OVERHEAD_MIN, TIME_MANAGEMENT_OVERHEAD_MAX);
    IO_PRINT("option name Hash type spin default %d min %d max %d\n",
                 TRANSPOSITIONS_SIZE_MB_DEFAULT, TRANSPOSITIONS_SIZE_MB_MIN, TRANSPOSITIONS_SIZE_MB_MAX);
    IO_PRINT("option name Ponder type check default false\n");
    IO_PRINT("option name OwnBook type check default false\n");
    IO_PRINT("option name Contempt type spin default %d min %d max %d\n", EVAL_CONTEMPT_DEFAULT, EVAL_CONTEMPT_MIN, EVAL_CONTEMPT_MAX);
    IO_PRINT("uciok\n");
    IO_FLUSH();
  }
  else if (string_equals(token, "isready")) {
    IO_PRINT("readyok\n");
    IO_FLUSH();
  }
  else if (string_equals(token, "quit")) {
    return false;
  }
  else if (string_equals(token, "setoption")) {
    uci_setoption(tokens + 1, length - 1);
  }
  
  //non uci commands
  else if (string_equals(token, "d")) {
    parsing_printboard(curr_board);
  }
  else if (string_equals(token, "info")) {
    search_async_printinfo();
  }
  else if (string_equals(token, "do")) {
    uci_do(tokens + 1, length - 1);
  }
  else if (string_equals(token, "perft")) {
    uci_perft(tokens + 1, length - 1);
  }
  else if (string_equals(token, "test")) {
    uci_test(tokens + 1, length - 1);
  }
  else if (string_equals(token, "bench")) {
    testsuite_search(3);
  }
  else if (string_equals(token, "tt")) {
    uci_tt(tokens + 1, length -1);
  }
  else if (string_equals(token, "eval")) {
    if (length == 1) {
      eval_evaluate_debug(curr_board);
    }
    else if (length == 2) {
      move m = parsing_move_from_uci(tokens[1]);
      if (curr_board->active)  movepick_eval<BLACK, true>(curr_board, m);
      else movepick_eval<WHITE, true>(curr_board, m);
    }
  }
  else if (string_equals(token, "moves")) {
    evalmove moves[128];
    uint8_t size = movegen_generatef(curr_board, moves);
    if (curr_board->active) movepick_annotate_list<BLACK>(curr_board, moves, size, 0);
    else movepick_annotate_list<WHITE>(curr_board, moves, size, 0);
    for (uint8_t n = 0; n < size; ++n) parsing_printmove_extra(curr_board, movepick_find_best(moves, size, n));
  }
  else if (string_equals(token, "pf")) {
    Board b = parsing_from_fen_parts(tokens + 1, length - 1);
    *curr_board = b;
  }
  else {
    uci_handleToken(tokens + 1, length - 1);
  }
  return true;
}

bool uci_handlecommand(char* command) {
  
  int token_length = strings_split(command, curr_tokens, " ");
  if (token_length <= 0) return true;

  assert(token_length < 512);
  for (int i = 0; i < token_length; ++i) {
    strings_trim(curr_tokens[i]);
  }

  int result = uci_handleToken(curr_tokens, token_length);
  return result;
}