--- /dev/null
+stockfish
+*.o
+.depend
### Object files
OBJS = benchmark.o bitbase.o bitboard.o endgame.o evaluate.o main.o \
material.o misc.o movegen.o movepick.o pawns.o position.o psqt.o \
- search.o thread.o timeman.o tt.o uci.o ucioption.o syzygy/tbprobe.o
+ search.o thread.o timeman.o tt.o uci.o ucioption.o variant.o syzygy/tbprobe.o
### Establish the operating system name
KERNEL = $(shell uname -s)
if ( distance(ksq[WHITE], ksq[BLACK]) <= 1
|| ksq[WHITE] == psq
|| ksq[BLACK] == psq
- || (us == WHITE && (PawnAttacks[WHITE][psq] & ksq[BLACK])))
+ || (us == WHITE && (PseudoAttacks[WHITE][PAWN][psq] & ksq[BLACK])))
result = INVALID;
// Immediate win if a pawn can be promoted without getting captured
&& rank_of(psq) == RANK_7
&& ksq[us] != psq + NORTH
&& ( distance(ksq[~us], psq + NORTH) > 1
- || (PseudoAttacks[KING][ksq[us]] & (psq + NORTH))))
+ || (PseudoAttacks[us][KING][ksq[us]] & (psq + NORTH))))
result = WIN;
// Immediate draw if it is a stalemate or a king captures undefended pawn
else if ( us == BLACK
- && ( !(PseudoAttacks[KING][ksq[us]] & ~(PseudoAttacks[KING][ksq[~us]] | PawnAttacks[~us][psq]))
- || (PseudoAttacks[KING][ksq[us]] & psq & ~PseudoAttacks[KING][ksq[~us]])))
+ && ( !(PseudoAttacks[us][KING][ksq[us]] & ~(PseudoAttacks[us][KING][ksq[~us]] | PseudoAttacks[~us][PAWN][psq]))
+ || (PseudoAttacks[us][KING][ksq[us]] & psq & ~PseudoAttacks[us][KING][ksq[~us]])))
result = DRAW;
// Position will be classified later
constexpr Result Bad = (Us == WHITE ? DRAW : WIN);
Result r = INVALID;
- Bitboard b = PseudoAttacks[KING][ksq[Us]];
+ Bitboard b = PseudoAttacks[Us][KING][ksq[Us]];
while (b)
r |= Us == WHITE ? db[index(Them, ksq[Them] , pop_lsb(&b), psq)]
Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
-Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
-Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
+Bitboard PseudoAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+Bitboard PseudoMoves[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+Bitboard LeaperAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+Bitboard LeaperMoves[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Magic RookMagics[SQUARE_NB];
Magic BishopMagics[SQUARE_NB];
u = ((u >> 4) + u) & 0x0F0FU;
return (u * 0x0101U) >> 8;
}
+
+ Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied, int maxDist = 7) {
+
+ Bitboard attack = 0;
+
+ for (int i = 0; directions[i]; ++i)
+ for (Square s = sq + directions[i];
+ is_ok(s) && distance(s, s - directions[i]) == 1 && distance(s, sq) <= maxDist;
+ s += directions[i])
+ {
+ attack |= s;
+
+ if (occupied & s)
+ break;
+ }
+
+ return attack;
+ }
}
DistanceRingBB[s1][SquareDistance[s1][s2] - 1] |= s2;
}
- int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
+ // Piece moves
+ Direction RookDirections[5] = { NORTH, EAST, SOUTH, WEST };
+ Direction BishopDirections[5] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
+
+ init_magics(RookTable, RookMagics, RookDirections);
+ init_magics(BishopTable, BishopMagics, BishopDirections);
+
+ int stepsCapture[][13] = {
+ {}, // NO_PIECE_TYPE
+ { 7, 9 }, // pawn
+ { -17, -15, -10, -6, 6, 10, 15, 17 }, // knight
+ {}, // bishop
+ {}, // rook
+ {}, // queen
+ { -9, -7, 7, 9 }, // fers/met
+ { -18, -14, 14, 18 }, // alfil
+ { -9, -7, 7, 8, 9 }, // silver/khon
+ { -17, -15, -10, -9, -7, -6, 6, 7, 9, 10, 15, 17 }, // aiwok
+ { -17, -15, -10, -6, 6, 10, 15, 17 }, // amazon
+ {}, // knibis
+ { -17, -15, -10, -6, 6, 10, 15, 17 }, // biskni
+ { -9, -8, -7, -1, 1, 7, 8, 9 } // king
+ };
+ int stepsQuiet[][13] = {
+ {}, // NO_PIECE_TYPE
+ { 8 }, // pawn
+ { -17, -15, -10, -6, 6, 10, 15, 17 }, // knight
+ {}, // bishop
+ {}, // rook
+ {}, // queen
+ { -9, -7, 7, 9 }, // fers/met
+ { -18, -14, 14, 18 }, // alfil
+ { -9, -7, 7, 8, 9 }, // silver/khon
+ { -17, -15, -10, -9, -7, -6, 6, 7, 9, 10, 15, 17 }, // aiwok
+ { -17, -15, -10, -6, 6, 10, 15, 17 }, // amazon
+ { -17, -15, -10, -6, 6, 10, 15, 17 }, // knibis
+ {}, // biskni
+ { -9, -8, -7, -1, 1, 7, 8, 9 } // king
+ };
+ Direction sliderCapture[][9] = {
+ {}, // NO_PIECE_TYPE
+ {}, // pawn
+ {}, // knight
+ { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // bishop
+ { NORTH, EAST, SOUTH, WEST }, // rook
+ { NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // queen
+ {}, // fers/met
+ {}, // alfil
+ {}, // silver/khon
+ { NORTH, EAST, SOUTH, WEST }, // aiwok
+ { NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // amazon
+ { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // knibis
+ {}, // biskni
+ {} // king
+ };
+ Direction sliderQuiet[][9] = {
+ {}, // NO_PIECE_TYPE
+ {}, // pawn
+ {}, // knight
+ { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // bishop
+ { NORTH, EAST, SOUTH, WEST }, // rook
+ { NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // queen
+ {}, // fers/met
+ {}, // alfil
+ {}, // silver/khon
+ { NORTH, EAST, SOUTH, WEST }, // aiwok
+ { NORTH, EAST, SOUTH, WEST, NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // amazon
+ {}, // knibis
+ { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }, // biskni
+ {} // king
+ };
+ int sliderDistCapture[] = {
+ 0, // NO_PIECE_TYPE
+ 0, // pawn
+ 0, // knight
+ 7, // bishop
+ 7, // rook
+ 7, // queen
+ 0, // fers/met
+ 0, // alfil
+ 0, // silver/khon
+ 7, // aiwok
+ 7, // amazon
+ 7, // knibis
+ 0, // biskni
+ 0 // king
+ };
+ int sliderDistQuiet[] = {
+ 0, // NO_PIECE_TYPE
+ 0, // pawn
+ 0, // knight
+ 7, // bishop
+ 7, // rook
+ 7, // queen
+ 0, // fers/met
+ 0, // alfil
+ 0, // silver/khon
+ 7, // aiwok
+ 7, // amazon
+ 0, // knibis
+ 7, // biskni
+ 0 // king
+ };
for (Color c = WHITE; c <= BLACK; ++c)
- for (PieceType pt : { PAWN, KNIGHT, KING })
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
for (Square s = SQ_A1; s <= SQ_H8; ++s)
- for (int i = 0; steps[pt][i]; ++i)
+ {
+ for (int i = 0; stepsCapture[pt][i]; ++i)
{
- Square to = s + Direction(c == WHITE ? steps[pt][i] : -steps[pt][i]);
+ Square to = s + Direction(c == WHITE ? stepsCapture[pt][i] : -stepsCapture[pt][i]);
- if (is_ok(to) && distance(s, to) < 3)
+ if (is_ok(to) && distance(s, to) < 4)
{
- if (pt == PAWN)
- PawnAttacks[c][s] |= to;
- else
- PseudoAttacks[pt][s] |= to;
+ PseudoAttacks[c][pt][s] |= to;
+ LeaperAttacks[c][pt][s] |= to;
}
}
+ for (int i = 0; stepsQuiet[pt][i]; ++i)
+ {
+ Square to = s + Direction(c == WHITE ? stepsQuiet[pt][i] : -stepsQuiet[pt][i]);
- Direction RookDirections[] = { NORTH, EAST, SOUTH, WEST };
- Direction BishopDirections[] = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
-
- init_magics(RookTable, RookMagics, RookDirections);
- init_magics(BishopTable, BishopMagics, BishopDirections);
+ if (is_ok(to) && distance(s, to) < 4)
+ {
+ PseudoMoves[c][pt][s] |= to;
+ LeaperMoves[c][pt][s] |= to;
+ }
+ }
+ PseudoAttacks[c][pt][s] |= sliding_attack(sliderCapture[pt], s, 0, sliderDistCapture[pt]);
+ PseudoMoves[c][pt][s] |= sliding_attack(sliderQuiet[pt], s, 0, sliderDistQuiet[pt]);
+ }
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
{
- PseudoAttacks[QUEEN][s1] = PseudoAttacks[BISHOP][s1] = attacks_bb<BISHOP>(s1, 0);
- PseudoAttacks[QUEEN][s1] |= PseudoAttacks[ ROOK][s1] = attacks_bb< ROOK>(s1, 0);
-
for (PieceType pt : { BISHOP, ROOK })
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{
- if (!(PseudoAttacks[pt][s1] & s2))
+ if (!(PseudoAttacks[WHITE][pt][s1] & s2))
continue;
- LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
- BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]);
+ LineBB[s1][s2] = (attacks_bb(WHITE, pt, s1, 0) & attacks_bb(WHITE, pt, s2, 0)) | s1 | s2;
+ BetweenBB[s1][s2] = attacks_bb(WHITE, pt, s1, SquareBB[s2]) & attacks_bb(WHITE, pt, s2, SquareBB[s1]);
}
}
}
namespace {
- Bitboard sliding_attack(Direction directions[], Square sq, Bitboard occupied) {
-
- Bitboard attack = 0;
-
- for (int i = 0; i < 4; ++i)
- for (Square s = sq + directions[i];
- is_ok(s) && distance(s, s - directions[i]) == 1;
- s += directions[i])
- {
- attack |= s;
-
- if (occupied & s)
- break;
- }
-
- return attack;
- }
-
-
// init_magics() computes all rook and bishop attacks at startup. Magic
// bitboards are used to look up attacks of sliding pieces. As a reference see
// chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
extern Bitboard ForwardFileBB[COLOR_NB][SQUARE_NB];
extern Bitboard PassedPawnMask[COLOR_NB][SQUARE_NB];
extern Bitboard PawnAttackSpan[COLOR_NB][SQUARE_NB];
-extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
-extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
+extern Bitboard PseudoAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+extern Bitboard PseudoMoves[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+extern Bitboard LeaperAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+extern Bitboard LeaperMoves[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
/// Magic holds all magic bitboards relevant data for a single square
template<PieceType Pt>
inline Bitboard attacks_bb(Square s, Bitboard occupied) {
+ assert(Pt == BISHOP || Pt == ROOK);
const Magic& m = Pt == ROOK ? RookMagics[s] : BishopMagics[s];
return m.attacks[m.index(occupied)];
}
-inline Bitboard attacks_bb(PieceType pt, Square s, Bitboard occupied) {
-
- assert(pt != PAWN);
+inline Bitboard attacks_bb(Color c, PieceType pt, Square s, Bitboard occupied) {
+ return LeaperAttacks[c][pt][s] | (PseudoAttacks[c][pt][s] & (attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied)));
+}
- switch (pt)
- {
- case BISHOP: return attacks_bb<BISHOP>(s, occupied);
- case ROOK : return attacks_bb< ROOK>(s, occupied);
- case QUEEN : return attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied);
- default : return PseudoAttacks[pt][s];
- }
+inline Bitboard moves_bb(Color c, PieceType pt, Square s, Bitboard occupied) {
+ return LeaperMoves[c][pt][s] | (PseudoMoves[c][pt][s] & (attacks_bb<BISHOP>(s, occupied) | attacks_bb<ROOK>(s, occupied)));
}
&& relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
&& relative_rank(weakSide, rsq) == RANK_3
&& ( pos.pieces(weakSide, PAWN)
- & pos.attacks_from<KING>(kingSq)
- & pos.attacks_from<PAWN>(rsq, strongSide)))
+ & pos.attacks_from<KING>(weakSide, kingSq)
+ & pos.attacks_from<PAWN>(strongSide, rsq)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
// the corner
if ( rk == RANK_6
&& distance(psq + 2 * push, ksq) <= 1
- && (PseudoAttacks[BISHOP][bsq] & (psq + push))
+ && (PseudoAttacks[strongSide][BISHOP][bsq] & (psq + push))
&& distance<File>(bsq, psq) >= 2)
return ScaleFactor(8);
}
if ( ksq == blockSq1
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
- || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
+ || (pos.attacks_from<BISHOP>(weakSide, blockSq2) & pos.pieces(weakSide, BISHOP))
|| distance(r1, r2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1
- || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakSide, BISHOP))))
+ || (pos.attacks_from<BISHOP>(weakSide, blockSq1) & pos.pieces(weakSide, BISHOP))))
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
// King needs to get close to promoting pawn to prevent knight from blocking.
// Rules for this are very tricky, so just approximate.
- if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
+ if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(weakSide, bishopSq))
return ScaleFactor(distance(weakKingSq, pawnSq));
return SCALE_FACTOR_NONE;
mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them));
// Initialise attackedBy bitboards for kings and pawns
- attackedBy[Us][KING] = pos.attacks_from<KING>(pos.square<KING>(Us));
+ attackedBy[Us][KING] = pos.attacks_from<KING>(Us, pos.square<KING>(Us));
attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN];
// Find attacked squares, including x-ray attacks for bishops and rooks
b = Pt == BISHOP ? attacks_bb<BISHOP>(s, pos.pieces() ^ pos.pieces(QUEEN))
: Pt == ROOK ? attacks_bb< ROOK>(s, pos.pieces() ^ pos.pieces(QUEEN) ^ pos.pieces(Us, ROOK))
- : pos.attacks_from<Pt>(s);
+ : ( (pos.attacks_from(Us, Pt, s) & pos.pieces())
+ | (pos.moves_from(Us, Pt, s) & ~pos.pieces()));
if (pos.blockers_for_king(Us) & s)
b &= LineBB[pos.square<KING>(Us)][s];
{
// Bonus for aligning rook with enemy pawns on the same rank/file
if (relative_rank(Us, s) >= RANK_5)
- score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
+ score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[Us][ROOK][s]);
// Bonus for rook on an open or semi-open file
if (pe->semiopen_file(Us, file_of(s)))
unsafeChecks |= b2;
// Enemy knights checks
- b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
+ b = pos.attacks_from<KNIGHT>(Us, ksq) & attackedBy[Them][KNIGHT];
if (b & safe)
kingDanger += KnightSafeCheck;
else
Square s = pos.square<QUEEN>(Them);
safeThreats = mobilityArea[Us] & ~stronglyProtected;
- b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(s);
+ b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(Us, s);
score += KnightOnQueen * popcount(b & safeThreats);
- b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s))
- | (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(s));
+ b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(Us, s))
+ | (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(Us, s));
score += SliderOnQueen * popcount(b & safeThreats & attackedBy2[Us]);
}
// in the pawn's path attacked or occupied by the enemy.
defendedSquares = unsafeSquares = squaresToQueen = forward_file_bb(Us, s);
- bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(s);
+ bb = forward_file_bb(Them, s) & pos.pieces(ROOK, QUEEN) & pos.attacks_from<ROOK>(Us, s);
if (!(pos.pieces(Us) & bb))
defendedSquares &= attackedBy[Us][ALL_PIECES];
#include "thread.h"
#include "tt.h"
#include "uci.h"
+#include "variant.h"
#include "syzygy/tbprobe.h"
namespace PSQT {
std::cout << engine_info() << std::endl;
+ variants.init();
UCI::init(Options);
PSQT::init();
Bitboards::init();
UCI::loop(argc, argv);
Threads.set(0);
+ variants.clear_all();
return 0;
}
// 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)))
+ if (Chess960 && (pos.attackers_to(kto, pos.pieces() ^ rfrom) & pos.pieces(~us)))
return moveList;
Move m = make<CASTLING>(kfrom, rfrom);
}
- template<GenType Type, Direction D>
- ExtMove* make_promotions(ExtMove* moveList, Square to, Square ksq) {
+ template<Color c, GenType Type, Direction D>
+ ExtMove* make_promotions(const Position& pos, ExtMove* moveList, Square to) {
- if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
- *moveList++ = make<PROMOTION>(to - D, to, QUEEN);
+ const MoveType T = (D == NORTH_WEST || D == SOUTH_WEST) ? PROMOTION_LEFT
+ : (D == NORTH_EAST || D == SOUTH_EAST) ? PROMOTION_RIGHT
+ : PROMOTION_STRAIGHT;
- if (Type == QUIETS || Type == EVASIONS || Type == NON_EVASIONS)
- {
- *moveList++ = make<PROMOTION>(to - D, to, ROOK);
- *moveList++ = make<PROMOTION>(to - D, to, BISHOP);
- *moveList++ = make<PROMOTION>(to - D, 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 && (PseudoAttacks[KNIGHT][to] & ksq))
- *moveList++ = make<PROMOTION>(to - D, to, KNIGHT);
- else
- (void)ksq; // Silence a warning under MSVC
+ if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
+ for (PieceType pt : pos.promotion_piece_types())
+ *moveList++ = make<T>(to - D, to, pt);
return moveList;
}
// Compute our parametrized parameters at compile time, named according to
// the point of view of white side.
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
- constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
Bitboard emptySquares;
+ Bitboard TRank8BB = rank_bb(Us == WHITE ? pos.promotion_rank() : ~pos.promotion_rank());
+ Bitboard TRank7BB = shift<Down>(TRank8BB);
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces());
Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
- Bitboard b2 = shift<Up>(b1 & TRank3BB) & emptySquares;
+ Bitboard b2 = pos.double_step_enabled() ? shift<Up>(b1 & TRank3BB) & emptySquares : 0;
if (Type == EVASIONS) // Consider only blocking squares
{
{
Square ksq = pos.square<KING>(Them);
- b1 &= pos.attacks_from<PAWN>(ksq, Them);
- b2 &= pos.attacks_from<PAWN>(ksq, Them);
+ b1 &= pos.attacks_from<PAWN>(Them, ksq);
+ b2 &= pos.attacks_from<PAWN>(Them, ksq);
// 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
Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
- Square ksq = pos.square<KING>(Them);
-
while (b1)
- moveList = make_promotions<Type, UpRight>(moveList, pop_lsb(&b1), ksq);
+ moveList = make_promotions<Us, Type, UpRight>(pos, moveList, pop_lsb(&b1));
while (b2)
- moveList = make_promotions<Type, UpLeft >(moveList, pop_lsb(&b2), ksq);
+ moveList = make_promotions<Us, Type, UpLeft >(pos, moveList, pop_lsb(&b2));
while (b3)
- moveList = make_promotions<Type, Up >(moveList, pop_lsb(&b3), ksq);
+ moveList = make_promotions<Us, Type, Up >(pos, moveList, pop_lsb(&b3));
}
// Standard and en-passant captures
if (Type == EVASIONS && !(target & (pos.ep_square() - Up)))
return moveList;
- b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(pos.ep_square(), Them);
+ b1 = pawnsNotOn7 & pos.attacks_from<PAWN>(Them, pos.ep_square());
assert(b1);
}
- template<PieceType Pt, bool Checks>
- ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us,
+ template<bool Checks>
+ ExtMove* generate_moves(const Position& pos, ExtMove* moveList, Color us, PieceType pt,
Bitboard target) {
- assert(Pt != KING && Pt != PAWN);
+ assert(pt != KING && pt != PAWN);
- const Square* pl = pos.squares<Pt>(us);
+ const Square* pl = pos.squares(us, pt);
for (Square from = *pl; from != SQ_NONE; from = *++pl)
{
- if (Checks)
- {
- if ( (Pt == BISHOP || Pt == ROOK || Pt == QUEEN)
- && !(PseudoAttacks[Pt][from] & target & pos.check_squares(Pt)))
- continue;
-
- if (pos.blockers_for_king(~us) & from)
- continue;
- }
+ // Avoid generating discovered checks twice
+ if (Checks && (pos.blockers_for_king(~us) & from))
+ continue;
- Bitboard b = pos.attacks_from<Pt>(from) & target;
+ Bitboard b = ( (pos.attacks_from(us, pt, from) & pos.pieces())
+ | (pos.moves_from(us, pt, from) & ~pos.pieces())) & target;
if (Checks)
- b &= pos.check_squares(Pt);
+ b &= pos.check_squares(pt);
while (b)
*moveList++ = make_move(from, pop_lsb(&b));
constexpr bool Checks = Type == QUIET_CHECKS;
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
- moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
- moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
- moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target);
- moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
+ for (PieceType pt = PieceType(PAWN + 1); pt < KING; ++pt)
+ moveList = generate_moves<Checks>(pos, moveList, Us, pt, target);
if (Type != QUIET_CHECKS && Type != EVASIONS)
{
Square ksq = pos.square<KING>(Us);
- Bitboard b = pos.attacks_from<KING>(ksq) & target;
+ Bitboard b = pos.attacks_from<KING>(Us, ksq) & target;
while (b)
*moveList++ = make_move(ksq, pop_lsb(&b));
}
- if (Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
+ if (pos.castling_enabled() && Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
{
if (pos.is_chess960())
{
if (pt == PAWN)
continue; // Will be generated together with direct checks
- Bitboard b = pos.attacks_from(pt, from) & ~pos.pieces();
+ Bitboard b = pos.moves_from(us, pt, from) & ~pos.pieces();
if (pt == KING)
- b &= ~PseudoAttacks[QUEEN][pos.square<KING>(~us)];
+ b &= ~PseudoAttacks[~us][QUEEN][pos.square<KING>(~us)];
while (b)
*moveList++ = make_move(from, pop_lsb(&b));
Color us = pos.side_to_move();
Square ksq = pos.square<KING>(us);
Bitboard sliderAttacks = 0;
- Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
+ Bitboard sliders = pos.checkers();
// 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
while (sliders)
{
Square checksq = pop_lsb(&sliders);
- sliderAttacks |= LineBB[checksq][ksq] ^ checksq;
+ sliderAttacks |= attacks_bb(~us, type_of(pos.piece_on(checksq)), checksq, pos.pieces() ^ ksq);
}
// Generate evasions for king, capture and non capture moves
- Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
+ Bitboard b = pos.attacks_from<KING>(us, ksq) & ~pos.pieces(us) & ~sliderAttacks;
while (b)
*moveList++ = make_move(ksq, pop_lsb(&b));
// Generate blocking evasions or captures of the checking piece
Square checksq = lsb(pos.checkers());
Bitboard target = between_bb(checksq, ksq) | checksq;
+ // Leaper attacks can not be blocked
+ if (LeaperAttacks[~us][type_of(pos.piece_on(checksq))][checksq] & ksq)
+ target = SquareBB[checksq];
return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
: generate_all<BLACK, EVASIONS>(pos, moveList, target);
// Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s);
stoppers = theirPawns & passed_pawn_mask(Us, s);
- lever = theirPawns & PawnAttacks[Us][s];
- leverPush = theirPawns & PawnAttacks[Us][s + Up];
+ lever = theirPawns & PseudoAttacks[Us][PAWN][s];
+ leverPush = theirPawns & PseudoAttacks[Us][PAWN][s + Up];
doubled = ourPawns & (s - Up);
neighbours = ourPawns & adjacent_files_bb(f);
phalanx = neighbours & rank_bb(s);
{
b = shift<Up>(supported) & ~theirPawns;
while (b)
- if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
+ if (!more_than_one(theirPawns & PseudoAttacks[Us][PAWN][pop_lsb(&b)]))
e->passedPawns[Us] |= s;
}
namespace {
-const string PieceToChar(" PNBRQK pnbrqk");
-
-constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
- B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
-
// min_attacker() is a helper function used by see_ge() 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.
for (Rank r = RANK_8; r >= RANK_1; --r)
{
for (File f = FILE_A; f <= FILE_H; ++f)
- os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
+ os << " | " << pos.piece_to_char()[pos.piece_on(make_square(f, r))];
os << " |\n +---+---+---+---+---+---+---+---+\n";
}
{
StateInfo st;
Position p;
- p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
+ p.set(pos.variant(), pos.fen(), pos.is_chess960(), &st, pos.this_thread());
Tablebases::ProbeState s1, s2;
Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1);
int dtz = Tablebases::probe_dtz(p, &s2);
PRNG rng(1070372);
- for (Piece pc : Pieces)
- for (Square s = SQ_A1; s <= SQ_H8; ++s)
- Zobrist::psq[pc][s] = rng.rand<Key>();
+ 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[make_piece(c, pt)][s] = rng.rand<Key>();
for (File f = FILE_A; f <= FILE_H; ++f)
Zobrist::enpassant[f] = rng.rand<Key>();
// Prepare the cuckoo tables
int count = 0;
- for (Piece pc : Pieces)
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt = KNIGHT; pt <= QUEEN || pt == KING; pt != QUEEN ? ++pt : pt = KING)
+ {
+ Piece pc = make_piece(c, pt);
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
- if (PseudoAttacks[type_of(pc)][s1] & s2)
+ if (PseudoAttacks[WHITE][type_of(pc)][s1] & s2)
{
Move move = make_move(s1, s2);
Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side;
}
count++;
}
+ }
assert(count == 3668);
}
/// This function is not very robust - make sure that input FENs are correct,
/// this is assumed to be the responsibility of the GUI.
-Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
+Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
/*
A FEN string defines a particular position using only the ASCII character set.
std::memset(this, 0, sizeof(Position));
std::memset(si, 0, sizeof(StateInfo));
std::fill_n(&pieceList[0][0], sizeof(pieceList) / sizeof(Square), SQ_NONE);
+ var = v;
st = si;
ss >> std::noskipws;
else if (token == '/')
sq += 2 * SOUTH;
- else if ((idx = PieceToChar.find(token)) != string::npos)
+ else if ((idx = piece_to_char().find(token)) != string::npos)
{
put_piece(Piece(idx), sq);
++sq;
sideToMove = (token == 'w' ? WHITE : BLACK);
ss >> token;
+ // 3-4. Skip parsing castling and en passant flags if not present
+ st->epSquare = SQ_NONE;
+ if (!isdigit(ss.peek()))
+ {
// 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
// Shredder-FEN that uses the letters of the columns on which the rooks began
// the game instead of KQkq and also X-FEN standard that, in case of Chess960,
|| !(pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove))))
st->epSquare = SQ_NONE;
}
- else
- st->epSquare = SQ_NONE;
+ }
// 5-6. Halfmove clock and fullmove number
ss >> std::skipws >> st->rule50 >> gamePly;
Square ksq = square<KING>(~sideToMove);
- si->checkSquares[PAWN] = attacks_from<PAWN>(ksq, ~sideToMove);
- si->checkSquares[KNIGHT] = attacks_from<KNIGHT>(ksq);
- si->checkSquares[BISHOP] = attacks_from<BISHOP>(ksq);
- si->checkSquares[ROOK] = attacks_from<ROOK>(ksq);
- si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
+ for (PieceType pt = PAWN; pt < KING; ++pt)
+ si->checkSquares[pt] = attacks_from(~sideToMove, pt, ksq);
si->checkSquares[KING] = 0;
}
si->pawnKey ^= Zobrist::psq[piece_on(s)][s];
}
- for (Piece pc : Pieces)
- {
- if (type_of(pc) != PAWN && type_of(pc) != KING)
- si->nonPawnMaterial[color_of(pc)] += pieceCount[pc] * PieceValue[MG][pc];
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
+ {
+ Piece pc = make_piece(c, pt);
+ if (pt != PAWN && pt != KING)
+ si->nonPawnMaterial[c] += pieceCount[pc] * PieceValue[MG][pc];
- for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
- si->materialKey ^= Zobrist::psq[pc][cnt];
- }
+ for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
+ si->materialKey ^= Zobrist::psq[pc][cnt];
+ }
}
string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/"
+ sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10";
- return set(fenStr, false, si, nullptr);
+ return set(variants.find("chess")->second, fenStr, false, si, nullptr);
}
ss << emptyCnt;
if (f <= FILE_H)
- ss << PieceToChar[piece_on(make_square(f, r))];
+ ss << piece_to_char()[piece_on(make_square(f, r))];
}
if (r > RANK_1)
pinners = 0;
// Snipers are sliders that attack 's' when a piece is removed
- Bitboard snipers = ( (PseudoAttacks[ ROOK][s] & pieces(QUEEN, ROOK))
- | (PseudoAttacks[BISHOP][s] & pieces(QUEEN, BISHOP))) & sliders;
+ Bitboard snipers = sliders
+ & attackers_to(s, 0)
+ & ~attackers_to(s);
while (snipers)
{
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
- return (attacks_from<PAWN>(s, BLACK) & pieces(WHITE, PAWN))
- | (attacks_from<PAWN>(s, WHITE) & pieces(BLACK, PAWN))
- | (attacks_from<KNIGHT>(s) & pieces(KNIGHT))
- | (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
- | (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
- | (attacks_from<KING>(s) & pieces(KING));
+ Bitboard b = 0;
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
+ b |= attacks_bb(~c, pt, s, occupied) & pieces(c, pt);
+ return b;
}
Color us = sideToMove;
Square from = from_sq(m);
+ Square to = to_sq(m);
+ Square ksq = square<KING>(us);
assert(color_of(moved_piece(m)) == us);
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
// the move is made.
if (type_of(m) == ENPASSANT)
{
- Square ksq = square<KING>(us);
- Square to = to_sq(m);
Square capsq = to - pawn_push(us);
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
assert(piece_on(capsq) == make_piece(~us, PAWN));
assert(piece_on(to) == NO_PIECE);
- return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
- && !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
+ return !(attackers_to(ksq, occupied) & pieces(~us) & occupied);
}
// If the moving piece is a king, check whether the destination
// square is attacked by the opponent. Castling moves are checked
// for legality during move generation.
if (type_of(piece_on(from)) == KING)
- return type_of(m) == CASTLING || !(attackers_to(to_sq(m)) & pieces(~us));
+ return type_of(m) == CASTLING || !(attackers_to(to) & pieces(~us));
- // A non-king move is legal if and only if it is not pinned or it
- // is moving along the ray towards or away from the king.
- return !(blockers_for_king(us) & from)
- || aligned(from, to_sq(m), square<KING>(us));
+ // A non-king move is legal if the king is not under attack after the move.
+ return !(attackers_to(ksq, (pieces() ^ from) | to) & pieces(~us) & ~SquareBB[to]);
}
return MoveList<LEGAL>(*this).contains(m);
// Is not a promotion, so promotion piece must be empty
- if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
+ if (promotion_type(m) != NO_PIECE_TYPE)
return false;
// If the 'from' square is not occupied by a piece belonging to the side to
{
// We have already handled promotion moves, so destination
// cannot be on the 8th/1st rank.
- if (rank_of(to) == relative_rank(us, RANK_8))
+ if (rank_of(to) == relative_rank(us, promotion_rank()))
return false;
- if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
+ if ( !(attacks_from<PAWN>(us, from) & pieces(~us) & to) // Not a capture
&& !((from + pawn_push(us) == to) && empty(to)) // Not a single push
&& !( (from + 2 * pawn_push(us) == to) // Not a double push
&& (rank_of(from) == relative_rank(us, RANK_2))
&& empty(to - pawn_push(us))))
return false;
}
- else if (!(attacks_from(type_of(pc), from) & to))
+ else if (!(attacks_from(us, type_of(pc), from) & to))
return false;
// Evasions generator already takes care to avoid some kind of illegal moves
return false;
// Our move must be a blocking evasion or a capture of the checking piece
- if (!((between_bb(lsb(checkers()), square<KING>(us)) | checkers()) & to))
+ Square checksq = lsb(checkers());
+ if ( !((between_bb(checksq, square<KING>(us)) | checkers()) & to)
+ || (LeaperAttacks[~us][type_of(piece_on(checksq))][checksq] & square<KING>(us)))
return false;
}
// In case of king moves under check we have to remove king so as to catch
// Is there a discovered check?
if ( (st->blockersForKing[~sideToMove] & from)
- && !aligned(from, to, square<KING>(~sideToMove)))
+ && ( !aligned(from, to, square<KING>(~sideToMove))
+ || (attackers_to(square<KING>(~sideToMove), (pieces() ^ from) | to) & pieces(sideToMove))))
return true;
switch (type_of(m))
return false;
case PROMOTION:
- return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
+ return attacks_bb(sideToMove, promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
// En passant capture with check? We have already handled the case
// of direct checks and ordinary discovered check, so the only case we
Square capsq = make_square(file_of(to), rank_of(from));
Bitboard b = (pieces() ^ from ^ capsq) | to;
- return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
- | (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
+ return attackers_to(square<KING>(~sideToMove), b) & pieces(sideToMove) & b;
}
case CASTLING:
{
Square kto = relative_square(sideToMove, rfrom > kfrom ? SQ_G1 : SQ_C1);
Square rto = relative_square(sideToMove, rfrom > kfrom ? SQ_F1 : SQ_D1);
- return (PseudoAttacks[ROOK][rto] & square<KING>(~sideToMove))
+ return (PseudoAttacks[sideToMove][ROOK][rto] & square<KING>(~sideToMove))
&& (attacks_bb<ROOK>(rto, (pieces() ^ kfrom ^ rfrom) | rto | kto) & square<KING>(~sideToMove));
}
default:
{
// Set en-passant square if the moved pawn can be captured
if ( (int(to) ^ int(from)) == 16
- && (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
+ && (attacks_from<PAWN>(us, to - pawn_push(us)) & pieces(them, PAWN)))
{
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
{
Piece promotion = make_piece(us, promotion_type(m));
- assert(relative_rank(us, to) == RANK_8);
- assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
+ assert(relative_rank(us, to) == promotion_rank());
+ assert(type_of(promotion) >= KNIGHT && type_of(promotion) < KING);
remove_piece(pc, to);
put_piece(promotion, to);
if (type_of(m) == PROMOTION)
{
- assert(relative_rank(us, to) == RANK_8);
+ assert(relative_rank(us, to) == promotion_rank());
assert(type_of(pc) == promotion_type(m));
- assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
+ assert(type_of(pc) >= KNIGHT && type_of(pc) < KING);
remove_piece(pc, to);
pc = make_piece(us, PAWN);
std::getline(ss, token); // Half and full moves
f += token;
- set(f, is_chess960(), st, this_thread());
+ set(variant(), f, is_chess960(), st, this_thread());
assert(pos_is_ok());
}
constexpr bool Fast = true; // Quick (default) or full check?
if ( (sideToMove != WHITE && sideToMove != BLACK)
- || piece_on(square<KING>(WHITE)) != W_KING
- || piece_on(square<KING>(BLACK)) != B_KING
+ || piece_on(square<KING>(WHITE)) != make_piece(WHITE, KING)
+ || piece_on(square<KING>(BLACK)) != make_piece(BLACK, KING)
|| ( ep_square() != SQ_NONE
&& relative_rank(sideToMove, ep_square()) != RANK_6))
assert(0 && "pos_is_ok: Default");
if (Fast)
return true;
- if ( pieceCount[W_KING] != 1
- || pieceCount[B_KING] != 1
+ if ( pieceCount[make_piece(WHITE, KING)] != 1
+ || pieceCount[make_piece(BLACK, KING)] != 1
|| attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
assert(0 && "pos_is_ok: Kings");
if ( (pieces(PAWN) & (Rank1BB | Rank8BB))
- || pieceCount[W_PAWN] > 8
- || pieceCount[B_PAWN] > 8)
+ || pieceCount[make_piece(WHITE, PAWN)] > 8
+ || pieceCount[make_piece(BLACK, PAWN)] > 8)
assert(0 && "pos_is_ok: Pawns");
if ( (pieces(WHITE) & pieces(BLACK))
if (std::memcmp(&si, st, sizeof(StateInfo)))
assert(0 && "pos_is_ok: State");
- for (Piece pc : Pieces)
- {
- if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
- || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
- assert(0 && "pos_is_ok: Pieces");
-
- for (int i = 0; i < pieceCount[pc]; ++i)
- if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
- assert(0 && "pos_is_ok: Index");
- }
+ for (Color c = WHITE; c <= BLACK; ++c)
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
+ {
+ Piece pc = make_piece(c, pt);
+ if ( pieceCount[pc] != popcount(pieces(c, pt))
+ || pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
+ assert(0 && "pos_is_ok: Pieces");
+
+ for (int i = 0; i < pieceCount[pc]; ++i)
+ if (board[pieceList[pc][i]] != pc || index[pieceList[pc][i]] != i)
+ assert(0 && "pos_is_ok: Index");
+ }
for (Color c = WHITE; c <= BLACK; ++c)
for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1))
#include "bitboard.h"
#include "types.h"
+#include "variant.h"
/// StateInfo struct stores information needed to restore a Position object to
Position& operator=(const Position&) = delete;
// FEN string input/output
- Position& set(const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
+ Position& set(const Variant* v, const std::string& fenStr, bool isChess960, StateInfo* si, Thread* th);
Position& set(const std::string& code, Color c, StateInfo* si);
const std::string fen() const;
+ // Variant rule properties
+ const Variant* variant() const;
+ const std::string piece_to_char() const;
+ Rank promotion_rank() const;
+ std::vector<PieceType> promotion_piece_types() const;
+ bool double_step_enabled() const;
+ bool castling_enabled() const;
+
// Position representation
Bitboard pieces() const;
Bitboard pieces(PieceType pt) const;
template<PieceType Pt> int count(Color c) const;
template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const;
+ const Square* squares(Color c, PieceType pt) const;
template<PieceType Pt> Square square(Color c) const;
// Castling
// Attacks to/from a given square
Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occupied) const;
- Bitboard attacks_from(PieceType pt, Square s) const;
- template<PieceType> Bitboard attacks_from(Square s) const;
- template<PieceType> Bitboard attacks_from(Square s, Color c) const;
+ Bitboard attacks_from(Color c, PieceType pt, Square s) const;
+ template<PieceType> Bitboard attacks_from(Color c, Square s) const;
+ Bitboard moves_from(Color c, PieceType pt, Square s) const;
Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
// Properties of moves
Color sideToMove;
Thread* thisThread;
StateInfo* st;
+ const Variant* var;
bool chess960;
};
extern std::ostream& operator<<(std::ostream& os, const Position& pos);
+inline const Variant* Position::variant() const {
+ assert(var != nullptr);
+ return var;
+}
+
+inline const std::string Position::piece_to_char() const {
+ assert(var != nullptr);
+ return var->pieceToChar;
+}
+
+inline Rank Position::promotion_rank() const {
+ assert(var != nullptr);
+ return var->promotionRank;
+}
+
+inline std::vector<PieceType> Position::promotion_piece_types() const {
+ assert(var != nullptr);
+ return var->promotionPieceTypes;
+}
+
+inline bool Position::double_step_enabled() const {
+ assert(var != nullptr);
+ return var->doubleStep;
+}
+
+inline bool Position::castling_enabled() const {
+ assert(var != nullptr);
+ return var->castling;
+}
+
inline Color Position::side_to_move() const {
return sideToMove;
}
return pieceList[make_piece(c, Pt)];
}
+inline const Square* Position::squares(Color c, PieceType pt) const {
+ return pieceList[make_piece(c, pt)];
+}
+
template<PieceType Pt> inline Square Position::square(Color c) const {
assert(pieceCount[make_piece(c, Pt)] == 1);
return pieceList[make_piece(c, Pt)][0];
}
template<PieceType Pt>
-inline Bitboard Position::attacks_from(Square s) const {
- assert(Pt != PAWN);
- return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
- : Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
- : PseudoAttacks[Pt][s];
+inline Bitboard Position::attacks_from(Color c, Square s) const {
+ return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]);
}
-template<>
-inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
- return PawnAttacks[c][s];
+inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const {
+ return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]);
}
-inline Bitboard Position::attacks_from(PieceType pt, Square s) const {
- return attacks_bb(pt, s, byTypeBB[ALL_PIECES]);
+inline Bitboard Position::moves_from(Color c, PieceType pt, Square s) const {
+ return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]);
}
inline Bitboard Position::attackers_to(Square s) const {
}
inline bool Position::opposite_bishops() const {
- return pieceCount[W_BISHOP] == 1
- && pieceCount[B_BISHOP] == 1
+ return pieceCount[make_piece(WHITE, BISHOP)] == 1
+ && pieceCount[make_piece(BLACK, BISHOP)] == 1
&& opposite_colors(square<BISHOP>(WHITE), square<BISHOP>(BLACK));
}
#include "types.h"
Value PieceValue[PHASE_NB][PIECE_NB] = {
- { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg },
- { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg }
+ { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg,
+ FersValueMg, AlfilValueMg, SilverValueMg, AiwokValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg },
+ { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg,
+ FersValueEg, AlfilValueEg, SilverValueEg, AiwokValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg }
};
namespace PSQT {
// type on a given square a (middlegame, endgame) score pair is assigned. Table
// is defined for files A..D and white side: it is symmetric for black side and
// second half of the files.
-constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
+constexpr Score Bonus[PIECE_TYPE_NB][RANK_NB][int(FILE_NB) / 2] = {
{ },
{ // Pawn
{ S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
// tables are initialized by flipping and changing the sign of the white scores.
void init() {
- for (Piece pc = W_PAWN; pc <= W_KING; ++pc)
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
{
+ Piece pc = make_piece(WHITE, pt);
+
PieceValue[MG][~pc] = PieceValue[MG][pc];
PieceValue[EG][~pc] = PieceValue[EG][pc];
pos.undo_move(m);
}
if (Root)
- sync_cout << UCI::move(m, pos.is_chess960()) << ": " << cnt << sync_endl;
+ sync_cout << UCI::move(pos, m) << ": " << cnt << sync_endl;
}
return nodes;
}
if (bestThread != this)
sync_cout << UCI::pv(bestThread->rootPos, bestThread->completedDepth, -VALUE_INFINITE, VALUE_INFINITE) << sync_endl;
- sync_cout << "bestmove " << UCI::move(bestThread->rootMoves[0].pv[0], rootPos.is_chess960());
+ sync_cout << "bestmove " << UCI::move(rootPos, bestThread->rootMoves[0].pv[0]);
if (bestThread->rootMoves[0].pv.size() > 1 || bestThread->rootMoves[0].extract_ponder_from_tt(rootPos))
- std::cout << " ponder " << UCI::move(bestThread->rootMoves[0].pv[1], rootPos.is_chess960());
+ std::cout << " ponder " << UCI::move(rootPos, bestThread->rootMoves[0].pv[1]);
std::cout << sync_endl;
}
if (rootNode && thisThread == Threads.main() && Time.elapsed() > 3000)
sync_cout << "info depth " << depth / ONE_PLY
- << " currmove " << UCI::move(move, pos.is_chess960())
+ << " currmove " << UCI::move(pos, move)
<< " currmovenumber " << moveCount + thisThread->pvIdx << sync_endl;
if (PvNode)
(ss+1)->pv = nullptr;
<< " pv";
for (Move m : rootMoves[i].pv)
- ss << " " << UCI::move(m, pos.is_chess960());
+ ss << " " << UCI::move(pos, m);
}
return ss.str();
if (MapA1D1D4[s1] == idx && (idx || s1 == SQ_B1)) // SQ_B1 is mapped to 0
{
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
- if ((PseudoAttacks[KING][s1] | s1) & s2)
+ if ((PseudoAttacks[WHITE][KING][s1] | s1) & s2)
continue; // Illegal position
else if (!off_A1H8(s1) && off_A1H8(s2) > 0)
th->nodes = th->tbHits = th->nmpMinPly = 0;
th->rootDepth = th->completedDepth = DEPTH_ZERO;
th->rootMoves = rootMoves;
- th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
+ th->rootPos.set(pos.variant(), pos.fen(), pos.is_chess960(), &setupStates->back(), th);
}
setupStates->back() = tmp;
enum MoveType {
NORMAL,
- PROMOTION = 1 << 14,
- ENPASSANT = 2 << 14,
- CASTLING = 3 << 14
+ ENPASSANT = 1 << 12,
+ CASTLING = 2 << 12,
+ PROMOTION = 3 << 12,
+ PROMOTION_STRAIGHT = PROMOTION,
+ PROMOTION_LEFT = 4 << 12,
+ PROMOTION_RIGHT = 5 << 12,
};
enum Color {
BishopValueMg = 826, BishopValueEg = 891,
RookValueMg = 1282, RookValueEg = 1373,
QueenValueMg = 2500, QueenValueEg = 2670,
+ FersValueMg = 400, FersValueEg = 400,
+ AlfilValueMg = 300, AlfilValueEg = 300,
+ SilverValueMg = 600, SilverValueEg = 600,
+ AiwokValueMg = 2500, AiwokValueEg = 2500,
+ AmazonValueMg = 3000, AmazonValueEg = 3000,
+ KnibisValueMg = 800, KnibisValueEg = 800,
+ BiskniValueMg = 800, BiskniValueEg = 800,
MidgameLimit = 15258, EndgameLimit = 3915
};
+const int PIECE_TYPE_BITS = 6; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS)
+
enum PieceType {
- NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING,
+ NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK,
+ QUEEN, FERS, MET = FERS, ALFIL, SILVER, KHON = SILVER, AIWOK,
+ AMAZON, KNIBIS, BISKNI, KING,
ALL_PIECES = 0,
- PIECE_TYPE_NB = 8
+
+ PIECE_TYPE_NB = 1 << PIECE_TYPE_BITS
};
+static_assert(PIECE_TYPE_BITS <= 6, "PIECE_TYPE uses more than 6 bit");
+static_assert(!(PIECE_TYPE_NB & (PIECE_TYPE_NB - 1)), "PIECE_TYPE_NB is not a power of 2");
enum Piece {
NO_PIECE,
- W_PAWN = 1, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
- B_PAWN = 9, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING,
- PIECE_NB = 16
+ PIECE_NB = 2 * PIECE_TYPE_NB
};
extern Value PieceValue[PHASE_NB][PIECE_NB];
return File(f ^ FILE_H); // Horizontal flip FILE_A -> FILE_H
}
+constexpr Rank operator~(Rank r) {
+ return Rank(r ^ RANK_8); // Vertical flip Rank_1 -> Rank_8
+}
+
constexpr Piece operator~(Piece pc) {
- return Piece(pc ^ 8); // Swap color of piece B_KNIGHT -> W_KNIGHT
+ return Piece(pc ^ PIECE_TYPE_NB); // Swap color of piece BLACK KNIGHT -> WHITE KNIGHT
}
constexpr CastlingRight operator|(Color c, CastlingSide s) {
}
constexpr Piece make_piece(Color c, PieceType pt) {
- return Piece((c << 3) + pt);
+ return Piece((c << PIECE_TYPE_BITS) + pt);
}
constexpr PieceType type_of(Piece pc) {
- return PieceType(pc & 7);
+ return PieceType(pc & (PIECE_TYPE_NB - 1));
}
inline Color color_of(Piece pc) {
assert(pc != NO_PIECE);
- return Color(pc >> 3);
+ return Color(pc >> PIECE_TYPE_BITS);
}
constexpr bool is_ok(Square s) {
return c == WHITE ? NORTH : SOUTH;
}
-constexpr Square from_sq(Move m) {
- return Square((m >> 6) & 0x3F);
+inline MoveType type_of(Move m) {
+ MoveType t = MoveType(m & (15 << 12));
+ if (t == PROMOTION_STRAIGHT || t == PROMOTION_LEFT || t == PROMOTION_RIGHT)
+ return PROMOTION;
+ return t;
}
constexpr Square to_sq(Move m) {
return Square(m & 0x3F);
}
-constexpr int from_to(Move m) {
- return m & 0xFFF;
+inline Square from_sq(Move m) {
+ if (type_of(m) == PROMOTION)
+ {
+ Square to = to_sq(m);
+ MoveType t = MoveType(m & (15 << 12));
+ // Assume here that promotion occur only for relative ranks >= RANK_5.
+ Direction up = (to & 32) ? NORTH : SOUTH;
+ if (t == PROMOTION_STRAIGHT)
+ return to - up;
+ if (t == PROMOTION_LEFT)
+ return to - up - WEST;
+ if (t == PROMOTION_RIGHT)
+ return to - up - EAST;
+ assert(false);
+ }
+ return Square((m >> 6) & 0x3F);
}
-constexpr MoveType type_of(Move m) {
- return MoveType(m & (3 << 14));
+inline int from_to(Move m) {
+ return to_sq(m) + (from_sq(m) << 6);
}
-constexpr PieceType promotion_type(Move m) {
- return PieceType(((m >> 12) & 3) + KNIGHT);
+inline PieceType promotion_type(Move m) {
+ return type_of(m) == PROMOTION ? PieceType((m >> 6) & 63) : NO_PIECE_TYPE;
}
inline Move make_move(Square from, Square to) {
}
template<MoveType T>
-constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
- return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
+inline Move make(Square from, Square to, PieceType pt = NO_PIECE_TYPE) {
+ if (T == PROMOTION_STRAIGHT || T == PROMOTION_LEFT || T == PROMOTION_RIGHT)
+ return Move(T + (pt << 6) + to);
+ return Move(T + (from << 6) + to);
}
-constexpr bool is_ok(Move m) {
+inline bool is_ok(Move m) {
return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
}
namespace {
- // FEN string of the initial position, normal chess
- const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
-
-
// position() is called when engine receives the "position" UCI command.
// The function sets up the position described in the given FEN string ("fen")
// or the starting position ("startpos") and then makes the moves given in the
if (token == "startpos")
{
- fen = StartFEN;
+ fen = variants.find(Options["UCI_Variant"])->second->startFen;
is >> token; // Consume "moves" token if any
}
else if (token == "fen")
return;
states = StateListPtr(new std::deque<StateInfo>(1)); // Drop old and create a new one
- pos.set(fen, Options["UCI_Chess960"], &states->back(), Threads.main());
+ pos.set(variants.find(Options["UCI_Variant"])->second, fen, Options["UCI_Chess960"], &states->back(), Threads.main());
// Parse move list (if any)
while (is >> token && (m = UCI::to_move(pos, token)) != MOVE_NONE)
StateListPtr states(new std::deque<StateInfo>(1));
auto uiThread = std::make_shared<Thread>(0);
- pos.set(StartFEN, false, &states->back(), uiThread.get());
+ assert(variants.find(Options["UCI_Variant"])->second != nullptr);
+ pos.set(variants.find(Options["UCI_Variant"])->second, variants.find(Options["UCI_Variant"])->second->startFen, false, &states->back(), uiThread.get());
for (int i = 1; i < argc; ++i)
cmd += std::string(argv[i]) + " ";
/// normal chess mode, and in e1h1 notation in chess960 mode. Internally all
/// castling moves are always encoded as 'king captures rook'.
-string UCI::move(Move m, bool chess960) {
+string UCI::move(const Position& pos, Move m) {
Square from = from_sq(m);
Square to = to_sq(m);
if (m == MOVE_NULL)
return "0000";
- if (type_of(m) == CASTLING && !chess960)
+ if (type_of(m) == CASTLING && !pos.is_chess960())
to = make_square(to > from ? FILE_G : FILE_C, rank_of(from));
string move = UCI::square(from) + UCI::square(to);
if (type_of(m) == PROMOTION)
- move += " pnbrqk"[promotion_type(m)];
+ move += pos.piece_to_char()[make_piece(BLACK, promotion_type(m))];
return move;
}
str[4] = char(tolower(str[4]));
for (const auto& m : MoveList<LEGAL>(pos))
- if (str == UCI::move(m, pos.is_chess960()))
+ if (str == UCI::move(pos, m))
return m;
return MOVE_NONE;
#include <map>
#include <string>
+#include <vector>
#include "types.h"
Option(OnChange = nullptr);
Option(bool v, OnChange = nullptr);
Option(const char* v, OnChange = nullptr);
+ Option(const char* v, const std::vector<std::string>& variants, OnChange = nullptr);
Option(double v, int minv, int maxv, OnChange = nullptr);
Option(const char* v, const char *cur, OnChange = nullptr);
std::string defaultValue, currentValue, type;
int min, max;
+ std::vector<std::string> comboValues;
size_t idx;
OnChange on_change;
};
void loop(int argc, char* argv[]);
std::string value(Value v);
std::string square(Square s);
-std::string move(Move m, bool chess960);
+std::string move(const Position& pos, Move m);
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
Move to_move(const Position& pos, std::string& str);
#include <algorithm>
#include <cassert>
-#include <ostream>
+#include <iostream>
#include "misc.h"
#include "search.h"
#include "thread.h"
#include "tt.h"
#include "uci.h"
+#include "variant.h"
#include "syzygy/tbprobe.h"
using std::string;
void on_logger(const Option& o) { start_logger(o); }
void on_threads(const Option& o) { Threads.set(o); }
void on_tb_path(const Option& o) { Tablebases::init(o); }
+void on_variant_change(const Option &o) {
+ sync_cout << "info string variant " << (std::string)o << " startpos " << variants.find(o)->second->startFen << sync_endl;
+}
/// Our case insensitive less() function as required by UCI protocol
o["Slow Mover"] << Option(84, 10, 1000);
o["nodestime"] << Option(0, 0, 10000);
o["UCI_Chess960"] << Option(false);
+ o["UCI_Variant"] << Option("chess", variants.get_keys(), on_variant_change);
o["UCI_AnalyseMode"] << Option(false);
o["SyzygyPath"] << Option("<empty>", on_tb_path);
o["SyzygyProbeDepth"] << Option(1, 1, 100);
if (o.type == "string" || o.type == "check" || o.type == "combo")
os << " default " << o.defaultValue;
+ if (o.type == "combo")
+ for (string value : o.comboValues)
+ os << " var " << value;
+
if (o.type == "spin")
os << " default " << int(stof(o.defaultValue))
<< " min " << o.min
Option::Option(const char* v, OnChange f) : type("string"), min(0), max(0), on_change(f)
{ defaultValue = currentValue = v; }
+Option::Option(const char* v, const std::vector<std::string>& variants, OnChange f) : type("combo"), min(0), max(0), comboValues(variants), on_change(f)
+{ defaultValue = currentValue = v; }
+
Option::Option(bool v, OnChange f) : type("check"), min(0), max(0), on_change(f)
{ defaultValue = currentValue = (v ? "true" : "false"); }
}
Option::operator std::string() const {
- assert(type == "string");
+ assert(type == "string" || type == "combo");
return currentValue;
}
if ( (type != "button" && v.empty())
|| (type == "check" && v != "true" && v != "false")
+ || (type == "combo" && (std::find(comboValues.begin(), comboValues.end(), v) == comboValues.end()))
|| (type == "spin" && (stof(v) < min || stof(v) > max)))
return *this;
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, 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 <string>
+
+#include "variant.h"
+
+using std::string;
+
+VariantMap variants; // Global object
+
+void VariantMap::init() {
+ const Variant* chess = [&]{
+ Variant* v = new Variant();
+ return v;
+ } ();
+ const Variant* makruk = [&]{
+ Variant* v = new Variant();
+ v->reset_pieces();
+ v->set_piece(PAWN, 'p');
+ v->set_piece(KNIGHT, 'n');
+ v->set_piece(KHON, 's');
+ v->set_piece(ROOK, 'r');
+ v->set_piece(MET, 'm');
+ v->set_piece(KING, 'k');
+ v->startFen = "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w - - 0 1";
+ v->promotionRank = RANK_6;
+ v->promotionPieceTypes = {MET};
+ v->doubleStep = false;
+ v->castling = false;
+ return v;
+ } ();
+ const Variant* asean = [&]{
+ Variant* v = new Variant();
+ v->reset_pieces();
+ v->set_piece(PAWN, 'p');
+ v->set_piece(KNIGHT, 'n');
+ v->set_piece(KHON, 'b');
+ v->set_piece(ROOK, 'r');
+ v->set_piece(MET, 'q');
+ v->set_piece(KING, 'k');
+ v->startFen = "rnbqkbnr/8/pppppppp/8/8/PPPPPPPP/8/RNBQKBNR w - - 0 1";
+ v->promotionPieceTypes = {ROOK, KNIGHT, KHON, MET};
+ v->doubleStep = false;
+ v->castling = false;
+ return v;
+ } ();
+ const Variant* aiwok = [&]{
+ Variant* v = new Variant();
+ v->reset_pieces();
+ v->set_piece(PAWN, 'p');
+ v->set_piece(KNIGHT, 'n');
+ v->set_piece(KHON, 's');
+ v->set_piece(ROOK, 'r');
+ v->set_piece(AIWOK, 'a');
+ v->set_piece(KING, 'k');
+ v->startFen = "rnsaksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKASNR w - - 0 1";
+ v->promotionRank = RANK_6;
+ v->promotionPieceTypes = {AIWOK};
+ v->doubleStep = false;
+ v->castling = false;
+ return v;
+ } ();
+ const Variant* shatranj = [&]{
+ Variant* v = new Variant();
+ v->reset_pieces();
+ v->set_piece(PAWN, 'p');
+ v->set_piece(KNIGHT, 'n');
+ v->set_piece(ALFIL, 'b');
+ v->set_piece(ROOK, 'r');
+ v->set_piece(FERS, 'q');
+ v->set_piece(KING, 'k');
+ v->startFen = "rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR w - - 0 1";
+ v->promotionPieceTypes = {FERS};
+ v->doubleStep = false;
+ v->castling = false;
+ // TODO: bare king, stalemate
+ return v;
+ } ();
+ const Variant* amazon = [&]{
+ Variant* v = new Variant();
+ v->set_piece(QUEEN, ' ');
+ v->set_piece(AMAZON, 'a');
+ v->startFen = "rnbakbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBAKBNR w KQkq - 0 1";
+ v->promotionPieceTypes = {AMAZON, ROOK, BISHOP, KNIGHT};
+ return v;
+ } ();
+ const Variant* hoppelpoppel = [&]{
+ Variant* v = new Variant();
+ v->set_piece(KNIGHT, ' ');
+ v->set_piece(BISHOP, ' ');
+ v->set_piece(KNIBIS, 'n');
+ v->set_piece(BISKNI, 'b');
+ v->promotionPieceTypes = {QUEEN, ROOK, BISKNI, KNIBIS};
+ return v;
+ } ();
+ insert(std::pair<std::string, const Variant*>(std::string("chess"), chess));
+ insert(std::pair<std::string, const Variant*>(std::string("makruk"), makruk));
+ insert(std::pair<std::string, const Variant*>(std::string("asean"), asean));
+ insert(std::pair<std::string, const Variant*>(std::string("ai-wok"), aiwok));
+ insert(std::pair<std::string, const Variant*>(std::string("shatranj"), shatranj));
+ insert(std::pair<std::string, const Variant*>(std::string("amazon"), amazon));
+ insert(std::pair<std::string, const Variant*>(std::string("hoppelpoppel"), hoppelpoppel));
+}
+
+void VariantMap::clear_all() {
+ for (auto const& element : *this) {
+ delete element.second;
+ }
+ clear();
+}
+
+std::vector<std::string> VariantMap::get_keys() {
+ std::vector<std::string> keys;
+ for (auto const& element : *this) {
+ keys.push_back(element.first);
+ }
+ return keys;
+}
--- /dev/null
+/*
+ Stockfish, a UCI chess playing engine derived from Glaurung 2.1
+ Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
+ Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
+ Copyright (C) 2015-2018 Marco Costalba, Joona Kiiski, Gary Linscott, 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 VARIANT_H_INCLUDED
+#define VARIANT_H_INCLUDED
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include "types.h"
+
+
+/// Variant struct stores information needed to determine the rules of a variant.
+
+struct Variant {
+ std::string pieceToChar = " PNBRQ" + std::string(KING - QUEEN - 1, ' ') + "K" + std::string(PIECE_TYPE_NB - KING - 1, ' ')
+ + " pnbrq" + std::string(KING - QUEEN - 1, ' ') + "k" + std::string(PIECE_TYPE_NB - KING - 1, ' ');
+ std::string startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+ Rank promotionRank = RANK_8;
+ std::vector<PieceType> promotionPieceTypes = {QUEEN, ROOK, BISHOP, KNIGHT};
+ bool doubleStep = true;
+ bool castling = true;
+
+ void set_piece(PieceType pt, char c) {
+ pieceToChar[make_piece(WHITE, pt)] = toupper(c);
+ pieceToChar[make_piece(BLACK, pt)] = tolower(c);
+ }
+
+ void reset_pieces() {
+ pieceToChar = std::string(PIECE_NB, ' ');
+ }
+};
+
+struct VariantMap : public std::map<std::string, const Variant*> {
+ void init();
+ void clear_all();
+ std::vector<std::string> get_keys();
+};
+
+extern VariantMap variants;
+
+#endif // #ifndef VARIANT_H_INCLUDED