// Mating pieces
for (PieceType pt : { ROOK, QUEEN, ARCHBISHOP, CHANCELLOR, SILVER, GOLD, COMMONER, CENTAUR })
- if ((pos.pieces(c, pt) & ~restricted) || (pos.count(c, PAWN) && pos.promotion_piece_types().find(pt) != pos.promotion_piece_types().end()))
+ if ((pos.pieces(c, pt) & ~restricted) || (pos.count(c, pos.promotion_pawn_type(c)) && pos.promotion_piece_types(c).find(pt) != pos.promotion_piece_types(c).end()))
return false;
// Color-bound pieces
// Whether or not '-', '+', '~', '[', ']' are valid depends on the variant being played.
if (v->shogiStylePromotions)
validSpecialCharactersFirstField += '+';
- if (!v->promotionPieceTypes.empty())
+ if (!v->promotionPieceTypes[WHITE].empty() || !v->promotionPieceTypes[BLACK].empty())
validSpecialCharactersFirstField += '~';
if (!v->freeDrops && (v->pieceDrops || v->seirawanGating))
validSpecialCharactersFirstField += "[-]";
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard PseudoAttacks[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
-Bitboard PseudoMoves[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
+Bitboard PseudoMoves[2][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];
+Bitboard LeaperMoves[2][COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
Bitboard BoardSizeBB[FILE_NB][RANK_NB];
RiderType AttackRiderTypes[PIECE_TYPE_NB];
-RiderType MoveRiderTypes[PIECE_TYPE_NB];
+RiderType MoveRiderTypes[2][PIECE_TYPE_NB];
Magic RookMagicsH[SQUARE_NB];
Magic RookMagicsV[SQUARE_NB];
Magic BishopMagics[SQUARE_NB];
Magic CannonMagicsH[SQUARE_NB];
Magic CannonMagicsV[SQUARE_NB];
+Magic LameDabbabaMagics[SQUARE_NB];
Magic HorseMagics[SQUARE_NB];
Magic ElephantMagics[SQUARE_NB];
Magic JanggiElephantMagics[SQUARE_NB];
Magic GrasshopperMagicsD[SQUARE_NB];
Magic* magics[] = {BishopMagics, RookMagicsH, RookMagicsV, CannonMagicsH, CannonMagicsV,
- HorseMagics, ElephantMagics, JanggiElephantMagics, CannonDiagMagics, NightriderMagics,
+ LameDabbabaMagics, HorseMagics, ElephantMagics, JanggiElephantMagics, CannonDiagMagics, NightriderMagics,
GrasshopperMagicsH, GrasshopperMagicsV, GrasshopperMagicsD};
namespace {
Bitboard BishopTable[0x33C00]; // To store bishop attacks
Bitboard CannonTableH[0x11800]; // To store horizontal cannon attacks
Bitboard CannonTableV[0x4800]; // To store vertical cannon attacks
+ Bitboard LameDabbabaTable[0x500]; // To store lame dabbaba attacks
Bitboard HorseTable[0x500]; // To store horse attacks
Bitboard ElephantTable[0x400]; // To store elephant attacks
Bitboard JanggiElephantTable[0x1C000]; // To store janggi elephant attacks
Bitboard BishopTable[0x1480]; // To store bishop attacks
Bitboard CannonTableH[0xA00]; // To store horizontal cannon attacks
Bitboard CannonTableV[0xA00]; // To store vertical cannon attacks
+ Bitboard LameDabbabaTable[0x240]; // To store lame dabbaba attacks
Bitboard HorseTable[0x240]; // To store horse attacks
Bitboard ElephantTable[0x1A0]; // To store elephant attacks
Bitboard JanggiElephantTable[0x5C00]; // To store janggi elephant attacks
const std::map<Direction, int> RookDirectionsV { {NORTH, 0}, {SOUTH, 0}};
const std::map<Direction, int> RookDirectionsH { {EAST, 0}, {WEST, 0} };
const std::map<Direction, int> BishopDirections { {NORTH_EAST, 0}, {SOUTH_EAST, 0}, {SOUTH_WEST, 0}, {NORTH_WEST, 0} };
+ const std::map<Direction, int> LameDabbabaDirections { {2 * NORTH, 0}, {2 * EAST, 0}, {2 * SOUTH, 0}, {2 * WEST, 0} };
const std::map<Direction, int> HorseDirections { {2 * SOUTH + WEST, 0}, {2 * SOUTH + EAST, 0}, {SOUTH + 2 * WEST, 0}, {SOUTH + 2 * EAST, 0},
{NORTH + 2 * WEST, 0}, {NORTH + 2 * EAST, 0}, {2 * NORTH + WEST, 0}, {2 * NORTH + EAST, 0} };
const std::map<Direction, int> ElephantDirections { {2 * NORTH_EAST, 0}, {2 * SOUTH_EAST, 0}, {2 * SOUTH_WEST, 0}, {2 * NORTH_WEST, 0} };
// Detect rider types
for (auto modality : {MODALITY_QUIET, MODALITY_CAPTURE})
{
- auto& riderTypes = modality == MODALITY_CAPTURE ? AttackRiderTypes[pt] : MoveRiderTypes[pt];
- riderTypes = NO_RIDER;
- for (auto const& [d, limit] : pi->steps[modality])
+ for (bool initial : {false, true})
{
- if (limit && HorseDirections.find(d) != HorseDirections.end())
- riderTypes |= RIDER_HORSE;
- if (limit && ElephantDirections.find(d) != ElephantDirections.end())
- riderTypes |= RIDER_ELEPHANT;
- if (limit && JanggiElephantDirections.find(d) != JanggiElephantDirections.end())
- riderTypes |= RIDER_JANGGI_ELEPHANT;
- }
- for (auto const& [d, limit] : pi->slider[modality])
- {
- if (BishopDirections.find(d) != BishopDirections.end())
- riderTypes |= RIDER_BISHOP;
- if (RookDirectionsH.find(d) != RookDirectionsH.end())
- riderTypes |= RIDER_ROOK_H;
- if (RookDirectionsV.find(d) != RookDirectionsV.end())
- riderTypes |= RIDER_ROOK_V;
- if (HorseDirections.find(d) != HorseDirections.end())
- riderTypes |= RIDER_NIGHTRIDER;
- }
- for (auto const& [d, limit] : pi->hopper[modality])
- {
- if (RookDirectionsH.find(d) != RookDirectionsH.end())
- riderTypes |= limit == 1 ? RIDER_GRASSHOPPER_H : RIDER_CANNON_H;
- if (RookDirectionsV.find(d) != RookDirectionsV.end())
- riderTypes |= limit == 1 ? RIDER_GRASSHOPPER_V : RIDER_CANNON_V;
- if (BishopDirections.find(d) != BishopDirections.end())
- riderTypes |= limit == 1 ? RIDER_GRASSHOPPER_D : RIDER_CANNON_DIAG;
+ // We do not support initial captures
+ if (modality == MODALITY_CAPTURE && initial)
+ continue;
+ auto& riderTypes = modality == MODALITY_CAPTURE ? AttackRiderTypes[pt] : MoveRiderTypes[initial][pt];
+ riderTypes = NO_RIDER;
+ for (auto const& [d, limit] : pi->steps[initial][modality])
+ {
+ if (limit && LameDabbabaDirections.find(d) != LameDabbabaDirections.end())
+ riderTypes |= RIDER_LAME_DABBABA;
+ if (limit && HorseDirections.find(d) != HorseDirections.end())
+ riderTypes |= RIDER_HORSE;
+ if (limit && ElephantDirections.find(d) != ElephantDirections.end())
+ riderTypes |= RIDER_ELEPHANT;
+ if (limit && JanggiElephantDirections.find(d) != JanggiElephantDirections.end())
+ riderTypes |= RIDER_JANGGI_ELEPHANT;
+ }
+ for (auto const& [d, limit] : pi->slider[initial][modality])
+ {
+ if (BishopDirections.find(d) != BishopDirections.end())
+ riderTypes |= RIDER_BISHOP;
+ if (RookDirectionsH.find(d) != RookDirectionsH.end())
+ riderTypes |= RIDER_ROOK_H;
+ if (RookDirectionsV.find(d) != RookDirectionsV.end())
+ riderTypes |= RIDER_ROOK_V;
+ if (HorseDirections.find(d) != HorseDirections.end())
+ riderTypes |= RIDER_NIGHTRIDER;
+ }
+ for (auto const& [d, limit] : pi->hopper[initial][modality])
+ {
+ if (RookDirectionsH.find(d) != RookDirectionsH.end())
+ riderTypes |= limit == 1 ? RIDER_GRASSHOPPER_H : RIDER_CANNON_H;
+ if (RookDirectionsV.find(d) != RookDirectionsV.end())
+ riderTypes |= limit == 1 ? RIDER_GRASSHOPPER_V : RIDER_CANNON_V;
+ if (BishopDirections.find(d) != BishopDirections.end())
+ riderTypes |= limit == 1 ? RIDER_GRASSHOPPER_D : RIDER_CANNON_DIAG;
+ }
}
}
{
for (auto modality : {MODALITY_QUIET, MODALITY_CAPTURE})
{
- auto& pseudo = modality == MODALITY_CAPTURE ? PseudoAttacks[c][pt][s] : PseudoMoves[c][pt][s];
- auto& leaper = modality == MODALITY_CAPTURE ? LeaperAttacks[c][pt][s] : LeaperMoves[c][pt][s];
- pseudo = 0;
- leaper = 0;
- for (auto const& [d, limit] : pi->steps[modality])
+ for (bool initial : {false, true})
{
- pseudo |= safe_destination(s, c == WHITE ? d : -d);
- if (!limit)
- leaper |= safe_destination(s, c == WHITE ? d : -d);
+ // We do not support initial captures
+ if (modality == MODALITY_CAPTURE && initial)
+ continue;
+ auto& pseudo = modality == MODALITY_CAPTURE ? PseudoAttacks[c][pt][s] : PseudoMoves[initial][c][pt][s];
+ auto& leaper = modality == MODALITY_CAPTURE ? LeaperAttacks[c][pt][s] : LeaperMoves[initial][c][pt][s];
+ pseudo = 0;
+ leaper = 0;
+ for (auto const& [d, limit] : pi->steps[initial][modality])
+ {
+ pseudo |= safe_destination(s, c == WHITE ? d : -d);
+ if (!limit)
+ leaper |= safe_destination(s, c == WHITE ? d : -d);
+ }
+ pseudo |= sliding_attack<RIDER>(pi->slider[initial][modality], s, 0, c);
+ pseudo |= sliding_attack<UNLIMITED_RIDER>(pi->hopper[initial][modality], s, 0, c);
}
- pseudo |= sliding_attack<RIDER>(pi->slider[modality], s, 0, c);
- pseudo |= sliding_attack<UNLIMITED_RIDER>(pi->hopper[modality], s, 0, c);
}
}
}
init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections, BishopMagicInit);
init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH, CannonMagicHInit);
init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit);
+ init_magics<LAME_LEAPER>(LameDabbabaTable, LameDabbabaMagics, LameDabbabaDirections, LameDabbabaMagicInit);
init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections, HorseMagicInit);
init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit);
init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections, JanggiElephantMagicInit);
init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections);
init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH);
init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV);
+ init_magics<LAME_LEAPER>(LameDabbabaTable, LameDabbabaMagics, LameDabbabaDirections);
init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections);
init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections);
init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections);
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_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 PseudoMoves[2][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];
+extern Bitboard LeaperMoves[2][COLOR_NB][PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard BoardSizeBB[FILE_NB][RANK_NB];
extern RiderType AttackRiderTypes[PIECE_TYPE_NB];
-extern RiderType MoveRiderTypes[PIECE_TYPE_NB];
+extern RiderType MoveRiderTypes[2][PIECE_TYPE_NB];
#ifdef LARGEBOARDS
int popcount(Bitboard b); // required for 128 bit pext
extern Magic BishopMagics[SQUARE_NB];
extern Magic CannonMagicsH[SQUARE_NB];
extern Magic CannonMagicsV[SQUARE_NB];
+extern Magic LameDabbabaMagics[SQUARE_NB];
extern Magic HorseMagics[SQUARE_NB];
extern Magic ElephantMagics[SQUARE_NB];
extern Magic JanggiElephantMagics[SQUARE_NB];
: R == RIDER_ROOK_V ? RookMagicsV[s]
: R == RIDER_CANNON_H ? CannonMagicsH[s]
: R == RIDER_CANNON_V ? CannonMagicsV[s]
+ : R == RIDER_LAME_DABBABA ? LameDabbabaMagics[s]
: R == RIDER_HORSE ? HorseMagics[s]
: R == RIDER_ELEPHANT ? ElephantMagics[s]
: R == RIDER_JANGGI_ELEPHANT ? JanggiElephantMagics[s]
}
+template <bool Initial=false>
inline Bitboard moves_bb(Color c, PieceType pt, Square s, Bitboard occupied) {
- Bitboard b = LeaperMoves[c][pt][s];
- RiderType r = MoveRiderTypes[pt];
+ Bitboard b = LeaperMoves[Initial][c][pt][s];
+ RiderType r = MoveRiderTypes[Initial][pt];
while (r)
b |= rider_attacks_bb(pop_rider(&r), s, occupied);
- return b & PseudoMoves[c][pt][s];
+ return b & PseudoMoves[Initial][c][pt][s];
}
Color us = strongSide == pos.side_to_move() ? WHITE : BLACK;
// Non-standard promotion, evaluation unclear
- if ( pos.promotion_rank() != RANK_8
+ if ( pos.promotion_zone(us) != rank_bb(relative_rank(us, RANK_8, pos.max_rank()))
|| RANK_MAX != RANK_8
- || pos.promotion_piece_types().find(QUEEN) == pos.promotion_piece_types().end())
+ || pos.promotion_piece_types(us).find(QUEEN) == pos.promotion_piece_types(us).end())
{
Value result = PawnValueEg + Value(rank_of(strongPawn));
return strongSide == pos.side_to_move() ? result : -result;
Bitboard b = pos.pieces(strongSide, PAWN);
while (b && (!dark || !light))
{
- if (file_of(pop_lsb(b)) % 2 != relative_rank(strongSide, pos.promotion_rank(), pos.max_rank()) % 2)
- light = true;
- else
- dark = true;
+ Square s = pos.promotion_square(strongSide, pop_lsb(b));
+ if (s != SQ_NONE)
+ {
+ if (DarkSquares & s)
+ dark = true;
+ else
+ light = true;
+ }
}
if (!dark || !light)
return VALUE_DRAW; // we can not checkmate with same colored ferzes
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn.
- if ( pos.promotion_rank() != RANK_8
+ if ( pos.promotion_zone(us) != rank_bb(relative_rank(us, RANK_8, pos.max_rank()))
|| RANK_MAX != RANK_8
- || pos.promotion_piece_types().find(QUEEN) == pos.promotion_piece_types().end())
+ || pos.promotion_piece_types(us).find(QUEEN) == pos.promotion_piece_types(us).end())
return SCALE_FACTOR_NONE;
return Bitbases::probe(strongKing, strongPawn, weakKing, us) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
// Piece promotion bonus
if (pos.promoted_piece_type(Pt) != NO_PIECE_TYPE)
{
- Bitboard zone = zone_bb(Us, pos.promotion_rank(), pos.max_rank());
+ Bitboard zone = pos.promotion_zone(Us);
if (zone & (b | s))
score += make_score(PieceValue[MG][pos.promoted_piece_type(Pt)] - PieceValue[MG][Pt],
PieceValue[EG][pos.promoted_piece_type(Pt)] - PieceValue[EG][Pt]) / (zone & s && b ? 6 : 12);
if (pos.promoted_piece_type(pt))
{
otherChecks = attacks_bb(Us, pos.promoted_piece_type(pt), ksq, pos.pieces()) & attackedBy[Them][pt]
- & zone_bb(Them, pos.promotion_rank(), pos.max_rank()) & pos.board_bb();
+ & pos.promotion_zone(Them) & pos.board_bb();
if (otherChecks & safe)
kingDanger += SafeCheck[FAIRY_PIECES][more_than_one(otherChecks & safe)];
else
assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
- int r = std::max(RANK_8 - std::max(pos.promotion_rank() - relative_rank(Us, s, pos.max_rank()), 0), 0);
+ int r = std::max(RANK_8 - std::max(relative_rank(Us, pos.promotion_square(Us, s), pos.max_rank()) - relative_rank(Us, s, pos.max_rank()), 0), 0);
Score bonus = PassedRank[r];
// Scale by maximum promotion piece value
Value maxMg = VALUE_ZERO, maxEg = VALUE_ZERO;
- for (PieceType pt : pos.promotion_piece_types())
+ for (PieceType pt : pos.promotion_piece_types(Us))
{
maxMg = std::max(maxMg, PieceValue[MG][pt]);
maxEg = std::max(maxEg, PieceValue[EG][pt]);
while (b)
{
Square s = pop_lsb(b);
- if ((pos.pieces(Them, SHOGI_PAWN) & forward_file_bb(Us, s)) || relative_rank(Us, s, pos.max_rank()) == pos.max_rank())
+ if ((pos.pieces(Them, SHOGI_PAWN) & forward_file_bb(Us, s)) || pos.promotion_square(Us, s) == SQ_NONE)
continue;
Square blockSq = s + Up;
- int d = 2 * std::max(pos.promotion_rank() - relative_rank(Us, s, pos.max_rank()), 1);
+ int d = 2 * std::max(relative_rank(Us, pos.promotion_square(Us, s), pos.max_rank()) - relative_rank(Us, s, pos.max_rank()), 1);
d += !!(attackedBy[Them][ALL_PIECES] & ~attackedBy2[Us] & blockSq);
score += make_score(PieceValue[MG][pt], PieceValue[EG][pt]) / (d * d);
}
bool pawnsOnly = !(pos.pieces(Us) ^ pos.pieces(Us, PAWN));
// Early exit if, for example, both queens or 6 minor pieces have been exchanged
- if (pos.non_pawn_material() < SpaceThreshold && !pawnsOnly && pos.double_step_enabled())
+ if (pos.non_pawn_material() < SpaceThreshold && !pawnsOnly && pos.double_step_region(Us))
return SCORE_ZERO;
constexpr Color Them = ~Us;
{
Square s = pop_lsb(current);
Bitboard attacks = ( (PseudoAttacks[Us][ptCtf][s] & pos.pieces())
- | (PseudoMoves[Us][ptCtf][s] & ~pos.pieces())) & ~processed & pos.board_bb();
+ | (PseudoMoves[0][Us][ptCtf][s] & ~pos.pieces())) & ~processed & pos.board_bb();
ctfPieces |= attacks & ~blocked;
onHold |= attacks & ~doubleBlocked;
onHold2 |= attacks & ~inaccessible;
// Compute the initiative bonus for the attacking side
complexity = 9 * pe->passed_count()
- + 12 * pos.count<PAWN>()
+ + 12 * pos.count(WHITE, pos.promotion_pawn_type(WHITE)) * bool(pos.promotion_pawn_type(WHITE))
+ + 12 * pos.count(BLACK, pos.promotion_pawn_type(BLACK)) * bool(pos.promotion_pawn_type(BLACK))
+ 15 * pos.count<SOLDIER>()
+ 9 * outflanking
+ 21 * pawnsOnBothFlanks
B(0xA00002102810020, 0xB1080240015408),
B(0x810080200806, 0x410440804080046),
};
+ constexpr Bitboard LameDabbabaMagicInit[SQUARE_NB] = {
+ B(0x3C080482A592000C, 0x540104000020000),
+ B(0x2802C40008000420, 0x4A00000001818009),
+ B(0x1083040280804000, 0x120004C20100880),
+ B(0x6840940880000892, 0x2014A01080800C2),
+ B(0x8401489004000180, 0x2000800000400000),
+ B(0x820161C800000110, 0x8000100000204020),
+ B(0x610011A122000109, 0x1000004020008004),
+ B(0x83282004023000, 0xE000020004848446),
+ B(0x6840940880000892, 0x2014A01080800C2),
+ B(0x4020120800800002, 0x88008000010020),
+ B(0x30025B140A1000, 0x3141801401000040),
+ B(0x41104D1810100050, 0x8141002010910),
+ B(0x4200828A298400, 0x400340001040C000),
+ B(0x8016A4900110040, 0x844812001068020),
+ B(0x2250035820400A2, 0x8012010080900),
+ B(0x820080083A009000, 0x880404091080110),
+ B(0x80401500AF0020, 0x240000082201A04),
+ B(0x668020020C081005, 0x4008001004100021),
+ B(0x240100910000000, 0x82000A0030454000),
+ B(0xA24091400008, 0x200014880004A921),
+ B(0x840110042200410, 0x100080000A400000),
+ B(0x40024024102000, 0x1000000002180404),
+ B(0x92828423000530, 0x118800020110),
+ B(0x1122404A1C90A8, 0x822040280020D00),
+ B(0x41201A40900A000, 0x80C0480040605100),
+ B(0x2504A85005488280, 0x3028112120022800),
+ B(0x210180080626B048, 0x8000401000014000),
+ B(0x1000410401040200, 0x41014000050C0106),
+ B(0x1040650210802200, 0x80C0041000000),
+ B(0x4020C10110900002, 0x2140C2001050009),
+ B(0x191180092200022, 0x6010008400400800),
+ B(0x8010821088080202, 0xCA240011008208),
+ B(0x8C0488120024214, 0x8414880202291),
+ B(0x8C0488120024214, 0x8414880202291),
+ B(0x22080C8A0161401, 0x200C10004C002002),
+ B(0x8430818023034080, 0x210090800000801),
+ B(0x4845087008200, 0x40661480000),
+ B(0x1202804428812050, 0x100022038020000),
+ B(0x400016001201080, 0x24002200402060),
+ B(0x680E041300800800, 0xE00130080004000),
+ B(0x3409080200, 0x282840210000000),
+ B(0x803310108400, 0x85200000080100A0),
+ B(0xE180008A04162104, 0x9088240412404),
+ B(0x20080100920020, 0x2002248010242052),
+ B(0x8A000400C2410, 0x1000024086014300),
+ B(0x1821040024663, 0x100000100010009),
+ B(0x4000822310611, 0x120280406014008),
+ B(0x1004008010818D08, 0x800000141892000),
+ B(0x8010800004024042, 0x44B106008800896),
+ B(0xA0063423444, 0x41002C15811008),
+ B(0x2040012381001282, 0x4804080104A4000),
+ B(0x10840101820880, 0xA800008000020020),
+ B(0x10840101820880, 0xA800008000020020),
+ B(0x60201D8300408190, 0x2010020920200000),
+ B(0x4048100200090090, 0x2008090100000900),
+ B(0x24200000280210, 0xD440050008004000),
+ B(0x1280001000580020, 0x2200040089000A4),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x4108000010209089, 0x913000000024840),
+ B(0x410C208008008E02, 0xE8000000000001),
+ B(0x802208004005, 0x94206000022080),
+ B(0xC00290018902002, 0x4204100000000000),
+ B(0x2102801400093816, 0x9810004001000202),
+ B(0x8008304000015800, 0x4A5C000000020000),
+ B(0x1020108380800514, 0x1144210000000080),
+ B(0xC0001000008090, 0x2812060000204000),
+ B(0x1001100200003100, 0x246240060A004004),
+ B(0xA00020A008002030, 0x2440C40000110B00),
+ B(0x80502104000C008, 0x8222200042100010),
+ B(0xC020200088014, 0x422094000000480),
+ B(0x1029002000001030, 0x8105841120000210),
+ B(0x49040D, 0x2310808A14042C0),
+ B(0x200040200080A02C, 0xB890290400080000),
+ B(0x2240180C0800002, 0x4151050280000100),
+ B(0x2240180C0800002, 0x4151050280000100),
+ B(0x8220224180420006, 0x4024501212011000),
+ B(0x1806810A0881000, 0x802002048400080),
+ B(0x400400A080842, 0x9305000401180000),
+ B(0x10008001444110, 0x4420401040041833),
+ B(0x2000002C02010E00, 0x400408D08009804),
+ B(0x69D008200020100, 0x100842240049021),
+ B(0x42C24450020000, 0xD38400880090884),
+ B(0x485800800100001, 0x2484086522018840),
+ B(0x900200020820042, 0x22302421400040C0),
+ B(0x50B0413001818000, 0x452014040800C40),
+ B(0x8004040021008, 0x20088A08000290),
+ B(0x600C000801000004, 0x8015084010200020),
+ B(0x208000C00, 0xE004804021100100),
+ B(0x20001000040204, 0x948110C0B2081),
+ B(0x268502400100021, 0x80A201840802080),
+ B(0x408C000008, 0x8822102408014),
+ B(0x1182080410100000, 0x608002046A0100),
+ B(0x100820A083C00002, 0x3100100410A00),
+ B(0x8401040000400124, 0x2000081288202200),
+ B(0xB014040003000800, 0x11960D1101210),
+ B(0x10040001900C000, 0x85603C1001280),
+ B(0x2000844000000100, 0x2000024C60800800),
+ B(0x120004234800900, 0x210010841040),
+ B(0x8010300040000002, 0x4200008222104100),
+ B(0x1000120402200100, 0x209080CC040108B4),
+ B(0x110049A00000800, 0x80000420022180A8),
+ B(0x80001C00080384, 0x1400101111081001),
+ B(0x8011200008100428, 0x2020000880800922),
+ B(0x10001000000204C8, 0x280C11104240),
+ B(0x50100C82C000500, 0x28000280618DD1),
+ B(0x8800498020000, 0x20500A0200320128),
+ B(0x20010104000860, 0x8021720186008),
+ B(0x4000000000100080, 0x35040084270C04),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0x10400000000000, 0x20080051100130C2),
+ B(0x10400000000000, 0x20080051100130C2),
+ B(0x2000002110202014, 0x121004004004681),
+ B(0x400202001006D40, 0x82240082202424),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0xC6000000D00804, 0x1050020C0081090C),
+ B(0x200080000000042, 0x10800661),
+ B(0x2000001011200200, 0x2A420000802A0222),
+ B(0x802020001202412, 0x2400404148426),
+ B(0x8000440801040002, 0x444002800010052A),
+ };
constexpr Bitboard HorseMagicInit[SQUARE_NB] = {
B(0x3C080482A592000C, 0x540104000020000),
B(0x2802C40008000420, 0x4A00000001818009),
// Helper used to detect a given material distribution
bool is_KFsPsK(const Position& pos, Color us) {
- return pos.promotion_piece_types().size() == 1
- && pos.promotion_piece_types().find(FERS) != pos.promotion_piece_types().end()
+ return pos.promotion_piece_types(us).size() == 1
+ && pos.promotion_piece_types(us).find(FERS) != pos.promotion_piece_types(us).end()
&& !more_than_one(pos.pieces(~us))
&& (pos.count<FERS>(us) || pos.count<PAWN>(us))
&& !(pos.count<ALL_PIECES>(us) - pos.count<FERS>(us) - pos.count<PAWN>(us) - pos.count<KING>(us));
b ^= square_bb(to) ^ kto ^ rto;
}
if (T == EN_PASSANT)
- b ^= to - pawn_push(us);
+ b ^= pos.capture_square(to);
if (pos.variant()->arrowGating)
b &= moves_bb(us, type_of(pos.piece_on(from)), to, pos.pieces() ^ from);
if (pos.variant()->staticGating)
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
- for (PieceType pt : pos.promotion_piece_types())
+ for (PieceType pt : pos.promotion_piece_types(c))
if (!pos.promotion_limit(pt) || pos.promotion_limit(pt) > pos.count(c, pt))
moveList = make_move_and_gating<PROMOTION>(pos, moveList, pos.side_to_move(), to - D, to, pt);
PieceType pt = pos.promoted_piece_type(PAWN);
template<Color Us, GenType Type>
ExtMove* generate_pawn_moves(const Position& pos, ExtMove* moveList, Bitboard target) {
+ if (!pos.pieces(Us, PAWN))
+ return moveList;
+
constexpr Color Them = ~Us;
constexpr Direction Up = pawn_push(Us);
- constexpr Direction Down = -pawn_push(Us);
constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
- Bitboard TRank8BB = pos.sittuyin_promotion() ? Bitboard(0) : zone_bb(Us, pos.promotion_rank(), pos.max_rank());
- Bitboard TRank7BB = shift<Down>(TRank8BB);
- // Define squares a pawn can pass during a double step
- Bitboard TRank3BB = forward_ranks_bb(Us, relative_rank(Us, pos.double_step_rank_min(), pos.max_rank()))
- & ~shift<Up>(forward_ranks_bb(Us, relative_rank(Us, pos.double_step_rank_max(), pos.max_rank())));
+ const Bitboard promotionZone = pos.promotion_zone(Us);
+ const Bitboard standardPromotionZone = pos.sittuyin_promotion() ? Bitboard(0) : promotionZone;
+ const Bitboard doubleStepRegion = pos.double_step_region(Us);
+ const Bitboard tripleStepRegion = pos.triple_step_region(Us);
- const Bitboard emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces()) & pos.board_bb(Us, PAWN);
- const Bitboard enemies = (Type == EVASIONS ? (pos.checkers() & pos.non_sliding_riders() ? pos.pieces(Them) : pos.checkers())
- : Type == CAPTURES ? target : pos.pieces(Them)) & pos.board_bb(Us, PAWN);
+ const Bitboard pawns = pos.pieces(Us, PAWN);
+ const Bitboard movable = pos.board_bb(Us, PAWN) & ~pos.pieces();
+ const Bitboard capturable = pos.board_bb(Us, PAWN) & pos.pieces(Them);
- Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
- Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & (pos.mandatory_pawn_promotion() ? ~TRank7BB : AllSquares);
+ target = Type == EVASIONS ? target : AllSquares;
- // Single and double pawn pushes, no promotions
- if (Type != CAPTURES)
- {
- Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
- Bitboard b2 = pos.double_step_enabled() ? shift<Up>(b1 & TRank3BB) & emptySquares : Bitboard(0);
+ // Define single and double push, left and right capture, as well as respective promotion moves
+ Bitboard b1 = shift<Up>(pawns) & movable & target;
+ Bitboard b2 = shift<Up>(shift<Up>(pawns & doubleStepRegion) & movable) & movable & target;
+ Bitboard b3 = shift<Up>(shift<Up>(shift<Up>(pawns & tripleStepRegion) & movable) & movable) & movable & target;
+ Bitboard brc = shift<UpRight>(pawns) & capturable & target;
+ Bitboard blc = shift<UpLeft >(pawns) & capturable & target;
- if (Type == EVASIONS) // Consider only blocking squares
- {
- b1 &= target;
- b2 &= target;
- }
+ Bitboard b1p = b1 & standardPromotionZone;
+ Bitboard b2p = b2 & standardPromotionZone;
+ Bitboard b3p = b3 & standardPromotionZone;
+ Bitboard brcp = brc & standardPromotionZone;
+ Bitboard blcp = blc & standardPromotionZone;
- if (Type == QUIET_CHECKS && pos.count<KING>(Them))
- {
- // To make a quiet check, you either make a direct check by pushing a pawn
- // or push a blocker pawn that is not on the same file as the enemy king.
- // Discovered check promotion has been already generated amongst the captures.
- Square ksq = pos.square<KING>(Them);
- Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq);
- b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns);
- b2 &= pawn_attacks_bb(Them, ksq) | shift<Up+Up>(dcCandidatePawns);
- }
+ // Restrict regions based on rules and move generation type
+ if (pos.mandatory_pawn_promotion())
+ {
+ b1 &= ~standardPromotionZone;
+ b2 &= ~standardPromotionZone;
+ b3 &= ~standardPromotionZone;
+ brc &= ~standardPromotionZone;
+ blc &= ~standardPromotionZone;
+ }
+ if (Type == QUIET_CHECKS && pos.count<KING>(Them))
+ {
+ // To make a quiet check, you either make a direct check by pushing a pawn
+ // or push a blocker pawn that is not on the same file as the enemy king.
+ // Discovered check promotion has been already generated amongst the captures.
+ Square ksq = pos.square<KING>(Them);
+ Bitboard dcCandidatePawns = pos.blockers_for_king(Them) & ~file_bb(ksq);
+ b1 &= pawn_attacks_bb(Them, ksq) | shift< Up>(dcCandidatePawns);
+ b2 &= pawn_attacks_bb(Them, ksq) | shift<Up+Up>(dcCandidatePawns);
+ }
+
+ // Single and double pawn pushes, no promotions
+ if (Type != CAPTURES)
+ {
while (b1)
{
Square to = pop_lsb(b1);
Square to = pop_lsb(b2);
moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, to - Up - Up, to);
}
+
+ while (b3)
+ {
+ Square to = pop_lsb(b3);
+ moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, to - Up - Up - Up, to);
+ }
}
// Promotions and underpromotions
- if (pawnsOn7)
- {
- Bitboard b1 = shift<UpRight>(pawnsOn7) & enemies;
- Bitboard b2 = shift<UpLeft >(pawnsOn7) & enemies;
- Bitboard b3 = shift<Up >(pawnsOn7) & emptySquares;
+ while (brcp)
+ moveList = make_promotions<Us, Type, UpRight>(pos, moveList, pop_lsb(brcp));
- if (Type == EVASIONS)
- b3 &= target;
+ while (blcp)
+ moveList = make_promotions<Us, Type, UpLeft >(pos, moveList, pop_lsb(blcp));
- while (b1)
- moveList = make_promotions<Us, Type, UpRight>(pos, moveList, pop_lsb(b1));
+ while (b1p)
+ moveList = make_promotions<Us, Type, Up >(pos, moveList, pop_lsb(b1p));
- while (b2)
- moveList = make_promotions<Us, Type, UpLeft >(pos, moveList, pop_lsb(b2));
+ while (b2p)
+ moveList = make_promotions<Us, Type, Up+Up >(pos, moveList, pop_lsb(b2p));
- while (b3)
- moveList = make_promotions<Us, Type, Up >(pos, moveList, pop_lsb(b3));
- }
+ while (b3p)
+ moveList = make_promotions<Us, Type, Up+Up+Up>(pos, moveList, pop_lsb(b3p));
// Sittuyin promotions
if (pos.sittuyin_promotion() && (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS))
{
- Bitboard pawns = pos.pieces(Us, PAWN);
- // Pawns need to be on diagonals on opponent's half if there is more than one pawn
- if (pos.count<PAWN>(Us) > 1)
- pawns &= ( PseudoAttacks[Us][BISHOP][make_square(FILE_A, relative_rank(Us, RANK_1, pos.max_rank()))]
- | PseudoAttacks[Us][BISHOP][make_square(pos.max_file(), relative_rank(Us, RANK_1, pos.max_rank()))])
- & forward_ranks_bb(Us, relative_rank(Us, Rank((pos.max_rank() - 1) / 2), pos.max_rank()));
- while (pawns)
+ // Pawns need to be in promotion zone if there is more than one pawn
+ Bitboard promotionPawns = pos.count<PAWN>(Us) > 1 ? pawns & promotionZone : pawns;
+ while (promotionPawns)
{
- Square from = pop_lsb(pawns);
- for (PieceType pt : pos.promotion_piece_types())
+ Square from = pop_lsb(promotionPawns);
+ for (PieceType pt : pos.promotion_piece_types(Us))
{
if (pos.promotion_limit(pt) && pos.promotion_limit(pt) <= pos.count(Us, pt))
continue;
- Bitboard b = (pos.attacks_from(Us, pt, from) & ~pos.pieces()) | from;
- if (Type == EVASIONS)
- b &= target;
-
+ Bitboard b = ((pos.attacks_from(Us, pt, from) & ~pos.pieces()) | from) & target;
while (b)
{
Square to = pop_lsb(b);
// Standard and en passant captures
if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
{
- Bitboard b1 = shift<UpRight>(pawnsNotOn7) & enemies;
- Bitboard b2 = shift<UpLeft >(pawnsNotOn7) & enemies;
-
- while (b1)
+ while (brc)
{
- Square to = pop_lsb(b1);
+ Square to = pop_lsb(brc);
moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, to - UpRight, to);
}
- while (b2)
+ while (blc)
{
- Square to = pop_lsb(b2);
+ Square to = pop_lsb(blc);
moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, to - UpLeft, to);
}
- if (pos.ep_square() != SQ_NONE)
+ for (Bitboard epSquares = pos.ep_squares() & ~pos.pieces(); epSquares; )
{
- assert(relative_rank(Them, rank_of(pos.ep_square()), pos.max_rank()) <= Rank(pos.double_step_rank_max() + 1));
+ Square epSquare = pop_lsb(epSquares);
- // An en passant capture cannot resolve a discovered check
- if (Type == EVASIONS && (target & (pos.ep_square() + Up)))
+ // An en passant capture cannot resolve a discovered check (unless there non-sliding riders)
+ if (Type == EVASIONS && (target & (epSquare + Up)) && !pos.non_sliding_riders())
return moveList;
- b1 = pawnsNotOn7 & pawn_attacks_bb(Them, pos.ep_square());
+ Bitboard b = pawns & pawn_attacks_bb(Them, epSquare);
- assert(b1);
+ assert(b);
- while (b1)
- moveList = make_move_and_gating<EN_PASSANT>(pos, moveList, Us, pop_lsb(b1), pos.ep_square());
+ while (b)
+ moveList = make_move_and_gating<EN_PASSANT>(pos, moveList, Us, pop_lsb(b), epSquare);
}
}
}
- template<Color Us, bool Checks>
+ template<Color Us, GenType Type>
ExtMove* generate_moves(const Position& pos, ExtMove* moveList, PieceType Pt, Bitboard target) {
assert(Pt != KING && Pt != PAWN);
{
Square from = pop_lsb(bb);
- Bitboard b1 = ( (pos.attacks_from(Us, Pt, from) & pos.pieces())
- | (pos.moves_from(Us, Pt, from) & ~pos.pieces())) & target;
+ Bitboard attacks = pos.attacks_from(Us, Pt, from);
+ Bitboard quiets = pos.moves_from(Us, Pt, from);
+ Bitboard b = ( (attacks & pos.pieces())
+ | (quiets & ~pos.pieces()));
+ Bitboard b1 = b & target;
+ Bitboard promotion_zone = pos.promotion_zone(Us);
PieceType promPt = pos.promoted_piece_type(Pt);
Bitboard b2 = promPt && (!pos.promotion_limit(promPt) || pos.promotion_limit(promPt) > pos.count(Us, promPt)) ? b1 : Bitboard(0);
Bitboard b3 = pos.piece_demotion() && pos.is_promoted(from) ? b1 : Bitboard(0);
+ Bitboard pawnPromotions = pos.variant()->promotionPawnTypes[Us] & Pt ? b & (Type == EVASIONS ? target : ~pos.pieces(Us)) & promotion_zone : Bitboard(0);
+ Bitboard epSquares = pos.variant()->enPassantTypes[Us] & Pt ? attacks & ~quiets & pos.ep_squares() & ~pos.pieces() : Bitboard(0);
+
+ // target squares considering pawn promotions
+ if (pawnPromotions && pos.mandatory_pawn_promotion())
+ b1 &= ~pawnPromotions;
// Restrict target squares considering promotion zone
if (b2 | b3)
{
- Bitboard promotion_zone = zone_bb(Us, pos.promotion_rank(), pos.max_rank());
if (pos.mandatory_piece_promotion())
b1 &= (promotion_zone & from ? Bitboard(0) : ~promotion_zone) | (pos.piece_promotion_on_capture() ? ~pos.pieces() : Bitboard(0));
// Exclude quiet promotions/demotions
}
}
- if (Checks)
+ if (Type == QUIET_CHECKS)
{
b1 &= pos.check_squares(Pt);
if (b2)
// Piece demotions
while (b3)
*moveList++ = make<PIECE_DEMOTION>(from, pop_lsb(b3));
+
+ // Pawn-style promotions
+ if ((Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS) && pawnPromotions)
+ for (PieceType ptP : pos.promotion_piece_types(Us))
+ if (!pos.promotion_limit(ptP) || pos.promotion_limit(ptP) > pos.count(Us, ptP))
+ for (Bitboard promotions = pawnPromotions; promotions; )
+ moveList = make_move_and_gating<PROMOTION>(pos, moveList, pos.side_to_move(), from, pop_lsb(promotions), ptP);
+
+ // En passant captures
+ if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
+ while (epSquares)
+ moveList = make_move_and_gating<EN_PASSANT>(pos, moveList, Us, from, pop_lsb(epSquares));
}
return moveList;
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
for (PieceType pt : pos.piece_types())
if (pt != PAWN && pt != KING)
- moveList = generate_moves<Us, Checks>(pos, moveList, pt, target);
+ moveList = generate_moves<Us, Type>(pos, moveList, pt, target);
// generate drops
if (pos.piece_drops() && Type != CAPTURES && (pos.can_drop(Us, ALL_PIECES) || pos.two_boards()))
for (PieceType pt : pos.piece_types())
return !ss.fail();
}
+ template <typename T> void set(PieceType pt, T& target) {
+ target.insert(pt);
+ }
+
+ template <> void set(PieceType pt, PieceType& target) {
+ target = pt;
+ }
+
+ template <> void set(PieceType pt, PieceSet& target) {
+ target |= pt;
+ }
+
} // namespace
template <bool DoCheck>
-template <class T> void VariantParser<DoCheck>::parse_attribute(const std::string& key, T& target) {
+template <bool Current, class T> bool VariantParser<DoCheck>::parse_attribute(const std::string& key, T& target) {
const auto& it = config.find(key);
if (it != config.end())
{
bool valid = set(it->second, target);
+ if (DoCheck && !Current)
+ std::cerr << key << " - Deprecated option might be removed in future version." << std::endl;
if (DoCheck && !valid)
{
std::string typeName = std::is_same<T, int>() ? "int"
: typeid(T).name();
std::cerr << key << " - Invalid value " << it->second << " for type " << typeName << std::endl;
}
+ return valid;
}
+ return false;
}
template <bool DoCheck>
-void VariantParser<DoCheck>::parse_attribute(const std::string& key, PieceType& target, std::string pieceToChar) {
+template <bool Current, class T> bool VariantParser<DoCheck>::parse_attribute(const std::string& key, T& target, std::string pieceToChar) {
const auto& it = config.find(key);
if (it != config.end())
{
+ target = T();
char token;
size_t idx;
std::stringstream ss(it->second);
- if (ss >> token && (idx = token == '-' ? 0 : pieceToChar.find(toupper(token))) != std::string::npos)
- target = PieceType(idx);
- else if (DoCheck)
+ while (ss >> token && (idx = pieceToChar.find(toupper(token))) != std::string::npos)
+ set(PieceType(idx), target);
+ if (DoCheck && idx == std::string::npos && token != '-')
std::cerr << key << " - Invalid piece type: " << token << std::endl;
+ return idx != std::string::npos || token == '-';
}
+ return false;
}
template <bool DoCheck>
Variant* VariantParser<DoCheck>::parse() {
Variant* v = new Variant();
v->reset_pieces();
- v->promotionPieceTypes = {};
return parse(v);
}
template <bool DoCheck>
Variant* VariantParser<DoCheck>::parse(Variant* v) {
+ parse_attribute("maxRank", v->maxRank);
+ parse_attribute("maxFile", v->maxFile);
// piece types
for (PieceType pt = PAWN; pt <= KING; ++pt)
{
if (is_custom(pt))
{
if (keyValue->second.size() > 1)
+ {
v->customPiece[pt - CUSTOM_PIECES] = keyValue->second.substr(2);
+ // Is there an en passant flag in the Betza notation?
+ if (v->customPiece[pt - CUSTOM_PIECES].find('e') != std::string::npos)
+ {
+ v->enPassantTypes[WHITE] |= piece_set(pt);
+ v->enPassantTypes[BLACK] |= piece_set(pt);
+ }
+ }
else if (DoCheck)
std::cerr << name << " - Missing Betza move notation" << std::endl;
}
std::cerr << optionName << " - Invalid piece value for type: " << v->pieceToChar[idx] << std::endl;
}
}
+
+ // Parse deprecate values for backwards compatibility
+ Rank promotionRank = RANK_8;
+ if (parse_attribute<false>("promotionRank", promotionRank))
+ {
+ for (Color c : {WHITE, BLACK})
+ v->promotionRegion[c] = zone_bb(c, promotionRank, v->maxRank);
+ }
+ Rank doubleStepRank = RANK_2;
+ Rank doubleStepRankMin = RANK_2;
+ if ( parse_attribute<false>("doubleStepRank", doubleStepRank)
+ || parse_attribute<false>("doubleStepRankMin", doubleStepRankMin))
+ {
+ for (Color c : {WHITE, BLACK})
+ v->doubleStepRegion[c] = zone_bb(c, doubleStepRankMin, v->maxRank)
+ & ~forward_ranks_bb(c, relative_rank(c, doubleStepRank, v->maxRank));
+ }
+ parse_attribute<false>("whiteFlag", v->flagRegion[WHITE]);
+ parse_attribute<false>("blackFlag", v->flagRegion[BLACK]);
+
+ // Parse aliases
+ parse_attribute("pawnTypes", v->promotionPawnType[WHITE], v->pieceToChar);
+ parse_attribute("pawnTypes", v->promotionPawnType[BLACK], v->pieceToChar);
+ parse_attribute("pawnTypes", v->promotionPawnTypes[WHITE], v->pieceToChar);
+ parse_attribute("pawnTypes", v->promotionPawnTypes[BLACK], v->pieceToChar);
+ parse_attribute("pawnTypes", v->enPassantTypes[WHITE], v->pieceToChar);
+ parse_attribute("pawnTypes", v->enPassantTypes[BLACK], v->pieceToChar);
+ parse_attribute("pawnTypes", v->nMoveRuleTypes[WHITE], v->pieceToChar);
+ parse_attribute("pawnTypes", v->nMoveRuleTypes[BLACK], v->pieceToChar);
+
+ // Parse the official config options
parse_attribute("variantTemplate", v->variantTemplate);
parse_attribute("pieceToCharTable", v->pieceToCharTable);
parse_attribute("pocketSize", v->pocketSize);
- parse_attribute("maxRank", v->maxRank);
- parse_attribute("maxFile", v->maxFile);
parse_attribute("chess960", v->chess960);
parse_attribute("twoBoards", v->twoBoards);
parse_attribute("startFen", v->startFen);
- parse_attribute("promotionRank", v->promotionRank);
- // promotion piece types
- const auto& it_prom = config.find("promotionPieceTypes");
- if (it_prom != config.end())
- {
- v->promotionPieceTypes = {};
- char token;
- size_t idx = 0;
- std::stringstream ss(it_prom->second);
- while (ss >> token && ((idx = v->pieceToChar.find(toupper(token))) != std::string::npos))
- v->promotionPieceTypes.insert(PieceType(idx));
- if (DoCheck && idx == std::string::npos && token != '-')
- std::cerr << "promotionPieceTypes - Invalid piece type: " << token << std::endl;
- }
+ parse_attribute("promotionRegionWhite", v->promotionRegion[WHITE]);
+ parse_attribute("promotionRegionBlack", v->promotionRegion[BLACK]);
+ // Take the first promotionPawnTypes as the main promotionPawnType
+ parse_attribute("promotionPawnTypes", v->promotionPawnType[WHITE], v->pieceToChar);
+ parse_attribute("promotionPawnTypes", v->promotionPawnType[BLACK], v->pieceToChar);
+ parse_attribute("promotionPawnTypes", v->promotionPawnTypes[WHITE], v->pieceToChar);
+ parse_attribute("promotionPawnTypes", v->promotionPawnTypes[BLACK], v->pieceToChar);
+ parse_attribute("promotionPawnTypesWhite", v->promotionPawnType[WHITE], v->pieceToChar);
+ parse_attribute("promotionPawnTypesBlack", v->promotionPawnType[BLACK], v->pieceToChar);
+ parse_attribute("promotionPawnTypesWhite", v->promotionPawnTypes[WHITE], v->pieceToChar);
+ parse_attribute("promotionPawnTypesBlack", v->promotionPawnTypes[BLACK], v->pieceToChar);
+ parse_attribute("promotionPieceTypes", v->promotionPieceTypes[WHITE], v->pieceToChar);
+ parse_attribute("promotionPieceTypes", v->promotionPieceTypes[BLACK], v->pieceToChar);
+ parse_attribute("promotionPieceTypesWhite", v->promotionPieceTypes[WHITE], v->pieceToChar);
+ parse_attribute("promotionPieceTypesBlack", v->promotionPieceTypes[BLACK], v->pieceToChar);
parse_attribute("sittuyinPromotion", v->sittuyinPromotion);
// promotion limit
const auto& it_prom_limit = config.find("promotionLimit");
parse_attribute("blastOnCapture", v->blastOnCapture);
parse_attribute("petrifyOnCapture", v->petrifyOnCapture);
parse_attribute("doubleStep", v->doubleStep);
- parse_attribute("doubleStepRank", v->doubleStepRank);
- parse_attribute("doubleStepRankMin", v->doubleStepRankMin);
+ parse_attribute("doubleStepRegionWhite", v->doubleStepRegion[WHITE]);
+ parse_attribute("doubleStepRegionBlack", v->doubleStepRegion[BLACK]);
+ parse_attribute("tripleStepRegionWhite", v->tripleStepRegion[WHITE]);
+ parse_attribute("tripleStepRegionBlack", v->tripleStepRegion[BLACK]);
parse_attribute("enPassantRegion", v->enPassantRegion);
+ parse_attribute("enPassantTypes", v->enPassantTypes[WHITE], v->pieceToChar);
+ parse_attribute("enPassantTypes", v->enPassantTypes[BLACK], v->pieceToChar);
+ parse_attribute("enPassantTypesWhite", v->enPassantTypes[WHITE], v->pieceToChar);
+ parse_attribute("enPassantTypesBlack", v->enPassantTypes[BLACK], v->pieceToChar);
parse_attribute("castling", v->castling);
parse_attribute("castlingDroppedPiece", v->castlingDroppedPiece);
parse_attribute("castlingKingsideFile", v->castlingKingsideFile);
parse_attribute("soldierPromotionRank", v->soldierPromotionRank);
parse_attribute("flipEnclosedPieces", v->flipEnclosedPieces);
// game end
+ parse_attribute("nMoveRuleTypes", v->nMoveRuleTypes[WHITE], v->pieceToChar);
+ parse_attribute("nMoveRuleTypes", v->nMoveRuleTypes[BLACK], v->pieceToChar);
+ parse_attribute("nMoveRuleTypesWhite", v->nMoveRuleTypes[WHITE], v->pieceToChar);
+ parse_attribute("nMoveRuleTypesBlack", v->nMoveRuleTypes[BLACK], v->pieceToChar);
parse_attribute("nMoveRule", v->nMoveRule);
parse_attribute("nFoldRule", v->nFoldRule);
parse_attribute("nFoldValue", v->nFoldValue);
parse_attribute("extinctionValue", v->extinctionValue);
parse_attribute("extinctionClaim", v->extinctionClaim);
parse_attribute("extinctionPseudoRoyal", v->extinctionPseudoRoyal);
+ parse_attribute("dupleCheck", v->dupleCheck);
// extinction piece types
const auto& it_ext = config.find("extinctionPieceTypes");
if (it_ext != config.end())
parse_attribute("extinctionPieceCount", v->extinctionPieceCount);
parse_attribute("extinctionOpponentPieceCount", v->extinctionOpponentPieceCount);
parse_attribute("flagPiece", v->flagPiece, v->pieceToChar);
- parse_attribute("whiteFlag", v->whiteFlag);
- parse_attribute("blackFlag", v->blackFlag);
+ parse_attribute("flagRegionWhite", v->flagRegion[WHITE]);
+ parse_attribute("flagRegionBlack", v->flagRegion[BLACK]);
parse_attribute("flagMove", v->flagMove);
parse_attribute("checkCounting", v->checkCounting);
parse_attribute("connectN", v->connectN);
// Contradictory options
if (!v->checking && v->checkCounting)
std::cerr << "checkCounting=true requires checking=true." << std::endl;
- if (v->doubleStep && v->doubleStepRankMin > v->doubleStepRank)
- std::cerr << "Inconsistent settings: doubleStepRankMin > doubleStepRank." << std::endl;
if (v->castling && v->castlingRank > v->maxRank)
std::cerr << "Inconsistent settings: castlingRank > maxRank." << std::endl;
if (v->castling && v->castlingQueensideFile > v->castlingKingsideFile)
if (!is_custom(v->kingType))
{
const PieceInfo* pi = pieceMap.find(v->kingType)->second;
- if ( pi->hopper[MODALITY_QUIET].size()
- || pi->hopper[MODALITY_CAPTURE].size()
- || std::any_of(pi->steps[MODALITY_CAPTURE].begin(),
- pi->steps[MODALITY_CAPTURE].end(),
- [](const std::pair<const Direction, int>& d) { return d.second; }))
+ if ( pi->hopper[0][MODALITY_QUIET].size()
+ || pi->hopper[0][MODALITY_CAPTURE].size()
+ || std::any_of(pi->steps[0][MODALITY_CAPTURE].begin(),
+ pi->steps[0][MODALITY_CAPTURE].end(),
+ [](const std::pair<const Direction, int>& d) { return d.second; }))
std::cerr << piece_name(v->kingType) << " is not supported as kingType." << std::endl;
}
}
private:
Config config;
- template <class T> void parse_attribute(const std::string& key, T& target);
- void parse_attribute(const std::string& key, PieceType& target, std::string pieceToChar);
+ template <bool Current = true, class T> bool parse_attribute(const std::string& key, T& target);
+ template <bool Current = true, class T> bool parse_attribute(const std::string& key, T& target, std::string pieceToChar);
};
} // namespace Stockfish
// Passed pawns will be properly scored later in evaluation when we have
// full attack info.
- if (passed && is_ok(s + Up) && !pos.sittuyin_promotion())
+ if (passed && pos.promotion_square(Us, s) != SQ_NONE && !pos.sittuyin_promotion())
e->passedPawns[Us] |= s;
// Score this pawn
namespace {
- std::map<char, std::vector<std::pair<int, int>>> leaperAtoms = {
+ const std::map<char, std::vector<std::pair<int, int>>> leaperAtoms = {
{'W', {std::make_pair(1, 0)}},
{'F', {std::make_pair(1, 1)}},
{'D', {std::make_pair(2, 0)}},
{'G', {std::make_pair(3, 3)}},
{'K', {std::make_pair(1, 0), std::make_pair(1, 1)}},
};
- std::map<char, std::vector<std::pair<int, int>>> riderAtoms = {
+ const std::map<char, std::vector<std::pair<int, int>>> riderAtoms = {
{'R', {std::make_pair(1, 0)}},
{'B', {std::make_pair(1, 1)}},
{'Q', {std::make_pair(1, 0), std::make_pair(1, 1)}},
bool hopper = false;
bool rider = false;
bool lame = false;
+ bool initial = false;
int distance = 0;
std::vector<std::string> prelimDirections = {};
for (std::string::size_type i = 0; i < betza.size(); i++)
// Lame leaper
else if (c == 'n')
lame = true;
+ // Initial move
+ else if (c == 'i')
+ initial = true;
// Directional modifiers
else if (verticals.find(c) != std::string::npos || horizontals.find(c) != std::string::npos)
{
// Add moves
for (auto modality : moveModalities)
{
- auto& v = hopper ? p->hopper[modality]
- : rider ? p->slider[modality]
- : p->steps[modality];
+ auto& v = hopper ? p->hopper[initial][modality]
+ : rider ? p->slider[initial][modality]
+ : p->steps[initial][modality];
auto has_dir = [&](std::string s) {
return std::find(directions.begin(), directions.end(), s) != directions.end();
};
prelimDirections.clear();
hopper = false;
rider = false;
+ lame = false;
+ initial = false;
+ distance = 0;
}
}
return p;
struct PieceInfo {
std::string name = "";
std::string betza = "";
- std::map<Direction, int> steps[MOVE_MODALITY_NB] = {};
- std::map<Direction, int> slider[MOVE_MODALITY_NB] = {};
- std::map<Direction, int> hopper[MOVE_MODALITY_NB] = {};
+ std::map<Direction, int> steps[2][MOVE_MODALITY_NB] = {};
+ std::map<Direction, int> slider[2][MOVE_MODALITY_NB] = {};
+ std::map<Direction, int> hopper[2][MOVE_MODALITY_NB] = {};
};
struct PieceMap : public std::map<PieceType, const PieceInfo*> {
ss >> token;
// 3-4. Skip parsing castling and en passant flags if not present
- st->epSquare = SQ_NONE;
+ st->epSquares = 0;
st->castlingKingSquare[WHITE] = st->castlingKingSquare[BLACK] = SQ_NONE;
if (!isdigit(ss.peek()) && !sfen)
{
// 4. En passant square.
// Ignore if square is invalid or not on side to move relative rank 6.
- else if ( ((ss >> col) && (col >= 'a' && col <= 'a' + max_file()))
- && ((ss >> row) && (row >= '1' && row <= '1' + max_rank())))
- {
- st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
+ else
+ while ( ((ss >> col) && (col >= 'a' && col <= 'a' + max_file()))
+ && ((ss >> row) && (row >= '1' && row <= '1' + max_rank())))
+ {
+ Square epSquare = make_square(File(col - 'a'), Rank(row - '1'));
#ifdef LARGEBOARDS
- // Consider different rank numbering in CECP
- if (max_rank() == RANK_10 && CurrentProtocol == XBOARD)
- st->epSquare += NORTH;
+ // Consider different rank numbering in CECP
+ if (max_rank() == RANK_10 && CurrentProtocol == XBOARD)
+ epSquare += NORTH;
#endif
- // En passant square will be considered only if
- // a) side to move have a pawn threatening epSquare
- // b) there is an enemy pawn in front of epSquare
- // c) there is no piece on epSquare or behind epSquare
- bool enpassant;
- enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
- && (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
- && !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
- if (!enpassant)
- st->epSquare = SQ_NONE;
- }
+ // En passant square will be considered only if
+ // epSquare is within enPassantRegion and
+ // 1) variant has non-standard rules
+ // or
+ // 2)
+ // a) side to move have a pawn threatening epSquare
+ // b) there is an enemy pawn one or two (for triple steps) squares in front of epSquare
+ // c) there is no piece on epSquare or behind epSquare
+ if ( (var->enPassantRegion & epSquare)
+ && ( !var->fastAttacks
+ || ( pawn_attacks_bb(~sideToMove, epSquare) & pieces(sideToMove, PAWN)
+ && ( (pieces(~sideToMove, PAWN) & (epSquare + pawn_push(~sideToMove)))
+ || (pieces(~sideToMove, PAWN) & (epSquare + 2 * pawn_push(~sideToMove))))
+ && !(pieces() & (epSquare | (epSquare + pawn_push(sideToMove)))))))
+ st->epSquares |= epSquare;
+ }
}
// Check counter for nCheck
si->legalCapture = NO_VALUE;
if (var->extinctionPseudoRoyal)
{
+ si->pseudoRoyalCandidates = 0;
si->pseudoRoyals = 0;
for (PieceType pt : extinction_piece_types())
{
+ si->pseudoRoyalCandidates |= pieces(pt);
if (count(sideToMove, pt) <= var->extinctionPieceCount + 1)
si->pseudoRoyals |= pieces(sideToMove, pt);
if (count(~sideToMove, pt) <= var->extinctionPieceCount + 1)
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
}
- if (si->epSquare != SQ_NONE)
- si->key ^= Zobrist::enpassant[file_of(si->epSquare)];
+ for (Bitboard b = si->epSquares; b; )
+ si->key ^= Zobrist::enpassant[file_of(pop_lsb(b))];
if (sideToMove == BLACK)
si->key ^= Zobrist::side;
// Counting limit or ep-square
if (st->countingLimit)
ss << " " << counting_limit(countStarted) << " ";
+ else if (!ep_squares())
+ ss << " - ";
else
- ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(*this, ep_square()) + " ");
+ {
+ ss << " ";
+ for (Bitboard b = ep_squares(); b; )
+ ss << UCI::square(*this, pop_lsb(b));
+ ss << " ";
+ }
// Check count
if (check_counting())
}
// No legal moves from target square
- if (immobility_illegal() && (type_of(m) == DROP || type_of(m) == NORMAL) && !(PseudoMoves[us][type_of(moved_piece(m))][to] & board_bb()))
+ if (immobility_illegal() && (type_of(m) == DROP || type_of(m) == NORMAL) && !(PseudoMoves[0][us][type_of(moved_piece(m))][to] & board_bb()))
return false;
// Illegal king passing move
occupied = (pieces() ^ from ^ to) | kto | rto;
}
if (type_of(m) == EN_PASSANT)
- occupied &= ~square_bb(kto - pawn_push(us));
+ occupied &= ~square_bb(capture_square(kto));
if (capture(m) && blast_on_capture())
occupied &= ~((attacks_bb<KING>(kto) & ((pieces(WHITE) | pieces(BLACK)) ^ pieces(PAWN))) | kto);
Bitboard pseudoRoyals = st->pseudoRoyals & pieces(sideToMove);
if (is_ok(from) && (pseudoRoyals & from))
pseudoRoyals ^= square_bb(from) ^ kto;
if (type_of(m) == PROMOTION && extinction_piece_types().find(promotion_type(m)) != extinction_piece_types().end())
- pseudoRoyals |= kto;
+ {
+ if (count(sideToMove, promotion_type(m)) > extinction_piece_count())
+ // increase in count leads to loss of pseudo-royalty
+ pseudoRoyals &= ~pieces(sideToMove, promotion_type(m));
+ else
+ // promoted piece is pseudo-royal
+ pseudoRoyals |= kto;
+ }
// Self-explosions are illegal
if (pseudoRoyals & ~occupied)
return false;
&& (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto))))
return false;
}
+ // Look for duple check
+ if (var->dupleCheck)
+ {
+ Bitboard pseudoRoyalCandidates = st->pseudoRoyalCandidates & pieces(sideToMove);
+ if (is_ok(from) && (pseudoRoyalCandidates & from))
+ pseudoRoyalCandidates ^= square_bb(from) ^ kto;
+ if (type_of(m) == PROMOTION && extinction_piece_types().find(promotion_type(m)) != extinction_piece_types().end())
+ pseudoRoyalCandidates |= kto;
+ bool allCheck = bool(pseudoRoyalCandidates);
+ while (allCheck && pseudoRoyalCandidates)
+ {
+ Square sr = pop_lsb(pseudoRoyalCandidates);
+ // Touching pseudo-royal pieces are immune
+ if (!( !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb<KING>(sr)))
+ && (attackers_to(sr, occupied, ~us) & (occupied & ~square_bb(kto)))))
+ allCheck = false;
+ }
+ if (allCheck)
+ return false;
+ }
}
// Petrifying the king is illegal
if (type_of(m) == EN_PASSANT && count<KING>(us))
{
Square ksq = square<KING>(us);
- Square capsq = to - pawn_push(us);
+ Square capsq = capture_square(to);
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
- assert(to == ep_square());
- assert(moved_piece(m) == make_piece(us, PAWN));
- assert(piece_on(capsq) == make_piece(~us, PAWN));
+ assert(ep_squares() & to);
assert(piece_on(to) == NO_PIECE);
return !(attackers_to(ksq, occupied, ~us) & occupied);
// Handle the case where a mandatory piece promotion/demotion is not taken
if ( mandatory_piece_promotion()
&& (is_promoted(from) ? piece_demotion() : promoted_piece_type(type_of(pc)) != NO_PIECE_TYPE)
- && (zone_bb(us, promotion_rank(), max_rank()) & (SquareBB[from] | to))
+ && (promotion_zone(us) & (SquareBB[from] | to))
&& (!piece_promotion_on_capture() || capture(m)))
return false;
{
// We have already handled promotion moves, so destination
// cannot be on the 8th/1st rank.
- if (mandatory_pawn_promotion() && rank_of(to) == relative_rank(us, promotion_rank(), max_rank()) && !sittuyin_promotion())
+ if (mandatory_pawn_promotion() && (promotion_zone(us) & to) && !sittuyin_promotion())
return false;
if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture
&& !((from + pawn_push(us) == to) && !(pieces() & to)) // Not a single push
&& !( (from + 2 * pawn_push(us) == to) // Not a double push
- && ( relative_rank(us, from, max_rank()) <= double_step_rank_max()
- && relative_rank(us, from, max_rank()) >= double_step_rank_min())
- && !(pieces() & to)
- && !(pieces() & (to - pawn_push(us)))
- && double_step_enabled()))
+ && (double_step_region(us) & from)
+ && !(pieces() & (to | (to - pawn_push(us)))))
+ && !( (from + 3 * pawn_push(us) == to) // Not a triple push
+ && (triple_step_region(us) & from)
+ && !(pieces() & (to | (to - pawn_push(us)) | (to - 2 * pawn_push(us))))))
return false;
}
else if (!((capture(m) ? attacks_from(us, type_of(pc), from) : moves_from(us, type_of(pc), from)) & to))
// the captured pawn.
case EN_PASSANT:
{
- Square capsq = make_square(file_of(to), rank_of(from));
+ Square capsq = capture_square(to);
Bitboard b = (pieces() ^ from ^ capsq) | to;
return attackers_to(square<KING>(~sideToMove), b) & pieces(sideToMove) & b;
Square from = from_sq(m);
Square to = to_sq(m);
Piece pc = moved_piece(m);
- Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
+ Piece captured = piece_on(type_of(m) == EN_PASSANT ? capture_square(to) : to);
if (to == from)
{
assert((type_of(m) == PROMOTION && sittuyin_promotion()) || (is_pass(m) && pass()));
{
Square capsq = to;
+ if (type_of(m) == EN_PASSANT)
+ {
+ capsq = capture_square(to);
+ st->captureSquare = capsq;
+
+ assert(st->epSquares & to);
+ assert(var->enPassantRegion & to);
+ assert(piece_on(to) == NO_PIECE);
+ }
+
// If the captured piece is a pawn, update pawn hash key, otherwise
// update non-pawn material.
if (type_of(captured) == PAWN)
- {
- if (type_of(m) == EN_PASSANT)
- {
- capsq -= pawn_push(us);
-
- assert(pc == make_piece(us, PAWN));
- assert(to == st->epSquare);
- assert((var->enPassantRegion & to)
- && relative_rank(~us, to, max_rank()) <= Rank(double_step_rank_max() + 1)
- && relative_rank(~us, to, max_rank()) > double_step_rank_min());
- assert(piece_on(to) == NO_PIECE);
- assert(piece_on(capsq) == make_piece(them, PAWN));
- }
-
st->pawnKey ^= Zobrist::psq[captured][capsq];
- }
else
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
{
Piece pieceToHand = !capturedPromoted || drop_loop() ? ~captured
: unpromotedCaptured ? ~unpromotedCaptured
- : make_piece(~color_of(captured), PAWN);
+ : make_piece(~color_of(captured), promotion_pawn_type(color_of(captured)));
add_to_hand(pieceToHand);
k ^= Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)] - 1]
^ Zobrist::inHand[pieceToHand][pieceCountInHand[color_of(pieceToHand)][type_of(pieceToHand)]];
else
k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
- // Reset en passant square
- if (st->epSquare != SQ_NONE)
- {
- k ^= Zobrist::enpassant[file_of(st->epSquare)];
- st->epSquare = SQ_NONE;
- }
+ // Reset en passant squares
+ while (st->epSquares)
+ k ^= Zobrist::enpassant[file_of(pop_lsb(st->epSquares))];
// Update castling rights if needed
if (type_of(m) != DROP && !is_pass(m) && st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
// If the moving piece is a pawn do some special extra work
if (type_of(pc) == PAWN)
{
- // Set en passant square if the moved pawn can be captured
- if ( type_of(m) != DROP
- && std::abs(int(to) - int(from)) == 2 * NORTH
- && (var->enPassantRegion & (to - pawn_push(us)))
- && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))
- && !(wall_gating() && gating_square(m) == to - pawn_push(us)))
- {
- st->epSquare = to - pawn_push(us);
- k ^= Zobrist::enpassant[file_of(st->epSquare)];
- }
-
- else if (type_of(m) == PROMOTION || type_of(m) == PIECE_PROMOTION)
+ if (type_of(m) == PROMOTION || type_of(m) == PIECE_PROMOTION)
{
Piece promotion = make_piece(us, type_of(m) == PROMOTION ? promotion_type(m) : promoted_piece_type(PAWN));
- assert(relative_rank(us, to, max_rank()) >= promotion_rank() || sittuyin_promotion());
+ assert((promotion_zone(us) & to) || sittuyin_promotion());
assert(type_of(promotion) >= KNIGHT && type_of(promotion) < KING);
+ st->promotionPawn = piece_on(to);
remove_piece(to);
put_piece(promotion, to, true, type_of(m) == PIECE_PROMOTION ? pc : NO_PIECE);
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
}
+ // Set en passant square(s) if the moved pawn can be captured
+ else if ( type_of(m) != DROP
+ && ( std::abs(int(to) - int(from)) == 2 * NORTH
+ || std::abs(int(to) - int(from)) == 3 * NORTH))
+ {
+ if ( (var->enPassantRegion & (to - pawn_push(us)))
+ && (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN))
+ && !(wall_gating() && gating_square(m) == to - pawn_push(us)))
+ {
+ st->epSquares |= to - pawn_push(us);
+ k ^= Zobrist::enpassant[file_of(to)];
+ }
+ if ( std::abs(int(to) - int(from)) == 3 * NORTH
+ && (var->enPassantRegion & (to - 2 * pawn_push(us)))
+ && (pawn_attacks_bb(us, to - 2 * pawn_push(us)) & pieces(them, PAWN))
+ && !(wall_gating() && gating_square(m) == to - 2 * pawn_push(us)))
+ {
+ st->epSquares |= to - 2 * pawn_push(us);
+ k ^= Zobrist::enpassant[file_of(to)];
+ }
+ }
+
// Update pawn hash key
st->pawnKey ^= (type_of(m) != DROP ? Zobrist::psq[pc][from] : 0) ^ Zobrist::psq[pc][to];
-
- // Reset rule 50 draw counter
- st->rule50 = 0;
}
- else if (type_of(m) == PIECE_PROMOTION)
+ else if (type_of(m) == PROMOTION || type_of(m) == PIECE_PROMOTION)
{
- Piece promotion = make_piece(us, promoted_piece_type(type_of(pc)));
+ Piece promotion = make_piece(us, type_of(m) == PROMOTION ? promotion_type(m) : promoted_piece_type(type_of(pc)));
+ st->promotionPawn = piece_on(to);
remove_piece(to);
- put_piece(promotion, to, true, pc);
+ put_piece(promotion, to, true, type_of(m) == PIECE_PROMOTION ? pc : NO_PIECE);
if (Eval::useNNUE)
{
// Update material
st->nonPawnMaterial[us] += PieceValue[MG][demotion] - PieceValue[MG][pc];
}
+ // Set en passant square(s) if the moved piece can be captured
+ else if ( type_of(m) != DROP
+ && ((PseudoMoves[1][us][type_of(pc)][from] & ~PseudoMoves[0][us][type_of(pc)][from]) & to))
+ {
+ assert(type_of(pc) != PAWN);
+ st->epSquares = between_bb(from, to) & var->enPassantRegion;
+ for (Bitboard b = st->epSquares; b; )
+ k ^= Zobrist::enpassant[file_of(pop_lsb(b))];
+ }
+
+ // Reset rule 50 draw counter
+ if (var->nMoveRuleTypes[us] & type_of(pc))
+ st->rule50 = 0;
// Set capture piece
st->capturedPiece = captured;
if (type_of(m) == PROMOTION)
{
- assert(relative_rank(us, to, max_rank()) >= promotion_rank() || sittuyin_promotion());
+ assert((promotion_zone(us) & to) || sittuyin_promotion());
assert(type_of(pc) == promotion_type(m));
assert(type_of(pc) >= KNIGHT && type_of(pc) < KING);
+ assert(type_of(st->promotionPawn) == promotion_pawn_type(us) || !captures_to_hand());
remove_piece(to);
- pc = make_piece(us, PAWN);
+ pc = st->promotionPawn;
put_piece(pc, to);
}
else if (type_of(m) == PIECE_PROMOTION)
if (type_of(m) == EN_PASSANT)
{
- capsq -= pawn_push(us);
+ capsq = st->captureSquare;
- assert(type_of(pc) == PAWN);
- assert(to == st->previous->epSquare);
- assert(relative_rank(~us, to, max_rank()) <= Rank(double_step_rank_max() + 1));
+ assert(st->previous->epSquares & to);
+ assert(var->enPassantRegion & to);
assert(piece_on(capsq) == NO_PIECE);
- assert(st->capturedPiece == make_piece(~us, PAWN));
}
put_piece(st->capturedPiece, capsq, st->capturedpromoted, st->unpromotedCapturedPiece); // Restore the captured piece
if (captures_to_hand())
remove_from_hand(!drop_loop() && st->capturedpromoted ? (st->unpromotedCapturedPiece ? ~st->unpromotedCapturedPiece
- : make_piece(~color_of(st->capturedPiece), PAWN))
+ : make_piece(~color_of(st->capturedPiece), promotion_pawn_type(us)))
: ~st->capturedPiece);
}
}
st->accumulator.computed[WHITE] = false;
st->accumulator.computed[BLACK] = false;
- if (st->epSquare != SQ_NONE)
- {
- st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
- st->epSquare = SQ_NONE;
- }
+ while (st->epSquares)
+ st->key ^= Zobrist::enpassant[file_of(pop_lsb(st->epSquares))];
st->key ^= Zobrist::side;
prefetch(TT.first_entry(key()));
k ^= Zobrist::psq[captured][to];
if (captures_to_hand())
{
- Piece removeFromHand = !drop_loop() && is_promoted(to) ? make_piece(~color_of(captured), PAWN) : ~captured;
+ Piece removeFromHand = !drop_loop() && is_promoted(to) ? make_piece(~color_of(captured), promotion_pawn_type(color_of(captured))) : ~captured;
k ^= Zobrist::inHand[removeFromHand][pieceCountInHand[color_of(removeFromHand)][type_of(removeFromHand)] + 1]
^ Zobrist::inHand[removeFromHand][pieceCountInHand[color_of(removeFromHand)][type_of(removeFromHand)]];
}
if ( (sideToMove != WHITE && sideToMove != BLACK)
|| (count<KING>(WHITE) && piece_on(square<KING>(WHITE)) != make_piece(WHITE, KING))
|| (count<KING>(BLACK) && piece_on(square<KING>(BLACK)) != make_piece(BLACK, KING))
- || ( ep_square() != SQ_NONE
- && relative_rank(~sideToMove, ep_square(), max_rank()) > Rank(double_step_rank_max() + 1)))
+ || (ep_squares() & ~var->enPassantRegion))
assert(0 && "pos_is_ok: Default");
if (Fast)
int countingPly;
int countingLimit;
CheckCount checksRemaining[COLOR_NB];
- Square epSquare;
+ Bitboard epSquares;
Square castlingKingSquare[COLOR_NB];
Bitboard wallSquares;
Bitboard gatesBB[COLOR_NB];
Bitboard pinners[COLOR_NB];
Bitboard checkSquares[PIECE_TYPE_NB];
Piece capturedPiece;
+ Square captureSquare; // when != to_sq, e.g., en passant
+ Piece promotionPawn;
Bitboard nonSlidingRiders;
Bitboard flippedPieces;
+ Bitboard pseudoRoyalCandidates;
Bitboard pseudoRoyals;
OptBool legalCapture;
bool capturedpromoted;
const std::set<PieceType>& piece_types() const;
const std::string& piece_to_char() const;
const std::string& piece_to_char_synonyms() const;
- Rank promotion_rank() const;
- const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types() const;
+ Bitboard promotion_zone(Color c) const;
+ Square promotion_square(Color c, Square s) const;
+ PieceType promotion_pawn_type(Color c) const;
+ const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types(Color c) const;
bool sittuyin_promotion() const;
int promotion_limit(PieceType pt) const;
PieceType promoted_piece_type(PieceType pt) const;
bool piece_demotion() const;
bool blast_on_capture() const;
bool endgame_eval() const;
- bool double_step_enabled() const;
- Rank double_step_rank_max() const;
- Rank double_step_rank_min() const;
+ Bitboard double_step_region(Color c) const;
+ Bitboard triple_step_region(Color c) const;
bool castling_enabled() const;
bool castling_dropped_piece() const;
File castling_kingside_file() const;
Bitboard non_sliding_riders() const;
Piece piece_on(Square s) const;
Piece unpromoted_piece_on(Square s) const;
- Square ep_square() const;
+ Bitboard ep_squares() const;
Square castling_king_square(Color c) const;
Bitboard gates(Color c) const;
bool empty(Square s) const;
bool virtual_drop(Move m) const;
bool capture(Move m) const;
bool capture_or_promotion(Move m) const;
+ Square capture_square(Square to) const;
bool gives_check(Move m) const;
Piece moved_piece(Move m) const;
Piece captured_piece() const;
return var->pieceToCharSynonyms;
}
-inline Rank Position::promotion_rank() const {
+inline Bitboard Position::promotion_zone(Color c) const {
assert(var != nullptr);
- return var->promotionRank;
+ return var->promotionRegion[c];
}
-inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types() const {
+inline Square Position::promotion_square(Color c, Square s) const {
assert(var != nullptr);
- return var->promotionPieceTypes;
+ Bitboard b = promotion_zone(c) & forward_file_bb(c, s) & board_bb();
+ return !b ? SQ_NONE : c == WHITE ? lsb(b) : msb(b);
+}
+
+inline PieceType Position::promotion_pawn_type(Color c) const {
+ assert(var != nullptr);
+ return var->promotionPawnType[c];
+}
+
+inline const std::set<PieceType, std::greater<PieceType> >& Position::promotion_piece_types(Color c) const {
+ assert(var != nullptr);
+ return var->promotionPieceTypes[c];
}
inline bool Position::sittuyin_promotion() const {
return var->endgameEval && !count_in_hand(ALL_PIECES) && count<KING>() == 2;
}
-inline bool Position::double_step_enabled() const {
- assert(var != nullptr);
- return var->doubleStep;
-}
-
-inline Rank Position::double_step_rank_max() const {
+inline Bitboard Position::double_step_region(Color c) const {
assert(var != nullptr);
- return var->doubleStepRank;
+ return var->doubleStepRegion[c];
}
-inline Rank Position::double_step_rank_min() const {
+inline Bitboard Position::triple_step_region(Color c) const {
assert(var != nullptr);
- return var->doubleStepRankMin;
+ return var->tripleStepRegion[c];
}
inline bool Position::castling_enabled() const {
if (pt == PAWN)
{
if (!var->promotionZonePawnDrops)
- b &= ~zone_bb(c, promotion_rank(), max_rank());
+ b &= ~promotion_zone(c);
if (!first_rank_pawn_drops())
b &= ~rank_bb(relative_rank(c, RANK_1, max_rank()));
}
&& attackers_to(sr, ~sideToMove))
return convert_mate_value(var->checkmateValue, ply);
}
+ // Look for duple check
+ if (var->dupleCheck)
+ {
+ Bitboard pseudoRoyalCandidates = st->pseudoRoyalCandidates & pieces(sideToMove);
+ bool allCheck = bool(pseudoRoyalCandidates);
+ while (allCheck && pseudoRoyalCandidates)
+ {
+ Square sr = pop_lsb(pseudoRoyalCandidates);
+ // Touching pseudo-royal pieces are immune
+ if (!( !(blast_on_capture() && (pseudoRoyalsTheirs & attacks_bb<KING>(sr)))
+ && attackers_to(sr, ~sideToMove)))
+ allCheck = false;
+ }
+ if (allCheck)
+ return convert_mate_value(var->checkmateValue, ply);
+ }
}
return convert_mate_value(var->stalemateValue, ply);
}
inline Bitboard Position::capture_the_flag(Color c) const {
assert(var != nullptr);
- return c == WHITE ? var->whiteFlag : var->blackFlag;
+ return var->flagRegion[c];
}
inline bool Position::flag_move() const {
return lsb(pieces(c, pt));
}
-inline Square Position::ep_square() const {
- return st->epSquare;
+inline Bitboard Position::ep_squares() const {
+ return st->epSquares;
}
inline Square Position::castling_king_square(Color c) const {
PieceType movePt = pt == KING ? king_type() : pt;
Bitboard b = moves_bb(c, movePt, s, byTypeBB[ALL_PIECES]);
+ // Add initial moves
+ if (double_step_region(c) & s)
+ b |= moves_bb<true>(c, movePt, s, byTypeBB[ALL_PIECES]);
// Xiangqi soldier
if (pt == SOLDIER && !(promoted_soldiers(c) & s))
b &= file_bb(file_of(s));
return (!empty(to_sq(m)) && type_of(m) != CASTLING && from_sq(m) != to_sq(m)) || type_of(m) == EN_PASSANT;
}
+inline Square Position::capture_square(Square to) const {
+ assert(is_ok(to));
+ // The capture square of en passant is either the marked ep piece or the closest piece behind the target square
+ Bitboard b = ep_squares() & pieces() ? ep_squares() & pieces() : pieces(~sideToMove) & forward_file_bb(~sideToMove, to);
+ return sideToMove == WHITE ? msb(b) : lsb(b);
+}
+
inline bool Position::virtual_drop(Move m) const {
assert(is_ok(m));
return type_of(m) == DROP && !can_drop(side_to_move(), in_hand_piece_type(m));
Value piece_value(Phase phase, PieceType pt)
{
const PieceInfo* pi = pieceMap.find(pt)->second;
- int v0 = (phase == MG ? 55 : 60) * pi->steps[MODALITY_CAPTURE].size()
- + (phase == MG ? 30 : 40) * pi->steps[MODALITY_QUIET].size()
- + (phase == MG ? 185 : 180) * pi->slider[MODALITY_CAPTURE].size()
- + (phase == MG ? 55 : 50) * pi->slider[MODALITY_QUIET].size()
+ int v0 = (phase == MG ? 55 : 60) * pi->steps[0][MODALITY_CAPTURE].size()
+ + (phase == MG ? 30 : 40) * pi->steps[0][MODALITY_QUIET].size()
+ + (phase == MG ? 185 : 180) * pi->slider[0][MODALITY_CAPTURE].size()
+ + (phase == MG ? 55 : 50) * pi->slider[0][MODALITY_QUIET].size()
// Hoppers are more useful with more pieces on the board
- + (phase == MG ? 100 : 80) * pi->hopper[MODALITY_CAPTURE].size()
- + (phase == MG ? 80 : 60) * pi->hopper[MODALITY_QUIET].size()
+ + (phase == MG ? 100 : 80) * pi->hopper[0][MODALITY_CAPTURE].size()
+ + (phase == MG ? 80 : 60) * pi->hopper[0][MODALITY_QUIET].size()
// Rook sliding directions are more valuable, especially in endgame
- + (phase == MG ? 10 : 30) * std::count_if(pi->slider[MODALITY_CAPTURE].begin(), pi->slider[MODALITY_CAPTURE].end(), [](const std::pair<const Direction, int>& d) { return std::abs(d.first) == NORTH || std::abs(d.first) == 1; })
- + (phase == MG ? 30 : 45) * std::count_if(pi->slider[MODALITY_QUIET].begin(), pi->slider[MODALITY_QUIET].end(), [](const std::pair<const Direction, int>& d) { return std::abs(d.first) == NORTH || std::abs(d.first) == 1; });
+ + (phase == MG ? 10 : 30) * std::count_if(pi->slider[0][MODALITY_CAPTURE].begin(), pi->slider[0][MODALITY_CAPTURE].end(), [](const std::pair<const Direction, int>& d) { return std::abs(d.first) == NORTH || std::abs(d.first) == 1; })
+ + (phase == MG ? 30 : 45) * std::count_if(pi->slider[0][MODALITY_QUIET].begin(), pi->slider[0][MODALITY_QUIET].end(), [](const std::pair<const Direction, int>& d) { return std::abs(d.first) == NORTH || std::abs(d.first) == 1; });
return Value(v0 * exp(double(v0) / 10000));
}
}
Value maxPromotion = VALUE_ZERO;
- for (PieceType pt : v->promotionPieceTypes)
+ for (PieceType pt : v->promotionPieceTypes[WHITE])
maxPromotion = std::max(maxPromotion, PieceValue[EG][pt]);
for (PieceType pt = PAWN; pt <= KING; ++pt)
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
// Consider promotion types in pawn score
- if (pt == PAWN)
+ if (pt == v->promotionPawnType[WHITE])
{
score -= make_score(0, (QueenValueEg - maxPromotion) / 100);
if (v->blastOnCapture)
}
const PieceInfo* pi = pieceMap.find(pt)->second;
- bool isSlider = pi->slider[MODALITY_QUIET].size() || pi->slider[MODALITY_CAPTURE].size() || pi->hopper[MODALITY_QUIET].size() || pi->hopper[MODALITY_CAPTURE].size();
- bool isPawn = !isSlider && pi->steps[MODALITY_QUIET].size() && !std::any_of(pi->steps[MODALITY_QUIET].begin(), pi->steps[MODALITY_QUIET].end(), [](const std::pair<const Direction, int>& d) { return d.first < SOUTH / 2; });
- bool isSlowLeaper = !isSlider && !std::any_of(pi->steps[MODALITY_QUIET].begin(), pi->steps[MODALITY_QUIET].end(), [](const std::pair<const Direction, int>& d) { return dist(d.first) > 1; });
+ bool isSlider = pi->slider[0][MODALITY_QUIET].size() || pi->slider[0][MODALITY_CAPTURE].size() || pi->hopper[0][MODALITY_QUIET].size() || pi->hopper[0][MODALITY_CAPTURE].size();
+ bool isPawn = !isSlider && pi->steps[0][MODALITY_QUIET].size() && !std::any_of(pi->steps[0][MODALITY_QUIET].begin(), pi->steps[0][MODALITY_QUIET].end(), [](const std::pair<const Direction, int>& d) { return d.first < SOUTH / 2; });
+ bool isSlowLeaper = !isSlider && !std::any_of(pi->steps[0][MODALITY_QUIET].begin(), pi->steps[0][MODALITY_QUIET].end(), [](const std::pair<const Direction, int>& d) { return dist(d.first) > 1; });
// Scale slider piece values with board size
if (isSlider)
constexpr int rm = 5;
constexpr int r0 = rm + RANK_8;
int r1 = rm + (v->maxRank + v->maxFile - 2 * v->capturesToHand) / 2;
- int leaper = pi->steps[MODALITY_QUIET].size() + pi->steps[MODALITY_CAPTURE].size();
- int slider = pi->slider[MODALITY_QUIET].size() + pi->slider[MODALITY_CAPTURE].size() + pi->hopper[MODALITY_QUIET].size() + pi->hopper[MODALITY_CAPTURE].size();
+ int leaper = pi->steps[0][MODALITY_QUIET].size() + pi->steps[0][MODALITY_CAPTURE].size();
+ int slider = pi->slider[0][MODALITY_QUIET].size() + pi->slider[0][MODALITY_CAPTURE].size() + pi->hopper[0][MODALITY_QUIET].size() + pi->hopper[0][MODALITY_CAPTURE].size();
score = make_score(mg_value(score) * (lc * leaper + r1 * slider) / (lc * leaper + r0 * slider),
eg_value(score) * (lc * leaper + r1 * slider) / (lc * leaper + r0 * slider));
}
// Increase leapers' value in makpong
else if (v->makpongRule)
{
- if (std::any_of(pi->steps[MODALITY_CAPTURE].begin(), pi->steps[MODALITY_CAPTURE].end(), [](const std::pair<const Direction, int>& d) { return dist(d.first) > 1 && !d.second; }))
+ if (std::any_of(pi->steps[0][MODALITY_CAPTURE].begin(), pi->steps[0][MODALITY_CAPTURE].end(), [](const std::pair<const Direction, int>& d) { return dist(d.first) > 1 && !d.second; }))
score = make_score(mg_value(score) * 4200 / (3500 + mg_value(score)),
eg_value(score) * 4700 / (3500 + mg_value(score)));
}
// For antichess variants, use negative piece values
if (v->extinctionValue == VALUE_MATE)
- score = -make_score(mg_value(score) / 8, eg_value(score) / 8 / (1 + !pi->slider[MODALITY_CAPTURE].size()));
+ score = -make_score(mg_value(score) / 8, eg_value(score) / 8 / (1 + !pi->slider[0][MODALITY_CAPTURE].size()));
// Override variant piece value
if (v->pieceValue[MG][pt])
&& (ss-1)->statScore < 23767
&& eval >= beta
&& eval >= ss->staticEval
- && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 + 200 * (!pos.double_step_enabled() && pos.piece_to_char()[PAWN] != ' ')
+ && ss->staticEval >= beta - 20 * depth - 22 * improving + 168 * ss->ttPv + 159 + 200 * (!pos.double_step_region(pos.side_to_move()) && pos.piece_to_char()[PAWN] != ' ')
&& !excludedMove
&& pos.non_pawn_material(us)
&& pos.count<ALL_PIECES>(~us) != pos.count<PAWN>(~us)
PIECE_NB = 2 * PIECE_TYPE_NB
};
+enum PieceSet : uint64_t {};
+
enum RiderType : int {
NO_RIDER = 0,
RIDER_BISHOP = 1 << 0,
RIDER_ROOK_V = 1 << 2,
RIDER_CANNON_H = 1 << 3,
RIDER_CANNON_V = 1 << 4,
- RIDER_HORSE = 1 << 5,
- RIDER_ELEPHANT = 1 << 6,
- RIDER_JANGGI_ELEPHANT = 1 << 7,
- RIDER_CANNON_DIAG = 1 << 8,
- RIDER_NIGHTRIDER = 1 << 9,
- RIDER_GRASSHOPPER_H = 1 << 10,
- RIDER_GRASSHOPPER_V = 1 << 11,
- RIDER_GRASSHOPPER_D = 1 << 12,
+ RIDER_LAME_DABBABA = 1 << 5,
+ RIDER_HORSE = 1 << 6,
+ RIDER_ELEPHANT = 1 << 7,
+ RIDER_JANGGI_ELEPHANT = 1 << 8,
+ RIDER_CANNON_DIAG = 1 << 9,
+ RIDER_NIGHTRIDER = 1 << 10,
+ RIDER_GRASSHOPPER_H = 1 << 11,
+ RIDER_GRASSHOPPER_V = 1 << 12,
+ RIDER_GRASSHOPPER_D = 1 << 13,
HOPPING_RIDERS = RIDER_CANNON_H | RIDER_CANNON_V | RIDER_CANNON_DIAG
| RIDER_GRASSHOPPER_H | RIDER_GRASSHOPPER_V | RIDER_GRASSHOPPER_D,
- LAME_LEAPERS = RIDER_HORSE | RIDER_ELEPHANT | RIDER_JANGGI_ELEPHANT,
+ LAME_LEAPERS = RIDER_LAME_DABBABA | RIDER_HORSE | RIDER_ELEPHANT | RIDER_JANGGI_ELEPHANT,
ASYMMETRICAL_RIDERS = RIDER_HORSE | RIDER_JANGGI_ELEPHANT
| RIDER_GRASSHOPPER_H | RIDER_GRASSHOPPER_V | RIDER_GRASSHOPPER_D,
NON_SLIDING_RIDERS = HOPPING_RIDERS | LAME_LEAPERS | RIDER_NIGHTRIDER,
#undef ENABLE_BASE_OPERATORS_ON
#undef ENABLE_BIT_OPERATORS_ON
+constexpr PieceSet piece_set(PieceType pt) {
+ return PieceSet(1ULL << pt);
+}
+
+constexpr PieceSet operator| (PieceSet ps1, PieceSet ps2) { return (PieceSet)((uint64_t)ps1 | (uint64_t)ps2); }
+constexpr PieceSet operator& (PieceSet ps1, PieceSet ps2) { return (PieceSet)((uint64_t)ps1 & (uint64_t)ps2); }
+constexpr PieceSet operator& (PieceSet ps, PieceType pt) { return ps & piece_set(pt); }
+inline PieceSet& operator|= (PieceSet& ps1, PieceSet ps2) { return (PieceSet&)((uint64_t&)ps1 |= (uint64_t)ps2); }
+inline PieceSet& operator|= (PieceSet& ps, PieceType pt) { return ps |= piece_set(pt); }
+
+static_assert(piece_set(PAWN) & PAWN);
+static_assert(piece_set(KING) & KING);
+
/// Additional operators to add a Direction to a Square
constexpr Square operator+(Square s, Direction d) { return Square(int(s) + int(d)); }
constexpr Square operator-(Square s, Direction d) { return Square(int(s) - int(d)); }
std::set<string> standard_variants = {
"normal", "nocastle", "fischerandom", "knightmate", "3check", "makruk", "shatranj",
"asean", "seirawan", "crazyhouse", "bughouse", "suicide", "giveaway", "losers", "atomic",
- "capablanca", "gothic", "janus", "caparandom", "grand", "shogi", "xiangqi", "duck"
+ "capablanca", "gothic", "janus", "caparandom", "grand", "shogi", "xiangqi", "duck",
+ "berolina", "spartan"
};
void init_variant(const Variant* v) {
suffix += std::string(v->dropNoDoubledCount, 'f');
else if (pt == BISHOP && v->dropOppositeColoredBishop)
suffix += "s";
- suffix += "@" + std::to_string(pt == PAWN && !v->promotionZonePawnDrops ? v->promotionRank : v->maxRank + 1);
+ suffix += "@" + std::to_string(pt == PAWN && !v->promotionZonePawnDrops && v->promotionRegion[WHITE] ? rank_of(lsb(v->promotionRegion[WHITE])) : v->maxRank + 1);
}
sync_cout << "piece " << v->pieceToChar[pt] << "& " << pieceMap.find(pt == KING ? v->kingType : pt)->second->betza << suffix << sync_endl;
PieceType promType = v->promotedPieceType[pt];
v->materialCounting = BLACK_DRAW_ODDS;
return v;
}
+ // Torpedo Chess
+ // https://arxiv.org/abs/2009.04374
+ Variant* torpedo_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->doubleStepRegion[WHITE] = AllSquares;
+ v->doubleStepRegion[BLACK] = AllSquares;
+ return v;
+ }
+ // Berolina Chess
+ // https://www.chessvariants.com/dpieces.dir/berlin.html
+ Variant* berolina_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->remove_piece(PAWN);
+ v->add_piece(CUSTOM_PIECES, 'p', "mfFcfeWimfnA");
+ v->promotionPawnType[WHITE] = v->promotionPawnType[BLACK] = CUSTOM_PIECES;
+ v->promotionPawnTypes[WHITE] = v->promotionPawnTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->enPassantTypes[WHITE] = v->enPassantTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->nMoveRuleTypes[WHITE] = v->nMoveRuleTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ return v;
+ }
+ // Pawnsideways
+ // https://arxiv.org/abs/2009.04374
+ Variant* pawnsideways_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->remove_piece(PAWN);
+ v->add_piece(CUSTOM_PIECES, 'p', "fsmWfceFifmnD");
+ v->promotionPawnType[WHITE] = v->promotionPawnType[BLACK] = CUSTOM_PIECES;
+ v->promotionPawnTypes[WHITE] = v->promotionPawnTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->enPassantTypes[WHITE] = v->enPassantTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->nMoveRuleTypes[WHITE] = v->nMoveRuleTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ return v;
+ }
+ // Pawnback
+ // https://arxiv.org/abs/2009.04374
+ Variant* pawnback_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->remove_piece(PAWN);
+ v->add_piece(CUSTOM_PIECES, 'p', "fbmWfceFifmnD");
+ v->mobilityRegion[WHITE][CUSTOM_PIECES] = (Rank2BB | Rank3BB | Rank4BB | Rank5BB | Rank6BB | Rank7BB | Rank8BB);
+ v->mobilityRegion[BLACK][CUSTOM_PIECES] = (Rank7BB | Rank6BB | Rank5BB | Rank4BB | Rank3BB | Rank2BB | Rank1BB);
+ v->promotionPawnType[WHITE] = v->promotionPawnType[BLACK] = CUSTOM_PIECES;
+ v->promotionPawnTypes[WHITE] = v->promotionPawnTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->enPassantTypes[WHITE] = v->enPassantTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->nMoveRuleTypes[WHITE] = v->nMoveRuleTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ return v;
+ }
+ // Legan Chess
+ // https://en.wikipedia.org/wiki/Legan_chess
+ Variant* legan_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->remove_piece(PAWN);
+ v->add_piece(CUSTOM_PIECES, 'p', "mflFcflW");
+ v->promotionRegion[WHITE] = make_bitboard(SQ_A8, SQ_B8, SQ_C8, SQ_D8, SQ_A7, SQ_A6, SQ_A5);
+ v->promotionRegion[BLACK] = make_bitboard(SQ_E1, SQ_F1, SQ_G1, SQ_H1, SQ_H2, SQ_H3, SQ_H4);
+ v->promotionPawnType[WHITE] = v->promotionPawnType[BLACK] = CUSTOM_PIECES;
+ v->promotionPawnTypes[WHITE] = v->promotionPawnTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->nMoveRuleTypes[WHITE] = v->nMoveRuleTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->startFen = "knbrp3/bqpp4/npp5/rp1p3P/p3P1PR/5PPN/4PPQB/3PRBNK w - - 0 1";
+ v->doubleStep = false;
+ return v;
+ }
// Pseudo-variant only used for endgame initialization
Variant* fairy_variant() {
Variant* v = chess_variant_base()->init();
}
// Raazuva (Maldivian Chess)
Variant* raazuvaa_variant() {
- Variant* v = chess_variant()->init();
+ Variant* v = chess_variant_base()->init();
v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - - 0 1";
v->castling = false;
v->doubleStep = false;
v->add_piece(KHON, 's');
v->add_piece(MET, 'm');
v->startFen = "rnsmksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKMSNR w - - 0 1";
- v->promotionRank = RANK_6;
- v->promotionPieceTypes = {MET};
+ v->promotionRegion[WHITE] = Rank6BB | Rank7BB | Rank8BB;
+ v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
+ v->promotionPieceTypes[WHITE] = {MET};
+ v->promotionPieceTypes[BLACK] = {MET};
v->doubleStep = false;
v->castling = false;
v->nMoveRule = 0;
v->add_piece(KHON, 'b');
v->add_piece(MET, 'q');
v->startFen = "rnbqkbnr/8/pppppppp/8/8/PPPPPPPP/8/RNBQKBNR w - - 0 1";
- v->promotionPieceTypes = {ROOK, KNIGHT, KHON, MET};
+ v->promotionPieceTypes[WHITE] = {ROOK, KNIGHT, KHON, MET};
+ v->promotionPieceTypes[BLACK] = {ROOK, KNIGHT, KHON, MET};
v->doubleStep = false;
v->castling = false;
v->countingRule = ASEAN_COUNTING;
v->remove_piece(MET);
v->add_piece(AIWOK, 'a');
v->startFen = "rnsaksnr/8/pppppppp/8/8/PPPPPPPP/8/RNSKASNR w - - 0 1";
- v->promotionPieceTypes = {AIWOK};
+ v->promotionPieceTypes[WHITE] = {AIWOK};
+ v->promotionPieceTypes[BLACK] = {AIWOK};
return v;
}
// Shatranj
v->add_piece(ALFIL, 'b');
v->add_piece(FERS, 'q');
v->startFen = "rnbkqbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBKQBNR w - - 0 1";
- v->promotionPieceTypes = {FERS};
+ v->promotionPieceTypes[WHITE] = {FERS};
+ v->promotionPieceTypes[BLACK] = {FERS};
v->doubleStep = false;
v->castling = false;
v->extinctionValue = -VALUE_MATE;
v->remove_piece(QUEEN);
v->add_piece(AMAZON, 'a');
v->startFen = "rnbakbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBAKBNR w KQkq - 0 1";
- v->promotionPieceTypes = {AMAZON, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {AMAZON, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {AMAZON, ROOK, BISHOP, KNIGHT};
return v;
}
// Nightrider chess
Variant* v = chess_variant_base()->init();
v->remove_piece(KNIGHT);
v->add_piece(CUSTOM_PIECES, 'n', "NN");
- v->promotionPieceTypes = {QUEEN, ROOK, BISHOP, CUSTOM_PIECES};
+ v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, BISHOP, CUSTOM_PIECES};
+ v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISHOP, CUSTOM_PIECES};
return v;
}
// Grasshopper chess
Variant* grasshopper_variant() {
Variant* v = chess_variant_base()->init();
v->add_piece(CUSTOM_PIECES, 'g', "gQ");
- v->promotionPieceTypes.insert(CUSTOM_PIECES);
+ v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES);
+ v->promotionPieceTypes[BLACK].insert(CUSTOM_PIECES);
v->startFen = "rnbqkbnr/gggggggg/pppppppp/8/8/PPPPPPPP/GGGGGGGG/RNBQKBNR w KQkq - 0 1";
v->doubleStep = false;
return v;
v->remove_piece(BISHOP);
v->add_piece(KNIBIS, 'n');
v->add_piece(BISKNI, 'b');
- v->promotionPieceTypes = {QUEEN, ROOK, BISKNI, KNIBIS};
+ v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, BISKNI, KNIBIS};
+ v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISKNI, KNIBIS};
return v;
}
// New Zealand
v->add_piece(ROOKNI, 'r');
v->add_piece(KNIROO, 'n');
v->castlingRookPiece = ROOKNI;
- v->promotionPieceTypes = {QUEEN, ROOKNI, BISHOP, KNIROO};
+ v->promotionPieceTypes[WHITE] = {QUEEN, ROOKNI, BISHOP, KNIROO};
+ v->promotionPieceTypes[BLACK] = {QUEEN, ROOKNI, BISHOP, KNIROO};
return v;
}
// King of the Hill
Variant* kingofthehill_variant() {
Variant* v = chess_variant_base()->init();
v->flagPiece = KING;
- v->whiteFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
- v->blackFlag = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
+ v->flagRegion[WHITE] = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
+ v->flagRegion[BLACK] = (Rank4BB | Rank5BB) & (FileDBB | FileEBB);
v->flagMove = false;
return v;
}
Variant* v = chess_variant_base()->init();
v->startFen = "8/8/8/8/8/8/krbnNBRK/qrbnNBRQ w - - 0 1";
v->flagPiece = KING;
- v->whiteFlag = Rank8BB;
- v->blackFlag = Rank8BB;
+ v->flagRegion[WHITE] = Rank8BB;
+ v->flagRegion[BLACK] = Rank8BB;
v->flagMove = true;
v->castling = false;
v->checking = false;
v->startFen = "rmbqkbmr/pppppppp/8/8/8/8/PPPPPPPP/RMBQKBMR w KQkq - 0 1";
v->kingType = KNIGHT;
v->castlingKingPiece = KING;
- v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP};
+ v->promotionPieceTypes[WHITE] = {COMMONER, QUEEN, ROOK, BISHOP};
+ v->promotionPieceTypes[BLACK] = {COMMONER, QUEEN, ROOK, BISHOP};
return v;
}
// Losers chess
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
v->castlingKingPiece = COMMONER;
- v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
v->stalemateValue = VALUE_MATE;
v->extinctionValue = VALUE_MATE;
v->extinctionPieceTypes = {ALL_PIECES};
// http://www.binnewirtz.com/Schlagschach1.htm
Variant* codrus_variant() {
Variant* v = giveaway_variant()->init();
- v->promotionPieceTypes = {QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISHOP, KNIGHT};
v->extinctionPieceTypes = {COMMONER};
return v;
}
v->remove_piece(KING);
v->add_piece(COMMONER, 'k');
v->castlingKingPiece = COMMONER;
- v->promotionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT};
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = {COMMONER, QUEEN, ROOK, BISHOP, KNIGHT, PAWN};
return v;
// https://en.wikipedia.org/wiki/V._R._Parton#Kinglet_chess
Variant* kinglet_variant() {
Variant* v = extinction_variant()->init();
- v->promotionPieceTypes = {COMMONER};
+ v->promotionPieceTypes[WHITE] = {COMMONER};
+ v->promotionPieceTypes[BLACK] = {COMMONER};
v->extinctionPieceTypes = {PAWN};
return v;
}
Variant* horde_variant() {
Variant* v = chess_variant_base()->init();
v->startFen = "rnbqkbnr/pppppppp/8/1PP2PP1/PPPPPPPP/PPPPPPPP/PPPPPPPP/PPPPPPPP w kq - 0 1";
- v->doubleStepRankMin = RANK_1;
+ v->doubleStepRegion[WHITE] |= Rank1BB;
v->enPassantRegion = Rank3BB | Rank6BB; // exclude en passant on second rank
v->extinctionValue = -VALUE_MATE;
v->extinctionPieceTypes = {ALL_PIECES};
v->stalemateValue = VALUE_MATE;
return v;
}
-
+
Variant* isolation_variant() { //https://boardgamegeek.com/boardgame/1875/isolation
Variant* v = chess_variant_base()->init();
v->maxRank = RANK_8;
v->variantTemplate = "bughouse";
v->pieceToCharTable = "PN.R.F....SKpn.r.f....sk";
v->startFen = "8/8/4pppp/pppp4/4PPPP/PPPP4/8/8[KFRRSSNNkfrrssnn] w - - 0 1";
- v->remove_piece(MET);
v->add_piece(MET, 'f');
v->mustDrop = true;
v->pieceDrops = true;
v->blackDropRegion = Rank8BB | Rank7BB | Rank6BB;
v->sittuyinRookDrop = true;
v->sittuyinPromotion = true;
+ v->promotionRegion[WHITE] = make_bitboard(SQ_A8, SQ_B7, SQ_C6, SQ_D5, SQ_E5, SQ_F6, SQ_G7, SQ_H8);
+ v->promotionRegion[BLACK] = make_bitboard(SQ_A1, SQ_B2, SQ_C3, SQ_D4, SQ_E4, SQ_F3, SQ_G2, SQ_H1);
v->promotionLimit[FERS] = 1;
v->immobilityIllegal = false;
v->countingRule = ASEAN_COUNTING;
v->startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[HEhe] w KQBCDFGkqbcdfg - 0 1";
v->gating = true;
v->seirawanGating = true;
- v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
// S-House
Variant *v = chess_variant_base()->init();
v->remove_piece(BISHOP);
v->add_piece(CUSTOM_PIECES, 'b', "BnN");
- v->promotionPieceTypes = {QUEEN, CUSTOM_PIECES, ROOK, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {QUEEN, CUSTOM_PIECES, ROOK, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {QUEEN, CUSTOM_PIECES, ROOK, KNIGHT};
return v;
}
// Base used for most shogi variants
v->startFen = "rbsgk/4p/5/P4/KGSBR[-] w 0 1";
v->pieceDrops = true;
v->capturesToHand = true;
- v->promotionRank = RANK_5;
- v->promotionPieceTypes = {};
+ v->promotionRegion[WHITE] = Rank5BB;
+ v->promotionRegion[BLACK] = Rank1BB;
v->doubleStep = false;
v->castling = false;
v->promotedPieceType[SHOGI_PAWN] = GOLD;
v->add_piece(LANCE, 'l');
v->add_piece(SHOGI_KNIGHT, 'n');
v->startFen = "p+nks+l/5/5/5/+LSK+NP[-] w 0 1";
- v->promotionRank = RANK_1;
+ v->promotionRegion[WHITE] = AllSquares;
+ v->promotionRegion[BLACK] = AllSquares;
v->mandatoryPiecePromotion = true;
v->pieceDemotion = true;
v->dropPromoted = true;
Variant* v = kyotoshogi_variant()->init();
v->maxFile = FILE_D;
v->startFen = "kb+r+l/p3/4/3P/+L+RBK[-] w 0 1";
- v->promotionRank = RANK_1;
+ v->promotionRegion[WHITE] = AllSquares;
+ v->promotionRegion[BLACK] = AllSquares;
v->piecePromotionOnCapture = true;
v->promotedPieceType[LANCE] = SILVER;
v->promotedPieceType[BISHOP] = GOLD;
v->add_piece(WAZIR, 'g');
v->add_piece(KING, 'l');
v->startFen = "gle/1c1/1C1/ELG[-] w 0 1";
- v->promotionRank = RANK_4;
+ v->promotionRegion[WHITE] = Rank4BB;
+ v->promotionRegion[BLACK] = Rank1BB;
v->mandatoryPiecePromotion = true;
v->immobilityIllegal = false;
v->shogiPawnDropMateIllegal = false;
v->flagPiece = KING;
- v->whiteFlag = Rank4BB;
- v->blackFlag = Rank1BB;
+ v->flagRegion[WHITE] = Rank4BB;
+ v->flagRegion[BLACK] = Rank1BB;
v->dropNoDoubled = NO_PIECE_TYPE;
v->nFoldValue = VALUE_DRAW;
v->perpetualCheckIllegal = false;
v->maxRank = RANK_6;
v->maxFile = FILE_E;
v->startFen = "sgkgs/5/1ppp1/1PPP1/5/SGKGS[-] w 0 1";
- v->promotionRank = RANK_5;
+ v->promotionRegion[WHITE] = Rank5BB | Rank6BB;
+ v->promotionRegion[BLACK] = Rank2BB | Rank1BB;
return v;
}
// Judkins shogi
v->maxFile = FILE_F;
v->add_piece(SHOGI_KNIGHT, 'n');
v->startFen = "rbnsgk/5p/6/6/P5/KGSNBR[-] w 0 1";
- v->promotionRank = RANK_5;
+ v->promotionRegion[WHITE] = Rank5BB | Rank6BB;
+ v->promotionRegion[BLACK] = Rank2BB | Rank1BB;
v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
return v;
}
v->startFen = "rpckcpl/3f3/sssssss/2s1S2/SSSSSSS/3F3/LPCKCPR[-] w 0 1";
v->pieceDrops = true;
v->capturesToHand = true;
- v->promotionRank = RANK_6;
- v->promotionPieceTypes = {};
+ v->promotionRegion[WHITE] = Rank6BB | Rank7BB;
+ v->promotionRegion[BLACK] = Rank2BB | Rank1BB;
v->doubleStep = false;
v->castling = false;
v->promotedPieceType[SHOGI_PAWN] = CUSTOM_PIECES + 5; // swallow promotes to goose
v->maxFile = FILE_H;
v->add_piece(CUSTOM_PIECES, 'n', "fNsW");
v->startFen = "1nbgkgn1/1r4b1/pppppppp/8/8/PPPPPPPP/1B4R1/1NGKGBN1[-] w 0 1";
- v->promotionRank = RANK_6;
+ v->promotionRegion[WHITE] = Rank6BB | Rank7BB | Rank8BB;
+ v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
v->promotedPieceType[CUSTOM_PIECES] = GOLD;
v->mandatoryPiecePromotion = true;
return v;
v->maxFile = FILE_F;
v->remove_piece(BISHOP);
v->startFen = "rnqknr/pppppp/6/6/PPPPPP/RNQKNR w - - 0 1";
- v->promotionRank = RANK_6;
- v->promotionPieceTypes = {QUEEN, ROOK, KNIGHT};
+ v->promotionRegion[WHITE] = Rank6BB;
+ v->promotionRegion[BLACK] = Rank1BB;
+ v->promotionPieceTypes[WHITE] = {QUEEN, ROOK, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, KNIGHT};
v->doubleStep = false;
v->castling = false;
return v;
v->maxRank = RANK_5;
v->maxFile = FILE_E;
v->startFen = "rnbqk/ppppp/5/PPPPP/RNBQK w - - 0 1";
- v->promotionRank = RANK_5;
+ v->promotionRegion[WHITE] = Rank5BB;
+ v->promotionRegion[BLACK] = Rank1BB;
v->doubleStep = false;
v->castling = false;
return v;
v->remove_piece(QUEEN);
v->add_piece(CHANCELLOR, 'c');
v->startFen = "rnbckbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBCKBNR w KQkq - 0 1";
- v->promotionPieceTypes = {CHANCELLOR, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {CHANCELLOR, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {CHANCELLOR, ROOK, BISHOP, KNIGHT};
return v;
}
// Chigorin chess
v->pieceToCharTable = "PNBR............CKpnbrq............k";
v->add_piece(CHANCELLOR, 'c');
v->startFen = "rbbqkbbr/pppppppp/8/8/8/8/PPPPPPPP/RNNCKNNR w KQkq - 0 1";
- v->promotionPieceTypes = {QUEEN, CHANCELLOR, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {CHANCELLOR, ROOK, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {QUEEN, ROOK, BISHOP};
+ return v;
+ }
+ // Spartan chess
+ // https://www.chessvariants.com/rules/spartan-chess
+ Variant* spartan_variant() {
+ Variant* v = threekings_variant()->init();
+ v->add_piece(DRAGON, 'g');
+ v->add_piece(ARCHBISHOP, 'w');
+ v->add_piece(CUSTOM_PIECES, 'h', "fmFfcWimA");
+ v->add_piece(CUSTOM_PIECES + 1, 'l', "FAsmW");
+ v->add_piece(CUSTOM_PIECES + 2, 'c', "WD");
+ v->startFen = "lgkcckwl/hhhhhhhh/8/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1";
+ v->promotionPawnType[BLACK] = CUSTOM_PIECES;
+ v->promotionPawnTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->nMoveRuleTypes[BLACK] = piece_set(CUSTOM_PIECES);
+ v->promotionPieceTypes[BLACK] = {COMMONER, DRAGON, ARCHBISHOP, CUSTOM_PIECES + 1, CUSTOM_PIECES + 2};
+ v->promotionLimit[COMMONER] = 2;
+ v->enPassantRegion = 0;
+ v->extinctionPieceCount = 0;
+ v->extinctionPseudoRoyal = true;
+ v->dupleCheck = true;
return v;
}
// Shatar (Mongolian chess)
v->remove_piece(QUEEN);
v->add_piece(BERS, 'j');
v->startFen = "rnbjkbnr/ppp1pppp/8/3p4/3P4/8/PPP1PPPP/RNBJKBNR w - - 0 1";
- v->promotionPieceTypes = {BERS};
+ v->promotionPieceTypes[WHITE] = {BERS};
+ v->promotionPieceTypes[BLACK] = {BERS};
v->doubleStep = false;
v->castling = false;
v->extinctionValue = VALUE_DRAW; // Robado
v->reset_pieces();
v->add_piece(CLOBBER_PIECE, 'p');
v->startFen = "PpPpP/pPpPp/PpPpP/pPpPp/PpPpP/pPpPp w 0 1";
- v->promotionPieceTypes = {};
v->doubleStep = false;
v->castling = false;
v->stalemateValue = -VALUE_MATE;
v->reset_pieces();
v->add_piece(BREAKTHROUGH_PIECE, 'p');
v->startFen = "pppppppp/pppppppp/8/8/8/8/PPPPPPPP/PPPPPPPP w 0 1";
- v->promotionPieceTypes = {};
v->doubleStep = false;
v->castling = false;
v->stalemateValue = -VALUE_MATE;
v->flagPiece = BREAKTHROUGH_PIECE;
- v->whiteFlag = Rank8BB;
- v->blackFlag = Rank1BB;
+ v->flagRegion[WHITE] = Rank8BB;
+ v->flagRegion[BLACK] = Rank1BB;
return v;
}
// Ataxx
v->reset_pieces();
v->add_piece(CUSTOM_PIECES, 'p', "mDmNmA");
v->startFen = "P5p/7/7/7/7/7/p5P w 0 1";
- v->promotionPieceTypes = {};
v->pieceDrops = true;
v->doubleStep = false;
v->castling = false;
v->reset_pieces();
v->add_piece(IMMOBILE_PIECE, 'p');
v->startFen = "8/8/8/8/8/8/8/8[PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPpppppppppppppppppppppppppppppppp] w 0 1";
- v->promotionPieceTypes = {};
v->pieceDrops = true;
v->doubleStep = false;
v->castling = false;
v->mobilityRegion[WHITE][KING] = (Rank1BB | Rank2BB | Rank3BB) & (FileCBB | FileDBB | FileEBB);
v->mobilityRegion[BLACK][KING] = (Rank5BB | Rank6BB | Rank7BB) & (FileCBB | FileDBB | FileEBB);
v->kingType = WAZIR;
- v->promotionPieceTypes = {};
v->doubleStep = false;
v->castling = false;
v->stalemateValue = -VALUE_MATE;
v->add_piece(LANCE, 'l');
v->add_piece(SHOGI_KNIGHT, 'n');
v->startFen = "lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL[-] w 0 1";
- v->promotionRank = RANK_7;
+ v->promotionRegion[WHITE] = Rank7BB | Rank8BB | Rank9BB;
+ v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
v->promotedPieceType[LANCE] = GOLD;
v->promotedPieceType[SHOGI_KNIGHT] = GOLD;
return v;
v->add_piece(CUSTOM_PIECES + 3, 'g', "WfFbR"); // Yari gold
v->add_piece(CUSTOM_PIECES + 4, 's', "fKbR"); // Yari silver
v->startFen = "rnnkbbr/7/ppppppp/7/7/7/PPPPPPP/7/RBBKNNR[-] w 0 1";
- v->promotionRank = RANK_7;
+ v->promotionRegion[WHITE] = Rank7BB | Rank8BB | Rank9BB;
+ v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
v->promotedPieceType[SHOGI_PAWN] = CUSTOM_PIECES + 4;
v->promotedPieceType[CUSTOM_PIECES] = CUSTOM_PIECES + 3;
v->promotedPieceType[CUSTOM_PIECES + 1] = CUSTOM_PIECES + 3;
v->promotedPieceType[CUSTOM_PIECES + 2] = ROOK;
v->pieceDrops = true;
v->capturesToHand = true;
- v->promotionPieceTypes = {};
v->doubleStep = false;
v->castling = false;
v->dropNoDoubled = SHOGI_PAWN;
v->add_piece(KNIGHT, 'n');
v->add_piece(QUEEN, 'q');
v->startFen = "lnsgkqgsnl/1r6b1/pppppppppp/10/10/10/10/PPPPPPPPPP/1B6R1/LNSGQKGSNL[-] w 0 1";
- v->promotionRank = RANK_8;
+ v->promotionRegion[WHITE] = Rank8BB | Rank9BB | Rank10BB;
+ v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
v->promotedPieceType[CUSTOM_PIECES] = GOLD;
v->promotedPieceType[KNIGHT] = GOLD;
return v;
v->add_piece(ARCHBISHOP, 'a');
v->add_piece(CHANCELLOR, 'c');
v->startFen = "rnabqkbcnr/pppppppppp/10/10/10/10/PPPPPPPPPP/RNABQKBCNR w KQkq - 0 1";
- v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
// Capahouse
v->castlingQueensideFile = FILE_B;
v->add_piece(ARCHBISHOP, 'j');
v->startFen = "rjnbkqbnjr/pppppppppp/10/10/10/10/PPPPPPPPPP/RJNBKQBNJR w KQkq - 0 1";
- v->promotionPieceTypes = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
// Modern chess
v->pieceToCharTable = "PNBRQ..M.............Kpnbrq..m.............k";
v->maxRank = RANK_9;
v->maxFile = FILE_I;
- v->promotionRank = RANK_9;
+ v->promotionRegion[WHITE] = Rank9BB;
+ v->promotionRegion[BLACK] = Rank1BB;
+ v->doubleStepRegion[WHITE] = Rank2BB;
+ v->doubleStepRegion[BLACK] = Rank8BB;
v->castlingKingsideFile = FILE_G;
v->castlingQueensideFile = FILE_C;
v->add_piece(ARCHBISHOP, 'm');
v->startFen = "rnbqkmbnr/ppppppppp/9/9/9/9/9/PPPPPPPPP/RNBMKQBNR w KQkq - 0 1";
- v->promotionPieceTypes = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {ARCHBISHOP, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
// Chancellor chess
v->pieceToCharTable = "PNBRQ...........CKpnbrq...........ck";
v->maxRank = RANK_9;
v->maxFile = FILE_I;
- v->promotionRank = RANK_9;
+ v->promotionRegion[WHITE] = Rank9BB;
+ v->promotionRegion[BLACK] = Rank1BB;
+ v->doubleStepRegion[WHITE] = Rank2BB;
+ v->doubleStepRegion[BLACK] = Rank8BB;
v->castlingKingsideFile = FILE_G;
v->castlingQueensideFile = FILE_C;
v->add_piece(CHANCELLOR, 'c');
v->startFen = "rnbqkcnbr/ppppppppp/9/9/9/9/9/PPPPPPPPP/RNBQKCNBR w KQkq - 0 1";
- v->promotionPieceTypes = {CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
// Embassy chess
v->castlingQueensideFile = FILE_C;
v->add_piece(CENTAUR, 'c');
v->startFen = "rcnbqkbncr/pppppppppp/10/10/10/10/PPPPPPPPPP/RCNBQKBNCR w KQkq - 0 1";
- v->promotionPieceTypes = {CENTAUR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {CENTAUR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {CENTAUR, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
// Gustav III chess
v->castlingQueensideFile = FILE_D;
v->add_piece(AMAZON, 'a');
v->startFen = "arnbqkbnra/*pppppppp*/*8*/*8*/*8*/*8*/*PPPPPPPP*/ARNBQKBNRA w KQkq - 0 1";
- v->promotionPieceTypes = {AMAZON, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[WHITE] = {AMAZON, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {AMAZON, QUEEN, ROOK, BISHOP, KNIGHT};
return v;
}
// Jeson mor
v->reset_pieces();
v->add_piece(KNIGHT, 'n');
v->startFen = "nnnnnnnnn/9/9/9/9/9/9/9/NNNNNNNNN w - - 0 1";
- v->promotionPieceTypes = {};
v->doubleStep = false;
v->castling = false;
v->stalemateValue = -VALUE_MATE;
v->flagPiece = KNIGHT;
- v->whiteFlag = make_bitboard(SQ_E5);
- v->blackFlag = make_bitboard(SQ_E5);
+ v->flagRegion[WHITE] = make_bitboard(SQ_E5);
+ v->flagRegion[BLACK] = make_bitboard(SQ_E5);
v->flagMove = true;
return v;
}
v->add_piece(COMMONER, 'm');
v->add_piece(WAZIR, 'w');
v->startFen = "rnebmk1wbenr/1ppppp1pppp1/6f5/p5p4p/P5P4P/6F5/1PPPPP1PPPP1/RNEBMK1WBENR w - - 0 1";
- v->promotionPieceTypes = {FERS};
+ v->promotionPieceTypes[WHITE] = {FERS};
+ v->promotionPieceTypes[BLACK] = {FERS};
v->doubleStep = false;
v->castling = false;
v->extinctionValue = -VALUE_MATE;
v->add_piece(ARCHBISHOP, 'a');
v->add_piece(CHANCELLOR, 'c');
v->startFen = "r8r/1nbqkcabn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKCABN1/R8R w - - 0 1";
- v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
- v->promotionRank = RANK_8;
+ v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionRegion[WHITE] = Rank8BB | Rank9BB | Rank10BB;
+ v->promotionRegion[BLACK] = Rank3BB | Rank2BB | Rank1BB;
v->promotionLimit[ARCHBISHOP] = 1;
v->promotionLimit[CHANCELLOR] = 1;
v->promotionLimit[QUEEN] = 1;
v->promotionLimit[KNIGHT] = 2;
v->mandatoryPawnPromotion = false;
v->immobilityIllegal = true;
- v->doubleStepRank = RANK_3;
- v->doubleStepRankMin = RANK_3;
+ v->doubleStepRegion[WHITE] = Rank3BB;
+ v->doubleStepRegion[BLACK] = Rank8BB;
v->castling = false;
return v;
}
v->add_piece(CUSTOM_PIECES + 1, 'w', "CF");
v->add_piece(CUSTOM_PIECES + 2, 'l', "FDH");
v->startFen = "rw6wr/clbnqknbla/pppppppppp/10/10/10/10/PPPPPPPPPP/CLBNQKNBLA/RW6WR w - - 0 1";
- v->promotionPieceTypes.erase(KNIGHT);
- v->promotionPieceTypes.insert(CUSTOM_PIECES);
- v->promotionPieceTypes.insert(CUSTOM_PIECES + 1);
- v->promotionPieceTypes.insert(CUSTOM_PIECES + 2);
+ v->promotionPieceTypes[WHITE].erase(KNIGHT);
+ v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES);
+ v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES + 1);
+ v->promotionPieceTypes[WHITE].insert(CUSTOM_PIECES + 2);
+ v->promotionPieceTypes[BLACK] = v->promotionPieceTypes[WHITE];
v->promotionLimit[CUSTOM_PIECES] = 2;
v->promotionLimit[CUSTOM_PIECES + 1] = 2;
v->promotionLimit[CUSTOM_PIECES + 2] = 2;
v->add_piece(CHANCELLOR, 'm');
v->add_piece(CUSTOM_PIECES, 'c', "DAW"); // Champion
v->add_piece(CUSTOM_PIECES + 1, 'w', "CF"); // Wizard
- v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN};
- v->promotionRank = RANK_10;
- v->doubleStepRank = RANK_3;
- v->doubleStepRankMin = RANK_3;
+ v->promotionPieceTypes[WHITE] = {ARCHBISHOP, CHANCELLOR, QUEEN};
+ v->promotionPieceTypes[BLACK] = {ARCHBISHOP, CHANCELLOR, QUEEN};
+ v->promotionRegion[WHITE] = Rank10BB;
+ v->promotionRegion[BLACK] = Rank1BB;
+ v->doubleStepRegion[WHITE] = Rank3BB;
+ v->doubleStepRegion[BLACK] = Rank8BB;
v->castling = false;
return v;
}
v->castlingKingsideFile = FILE_I;
v->castlingQueensideFile = FILE_E;
v->castlingRank = RANK_2;
- v->promotionRank = RANK_9;
- v->promotionPieceTypes = {CUSTOM_PIECES + 1, CUSTOM_PIECES, QUEEN, ROOK, BISHOP, KNIGHT};
- v->doubleStepRank = RANK_3;
- v->doubleStepRankMin = RANK_3;
+ v->promotionRegion[WHITE] = Rank9BB | Rank10BB;
+ v->promotionRegion[BLACK] = Rank2BB | Rank1BB;
+ v->promotionPieceTypes[WHITE] = {CUSTOM_PIECES + 1, CUSTOM_PIECES, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->promotionPieceTypes[BLACK] = {CUSTOM_PIECES + 1, CUSTOM_PIECES, QUEEN, ROOK, BISHOP, KNIGHT};
+ v->doubleStepRegion[WHITE] = Rank3BB;
+ v->doubleStepRegion[BLACK] = Rank8BB;
+ return v;
+ }
+ // Troitzky Chess
+ // https://www.chessvariants.com/play/troitzky-chess
+ Variant* troitzky_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->maxRank = RANK_10;
+ v->maxFile = FILE_J;
+ v->startFen = "****qk****/**rnbbnr**/*pppppppp*/*8*/10/10/*8*/*PPPPPPPP*/**RNBBNR**/****QK**** w - - 0 1";
+ v->promotionRegion[WHITE] = make_bitboard(SQ_A6, SQ_B8, SQ_C9, SQ_D9, SQ_E10, SQ_F10, SQ_G9, SQ_H9, SQ_I8, SQ_J6);
+ v->promotionRegion[BLACK] = make_bitboard(SQ_A5, SQ_B3, SQ_C2, SQ_D2, SQ_E1, SQ_F1, SQ_G2, SQ_H2, SQ_I3, SQ_J5);
+ v->doubleStepRegion[WHITE] = Rank3BB;
+ v->doubleStepRegion[BLACK] = Rank8BB;
+ v->castling = false;
+ return v;
+ }
+ // Wolf chess
+ // https://en.wikipedia.org/wiki/Wolf_chess
+ Variant* wolf_variant() {
+ Variant* v = chess_variant_base()->init();
+ v->maxRank = RANK_10;
+ v->remove_piece(KNIGHT);
+ v->add_piece(CHANCELLOR, 'w'); // wolf
+ v->add_piece(ARCHBISHOP, 'f'); // fox
+ v->add_piece(CUSTOM_PIECES, 's', "fKifmnD"); // seargent
+ v->add_piece(CUSTOM_PIECES + 1, 'n', "NN"); // nightrider
+ v->add_piece(CUSTOM_PIECES + 2, 'e', "NNQ"); // elephant
+ v->startFen = "qwfrbbnk/pssppssp/1pp2pp1/8/8/8/8/1PP2PP1/PSSPPSSP/KNBBRFWQ w - - 0 1";
+ v->promotionPawnType[WHITE] = v->promotionPawnType[BLACK] = PAWN;
+ v->promotionPawnTypes[WHITE] = v->promotionPawnTypes[BLACK] = piece_set(PAWN) | piece_set(CUSTOM_PIECES);
+ v->promotionPieceTypes[WHITE] = {QUEEN, CHANCELLOR, ARCHBISHOP, ROOK, BISHOP};
+ v->promotionPieceTypes[BLACK] = {QUEEN, CHANCELLOR, ARCHBISHOP, ROOK, BISHOP};
+ v->promotedPieceType[PAWN] = CUSTOM_PIECES + 2;
+ v->promotionRegion[WHITE] = Rank10BB;
+ v->promotionRegion[BLACK] = Rank1BB;
+ v->doubleStepRegion[WHITE] = Rank2BB | make_bitboard(SQ_B3, SQ_C3, SQ_F3, SQ_G3);
+ v->doubleStepRegion[BLACK] = Rank9BB | make_bitboard(SQ_B8, SQ_C8, SQ_F8, SQ_G8);
+ v->enPassantTypes[WHITE] = v->enPassantTypes[BLACK] = piece_set(PAWN);
+ v->nMoveRuleTypes[WHITE] = v->nMoveRuleTypes[BLACK] = piece_set(PAWN) | piece_set(CUSTOM_PIECES);
+ v->castling = false;
return v;
}
// Shako
v->add_piece(FERS_ALFIL, 'e');
v->add_piece(CANNON, 'c');
v->startFen = "c8c/ernbqkbnre/pppppppppp/10/10/10/10/PPPPPPPPPP/ERNBQKBNRE/C8C w KQkq - 0 1";
- v->promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT, CANNON, FERS_ALFIL };
- v->promotionRank = RANK_10;
+ v->promotionPieceTypes[WHITE] = { QUEEN, ROOK, BISHOP, KNIGHT, CANNON, FERS_ALFIL };
+ v->promotionPieceTypes[BLACK] = { QUEEN, ROOK, BISHOP, KNIGHT, CANNON, FERS_ALFIL };
+ v->promotionRegion[WHITE] = Rank10BB;
+ v->promotionRegion[BLACK] = Rank1BB;
v->castlingKingsideFile = FILE_H;
v->castlingQueensideFile = FILE_D;
v->castlingRank = RANK_2;
- v->doubleStepRank = RANK_3;
- v->doubleStepRankMin = RANK_3;
+ v->doubleStepRegion[WHITE] = Rank3BB;
+ v->doubleStepRegion[BLACK] = Rank8BB;
return v;
}
// Clobber 10x10
add("fischerandom", chess960_variant());
add("nocastle", nocastle_variant());
add("armageddon", armageddon_variant());
+ add("torpedo", torpedo_variant());
+ add("berolina", berolina_variant());
+ add("pawnsideways", pawnsideways_variant());
+ add("pawnback", pawnback_variant());
+ add("legan", legan_variant());
add("fairy", fairy_variant()); // fairy variant used for endgame code initialization
add("makruk", makruk_variant());
add("makpong", makpong_variant());
add("gardner", gardner_variant());
add("almost", almost_variant());
add("chigorin", chigorin_variant());
+ add("spartan", spartan_variant());
add("shatar", shatar_variant());
add("coregal", coregal_variant());
add("clobber", clobber_variant());
add("opulent", opulent_variant());
add("tencubed", tencubed_variant());
add("omicron", omicron_variant());
+ add("troitzky", troitzky_variant());
+ add("wolf", wolf_variant());
add("shako", shako_variant());
add("clobber10", clobber10_variant());
add("flipello10", flipello10_variant());
std::string pieceToCharSynonyms = std::string(PIECE_NB, ' ');
std::string startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
Bitboard mobilityRegion[COLOR_NB][PIECE_TYPE_NB] = {};
- Rank promotionRank = RANK_8;
- std::set<PieceType, std::greater<PieceType> > promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT };
+ Bitboard promotionRegion[COLOR_NB] = {Rank8BB, Rank1BB};
+ PieceType promotionPawnType[COLOR_NB] = {PAWN, PAWN};
+ PieceSet promotionPawnTypes[COLOR_NB] = {piece_set(PAWN), piece_set(PAWN)};
+ std::set<PieceType, std::greater<PieceType> > promotionPieceTypes[COLOR_NB] = {{ QUEEN, ROOK, BISHOP, KNIGHT },
+ { QUEEN, ROOK, BISHOP, KNIGHT }};
bool sittuyinPromotion = false;
int promotionLimit[PIECE_TYPE_NB] = {}; // 0 means unlimited
PieceType promotedPieceType[PIECE_TYPE_NB] = {};
bool blastOnCapture = false;
bool petrifyOnCapture = false;
bool doubleStep = true;
- Rank doubleStepRank = RANK_2;
- Rank doubleStepRankMin = RANK_2;
+ Bitboard doubleStepRegion[COLOR_NB] = {Rank2BB, Rank7BB};
+ Bitboard tripleStepRegion[COLOR_NB] = {};
Bitboard enPassantRegion = AllSquares;
+ PieceSet enPassantTypes[COLOR_NB] = {piece_set(PAWN), piece_set(PAWN)};
bool castling = true;
bool castlingDroppedPiece = false;
File castlingKingsideFile = FILE_G;
bool freeDrops = false;
// game end
+ PieceSet nMoveRuleTypes[COLOR_NB] = {piece_set(PAWN), piece_set(PAWN)};
int nMoveRule = 50;
int nFoldRule = 3;
Value nFoldValue = VALUE_DRAW;
Value extinctionValue = VALUE_NONE;
bool extinctionClaim = false;
bool extinctionPseudoRoyal = false;
+ bool dupleCheck = false;
std::set<PieceType> extinctionPieceTypes = {};
int extinctionPieceCount = 0;
int extinctionOpponentPieceCount = 0;
PieceType flagPiece = NO_PIECE_TYPE;
- Bitboard whiteFlag = 0;
- Bitboard blackFlag = 0;
+ Bitboard flagRegion[COLOR_NB] = {};
bool flagMove = false;
bool checkCounting = false;
int connectN = 0;
bool shogiStylePromotions = false;
void add_piece(PieceType pt, char c, std::string betza = "", char c2 = ' ') {
+ // Avoid ambiguous definition by removing existing piece with same letter
+ size_t idx;
+ if ((idx = pieceToChar.find(toupper(c))) != std::string::npos)
+ remove_piece(PieceType(idx));
+ // Now add new piece
pieceToChar[make_piece(WHITE, pt)] = toupper(c);
pieceToChar[make_piece(BLACK, pt)] = tolower(c);
pieceToCharSynonyms[make_piece(WHITE, pt)] = toupper(c2);
pieceToCharSynonyms[make_piece(WHITE, pt)] = ' ';
pieceToCharSynonyms[make_piece(BLACK, pt)] = ' ';
pieceTypes.erase(pt);
+ // erase from promotion types to ensure consistency
+ promotionPieceTypes[WHITE].erase(pt);
+ promotionPieceTypes[BLACK].erase(pt);
}
void reset_pieces() {
pieceToChar = std::string(PIECE_NB, ' ');
pieceToCharSynonyms = std::string(PIECE_NB, ' ');
pieceTypes.clear();
+ // clear promotion types to ensure consistency
+ promotionPieceTypes[WHITE].clear();
+ promotionPieceTypes[BLACK].clear();
}
// Reset values that always need to be redefined
// Pre-calculate derived properties
Variant* conclude() {
+ // Enforce consistency to allow runtime optimizations
+ if (!doubleStep)
+ doubleStepRegion[WHITE] = doubleStepRegion[BLACK] = 0;
+ if (!doubleStepRegion[WHITE] && !doubleStepRegion[BLACK])
+ doubleStep = false;
+
fastAttacks = std::all_of(pieceTypes.begin(), pieceTypes.end(), [this](PieceType pt) {
return ( pt < FAIRY_PIECES
|| pt == COMMONER || pt == IMMOBILE_PIECE
# [File]: denotes a file of the board [1-12, a-i]
# [int]: any natural number [0, 1, ...]
# [PieceType]: a piece type [letters defined for pieces, e.g., p]
+# [PieceSet]: multiple piece types [letters defined for pieces, e.g., nbrq]
# [Bitboard]: list of squares [e.g., d4 e4 d5 e5]. * can be used as wildcard for files (e.g., *1 is the first rank)
# [Value]: game result for the side to move [win, loss, draw]
# [MaterialCounting]: material couting rules for adjudication [janggi, unweighted, whitedrawodds, blackdrawodds, none]
# startFen: FEN of starting position (default: rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1)
# mobilityRegion: the mobility area can be defined via options specific to color and piece,
# .e.g., mobilityRegionWhiteRook, mobilityRegionBlackJanggiElephant, etc. [Bitboard]
-# promotionRank: relative rank required to reach for promotion [Rank] (default: 8)
-# promotionPieceTypes: pawn promotion options using their one-letter representations (default: nbrq)
+# pawnTypes: define pieces considered as "pawns" for promotion, en passant, and n move rule [PieceSet] (default: p)
+# see promotionPawnTypes, enPassantTypes, and nMoveRuleTypes for more specific overrides.
+# promotionRegionWhite: region where promotions are allowed for white [Bitboard] (default: *8)
+# promotionRegionBlack: region where promotions are allowed for black [Bitboard] (default: *1)
+# promotionPawnTypes: promotion pawn types for both colors [PieceSet] (default: p)
+# promotionPawnTypesWhite: white promotion pawn types [PieceSet] (default: p)
+# promotionPawnTypesBlack: black promotion pawn types [PieceSet] (default: p)
+# promotionPieceTypes: pawn promotion options [PieceSet] (default: nbrq)
+# promotionPieceTypesWhite: white pawn promotion options [PieceSet] (default: nbrq)
+# promotionPieceTypesBlack: black pawn promotion options [PieceSet] (default: nbrq)
# sittuyinPromotion: enable Sittuyin-style pawn promotion [bool] (default: false)
# promotionLimit: maximum number of pieces of a type, e.g., q:1 r:2 (default: )
# promotedPieceType: mapping between unpromoted and promoted non-pawn piece types, e.g., p:g s:g (default: )
# blastOnCapture: captures explode all adjacent non-pawn pieces (e.g., atomic chess) [bool] (default: false)
# petrifyOnCapture: non-pawn pieces are turned into wall squares when capturing [bool] (default: false)
# doubleStep: enable pawn double step [bool] (default: true)
-# doubleStepRank: relative rank from where pawn double steps are allowed [Rank] (default: 2)
-# doubleStepRankMin: earlist relative rank from where pawn double steps are allowed [Rank] (default: 2)
+# doubleStepRegionWhite: region where pawn double steps are allowed for white [Bitboard] (default: *2)
+# doubleStepRegionBlack: region where pawn double steps are allowed for black [Bitboard] (default: *2)
+# tripleStepRegionWhite: region where pawn triple steps are allowed for white [Bitboard] (default: -)
+# tripleStepRegionBlack: region where pawn triple steps are allowed for black [Bitboard] (default: -)
# enPassantRegion: define region (target squares) where en passant is allowed after double steps [Bitboard]
+# enPassantTypes: define pieces able to capture en passant [PieceSet] (default: p)
+# enPassantTypesWhite: define white pieces able to capture en passant [PieceSet] (default: p)
+# enPassantTypesBlack: define black pieces able to capture en passant [PieceSet] (default: p)
# castling: enable castling [bool] (default: true)
# castlingDroppedPiece: enable castling with dropped rooks/kings [bool] (default: false)
# castlingKingsideFile: destination file of king after kingside castling [File] (default: g)
# dropOppositeColoredBishop: dropped bishops have to be on opposite-colored squares [bool] (default: false)
# dropPromoted: pieces may be dropped in promoted state [bool] (default: false)
# dropNoDoubled: specified piece type can not be dropped to the same file (e.g. shogi pawn) [PieceType] (default: -)
-# dropNoDoubledCount: specifies the count of already existing pieces for dropNoDoubled [PieceType] (default: 1)
+# dropNoDoubledCount: specifies the count of already existing pieces for dropNoDoubled [int] (default: 1)
# immobilityIllegal: pieces may not move to squares where they can never move from [bool] (default: false)
# gating: maintain squares on backrank with extra rights in castling field of FEN [bool] (default: false)
# arrowGating: gating of wall squares in Game of the Amazons style [bool] (default: false)
# flyingGeneral: disallow general face-off like in xiangqi [bool] (default: false)
# soldierPromotionRank: restrict soldier to shogi pawn movements until reaching n-th rank [bool] (default: 1)
# flipEnclosedPieces: change color of pieces that are enclosed by a drop [EnclosingRule] (default: none)
+# nMoveRuleTypes: define pieces resetting n move rule [PieceSet] (default: p)
+# nMoveRuleTypesWhite: define white pieces resetting n move rule [PieceSet] (default: p)
+# nMoveRuleTypesBlack: define black pieces resetting n move rule [PieceSet] (default: p)
# nMoveRule: move count for 50/n-move rule [int] (default: 50)
# nFoldRule: move count for 3/n-fold repetition rule [int] (default: 3)
# nFoldValue: result in case of 3/n-fold repetition [Value] (default: draw)
# extinctionValue: result when one of extinctionPieceTypes is extinct [Value] (default: none)
# extinctionClaim: extinction of opponent pieces can only be claimed as side to move [bool] (default: false)
# extinctionPseudoRoyal: treat the last extinction piece like a royal piece [bool] (default: false)
+# dupleCheck: when all pseudo-royal pieces are attacked, it counts as a check [bool] (default: false)
# extinctionPieceTypes: list of piece types for extinction rules, e.g., pnbrq (* means all) (default: )
# extinctionPieceCount: piece count at which the game is decided by extinction rule (default: 0)
# extinctionOpponentPieceCount: opponent piece count required to adjudicate by extinction rule (default: 0)
# flagPiece: piece type for capture the flag win rule [PieceType] (default: -)
-# whiteFlag: white's target region for capture the flag win rule [Bitboard] (default: )
-# blackFlag: black's target region for capture the flag win rule [Bitboard] (default: )
+# flagRegionWhite: white's target region for capture the flag win rule [Bitboard] (default: )
+# flagRegionBlack: black's target region for capture the flag win rule [Bitboard] (default: )
# flagMove: black gets one more move after white captures the flag [bool] (default: false)
# checkCounting: enable check count win rule (check count is communicated via FEN, see 3check) [bool] (default: false)
# connectN: number of aligned pieces for win [int] (default: 0)
# startFen = rbsgk/4p/5/P4/KGSBR[-] w 0 1
# pieceDrops = true
# capturesToHand = true
-# promotionRank = 5
+# promotionRegionWhite = *5
+# promotionRegionBlack = *1
# doubleStep = false
# castling = false
# promotedPieceType = p:g s:g b:h r:d
# nFoldValue = loss
# nFoldValueAbsolute = true
+# pawns with extra sideways and backwards movement
+# example for defining a custom pawn type
+# resetting the original "p" piece with "pawn = -" is optional as already done implicitly
+[allwayspawns:chess]
+customPiece1 = p:mWfceFifmnD
+pawnTypes = p
+
# Hybrid variant of three-check chess and crazyhouse, using crazyhouse as a template
[3check-crazyhouse:crazyhouse]
startFen = rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR[] w KQkq - 3+3 0 1
[atomic-giveaway-hill:giveaway]
blastOnCapture = true
flagPiece = k
-whiteFlag = d4 e4 d5 e5
-blackFlag = d4 e4 d5 e5
+flagRegionWhite = d4 e4 d5 e5
+flagRegionBlack = d4 e4 d5 e5
# Crazyhouse with mandatory captures, using crazyhouse as a template
[coffeehouse:crazyhouse]
maxRank = 10
maxFile = 10
startFen = rnbqkgvbnr/ppppwwpppp/4pp4/10/10/10/10/4PP4/PPPPWWPPPP/RNBVGKQBNR w - - 0 1
-promotionRank = 10
+promotionRegionWhite = *10
+promotionRegionBlack = *1
promotionPieceTypes = q
doubleStep = false
castling = false
# Semi-torpedo chess
[semitorpedo:chess]
-doubleStepRank = 3
+doubleStepRegionWhite = *2 *3
+doubleStepRegionBlack = *7 *6
# This variant is similar to Capablanca Chess, but with two archbishops and no chancellor piece.
[gemini:janus]
castling = false
stalemateValue = loss
flagPiece = q
-whiteFlag = *8
-blackFlag = *1
+flagRegionWhite = *8
+flagRegionBlack = *1
[tictactoe]
maxRank = 3
archbishop = a
chancellor = m
fers = f
-promotionRank = 6
+promotionRegionWhite = *6 *7 *8
+promotionRegionBlack = *3 *2 *1
promotionLimit = g:1 a:1 m:1 q:1
promotionPieceTypes = -
promotedPieceType = p:c n:g b:a r:m f:q
promotionPieceTypes = qh
startFen = lhaykahl/8/pppppppp/8/8/8/PPPPPPPP/RNBQKBNR w KQ - 0 1
flagPiece = k
-whiteFlag = *8
-blackFlag = *1
+flagRegionWhite = *8
+flagRegionBlack = *1
# Ordamirror
# https://vchess.club/#/variants/Ordamirror
promotionPieceTypes = lhaf
startFen = lhafkahl/8/pppppppp/8/8/PPPPPPPP/8/LHAFKAHL w - - 0 1
flagPiece = k
-whiteFlag = *8
-blackFlag = *1
+flagRegionWhite = *8
+flagRegionBlack = *1
# Hybrid variant of Gothic-chess and crazyhouse, using Capablanca as a template
[gothhouse:capablanca]
capturesToHand = false
blackDropRegion = *5
flagPiece = k
-whiteFlag = *8
-blackFlag = *1
+flagRegionWhite = *8
+flagRegionBlack = *1
# Capture chess
# https://vchess.club/#/variants/Capture
# Crossing chess
# https://vchess.club/#/variants/Crossing
[crossing:kingofthehill]
-whiteFlag = *5
-blackFlag = *4
+flagRegionWhite = *5
+flagRegionBlack = *4
# 4x5 Chess
# https://greenchess.net/rules.php?v=4x5-chess --> Solved draw
[4x6chess:gardner]
maxRank = 6
maxFile = d
-promotionRank = 6
+promotionRegionWhite = *6
+promotionRegionBlack = *1
startFen = rnbk/pppp/4/4/PPPP/RNBK w - - 0 1
# 5x6 chess
[5x6chess:gardner]
maxRank = 6
maxFile = e
-promotionRank = 6
+promotionRegionWhite = *6
+promotionRegionBlack = *1
startFen = rnbqk/ppppp/5/5/PPPPP/RNBQK w - - 0 1
# Active chess
extinctionPseudoRoyal = true
maxRank = 6
maxFile = f
-promotionRank = 6
+promotionRegionWhite = *6
+promotionRegionBlack = *1
doubleStep = false
startFen = rbqkbr/pppppp/6/6/PPPPPP/RBQKBR w - - 0 1
stalemateValue = loss
nFoldValue = loss
flagPiece = k
-whiteFlag = *8
-blackFlag = *1
+flagRegionWhite = *8
+flagRegionBlack = *1
flyingGeneral = true
# Shinobi Chess
fers = m
shogiKnight = h
lance = l
-promotionRank = 7
+promotionRegionWhite = *7 *8
+promotionRegionBlack = *2 *1
promotionPieceTypes = -
promotedPieceType = p:c m:b h:n l:r
mandatoryPiecePromotion = true
blackDropRegion = *5 *6 *7 *8
immobilityIllegal = true
flagPiece = k
-whiteFlag = *8
-blackFlag = *1
+flagRegionWhite = *8
+flagRegionBlack = *1
# Wildebeest
# https://vchess.club/#/variants/Wildebeest
maxFile = k
customPiece1 = c:C
customPiece2 = w:NC
+doubleStepRegionWhite = *2 *3
+doubleStepRegionBlack = *9 *8
+tripleStepRegionWhite = *2
+tripleStepRegionBlack = *9
pieceToCharTable = PNBRQ.......C....WKpnbrq.......c....wk
startFen = rnccwkqbbnr/ppppppppppp/11/11/11/11/11/11/PPPPPPPPPPP/RNBBQKWCCNR w KQkq - 0 1
promotionPieceTypes = qw
-promotionRank = 9
+promotionRegionWhite = *9 *10
+promotionRegionBlack = *2 *1
mandatoryPawnPromotion = false
castling = false
fers = f
wazir = v
centaur = t
-promotionRank = 7
+promotionRegionWhite = *7 *8 *9
+promotionRegionBlack = *3 *2 *1
promotedPieceType = p:g n:o b:h r:d a:c v:m f:q s:w u:t
doubleStep = false
perpetualCheckIllegal = true
nFoldValue = loss
stalemateValue = loss
flagPiece = k
-whiteFlag = *9
-blackFlag = *1
+flagRegionWhite = *9
+flagRegionBlack = *1
# 5x5 breakthrough
[breakthrough5:breakthrough]
maxFile = 5
maxRank = 5
startFen = ppppp/ppppp/5/PPPPP/PPPPP w 0 1
-whiteFlag = *5
-blackFlag = *1
+flagRegionWhite = *5
+flagRegionBlack = *1
# 6x6 breakthrough
[breakthrough6:breakthrough]
maxFile = 6
maxRank = 6
startFen = pppppp/pppppp/6/6/PPPPPP/PPPPPP w 0 1
-whiteFlag = *6
-blackFlag = *1
+flagRegionWhite = *6
+flagRegionBlack = *1
# 7x7 breakthrough
[breakthrough7:breakthrough]
maxFile = 7
maxRank = 7
startFen = ppppppp/ppppppp/7/7/7/PPPPPPP/PPPPPPP w 0 1
-whiteFlag = *7
-blackFlag = *1
+flagRegionWhite = *7
+flagRegionBlack = *1
# Mansindam (Pantheon tale)
# A variant that combines drop rule and powerful pieces, and there is no draw
bers = t
customPiece1 = i:BNW
customPiece2 = s:RNF
-promotionRank = 7
+promotionRegionWhite = *7 *8 *9
+promotionRegionBlack = *3 *2 *1
doubleStep = false
castling = false
promotedPieceType = p:g n:e b:h r:t c:i m:s
nMoveRule = 0
nFoldValue = loss
flagPiece = k
-whiteFlag = *9
-blackFlag = *1
+flagRegionWhite = *9
+flagRegionBlack = *1
immobilityIllegal = true
mandatoryPiecePromotion = true
archbishop = a
chancellor = m
fers = f
-promotionRank = 6
+promotionRegionWhite = *6 *7 *8
+promotionRegionBlack = *3 *2 *1
promotionLimit = g:1 a:1 m:1 q:1
promotionPieceTypes = -
promotedPieceType = p:c n:g b:a r:m f:q
silver = y
promotionPieceTypes = qh
flagPiece = k
-whiteFlag = *8
-blackFlag = *1
+flagRegionWhite = *8
+flagRegionBlack = *1
[diana:losalamos]
pieceToCharTable = PNBRQ................Kpnbrq................k
expect perft.exp losalamos startpos 5 191846 > /dev/null
expect perft.exp losalamos "fen 6/2P3/6/1K1k2/6/6 w - - 0 1" 6 187431 > /dev/null
# fairy
+ expect perft.exp torpedo startpos 4 209719 > /dev/null
+ expect perft.exp torpedo "fen rnbqkbnr/1ppppppp/8/6P1/p7/8/PPPPPP1P/RNBQKBNR w KQkq - 0 1" 4 232819 > /dev/null
+ expect perft.exp berolina "fen rnbqkbnr/pppp1ppp/8/2p5/5P2/8/PPP1PPPP/RNBQKBNR w KQkq c5d6 2 2" 3 46643 > /dev/null
+ expect perft.exp berolina "fen k7/6P1/8/8/8/2K2p2/4p3/8 w - - 0 1" 3 1983 > /dev/null
+ expect perft.exp berolina "fen rnbqkbnr/pp1p1ppp/8/2pPp3/8/8/PP1PPPPP/RNBQKBNR w KQkq d6c5 0 1" 2 1047 > /dev/null
+ expect perft.exp pawnsideways startpos 3 10022 > /dev/null
+ expect perft.exp pawnback startpos 3 9222 > /dev/null
+ expect perft.exp legan startpos 4 8138 > /dev/null
expect perft.exp makruk startpos 4 273026 > /dev/null
expect perft.exp cambodian startpos 4 361719 > /dev/null
expect perft.exp cambodian "fen r1s1ks1r/3nm3/pppNpppp/3n4/5P2/PPPPPNPP/8/R1SKMS1R b DEe 0 0 5" 2 72 > /dev/null
expect perft.exp sittuyin "fen 2r5/6k1/6p1/3s2P1/3npR2/8/p2N2F1/3K4[] w - - 1 50" 4 373984 > /dev/null
expect perft.exp sittuyin "fen 8/6s1/5P2/3n4/pR2K2S/1P6/1k4p1/8[] w - - 1 50" 4 268869 > /dev/null
expect perft.exp sittuyin "fen 1k5K/3r2P1/8/8/8/8/8/8[] w - - 0 1" 5 68662 > /dev/null
+ expect perft.exp chigorin "fen 8/7P/2k5/8/8/5K2/p7/8 w - - 0 1" 2 120 > /dev/null
+ expect perft.exp spartan startpos 3 14244 > /dev/null
+ # duple check & mate
+ expect perft.exp spartan "fen k6k/hh2Q2h/8/8/8/8/8/4K3 w - - 0 1" 3 6130 > /dev/null
+ # self duple check with promotions
+ expect perft.exp spartan "fen 8/8/8/8/6Q1/8/2h3h1/4K1k1 b - - 0 1" 3 3456 > /dev/null
expect perft.exp shatar startpos 4 177344 > /dev/null
expect perft.exp shatranj startpos 4 68122 > /dev/null
expect perft.exp amazon startpos 4 318185 > /dev/null
expect perft.exp losers startpos 4 152955 > /dev/null
expect perft.exp kinglet startpos 4 197742 > /dev/null
expect perft.exp threekings startpos 4 199514 > /dev/null
+
# pockets
expect perft.exp crazyhouse startpos 4 197281 > /dev/null
expect perft.exp crazyhouse "fen 2k5/8/8/8/8/8/8/4K3[QRBNPqrbnp] w - - 0 1" 2 75353 > /dev/null
expect perft.exp centaur startpos 3 24490 > /dev/null
expect perft.exp gustav3 startpos 4 331659 > /dev/null
expect perft.exp omicron startpos 4 967381 > /dev/null
+ expect perft.exp troitzky startpos 3 8766 > /dev/null
+ expect perft.exp wolf startpos 3 13722 > /dev/null
+ expect perft.exp wolf "fen 8/k5SP/8/8/8/8/8/8/8/7K w - - 0 1" 4 10587 > /dev/null
expect perft.exp shako "fen 4kc3c/ernbq1b1re/ppp3p1pp/3p2pp2/4p5/5P4/2PN2P3/PP1PP2PPP/ER1BQKBNR1/5C3C w KQ - 0 9" 3 26325 > /dev/null
expect perft.exp shako "fen 4ncr1k1/1cr2P4/pp2p2pp1/P7PN/2Ep1p4/B3P1eN2/2P1n1P3/1B1P1K4/9p/5C2CR w - - 0 1" 3 180467 > /dev/null
expect perft.exp shako "fen r5k3/4q2c2/1ebppnp3/1pp3BeEQ/10/2PE2P3/1P3P4/5NP2P/rR3KB3/7C2 w Q - 3 35" 2 4940 > /dev/null