#include "bitcount.h"
#include "movegen.h"
+#include "notation.h"
#include "position.h"
#include "psqtab.h"
#include "rkiss.h"
using std::cout;
using std::endl;
-Key Position::zobrist[2][8][64];
-Key Position::zobEp[8];
-Key Position::zobCastle[16];
-Key Position::zobSideToMove;
-Key Position::zobExclusion;
-
-Score Position::pieceSquareTable[16][64];
-
-// Material values arrays, indexed by Piece
-const Value PieceValueMidgame[17] = {
- VALUE_ZERO,
- PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
- RookValueMidgame, QueenValueMidgame,
- VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
- PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
- RookValueMidgame, QueenValueMidgame
-};
-
-const Value PieceValueEndgame[17] = {
- VALUE_ZERO,
- PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
- RookValueEndgame, QueenValueEndgame,
- VALUE_ZERO, VALUE_ZERO, VALUE_ZERO,
- PawnValueEndgame, KnightValueEndgame, BishopValueEndgame,
- RookValueEndgame, QueenValueEndgame
-};
-
-// To convert a Piece to and from a FEN char
-static const string PieceToChar(" PNBRQK pnbrqk .");
+static const string PieceToChar(" PNBRQK pnbrqk");
+
+CACHE_LINE_ALIGNMENT
+
+Score pieceSquareTable[16][64]; // [piece][square]
+Value PieceValue[2][18] = { // [Mg / Eg][piece / pieceType]
+{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
+{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
+
+namespace Zobrist {
+
+Key psq[2][8][64]; // [color][pieceType][square / piece count]
+Key enpassant[8]; // [file]
+Key castle[16]; // [castleRight]
+Key side;
+Key exclusion;
+
+/// init() initializes at startup the various arrays used to compute hash keys
+/// and the piece square tables. The latter is a two-step operation: First, the
+/// white halves of the tables are copied from PSQT[] tables. Second, the black
+/// halves of the tables are initialized by flipping and changing the sign of
+/// the white scores.
+
+void init() {
+
+ RKISS rk;
+
+ for (Color c = WHITE; c <= BLACK; c++)
+ for (PieceType pt = PAWN; pt <= KING; pt++)
+ for (Square s = SQ_A1; s <= SQ_H8; s++)
+ psq[c][pt][s] = rk.rand<Key>();
+
+ for (File f = FILE_A; f <= FILE_H; f++)
+ enpassant[f] = rk.rand<Key>();
+
+ for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
+ {
+ Bitboard b = cr;
+ while (b)
+ {
+ Key k = castle[1ULL << pop_lsb(&b)];
+ castle[cr] ^= k ? k : rk.rand<Key>();
+ }
+ }
+
+ side = rk.rand<Key>();
+ exclusion = rk.rand<Key>();
+
+ for (PieceType pt = PAWN; pt <= KING; pt++)
+ {
+ PieceValue[Mg][make_piece(BLACK, pt)] = PieceValue[Mg][pt];
+ PieceValue[Eg][make_piece(BLACK, pt)] = PieceValue[Eg][pt];
+
+ Score v = make_score(PieceValue[Mg][pt], PieceValue[Eg][pt]);
+
+ for (Square s = SQ_A1; s <= SQ_H8; s++)
+ {
+ pieceSquareTable[make_piece(WHITE, pt)][ s] = (v + PSQT[pt][s]);
+ pieceSquareTable[make_piece(BLACK, pt)][~s] = -(v + PSQT[pt][s]);
+ }
+ }
+}
+
+} // namespace Zobrist
/// CheckInfo c'tor
/// object do not depend on any external data so we detach state pointer from
/// the source one.
-void Position::operator=(const Position& pos) {
+Position& Position::operator=(const Position& pos) {
memcpy(this, &pos, sizeof(Position));
startState = *st;
nodes = 0;
assert(pos_is_ok());
+
+ return *this;
}
sq += Square(token - '0'); // Advance the given number of files
else if (token == '/')
- sq = make_square(FILE_A, rank_of(sq) - Rank(2));
+ sq -= Square(16);
else if ((p = PieceToChar.find(token)) != string::npos)
{
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));
+ rsq = File(token - 'A') | relative_rank(c, RANK_1);
else
continue;
if ( ((fen >> col) && (col >= 'a' && col <= 'h'))
&& ((fen >> row) && (row == '3' || row == '6')))
{
- st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+ st->epSquare = File(col - 'a') | Rank(row - '1');
if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN)))
st->epSquare = SQ_NONE;
for (File file = FILE_A; file <= FILE_H; file++)
{
- sq = make_square(file, rank);
+ sq = file | rank;
if (is_empty(sq))
emptyCnt++;
void Position::print(Move move) const {
- const char* dottedLine = "\n+---+---+---+---+---+---+---+---+\n";
+ const string dottedLine = "\n+---+---+---+---+---+---+---+---+";
+ const string twoRows = dottedLine + "\n| | . | | . | | . | | . |"
+ + dottedLine + "\n| . | | . | | . | | . | |";
+
+ string brd = twoRows + twoRows + twoRows + twoRows + dottedLine;
if (move)
{
cout << "\nMove is: " << (sideToMove == BLACK ? ".." : "") << move_to_san(p, move);
}
- for (Rank rank = RANK_8; rank >= RANK_1; rank--)
- {
- cout << dottedLine << '|';
- for (File file = FILE_A; file <= FILE_H; file++)
- {
- Square sq = make_square(file, rank);
- Piece piece = piece_on(sq);
- char c = (color_of(piece) == BLACK ? '=' : ' ');
-
- if (piece == NO_PIECE && !opposite_colors(sq, SQ_A1))
- piece++; // Index the dot
+ for (Square sq = SQ_A1; sq <= SQ_H8; sq++)
+ if (piece_on(sq) != NO_PIECE)
+ brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
- cout << c << PieceToChar[piece] << c << '|';
- }
- }
- cout << dottedLine << "Fen is: " << to_fen() << "\nKey is: " << st->key << endl;
+ cout << brd << "\nFen is: " << to_fen() << "\nKey is: " << st->key << endl;
}
while (pinners)
{
- b = between_bb(ksq, pop_1st_bit(&pinners)) & pieces();
+ b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
if (b && !more_than_one(b) && (b & pieces(sideToMove)))
result |= b;
// 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 (is_enpassant(m))
+ if (type_of(m) == ENPASSANT)
{
Color them = ~us;
Square to = to_sq(m);
// square is attacked by the opponent. Castling moves are checked
// for legality during move generation.
if (type_of(piece_on(from)) == KING)
- return is_castle(m) || !(attackers_to(to_sq(m)) & pieces(~us));
+ return type_of(m) == CASTLE || !(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.
bool Position::move_is_legal(const Move m) const {
- for (MoveList<MV_LEGAL> ml(*this); !ml.end(); ++ml)
+ for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
if (ml.move() == m)
return true;
Piece pc = piece_moved(m);
// Use a slower but simpler function for uncommon cases
- if (is_special(m))
+ if (type_of(m) != NORMAL)
return move_is_legal(m);
// Is not a promotion, so promotion piece must be empty
if (type_of(pc) != KING)
{
Bitboard b = checkers();
- Square checksq = pop_1st_bit(&b);
+ Square checksq = pop_lsb(&b);
if (b) // double check ? In this case a king move is required
return false;
}
// Can we skip the ugly special cases ?
- if (!is_special(m))
+ if (type_of(m) == NORMAL)
return false;
Color us = sideToMove;
Square ksq = king_square(~us);
// Promotion with check ?
- if (is_promotion(m))
+ if (type_of(m) == PROMOTION)
return attacks_from(Piece(promotion_type(m)), to, pieces() ^ from) & ksq;
// En passant capture with check ? We have already handled the case
// of direct checks and ordinary discovered check, the only case we
// need to handle is the unusual case of a discovered check through
// the captured pawn.
- if (is_enpassant(m))
+ if (type_of(m) == ENPASSANT)
{
- Square capsq = make_square(file_of(to), rank_of(from));
+ Square capsq = file_of(to) | rank_of(from);
Bitboard b = (pieces() ^ from ^ capsq) | to;
return (attacks_bb< ROOK>(ksq, b) & pieces(us, QUEEN, ROOK))
}
// Castling with check ?
- if (is_castle(m))
+ if (type_of(m) == CASTLE)
{
Square kfrom = from;
Square rfrom = to; // 'King captures the rook' notation
// Copy some fields of old state to our new StateInfo object except the ones
// which are recalculated from scratch anyway, then switch our state pointer
// to point to the new, ready to be updated, state.
- struct ReducedStateInfo {
- Key pawnKey, materialKey;
- Value npMaterial[2];
- int castleRights, rule50, pliesFromNull;
- Score psq_score;
- Square epSquare;
- };
-
memcpy(&newSt, st, sizeof(ReducedStateInfo));
newSt.previous = st;
st = &newSt;
// Update side to move
- k ^= zobSideToMove;
+ k ^= Zobrist::side;
// Increment the 50 moves rule draw counter. Resetting it to zero in the
// case of a capture or a pawn move is taken care of later.
st->rule50++;
st->pliesFromNull++;
- if (is_castle(m))
+ if (type_of(m) == CASTLE)
{
st->key = k;
do_castle_move<true>(m);
Square to = to_sq(m);
Piece piece = piece_on(from);
PieceType pt = type_of(piece);
- PieceType capture = is_enpassant(m) ? PAWN : type_of(piece_on(to));
+ PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
assert(color_of(piece) == us);
assert(color_of(piece_on(to)) != us);
// update non-pawn material.
if (capture == PAWN)
{
- if (is_enpassant(m))
+ if (type_of(m) == ENPASSANT)
{
capsq += pawn_push(them);
board[capsq] = NO_PIECE;
}
- st->pawnKey ^= zobrist[them][PAWN][capsq];
+ st->pawnKey ^= Zobrist::psq[them][PAWN][capsq];
}
else
- st->npMaterial[them] -= PieceValueMidgame[capture];
+ st->npMaterial[them] -= PieceValue[Mg][capture];
// Remove the captured piece
byTypeBB[ALL_PIECES] ^= capsq;
pieceList[them][capture][pieceCount[them][capture]] = SQ_NONE;
// Update hash keys
- k ^= zobrist[them][capture][capsq];
- st->materialKey ^= zobrist[them][capture][pieceCount[them][capture]];
+ k ^= Zobrist::psq[them][capture][capsq];
+ st->materialKey ^= Zobrist::psq[them][capture][pieceCount[them][capture]];
// Update incremental scores
st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
}
// Update hash key
- k ^= zobrist[us][pt][from] ^ zobrist[us][pt][to];
+ k ^= Zobrist::psq[us][pt][from] ^ Zobrist::psq[us][pt][to];
// Reset en passant square
if (st->epSquare != SQ_NONE)
{
- k ^= zobEp[file_of(st->epSquare)];
+ k ^= Zobrist::enpassant[file_of(st->epSquare)];
st->epSquare = SQ_NONE;
}
if (st->castleRights && (castleRightsMask[from] | castleRightsMask[to]))
{
int cr = castleRightsMask[from] | castleRightsMask[to];
- k ^= zobCastle[st->castleRights & cr];
+ k ^= Zobrist::castle[st->castleRights & cr];
st->castleRights &= ~cr;
}
&& (attacks_from<PAWN>(from + pawn_push(us), us) & pieces(them, PAWN)))
{
st->epSquare = Square((from + to) / 2);
- k ^= zobEp[file_of(st->epSquare)];
+ k ^= Zobrist::enpassant[file_of(st->epSquare)];
}
- if (is_promotion(m))
+ if (type_of(m) == PROMOTION)
{
PieceType promotion = promotion_type(m);
pieceList[us][promotion][index[to]] = to;
// Update hash keys
- k ^= zobrist[us][PAWN][to] ^ zobrist[us][promotion][to];
- st->pawnKey ^= zobrist[us][PAWN][to];
- st->materialKey ^= zobrist[us][promotion][pieceCount[us][promotion]++]
- ^ zobrist[us][PAWN][pieceCount[us][PAWN]];
+ 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]++]
+ ^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
// Update incremental score
st->psqScore += pieceSquareTable[make_piece(us, promotion)][to]
- pieceSquareTable[make_piece(us, PAWN)][to];
// Update material
- st->npMaterial[us] += PieceValueMidgame[promotion];
+ st->npMaterial[us] += PieceValue[Mg][promotion];
}
// Update pawn hash key
- st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
+ st->pawnKey ^= Zobrist::psq[us][PAWN][from] ^ Zobrist::psq[us][PAWN][to];
// Reset rule 50 draw counter
st->rule50 = 0;
if (moveIsCheck)
{
- if (is_special(m))
+ if (type_of(m) != NORMAL)
st->checkersBB = attackers_to(king_square(them)) & pieces(us);
else
{
sideToMove = ~sideToMove;
- if (is_castle(m))
+ if (type_of(m) == CASTLE)
{
do_castle_move<false>(m);
return;
assert(color_of(piece) == us);
assert(capture != KING);
- if (is_promotion(m))
+ if (type_of(m) == PROMOTION)
{
PieceType promotion = promotion_type(m);
{
Square capsq = to;
- if (is_enpassant(m))
+ if (type_of(m) == ENPASSANT)
{
capsq -= pawn_push(us);
void Position::do_castle_move(Move m) {
assert(is_ok(m));
- assert(is_castle(m));
+ assert(type_of(m) == CASTLE);
Square kto, kfrom, rfrom, rto, kAfter, rAfter;
st->psqScore += psq_delta(rook, rfrom, rto);
// Update hash key
- st->key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto];
- st->key ^= zobrist[us][ROOK][rfrom] ^ zobrist[us][ROOK][rto];
+ st->key ^= Zobrist::psq[us][KING][kfrom] ^ Zobrist::psq[us][KING][kto];
+ st->key ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
// Clear en passant square
if (st->epSquare != SQ_NONE)
{
- st->key ^= zobEp[file_of(st->epSquare)];
+ st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
st->epSquare = SQ_NONE;
}
// Update castling rights
- st->key ^= zobCastle[st->castleRights & castleRightsMask[kfrom]];
+ st->key ^= Zobrist::castle[st->castleRights & castleRightsMask[kfrom]];
st->castleRights &= ~castleRightsMask[kfrom];
// Update checkers BB
if (Do)
{
if (st->epSquare != SQ_NONE)
- st->key ^= zobEp[file_of(st->epSquare)];
+ st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
- st->key ^= zobSideToMove;
+ st->key ^= Zobrist::side;
prefetch((char*)TT.first_entry(st->key));
st->epSquare = SQ_NONE;
// 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 (PieceValueMidgame[piece_on(to_sq(m))] >= PieceValueMidgame[piece_moved(m)])
+ if (PieceValue[Mg][piece_on(to_sq(m))] >= PieceValue[Mg][piece_moved(m)])
return 1;
return see(m);
// As castle moves are implemented as capturing the rook, they have
// SEE == RookValueMidgame most of the times (unless the rook is under
// attack).
- if (is_castle(m))
+ if (type_of(m) == CASTLE)
return 0;
from = from_sq(m);
occ = pieces();
// Handle en passant moves
- if (is_enpassant(m))
+ if (type_of(m) == ENPASSANT)
{
Square capQq = to - pawn_push(sideToMove);
stm = ~color_of(piece_on(from));
stmAttackers = attackers & pieces(stm);
if (!stmAttackers)
- return PieceValueMidgame[capturedType];
+ return PieceValue[Mg][capturedType];
// The destination square is defended, which makes things rather more
// difficult to compute. We proceed by building up a "swap list" containing
// 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.
- swapList[0] = PieceValueMidgame[capturedType];
+ swapList[0] = PieceValue[Mg][capturedType];
capturedType = type_of(piece_on(from));
do {
// Locate the least valuable attacker for the side to move. The loop
// below looks like it is potentially infinite, but it isn't. We know
// that the side to move still has at least one attacker left.
- for (pt = PAWN; !(stmAttackers & pieces(pt)); pt++)
+ for (pt = PAWN; (b = stmAttackers & pieces(pt)) == 0; pt++)
assert(pt < KING);
// Remove the attacker we just found from the 'occupied' bitboard,
// and scan for new X-ray attacks behind the attacker.
- b = stmAttackers & pieces(pt);
occ ^= (b & (~b + 1));
attackers |= (attacks_bb<ROOK>(to, occ) & pieces(ROOK, QUEEN))
| (attacks_bb<BISHOP>(to, occ) & pieces(BISHOP, QUEEN));
// Add the new entry to the swap list
assert(slIndex < 32);
- swapList[slIndex] = -swapList[slIndex - 1] + PieceValueMidgame[capturedType];
+ swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[Mg][capturedType];
slIndex++;
// Remember the value of the capturing piece, and change the side to
if (capturedType == KING && stmAttackers)
{
assert(slIndex < 32);
- swapList[slIndex++] = QueenValueMidgame*10;
+ swapList[slIndex++] = QueenValueMg * 16;
break;
}
} while (stmAttackers);
Key Position::compute_key() const {
- Key k = zobCastle[st->castleRights];
+ Key k = Zobrist::castle[st->castleRights];
for (Bitboard b = pieces(); b; )
{
- Square s = pop_1st_bit(&b);
- k ^= zobrist[color_of(piece_on(s))][type_of(piece_on(s))][s];
+ Square s = pop_lsb(&b);
+ k ^= Zobrist::psq[color_of(piece_on(s))][type_of(piece_on(s))][s];
}
if (ep_square() != SQ_NONE)
- k ^= zobEp[file_of(ep_square())];
+ k ^= Zobrist::enpassant[file_of(ep_square())];
if (sideToMove == BLACK)
- k ^= zobSideToMove;
+ k ^= Zobrist::side;
return k;
}
for (Bitboard b = pieces(PAWN); b; )
{
- Square s = pop_1st_bit(&b);
- k ^= zobrist[color_of(piece_on(s))][PAWN][s];
+ Square s = pop_lsb(&b);
+ k ^= Zobrist::psq[color_of(piece_on(s))][PAWN][s];
}
return k;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
for (int cnt = 0; cnt < piece_count(c, pt); cnt++)
- k ^= zobrist[c][pt][cnt];
+ k ^= Zobrist::psq[c][pt][cnt];
return k;
}
for (Bitboard b = pieces(); b; )
{
- Square s = pop_1st_bit(&b);
+ Square s = pop_lsb(&b);
score += pieceSquareTable[piece_on(s)][s];
}
Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
- value += piece_count(c, pt) * PieceValueMidgame[pt];
+ value += piece_count(c, pt) * PieceValue[Mg][pt];
return value;
}
// Draw by material?
if ( !pieces(PAWN)
- && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMidgame))
+ && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg))
return true;
// Draw by the 50 moves rule?
- if (st->rule50 > 99 && (!in_check() || MoveList<MV_LEGAL>(*this).size()))
+ if (st->rule50 > 99 && (!in_check() || MoveList<LEGAL>(*this).size()))
return true;
// Draw by repetition?
template bool Position::is_draw<true>() const;
-/// Position::init() is a static member function which initializes at startup
-/// the various arrays used to compute hash keys and the piece square tables.
-/// The latter is a two-step operation: First, the white halves of the tables
-/// are copied from PSQT[] tables. Second, the black halves of the tables are
-/// initialized by flipping and changing the sign of the white scores.
-
-void Position::init() {
-
- RKISS rk;
-
- for (Color c = WHITE; c <= BLACK; c++)
- for (PieceType pt = PAWN; pt <= KING; pt++)
- for (Square s = SQ_A1; s <= SQ_H8; s++)
- zobrist[c][pt][s] = rk.rand<Key>();
-
- for (File f = FILE_A; f <= FILE_H; f++)
- zobEp[f] = rk.rand<Key>();
-
- for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
- {
- Bitboard b = cr;
- while (b)
- {
- Key k = zobCastle[1ULL << pop_1st_bit(&b)];
- zobCastle[cr] ^= k ? k : rk.rand<Key>();
- }
- }
-
- zobSideToMove = rk.rand<Key>();
- zobExclusion = rk.rand<Key>();
-
- for (PieceType pt = PAWN; pt <= KING; pt++)
- {
- Score v = make_score(PieceValueMidgame[pt], PieceValueEndgame[pt]);
-
- for (Square s = SQ_A1; s <= SQ_H8; s++)
- {
- pieceSquareTable[make_piece(WHITE, pt)][ s] = (v + PSQT[pt][s]);
- pieceSquareTable[make_piece(BLACK, pt)][~s] = -(v + PSQT[pt][s]);
- }
- }
-}
-
-
/// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging especially for finding evaluation symmetry bugs.