home / fornax / fornax-v4-0 / src / moving / makemove.hpp

makemove.hpp



//
//  makemove.hpp
//  fornax3
//
//  Created by Anders on 31/12/2020.
//

#ifndef makemove_h
#define makemove_h

#include "movegen.hpp"
#include "derived_knowledge.h"
#include "../parsing.hpp"
#include "../tt/zobrist.hpp"
#include "../eval/eval.hpp"
#include "../eval/eval_piecetables.h"
#include "../libs/require.h"

template<color active>
static constexpr void makemove_standard_set(Plyinfo* info,
                                            Side* side,
                                            piecetype piece,
                                            square sq) {
  info->hash ^= zobrist_get_piece<active>(piece, sq);
  BITS_CLEAR(side->pieces[piece], sq);
}

template<color active>
static constexpr void makemove_make_castling(Board* board, move mov) {
  constexpr const piecetable *a_mg_psqt = active ? &BLACK_MG_PIECETABLE : &WHITE_MG_PIECETABLE;
  constexpr const piecetable *a_eg_psqt = active ? &BLACK_EG_PIECETABLE : &WHITE_EG_PIECETABLE;
  constexpr move move_cq = active ? MOVE_BCQ : MOVE_WCQ;
  constexpr move move_ck = active ? MOVE_BCK : MOVE_WCK;
  
  Plyinfo* info = board_get_nextinfo(board);
  
  if (mov == move_cq) {
    constexpr square a = active ? 56 : 0;
    constexpr square d = active ? 59 : 3;
    
    board->piecemap[a] = NONE;
    BITS_CLEAR(board->side[active].pieces[ROOK], a);
    info->hash ^= zobrist_get_piece<active>(ROOK, a);
    board->side[active].eval_mg_psqt -= (*a_mg_psqt)[ROOK][a];
    board->side[active].eval_eg_psqt -= (*a_eg_psqt)[ROOK][a];
    
    board->piecemap[d] = ROOK;
    BITS_SET(board->side[active].pieces[ROOK], d);
    info->hash ^= zobrist_get_piece<active>(ROOK, d);
    board->side[active].eval_mg_psqt += (*a_mg_psqt)[ROOK][d];
    board->side[active].eval_eg_psqt += (*a_eg_psqt)[ROOK][d];
  }
  else if (mov == move_ck) {
    constexpr square f = active ? 61 : 5;
    constexpr square h = active ? 63 : 7;
    
    board->piecemap[h] = NONE;
    BITS_CLEAR(board->side[active].pieces[ROOK], h);
    info->hash ^= zobrist_get_piece<active>(ROOK, h);
    board->side[active].eval_mg_psqt -= (*a_mg_psqt)[ROOK][h];
    board->side[active].eval_eg_psqt -= (*a_eg_psqt)[ROOK][h];
    
    board->piecemap[f] = ROOK;
    BITS_SET(board->side[active].pieces[ROOK], f);
    info->hash ^= zobrist_get_piece<active>(ROOK, f);
    board->side[active].eval_mg_psqt += (*a_mg_psqt)[ROOK][f];
    board->side[active].eval_eg_psqt += (*a_eg_psqt)[ROOK][f];
  }
}

