//
// 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 */