Beruflich Dokumente
Kultur Dokumente
of
See
<algorithm>
<cassert>
<cstring>
<iomanip>
<sstream>
#include
#include
#include
#include
#include
#include
#include
#include
"bitcount.h"
"movegen.h"
"notation.h"
"position.h"
"psqtab.h"
"rkiss.h"
"thread.h"
"tt.h"
using std::string;
static const string PieceToChar(" PNBRQK
pnbrqk");
CACHE_LINE_ALIGNMENT
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg,
RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg,
RookValueEg, QueenValueEg } };
static Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
namespace Zobrist {
Key
Key
Key
Key
Key
psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
enpassant[FILE_NB];
castling[CASTLING_RIGHT_NB];
side;
exclusion;
checkSq[PAWN]
checkSq[KNIGHT]
checkSq[BISHOP]
checkSq[ROOK]
checkSq[QUEEN]
checkSq[KING]
=
=
=
=
=
=
pos.attacks_from<PAWN>(ksq, them);
pos.attacks_from<KNIGHT>(ksq);
pos.attacks_from<BISHOP>(ksq);
pos.attacks_from<ROOK>(ksq);
checkSq[BISHOP] | checkSq[ROOK];
0;
return *this;
*/
// 1. Piece placement
while ((ss >> token) && !isspace(token))
{
if (isdigit(token))
sq += Square(token - '0'); // Advance the given
number of files
else if (token == '/')
sq -= Square(16);
else if ((idx = PieceToChar.find(token)) !=
string::npos)
{
put_piece(sq, color_of(Piece(idx)),
type_of(Piece(idx)));
++sq;
}
}
// 2. Active color
ss >> token;
sideToMove = (token == 'w' ? WHITE : BLACK);
ss >> token;
// 3. Castling availability. Compatible with 3 standards:
Normal FEN standard,
// Shredder-FEN that uses the letters of the columns on
which the rooks began
// the game instead of KQkq and also X-FEN standard that, in
case of Chess960,
// if an inner rook is associated with the castling right,
the castling tag is
// replaced by the file letter of the involved rook, as for
the Shredder-FEN.
while ((ss >> token) && !isspace(token))
{
Square rsq;
Color c = islower(token) ? BLACK : WHITE;
token = char(toupper(token));
if (token == 'K')
for (rsq = relative_square(c, SQ_H1);
type_of(piece_on(rsq)) != ROOK; --rsq) {}
else if (token == 'Q')
for (rsq = relative_square(c, SQ_A1);
type_of(piece_on(rsq)) != ROOK; ++rsq) {}
else if (token >= 'A' && token <= 'H')
rsq = make_square(File(token - 'A'),
relative_rank(c, RANK_1));
else
continue;
set_castling_right(c, rsq);
set_state(st);
}
assert(pos_is_ok());
if (f <= FILE_H)
ss << PieceToChar[piece_on(make_square(f, r))];
if (r > RANK_1)
ss << '/';
KING_SIDE)),
if (can_castle(WHITE_OOO))
ss << (chess960 ?
to_char(file_of(castling_rook_square(WHITE | QUEEN_SIDE)),
false) : 'Q');
if (can_castle(BLACK_OO))
ss << (chess960 ?
to_char(file_of(castling_rook_square(BLACK |
true) : 'k');
KING_SIDE)),
if (can_castle(BLACK_OOO))
ss << (chess960 ?
to_char(file_of(castling_rook_square(BLACK | QUEEN_SIDE)),
true) : 'q');
if (!can_castle(WHITE) && !can_castle(BLACK))
ss << '-';
ss << (ep_square() == SQ_NONE ? " - " : " " +
to_string(ep_square()) + " ")
<< st->rule50 << " " << 1 + (gamePly - (sideToMove ==
BLACK)) / 2;
}
return ss.str();
ss << "\nFen: " << fen() << "\nKey: " << std::hex <<
std::uppercase
<< std::setfill('0') << std::setw(16) << st->key <<
"\nCheckers: ";
for (Bitboard b = checkers(); b; )
ss << to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: ";
return ss.str();
}
return result;
indicate occupancy.
Bitboard Position::attackers_to(Square s, Bitboard occ) const
{
return
|
|
|
|
QUEEN))
|
}
(attacks_from<PAWN>(s, BLACK)
(attacks_from<PAWN>(s, WHITE)
(attacks_from<KNIGHT>(s)
(attacks_bb<ROOK>(s, occ)
(attacks_bb<BISHOP>(s, occ)
&
&
&
&
&
pieces(WHITE, PAWN))
pieces(BLACK, PAWN))
pieces(KNIGHT))
pieces(ROOK, QUEEN))
pieces(BISHOP,
(attacks_from<KING>(s)
& pieces(KING));
!(attacks_bb<
QUEEN, ROOK))
&& !(attacks_bb<BISHOP>(ksq, occ) & pieces(~us,
QUEEN, BISHOP));
}
// If the moving piece is a king, check whether the
destination
// square is attacked by the opponent. Castling moves are
checked
// for legality during move generation.
if (type_of(piece_on(from)) == KING)
return type_of(m) == CASTLING || !
(attackers_to(to_sq(m)) & pieces(~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.
return
!pinned
|| !(pinned & from)
|| aligned(from, to_sq(m), king_square(us));
}
/// Position::pseudo_legal() takes a random move and tests
whether the move is
/// pseudo legal. It is used to validate moves from TT that
can be corrupted
/// due to SMP concurrent access or hash position key
aliasing.
bool Position::pseudo_legal(const Move m) const {
Color us = sideToMove;
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = moved_piece(m);
// Use a slower but simpler function for uncommon cases
if (type_of(m) != NORMAL)
return MoveList<LEGAL>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty
if (promotion_type(m) - 2 != NO_PIECE_TYPE)
return false;
// If the 'from' square is not occupied by a piece belonging
to the side to
&& !(
(from + 2 * pawn_push(us) ==
// Not a double push
&& (rank_of(from) == relative_rank(us, RANK_2))
&& empty(to)
&& empty(to - pawn_push(us))))
return false;
}
else if (!(attacks_from(pc, from) & to))
return false;
return true;
case PROMOTION:
return attacks_bb(Piece(promotion_type(m)), to, pieces()
^ from) & ci.ksq;
// En passant capture with check? We have already handled
the case
// of direct checks and ordinary discovered check, so the
only case we
// need to handle is the unusual case of a discovered check
through
// the captured pawn.
case ENPASSANT:
{
Square capsq = make_square(file_of(to), rank_of(from));
Bitboard b = (pieces() ^ from ^ capsq) | to;
return (attacks_bb< ROOK>(ci.ksq, b) &
pieces(sideToMove, QUEEN, ROOK))
| (attacks_bb<BISHOP>(ci.ksq, b) &
pieces(sideToMove, QUEEN, BISHOP));
}
case CASTLING:
{
Square kfrom = from;
Square rfrom = to; // Castling is encoded as 'King
captures the rook'
Square kto = relative_square(sideToMove, rfrom > kfrom ?
SQ_G1 : SQ_C1);
Square rto = relative_square(sideToMove, rfrom > kfrom ?
SQ_F1 : SQ_D1);
return
(PseudoAttacks[ROOK][rto] & ci.ksq)
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^
rfrom) | rto | kto) & ci.ksq);
}
default:
assert(false);
return false;
}
}
/// Position::do_move() makes a move, and saves all
information necessary
/// to a StateInfo object. The move is assumed to be legal.
Pseudo-legal
CheckInfo ci(*this);
do_move(m, newSt, ci, gives_check(m, ci));
assert(color_of(pc) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) ==
them || type_of(m) == CASTLING);
assert(captured != KING);
if (type_of(m) == CASTLING)
{
assert(pc == make_piece(us, KING));
Square rfrom, rto;
do_castling<true>(from, to, rfrom, rto);
captured = NO_PIECE_TYPE;
st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us]
[ROOK][rto];
}
if (captured)
{
Square capsq = to;
// If the captured piece is a pawn, update pawn hash
key, otherwise
// update non-pawn material.
if (captured == PAWN)
{
if (type_of(m) == ENPASSANT)
{
capsq += pawn_push(them);
assert(pt == PAWN);
assert(to == st->epSquare);
assert(relative_rank(us, to) == RANK_6);
assert(piece_on(to) == NO_PIECE);
assert(piece_on(capsq) == make_piece(them,
PAWN));
}
board[capsq] = NO_PIECE;
st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
}
else
st->npMaterial[them] -= PieceValue[MG][captured];
if (pt == PAWN)
{
// Set en-passant square if the moved pawn can be
captured
if (
(int(to) ^ int(from)) == 16
&& (attacks_from<PAWN>(from + pawn_push(us), us) &
pieces(them, PAWN)))
{
st->epSquare = Square((from + to) / 2);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
else if (type_of(m) == PROMOTION)
{
PieceType promotion = promotion_type(m);
assert(relative_rank(us, to) == RANK_8);
assert(promotion >= KNIGHT && promotion <= QUEEN);
remove_piece(to, us, PAWN);
put_piece(to, us, promotion);
// Update hash keys
k ^= Zobrist::psq[us][PAWN][to] ^ Zobrist::psq[us]
[promotion][to];
st->pawnKey ^= Zobrist::psq[us][PAWN][to];
st->materialKey ^= Zobrist::psq[us][promotion]
[pieceCount[us][promotion]-1]
^ Zobrist::psq[us][PAWN]
[pieceCount[us][PAWN]];
// Update incremental score
st->psq += psq[us][promotion][to] - psq[us][PAWN]
[to];
// Update material
st->npMaterial[us] += PieceValue[MG][promotion];
}
// Update incremental scores
st->psq += psq[us][pt][to] - psq[us][pt][from];
// Set capture piece
st->capturedType = captured;
// Update the key with the final value
st->key = k;
// Update checkers bitboard: piece must be already moved due
to attacks_from()
st->checkersBB = 0;
if (moveIsCheck)
{
if (type_of(m) != NORMAL)
st->checkersBB = attackers_to(king_square(them)) &
pieces(us);
else
{
// Direct checks
if (ci.checkSq[pt] & to)
st->checkersBB |= to;
from))
// Discovered checks
if (unlikely(ci.dcCandidates) && (ci.dcCandidates &
{
if (pt != ROOK)
st->checkersBB |=
attacks_from<ROOK>(king_square(them)) & pieces(us, QUEEN,
ROOK);
if (pt != BISHOP)
st->checkersBB |=
attacks_from<BISHOP>(king_square(them)) & pieces(us, QUEEN,
BISHOP);
}
}
}
sideToMove = ~sideToMove;
}
assert(pos_is_ok());
if (type_of(m) == CASTLING)
{
Square rfrom, rto;
do_castling<false>(from, to, rfrom, rto);
}
else
{
move_piece(to, from, us, pt); // Put the piece back at
the source square
if (st->capturedType)
{
Square capsq = to;
if (type_of(m) == ENPASSANT)
{
capsq -= pawn_push(us);
assert(pt == PAWN);
assert(to == st->previous->epSquare);
assert(relative_rank(us, to) == RANK_6);
assert(piece_on(capsq) == NO_PIECE);
assert(pos_is_ok());
assert(pos_is_ok());
void Position::undo_null_move() {
assert(!checkers());
st = st->previous;
sideToMove = ~sideToMove;
assert(is_ok(m));
// Early return if SEE cannot be negative because captured
piece value
// is not less then capturing one. Note that king moves
always return
// here because king midgame value is set to 0.
if (PieceValue[MG][moved_piece(m)] <= PieceValue[MG]
[piece_on(to_sq(m))])
return VALUE_KNOWN_WIN;
}
return see(m);
moving piece
// removed, but possibly an X-ray attacker added behind it.
attackers = attackers_to(to, occupied) & occupied;
// If the opponent has no attackers we are finished
stm = ~stm;
stmAttackers = attackers & pieces(stm);
if (!stmAttackers)
return swapList[0];
// 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
// destination square, where the sides alternately capture,
and always
// capture with the least valuable piece. After each
capture, we look for
// new X-ray attacks from behind the capturing piece.
captured = type_of(piece_on(from));
do {
break;
stm = ~stm;
stmAttackers = attackers & pieces(stm);
++slIndex;
} while (stmAttackers);
// Having built the swap list, we negamax through it to find
the best
// achievable score from the point of view of the side to
move.
while (--slIndex)
swapList[slIndex - 1] = std::min(-swapList[slIndex],
swapList[slIndex - 1]);
}
return swapList[0];
!pieces(PAWN)
&& (non_pawn_material(WHITE) + non_pawn_material(BLACK)
<= BishopValueMg))
return true;
if (st->rule50 > 99 && (!checkers() ||
MoveList<LEGAL>(*this).size()))
return true;
StateInfo* stp = st;
for (int i = 2, e = std::min(st->rule50, st->pliesFromNull);
i <= e; i += 2)
{
stp = stp->previous->previous;
}
}
if (stp->key == st->key)
return true; // Draw at first repetition
return false;
assert(pos_is_ok());
const
const
const
const
const
const
const
bool
bool
bool
bool
bool
bool
bool
testBitboards
testState
testKingCount
testKingCapture
testPieceCounts
testPieceList
testCastlingSquares
=
=
=
=
=
=
=
all
all
all
all
all
all
all
||
||
||
||
||
||
||
false;
false;
false;
false;
false;
false;
false;
if (step)
*step = 1;
if (
|| st->npMaterial[WHITE] != si.npMaterial[WHITE]
|| st->npMaterial[BLACK] != si.npMaterial[BLACK]
|| st->psq != si.psq
|| st->checkersBB != si.checkersBB)
return false;
1)
return false;
s]] != (c | s))
}
}
return true;
return false;