template<color active>
static constexpr void makemove_unmake_castling(Board* board, move mov) {
  constexpr color opp = COLOR_OPPOSITE(active);
  constexpr const piecetable *o_mg_psqt = opp ? &BLACK_MG_PIECETABLE : &WHITE_MG_PIECETABLE;
  constexpr const piecetable *o_eg_psqt = opp ? &BLACK_EG_PIECETABLE : &WHITE_EG_PIECETABLE;
  constexpr move move_cq = opp ? MOVE_BCQ : MOVE_WCQ;
  constexpr move move_ck = opp ? MOVE_BCK : MOVE_WCK;
  
  if (mov == move_cq) {
    constexpr square a = opp ? 56 : 0;
    constexpr square d = opp ? 59 : 3;
    
    board->piecemap[d] = NONE;
    BITS_CLEAR(board->side[opp].pieces[ROOK], d);
    board->side[opp].eval_mg_psqt -= (*o_mg_psqt)[ROOK][d];
    board->side[opp].eval_eg_psqt -= (*o_eg_psqt)[ROOK][d];
    
    board->piecemap[a] = ROOK;
    BITS_SET(board->side[opp].pieces[ROOK], a);
    board->side[opp].eval_mg_psqt += (*o_mg_psqt)[ROOK][a];
    board->side[opp].eval_eg_psqt += (*o_eg_psqt)[ROOK][a];
  }
  else if (mov == move_ck) {
    constexpr square f = opp ? 61 : 5;
    constexpr square h = opp ? 63 : 7;
    
    board->piecemap[f] = NONE;
    BITS_CLEAR(board->side[opp].pieces[ROOK], f);
    board->side[opp].eval_mg_psqt -= (*o_mg_psqt)[ROOK][f];
    board->side[opp].eval_eg_psqt -= (*o_eg_psqt)[ROOK][f];
    
    board->piecemap[h] = ROOK;
    BITS_SET(board->side[opp].pieces[ROOK], h);
    board->side[opp].eval_mg_psqt += (*o_mg_psqt)[ROOK][h];
    board->side[opp].eval_eg_psqt += (*o_eg_psqt)[ROOK][h];
  }
}

static constexpr void makemove_check_castling_rights(Plyinfo* info, bits64 bits, square sq) {
  if (info->castlingrights & bits) {
    info->castlingrights &= ~bits;
    info->hash ^= zobrist_get_castle(sq);
  }
}

template<color active>
static constexpr void makemove_remove_castling_rights(Plyinfo* info) {
  if (active == WHITE) {
    makemove_check_castling_rights(info, BB_RIGHTS_WCK, 7);
    makemove_check_castling_rights(info, BB_RIGHTS_WCQ, 0);
  }
  else {
    makemove_check_castling_rights(info, BB_RIGHTS_BCK, 63);
    makemove_check_castling_rights(info, BB_RIGHTS_BCQ, 56);
  }
}

static constexpr bool makemove_is_irreversible(piecetype piece, piecetype capture) {
  return capture != NONE || piece == PAWN;
}

#ifndef NDEBUG
static void makemove_check_state(const Board* board) {
  const Plyinfo* info = board_get_currinfo(board);
  if (info->hash != zobrist_compute_hash(board)) {
    parsing_printboard(board);
    printf("\n dyn %llu vs static %llu\n", info->hash, zobrist_compute_hash(board));
    assert(false);
  }
  uint8_t wdp = board->side[WHITE].eval_phase;
  uint8_t wsp = board_count_phase(&board->side[WHITE]);
  if (wdp != wsp) {
    parsing_printboard(board);
    printf("\n white phase dyn %d vs static %d\n", wdp, wsp);
    assert(false);
  }
  uint8_t bdp = board->side[BLACK].eval_phase;
  uint8_t bsp = board_count_phase(&board->side[BLACK]);
  if (bdp != bsp) {
    parsing_printboard(board);
    printf("\n black phase dyn %d vs static %d\n", bdp, bsp);
    assert(false);
  }
  eval wde = board->side[WHITE].eval_material;
  eval wse = eval_count_material(&board->side[WHITE]);
  if (wde != wse) {
    parsing_printboard(board);
    printf("\n white material dyn %d vs static %d\n", wde, wse);
    assert(false);
  }
  eval bde = board->side[BLACK].eval_material;
  eval bse = eval_count_material(&board->side[BLACK]);
  if (bde != bse) {
    parsing_printboard(board);
    printf("\n black material dyn %d vs static %d\n", bde, bse);
    assert(false);
  }
  eval wdmg = board->side[WHITE].eval_mg_psqt;
  eval wsmg = eval_count_psqt(&board->side[WHITE], &WHITE_MG_PIECETABLE);
  if (wdmg != wsmg) {
    parsing_printboard(board);
    printf("\n white mg psqt dyn %d vs static %d\n", wdmg, wsmg);
    assert(false);
  }
  eval bdmg = board->side[BLACK].eval_mg_psqt;
  eval bsmg = eval_count_psqt(&board->side[BLACK], &BLACK_MG_PIECETABLE);
  if (bdmg != bsmg) {
    parsing_printboard(board);
    printf("\n black mg psqt dyn %d vs static %d\n", bdmg, bsmg);
    assert(false);
  }
  eval wdeg = board->side[WHITE].eval_eg_psqt;
  eval wseg = eval_count_psqt(&board->side[WHITE], &WHITE_EG_PIECETABLE);
  if (wdeg != wseg) {
    parsing_printboard(board);
    printf("\n white eg psqt dyn %d vs static %d\n", wdeg, wseg);
    assert(false);
  }
  eval bdeg = board->side[BLACK].eval_eg_psqt;
  eval bseg = eval_count_psqt(&board->side[BLACK], &BLACK_EG_PIECETABLE);
  if (bdeg != bseg) {
    parsing_printboard(board);
    printf("\n black eg psqt dyn %d vs static %d\n", bdeg, bseg);
    assert(false);
  }
}
#endif

