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

parsing.cpp



//
//  parsing.cpp
//  fornax3
//
//  Created by Anders on 09/12/2020.
//

#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <inttypes.h>
#include "parsing.hpp"
#include "libs/require.h"
#include "libs/strings.h"
#include "libs/iolog.hpp"
#include "bits.h"
#include "tt/zobrist.hpp"
#include "moving/makemove.hpp"
#include "moving/movegen.hpp"
#include "moving/derived_knowledge.h"
#include "tt/transpositions.hpp"


void parsing_printbestmove(move best, move ponder) {
  IO_PRINT("bestmove ");
  if (!best) {
    IO_PRINT("none");
  }
  else {
    parsing_printmove(best);
    if (ponder) {
      IO_PRINT(" ponder ");
      parsing_printmove(ponder);
    }
  }
  IO_PRINT("\n");
  IO_FLUSH();
}


void parsing_printcurrmove(move m, uint8_t depth, uint8_t index) {
  IO_PRINT("info depth %" PRIu8 " currmove ", depth);
  if (m) parsing_printmove(m);
  else IO_PRINT("none");
  IO_PRINT(" currmovenumber %d\n", index);
  IO_FLUSH();
}

void parsing_printpv_inner(Board* board, move m, uint8_t depth) {
  if (depth == 0) return;
  if (!m) {
    IO_PRINT("none");
    return;
  }
  parsing_printmove(m);
  
  makemove_makef(board, m);
  
  ttkey hash = board_get_hash(board);
  Bucket* bucket = transpositions_get(hash);
  if (bucket->hash != hash || bucket->ttmove == 0);
  else {
    IO_PRINT(" ");
    parsing_printpv_inner(board, bucket->ttmove, --depth);
  }
  
  makemove_takebackf(board, m);
}

void parsing_printpv(Board* board, move m, uint8_t depth) {
  IO_PRINT("pv ");
  parsing_printpv_inner(board, m, depth);
  derived_knowledge_compute(board);
}

square parsing_square_from_file (char file) {
  square sf = (file < 'a' || file > 'h') ? SQUARE_AXIS_NONE : (square) (file - 'a');
  assert(sf <= 7 || sf == SQUARE_AXIS_NONE);
  return sf;
}

square parsing_square_from_rank (char rank) {
  square sr = (rank < '1' || rank > '8') ? SQUARE_AXIS_NONE : (square) (rank - '1');
  assert(sr <= 7 || sr == SQUARE_AXIS_NONE);
  return sr;
}

char parsing_square_to_file(square sq) {
  return sq > 63 ? '?' : (char) ('a' + SQUARE_X(sq));
}

char parsing_square_to_rank(square sq) {
  return sq > 63 ? '?' : (char) ('1' + SQUARE_Y(sq));
}

square parsing_square_from_uci(const char *ucisquare) {
  size_t length = strlen(ucisquare);
  require(length >= 2, "illegal uci square: %s", ucisquare);
  square file = parsing_square_from_file(ucisquare[0]);
  square rank = parsing_square_from_rank(ucisquare[1]);
  require(file != SQUARE_AXIS_NONE && rank != SQUARE_AXIS_NONE, "illegal uci square: %s", ucisquare);
  return SQUARE_FLATTEN(file, rank);
}

piecetype parsing_piece_from_symbol(char symbol) {
  switch (symbol) {
    case 'r': return ROOK;
    case 'n': return KNIGHT;
    case 'b': return BISHOP;
    case 'q': return QUEEN;
    case 'k': return KING;
    case 'p': return PAWN;
    default: return NONE;
  }
}

char parsing_symbol_from_piece(piecetype piece) {
  switch (piece) {
    case ROOK: return 'r';
    case KNIGHT: return 'n';
    case BISHOP: return 'b';
    case QUEEN: return 'q';
    case KING: return 'k';
    case PAWN: return 'p';
    default: return '\0';
  }
}

void parsing_printmove(move m) {
  square origin = MOVE_GET_ORIGIN(m);
  square dest = MOVE_GET_DEST(m);
  piecetype promotion = MOVE_GET_PROMOTION(m);
  
  IO_PRINT("%c%c%c%c",
           parsing_square_to_file(origin),
           parsing_square_to_rank(origin),
           parsing_square_to_file(dest),
           parsing_square_to_rank(dest));
  if (promotion != KING) IO_PRINT("%c", parsing_symbol_from_piece(promotion));
}

void parsing_printmove_extra(const Board* board, evalmove m) {
  square origin = MOVE_GET_ORIGIN(m);
  square dest = MOVE_GET_DEST(m);
  piecetype piece = board->piecemap[origin];
  piecetype capture = board->piecemap[dest];
  
  parsing_printmove((move) m);
  IO_PRINT(" %c", parsing_symbol_from_piece(piece));
  IO_PRINT(" [value = %d]", (eval) (m >> 16));
  if (capture != NONE) {
    IO_PRINT(" [capture = %c]", parsing_symbol_from_piece(capture));
  }
  IO_PRINT("\n");
}

