home / fornax-v3-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 "../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 square) {
  info->hash ^= zobrist_get_piece<active>(piece, square);
  BITS_CLEAR(side->pieces[piece], square);
}

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 square) {
  if (info->castlingrights & bits) {
    info->castlingrights &= ~bits;
    info->hash ^= zobrist_get_castle(square);
  }
}

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);
  }
}

template<color col>
static constexpr bits64 makemove_piece_mask(const Board* board) {
  const bits64* pieces = board->side[col].pieces;
  return pieces[KING] | pieces[QUEEN] | pieces[ROOK] | pieces[BISHOP] | pieces[KNIGHT] | pieces[PAWN];
}

template <piecetype type, color active>
static constexpr void makemove_poplulate_maps(bits64* mask, Attackmap** cursor, bits64 pieces, bits64 occupants) {
  while (pieces) {
    square s = bits_pop_trailing(&pieces);
    bits64 bits = movegen_get_attacks<type>(s, occupants);
    *mask |= bits;
    *(*cursor)++ = {type, bits};
  }
}

template<color col>
static constexpr void makemove_populate_attacks(Board* board) {
  constexpr color opp = COLOR_OPPOSITE(col);
  Side* col_side = &board->side[col];
  Side* opp_side = &board->side[opp];
  bits64 occupants = (col_side->piecemask | opp_side->piecemask) ^ opp_side->pieces[KING];
  Attackmap* cursor = col_side->attackkmaps;
  
  bits64* pieces = col_side->pieces;
  
  bits64 up =  bits_shift<col ? S : N>(pieces[PAWN]);
  bits64 east = (bits_shift_guarded<W>(up));
  bits64 west = (bits_shift_guarded<E>(up));
  col_side->attackmask = east | west;
  
  /* set king first for movepick use*/
  makemove_poplulate_maps<KING, col>(&col_side->attackmask, &cursor, pieces[KING], occupants);
  *cursor++ = {PAWN, east};
  *cursor++ = {PAWN, west};
  makemove_poplulate_maps<QUEEN, col>(&col_side->attackmask, &cursor, pieces[QUEEN], occupants);
  makemove_poplulate_maps<ROOK, col>(&col_side->attackmask, &cursor, pieces[ROOK], occupants);
  makemove_poplulate_maps<BISHOP, col>(&col_side->attackmask, &cursor, pieces[BISHOP], occupants);
  makemove_poplulate_maps<KNIGHT, col>(&col_side->attackmask, &cursor, pieces[KNIGHT], occupants);
  
  col_side->attackmapsize = (uint8_t) (cursor - col_side->attackkmaps);
  assert(col_side->attackmapsize <= 16);
}

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 = eval_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 = eval_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

static constexpr void makemove_genmaps(Board* board) {
  board->side[WHITE].piecemask = makemove_piece_mask<WHITE>(board);
  board->side[BLACK].piecemask = makemove_piece_mask<BLACK>(board);
  makemove_populate_attacks<WHITE>(board);
  makemove_populate_attacks<BLACK>(board);
}


template<color active>
static constexpr void makemove_make(Board* board, move move) {
  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(move);
  const square dest = MOVE_GET_DEST(move);
  
  const piecetype promotion = MOVE_GET_PROMOTION(move);
  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_PIECE[promotion] - EVAL_PIECE_PAWNS;
      a_side->eval_phase += EVAL_PHASE[promotion];
  }

  if (destpiece == KING) {
    makemove_make_castling<active>(board, move);
    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];
    }
  }
  
  makemove_genmaps(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 = o_side->attackkmaps[1].bits | o_side->attackkmaps[2].bits;
      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++;
  board->halfmovecount = makemove_is_irreversible(originpiece, capture) ? 0 : board->halfmovecount + 1;

  
#ifndef NDEBUG
  makemove_check_state(board);
#endif
}

template<color active>
static constexpr void makemove_takeback(Board* board, move move) {
  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(move);
  const square dest = MOVE_GET_DEST(move);
  
  const piecetype promotion = MOVE_GET_PROMOTION(move);
  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_PIECE[promotion] - EVAL_PIECE_PAWNS;
    o_side->eval_phase -= EVAL_PHASE[promotion];
  }
  
  if (destpiece == KING) {
    makemove_unmake_castling<active>(board, move);
  }
  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--;
  board->halfmovecount = makemove_is_irreversible(originpiece, capture) ? 0 : board->halfmovecount - 1;
  
#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;
  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 move) {
  require(move != 0, "invalid move");
  return board->active
  ? makemove_make<BLACK>(board, move)
  : makemove_make<WHITE>(board, move);
}

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

#endif /* makemove_h */