template<color active>
static constexpr void makemove_make(Board* board, move mov) {
  constexpr color opp = COLOR_OPPOSITE(active);
  constexpr const piecetable *a_mg_psqt = active ? &BLACK_MG_PIECETABLE : &WHITE_MG_PIECETABLE;
  constexpr const piecetable *a_eg_psqt = active ? &BLACK_EG_PIECETABLE : &WHITE_EG_PIECETABLE;
  constexpr const piecetable *o_mg_psqt = opp ? &BLACK_MG_PIECETABLE  : &WHITE_MG_PIECETABLE;
  constexpr const piecetable *o_eg_psqt = opp ? &BLACK_EG_PIECETABLE : &WHITE_EG_PIECETABLE;
  
  Side *a_side = &board->side[active];
  Side *o_side = &board->side[opp];
  
  const square origin = MOVE_GET_ORIGIN(mov);
  const square dest = MOVE_GET_DEST(mov);
  
  const piecetype promotion = MOVE_GET_PROMOTION(mov);
  const piecetype originpiece = board->piecemap[origin];
  assert(originpiece != NONE);
  const piecetype destpiece = promotion ? promotion : originpiece;
  assert(destpiece != NONE);
  const piecetype capture = board->piecemap[dest];
  assert(capture != KING);
  
  const Plyinfo *currinfo = board_get_currinfo(board);
  Plyinfo *nextinfo = board_get_nextinfo(board);
  nextinfo->castlingrights = currinfo->castlingrights;
  nextinfo->capture = capture;
  nextinfo->hash = currinfo->hash ^ zobrist_get_switchmove();
  makemove_check_castling_rights(nextinfo, BITS_FROM_SQUARE(dest), dest);
  makemove_check_castling_rights(nextinfo, BITS_FROM_SQUARE(origin), origin);
  
  nextinfo->enpassant = 0;
  if (currinfo->enpassant) {
    nextinfo->hash ^= zobrist_get_enpassant(currinfo->enpassant);
  }
  
  /* clear origin square */
  board->piecemap[origin] = NONE;
  BITS_CLEAR(a_side->pieces[originpiece], origin);
  nextinfo->hash ^= zobrist_get_piece<active>(originpiece, origin);
  a_side->eval_mg_psqt -= (*a_mg_psqt)[originpiece][origin];
  a_side->eval_eg_psqt -= (*a_eg_psqt)[originpiece][origin];
  
  /* clear captured */
  if (capture != NONE) {
    BITS_CLEAR(o_side->pieces[capture], dest);
    o_side->eval_material -= EVAL_PIECE[capture];
    o_side->eval_phase -= EVAL_PHASE[capture];
    nextinfo->hash ^= zobrist_get_piece<opp>(capture, dest);
    o_side->eval_mg_psqt -= (*o_mg_psqt)[capture][dest];
    o_side->eval_eg_psqt -= (*o_eg_psqt)[capture][dest];
  }
  
  /* set dest square */
  board->piecemap[dest] = destpiece;
  BITS_SET(a_side->pieces[destpiece], dest);
  nextinfo->hash ^= zobrist_get_piece<active>(destpiece, dest);
  a_side->eval_mg_psqt += (*a_mg_psqt)[destpiece][dest];
  a_side->eval_eg_psqt += (*a_eg_psqt)[destpiece][dest];
  
  if (promotion) {
      a_side->eval_material += (eval) (EVAL_PIECE[promotion] - EVAL_PIECE_PAWNS);
      a_side->eval_phase += EVAL_PHASE[promotion];
  }

  if (destpiece == KING) {
    makemove_make_castling<active>(board, mov);
    makemove_remove_castling_rights<active>(nextinfo);
  }
  else if (destpiece == PAWN) {
    if (BITS_TEST(currinfo->enpassant, dest)) {
      constexpr int8_t modifier = active ? 8 : -8;
      square ep_capture_square = (square) (dest + modifier);
      
      board->piecemap[ep_capture_square] = NONE;
      BITS_CLEAR(o_side->pieces[PAWN], ep_capture_square);
      o_side->eval_material -= EVAL_PIECE_PAWNS;
      nextinfo->hash ^= zobrist_get_piece<opp>(PAWN, ep_capture_square);
      o_side->eval_mg_psqt -= (*o_mg_psqt)[PAWN][ep_capture_square];
      o_side->eval_eg_psqt -= (*o_eg_psqt)[PAWN][ep_capture_square];
    }
  }
  
  derived_knowledge_compute(board);
  
  /* check if pawn was double push for setting ep flag*/
  if (destpiece == PAWN) {
    constexpr int8_t modifier = active ? -8 : 8;
    int length = active ? origin - dest : dest - origin;
    if (length == 16) {
      bits64 ep_sq = BITS_FROM_SQUARE((origin + modifier));
      
      /* only set ep if a capture is possible (not checking if pawn is pinned) */
      bits64 o_pawn_attacks = board_get_pawns_attackmask(o_side);
      bits64 is_attacked = ep_sq & o_pawn_attacks;
      if (is_attacked) {
        nextinfo->enpassant = ep_sq;
        nextinfo->hash ^= zobrist_get_enpassant(ep_sq);
      }
    }
  }
  board->active = opp;
  board->ply++;
  nextinfo->halfmovecount = makemove_is_irreversible(originpiece, capture) ? 0 : currinfo->halfmovecount + 1;

  
#ifndef NDEBUG
  makemove_check_state(board);
#endif
}