move parsing_move_from_uci(const char *ucimove) {
  size_t length = strlen(ucimove);
  require(length >= 4, "illegal uci move: %s", ucimove);
  square origin = parsing_square_from_uci(ucimove);
  square dest = parsing_square_from_uci(ucimove + 2);
  
  move m = MOVE_CREATE(origin, dest);
  if (length == 5) {
    m = MOVE_ADD_PROMOTION(m, parsing_piece_from_symbol(ucimove[4]));
  }
  return m;
}

piecetype parsing_piece_from_san_symbol(char symbol) {
  switch (symbol) {
    case 'R': return ROOK;
    case 'N': return KNIGHT;
    case 'B': return BISHOP;
    case 'Q': return QUEEN;
    case 'K': return KING;
    case 'P': return PAWN;
    default: return NONE;
  }
}

bool parsing_is_valid_move(const Board* b, move m) {
  evalmove moves[128];
  uint8_t moveCount = movegen_generatef(b, moves);
  for (uint8_t i = 0; i < moveCount; ++i) {
    if (moves[i] == m) return true;
  }
  return false;
}

move parsing_move_from_san(const Board* b, const char* sanmove) {
  if (string_starts_with(sanmove, "O-O-O")) {
    return b->active == WHITE ? MOVE_WCQ : MOVE_BCQ;
  }
  else if (string_starts_with(sanmove, "O-O")) {
    return b->active == WHITE ? MOVE_WCK : MOVE_BCK;
  }
  require(strlen(sanmove) <= 7, "san parse error: move string was too long (1)");
  
  piecetype type = parsing_piece_from_san_symbol(sanmove[0]);
  if (type == NONE) {
    type = PAWN;
  }
  else {
    sanmove++;
  }
  
  piecetype promotion = KING;
  bool is_capture = false;
  char stripped[5];
  for (int i = 0; i < 5; ++i) stripped[i] = '\0';
  int i = 0;
  while (strlen(sanmove) > 0) {
    char c = *sanmove++;
    if (c == 'x') {
      is_capture = true;
      continue;
    }
    if (c == '=') {
      promotion = parsing_piece_from_san_symbol(*sanmove);
      break;;
    }
    if ((c >= '1' && c <= '8') || (c >= 'a' && c <= 'h')) {
      stripped[i++] = c;
    }
  }
  
  sanmove = stripped;
  
  require(strlen(sanmove) <= 4, "san parse error (2) \n");
  bits64 ambiguity_remover = UINT64_MAX;
  
  if (strlen(sanmove) == 3) {
    square origin_rank = parsing_square_from_rank(*sanmove);
    
    ambiguity_remover = origin_rank < 8
    ? BB_RANKS[origin_rank]
    : BB_FILES[parsing_square_from_file(*sanmove)];
    
    require(ambiguity_remover < UINT64_MAX, "san parse error (3)\n");
    sanmove++;
  }
  
  
  square origin;
  square dest;
  
  if (strlen(sanmove) == 4) {
    origin = parsing_square_from_uci(sanmove);
    dest = parsing_square_from_uci(sanmove + 2);
  }
  else {
    dest = parsing_square_from_uci(sanmove);
    bits64 pieces = b->side[b->active].pieces[type] & ambiguity_remover;
    
    if (type == PAWN) {
      if (is_capture) {
        int mod = b->active ? 1 : -1;
        bits64 rank = BB_RANKS[SQUARE_Y(dest) + mod];
        bits64 remaining = pieces & rank;
        if (BITS_COUNT(remaining) > 1) remaining &= ~movegen_get_pinned(b, b->active);
        origin = bits_get_trailing(remaining);
      }
      else {
        int mod = b->active ? 1 : -1;
        bits64 rank = BB_RANKS[SQUARE_Y(dest) + (1 * mod)] | BB_RANKS[SQUARE_Y(dest) + (2 * mod)];
        bits64 file = BB_FILES[SQUARE_X(dest)];
        bits64 candidates = pieces & rank & file;
        require(candidates > 0, "san parse error (4)\n");
        origin = b->active ? bits_get_trailing(candidates) : bits_get_leading(candidates);
      }
    }
    else {
      bits64 attacks = movegen_get_attacksf(type ,dest, b->side[WHITE].piecemask | b->side[BLACK].piecemask);
      bits64 remaining = pieces & attacks;
      if (BITS_COUNT(remaining) > 1) remaining &= ~movegen_get_pinned(b, b->active);
      origin = bits_get_trailing(remaining);
    }
  }
  
  move m = MOVE_CREATE(origin, dest);
  if (promotion) m = MOVE_ADD_PROMOTION(m, promotion);

  require(parsing_is_valid_move(b, m), "san parse error (5)\n");
  return m;
}

