diff --git a/src/move-do.c b/src/move-do.c index 16cf79c..9c982a0 100644 --- a/src/move-do.c +++ b/src/move-do.c @@ -25,6 +25,20 @@ #include "move-do.h" #include "hash.h" +/** + * sq_castle - castling rights per square. + * + * position castling rights mask has to be AND'ed with this array 'from' and 'to'. + */ +castle_rights_t sq_castle[64] = { + [A1] = ~CASTLE_WQ, [B1 ... D1] = ~CASTLE_NONE, + [E1] = ~CASTLE_W, [F1 ... G1] = ~CASTLE_NONE, + [H1] = ~CASTLE_WK, [A2 ... H7] = ~CASTLE_NONE, + [A8] = ~CASTLE_BQ, [B8 ... D8] = ~CASTLE_NONE, + [E8] = ~CASTLE_B, [F8 ... G8] = ~CASTLE_NONE, + [H8] = ~CASTLE_BK, +}; + /** * move_do() - do move. * @pos: &pos_t position @@ -60,9 +74,8 @@ pos_t *move_do(pos_t *pos, const move_t move, state_t *state) *state = pos->state; /* save irreversible changes */ - /* update key: switch turn, reset castling and ep */ + /* update key: switch turn, reset ep */ key ^= zobrist_turn; - key ^= zobrist_castling[pos->castle]; key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; ++pos->clock_50; @@ -74,80 +87,69 @@ pos_t *move_do(pos_t *pos, const move_t move, state_t *state) bug_on(COLOR(piece) != us); - if (is_promotion(move)) { - bug_on(sq_rank(to) != sq_rel_rank(RANK_8, us)); - new_piece = MAKE_PIECE(move_promoted(move), us); - } - - if (captured != EMPTY) { + /* special moves: captures, pawn double push, en-passant, castling. + * All these moves are exclusive (so we use if ... else) + */ + if (captured != EMPTY) { /* capture: remove piece */ pos->clock_50 = 0; bug_on(pos->board[to] == EMPTY || COLOR(pos->captured) != them); key ^= zobrist_pieces[captured][to]; - pos_clr_sq(pos, to); /* clear square */ - } else if (is_castle(move)) { /* handle rook move */ - square_t rookfrom, rookto; - if (to > from) { - rookfrom = sq_rel(H1, us); - rookto = sq_rel(F1, us); - } else { - rookfrom = sq_rel(A1, us); - rookto = sq_rel(D1, us); - } - key ^= zobrist_pieces[pos->board[rookfrom]][rookto] ^ - zobrist_pieces[pos->board[rookfrom]][rookfrom]; - pos_set_sq(pos, rookto, pos->board[rookfrom]); - pos_clr_sq(pos, rookfrom); - pos->castle = clr_castle(pos->castle, us); - } else if (ptype == PAWN) { /* pawn non capture or e.p. */ + pos_clr_sq(pos, to); + } else if (ptype == PAWN) { pos->clock_50 = 0; - if (from + up + up == to) { /* if pawn double push, set e.p. */ + if (from + up + up == to) { /* double push: set e.p. */ square_t ep = from + up; + /* check that opponent can e.p. */ if (bb_pawn_attacks[us][ep] & pos->bb[them][PAWN]) { pos->en_passant = ep; key ^= zobrist_ep[EP_ZOBRIST_IDX(pos->en_passant)]; } - } else if (is_enpassant(move)) { /* clear grabbed pawn */ + } else if (is_enpassant(move)) { /* e.p.: clear grabbed pawn */ square_t grabbed = to - up; piece_t pc = pos->board[grabbed]; key ^= zobrist_pieces[pc][grabbed]; pos_clr_sq(pos, grabbed); } + } else if (is_castle(move)) { /* castling: handle rook move */ + square_t rookfrom, rookto; + if (to > from) { /* o-o */ + rookfrom = to + 1; + rookto = to - 1; + } else { /* o-o-o */ + rookfrom = to - 2; + rookto = to + 1; + } + key ^= zobrist_pieces[pos->board[rookfrom]][rookto] ^ + zobrist_pieces[pos->board[rookfrom]][rookfrom]; + pos_set_sq(pos, rookto, pos->board[rookfrom]); + pos_clr_sq(pos, rookfrom); + } + + /* common part: captures and non captures + * We could improve slightly, as en-passant and double push will make + * the following tests. + * Improvement could be to add promotion test and king test twice above. + * (in capture and after ). + */ + if (is_promotion(move)) { + bug_on(sq_rank(to) != sq_rel_rank(RANK_8, us)); + new_piece = MAKE_PIECE(move_promoted(move), us); } key ^= zobrist_pieces[piece][from] ^ zobrist_pieces[new_piece][to]; - pos_clr_sq(pos, from); /* clear "from" and set "to" */ + pos_clr_sq(pos, from); pos_set_sq(pos, to, new_piece); - if (ptype == KING) - pos->king[us] = to; - - /* update castling flags - * As we always consider flags are valid, we : - * - adjust our flags if relative from is "E1", "A1", H1" - * - adjust opp flags if relative to if "A8", H8" - */ - if (can_castle(pos->castle, us)) { /* do we save time with this test ? */ - square_t rel_e1 = sq_rel(E1, us); - square_t rel_a1 = sq_rel(A1, us); - square_t rel_h1 = sq_rel(H1, us); - if (from == rel_e1) - pos->castle = clr_castle(pos->castle, us); - else if (from == rel_a1) - pos->castle = clr_ooo(pos->castle, us); - else if (from == rel_h1) - pos->castle = clr_oo(pos->castle, us); - } - if (can_castle(pos->castle, them)) { - square_t rel_a8 = sq_rel(A8, us); - square_t rel_h8 = sq_rel(H8, us); - if (to == rel_a8) - pos->castle = clr_ooo(pos->castle, them); - else if (to == rel_h8) - pos->castle = clr_oo(pos->castle, them); - } - - /* update castling rights key */ + /* update castling flags */ key ^= zobrist_castling[pos->castle]; + pos->castle &= sq_castle[from] & sq_castle[to]; + key ^= zobrist_castling[pos->castle]; + + //if (ptype == KING) { + // pos->king[us] = to; + //} else + /* do this always, cheaper than test on K move */ + pos->king[us] = ctz64(pos->bb[us][KING]); pos->key = key; @@ -180,33 +182,43 @@ pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state) piece_t piece = pos->board[to]; int up = sq_up(them); + /* common part: captures and non captures + * We could improve slightly, as en-passant and double push will make + * the following tests. + * Improvement could be to add promotion test and king test twice above. + * (in capture and after ). + */ if (is_promotion(move)) piece = MAKE_PIECE(PAWN, us); - pos_clr_sq(pos, to); /* always clear "to" ... */ - pos_set_sq(pos, from, piece); /* ... and set "from" */ + pos_clr_sq(pos, to); + pos_set_sq(pos, from, piece); - if (PIECE(piece) == KING) - pos->king[us] = from; - - if (pos->captured != EMPTY) { - pos_set_sq(pos, to, pos->captured); /* restore captured piece */ - } else if (is_castle(move)) { /* make reverse rook move */ + /* special moves: capture, king (+ castling), en-passant + */ + if (pos->captured != EMPTY) { /* captured: restore piece */ + pos_set_sq(pos, to, pos->captured); + } else if (is_castle(move)) { /* castle: rook move */ square_t rookfrom, rookto; - if (to > from) { - rookfrom = sq_rel(F1, us); - rookto = sq_rel(H1, us); - } else { - rookfrom = sq_rel(D1, us); - rookto = sq_rel(A1, us); + if (to > from) { /* o-o */ + rookfrom = to - 1; + rookto = to + 1; + } else { /* o-o-o */ + rookfrom = to + 1; + rookto = to - 2; } pos_set_sq(pos, rookto, pos->board[rookfrom]); pos_clr_sq(pos, rookfrom); - } else if (is_enpassant(move)) { /* restore grabbed pawn */ + } else if (is_enpassant(move)) { /* e.p.: restore grabbed pawn */ square_t grabbed = to + up; pos_set_sq(pos, grabbed, MAKE_PIECE(PAWN, them)); } + /* do this always, cheaper than test on K move */ + pos->king[us] = ctz64(pos->bb[us][KING]); + //if (PIECE(piece) == KING) + // pos->king[us] = from; + pos->state = *state; /* restore irreversible changes */ pos->turn = us; return pos; @@ -284,7 +296,7 @@ pos_t *move_do_alt(pos_t *pos, const move_t move, state_t *state) } key ^= zobrist_pieces[piece][from] ^ zobrist_pieces[new_piece][to]; - pos_clr_sq(pos, from); /* clear "from" and set "to" */ + pos_clr_sq(pos, from); pos_set_sq(pos, to, new_piece); if (ptype == KING) diff --git a/src/move-do.h b/src/move-do.h index 17ca7dd..3fc5440 100644 --- a/src/move-do.h +++ b/src/move-do.h @@ -14,8 +14,11 @@ #ifndef MOVE_DO_H #define MOVE_DO_H +#include "chessdefs.h" #include "position.h" +extern castle_rights_t sq_castle[64]; /* to adjust castling rights */ + pos_t *move_do(pos_t *pos, const move_t move, state_t *state); pos_t *move_undo(pos_t *pos, const move_t move, const state_t *state);