template<color active>
static constexpr void makemove_takeback(Board* board, move mov) {
  constexpr color opp = COLOR_OPPOSITE(active);
  constexpr const piecetable *a_mg_psqt = active ? &BLACK_MG_PIECETABLE : &WHITE_MG_PIECETABLE;
  constexpr const piecetable *a_eg_psqt = active ? &BLACK_EG_PIECETABLE : &WHITE_EG_PIECETABLE;
  constexpr const piecetable *o_mg_psqt = opp ? &BLACK_MG_PIECETABLE  : &WHITE_MG_PIECETABLE;
  constexpr const piecetable *o_eg_psqt = opp ? &BLACK_EG_PIECETABLE : &WHITE_EG_PIECETABLE;
  
  Side *a_side = &board->side[active];
  Side *o_side = &board->side[opp];
  
  const Plyinfo *currinfo = board_get_currinfo(board);
  const Plyinfo *previnfo =  board_get_previnfo(board);
  const square origin = MOVE_GET_ORIGIN(mov);
  const square dest = MOVE_GET_DEST(mov);
  
  const piecetype promotion = MOVE_GET_PROMOTION(mov);
  const piecetype destpiece = board->piecemap[dest];
  assert(destpiece != NONE);
  const piecetype originpiece = promotion ? PAWN : destpiece;
  assert(originpiece != NONE);
  const piecetype capture = currinfo->capture;
  assert(capture != KING);
  
  /* clear destination square */
  BITS_CLEAR(o_side->pieces[destpiece], dest);
  board->piecemap[dest] = NONE;
  o_side->eval_mg_psqt -= (*o_mg_psqt)[destpiece][dest];
  o_side->eval_eg_psqt -= (*o_eg_psqt)[destpiece][dest];
  
  /* set captured */
  if (capture != NONE) {
    board->piecemap[dest] = capture;
    a_side->eval_material += EVAL_PIECE[capture];
    a_side->eval_phase += EVAL_PHASE[capture];
    BITS_SET(a_side->pieces[capture], dest);
    a_side->eval_mg_psqt += (*a_mg_psqt)[capture][dest];
    a_side->eval_eg_psqt += (*a_eg_psqt)[capture][dest];
  }
  
  /* set origin square */
  board->piecemap[origin] = originpiece;
  BITS_SET(o_side->pieces[originpiece], origin);
  o_side->eval_mg_psqt += (*o_mg_psqt)[originpiece][origin];
  o_side->eval_eg_psqt += (*o_eg_psqt)[originpiece][origin];

  if (promotion) {
    o_side->eval_material -= (eval) (EVAL_PIECE[promotion] - EVAL_PIECE_PAWNS);
    o_side->eval_phase -= EVAL_PHASE[promotion];
  }
  
  if (destpiece == KING) {
    makemove_unmake_castling<active>(board, mov);
  }
  else if (destpiece == PAWN && BITS_TEST(previnfo->enpassant, dest)) {
    constexpr int8_t modifier = active ? -8 : 8;
    square ep_capture_square = (square) (dest + modifier);
    
    board->piecemap[ep_capture_square] = PAWN;
    BITS_SET(a_side->pieces[PAWN], ep_capture_square);
    a_side->eval_material += EVAL_PIECE_PAWNS;
    a_side->eval_mg_psqt += (*a_mg_psqt)[PAWN][ep_capture_square];
    a_side->eval_eg_psqt += (*a_eg_psqt)[PAWN][ep_capture_square];
  }
  
  board->active = opp;
  board->ply--;

#ifndef NDEBUG
  makemove_check_state(board);
#endif
}