void parsing_printfen(const Board *b) {
  char dest[100];
  parsing_to_fen(b, dest);
  IO_PRINT("%s", dest);
}

void parsing_printboard(const Board *b) {
  
  const char edge[] = "  -----------------\n";
  
  IO_PRINT("%s", edge);
  bool mode = 0;
  
  for (int y = 7; y >= 0; --y) {
    IO_PRINT("%d| ", (y + 1));
    for (int x = 0; x < 8; ++x) {
      square sq = SQUARE_FLATTEN(x, y);
      bool isWhite = board_get_piece(b, WHITE, sq) != NONE;
      piecetype piece = b->piecemap[sq];
      char s = parsing_symbol_from_piece(piece);
      if (s == '\0') s = mode ? '+' : ' ';
      else s = isWhite ? (char) toupper(s) : s;
      IO_PRINT("%c ", s);
      
      mode = !mode;
    }
    mode = !mode;
    IO_PRINT("|\n");
  }
  
  const Plyinfo *info = &b->plyinfo[b->ply];
  
  IO_PRINT("%s", edge);
  IO_PRINT("   A B C D E F G H\n");
  IO_PRINT("\nhash: %" PRIu64 "", info->hash);
  
  if (info->enpassant) {
    square ep_sq = bits_get_trailing(info->enpassant);
    IO_PRINT("\nen passant: %c%c", parsing_square_to_file(ep_sq), parsing_square_to_rank(ep_sq));
  }
  
  IO_PRINT("\nfen: ");
  parsing_printfen(b);
  IO_PRINT("\n");
  IO_FLUSH();
}

void parsing_to_fen(const Board *b, char *dest) {
  const Plyinfo *info = &b->plyinfo[b->ply];
  bool isWhite = b->active == WHITE;
  for (int j = 7; j >= 0; --j) {
    char spaceCounter = 0;
    for (int i = 0; i < 8; ++i) {
      
      square sq = SQUARE_FLATTEN(i, j);
      char c = parsing_symbol_from_piece(b->piecemap[sq]);
      bool w = board_get_piece(b, WHITE, sq) != NONE;
      c = c > 0 && w ? (char) toupper(c) : c;
      
      if (c > 0) {
        if (spaceCounter > 0) {
          (*dest++) = '0' + spaceCounter;
          spaceCounter = 0;
        }
        *dest++ = c;
      }
      else {
        ++spaceCounter;
      }
    }
    if (spaceCounter > 0) {
      (*dest++) = '0' + spaceCounter;
    }
    (*dest++) = '/';
  }
  *(dest -1) = ' ';
  *dest++ = isWhite ? 'w' : 'b';
  *dest++ = ' ';
  char* d = dest;
  if (info->castlingrights & BB_RIGHTS_WCK) (*dest++) = 'K';
  if (info->castlingrights & BB_RIGHTS_WCQ) (*dest++) = 'Q';
  if (info->castlingrights & BB_RIGHTS_BCK) (*dest++) = 'k';
  if (info->castlingrights & BB_RIGHTS_BCQ) (*dest++) = 'q';
  if (d == dest) (*dest++) = '-';
  
  *dest++ = ' ';
  if (info->enpassant) {
    square pos = bits_get_trailing(info->enpassant);
    (*dest++) = parsing_square_to_file(pos);
    (*dest++) = parsing_square_to_rank(pos);
  }
  else *dest++ = '-';
  
  *dest++ = ' ';
  char halfmoves[10];
  sprintf(halfmoves, "%d", info->halfmovecount);
  char *digit = halfmoves;
  while (*digit != '\0') *dest++ = *digit++;
  
  *dest = 0;
}

