/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
- Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2008-2013 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <cassert>
#include <cstring>
+#include <iomanip>
#include <iostream>
#include <sstream>
#include <algorithm>
CACHE_LINE_ALIGNMENT
-Score pieceSquareTable[PIECE_NB][SQUARE_NB];
+Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Value PieceValue[PHASE_NB][PIECE_NB] = {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } };
namespace Zobrist {
-Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
-Key enpassant[FILE_NB];
-Key castle[CASTLE_RIGHT_NB];
-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]);
- }
- }
+ Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+ Key enpassant[FILE_NB];
+ Key castle[CASTLE_RIGHT_NB];
+ Key side;
+ Key exclusion;
}
-} // namespace Zobrist
-
+Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;}
namespace {
-/// next_attacker() is an helper function used by see() to locate the least
-/// valuable attacker for the side to move, remove the attacker we just found
-/// from the 'occupied' bitboard and scan for new X-ray attacks behind it.
+// min_attacker() is an helper function used by see() to locate the least
+// valuable attacker for the side to move, remove the attacker we just found
+// from the bitboards and scan for new X-ray attacks behind it.
template<int Pt> FORCE_INLINE
-PieceType next_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers,
- Bitboard& occupied, Bitboard& attackers) {
+PieceType min_attacker(const Bitboard* bb, const Square& to, const Bitboard& stmAttackers,
+ Bitboard& occupied, Bitboard& attackers) {
+
+ Bitboard b = stmAttackers & bb[Pt];
- if (stmAttackers & bb[Pt])
+ if (b)
{
- Bitboard b = stmAttackers & bb[Pt];
occupied ^= b & ~(b - 1);
if (Pt == PAWN || Pt == BISHOP || Pt == QUEEN)
return (PieceType)Pt;
}
- return next_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
+ return min_attacker<Pt+1>(bb, to, stmAttackers, occupied, attackers);
}
template<> FORCE_INLINE
-PieceType next_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
+PieceType min_attacker<KING>(const Bitboard*, const Square&, const Bitboard&, Bitboard&, Bitboard&) {
return KING; // No need to update bitboards, it is the last cycle
}
}
+/// Position::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 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::psq[c][pt][s] = rk.rand<Key>();
+
+ for (File f = FILE_A; f <= FILE_H; f++)
+ Zobrist::enpassant[f] = rk.rand<Key>();
+
+ for (int cr = CASTLES_NONE; cr <= ALL_CASTLES; cr++)
+ {
+ Bitboard b = cr;
+ while (b)
+ {
+ Key k = Zobrist::castle[1ULL << pop_lsb(&b)];
+ Zobrist::castle[cr] ^= k ? k : rk.rand<Key>();
+ }
+ }
+
+ Zobrist::side = rk.rand<Key>();
+ Zobrist::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++)
+ {
+ psq[WHITE][pt][ s] = (v + PSQT[pt][s]);
+ psq[BLACK][pt][~s] = -(v + PSQT[pt][s]);
+ }
+ }
+}
+
+
/// Position::operator=() creates a copy of 'pos'. We want the new born Position
/// object do not depend on any external data so we detach state pointer from
/// the source one.
Position& Position::operator=(const Position& pos) {
- memcpy(this, &pos, sizeof(Position));
+ std::memcpy(this, &pos, sizeof(Position));
startState = *st;
st = &startState;
nodes = 0;
}
// 5-6. Halfmove clock and fullmove number
- ss >> std::skipws >> st->rule50 >> startPosPly;
+ ss >> std::skipws >> st->rule50 >> gamePly;
// Convert from fullmove starting from 1 to ply starting from 0,
// handle also common incorrect FEN with fullmove = 0.
- startPosPly = std::max(2 * (startPosPly - 1), 0) + int(sideToMove == BLACK);
+ gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK);
st->key = compute_key();
st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key();
- st->psqScore = compute_psq_score();
+ st->psq = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
st->checkersBB = attackers_to(king_square(sideToMove)) & pieces(~sideToMove);
ss << '-';
ss << (ep_square() == SQ_NONE ? " - " : " " + square_to_string(ep_square()) + " ")
- << st->rule50 << " " << 1 + (startPosPly - int(sideToMove == BLACK)) / 2;
+ << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2;
return ss.str();
}
if (piece_on(sq) != NO_PIECE)
brd[513 - 68*rank_of(sq) + 4*file_of(sq)] = PieceToChar[piece_on(sq)];
- ss << brd << "\nFen: " << fen() << "\nKey: " << st->key << "\nCheckers: ";
+ ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase
+ << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: ";
for (Bitboard b = checkers(); b; )
ss << square_to_string(pop_lsb(&b)) << " ";
ss << "\nLegal moves: ";
- for (MoveList<LEGAL> ml(*this); !ml.end(); ++ml)
- ss << move_to_san(*const_cast<Position*>(this), ml.move()) << " ";
+ for (MoveList<LEGAL> it(*this); *it; ++it)
+ ss << move_to_san(*const_cast<Position*>(this), *it) << " ";
return ss.str();
}
-/// Position:hidden_checkers<>() returns a bitboard of all pinned (against the
-/// king) pieces for the given color. Or, when template parameter FindPinned is
-/// false, the function return the pieces of the given color candidate for a
-/// discovery check against the enemy king.
-template<bool FindPinned>
-Bitboard Position::hidden_checkers() const {
+/// Position:hidden_checkers() returns a bitboard of all pinned / discovery check
+/// pieces, according to the call parameters. Pinned pieces protect our king,
+/// discovery check pieces attack the enemy king.
- // Pinned pieces protect our king, dicovery checks attack the enemy king
- Bitboard b, result = 0;
- Bitboard pinners = pieces(FindPinned ? ~sideToMove : sideToMove);
- Square ksq = king_square(FindPinned ? sideToMove : ~sideToMove);
+Bitboard Position::hidden_checkers(Square ksq, Color c) const {
- // Pinners are sliders, that give check when candidate pinned is removed
- pinners &= (pieces(ROOK, QUEEN) & PseudoAttacks[ROOK][ksq])
- | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq]);
+ Bitboard b, pinners, result = 0;
+
+ // Pinners are sliders that give check when pinned piece is removed
+ pinners = ( (pieces( ROOK, QUEEN) & PseudoAttacks[ROOK ][ksq])
+ | (pieces(BISHOP, QUEEN) & PseudoAttacks[BISHOP][ksq])) & pieces(c);
while (pinners)
{
b = between_bb(ksq, pop_lsb(&pinners)) & pieces();
- if (b && !more_than_one(b) && (b & pieces(sideToMove)))
- result |= b;
+ if (!more_than_one(b))
+ result |= b & pieces(sideToMove);
}
return result;
}
-// Explicit template instantiations
-template Bitboard Position::hidden_checkers<true>() const;
-template Bitboard Position::hidden_checkers<false>() const;
-
/// Position::attackers_to() computes a bitboard of all pieces which attack a
/// given square. Slider attacks use occ bitboard as occupancy.
Square rfrom = to; // 'King captures the rook' notation
Square kto = relative_square(us, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, rfrom > kfrom ? SQ_F1 : SQ_D1);
- Bitboard b = (pieces() ^ kfrom ^ rfrom) | rto | kto;
- return attacks_bb<ROOK>(rto, b) & ksq;
+ return (PseudoAttacks[ROOK][rto] & ksq)
+ && (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & ksq);
}
default:
assert(false);
// Copy some fields of old state to our new StateInfo object except the ones
// which are going to be recalculated from scratch anyway, then switch our state
// pointer to point to the new, ready to be updated, state.
- memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
+ std::memcpy(&newSt, st, StateCopySize64 * sizeof(uint64_t));
newSt.previous = st;
st = &newSt;
// Update side to move
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.
+ // Increment ply counters.In particular rule50 will be later reset it to zero
+ // in case of a capture or a pawn move.
+ gamePly++;
st->rule50++;
st->pliesFromNull++;
Color them = ~us;
Square from = from_sq(m);
Square to = to_sq(m);
- Piece piece = piece_on(from);
- PieceType pt = type_of(piece);
+ Piece pc = piece_on(from);
+ PieceType pt = type_of(pc);
PieceType capture = type_of(m) == ENPASSANT ? PAWN : type_of(piece_on(to));
- assert(color_of(piece) == us);
+ assert(color_of(pc) == us);
assert(piece_on(to) == NO_PIECE || color_of(piece_on(to)) == them || type_of(m) == CASTLE);
assert(capture != KING);
if (type_of(m) == CASTLE)
{
- assert(piece == make_piece(us, KING));
+ assert(pc == make_piece(us, KING));
bool kingSide = to > from;
Square rfrom = to; // Castle is encoded as "king captures friendly rook"
do_castle(from, to, rfrom, rto);
- st->psqScore += psq_delta(make_piece(us, ROOK), rfrom, rto);
+ st->psq += psq[us][ROOK][rto] - psq[us][ROOK][rfrom];
k ^= Zobrist::psq[us][ROOK][rfrom] ^ Zobrist::psq[us][ROOK][rto];
}
prefetch((char*)thisThread->materialTable[st->materialKey]);
// Update incremental scores
- st->psqScore -= pieceSquareTable[make_piece(them, capture)][capsq];
+ st->psq -= psq[them][capture][capsq];
// Reset rule 50 counter
st->rule50 = 0;
byColorBB[us] ^= from_to_bb;
board[from] = NO_PIECE;
- board[to] = piece;
+ board[to] = pc;
// Update piece lists, index[from] is not updated and becomes stale. This
// works as long as index[] is accessed just by known occupied squares.
^ Zobrist::psq[us][PAWN][pieceCount[us][PAWN]];
// Update incremental score
- st->psqScore += pieceSquareTable[make_piece(us, promotion)][to]
- - pieceSquareTable[make_piece(us, PAWN)][to];
+ st->psq += psq[us][promotion][to] - psq[us][PAWN][to];
// Update material
st->npMaterial[us] += PieceValue[MG][promotion];
}
// Update incremental scores
- st->psqScore += psq_delta(piece, from, to);
+ st->psq += psq[us][pt][to] - psq[us][pt][from];
// Set capture piece
st->capturedType = capture;
// Finally point our state pointer back to the previous state
st = st->previous;
+ gamePly--;
assert(pos_is_ok());
}
assert(!checkers());
- memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
+ std::memcpy(&newSt, st, sizeof(StateInfo)); // Fully copy here
newSt.previous = st;
st = &newSt;
/// Position::see() is a static exchange evaluator: It tries to estimate the
-/// 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 captures.
+/// material gain or loss resulting from a move. Parameter 'asymmThreshold' takes
+/// tempi into account. If the side who initiated the capturing sequence does the
+/// last capture, he loses a tempo and if the result is below 'asymmThreshold'
+/// the capturing sequence is considered bad.
int Position::see_sign(Move m) const {
// 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][piece_on(to_sq(m))] >= PieceValue[MG][piece_moved(m)])
+ if (PieceValue[MG][piece_moved(m)] <= PieceValue[MG][piece_on(to_sq(m))])
return 1;
return see(m);
}
-int Position::see(Move m) const {
+int Position::see(Move m, int asymmThreshold) const {
Square from, to;
Bitboard occupied, attackers, stmAttackers;
swapList[slIndex] = -swapList[slIndex - 1] + PieceValue[MG][captured];
slIndex++;
- // Locate and remove from 'occupied' the next least valuable attacker
- captured = next_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
-
+ // Locate and remove the next least valuable attacker
+ captured = min_attacker<PAWN>(byTypeBB, to, stmAttackers, occupied, attackers);
attackers &= occupied; // Remove the just found attacker
stm = ~stm;
stmAttackers = attackers & pieces(stm);
} while (stmAttackers);
+ // If we are doing asymmetric SEE evaluation and the same side does the first
+ // and the last capture, he loses a tempo and gain must be at least worth
+ // 'asymmThreshold', otherwise we replace the score with a very low value,
+ // before negamaxing.
+ if (asymmThreshold)
+ for (int i = 0; i < slIndex; i += 2)
+ if (swapList[i] < asymmThreshold)
+ swapList[i] = - QueenValueMg * 16;
+
// 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)
void Position::clear() {
- memset(this, 0, sizeof(Position));
+ std::memset(this, 0, sizeof(Position));
startState.epSquare = SQ_NONE;
st = &startState;
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= QUEEN; pt++)
- for (int cnt = 0; cnt < piece_count(c, pt); cnt++)
+ for (int cnt = 0; cnt < pieceCount[c][pt]; cnt++)
k ^= Zobrist::psq[c][pt][cnt];
return k;
for (Bitboard b = pieces(); b; )
{
Square s = pop_lsb(&b);
- score += pieceSquareTable[piece_on(s)][s];
+ Piece pc = piece_on(s);
+ score += psq[color_of(pc)][type_of(pc)][s];
}
return score;
Value value = VALUE_ZERO;
for (PieceType pt = KNIGHT; pt <= QUEEN; pt++)
- value += piece_count(c, pt) * PieceValue[MG][pt];
+ value += pieceCount[c][pt] * PieceValue[MG][pt];
return value;
}
/// Position::is_draw() tests whether the position is drawn by material,
/// repetition, or the 50 moves rule. It does not detect stalemates, this
/// must be done by the search.
-template<bool SkipRepetition>
bool Position::is_draw() const {
// Draw by material?
return true;
// Draw by repetition?
- if (!SkipRepetition)
- {
- int i = 4, e = std::min(st->rule50, st->pliesFromNull);
+ int i = 4, e = std::min(st->rule50, st->pliesFromNull);
- if (i <= e)
- {
- StateInfo* stp = st->previous->previous;
+ if (i <= e)
+ {
+ StateInfo* stp = st->previous->previous;
- do {
- stp = stp->previous->previous;
+ do {
+ stp = stp->previous->previous;
- if (stp->key == st->key)
- return true;
+ if (stp->key == st->key)
+ return true;
- i += 2;
+ i += 2;
- } while (i <= e);
- }
+ } while (i <= e);
}
return false;
}
-// Explicit template instantiations
-template bool Position::is_draw<false>() const;
-template bool Position::is_draw<true>() const;
-
/// Position::flip() flips position with the white and black sides reversed. This
/// is only useful for debugging especially for finding evaluation symmetry bugs.
thisThread = pos.this_thread();
nodes = pos.nodes_searched();
chess960 = pos.is_chess960();
- startPosPly = pos.startpos_ply_counter();
+ gamePly = pos.game_ply();
for (Square s = SQ_A1; s <= SQ_H8; s++)
if (!pos.is_empty(s))
st->key = compute_key();
st->pawnKey = compute_pawn_key();
st->materialKey = compute_material_key();
- st->psqScore = compute_psq_score();
+ st->psq = compute_psq_score();
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
if ((*step)++, debugMaterialKey && st->materialKey != compute_material_key())
return false;
- if ((*step)++, debugIncrementalEval && st->psqScore != compute_psq_score())
+ if ((*step)++, debugIncrementalEval && st->psq != compute_psq_score())
return false;
if ((*step)++, debugNonPawnMaterial)
- {
if ( st->npMaterial[WHITE] != compute_non_pawn_material(WHITE)
|| st->npMaterial[BLACK] != compute_non_pawn_material(BLACK))
return false;
- }
if ((*step)++, debugPieceCounts)
for (Color c = WHITE; c <= BLACK; c++)
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (int i = 0; i < pieceCount[c][pt]; i++)
- {
- if (piece_on(piece_list(c, pt)[i]) != make_piece(c, pt))
- return false;
-
- if (index[piece_list(c, pt)[i]] != i)
+ if ( board[pieceList[c][pt][i]] != make_piece(c, pt)
+ || index[pieceList[c][pt][i]] != i)
return false;
- }
if ((*step)++, debugCastleSquares)
for (Color c = WHITE; c <= BLACK; c++)
if (!can_castle(cr))
continue;
- if ((castleRightsMask[king_square(c)] & cr) != cr)
- return false;
-
- if ( piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
+ if ( (castleRightsMask[king_square(c)] & cr) != cr
+ || piece_on(castleRookSquare[c][s]) != make_piece(c, ROOK)
|| castleRightsMask[castleRookSquare[c][s]] != cr)
return false;
}