template<color active>
static constexpr void makemove_make_null(Board* board) {
  const Plyinfo *currinfo = board_get_currinfo(board);
  Plyinfo *nextinfo =  board_get_nextinfo(board);
  nextinfo->hash = currinfo->hash ^ zobrist_get_switchmove();
  nextinfo->castlingrights = currinfo->castlingrights;
  nextinfo->capture = NONE;
  nextinfo->enpassant = 0;
  nextinfo->halfmovecount = 0;
  if (currinfo->enpassant) {
    nextinfo->hash ^= zobrist_get_enpassant(currinfo->enpassant);
  }
  
  constexpr color opp = COLOR_OPPOSITE(active);
  board->active = opp;
  board->ply++;
}

template<color active>
static constexpr void makemove_takeback_null(Board* board) {
  constexpr color opp = COLOR_OPPOSITE(active);
  board->active = opp;
  board->ply--;
}

static inline void makemove_makef(Board* board, move mov) {
  require(mov != 0, "invalid move");
  return board->active
  ? makemove_make<BLACK>(board, mov)
  : makemove_make<WHITE>(board, mov);
}

static inline void makemove_takebackf(Board* board, move mov) {
  require(mov != 0, "invalid move");
  return board->active
  ? makemove_takeback<BLACK>(board, mov)
  : makemove_takeback<WHITE>(board, mov);
}

#endif /* makemove_h */