Board parsing_from_fen_parts(char** parts, int parts_size) {
  require(parts_size >= 4, "FEN was not comprised of at least 4 parts\n");
  
  char* fenRanks[8];
  int splitSizeBoard = strings_split(parts[0], fenRanks, "/");
  
  require(splitSizeBoard == 8, "FEN board was not comprised of 8 ranks: '%s'\n", parts[0]);
  
  Board b{};
  Plyinfo *info = &b.plyinfo[b.ply];
  
  for (int i = 0; i < 64; ++i) b.piecemap[i] = NONE;
  
  for (int j = 0; j < 8; ++j) {
    size_t size = strlen(fenRanks[j]);
    require(size <= 8 && size > 0, "FEN rank could not be parsed\n");
    int x = 0;
    int y = 7 - j;
    size_t counter = 0;
    while (counter < size) {
      char symbol = fenRanks[j][counter];
      
      square sq = SQUARE_FLATTEN(x, y);
      switch (symbol) {
        case 'R': board_put(&b, WHITE, sq, ROOK); break;
        case 'N': board_put(&b, WHITE, sq, KNIGHT); break;
        case 'B': board_put(&b, WHITE, sq, BISHOP); break;
        case 'Q': board_put(&b, WHITE, sq, QUEEN); break;
        case 'K': board_put(&b, WHITE, sq, KING); break;
        case 'P': board_put(&b, WHITE, sq, PAWN); break;
        case 'r': board_put(&b, BLACK, sq, ROOK); break;
        case 'n': board_put(&b, BLACK, sq, KNIGHT); break;
        case 'b': board_put(&b, BLACK, sq, BISHOP); break;
        case 'q': board_put(&b, BLACK, sq, QUEEN); break;
        case 'k': board_put(&b, BLACK, sq, KING); break;
        case 'p': board_put(&b, BLACK, sq, PAWN); break;
        default: {
          int range = symbol - '0';
          
          if (range > 0 && range <= 8) x += range - 1;
          else require(false, "FEN unknown symbol: %c\n", symbol);
        }
      };
      counter++;
      x++;
    }
  }
  {
    if (parts[1][0] == 'w') b.active = WHITE;
    else if (parts[1][0] == 'b') b.active = BLACK;
    else require(false, "FEN unknown symbol: %s\n", parts[1]);
  }
  b.active_initial = b.active;
  size_t size = strlen(parts[2]);
  for (size_t i = 0; i < size; ++i) {
    if (parts[2][i] == '-') break;
    else if (parts[2][i] == 'K') info->castlingrights |= BB_RIGHTS_WCK;
    else if (parts[2][i] == 'Q') info->castlingrights |= BB_RIGHTS_WCQ;
    else if (parts[2][i] == 'k') info->castlingrights |= BB_RIGHTS_BCK;
    else if (parts[2][i] == 'q') info->castlingrights |= BB_RIGHTS_BCQ;
    else require(false, "FEN unknown symbol(s): %s\n", parts[2]);
  }
  if (parts_size > 4 && parts[4][0] != '-') info->halfmovecount = (uint8_t) string_to_long(parts[4]);
  derived_knowledge_compute(&b);
  if (parts[3][0] != '-') {
    /* only set ep if attacked by active sides pawns*/
    bits64 ep_sq = BITS_FROM_SQUARE(parsing_square_from_uci(parts[3]));
    bits64 pawn_attacks = board_get_pawns_attackmask(&b.side[b.active]);
    if (ep_sq & pawn_attacks) info->enpassant = ep_sq;
  }
  info->hash = zobrist_compute_hash(&b);
  b.side[WHITE].eval_phase = board_count_phase(&b.side[WHITE]);
  b.side[WHITE].eval_material = eval_count_material(&b.side[WHITE]);
  b.side[BLACK].eval_phase = board_count_phase(&b.side[BLACK]);
  b.side[BLACK].eval_material = eval_count_material(&b.side[BLACK]);
  b.side[WHITE].eval_mg_psqt = eval_count_psqt(&b.side[WHITE], &WHITE_MG_PIECETABLE);
  b.side[BLACK].eval_mg_psqt = eval_count_psqt(&b.side[BLACK], &BLACK_MG_PIECETABLE);
  b.side[WHITE].eval_eg_psqt = eval_count_psqt(&b.side[WHITE], &WHITE_EG_PIECETABLE);
  b.side[BLACK].eval_eg_psqt = eval_count_psqt(&b.side[BLACK], &BLACK_EG_PIECETABLE);
  return b;
}

Board parsing_from_fen(const char* fen) {
  char fenCopy[256];
  strcpy(fenCopy, fen);
  
  char* parts[6];
  
  int splitSize = strings_split(fenCopy, parts, " ");
  require(splitSize >= 4, "FEN was not comprised of at least 4 parts: '%s'\n", fen);
  return parsing_from_fen_parts(parts, splitSize);
}

void parsing_printbits_8by8(bits64 bits) {
  char boardString[64 + 64 + 8 + 1];
  char* cursor = &boardString[0];
  for (int j = 7; j >= 0; --j) {
    for (int i = 0; i < 8; ++i) {
      *cursor++ = BITS_TEST(bits, i + j * 8) ? '1' : '0';
      *cursor++ = ' ';
    }
    *cursor++ = '\n';
  }
  *cursor = '\0';
  IO_PRINT("%s\n", boardString);
}

void parsing_printbits_hex(bits64 bits) {
  IO_PRINT("0x%016" PRIx64 "\n", bits);
}