mirror of
https://github.com/sockspls/badfish
synced 2025-04-30 08:43:09 +00:00
Remove more unrelated files
This commit is contained in:
parent
ece8f0780f
commit
f3cf782beb
8 changed files with 0 additions and 3091 deletions
418
src/movegen.cpp
418
src/movegen.cpp
|
@ -1,418 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 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
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "movegen.h"
|
|
||||||
#include "position.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
template<CastlingRight Cr, bool Checks, bool Chess960>
|
|
||||||
ExtMove* generate_castling(const Position& pos, ExtMove* moveList, Color us, const CheckInfo* ci) {
|
|
||||||
|
|
||||||
static const bool KingSide = (Cr == WHITE_OO || Cr == BLACK_OO);
|
|
||||||
|
|
||||||
if (pos.castling_impeded(Cr) || !pos.can_castle(Cr))
|
|
||||||
return moveList;
|
|
||||||
|
|
||||||
// After castling, the rook and king final positions are the same in Chess960
|
|
||||||
// as they would be in standard chess.
|
|
||||||
Square kfrom = pos.king_square(us);
|
|
||||||
Square rfrom = pos.castling_rook_square(Cr);
|
|
||||||
Square kto = relative_square(us, KingSide ? SQ_G1 : SQ_C1);
|
|
||||||
Bitboard enemies = pos.pieces(~us);
|
|
||||||
|
|
||||||
assert(!pos.checkers());
|
|
||||||
|
|
||||||
const Square K = Chess960 ? kto > kfrom ? DELTA_W : DELTA_E
|
|
||||||
: KingSide ? DELTA_W : DELTA_E;
|
|
||||||
|
|
||||||
for (Square s = kto; s != kfrom; s += K)
|
|
||||||
if (pos.attackers_to(s) & enemies)
|
|
||||||
return moveList;
|
|
||||||
|
|
||||||
// Because we generate only legal castling moves we need to verify that
|
|
||||||
// when moving the castling rook we do not discover some hidden checker.
|
|
||||||
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
|
||||||
if (Chess960 && (attacks_bb<ROOK>(kto, pos.pieces() ^ rfrom) & pos.pieces(~us, ROOK, QUEEN)))
|
|
||||||
return moveList;
|
|
||||||
|
|
||||||
Move m = make<CASTLING>(kfrom, rfrom);
|
|
||||||
|
|
||||||
if (Checks && !pos.gives_check(m, *ci))
|
|
||||||
return moveList;
|
|
||||||
|
|
||||||
(moveList++)->move = m;
|
|
||||||
|
|
||||||
return moveList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<GenType Type, Square Delta>
|
|
||||||
inline ExtMove* make_promotions(ExtMove* moveList, Square to, const CheckInfo* ci) {
|
|
||||||
|
|
||||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
|
||||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, QUEEN);
|
|
||||||
|
|
||||||
if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
|
|
||||||
{
|
|
||||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, ROOK);
|
|
||||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, BISHOP);
|
|
||||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Knight promotion is the only promotion that can give a direct check
|
|
||||||
// that's not already included in the queen promotion.
|
|
||||||
if (Type == QUIET_CHECKS && (StepAttacksBB[W_KNIGHT][to] & ci->ksq))
|
|
||||||
(moveList++)->move = make<PROMOTION>(to - Delta, to, KNIGHT);
|
|
||||||
else
|
|
||||||
(void)ci; // Silence a warning under MSVC
|
|
||||||
|
|
||||||
return moveList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<Color Us, GenType Type>
|
|
||||||
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList,
|
|
||||||
Bitboard target, const CheckInfo* ci) {
|
|
||||||
|
|
||||||
// Compute our parametrized parameters at compile time, named according to
|
|
||||||
// the point of view of white side.
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
|
||||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
|
||||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
|
||||||
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
|
||||||
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
|
|
||||||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
|
||||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
|
||||||
|
|
||||||
Bitboard emptySquares;
|
|
||||||
|
|
||||||
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
|
|
||||||
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
|
|
||||||
|
|
||||||
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
|
|
||||||
Type == CAPTURES ? target : pos.pieces(Them));
|
|
||||||
|
|
||||||
// Single and double pawn pushes, no promotions
|
|
||||||
if (Type != CAPTURES)
|
|
||||||
{
|
|
||||||
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
|
|
||||||
|
|
||||||
Bitboard b1 = shift_bb<Up>(pawnsNotOn7) & emptySquares;
|
|
||||||
Bitboard b2 = shift_bb<Up>(b1 & TRank3BB) & emptySquares;
|
|
||||||
|
|
||||||
if (Type == EVASIONS) // Consider only blocking squares
|
|
||||||
{
|
|
||||||
b1 &= target;
|
|
||||||
b2 &= target;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Type == QUIET_CHECKS)
|
|
||||||
{
|
|
||||||
b1 &= pos.attacks_from<PAWN>(ci->ksq, Them);
|
|
||||||
b2 &= pos.attacks_from<PAWN>(ci->ksq, Them);
|
|
||||||
|
|
||||||
// Add pawn pushes which give discovered check. This is possible only
|
|
||||||
// if the pawn is not on the same file as the enemy king, because we
|
|
||||||
// don't generate captures. Note that a possible discovery check
|
|
||||||
// promotion has been already generated amongst the captures.
|
|
||||||
if (pawnsNotOn7 & ci->dcCandidates)
|
|
||||||
{
|
|
||||||
Bitboard dc1 = shift_bb<Up>(pawnsNotOn7 & ci->dcCandidates) & emptySquares & ~file_bb(ci->ksq);
|
|
||||||
Bitboard dc2 = shift_bb<Up>(dc1 & TRank3BB) & emptySquares;
|
|
||||||
|
|
||||||
b1 |= dc1;
|
|
||||||
b2 |= dc2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (b1)
|
|
||||||
{
|
|
||||||
Square to = pop_lsb(&b1);
|
|
||||||
(moveList++)->move = make_move(to - Up, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (b2)
|
|
||||||
{
|
|
||||||
Square to = pop_lsb(&b2);
|
|
||||||
(moveList++)->move = make_move(to - Up - Up, to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Promotions and underpromotions
|
|
||||||
if (pawnsOn7 && (Type != EVASIONS || (target & TRank8BB)))
|
|
||||||
{
|
|
||||||
if (Type == CAPTURES)
|
|
||||||
emptySquares = ~pos.pieces();
|
|
||||||
|
|
||||||
if (Type == EVASIONS)
|
|
||||||
emptySquares &= target;
|
|
||||||
|
|
||||||
Bitboard b1 = shift_bb<Right>(pawnsOn7) & enemies;
|
|
||||||
Bitboard b2 = shift_bb<Left >(pawnsOn7) & enemies;
|
|
||||||
Bitboard b3 = shift_bb<Up >(pawnsOn7) & emptySquares;
|
|
||||||
|
|
||||||
while (b1)
|
|
||||||
moveList = make_promotions<Type, Right>(moveList, pop_lsb(&b1), ci);
|
|
||||||
|
|
||||||
while (b2)
|
|
||||||
moveList = make_promotions<Type, Left >(moveList, pop_lsb(&b2), ci);
|
|
||||||
|
|
||||||
while (b3)
|
|
||||||
moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ci);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard and en-passant captures
|
|
||||||
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
|
|
||||||
{
|
|
||||||
Bitboard b1 = shift_bb<Right>(pawnsNotOn7) & enemies;
|
|
||||||
Bitboard b2 = shift_bb<Left >(pawnsNotOn7) & enemies;
|
|
||||||
|
|
||||||
while (b1)
|
|
||||||
{
|
|
||||||
Square to = pop_lsb(&b1);
|
|
||||||
(moveList++)->move = make_move(to - Right, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (b2)
|
|
||||||
{
|
|
||||||
Square to = pop_lsb(&b2);
|
|
||||||
(moveList++)->move = make_move(to - Left, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos.ep_square() != SQ_NONE)
|
|
||||||
{
|
|
||||||
assert(rank_of(pos.ep_square()) == relative_rank(Us, RANK_6));
|
|
||||||
|
|
||||||
// An en passant capture can be an evasion only if the checking piece
|
|
||||||
// is the double pushed pawn and so is in the target. Otherwise this
|
|
||||||
// is a discovery check and we are forced to do otherwise.
|
|
||||||
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
|
|
||||||
return moveList;
|
|
||||||
|
|
||||||
b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
|
||||||
|
|
||||||
assert(b1);
|
|
||||||
|
|
||||||
while (b1)
|
|
||||||
(moveList++)->move = make<ENPASSANT>(pop_lsb(&b1), pos.ep_square());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return moveList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<PieceType Pt, bool Checks> FORCE_INLINE
|
|
||||||
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
|
|
||||||
Bitboard target, const CheckInfo* ci) {
|
|
||||||
|
|
||||||
assert(Pt != KING && Pt != PAWN);
|
|
||||||
|
|
||||||
const Square* pl = pos.list<Pt>(us);
|
|
||||||
|
|
||||||
for (Square from = *pl; from != SQ_NONE; from = *++pl)
|
|
||||||
{
|
|
||||||
if (Checks)
|
|
||||||
{
|
|
||||||
if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
|
|
||||||
&& !(PseudoAttacks[Pt][from] & target & ci->checkSq[Pt]))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (unlikely(ci->dcCandidates) && (ci->dcCandidates & from))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Bitboard b = pos.attacks_from<Pt>(from) & target;
|
|
||||||
|
|
||||||
if (Checks)
|
|
||||||
b &= ci->checkSq[Pt];
|
|
||||||
|
|
||||||
while (b)
|
|
||||||
(moveList++)->move = make_move(from, pop_lsb(&b));
|
|
||||||
}
|
|
||||||
|
|
||||||
return moveList;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template<Color Us, GenType Type> FORCE_INLINE
|
|
||||||
ExtMove* generate_all(const Position& pos, ExtMove* moveList, Bitboard target,
|
|
||||||
const CheckInfo* ci = NULL) {
|
|
||||||
|
|
||||||
const bool Checks = Type == QUIET_CHECKS;
|
|
||||||
|
|
||||||
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target, ci);
|
|
||||||
moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target, ci);
|
|
||||||
moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target, ci);
|
|
||||||
moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target, ci);
|
|
||||||
moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target, ci);
|
|
||||||
|
|
||||||
if (Type != QUIET_CHECKS && Type != EVASIONS)
|
|
||||||
{
|
|
||||||
Square ksq = pos.king_square(Us);
|
|
||||||
Bitboard b = pos.attacks_from<KING>(ksq) & target;
|
|
||||||
while (b)
|
|
||||||
(moveList++)->move = make_move(ksq, pop_lsb(&b));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
|
|
||||||
{
|
|
||||||
if (pos.is_chess960())
|
|
||||||
{
|
|
||||||
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, true>(pos, moveList, Us, ci);
|
|
||||||
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, true>(pos, moveList, Us, ci);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
moveList = generate_castling<MakeCastling<Us, KING_SIDE>::right, Checks, false>(pos, moveList, Us, ci);
|
|
||||||
moveList = generate_castling<MakeCastling<Us, QUEEN_SIDE>::right, Checks, false>(pos, moveList, Us, ci);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return moveList;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
/// generate<CAPTURES> generates all pseudo-legal captures and queen
|
|
||||||
/// promotions. Returns a pointer to the end of the move list.
|
|
||||||
///
|
|
||||||
/// generate<QUIETS> generates all pseudo-legal non-captures and
|
|
||||||
/// underpromotions. Returns a pointer to the end of the move list.
|
|
||||||
///
|
|
||||||
/// generate<NON_EVASIONS> generates all pseudo-legal captures and
|
|
||||||
/// non-captures. Returns a pointer to the end of the move list.
|
|
||||||
|
|
||||||
template<GenType Type>
|
|
||||||
ExtMove* generate(const Position& pos, ExtMove* moveList) {
|
|
||||||
|
|
||||||
assert(Type == CAPTURES || Type == QUIETS || Type == NON_EVASIONS);
|
|
||||||
assert(!pos.checkers());
|
|
||||||
|
|
||||||
Color us = pos.side_to_move();
|
|
||||||
|
|
||||||
Bitboard target = Type == CAPTURES ? pos.pieces(~us)
|
|
||||||
: Type == QUIETS ? ~pos.pieces()
|
|
||||||
: Type == NON_EVASIONS ? ~pos.pieces(us) : 0;
|
|
||||||
|
|
||||||
return us == WHITE ? generate_all<WHITE, Type>(pos, moveList, target)
|
|
||||||
: generate_all<BLACK, Type>(pos, moveList, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicit template instantiations
|
|
||||||
template ExtMove* generate<CAPTURES>(const Position&, ExtMove*);
|
|
||||||
template ExtMove* generate<QUIETS>(const Position&, ExtMove*);
|
|
||||||
template ExtMove* generate<NON_EVASIONS>(const Position&, ExtMove*);
|
|
||||||
|
|
||||||
|
|
||||||
/// generate<QUIET_CHECKS> generates all pseudo-legal non-captures and knight
|
|
||||||
/// underpromotions that give check. Returns a pointer to the end of the move list.
|
|
||||||
template<>
|
|
||||||
ExtMove* generate<QUIET_CHECKS>(const Position& pos, ExtMove* moveList) {
|
|
||||||
|
|
||||||
assert(!pos.checkers());
|
|
||||||
|
|
||||||
Color us = pos.side_to_move();
|
|
||||||
CheckInfo ci(pos);
|
|
||||||
Bitboard dc = ci.dcCandidates;
|
|
||||||
|
|
||||||
while (dc)
|
|
||||||
{
|
|
||||||
Square from = pop_lsb(&dc);
|
|
||||||
PieceType pt = type_of(pos.piece_on(from));
|
|
||||||
|
|
||||||
if (pt == PAWN)
|
|
||||||
continue; // Will be generated together with direct checks
|
|
||||||
|
|
||||||
Bitboard b = pos.attacks_from(Piece(pt), from) & ~pos.pieces();
|
|
||||||
|
|
||||||
if (pt == KING)
|
|
||||||
b &= ~PseudoAttacks[QUEEN][ci.ksq];
|
|
||||||
|
|
||||||
while (b)
|
|
||||||
(moveList++)->move = make_move(from, pop_lsb(&b));
|
|
||||||
}
|
|
||||||
|
|
||||||
return us == WHITE ? generate_all<WHITE, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci)
|
|
||||||
: generate_all<BLACK, QUIET_CHECKS>(pos, moveList, ~pos.pieces(), &ci);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// generate<EVASIONS> generates all pseudo-legal check evasions when the side
|
|
||||||
/// to move is in check. Returns a pointer to the end of the move list.
|
|
||||||
template<>
|
|
||||||
ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
|
|
||||||
|
|
||||||
assert(pos.checkers());
|
|
||||||
|
|
||||||
Color us = pos.side_to_move();
|
|
||||||
Square ksq = pos.king_square(us);
|
|
||||||
Bitboard sliderAttacks = 0;
|
|
||||||
Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
|
|
||||||
|
|
||||||
// Find all the squares attacked by slider checkers. We will remove them from
|
|
||||||
// the king evasions in order to skip known illegal moves, which avoids any
|
|
||||||
// useless legality checks later on.
|
|
||||||
while (sliders)
|
|
||||||
{
|
|
||||||
Square checksq = pop_lsb(&sliders);
|
|
||||||
sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate evasions for king, capture and non capture moves
|
|
||||||
Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
|
|
||||||
while (b)
|
|
||||||
(moveList++)->move = make_move(ksq, pop_lsb(&b));
|
|
||||||
|
|
||||||
if (more_than_one(pos.checkers()))
|
|
||||||
return moveList; // Double check, only a king move can save the day
|
|
||||||
|
|
||||||
// Generate blocking evasions or captures of the checking piece
|
|
||||||
Square checksq = lsb(pos.checkers());
|
|
||||||
Bitboard target = between_bb(checksq, ksq) | checksq;
|
|
||||||
|
|
||||||
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
|
|
||||||
: generate_all<BLACK, EVASIONS>(pos, moveList, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// generate<LEGAL> generates all the legal moves in the given position
|
|
||||||
|
|
||||||
template<>
|
|
||||||
ExtMove* generate<LEGAL>(const Position& pos, ExtMove* moveList) {
|
|
||||||
|
|
||||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
|
||||||
Square ksq = pos.king_square(pos.side_to_move());
|
|
||||||
ExtMove* cur = moveList;
|
|
||||||
|
|
||||||
moveList = pos.checkers() ? generate<EVASIONS >(pos, moveList)
|
|
||||||
: generate<NON_EVASIONS>(pos, moveList);
|
|
||||||
while (cur != moveList)
|
|
||||||
if ( (pinned || from_sq(cur->move) == ksq || type_of(cur->move) == ENPASSANT)
|
|
||||||
&& !pos.legal(cur->move, pinned))
|
|
||||||
cur->move = (--moveList)->move;
|
|
||||||
else
|
|
||||||
++cur;
|
|
||||||
|
|
||||||
return moveList;
|
|
||||||
}
|
|
|
@ -1,58 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 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
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MOVEGEN_H_INCLUDED
|
|
||||||
#define MOVEGEN_H_INCLUDED
|
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
enum GenType {
|
|
||||||
CAPTURES,
|
|
||||||
QUIETS,
|
|
||||||
QUIET_CHECKS,
|
|
||||||
EVASIONS,
|
|
||||||
NON_EVASIONS,
|
|
||||||
LEGAL
|
|
||||||
};
|
|
||||||
|
|
||||||
class Position;
|
|
||||||
|
|
||||||
template<GenType>
|
|
||||||
ExtMove* generate(const Position& pos, ExtMove* moveList);
|
|
||||||
|
|
||||||
/// The MoveList struct is a simple wrapper around generate(). It sometimes comes
|
|
||||||
/// in handy to use this class instead of the low level generate() function.
|
|
||||||
template<GenType T>
|
|
||||||
struct MoveList {
|
|
||||||
|
|
||||||
explicit MoveList(const Position& pos) : cur(moveList), last(generate<T>(pos, moveList)) { last->move = MOVE_NONE; }
|
|
||||||
void operator++() { ++cur; }
|
|
||||||
Move operator*() const { return cur->move; }
|
|
||||||
size_t size() const { return last - moveList; }
|
|
||||||
bool contains(Move m) const {
|
|
||||||
for (const ExtMove* it(moveList); it != last; ++it) if (it->move == m) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
ExtMove moveList[MAX_MOVES];
|
|
||||||
ExtMove *cur, *last;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // #ifndef MOVEGEN_H_INCLUDED
|
|
385
src/movepick.cpp
385
src/movepick.cpp
|
@ -1,385 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 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
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "movepick.h"
|
|
||||||
#include "thread.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
enum Stages {
|
|
||||||
MAIN_SEARCH, CAPTURES_S1, KILLERS_S1, QUIETS_1_S1, QUIETS_2_S1, BAD_CAPTURES_S1,
|
|
||||||
EVASION, EVASIONS_S2,
|
|
||||||
QSEARCH_0, CAPTURES_S3, QUIET_CHECKS_S3,
|
|
||||||
QSEARCH_1, CAPTURES_S4,
|
|
||||||
PROBCUT, CAPTURES_S5,
|
|
||||||
RECAPTURE, CAPTURES_S6,
|
|
||||||
STOP
|
|
||||||
};
|
|
||||||
|
|
||||||
// Our insertion sort, which is guaranteed (and also needed) to be stable
|
|
||||||
void insertion_sort(ExtMove* begin, ExtMove* end)
|
|
||||||
{
|
|
||||||
ExtMove tmp, *p, *q;
|
|
||||||
|
|
||||||
for (p = begin + 1; p < end; ++p)
|
|
||||||
{
|
|
||||||
tmp = *p;
|
|
||||||
for (q = p; q != begin && *(q-1) < tmp; --q)
|
|
||||||
*q = *(q-1);
|
|
||||||
*q = tmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unary predicate used by std::partition to split positive values from remaining
|
|
||||||
// ones so as to sort the two sets separately, with the second sort delayed.
|
|
||||||
inline bool has_positive_value(const ExtMove& move) { return move.value > 0; }
|
|
||||||
|
|
||||||
// Picks the best move in the range (begin, end) and moves it to the front.
|
|
||||||
// It's faster than sorting all the moves in advance when there are few
|
|
||||||
// moves e.g. possible captures.
|
|
||||||
inline ExtMove* pick_best(ExtMove* begin, ExtMove* end)
|
|
||||||
{
|
|
||||||
std::swap(*begin, *std::max_element(begin, end));
|
|
||||||
return begin;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
|
|
||||||
/// Constructors of the MovePicker class. As arguments we pass information
|
|
||||||
/// to help it to return the (presumably) good moves first, to decide which
|
|
||||||
/// moves to return (in the quiescence search, for instance, we only want to
|
|
||||||
/// search captures, promotions and some checks) and how important good move
|
|
||||||
/// ordering is at the current node.
|
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
|
|
||||||
Move* cm, Move* fm, Search::Stack* s) : pos(p), history(h), depth(d) {
|
|
||||||
|
|
||||||
assert(d > DEPTH_ZERO);
|
|
||||||
|
|
||||||
cur = end = moves;
|
|
||||||
endBadCaptures = moves + MAX_MOVES - 1;
|
|
||||||
countermoves = cm;
|
|
||||||
followupmoves = fm;
|
|
||||||
ss = s;
|
|
||||||
|
|
||||||
if (pos.checkers())
|
|
||||||
stage = EVASION;
|
|
||||||
|
|
||||||
else
|
|
||||||
stage = MAIN_SEARCH;
|
|
||||||
|
|
||||||
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
|
||||||
end += (ttMove != MOVE_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const HistoryStats& h,
|
|
||||||
Square s) : pos(p), history(h), cur(moves), end(moves) {
|
|
||||||
|
|
||||||
assert(d <= DEPTH_ZERO);
|
|
||||||
|
|
||||||
if (pos.checkers())
|
|
||||||
stage = EVASION;
|
|
||||||
|
|
||||||
else if (d > DEPTH_QS_NO_CHECKS)
|
|
||||||
stage = QSEARCH_0;
|
|
||||||
|
|
||||||
else if (d > DEPTH_QS_RECAPTURES)
|
|
||||||
stage = QSEARCH_1;
|
|
||||||
|
|
||||||
else
|
|
||||||
{
|
|
||||||
stage = RECAPTURE;
|
|
||||||
recaptureSquare = s;
|
|
||||||
ttm = MOVE_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
|
||||||
end += (ttMove != MOVE_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
MovePicker::MovePicker(const Position& p, Move ttm, const HistoryStats& h, PieceType pt)
|
|
||||||
: pos(p), history(h), cur(moves), end(moves) {
|
|
||||||
|
|
||||||
assert(!pos.checkers());
|
|
||||||
|
|
||||||
stage = PROBCUT;
|
|
||||||
|
|
||||||
// In ProbCut we generate only captures that are better than the parent's
|
|
||||||
// captured piece.
|
|
||||||
captureThreshold = PieceValue[MG][pt];
|
|
||||||
ttMove = (ttm && pos.pseudo_legal(ttm) ? ttm : MOVE_NONE);
|
|
||||||
|
|
||||||
if (ttMove && (!pos.capture(ttMove) || pos.see(ttMove) <= captureThreshold))
|
|
||||||
ttMove = MOVE_NONE;
|
|
||||||
|
|
||||||
end += (ttMove != MOVE_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// score() assign a numerical value to each move in a move list. The moves with
|
|
||||||
/// highest values will be picked first.
|
|
||||||
template<>
|
|
||||||
void MovePicker::score<CAPTURES>() {
|
|
||||||
// Winning and equal captures in the main search are ordered by MVV/LVA.
|
|
||||||
// Suprisingly, this appears to perform slightly better than SEE based
|
|
||||||
// move ordering. The reason is probably that in a position with a winning
|
|
||||||
// capture, capturing a more valuable (but sufficiently defended) piece
|
|
||||||
// first usually doesn't hurt. The opponent will have to recapture, and
|
|
||||||
// the hanging piece will still be hanging (except in the unusual cases
|
|
||||||
// where it is possible to recapture with the hanging piece). Exchanging
|
|
||||||
// big pieces before capturing a hanging piece probably helps to reduce
|
|
||||||
// the subtree size.
|
|
||||||
// In main search we want to push captures with negative SEE values to the
|
|
||||||
// badCaptures[] array, but instead of doing it now we delay until the move
|
|
||||||
// has been picked up in pick_move_from_list(). This way we save some SEE
|
|
||||||
// calls in case we get a cutoff.
|
|
||||||
Move m;
|
|
||||||
|
|
||||||
for (ExtMove* it = moves; it != end; ++it)
|
|
||||||
{
|
|
||||||
m = it->move;
|
|
||||||
it->value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
|
||||||
- Value(type_of(pos.moved_piece(m)));
|
|
||||||
|
|
||||||
if (type_of(m) == ENPASSANT)
|
|
||||||
it->value += PieceValue[MG][PAWN];
|
|
||||||
|
|
||||||
else if (type_of(m) == PROMOTION)
|
|
||||||
it->value += PieceValue[MG][promotion_type(m)] - PieceValue[MG][PAWN];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
void MovePicker::score<QUIETS>() {
|
|
||||||
|
|
||||||
Move m;
|
|
||||||
|
|
||||||
for (ExtMove* it = moves; it != end; ++it)
|
|
||||||
{
|
|
||||||
m = it->move;
|
|
||||||
it->value = history[pos.moved_piece(m)][to_sq(m)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
void MovePicker::score<EVASIONS>() {
|
|
||||||
// Try good captures ordered by MVV/LVA, then non-captures if destination square
|
|
||||||
// is not under attack, ordered by history value, then bad-captures and quiet
|
|
||||||
// moves with a negative SEE. This last group is ordered by the SEE value.
|
|
||||||
Move m;
|
|
||||||
Value see;
|
|
||||||
|
|
||||||
for (ExtMove* it = moves; it != end; ++it)
|
|
||||||
{
|
|
||||||
m = it->move;
|
|
||||||
if ((see = pos.see_sign(m)) < VALUE_ZERO)
|
|
||||||
it->value = see - HistoryStats::Max; // At the bottom
|
|
||||||
|
|
||||||
else if (pos.capture(m))
|
|
||||||
it->value = PieceValue[MG][pos.piece_on(to_sq(m))]
|
|
||||||
- Value(type_of(pos.moved_piece(m))) + HistoryStats::Max;
|
|
||||||
else
|
|
||||||
it->value = history[pos.moved_piece(m)][to_sq(m)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// generate_next_stage() generates, scores and sorts the next bunch of moves,
|
|
||||||
/// when there are no more moves to try for the current stage.
|
|
||||||
|
|
||||||
void MovePicker::generate_next_stage() {
|
|
||||||
|
|
||||||
cur = moves;
|
|
||||||
|
|
||||||
switch (++stage) {
|
|
||||||
|
|
||||||
case CAPTURES_S1: case CAPTURES_S3: case CAPTURES_S4: case CAPTURES_S5: case CAPTURES_S6:
|
|
||||||
end = generate<CAPTURES>(pos, moves);
|
|
||||||
score<CAPTURES>();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case KILLERS_S1:
|
|
||||||
cur = killers;
|
|
||||||
end = cur + 2;
|
|
||||||
|
|
||||||
killers[0].move = ss->killers[0];
|
|
||||||
killers[1].move = ss->killers[1];
|
|
||||||
killers[2].move = killers[3].move = MOVE_NONE;
|
|
||||||
killers[4].move = killers[5].move = MOVE_NONE;
|
|
||||||
|
|
||||||
// Please note that following code is racy and could yield to rare (less
|
|
||||||
// than 1 out of a million) duplicated entries in SMP case. This is harmless.
|
|
||||||
|
|
||||||
// Be sure countermoves are different from killers
|
|
||||||
for (int i = 0; i < 2; ++i)
|
|
||||||
if ( countermoves[i] != (cur+0)->move
|
|
||||||
&& countermoves[i] != (cur+1)->move)
|
|
||||||
(end++)->move = countermoves[i];
|
|
||||||
|
|
||||||
// Be sure followupmoves are different from killers and countermoves
|
|
||||||
for (int i = 0; i < 2; ++i)
|
|
||||||
if ( followupmoves[i] != (cur+0)->move
|
|
||||||
&& followupmoves[i] != (cur+1)->move
|
|
||||||
&& followupmoves[i] != (cur+2)->move
|
|
||||||
&& followupmoves[i] != (cur+3)->move)
|
|
||||||
(end++)->move = followupmoves[i];
|
|
||||||
return;
|
|
||||||
|
|
||||||
case QUIETS_1_S1:
|
|
||||||
endQuiets = end = generate<QUIETS>(pos, moves);
|
|
||||||
score<QUIETS>();
|
|
||||||
end = std::partition(cur, end, has_positive_value);
|
|
||||||
insertion_sort(cur, end);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case QUIETS_2_S1:
|
|
||||||
cur = end;
|
|
||||||
end = endQuiets;
|
|
||||||
if (depth >= 3 * ONE_PLY)
|
|
||||||
insertion_sort(cur, end);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case BAD_CAPTURES_S1:
|
|
||||||
// Just pick them in reverse order to get MVV/LVA ordering
|
|
||||||
cur = moves + MAX_MOVES - 1;
|
|
||||||
end = endBadCaptures;
|
|
||||||
return;
|
|
||||||
|
|
||||||
case EVASIONS_S2:
|
|
||||||
end = generate<EVASIONS>(pos, moves);
|
|
||||||
if (end > moves + 1)
|
|
||||||
score<EVASIONS>();
|
|
||||||
return;
|
|
||||||
|
|
||||||
case QUIET_CHECKS_S3:
|
|
||||||
end = generate<QUIET_CHECKS>(pos, moves);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT: case RECAPTURE:
|
|
||||||
stage = STOP;
|
|
||||||
/* Fall through */
|
|
||||||
|
|
||||||
case STOP:
|
|
||||||
end = cur + 1; // Avoid another next_phase() call
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// next_move() is the most important method of the MovePicker class. It returns
|
|
||||||
/// a new pseudo legal move every time it is called, until there are no more moves
|
|
||||||
/// left. It picks the move with the biggest value from a list of generated moves
|
|
||||||
/// taking care not to return the ttMove if it has already been searched.
|
|
||||||
template<>
|
|
||||||
Move MovePicker::next_move<false>() {
|
|
||||||
|
|
||||||
Move move;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
while (cur == end)
|
|
||||||
generate_next_stage();
|
|
||||||
|
|
||||||
switch (stage) {
|
|
||||||
|
|
||||||
case MAIN_SEARCH: case EVASION: case QSEARCH_0: case QSEARCH_1: case PROBCUT:
|
|
||||||
++cur;
|
|
||||||
return ttMove;
|
|
||||||
|
|
||||||
case CAPTURES_S1:
|
|
||||||
move = pick_best(cur++, end)->move;
|
|
||||||
if (move != ttMove)
|
|
||||||
{
|
|
||||||
if (pos.see_sign(move) >= VALUE_ZERO)
|
|
||||||
return move;
|
|
||||||
|
|
||||||
// Losing capture, move it to the tail of the array
|
|
||||||
(endBadCaptures--)->move = move;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case KILLERS_S1:
|
|
||||||
move = (cur++)->move;
|
|
||||||
if ( move != MOVE_NONE
|
|
||||||
&& move != ttMove
|
|
||||||
&& pos.pseudo_legal(move)
|
|
||||||
&& !pos.capture(move))
|
|
||||||
return move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QUIETS_1_S1: case QUIETS_2_S1:
|
|
||||||
move = (cur++)->move;
|
|
||||||
if ( move != ttMove
|
|
||||||
&& move != killers[0].move
|
|
||||||
&& move != killers[1].move
|
|
||||||
&& move != killers[2].move
|
|
||||||
&& move != killers[3].move
|
|
||||||
&& move != killers[4].move
|
|
||||||
&& move != killers[5].move)
|
|
||||||
return move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BAD_CAPTURES_S1:
|
|
||||||
return (cur--)->move;
|
|
||||||
|
|
||||||
case EVASIONS_S2: case CAPTURES_S3: case CAPTURES_S4:
|
|
||||||
move = pick_best(cur++, end)->move;
|
|
||||||
if (move != ttMove)
|
|
||||||
return move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CAPTURES_S5:
|
|
||||||
move = pick_best(cur++, end)->move;
|
|
||||||
if (move != ttMove && pos.see(move) > captureThreshold)
|
|
||||||
return move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CAPTURES_S6:
|
|
||||||
move = pick_best(cur++, end)->move;
|
|
||||||
if (to_sq(move) == recaptureSquare)
|
|
||||||
return move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case QUIET_CHECKS_S3:
|
|
||||||
move = (cur++)->move;
|
|
||||||
if (move != ttMove)
|
|
||||||
return move;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STOP:
|
|
||||||
return MOVE_NONE;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Version of next_move() to use at split point nodes where the move is grabbed
|
|
||||||
/// from the split point's shared MovePicker object. This function is not thread
|
|
||||||
/// safe so must be lock protected by the caller.
|
|
||||||
template<>
|
|
||||||
Move MovePicker::next_move<true>() { return ss->splitPoint->movePicker->next_move<false>(); }
|
|
112
src/movepick.h
112
src/movepick.h
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 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
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef MOVEPICK_H_INCLUDED
|
|
||||||
#define MOVEPICK_H_INCLUDED
|
|
||||||
|
|
||||||
#include <algorithm> // For std::max
|
|
||||||
#include <cstring> // For std::memset
|
|
||||||
|
|
||||||
#include "movegen.h"
|
|
||||||
#include "position.h"
|
|
||||||
#include "search.h"
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
|
|
||||||
/// The Stats struct stores moves statistics. According to the template parameter
|
|
||||||
/// the class can store History, Gains and Countermoves. History records how often
|
|
||||||
/// different moves have been successful or unsuccessful during the current search
|
|
||||||
/// and is used for reduction and move ordering decisions. Gains records the move's
|
|
||||||
/// best evaluation gain from one ply to the next and is used for pruning decisions.
|
|
||||||
/// Countermoves store the move that refute a previous one. Entries are stored
|
|
||||||
/// using only the moving piece and destination square, hence two moves with
|
|
||||||
/// different origin but same destination and piece will be considered identical.
|
|
||||||
template<bool Gain, typename T>
|
|
||||||
struct Stats {
|
|
||||||
|
|
||||||
static const Value Max = Value(250);
|
|
||||||
|
|
||||||
const T* operator[](Piece pc) const { return table[pc]; }
|
|
||||||
void clear() { std::memset(table, 0, sizeof(table)); }
|
|
||||||
|
|
||||||
void update(Piece pc, Square to, Move m) {
|
|
||||||
|
|
||||||
if (m == table[pc][to].first)
|
|
||||||
return;
|
|
||||||
|
|
||||||
table[pc][to].second = table[pc][to].first;
|
|
||||||
table[pc][to].first = m;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(Piece pc, Square to, Value v) {
|
|
||||||
|
|
||||||
if (Gain)
|
|
||||||
table[pc][to] = std::max(v, table[pc][to] - 1);
|
|
||||||
|
|
||||||
else if (abs(table[pc][to] + v) < Max)
|
|
||||||
table[pc][to] += v;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
T table[PIECE_NB][SQUARE_NB];
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef Stats< true, Value> GainsStats;
|
|
||||||
typedef Stats<false, Value> HistoryStats;
|
|
||||||
typedef Stats<false, std::pair<Move, Move> > MovesStats;
|
|
||||||
|
|
||||||
|
|
||||||
/// MovePicker class is used to pick one pseudo legal move at a time from the
|
|
||||||
/// current position. The most important method is next_move(), which returns a
|
|
||||||
/// new pseudo legal move each time it is called, until there are no moves left,
|
|
||||||
/// when MOVE_NONE is returned. In order to improve the efficiency of the alpha
|
|
||||||
/// beta algorithm, MovePicker attempts to return the moves which are most likely
|
|
||||||
/// to get a cut-off first.
|
|
||||||
|
|
||||||
class MovePicker {
|
|
||||||
|
|
||||||
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
|
|
||||||
|
|
||||||
public:
|
|
||||||
MovePicker(const Position&, Move, Depth, const HistoryStats&, Square);
|
|
||||||
MovePicker(const Position&, Move, const HistoryStats&, PieceType);
|
|
||||||
MovePicker(const Position&, Move, Depth, const HistoryStats&, Move*, Move*, Search::Stack*);
|
|
||||||
|
|
||||||
template<bool SpNode> Move next_move();
|
|
||||||
|
|
||||||
private:
|
|
||||||
template<GenType> void score();
|
|
||||||
void generate_next_stage();
|
|
||||||
|
|
||||||
const Position& pos;
|
|
||||||
const HistoryStats& history;
|
|
||||||
Search::Stack* ss;
|
|
||||||
Move* countermoves;
|
|
||||||
Move* followupmoves;
|
|
||||||
Depth depth;
|
|
||||||
Move ttMove;
|
|
||||||
ExtMove killers[6];
|
|
||||||
Square recaptureSquare;
|
|
||||||
Value captureThreshold;
|
|
||||||
int stage;
|
|
||||||
ExtMove *cur, *end, *endQuiets, *endBadCaptures;
|
|
||||||
ExtMove moves[MAX_MOVES];
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // #ifndef MOVEPICK_H_INCLUDED
|
|
312
src/pawns.cpp
312
src/pawns.cpp
|
@ -1,312 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 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
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "bitboard.h"
|
|
||||||
#include "bitcount.h"
|
|
||||||
#include "pawns.h"
|
|
||||||
#include "position.h"
|
|
||||||
#include "thread.h"
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
#define V Value
|
|
||||||
#define S(mg, eg) make_score(mg, eg)
|
|
||||||
|
|
||||||
// Doubled pawn penalty by file
|
|
||||||
const Score Doubled[FILE_NB] = {
|
|
||||||
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
|
||||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43) };
|
|
||||||
|
|
||||||
// Isolated pawn penalty by opposed flag and file
|
|
||||||
const Score Isolated[2][FILE_NB] = {
|
|
||||||
{ S(37, 45), S(54, 52), S(60, 52), S(60, 52),
|
|
||||||
S(60, 52), S(60, 52), S(54, 52), S(37, 45) },
|
|
||||||
{ S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
|
||||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30) } };
|
|
||||||
|
|
||||||
// Backward pawn penalty by opposed flag and file
|
|
||||||
const Score Backward[2][FILE_NB] = {
|
|
||||||
{ S(30, 42), S(43, 46), S(49, 46), S(49, 46),
|
|
||||||
S(49, 46), S(49, 46), S(43, 46), S(30, 42) },
|
|
||||||
{ S(20, 28), S(29, 31), S(33, 31), S(33, 31),
|
|
||||||
S(33, 31), S(33, 31), S(29, 31), S(20, 28) } };
|
|
||||||
|
|
||||||
// Connected pawn bonus by opposed, phalanx flags and rank
|
|
||||||
Score Connected[2][2][RANK_NB];
|
|
||||||
|
|
||||||
// Levers bonus by rank
|
|
||||||
const Score Lever[RANK_NB] = {
|
|
||||||
S( 0, 0), S( 0, 0), S(0, 0), S(0, 0),
|
|
||||||
S(20,20), S(40,40), S(0, 0), S(0, 0) };
|
|
||||||
|
|
||||||
// Unsupported pawn penalty
|
|
||||||
const Score UnsupportedPawnPenalty = S(20, 10);
|
|
||||||
|
|
||||||
// Weakness of our pawn shelter in front of the king by [distance from edge][rank]
|
|
||||||
const Value ShelterWeakness[][RANK_NB] = {
|
|
||||||
{ V(100), V(13), V(24), V(64), V(89), V( 93), V(104) },
|
|
||||||
{ V(110), V( 1), V(29), V(75), V(96), V(102), V(107) },
|
|
||||||
{ V(102), V( 0), V(39), V(74), V(88), V(101), V( 98) },
|
|
||||||
{ V( 88), V( 4), V(33), V(67), V(92), V( 94), V(107) } };
|
|
||||||
|
|
||||||
// Danger of enemy pawns moving toward our king by [type][distance from edge][rank]
|
|
||||||
const Value StormDanger[][4][RANK_NB] = {
|
|
||||||
{ { V( 0), V( 63), V( 128), V(43), V(27) },
|
|
||||||
{ V( 0), V( 62), V( 131), V(44), V(26) },
|
|
||||||
{ V( 0), V( 59), V( 121), V(50), V(28) },
|
|
||||||
{ V( 0), V( 62), V( 127), V(54), V(28) } },
|
|
||||||
{ { V(24), V( 40), V( 93), V(42), V(22) },
|
|
||||||
{ V(24), V( 28), V( 101), V(38), V(20) },
|
|
||||||
{ V(24), V( 32), V( 95), V(36), V(23) },
|
|
||||||
{ V(27), V( 24), V( 99), V(36), V(24) } },
|
|
||||||
{ { V( 0), V( 0), V( 81), V(16), V( 6) },
|
|
||||||
{ V( 0), V( 0), V( 165), V(29), V( 9) },
|
|
||||||
{ V( 0), V( 0), V( 163), V(23), V(12) },
|
|
||||||
{ V( 0), V( 0), V( 161), V(28), V(13) } },
|
|
||||||
{ { V( 0), V(-296), V(-299), V(55), V(25) },
|
|
||||||
{ V( 0), V( 67), V( 131), V(46), V(21) },
|
|
||||||
{ V( 0), V( 65), V( 135), V(50), V(31) },
|
|
||||||
{ V( 0), V( 62), V( 128), V(51), V(24) } } };
|
|
||||||
|
|
||||||
// Max bonus for king safety. Corresponds to start position with all the pawns
|
|
||||||
// in front of the king and no enemy pawn on the horizon.
|
|
||||||
const Value MaxSafetyBonus = V(257);
|
|
||||||
|
|
||||||
#undef S
|
|
||||||
#undef V
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
Score evaluate(const Position& pos, Pawns::Entry* e) {
|
|
||||||
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
|
||||||
const Square Up = (Us == WHITE ? DELTA_N : DELTA_S);
|
|
||||||
const Square Right = (Us == WHITE ? DELTA_NE : DELTA_SW);
|
|
||||||
const Square Left = (Us == WHITE ? DELTA_NW : DELTA_SE);
|
|
||||||
|
|
||||||
Bitboard b, p, doubled, connected;
|
|
||||||
Square s;
|
|
||||||
bool passed, isolated, opposed, phalanx, backward, unsupported, lever;
|
|
||||||
Score score = SCORE_ZERO;
|
|
||||||
const Square* pl = pos.list<PAWN>(Us);
|
|
||||||
const Bitboard* pawnAttacksBB = StepAttacksBB[make_piece(Us, PAWN)];
|
|
||||||
|
|
||||||
Bitboard ourPawns = pos.pieces(Us , PAWN);
|
|
||||||
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
|
||||||
|
|
||||||
e->passedPawns[Us] = 0;
|
|
||||||
e->kingSquares[Us] = SQ_NONE;
|
|
||||||
e->semiopenFiles[Us] = 0xFF;
|
|
||||||
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) | shift_bb<Left>(ourPawns);
|
|
||||||
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(ourPawns & DarkSquares);
|
|
||||||
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
|
|
||||||
|
|
||||||
// Loop through all pawns of the current color and score each pawn
|
|
||||||
while ((s = *pl++) != SQ_NONE)
|
|
||||||
{
|
|
||||||
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
|
||||||
|
|
||||||
File f = file_of(s);
|
|
||||||
|
|
||||||
// This file cannot be semi-open
|
|
||||||
e->semiopenFiles[Us] &= ~(1 << f);
|
|
||||||
|
|
||||||
// Previous rank
|
|
||||||
p = rank_bb(s - pawn_push(Us));
|
|
||||||
|
|
||||||
// Flag the pawn as passed, isolated, doubled,
|
|
||||||
// unsupported or connected (but not the backward one).
|
|
||||||
connected = ourPawns & adjacent_files_bb(f) & (rank_bb(s) | p);
|
|
||||||
phalanx = connected & rank_bb(s);
|
|
||||||
unsupported = !(ourPawns & adjacent_files_bb(f) & p);
|
|
||||||
isolated = !(ourPawns & adjacent_files_bb(f));
|
|
||||||
doubled = ourPawns & forward_bb(Us, s);
|
|
||||||
opposed = theirPawns & forward_bb(Us, s);
|
|
||||||
passed = !(theirPawns & passed_pawn_mask(Us, s));
|
|
||||||
lever = theirPawns & pawnAttacksBB[s];
|
|
||||||
|
|
||||||
// Test for backward pawn.
|
|
||||||
// If the pawn is passed, isolated, or connected it cannot be
|
|
||||||
// backward. If there are friendly pawns behind on adjacent files
|
|
||||||
// or if it can capture an enemy pawn it cannot be backward either.
|
|
||||||
if ( (passed | isolated | connected)
|
|
||||||
|| (ourPawns & pawn_attack_span(Them, s))
|
|
||||||
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
|
||||||
backward = false;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// We now know that there are no friendly pawns beside or behind this
|
|
||||||
// pawn on adjacent files. We now check whether the pawn is
|
|
||||||
// backward by looking in the forward direction on the adjacent
|
|
||||||
// files, and picking the closest pawn there.
|
|
||||||
b = pawn_attack_span(Us, s) & (ourPawns | theirPawns);
|
|
||||||
b = pawn_attack_span(Us, s) & rank_bb(backmost_sq(Us, b));
|
|
||||||
|
|
||||||
// If we have an enemy pawn in the same or next rank, the pawn is
|
|
||||||
// backward because it cannot advance without being captured.
|
|
||||||
backward = (b | shift_bb<Up>(b)) & theirPawns;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(opposed | passed | (pawn_attack_span(Us, s) & theirPawns));
|
|
||||||
|
|
||||||
// Passed pawns will be properly scored in evaluation because we need
|
|
||||||
// full attack info to evaluate passed pawns. Only the frontmost passed
|
|
||||||
// pawn on each file is considered a true passed pawn.
|
|
||||||
if (passed && !doubled)
|
|
||||||
e->passedPawns[Us] |= s;
|
|
||||||
|
|
||||||
// Score this pawn
|
|
||||||
if (isolated)
|
|
||||||
score -= Isolated[opposed][f];
|
|
||||||
|
|
||||||
if (unsupported && !isolated)
|
|
||||||
score -= UnsupportedPawnPenalty;
|
|
||||||
|
|
||||||
if (doubled)
|
|
||||||
score -= Doubled[f] / distance<Rank>(s, frontmost_sq(Us, doubled));
|
|
||||||
|
|
||||||
if (backward)
|
|
||||||
score -= Backward[opposed][f];
|
|
||||||
|
|
||||||
if (connected)
|
|
||||||
score += Connected[opposed][phalanx][relative_rank(Us, s)];
|
|
||||||
|
|
||||||
if (lever)
|
|
||||||
score += Lever[relative_rank(Us, s)];
|
|
||||||
}
|
|
||||||
|
|
||||||
b = e->semiopenFiles[Us] ^ 0xFF;
|
|
||||||
e->pawnSpan[Us] = b ? int(msb(b) - lsb(b)) : 0;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
namespace Pawns {
|
|
||||||
|
|
||||||
/// Pawns::init() initializes some tables needed by evaluation. Instead of using
|
|
||||||
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
|
|
||||||
/// to reduce independent parameters and to allow easier tuning and better insight.
|
|
||||||
|
|
||||||
void init()
|
|
||||||
{
|
|
||||||
static const int Seed[RANK_NB] = { 0, 6, 15, 10, 57, 75, 135, 258 };
|
|
||||||
|
|
||||||
for (int opposed = 0; opposed <= 1; ++opposed)
|
|
||||||
for (int phalanx = 0; phalanx <= 1; ++phalanx)
|
|
||||||
for (Rank r = RANK_2; r < RANK_8; ++r)
|
|
||||||
{
|
|
||||||
int bonus = Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0);
|
|
||||||
Connected[opposed][phalanx][r] = make_score(bonus / 2, bonus >> opposed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Pawns::probe() looks up the current position's pawns configuration in
|
|
||||||
/// the pawns hash table. It returns a pointer to the Entry if the position
|
|
||||||
/// is found. Otherwise a new Entry is computed and stored there, so we don't
|
|
||||||
/// have to recompute all when the same pawns configuration occurs again.
|
|
||||||
|
|
||||||
Entry* probe(const Position& pos) {
|
|
||||||
|
|
||||||
Key key = pos.pawn_key();
|
|
||||||
Entry* e = pos.this_thread()->pawnsTable[key];
|
|
||||||
|
|
||||||
if (e->key == key)
|
|
||||||
return e;
|
|
||||||
|
|
||||||
e->key = key;
|
|
||||||
e->score = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos, e);
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Entry::shelter_storm() calculates shelter and storm penalties for the file
|
|
||||||
/// the king is on, as well as the two adjacent files.
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
Value Entry::shelter_storm(const Position& pos, Square ksq) {
|
|
||||||
|
|
||||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
|
||||||
|
|
||||||
enum { NoFriendlyPawn, Unblocked, BlockedByPawn, BlockedByKing };
|
|
||||||
|
|
||||||
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us, rank_of(ksq)) | rank_bb(ksq));
|
|
||||||
Bitboard ourPawns = b & pos.pieces(Us);
|
|
||||||
Bitboard theirPawns = b & pos.pieces(Them);
|
|
||||||
Value safety = MaxSafetyBonus;
|
|
||||||
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
|
|
||||||
|
|
||||||
for (File f = center - File(1); f <= center + File(1); ++f)
|
|
||||||
{
|
|
||||||
b = ourPawns & file_bb(f);
|
|
||||||
Rank rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
|
|
||||||
|
|
||||||
b = theirPawns & file_bb(f);
|
|
||||||
Rank rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
|
||||||
|
|
||||||
safety -= ShelterWeakness[std::min(f, FILE_H - f)][rkUs]
|
|
||||||
+ StormDanger
|
|
||||||
[f == file_of(ksq) && rkThem == relative_rank(Us, ksq) + 1 ? BlockedByKing :
|
|
||||||
rkUs == RANK_1 ? NoFriendlyPawn :
|
|
||||||
rkThem == rkUs + 1 ? BlockedByPawn : Unblocked]
|
|
||||||
[std::min(f, FILE_H - f)][rkThem];
|
|
||||||
}
|
|
||||||
|
|
||||||
return safety;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Entry::do_king_safety() calculates a bonus for king safety. It is called only
|
|
||||||
/// when king square changes, which is about 20% of total king_safety() calls.
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
Score Entry::do_king_safety(const Position& pos, Square ksq) {
|
|
||||||
|
|
||||||
kingSquares[Us] = ksq;
|
|
||||||
castlingRights[Us] = pos.can_castle(Us);
|
|
||||||
minKingPawnDistance[Us] = 0;
|
|
||||||
|
|
||||||
Bitboard pawns = pos.pieces(Us, PAWN);
|
|
||||||
if (pawns)
|
|
||||||
while (!(DistanceRingBB[ksq][minKingPawnDistance[Us]++] & pawns)) {}
|
|
||||||
|
|
||||||
if (relative_rank(Us, ksq) > RANK_4)
|
|
||||||
return make_score(0, -16 * minKingPawnDistance[Us]);
|
|
||||||
|
|
||||||
Value bonus = shelter_storm<Us>(pos, ksq);
|
|
||||||
|
|
||||||
// If we can castle use the bonus after the castling if it is bigger
|
|
||||||
if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right))
|
|
||||||
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_G1)));
|
|
||||||
|
|
||||||
if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
|
|
||||||
bonus = std::max(bonus, shelter_storm<Us>(pos, relative_square(Us, SQ_C1)));
|
|
||||||
|
|
||||||
return make_score(bonus, -16 * minKingPawnDistance[Us]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicit template instantiation
|
|
||||||
template Score Entry::do_king_safety<WHITE>(const Position& pos, Square ksq);
|
|
||||||
template Score Entry::do_king_safety<BLACK>(const Position& pos, Square ksq);
|
|
||||||
|
|
||||||
} // namespace Pawns
|
|
87
src/pawns.h
87
src/pawns.h
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 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
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PAWNS_H_INCLUDED
|
|
||||||
#define PAWNS_H_INCLUDED
|
|
||||||
|
|
||||||
#include "misc.h"
|
|
||||||
#include "position.h"
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
namespace Pawns {
|
|
||||||
|
|
||||||
/// Pawns::Entry contains various information about a pawn structure. A lookup
|
|
||||||
/// to the pawn hash table (performed by calling the probe function) returns a
|
|
||||||
/// pointer to an Entry object.
|
|
||||||
|
|
||||||
struct Entry {
|
|
||||||
|
|
||||||
Score pawns_score() const { return score; }
|
|
||||||
Bitboard pawn_attacks(Color c) const { return pawnAttacks[c]; }
|
|
||||||
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
|
|
||||||
|
|
||||||
int semiopen_file(Color c, File f) const {
|
|
||||||
return semiopenFiles[c] & (1 << f);
|
|
||||||
}
|
|
||||||
|
|
||||||
int semiopen_side(Color c, File f, bool leftSide) const {
|
|
||||||
return semiopenFiles[c] & (leftSide ? (1 << f) - 1 : ~((1 << (f + 1)) - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
int pawn_span(Color c) const {
|
|
||||||
return pawnSpan[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
int pawns_on_same_color_squares(Color c, Square s) const {
|
|
||||||
return pawnsOnSquares[c][!!(DarkSquares & s)];
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
Score king_safety(const Position& pos, Square ksq) {
|
|
||||||
return kingSquares[Us] == ksq && castlingRights[Us] == pos.can_castle(Us)
|
|
||||||
? kingSafety[Us] : (kingSafety[Us] = do_king_safety<Us>(pos, ksq));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
Score do_king_safety(const Position& pos, Square ksq);
|
|
||||||
|
|
||||||
template<Color Us>
|
|
||||||
Value shelter_storm(const Position& pos, Square ksq);
|
|
||||||
|
|
||||||
Key key;
|
|
||||||
Score score;
|
|
||||||
Bitboard passedPawns[COLOR_NB];
|
|
||||||
Bitboard pawnAttacks[COLOR_NB];
|
|
||||||
Square kingSquares[COLOR_NB];
|
|
||||||
Score kingSafety[COLOR_NB];
|
|
||||||
int minKingPawnDistance[COLOR_NB];
|
|
||||||
int castlingRights[COLOR_NB];
|
|
||||||
int semiopenFiles[COLOR_NB];
|
|
||||||
int pawnSpan[COLOR_NB];
|
|
||||||
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef HashTable<Entry, 16384> Table;
|
|
||||||
|
|
||||||
void init();
|
|
||||||
Entry* probe(const Position& pos);
|
|
||||||
|
|
||||||
} // namespace Pawns
|
|
||||||
|
|
||||||
#endif // #ifndef PAWNS_H_INCLUDED
|
|
1280
src/position.cpp
1280
src/position.cpp
File diff suppressed because it is too large
Load diff
439
src/position.h
439
src/position.h
|
@ -1,439 +0,0 @@
|
||||||
/*
|
|
||||||
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
||||||
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
|
||||||
Copyright (C) 2008-2014 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
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Stockfish is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef POSITION_H_INCLUDED
|
|
||||||
#define POSITION_H_INCLUDED
|
|
||||||
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstddef> // For offsetof()
|
|
||||||
|
|
||||||
#include "bitboard.h"
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
class Position;
|
|
||||||
struct Thread;
|
|
||||||
|
|
||||||
/// CheckInfo struct is initialized at c'tor time and keeps info used to detect
|
|
||||||
/// if a move gives check.
|
|
||||||
|
|
||||||
struct CheckInfo {
|
|
||||||
|
|
||||||
explicit CheckInfo(const Position&);
|
|
||||||
|
|
||||||
Bitboard dcCandidates;
|
|
||||||
Bitboard pinned;
|
|
||||||
Bitboard checkSq[PIECE_TYPE_NB];
|
|
||||||
Square ksq;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// StateInfo struct stores information needed to restore a Position object to
|
|
||||||
/// its previous state when we retract a move. Whenever a move is made on the
|
|
||||||
/// board (by calling Position::do_move), a StateInfo object must be passed.
|
|
||||||
|
|
||||||
struct StateInfo {
|
|
||||||
|
|
||||||
// Copied when making a move
|
|
||||||
Key pawnKey;
|
|
||||||
Key materialKey;
|
|
||||||
Value nonPawnMaterial[COLOR_NB];
|
|
||||||
int castlingRights;
|
|
||||||
int rule50;
|
|
||||||
int pliesFromNull;
|
|
||||||
Score psq;
|
|
||||||
Square epSquare;
|
|
||||||
|
|
||||||
// Not copied when making a move
|
|
||||||
Key key;
|
|
||||||
Bitboard checkersBB;
|
|
||||||
PieceType capturedType;
|
|
||||||
StateInfo* previous;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/// When making a move the current StateInfo up to 'key' excluded is copied to
|
|
||||||
/// the new one. Here we calculate the quad words (64 bit) needed to be copied.
|
|
||||||
const size_t StateCopySize64 = offsetof(StateInfo, key) / sizeof(uint64_t) + 1;
|
|
||||||
|
|
||||||
|
|
||||||
/// Position class stores information regarding the board representation as
|
|
||||||
/// pieces, side to move, hash keys, castling info, etc. Important methods are
|
|
||||||
/// do_move() and undo_move(), used by the search to update node info when
|
|
||||||
/// traversing the search tree.
|
|
||||||
|
|
||||||
class Position {
|
|
||||||
|
|
||||||
friend std::ostream& operator<<(std::ostream&, const Position&);
|
|
||||||
|
|
||||||
Position(const Position&); // Disable the default copy constructor
|
|
||||||
|
|
||||||
public:
|
|
||||||
static void init();
|
|
||||||
|
|
||||||
Position() {} // To define the global object RootPos
|
|
||||||
Position(const Position& pos, Thread* th) { *this = pos; thisThread = th; }
|
|
||||||
Position(const std::string& f, bool c960, Thread* th) { set(f, c960, th); }
|
|
||||||
Position& operator=(const Position&); // To assign RootPos from UCI
|
|
||||||
|
|
||||||
// FEN string input/output
|
|
||||||
void set(const std::string& fenStr, bool isChess960, Thread* th);
|
|
||||||
const std::string fen() const;
|
|
||||||
|
|
||||||
// Position representation
|
|
||||||
Bitboard pieces() const;
|
|
||||||
Bitboard pieces(PieceType pt) const;
|
|
||||||
Bitboard pieces(PieceType pt1, PieceType pt2) const;
|
|
||||||
Bitboard pieces(Color c) const;
|
|
||||||
Bitboard pieces(Color c, PieceType pt) const;
|
|
||||||
Bitboard pieces(Color c, PieceType pt1, PieceType pt2) const;
|
|
||||||
Piece piece_on(Square s) const;
|
|
||||||
Square king_square(Color c) const;
|
|
||||||
Square ep_square() const;
|
|
||||||
bool empty(Square s) const;
|
|
||||||
template<PieceType Pt> int count(Color c) const;
|
|
||||||
template<PieceType Pt> const Square* list(Color c) const;
|
|
||||||
|
|
||||||
// Castling
|
|
||||||
int can_castle(Color c) const;
|
|
||||||
int can_castle(CastlingRight cr) const;
|
|
||||||
bool castling_impeded(CastlingRight cr) const;
|
|
||||||
Square castling_rook_square(CastlingRight cr) const;
|
|
||||||
|
|
||||||
// Checking
|
|
||||||
Bitboard checkers() const;
|
|
||||||
Bitboard discovered_check_candidates() const;
|
|
||||||
Bitboard pinned_pieces(Color c) const;
|
|
||||||
|
|
||||||
// Attacks to/from a given square
|
|
||||||
Bitboard attackers_to(Square s) const;
|
|
||||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
|
||||||
Bitboard attacks_from(Piece pc, Square s) const;
|
|
||||||
template<PieceType> Bitboard attacks_from(Square s) const;
|
|
||||||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
|
||||||
|
|
||||||
// Properties of moves
|
|
||||||
bool legal(Move m, Bitboard pinned) const;
|
|
||||||
bool pseudo_legal(const Move m) const;
|
|
||||||
bool capture(Move m) const;
|
|
||||||
bool capture_or_promotion(Move m) const;
|
|
||||||
bool gives_check(Move m, const CheckInfo& ci) const;
|
|
||||||
bool advanced_pawn_push(Move m) const;
|
|
||||||
Piece moved_piece(Move m) const;
|
|
||||||
PieceType captured_piece_type() const;
|
|
||||||
|
|
||||||
// Piece specific
|
|
||||||
bool pawn_passed(Color c, Square s) const;
|
|
||||||
bool pawn_on_7th(Color c) const;
|
|
||||||
bool opposite_bishops() const;
|
|
||||||
|
|
||||||
// Doing and undoing moves
|
|
||||||
void do_move(Move m, StateInfo& st);
|
|
||||||
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
|
|
||||||
void undo_move(Move m);
|
|
||||||
void do_null_move(StateInfo& st);
|
|
||||||
void undo_null_move();
|
|
||||||
|
|
||||||
// Static exchange evaluation
|
|
||||||
Value see(Move m) const;
|
|
||||||
Value see_sign(Move m) const;
|
|
||||||
|
|
||||||
// Accessing hash keys
|
|
||||||
Key key() const;
|
|
||||||
Key key_after(Move m) const;
|
|
||||||
Key exclusion_key() const;
|
|
||||||
Key material_key() const;
|
|
||||||
Key pawn_key() const;
|
|
||||||
|
|
||||||
// Other properties of the position
|
|
||||||
Color side_to_move() const;
|
|
||||||
Phase game_phase() const;
|
|
||||||
int game_ply() const;
|
|
||||||
bool is_chess960() const;
|
|
||||||
Thread* this_thread() const;
|
|
||||||
uint64_t nodes_searched() const;
|
|
||||||
void set_nodes_searched(uint64_t n);
|
|
||||||
bool is_draw() const;
|
|
||||||
int rule50_count() const;
|
|
||||||
Score psq_score() const;
|
|
||||||
Value non_pawn_material(Color c) const;
|
|
||||||
|
|
||||||
// Position consistency check, for debugging
|
|
||||||
bool pos_is_ok(int* step = NULL) const;
|
|
||||||
void flip();
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Initialization helpers (used while setting up a position)
|
|
||||||
void clear();
|
|
||||||
void set_castling_right(Color c, Square rfrom);
|
|
||||||
void set_state(StateInfo* si) const;
|
|
||||||
|
|
||||||
// Other helpers
|
|
||||||
Bitboard check_blockers(Color c, Color kingColor) const;
|
|
||||||
void put_piece(Square s, Color c, PieceType pt);
|
|
||||||
void remove_piece(Square s, Color c, PieceType pt);
|
|
||||||
void move_piece(Square from, Square to, Color c, PieceType pt);
|
|
||||||
template<bool Do>
|
|
||||||
void do_castling(Square from, Square& to, Square& rfrom, Square& rto);
|
|
||||||
|
|
||||||
// Data members
|
|
||||||
Piece board[SQUARE_NB];
|
|
||||||
Bitboard byTypeBB[PIECE_TYPE_NB];
|
|
||||||
Bitboard byColorBB[COLOR_NB];
|
|
||||||
int pieceCount[COLOR_NB][PIECE_TYPE_NB];
|
|
||||||
Square pieceList[COLOR_NB][PIECE_TYPE_NB][16];
|
|
||||||
int index[SQUARE_NB];
|
|
||||||
int castlingRightsMask[SQUARE_NB];
|
|
||||||
Square castlingRookSquare[CASTLING_RIGHT_NB];
|
|
||||||
Bitboard castlingPath[CASTLING_RIGHT_NB];
|
|
||||||
StateInfo startState;
|
|
||||||
uint64_t nodes;
|
|
||||||
int gamePly;
|
|
||||||
Color sideToMove;
|
|
||||||
Thread* thisThread;
|
|
||||||
StateInfo* st;
|
|
||||||
bool chess960;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Color Position::side_to_move() const {
|
|
||||||
return sideToMove;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::empty(Square s) const {
|
|
||||||
return board[s] == NO_PIECE;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Piece Position::piece_on(Square s) const {
|
|
||||||
return board[s];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Piece Position::moved_piece(Move m) const {
|
|
||||||
return board[from_sq(m)];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::pieces() const {
|
|
||||||
return byTypeBB[ALL_PIECES];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::pieces(PieceType pt) const {
|
|
||||||
return byTypeBB[pt];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::pieces(PieceType pt1, PieceType pt2) const {
|
|
||||||
return byTypeBB[pt1] | byTypeBB[pt2];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::pieces(Color c) const {
|
|
||||||
return byColorBB[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::pieces(Color c, PieceType pt) const {
|
|
||||||
return byColorBB[c] & byTypeBB[pt];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::pieces(Color c, PieceType pt1, PieceType pt2) const {
|
|
||||||
return byColorBB[c] & (byTypeBB[pt1] | byTypeBB[pt2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<PieceType Pt> inline int Position::count(Color c) const {
|
|
||||||
return pieceCount[c][Pt];
|
|
||||||
}
|
|
||||||
|
|
||||||
template<PieceType Pt> inline const Square* Position::list(Color c) const {
|
|
||||||
return pieceList[c][Pt];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Square Position::king_square(Color c) const {
|
|
||||||
return pieceList[c][KING][0];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Square Position::ep_square() const {
|
|
||||||
return st->epSquare;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Position::can_castle(CastlingRight cr) const {
|
|
||||||
return st->castlingRights & cr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Position::can_castle(Color c) const {
|
|
||||||
return st->castlingRights & ((WHITE_OO | WHITE_OOO) << (2 * c));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::castling_impeded(CastlingRight cr) const {
|
|
||||||
return byTypeBB[ALL_PIECES] & castlingPath[cr];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Square Position::castling_rook_square(CastlingRight cr) const {
|
|
||||||
return castlingRookSquare[cr];
|
|
||||||
}
|
|
||||||
|
|
||||||
template<PieceType Pt>
|
|
||||||
inline Bitboard Position::attacks_from(Square s) const {
|
|
||||||
return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
|
|
||||||
: Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
|
|
||||||
: StepAttacksBB[Pt][s];
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
|
|
||||||
return StepAttacksBB[make_piece(c, PAWN)][s];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::attacks_from(Piece pc, Square s) const {
|
|
||||||
return attacks_bb(pc, s, byTypeBB[ALL_PIECES]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::attackers_to(Square s) const {
|
|
||||||
return attackers_to(s, byTypeBB[ALL_PIECES]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::checkers() const {
|
|
||||||
return st->checkersBB;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::discovered_check_candidates() const {
|
|
||||||
return check_blockers(sideToMove, ~sideToMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Bitboard Position::pinned_pieces(Color c) const {
|
|
||||||
return check_blockers(c, c);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::pawn_passed(Color c, Square s) const {
|
|
||||||
return !(pieces(~c, PAWN) & passed_pawn_mask(c, s));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::advanced_pawn_push(Move m) const {
|
|
||||||
return type_of(moved_piece(m)) == PAWN
|
|
||||||
&& relative_rank(sideToMove, from_sq(m)) > RANK_4;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Key Position::key() const {
|
|
||||||
return st->key;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Key Position::pawn_key() const {
|
|
||||||
return st->pawnKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Key Position::material_key() const {
|
|
||||||
return st->materialKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Score Position::psq_score() const {
|
|
||||||
return st->psq;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Value Position::non_pawn_material(Color c) const {
|
|
||||||
return st->nonPawnMaterial[c];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Position::game_ply() const {
|
|
||||||
return gamePly;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int Position::rule50_count() const {
|
|
||||||
return st->rule50;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline uint64_t Position::nodes_searched() const {
|
|
||||||
return nodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Position::set_nodes_searched(uint64_t n) {
|
|
||||||
nodes = n;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::opposite_bishops() const {
|
|
||||||
return pieceCount[WHITE][BISHOP] == 1
|
|
||||||
&& pieceCount[BLACK][BISHOP] == 1
|
|
||||||
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::pawn_on_7th(Color c) const {
|
|
||||||
return pieces(c, PAWN) & rank_bb(relative_rank(c, RANK_7));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::is_chess960() const {
|
|
||||||
return chess960;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::capture_or_promotion(Move m) const {
|
|
||||||
|
|
||||||
assert(is_ok(m));
|
|
||||||
return type_of(m) != NORMAL ? type_of(m) != CASTLING : !empty(to_sq(m));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool Position::capture(Move m) const {
|
|
||||||
|
|
||||||
// Castling is encoded as "king captures the rook"
|
|
||||||
assert(is_ok(m));
|
|
||||||
return (!empty(to_sq(m)) && type_of(m) != CASTLING) || type_of(m) == ENPASSANT;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline PieceType Position::captured_piece_type() const {
|
|
||||||
return st->capturedType;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Thread* Position::this_thread() const {
|
|
||||||
return thisThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Position::put_piece(Square s, Color c, PieceType pt) {
|
|
||||||
|
|
||||||
board[s] = make_piece(c, pt);
|
|
||||||
byTypeBB[ALL_PIECES] |= s;
|
|
||||||
byTypeBB[pt] |= s;
|
|
||||||
byColorBB[c] |= s;
|
|
||||||
index[s] = pieceCount[c][pt]++;
|
|
||||||
pieceList[c][pt][index[s]] = s;
|
|
||||||
pieceCount[c][ALL_PIECES]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Position::move_piece(Square from, Square to, Color c, PieceType pt) {
|
|
||||||
|
|
||||||
// index[from] is not updated and becomes stale. This works as long as index[]
|
|
||||||
// is accessed just by known occupied squares.
|
|
||||||
Bitboard from_to_bb = SquareBB[from] ^ SquareBB[to];
|
|
||||||
byTypeBB[ALL_PIECES] ^= from_to_bb;
|
|
||||||
byTypeBB[pt] ^= from_to_bb;
|
|
||||||
byColorBB[c] ^= from_to_bb;
|
|
||||||
board[from] = NO_PIECE;
|
|
||||||
board[to] = make_piece(c, pt);
|
|
||||||
index[to] = index[from];
|
|
||||||
pieceList[c][pt][index[to]] = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Position::remove_piece(Square s, Color c, PieceType pt) {
|
|
||||||
|
|
||||||
// WARNING: This is not a reversible operation. If we remove a piece in
|
|
||||||
// do_move() and then replace it in undo_move() we will put it at the end of
|
|
||||||
// the list and not in its original place, it means index[] and pieceList[]
|
|
||||||
// are not guaranteed to be invariant to a do_move() + undo_move() sequence.
|
|
||||||
byTypeBB[ALL_PIECES] ^= s;
|
|
||||||
byTypeBB[pt] ^= s;
|
|
||||||
byColorBB[c] ^= s;
|
|
||||||
/* board[s] = NO_PIECE; Not needed, overwritten by the capturing one */
|
|
||||||
Square lastSquare = pieceList[c][pt][--pieceCount[c][pt]];
|
|
||||||
index[lastSquare] = index[s];
|
|
||||||
pieceList[c][pt][index[lastSquare]] = lastSquare;
|
|
||||||
pieceList[c][pt][pieceCount[c][pt]] = SQ_NONE;
|
|
||||||
pieceCount[c][ALL_PIECES]--;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // #ifndef POSITION_H_INCLUDED
|
|
Loading…
Add table
Reference in a new issue