#include "position.h"
#include "psqtab.h"
#include "rkiss.h"
+#include "thread.h"
#include "tt.h"
#include "ucioption.h"
| (attacks_from<KING>(s) & pieces(KING));
}
+Bitboard Position::attackers_to(Square s, Bitboard occ) const {
+
+ return (attacks_from<PAWN>(s, BLACK) & pieces(PAWN, WHITE))
+ | (attacks_from<PAWN>(s, WHITE) & pieces(PAWN, BLACK))
+ | (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
+ | (rook_attacks_bb(s, occ) & pieces(ROOK, QUEEN))
+ | (bishop_attacks_bb(s, occ) & pieces(BISHOP, QUEEN))
+ | (attacks_from<KING>(s) & pieces(KING));
+}
+
/// Position::attacks_from() computes a bitboard of all attacks
/// of a given piece put in a given square.
assert(move_is_ok(m));
assert(pinned == pinned_pieces(side_to_move()));
- // Castling moves are checked for legality during move generation.
- if (move_is_castle(m))
- return true;
+ Color us = side_to_move();
+ Square from = move_from(m);
+
+ assert(color_of_piece_on(from) == us);
+ assert(piece_on(king_square(us)) == make_piece(us, KING));
// En passant captures are a tricky special case. Because they are
// rather uncommon, we do it simply by testing whether the king is attacked
// after the move is made
if (move_is_ep(m))
{
- Color us = side_to_move();
Color them = opposite_color(us);
- Square from = move_from(m);
Square to = move_to(m);
Square capsq = make_square(square_file(to), square_rank(from));
Square ksq = king_square(us);
&& !(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, them));
}
- Color us = side_to_move();
- Square from = move_from(m);
-
- assert(color_of_piece_on(from) == us);
- assert(piece_on(king_square(us)) == make_piece(us, KING));
-
// If the moving piece is a king, check whether the destination
- // square is attacked by the opponent.
+ // square is attacked by the opponent. Castling moves are checked
+ // for legality during move generation.
if (type_of_piece_on(from) == KING)
- return !(attackers_to(move_to(m)) & pieces_of_color(opposite_color(us)));
+ return move_is_castle(m) || !(attackers_to(move_to(m)) & pieces_of_color(opposite_color(us)));
// A non-king move is legal if and only if it is not pinned or it
// is moving along the ray towards or away from the king.
}
-/// Position::pl_move_is_evasion() tests whether a pseudo-legal move is a legal evasion
+/// Position::move_is_legal() takes a position and a (not necessarily pseudo-legal)
+/// move and tests whether the move is legal. This version is not very fast and
+/// should be used only in non time-critical paths.
-bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
-{
- assert(is_check());
+bool Position::move_is_pl_full(const Move m) const {
- Color us = side_to_move();
+ MoveStack mlist[MAX_MOVES];
+ MoveStack *cur, *last = generate<MV_PSEUDO_LEGAL>(*this, mlist);
+
+ for (cur = mlist; cur != last; cur++)
+ if (cur->move == m)
+ return true;
+
+ return false;
+}
+
+
+/// Fast version of Position::move_is_legal() that takes a position a move and
+/// a bitboard of pinned pieces as input, and tests whether the move is legal.
+
+bool Position::move_is_pl(const Move m) const {
+
+ assert(is_ok());
+
+ Color us = sideToMove;
+ Color them = opposite_color(sideToMove);
Square from = move_from(m);
Square to = move_to(m);
+ Piece pc = piece_on(from);
- // King moves and en-passant captures are verified in pl_move_is_legal()
- if (type_of_piece_on(from) == KING || move_is_ep(m))
- return pl_move_is_legal(m, pinned);
+ // Use a slower but simpler function for uncommon cases
+ if (move_is_special(m))
+ return move_is_pl_full(m);
- Bitboard target = checkers();
- Square checksq = pop_1st_bit(&target);
+ // Is not a promotion, so promotion piece must be empty
+ if (move_promotion_piece(m) - 2 != PIECE_TYPE_NONE)
+ return false;
+
+ // If the from square is not occupied by a piece belonging to the side to
+ // move, the move is obviously not legal.
+ if (pc == PIECE_NONE || color_of_piece(pc) != us)
+ return false;
- if (target) // double check ?
+ // The destination square cannot be occupied by a friendly piece
+ if (color_of_piece_on(to) == us)
return false;
- // Our move must be a blocking evasion or a capture of the checking piece
- target = squares_between(checksq, king_square(us)) | checkers();
- return bit_is_set(target, to) && pl_move_is_legal(m, pinned);
+ // Handle the special case of a pawn move
+ if (type_of_piece(pc) == PAWN)
+ {
+ // Move direction must be compatible with pawn color
+ int direction = to - from;
+ if ((us == WHITE) != (direction > 0))
+ return false;
+
+ // We have already handled promotion moves, so destination
+ // cannot be on the 8/1th rank.
+ if (square_rank(to) == RANK_8 || square_rank(to) == RANK_1)
+ return false;
+
+ // Proceed according to the square delta between the origin and
+ // destination squares.
+ switch (direction)
+ {
+ case DELTA_NW:
+ case DELTA_NE:
+ case DELTA_SW:
+ case DELTA_SE:
+ // Capture. The destination square must be occupied by an enemy
+ // piece (en passant captures was handled earlier).
+ if (color_of_piece_on(to) != them)
+ return false;
+
+ // From and to files must be one file apart, avoids a7h5
+ if (abs(square_file(from) - square_file(to)) != 1)
+ return false;
+ break;
+
+ case DELTA_N:
+ case DELTA_S:
+ // Pawn push. The destination square must be empty.
+ if (!square_is_empty(to))
+ return false;
+ break;
+
+ case DELTA_NN:
+ // Double white pawn push. The destination square must be on the fourth
+ // rank, and both the destination square and the square between the
+ // source and destination squares must be empty.
+ if ( square_rank(to) != RANK_4
+ || !square_is_empty(to)
+ || !square_is_empty(from + DELTA_N))
+ return false;
+ break;
+
+ case DELTA_SS:
+ // Double black pawn push. The destination square must be on the fifth
+ // rank, and both the destination square and the square between the
+ // source and destination squares must be empty.
+ if ( square_rank(to) != RANK_5
+ || !square_is_empty(to)
+ || !square_is_empty(from + DELTA_S))
+ return false;
+ break;
+
+ default:
+ return false;
+ }
+ }
+ else if (!bit_is_set(attacks_from(pc, from), to))
+ return false;
+
+ if (in_check())
+ {
+ // In case of king moves under check we have to remove king so to catch
+ // as invalid moves like b1a1 when opposite queen is on c1.
+ if (type_of_piece_on(from) == KING)
+ {
+ Bitboard b = occupied_squares();
+ clear_bit(&b, from);
+ if (attackers_to(move_to(m), b) & pieces_of_color(opposite_color(us)))
+ return false;
+ }
+ else
+ {
+ Bitboard target = checkers();
+ Square checksq = pop_1st_bit(&target);
+
+ if (target) // double check ? In this case a king move is required
+ return false;
+
+ // Our move must be a blocking evasion or a capture of the checking piece
+ target = squares_between(checksq, king_square(us)) | checkers();
+ if (!bit_is_set(target, move_to(m)))
+ return false;
+ }
+ }
+
+ return true;
}
-/// Position::move_is_check() tests whether a pseudo-legal move is a check
+/// Position::move_gives_check() tests whether a pseudo-legal move is a check
-bool Position::move_is_check(Move m) const {
+bool Position::move_gives_check(Move m) const {
- return move_is_check(m, CheckInfo(*this));
+ return move_gives_check(m, CheckInfo(*this));
}
-bool Position::move_is_check(Move m, const CheckInfo& ci) const {
+bool Position::move_gives_check(Move m, const CheckInfo& ci) const {
assert(is_ok());
assert(move_is_ok(m));
}
-/// Position::do_setup_move() makes a permanent move on the board.
-/// It should be used when setting up a position on board.
-/// You can't undo the move.
+/// Position::do_setup_move() makes a permanent move on the board. It should
+/// be used when setting up a position on board. You can't undo the move.
void Position::do_setup_move(Move m) {
startPosPlyCounter++;
// Our StateInfo newSt is about going out of scope so copy
- // its content inside pos before it disappears.
+ // its content before it disappears.
detach();
}
+
/// Position::do_move() makes a move, and saves all information necessary
-/// to a StateInfo object. The move is assumed to be legal.
-/// Pseudo-legal moves should be filtered out before this function is called.
+/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
+/// moves should be filtered out before this function is called.
void Position::do_move(Move m, StateInfo& newSt) {
CheckInfo ci(*this);
- do_move(m, newSt, ci, move_is_check(m, ci));
+ do_move(m, newSt, ci, move_gives_check(m, ci));
}
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
// Update pawn hash key and prefetch in L1/L2 cache
st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
- prefetchPawn(st->pawnKey, threadID);
// Set en passant square, only if moved pawn can be captured
if ((to ^ from) == 16)
}
}
+ // Prefetch pawn and material hash tables
+ Threads[threadID].pawnTable.prefetch(st->pawnKey);
+ Threads[threadID].materialTable.prefetch(st->materialKey);
+
// Update incremental scores
st->value += pst_delta(piece, from, to);
void Position::do_null_move(StateInfo& backupSt) {
assert(is_ok());
- assert(!is_check());
+ assert(!in_check());
// Back up the information necessary to undo the null move to the supplied
// StateInfo object.
void Position::undo_null_move() {
assert(is_ok());
- assert(!is_check());
+ assert(!in_check());
// Restore information from the our backup StateInfo object
StateInfo* backupSt = st->previous;
/// move, and one which takes a 'from' and a 'to' square. The function does
/// not yet understand promotions captures.
-int Position::see(Move m) const {
-
- assert(move_is_ok(m));
- return see(move_from(m), move_to(m));
-}
-
int Position::see_sign(Move m) const {
assert(move_is_ok(m));
if (midgame_value_of_piece_on(to) >= midgame_value_of_piece_on(from))
return 1;
- return see(from, to);
+ return see(m);
}
-int Position::see(Square from, Square to) const {
+int Position::see(Move m) const {
+ Square from, to;
Bitboard occupied, attackers, stmAttackers, b;
int swapList[32], slIndex = 1;
PieceType capturedType, pt;
Color stm;
- assert(square_is_ok(from));
- assert(square_is_ok(to));
-
- capturedType = type_of_piece_on(to);
+ assert(move_is_ok(m));
- // King cannot be recaptured
- if (capturedType == KING)
- return seeValues[capturedType];
+ // As castle moves are implemented as capturing the rook, they have
+ // SEE == RookValueMidgame most of the times (unless the rook is under
+ // attack).
+ if (move_is_castle(m))
+ return 0;
+ from = move_from(m);
+ to = move_to(m);
+ capturedType = type_of_piece_on(to);
occupied = occupied_squares();
// Handle en passant moves
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it.
clear_bit(&occupied, from);
- attackers = (rook_attacks_bb(to, occupied) & pieces(ROOK, QUEEN))
- | (bishop_attacks_bb(to, occupied)& pieces(BISHOP, QUEEN))
- | (attacks_from<KNIGHT>(to) & pieces(KNIGHT))
- | (attacks_from<KING>(to) & pieces(KING))
- | (attacks_from<PAWN>(to, WHITE) & pieces(PAWN, BLACK))
- | (attacks_from<PAWN>(to, BLACK) & pieces(PAWN, WHITE));
+ attackers = attackers_to(to, occupied);
// If the opponent has no attackers we are finished
stm = opposite_color(color_of_piece_on(from));
bool Position::is_mate() const {
- MoveStack moves[MOVES_MAX];
- return is_check() && generate<MV_LEGAL>(*this, moves) == moves;
+ MoveStack moves[MAX_MOVES];
+ return in_check() && generate<MV_LEGAL>(*this, moves) == moves;
}
}
-/// Position::flipped_copy() makes a copy of the input position, but with
-/// the white and black sides reversed. This is only useful for debugging,
-/// especially for finding evaluation symmetry bugs.
+/// Position::flip() flips position with the white and black sides reversed. This
+/// is only useful for debugging especially for finding evaluation symmetry bugs.
-void Position::flipped_copy(const Position& pos) {
+void Position::flip() {
+
+ assert(is_ok());
- assert(pos.is_ok());
+ // Make a copy of current position before to start changing
+ const Position pos(*this, threadID);
clear();
threadID = pos.thread();