#include "movepick.h"
#include "position.h"
#include "psqtab.h"
+#include "san.h"
#include "ucioption.h"
//// Variables
////
+extern SearchStack EmptySearchStack;
+
int Position::castleRightsMask[64];
Key Position::zobrist[2][8][64];
Value Position::MgPieceSquareTable[16][64];
Value Position::EgPieceSquareTable[16][64];
+static bool RequestPending = false;
////
//// Functions
fen += (char)skip + '0';
skip = 0;
}
- fen += pieceLetters[piece_on(sq)];
+ fen += pieceLetters[piece_on(sq)];
}
if (skip > 0)
fen += (char)skip + '0';
fen += (rank > RANK_1 ? '/' : ' ');
}
- fen += (sideToMove == WHITE ? 'w' : 'b') + ' ';
+ fen += (sideToMove == WHITE ? "w " : "b ");
if (castleRights != NO_CASTLES)
{
if (can_castle_kingside(WHITE)) fen += 'K';
/// Position::print() prints an ASCII representation of the position to
-/// the standard output.
-
-void Position::print() const {
- char pieceStrings[][8] =
- {"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ",
- "| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K="
- };
-
- for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
- std::cout << "+---+---+---+---+---+---+---+---+\n";
- for(File file = FILE_A; file <= FILE_H; file++) {
- Square sq = make_square(file, rank);
- Piece piece = piece_on(sq);
- if(piece == EMPTY)
- std::cout << ((square_color(sq) == WHITE)? "| " : "| . ");
- else
- std::cout << pieceStrings[piece];
- }
- std::cout << "|\n";
+/// the standard output. If a move is given then also the san is print.
+
+void Position::print(Move m) const {
+
+ static const std::string pieceLetters = " PNBRQK PNBRQK .";
+
+ // Check for reentrancy, as example when called from inside
+ // MovePicker that is used also here in move_to_san()
+ if (RequestPending)
+ return;
+
+ RequestPending = true;
+
+ std::cout << std::endl;
+ if (m != MOVE_NONE)
+ {
+ std::string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : "");
+ std::cout << "Move is: " << col << move_to_san(*this, m) << std::endl;
+ }
+ for (Rank rank = RANK_8; rank >= RANK_1; rank--)
+ {
+ std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
+ for (File file = FILE_A; file <= FILE_H; file++)
+ {
+ Square sq = make_square(file, rank);
+ Piece piece = piece_on(sq);
+ if (piece == EMPTY && square_color(sq) == WHITE)
+ piece = NO_PIECE;
+
+ char col = (color_of_piece_on(sq) == BLACK ? '=' : ' ');
+ std::cout << '|' << col << pieceLetters[piece] << col;
+ }
+ std::cout << '|' << std::endl;
}
- std::cout << "+---+---+---+---+---+---+---+---+\n";
- std::cout << to_fen() << std::endl;
- std::cout << key << std::endl;
+ std::cout << "+---+---+---+---+---+---+---+---+" << std::endl
+ << "Fen is: " << to_fen() << std::endl
+ << "Key is: " << key << std::endl;
+
+ RequestPending = false;
}
Square s;
Bitboard sliders, result = EmptyBoardBB;
-
+
if (Piece == ROOK) // Resolved at compile time
sliders = rooks_and_queens(FindPinned ? opposite_color(c) : c) & RookPseudoAttacks[ksq];
else
// King blockers are candidate pinned pieces
Bitboard candidate_pinned = piece_attacks<Piece>(ksq) & pieces_of_color(c);
- // Pinners are sliders, not checkers, that give check when
+ // Pinners are sliders, not checkers, that give check when
// candidate pinned are removed.
Bitboard pinners = (FindPinned ? sliders & ~checkersBB : sliders);
case WR: case BR: return piece_attacks_square<ROOK>(f, t);
case WQ: case BQ: return piece_attacks_square<QUEEN>(f, t);
case WK: case BK: return piece_attacks_square<KING>(f, t);
- default: return false;
+ default: break;
}
return false;
}
case WR: case BR: return piece_attacks_square<ROOK>(t, s);
case WQ: case BQ: return piece_attacks_square<QUEEN>(t, s);
case WK: case BK: return piece_attacks_square<KING>(t, s);
- default: assert(false);
+ default: break;
}
return false;
}
/// Position::find_checkers() computes the checkersBB bitboard, which
/// contains a nonzero bit for each checking piece (0, 1 or 2). It
/// currently works by calling Position::attacks_to, which is probably
-/// inefficient. Consider rewriting this function to use the last move
+/// inefficient. Consider rewriting this function to use the last move
/// played, like in non-bitboard versions of Glaurung.
void Position::find_checkers() {
- checkersBB = attacks_to(king_square(side_to_move()),opposite_color(side_to_move()));
+ Color us = side_to_move();
+ checkersBB = attacks_to(king_square(us), opposite_color(us));
}
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
- Color us, them;
- Square ksq, from;
-
assert(is_ok());
assert(move_is_ok(m));
assert(pinned == pinned_pieces(side_to_move()));
if (move_is_castle(m))
return true;
- us = side_to_move();
- them = opposite_color(us);
- from = move_from(m);
- ksq = king_square(us);
+ Color us = side_to_move();
+ Color them = opposite_color(us);
+ Square from = move_from(m);
+ Square ksq = king_square(us);
assert(color_of_piece_on(from) == us);
assert(piece_on(ksq) == king_of_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.
- if ( !bit_is_set(pinned, from)
- || (direction_between_squares(from, ksq) == direction_between_squares(move_to(m), ksq)))
- return true;
-
- return false;
+ return ( !bit_is_set(pinned, from)
+ || (direction_between_squares(from, ksq) == direction_between_squares(move_to(m), ksq)));
}
bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
- Color us, them;
- Square ksq, from, to;
-
assert(is_ok());
assert(move_is_ok(m));
assert(dcCandidates == discovered_check_candidates(side_to_move()));
- us = side_to_move();
- them = opposite_color(us);
- from = move_from(m);
- to = move_to(m);
- ksq = king_square(them);
+ Color us = side_to_move();
+ Color them = opposite_color(us);
+ Square from = move_from(m);
+ Square to = move_to(m);
+ Square ksq = king_square(them);
assert(color_of_piece_on(from) == us);
assert(piece_on(ksq) == king_of_color(them));
switch (type_of_piece_on(from))
{
case PAWN:
-
+
if (bit_is_set(pawn_attacks(them, ksq), to)) // Normal check?
return true;
-
+
if ( bit_is_set(dcCandidates, from) // Discovered check?
&& (direction_between_squares(from, ksq) != direction_between_squares(to, ksq)))
return true;
-
+
if (move_promotion(m)) // Promotion with check?
{
Bitboard b = occupied_squares();
}
return false;
- case KNIGHT:
+ case KNIGHT:
return bit_is_set(dcCandidates, from) // Discovered check?
|| bit_is_set(piece_attacks<KNIGHT>(ksq), to); // Normal check?
case QUEEN:
// Discovered checks are impossible!
- assert(!bit_is_set(dcCandidates, from));
+ assert(!bit_is_set(dcCandidates, from));
return bit_is_set(piece_attacks<QUEEN>(ksq), to); // Normal check?
case KING:
}
return false;
- default:
- assert(false);
+ default: // NO_PIECE_TYPE
+ break;
}
assert(false);
return false;
/// Position::move_is_capture() tests whether a move from the current
-/// position is a capture.
+/// position is a capture. Move must not be MOVE_NONE.
bool Position::move_is_capture(Move m) const {
- return color_of_piece_on(move_to(m)) == opposite_color(side_to_move())
- || move_is_ep(m);
+ assert(m != MOVE_NONE);
+
+ return ( !square_is_empty(move_to(m))
+ && (color_of_piece_on(move_to(m)) == opposite_color(side_to_move()))
+ )
+ || move_is_ep(m);
}
void Position::backup(UndoInfo& u) const {
u.castleRights = castleRights;
- u.epSquare = epSquare;
- u.checkersBB = checkersBB;
- u.key = key;
- u.pawnKey = pawnKey;
- u.materialKey = materialKey;
- u.rule50 = rule50;
- u.lastMove = lastMove;
- u.capture = NO_PIECE_TYPE;
- u.mgValue = mgValue;
- u.egValue = egValue;
+ u.epSquare = epSquare;
+ u.checkersBB = checkersBB;
+ u.key = key;
+ u.pawnKey = pawnKey;
+ u.materialKey = materialKey;
+ u.rule50 = rule50;
+ u.lastMove = lastMove;
+ u.mgValue = mgValue;
+ u.egValue = egValue;
+ u.capture = NO_PIECE_TYPE;
}
void Position::restore(const UndoInfo& u) {
castleRights = u.castleRights;
- epSquare = u.epSquare;
- checkersBB = u.checkersBB;
- key = u.key;
- pawnKey = u.pawnKey;
- materialKey = u.materialKey;
- rule50 = u.rule50;
- lastMove = u.lastMove;
+ epSquare = u.epSquare;
+ checkersBB = u.checkersBB;
+ key = u.key;
+ pawnKey = u.pawnKey;
+ materialKey = u.materialKey;
+ rule50 = u.rule50;
+ lastMove = u.lastMove;
+ mgValue = u.mgValue;
+ egValue = u.egValue;
// u.capture is restored in undo_move()
- mgValue = u.mgValue;
- egValue = u.egValue;
}
-
/// Position::do_move() makes a move, and backs up all information necessary
-/// to undo the move to an UndoInfo object. The move is assumed to be legal.
+/// to undo the move to an UndoInfo object. The move is assumed to be legal.
/// Pseudo-legal moves should be filtered out before this function is called.
/// There are two versions of this function, one which takes only the move and
/// the UndoInfo as input, and one which takes a third parameter, a bitboard of
-/// discovered check candidates. The second version is faster, because knowing
+/// discovered check candidates. The second version is faster, because knowing
/// the discovered check candidates makes it easier to update the checkersBB
/// member variable in the position object.
if (capture)
{
- assert(capture != KING);
-
- // Remove captured piece
- clear_bit(&(byColorBB[them]), to);
- clear_bit(&(byTypeBB[capture]), to);
-
- // Update hash key
- key ^= zobrist[them][capture][to];
-
- // If the captured piece was a pawn, update pawn hash key
- if (capture == PAWN)
- pawnKey ^= zobrist[them][PAWN][to];
-
- // Update incremental scores
- mgValue -= mg_pst(them, capture, to);
- egValue -= eg_pst(them, capture, to);
-
- // Update material
- if (capture != PAWN)
- npMaterial[them] -= piece_value_midgame(capture);
-
- // Update material hash key
- materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
-
- // Update piece count
- pieceCount[them][capture]--;
-
- // Update piece list
- pieceList[them][capture][index[to]] = pieceList[them][capture][pieceCount[them][capture]];
- index[pieceList[them][capture][index[to]]] = index[to];
-
- // Remember the captured piece, in order to be able to undo the move correctly
- u.capture = capture;
-
- // Reset rule 50 counter
- rule50 = 0;
+ u.capture = capture;
+ do_capture_move(m, capture, them, to);
}
// Move the piece
}
+/// Position::do_capture_move() is a private method used to update captured
+/// piece info. It is called from the main Position::do_move function.
+
+void Position::do_capture_move(Move m, PieceType capture, Color them, Square to) {
+
+ assert(capture != KING);
+
+ // Remove captured piece
+ clear_bit(&(byColorBB[them]), to);
+ clear_bit(&(byTypeBB[capture]), to);
+
+ // Update hash key
+ key ^= zobrist[them][capture][to];
+
+ // If the captured piece was a pawn, update pawn hash key
+ if (capture == PAWN)
+ pawnKey ^= zobrist[them][PAWN][to];
+
+ // Update incremental scores
+ mgValue -= mg_pst(them, capture, to);
+ egValue -= eg_pst(them, capture, to);
+
+ assert(!move_promotion(m) || capture != PAWN);
+
+ // Update material
+ if (capture != PAWN)
+ npMaterial[them] -= piece_value_midgame(capture);
+
+ // Update material hash key
+ materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
+
+ // Update piece count
+ pieceCount[them][capture]--;
+
+ // Update piece list
+ pieceList[them][capture][index[to]] = pieceList[them][capture][pieceCount[them][capture]];
+ index[pieceList[them][capture][index[to]]] = index[to];
+
+ // Reset rule 50 counter
+ rule50 = 0;
+}
+
+
/// Position::do_castle_move() is a private method used to make a castling
-/// move. It is called from the main Position::do_move function. Note that
+/// move. It is called from the main Position::do_move function. Note that
/// castling moves are encoded as "king captures friendly rook" moves, for
/// instance white short castling in a non-Chess960 game is encoded as e1h1.
if (capture)
{
- assert(capture != KING);
-
- // Remove captured piece
- clear_bit(&(byColorBB[them]), to);
- clear_bit(&(byTypeBB[capture]), to);
-
- // Update hash key
- key ^= zobrist[them][capture][to];
-
- // Update incremental scores
- mgValue -= mg_pst(them, capture, to);
- egValue -= eg_pst(them, capture, to);
-
- // Update material. Because our move is a promotion, we know that the
- // captured piece is not a pawn.
- assert(capture != PAWN);
- npMaterial[them] -= piece_value_midgame(capture);
-
- // Update material hash key
- materialKey ^= zobMaterial[them][capture][pieceCount[them][capture]];
-
- // Update piece count
- pieceCount[them][capture]--;
-
- // Update piece list
- pieceList[them][capture][index[to]] = pieceList[them][capture][pieceCount[them][capture]];
- index[pieceList[them][capture][index[to]]] = index[to];
-
- // Remember the captured piece, in order to be able to undo the move correctly
u.capture = capture;
+ do_capture_move(m, capture, them, to);
}
// Remove pawn
// Position::undo_move. In particular, the side to move has been switched,
// so the code below is correct.
Color us = side_to_move();
- Color them = opposite_color(us);
// Find source squares for king and rook
Square kfrom = move_from(m);
/// Position::do_move, is used to put back the captured piece (if any).
void Position::undo_promotion_move(Move m, const UndoInfo &u) {
-
+
Color us, them;
Square from, to;
PieceType capture, promotion;
/// Position::see() is a static exchange evaluator: It tries to estimate the
-/// material gain or loss resulting from a move. There are two versions of
-/// this function: One which takes a move as input, and one which takes a
-/// 'from' and a 'to' square. The function does not yet understand promotions
-/// or en passant captures.
+/// material gain or loss resulting from a move. There are three versions of
+/// this function: One which takes a destination square as input, one takes a
+/// move, and one which takes a 'from' and a 'to' square. The function does
+/// not yet understand promotions or en passant captures.
+
+int Position::see(Square to) const {
+
+ assert(square_is_ok(to));
+ return see(SQ_NONE, to);
+}
int Position::see(Move m) const {
int Position::see(Square from, Square to) const {
- // Approximate material values, with pawn = 1
+ // Material values
static const int seeValues[18] = {
- 0, 1, 3, 3, 5, 10, 100, 0, 0, 1, 3, 3, 5, 10, 100, 0, 0, 0
+ 0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
+ RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0,
+ 0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
+ RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0,
+ 0, 0
};
Bitboard attackers, occ, b;
- assert(square_is_ok(from));
+ assert(square_is_ok(from) || from == SQ_NONE);
assert(square_is_ok(to));
// Initialize colors
- Color us = color_of_piece_on(from);
+ Color us = (from != SQ_NONE ? color_of_piece_on(from) : opposite_color(color_of_piece_on(to)));
Color them = opposite_color(us);
// Initialize pieces
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it.
occ = occupied_squares();
- clear_bit(&occ, from);
- attackers = (rook_attacks_bb(to, occ) & rooks_and_queens())
- | (bishop_attacks_bb(to, occ) & bishops_and_queens())
- | (piece_attacks<KNIGHT>(to) & knights())
- | (piece_attacks<KING>(to) & kings())
- | (pawn_attacks(WHITE, to) & pawns(BLACK))
- | (pawn_attacks(BLACK, to) & pawns(WHITE));
-
- // If the opponent has no attackers, we are finished
+ while (true)
+ {
+ clear_bit(&occ, from);
+ attackers = (rook_attacks_bb(to, occ) & rooks_and_queens())
+ | (bishop_attacks_bb(to, occ) & bishops_and_queens())
+ | (piece_attacks<KNIGHT>(to) & knights())
+ | (piece_attacks<KING>(to) & kings())
+ | (pawn_attacks(WHITE, to) & pawns(BLACK))
+ | (pawn_attacks(BLACK, to) & pawns(WHITE));
+
+ if (from != SQ_NONE)
+ break;
+
+ // If we don't have any attacker we are finished
+ if ((attackers & pieces_of_color(us)) == EmptyBoardBB)
+ return 0;
+
+ // Locate the least valuable attacker to the destination square
+ // and use it to initialize from square.
+ PieceType pt;
+ for (pt = PAWN; !(attackers & pieces_of_color_and_type(us, pt)); pt++)
+ assert(pt < KING);
+
+ from = first_1(attackers & pieces_of_color_and_type(us, pt));
+ piece = piece_on(from);
+ }
+
+ // If the opponent has no attackers we are finished
if ((attackers & pieces_of_color(them)) == EmptyBoardBB)
return seeValues[capture];
attackers &= occ; // Remove the moving piece
- // If we don't have any attacker but the moving piece (common case)
- // then we loose our piece and gain the opponent attacked one.
- // Note that this is not perfect! It does not detect x-rays of
- // an our piece behind an opposite one. But is a very rare case.
- if ((attackers & pieces_of_color(us)) == EmptyBoardBB)
- return seeValues[capture] - seeValues[piece];
-
// The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing
// the material gain or loss at each stop in a sequence of captures to the
if (is_check())
{
- MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE,
- MOVE_NONE, MOVE_NONE, Depth(0));
+ MovePicker mp = MovePicker(*this, false, MOVE_NONE, EmptySearchStack, Depth(0));
return mp.get_next_move() == MOVE_NONE;
}
return false;
{
if (mgValue != compute_mg_value())
return false;
-
+
if (egValue != compute_eg_value())
return false;
}