[](https://travis-ci.org/ianfab/Fairy-Stockfish)
[](https://ci.appveyor.com/project/ianfab/Fairy-Stockfish/branch/master)
-Fairy-Stockfish is a UCI/USI chess variant engine derived from [Stockfish](https://github.com/official-stockfish/Stockfish/) designed for the support of fairy chess variants and easy extensibility with more games. It can play various historical, regional, and modern chess variants as well [games with user-defined rules](https://github.com/ianfab/Fairy-Stockfish/wiki/Variant-configuration).
+Fairy-Stockfish is a UCI/USI/XBoard chess variant engine derived from [Stockfish](https://github.com/official-stockfish/Stockfish/) designed for the support of fairy chess variants and easy extensibility with more games. It can play various historical, regional, and modern chess variants as well [games with user-defined rules](https://github.com/ianfab/Fairy-Stockfish/wiki/Variant-configuration).
The goal of the project is to create an engine supporting a large variety of chess-like games, equipped with the powerful search of Stockfish. It is complementary to Stockfish forks more specialized for certain chess variants, such as [multi-variant Stockfish](https://github.com/ddugovic/Stockfish), [Seirawan-Stockfish](https://github.com/ianfab/Seirawan-Stockfish), [Makruk-Stockfish](https://github.com/ianfab/Makruk-Stockfish), etc., supporting many more variants with the tradeoff of slightly lower performance compared to a specialized implementation.
The games currently supported besides chess are listed below. Fairy-Stockfish can also play user-defined variants loaded via a variant configuration file, see the file `src/variants.ini`.
### Regional and historical games
-- [Shatranj](https://en.wikipedia.org/wiki/Shatranj), [Courier](https://en.wikipedia.org/wiki/Courier_chess)
+- [Xiangqi](https://en.wikipedia.org/wiki/Xiangqi), [Minixiangqi](http://mlwi.magix.net/bg/minixiangqi.htm)
+- [Shogi](https://en.wikipedia.org/wiki/Shogi)
- [Makruk](https://en.wikipedia.org/wiki/Makruk), [Ouk Chatrang](https://en.wikipedia.org/wiki/Makruk#Cambodian_chess), [Kar Ouk](https://en.wikipedia.org/wiki/Makruk#Cambodian_chess), [ASEAN](http://hgm.nubati.net/rules/ASEAN.html), Ai-Wok
- [Sittuyin](https://en.wikipedia.org/wiki/Sittuyin)
- [Shatar](https://en.wikipedia.org/wiki/Shatar), [Jeson Mor](https://en.wikipedia.org/wiki/Jeson_Mor)
-- [Shogi](https://en.wikipedia.org/wiki/Shogi)
+- [Shatranj](https://en.wikipedia.org/wiki/Shatranj), [Courier](https://en.wikipedia.org/wiki/Courier_chess)
### Chess variants
- [Capablanca](https://en.wikipedia.org/wiki/Capablanca_Chess), [Janus](https://en.wikipedia.org/wiki/Janus_Chess), [Modern](https://en.wikipedia.org/wiki/Modern_Chess_(chess_variant)), [Chancellor](https://en.wikipedia.org/wiki/Chancellor_Chess), [Embassy](https://en.wikipedia.org/wiki/Embassy_Chess), [Gothic](https://www.chessvariants.com/large.dir/gothicchess.html), [Capablanca random chess](https://en.wikipedia.org/wiki/Capablanca_Random_Chess)
Magic BishopMagics[SQUARE_NB];
Magic CannonMagicsH[SQUARE_NB];
Magic CannonMagicsV[SQUARE_NB];
+Magic HorseMagics[SQUARE_NB];
+Magic ElephantMagics[SQUARE_NB];
namespace {
Bitboard RookTableH[0x11800]; // To store horizontalrook attacks
Bitboard RookTableV[0x4800]; // To store vertical rook attacks
Bitboard BishopTable[0x33C00]; // To store bishop attacks
- Bitboard CannonTableH[0x33C00]; // To store horizontal cannon attacks
- Bitboard CannonTableV[0x33C00]; // To store vertical cannon attacks
+ Bitboard CannonTableH[0x11800]; // To store horizontal cannon attacks
+ Bitboard CannonTableV[0x4800]; // To store vertical cannon attacks
+ Bitboard HorseTable[0x500]; // To store horse attacks
+ Bitboard ElephantTable[0x400]; // To store elephant attacks
#else
Bitboard RookTableH[0xA00]; // To store horizontal rook attacks
Bitboard RookTableV[0xA00]; // To store vertical rook attacks
Bitboard BishopTable[0x1480]; // To store bishop attacks
- Bitboard CannonTableH[0x11800]; // To store horizontal cannon attacks
- Bitboard CannonTableV[0x4800]; // To store vertical cannon attacks
+ Bitboard CannonTableH[0xA00]; // To store horizontal cannon attacks
+ Bitboard CannonTableV[0xA00]; // To store vertical cannon attacks
+ Bitboard HorseTable[0x240]; // To store horse attacks
+ Bitboard ElephantTable[0x1A0]; // To store elephant attacks
#endif
- enum MovementType { RIDER, HOPPER, LAZY_LEAPER };
+ enum MovementType { RIDER, HOPPER, LAME_LEAPER };
template <MovementType MT>
#ifdef PRECOMPUTED_MAGICS
template <MovementType MT>
Bitboard sliding_attack(std::vector<Direction> directions, Square sq, Bitboard occupied, Color c = WHITE) {
+ assert(MT != LAME_LEAPER);
Bitboard attack = 0;
return attack;
}
+
+ Bitboard lame_leaper_path(Direction d, Square s) {
+ Direction dr = d > 0 ? NORTH : SOUTH;
+ Direction df = (std::abs(d % NORTH) < NORTH / 2 ? d % NORTH : -(d % NORTH)) < 0 ? WEST : EAST;
+ Square to = s + d;
+ Bitboard b = 0;
+ if (!is_ok(to) || distance(s, to) >= 4)
+ return b;
+ while (s != to)
+ {
+ int diff = std::abs(file_of(to) - file_of(s)) - std::abs(rank_of(to) - rank_of(s));
+ if (diff > 0)
+ s += df;
+ else if (diff < 0)
+ s += dr;
+ else
+ s += df + dr;
+
+ if (s != to)
+ b |= s;
+ }
+ return b;
+ }
+
+ Bitboard lame_leaper_path(std::vector<Direction> directions, Square s) {
+ Bitboard b = 0;
+ for (Direction d : directions)
+ b |= lame_leaper_path(d, s);
+ return b;
+ }
+
+ Bitboard lame_leaper_attack(std::vector<Direction> directions, Square s, Bitboard occupied) {
+ Bitboard b = 0;
+ for (Direction d : directions)
+ {
+ Square to = s + d;
+ if (is_ok(to) && distance(s, to) < 4 && !(lame_leaper_path(d, s) & occupied))
+ b |= to;
+ }
+ return b;
+ }
}
{
const PieceInfo* pi = pieceMap.find(pt)->second;
+ if (pi->lameLeaper)
+ {
+ for (Direction d : pi->stepsCapture)
+ {
+ if ( d == 2 * SOUTH + WEST || d == 2 * SOUTH + EAST || d == SOUTH + 2 * WEST || d == SOUTH + 2 * EAST
+ || d == NORTH + 2 * WEST || d == NORTH + 2 * EAST || d == 2 * NORTH + WEST || d == 2 * NORTH + EAST)
+ AttackRiderTypes[pt] |= RIDER_HORSE;
+ if (d == 2 * NORTH_EAST || d == 2 * SOUTH_EAST || d == 2 * SOUTH_WEST || d == 2 * NORTH_WEST)
+ AttackRiderTypes[pt] |= RIDER_ELEPHANT;
+ }
+ for (Direction d : pi->stepsQuiet)
+ {
+ if ( d == 2 * SOUTH + WEST || d == 2 * SOUTH + EAST || d == SOUTH + 2 * WEST || d == SOUTH + 2 * EAST
+ || d == NORTH + 2 * WEST || d == NORTH + 2 * EAST || d == 2 * NORTH + WEST || d == 2 * NORTH + EAST)
+ MoveRiderTypes[pt] |= RIDER_HORSE;
+ if (d == 2 * NORTH_EAST || d == 2 * SOUTH_EAST || d == 2 * SOUTH_WEST || d == 2 * NORTH_WEST)
+ MoveRiderTypes[pt] |= RIDER_ELEPHANT;
+ }
+ }
for (Direction d : pi->sliderCapture)
{
if (d == NORTH_EAST || d == SOUTH_WEST || d == NORTH_WEST || d == SOUTH_EAST)
std::vector<Direction> RookDirectionsV = { NORTH, SOUTH};
std::vector<Direction> RookDirectionsH = { EAST, WEST };
std::vector<Direction> BishopDirections = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST };
+ std::vector<Direction> HorseDirections = {2 * SOUTH + WEST, 2 * SOUTH + EAST, SOUTH + 2 * WEST, SOUTH + 2 * EAST,
+ NORTH + 2 * WEST, NORTH + 2 * EAST, 2 * NORTH + WEST, 2 * NORTH + EAST };
+ std::vector<Direction> ElephantDirections = { 2 * NORTH_EAST, 2 * SOUTH_EAST, 2 * SOUTH_WEST, 2 * NORTH_WEST };
#ifdef PRECOMPUTED_MAGICS
init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH, RookMagicHInit);
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>(HorseTable, HorseMagics, HorseDirections, HorseMagicInit);
+ init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit);
#else
init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH);
init_magics<RIDER>(RookTableV, RookMagicsV, RookDirectionsV);
init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections);
init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH);
init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV);
+ init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections);
+ init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections);
#endif
for (Color c : { WHITE, BLACK })
if (is_ok(to) && distance(s, to) < 4)
{
PseudoAttacks[c][pt][s] |= to;
- LeaperAttacks[c][pt][s] |= to;
+ if (!pi->lameLeaper)
+ LeaperAttacks[c][pt][s] |= to;
}
}
for (Direction d : pi->stepsQuiet)
if (is_ok(to) && distance(s, to) < 4)
{
PseudoMoves[c][pt][s] |= to;
- LeaperMoves[c][pt][s] |= to;
+ if (!pi->lameLeaper)
+ LeaperMoves[c][pt][s] |= to;
}
}
PseudoAttacks[c][pt][s] |= sliding_attack<RIDER>(pi->sliderCapture, s, 0, c);
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
Magic& m = magics[s];
- m.mask = sliding_attack<MT == HOPPER ? RIDER : MT>(directions, s, 0) & ~edges;
+ m.mask = (MT == LAME_LEAPER ? lame_leaper_path(directions, s) : sliding_attack<MT == HOPPER ? RIDER : MT>(directions, s, 0)) & ~edges;
#ifdef LARGEBOARDS
m.shift = 128 - popcount(m.mask);
#else
b = size = 0;
do {
occupancy[size] = b;
- reference[size] = sliding_attack<MT>(directions, s, b);
+ reference[size] = MT == LAME_LEAPER ? lame_leaper_attack(directions, s, b) : sliding_attack<MT>(directions, s, b);
if (HasPext)
m.attacks[pext(b, m.mask)] = reference[size];
extern Magic BishopMagics[SQUARE_NB];
extern Magic CannonMagicsH[SQUARE_NB];
extern Magic CannonMagicsV[SQUARE_NB];
+extern Magic HorseMagics[SQUARE_NB];
+extern Magic ElephantMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_MAX);
template<RiderType R>
inline Bitboard rider_attacks_bb(Square s, Bitboard occupied) {
- assert(R == RIDER_BISHOP || R == RIDER_ROOK_H || R == RIDER_ROOK_V || R == RIDER_CANNON_H || R == RIDER_CANNON_V);
+ assert(R == RIDER_BISHOP || R == RIDER_ROOK_H || R == RIDER_ROOK_V || R == RIDER_CANNON_H || R == RIDER_CANNON_V
+ || R == RIDER_HORSE || R == RIDER_ELEPHANT);
const Magic& m = R == RIDER_ROOK_H ? RookMagicsH[s]
: R == RIDER_ROOK_V ? RookMagicsV[s]
: R == RIDER_CANNON_H ? CannonMagicsH[s]
: R == RIDER_CANNON_V ? CannonMagicsV[s]
+ : R == RIDER_HORSE ? HorseMagics[s]
+ : R == RIDER_ELEPHANT ? ElephantMagics[s]
: BishopMagics[s];
return m.attacks[m.index(occupied)];
}
b |= rider_attacks_bb<RIDER_ROOK_H>(s, occupied);
if (AttackRiderTypes[pt] & RIDER_ROOK_V)
b |= rider_attacks_bb<RIDER_ROOK_V>(s, occupied);
- if (AttackRiderTypes[pt] & RIDER_ROOK_H)
- b |= rider_attacks_bb<RIDER_ROOK_H>(s, occupied);
+ if (AttackRiderTypes[pt] & RIDER_CANNON_H)
+ b |= rider_attacks_bb<RIDER_CANNON_H>(s, occupied);
if (AttackRiderTypes[pt] & RIDER_CANNON_V)
b |= rider_attacks_bb<RIDER_CANNON_V>(s, occupied);
+ if (AttackRiderTypes[pt] & RIDER_HORSE)
+ b |= rider_attacks_bb<RIDER_HORSE>(s, occupied);
+ if (AttackRiderTypes[pt] & RIDER_ELEPHANT)
+ b |= rider_attacks_bb<RIDER_ELEPHANT>(s, occupied);
return b & PseudoAttacks[c][pt][s];
}
b |= rider_attacks_bb<RIDER_CANNON_H>(s, occupied);
if (MoveRiderTypes[pt] & RIDER_CANNON_V)
b |= rider_attacks_bb<RIDER_CANNON_V>(s, occupied);
+ if (MoveRiderTypes[pt] & RIDER_HORSE)
+ b |= rider_attacks_bb<RIDER_HORSE>(s, occupied);
+ if (MoveRiderTypes[pt] & RIDER_ELEPHANT)
+ b |= rider_attacks_bb<RIDER_ELEPHANT>(s, occupied);
return b & PseudoMoves[c][pt][s];
}
if (pos.captures_to_hand() && pos.count<KING>(Them) && pos.count<KING>(Us))
score -= KingProximity * distance(s, pos.square<KING>(Us)) * distance(s, pos.square<KING>(Them));
+ if (Pt == SOLDIER && pos.unpromoted_soldier(Us, s))
+ score -= make_score(PieceValue[MG][Pt], PieceValue[EG][Pt]) / 2;
+
if (Pt == BISHOP || Pt == KNIGHT)
{
// Bonus if piece is on an outpost square or can reach one
B(0xA00002102810020, 0xB1080240015408),
B(0x810080200806, 0x410440804080046),
};
+ Bitboard HorseMagicInit[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),
+ };
+ Bitboard ElephantMagicInit[SQUARE_NB] = {
+ B(0x64D2990200008, 0x4401880001C000),
+ B(0x29BAA00010020, 0x200000400800600),
+ B(0x3024240000000, 0x4100400010080),
+ B(0xA490A00480020, 0x20084001041010A4),
+ B(0x328C021008042, 0x100000000C10204),
+ B(0x1964090001018, 0x7002040148001205),
+ B(0x800302098404080, 0x4983020000000001),
+ B(0x8812244630A02080, 0x8200006204003C08),
+ B(0x41120231008000, 0x240441401020),
+ B(0x840091030C00040, 0x1400008200023400),
+ B(0x8001040E77030200, 0x100040090022000),
+ B(0x602022139D835040, 0x101002010025900),
+ B(0x405707C48400, 0x40010000008001),
+ B(0x982003456A82050, 0x60800820040030),
+ B(0x204184849200088, 0x101800004006),
+ B(0x300222470949200, 0x2A0800200200800),
+ B(0x400001211914000, 0x8200001407001),
+ B(0x2000008614831020, 0x4000020001404000),
+ B(0x84000024A2048048, 0x1200102000042),
+ B(0x424010A58422008, 0x88440242212A0110),
+ B(0x20020812C0C4408, 0x4121400000080010),
+ B(0x680200062042420, 0x2001100000800000),
+ B(0x200010060AEC855, 0x8083002040200000),
+ B(0x4000008BAA85810, 0x82000805C0200A90),
+ B(0x81450B200A025400, 0x4400101050000040),
+ B(0x820A2241409010, 0x888420030000),
+ B(0x909203000028, 0xC000004C00200041),
+ B(0x8021400A84880240, 0x100180002010020),
+ B(0x8001A20061410000, 0x14008499A000000),
+ B(0x8201444800A00080, 0x402010040588120),
+ B(0x100C06280020, 0x60010104840130),
+ B(0x520040800080044, 0x8220000080001402),
+ B(0x102021410040202, 0x2004400410006000),
+ B(0x5401832090020400, 0x300010020001),
+ B(0x180003105A84C108, 0x1012008800081000),
+ B(0x480C10210026904, 0xA006000004200418),
+ B(0x48050820210843A6, 0x108001004000C00),
+ B(0x1030101182206324, 0x4401008921502002),
+ B(0x40281060800800, 0x406000201260022),
+ B(0xC29002440040C820, 0x400001002008020),
+ B(0x40000400800241, 0xC220000000400280),
+ B(0x40880126014208, 0x2A8004C008940000),
+ B(0x121028100114080, 0x5010280481100082),
+ B(0x4000088280442, 0x908420140008041),
+ B(0x808C42400C0020, 0x3028100840801000),
+ B(0x4000000410078488, 0x501000000620000),
+ B(0x90080001421020A4, 0x4118400101060406),
+ B(0x280420004855, 0xD200100400820000),
+ B(0xA0063423444, 0x41002C15811008),
+ B(0x200061201808102, 0x4286969000200002),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x4001A04880402000, 0x8100824080000001),
+ B(0x60201D8300408190, 0x2010020920200000),
+ B(0x20018C04908019, 0x2010884002002040),
+ B(0x800000000C40810, 0x680100081150000D),
+ B(0x2002002000040040, 0x8810049000010600),
+ B(0x41618A0300040040, 0x21200200A421801),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x10208018C1020A20, 0x84C0432240610014),
+ B(0x5A04001400412854, 0x8A44006000010002),
+ B(0x13000C0810072432, 0x50049001021104),
+ B(0x400048801142130, 0x4C1204100226010C),
+ B(0x80001048, 0x408800104000080),
+ B(0x8104868204040412, 0x22244202000081),
+ B(0x8104868204040412, 0x22244202000081),
+ B(0x4140001000240440, 0x80209004410004E),
+ B(0x800800000100, 0xB111820100000002),
+ B(0x404240004220, 0x2110402802050080),
+ B(0x284010400004040, 0x100245002502020),
+ B(0x14880A100114010, 0x400208080010024),
+ B(0x4100004040440648, 0x10030D838041A80),
+ B(0x32004000210, 0x4010225C88014000),
+ B(0x2240180C0800002, 0x4151050280000100),
+ B(0x2010A12002000042, 0x189051442010000),
+ B(0x4060050080121883, 0x8250C10001000141),
+ B(0x10000000044100, 0x8401084010261009),
+ B(0xA00028040000, 0x2003224000002000),
+ B(0x2060009001000020, 0x1000432022020228),
+ B(0x404200000883080, 0x1080800848245000),
+ B(0x240000402080, 0xCA0820814210502),
+ B(0x200040200080A02C, 0xB890290400080000),
+ B(0x800000000300482, 0x9203008100100013),
+ B(0x8000210202042000, 0x22642104004C2400),
+ B(0x1040400805000401, 0x2A0300102C80010),
+ B(0x8010A01088020000, 0x122106105A06A030),
+ B(0x8000C00001010494, 0x130A1A20404120),
+ B(0x4B084010844290, 0x10A08008900840),
+ B(0x1180001802460000, 0xB08000034C82004),
+ B(0x4001880060028029, 0x204040002401000),
+ B(0x8021A0001308002A, 0x97001822040040),
+ B(0xC00000009A020AC1, 0x1000080900400),
+ B(0x60010110001990, 0x4000880900400000),
+ B(0x10290402401200, 0x230080402C08),
+ B(0x4220000219012000, 0x140204804100008),
+ B(0x1400200100002, 0x8E62200414128),
+ B(0x402808502004403, 0x20049100C0284520),
+ B(0xB30041004280280, 0x10020464DB200308),
+ B(0x440010800808, 0xA0102E295812100),
+ B(0x10008000B000, 0x2000058583220200),
+ B(0x2000844000000100, 0x2000024C60800800),
+ B(0x110000400100028, 0x24052304508004),
+ B(0x8458000000840004, 0x118006463400001),
+ B(0x804008000040050, 0x41044890228000),
+ B(0x20000050000400, 0x80A101824A00086),
+ B(0x600080404000020, 0x100007322480005),
+ B(0xD082200020020008, 0x642000630120001),
+ B(0x10000100040230, 0x8048114733320002),
+ B(0x20200442002A880A, 0x8200002CB4B8052),
+ B(0x290080000000, 0xA41297838F40D),
+ B(0x800205000080, 0xF221232039874400),
+ B(0x1444002004880C20, 0xC4100049144200),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0x281240881008, 0x204084004C101900),
+ B(0x1444002004880C20, 0xC4100049144200),
+ B(0x4500080000800, 0x280100002482C842),
+ B(0xC0010928430540, 0x92041902180),
+ B(0x1051001208A, 0x4900064800C20640),
+ B(0x882020418C00000, 0x30004040092A821),
+ B(0x224404002004268C, 0x202500204C7D254),
+ B(0x290080000000, 0xA41297838F40D),
+ };
#undef B
#endif
// Single and double pawn pushes, no promotions
if (Type != CAPTURES)
{
- emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces() & pos.board_bb());
+ emptySquares = (Type == QUIETS || Type == QUIET_CHECKS ? target : ~pos.pieces() & pos.board_bb(Us, PAWN));
Bitboard b1 = shift<Up>(pawnsNotOn7) & emptySquares;
Bitboard b2 = pos.double_step_enabled() ? shift<Up>(b1 & TRank3BB) & emptySquares : Bitboard(0);
if (pawnsOn7)
{
if (Type == CAPTURES)
- emptySquares = ~pos.pieces() & pos.board_bb();
+ emptySquares = ~pos.pieces() & pos.board_bb(Us, PAWN);
if (Type == EVASIONS)
emptySquares &= target;
{
if (pos.count(Us, pt))
continue;
- Bitboard b = (pos.attacks_from(Us, pt, from) & ~pos.pieces() & pos.board_bb()) | from;
+ Bitboard b = (pos.attacks_from(Us, pt, from) & ~pos.pieces()) | from;
if (Type == EVASIONS)
b &= target;
if (pt == PAWN)
continue; // Will be generated together with direct checks
- Bitboard b = pos.moves_from(us, pt, from) & ~pos.pieces() & pos.board_bb();
+ Bitboard b = pos.moves_from(us, pt, from) & ~pos.pieces();
if (pt == KING)
b &= ~PseudoAttacks[~us][QUEEN][pos.square<KING>(~us)];
Bitboard sliderAttacks = 0;
Bitboard sliders = pos.checkers();
+ // Consider all evasion moves for special pieces
+ if (sliders & (pos.pieces(CANNON) | pos.pieces(HORSE, ELEPHANT)))
+ {
+ Bitboard target = pos.board_bb() & ~pos.pieces(us);
+ Bitboard b = pos.attacks_from<KING>(us, ksq) & target;
+ while (b)
+ moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
+ return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
+ : generate_all<BLACK, EVASIONS>(pos, moveList, target);
+ }
+
// Find all the squares attacked by slider checkers. We will remove them from
// the king evasions in order to skip known illegal moves, which avoids any
// useless legality checks later on.
}
// Generate evasions for king, capture and non capture moves
- Bitboard b = pos.attacks_from<KING>(us, ksq) & ~pos.pieces(us) & ~sliderAttacks & pos.board_bb();
+ Bitboard b = pos.attacks_from<KING>(us, ksq) & ~pos.pieces(us) & ~sliderAttacks;
while (b)
moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
parse_attribute("gating", v->gating);
parse_attribute("seirawanGating", v->seirawanGating);
parse_attribute("cambodianMoves", v->cambodianMoves);
+ parse_attribute("flyingGeneral", v->flyingGeneral);
+ parse_attribute("xiangqiGeneral", v->xiangqiGeneral);
+ parse_attribute("xiangqiSoldier", v->xiangqiSoldier);
// game end
parse_attribute("nMoveRule", v->nMoveRule);
parse_attribute("nFoldRule", v->nFoldRule);
// Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s);
- blocked = theirPawns & (s + Up);
+ blocked = is_ok(s + Up) ? theirPawns & (s + Up) : Bitboard(0);
stoppers = theirPawns & passed_pawn_span(Us, s);
lever = theirPawns & PseudoAttacks[Us][PAWN][s];
leverPush = relative_rank(Them, s, pos.max_rank()) > RANK_1 ? theirPawns & PseudoAttacks[Us][PAWN][s + Up] : Bitboard(0);
p->stepsCapture = {SOUTH, WEST, EAST, NORTH_WEST, NORTH, NORTH_EAST};
return p;
}
- PieceInfo* horse_piece() {
+ PieceInfo* dragon_horse_piece() {
PieceInfo* p = bishop_piece();
- p->name = "horse";
+ p->name = "dragon_horse";
PieceInfo* p2 = wazir_piece();
p->merge(p2);
delete p2;
p->hopperCapture = {NORTH, EAST, SOUTH, WEST};
return p;
}
+ PieceInfo* soldier_piece() {
+ PieceInfo* p = new PieceInfo();
+ p->name = "soldier";
+ p->stepsQuiet = {NORTH, WEST, EAST};
+ p->stepsCapture = {NORTH, WEST, EAST};
+ return p;
+ }
+ PieceInfo* horse_piece() {
+ PieceInfo* p = knight_piece();
+ p->name = "horse";
+ p->lameLeaper = true;
+ return p;
+ }
+ PieceInfo* elephant_piece() {
+ PieceInfo* p = alfil_piece();
+ p->name = "elephant";
+ p->lameLeaper = true;
+ return p;
+ }
}
void PieceMap::init() {
add(SHOGI_KNIGHT, shogi_knight_piece());
add(EUROSHOGI_KNIGHT, euroshogi_knight_piece());
add(GOLD, gold_piece());
- add(HORSE, horse_piece());
+ add(DRAGON_HORSE, dragon_horse_piece());
add(CLOBBER_PIECE, clobber_piece());
add(BREAKTHROUGH_PIECE, breakthrough_piece());
add(IMMOBILE_PIECE, immobile_piece());
add(CANNON, cannon_piece());
+ add(SOLDIER, soldier_piece());
+ add(HORSE, horse_piece());
+ add(ELEPHANT, elephant_piece());
add(WAZIR, wazir_piece());
add(COMMONER, commoner_piece());
add(KING, king_piece());
std::vector<Direction> sliderCapture = {};
std::vector<Direction> hopperQuiet = {};
std::vector<Direction> hopperCapture = {};
+ bool lameLeaper = false;
void merge(const PieceInfo* pi);
};
break;
}
- else if ((idx = piece_to_char().find(token)) != string::npos)
+ else if ((idx = piece_to_char().find(token)) != string::npos || (idx = piece_to_char_synonyms().find(token)) != string::npos)
{
put_piece(Piece(idx), sq);
++sq;
Bitboard b = 0;
for (PieceType pt : piece_types())
- b |= attacks_bb(~c, pt, s, occupied) & pieces(c, pt);
+ if (board_bb(c, pt) & s)
+ {
+ // Consider asymmetrical move of horse
+ if (pt == HORSE)
+ {
+ Bitboard horses = PseudoAttacks[~c][KNIGHT][s] & pieces(c, HORSE);
+ while (horses)
+ {
+ Square s2 = pop_lsb(&horses);
+ if (attacks_bb(c, HORSE, s2, occupied) & s)
+ b |= s2;
+ }
+ }
+ else
+ b |= attacks_bb(~c, pt, s, occupied) & pieces(c, pt);
+ }
// Consider special move of neang in cambodian chess
if (cambodian_moves())
|| !(attackers_to(to, pieces() ^ to_sq(m), ~us));
}
+ // Flying general rule
+ if (var->flyingGeneral && count<KING>(us))
+ {
+ Square s = type_of(moved_piece(m)) == KING ? to : square<KING>(us);
+ if (attacks_bb(~us, ROOK, s, (pieces() ^ from) | to) & pieces(~us, KING) & ~square_bb(to))
+ return false;
+ }
+
+ // Xiangqi general
+ if (var->xiangqiGeneral && type_of(moved_piece(m)) == KING && !(PseudoAttacks[us][WAZIR][from] & to))
+ return false;
+
+ // Xiangqi soldier
+ if (type_of(moved_piece(m)) == SOLDIER && unpromoted_soldier(us, from) && file_of(from) != file_of(to))
+ return false;
+
// If the moving piece is a king, check whether the destination
// square is attacked by the opponent. Castling moves are checked
// for legality during move generation.
if (type_of(moved_piece(m)) == KING)
- return type_of(m) == CASTLING || !attackers_to(to, ~us);
+ return type_of(m) == CASTLING || !attackers_to(to, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us);
// A non-king move is legal if the king is not under attack after the move.
return !count<KING>(us) || !(attackers_to(square<KING>(us), (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us) & ~SquareBB[to]);
// Evasions generator already takes care to avoid some kind of illegal moves
// and legal() relies on this. We therefore have to take care that the same
// kind of moves are filtered out here.
- if (checkers())
+ if (checkers() & ~(pieces(CANNON) | pieces(HORSE, ELEPHANT)))
{
if (type_of(pc) != KING)
{
return false;
// Is there a direct check?
- if (type_of(m) != PROMOTION && type_of(m) != PIECE_PROMOTION && type_of(m) != PIECE_DEMOTION && (st->checkSquares[type_of(moved_piece(m))] & to))
- return true;
+ if (type_of(m) != PROMOTION && type_of(m) != PIECE_PROMOTION && type_of(m) != PIECE_DEMOTION)
+ {
+ if (type_of(moved_piece(m)) == CANNON || type_of(moved_piece(m)) == HORSE)
+ {
+ if (attacks_bb(sideToMove, type_of(moved_piece(m)), to, (pieces() ^ from) | to) & square<KING>(~sideToMove))
+ return true;
+ }
+ else if (st->checkSquares[type_of(moved_piece(m))] & to)
+ return true;
+ }
// Is there a discovered check?
if ( type_of(m) != DROP
- && ((st->blockersForKing[~sideToMove] & from) || pieces(sideToMove, CANNON))
+ && ((st->blockersForKing[~sideToMove] & from) || pieces(sideToMove, CANNON, HORSE))
&& attackers_to(square<KING>(~sideToMove), (pieces() ^ from) | to, sideToMove))
return true;
Rank max_rank() const;
File max_file() const;
Bitboard board_bb() const;
+ Bitboard board_bb(Color c, PieceType pt) const;
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;
bool sittuyin_promotion() const;
bool gating() const;
bool seirawan_gating() const;
bool cambodian_moves() const;
+ bool unpromoted_soldier(Color c, Square s) const;
// winning conditions
int n_move_rule() const;
int n_fold_rule() const;
return board_size_bb(var->maxFile, var->maxRank);
}
+inline Bitboard Position::board_bb(Color c, PieceType pt) const {
+ assert(var != nullptr);
+ return var->mobilityRegion[c][pt] ? var->mobilityRegion[c][pt] & board_bb() : board_bb();
+}
+
inline const std::set<PieceType>& Position::piece_types() const {
assert(var != nullptr);
return var->pieceTypes;
return var->pieceToChar;
}
+inline const std::string Position::piece_to_char_synonyms() const {
+ assert(var != nullptr);
+ return var->pieceToCharSynonyms;
+}
+
inline Rank Position::promotion_rank() const {
assert(var != nullptr);
return var->promotionRank;
}
inline Bitboard Position::drop_region(Color c, PieceType pt) const {
- Bitboard b = drop_region(c) & board_bb();
+ Bitboard b = drop_region(c) & board_bb(c, pt);
// Connect4-style drops
if (drop_on_top())
return var->cambodianMoves;
}
+inline bool Position::unpromoted_soldier(Color c, Square s) const {
+ assert(var != nullptr);
+ return var->xiangqiSoldier && relative_rank(c, s, var->maxRank) <= RANK_5;
+}
+
inline int Position::n_move_rule() const {
assert(var != nullptr);
return var->nMoveRule;
template<PieceType Pt>
inline Bitboard Position::attacks_from(Color c, Square s) const {
- return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]);
+ return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, Pt);
}
inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const {
- return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]);
+ return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
}
inline Bitboard Position::moves_from(Color c, PieceType pt, Square s) const {
- return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]);
+ return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
}
inline Bitboard Position::attackers_to(Square s) const {
{ VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg,
FersValueMg, AlfilValueMg, FersAlfilValueMg, SilverValueMg, AiwokValueMg, BersValueMg,
ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg,
- ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, HorseValueMg,
- ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg, CannonPieceValueMg, WazirValueMg, CommonerValueMg },
+ ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
+ ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg,
+ CannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, WazirValueMg, CommonerValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg,
FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg,
- ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, HorseValueEg,
- ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg, CannonPieceValueEg, WazirValueEg, CommonerValueEg }
+ ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
+ ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg,
+ CannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, WazirValueEg, CommonerValueEg }
};
namespace PSQT {
ShogiKnightValueMg = 350, ShogiKnightValueEg = 300,
EuroShogiKnightValueMg = 400, EuroShogiKnightValueEg = 400,
GoldValueMg = 640, GoldValueEg = 640,
- HorseValueMg = 1500, HorseValueEg = 1500,
+ DragonHorseValueMg = 1500, DragonHorseValueEg = 1500,
ClobberPieceValueMg = 300, ClobberPieceValueEg = 300,
BreakthroughPieceValueMg = 300, BreakthroughPieceValueEg = 300,
ImmobilePieceValueMg = 100, ImmobilePieceValueEg = 100,
- CannonPieceValueMg = 900, CannonPieceValueEg = 900,
+ CannonPieceValueMg = 800, CannonPieceValueEg = 700,
+ SoldierValueMg = 200, SoldierValueEg = 300,
+ HorseValueMg = 500, HorseValueEg = 800,
+ ElephantValueMg = 350, ElephantValueEg = 350,
WazirValueMg = 400, WazirValueEg = 400,
CommonerValueMg = 700, CommonerValueEg = 900,
MidgameLimit = 15258, EndgameLimit = 3915
};
-constexpr int PIECE_TYPE_BITS = 5; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS)
+constexpr int PIECE_TYPE_BITS = 6; // PIECE_TYPE_NB = pow(2, PIECE_TYPE_BITS)
enum PieceType {
NO_PIECE_TYPE, PAWN, KNIGHT, BISHOP, ROOK, QUEEN,
FERS, MET = FERS, ALFIL, FERS_ALFIL, SILVER, KHON = SILVER, AIWOK, BERS, DRAGON = BERS,
ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI,
- SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, HORSE,
- CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, CANNON, WAZIR, COMMONER, KING,
+ SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, DRAGON_HORSE,
+ CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, CANNON, SOLDIER, HORSE, ELEPHANT, WAZIR, COMMONER, KING,
ALL_PIECES = 0,
PIECE_TYPE_NB = 1 << PIECE_TYPE_BITS
RIDER_ROOK_V = 1 << 2,
RIDER_CANNON_H = 1 << 3,
RIDER_CANNON_V = 1 << 4,
+ RIDER_HORSE = 1 << 5,
+ RIDER_ELEPHANT = 1 << 6,
};
extern Value PieceValue[PHASE_NB][PIECE_NB];
v->add_piece(SILVER, 's');
v->add_piece(GOLD, 'g');
v->add_piece(BISHOP, 'b');
- v->add_piece(HORSE, 'h');
+ v->add_piece(DRAGON_HORSE, 'h');
v->add_piece(ROOK, 'r');
v->add_piece(DRAGON, 'd');
v->add_piece(KING, 'k');
v->castling = false;
v->promotedPieceType[SHOGI_PAWN] = GOLD;
v->promotedPieceType[SILVER] = GOLD;
- v->promotedPieceType[BISHOP] = HORSE;
+ v->promotedPieceType[BISHOP] = DRAGON_HORSE;
v->promotedPieceType[ROOK] = DRAGON;
v->shogiDoubledPawn = false;
v->immobilityIllegal = true;
v->blackFlag = Rank1BB;
return v;
}
+ Variant* minixiangqi_variant() {
+ Variant* v = fairy_variant_base();
+ v->maxRank = RANK_7;
+ v->maxFile = FILE_G;
+ v->reset_pieces();
+ v->add_piece(ROOK, 'r');
+ v->add_piece(HORSE, 'n', 'h');
+ v->add_piece(KING, 'k');
+ v->add_piece(CANNON, 'c');
+ v->add_piece(SOLDIER, 'p');
+ v->startFen = "rcnkncr/p1ppp1p/7/7/7/P1PPP1P/RCNKNCR w - - 0 1";
+ Bitboard white_castle = make_bitboard(SQ_C1, SQ_D1, SQ_E1,
+ SQ_C2, SQ_D2, SQ_E2,
+ SQ_C3, SQ_D3, SQ_E3);
+ Bitboard black_castle = make_bitboard(SQ_C5, SQ_D5, SQ_E5,
+ SQ_C6, SQ_D6, SQ_E6,
+ SQ_C7, SQ_D7, SQ_E7);
+ v->mobilityRegion[WHITE][KING] = white_castle;
+ v->mobilityRegion[BLACK][KING] = black_castle;
+ v->promotionPieceTypes = {};
+ v->doubleStep = false;
+ v->castling = false;
+ v->stalemateValue = -VALUE_MATE;
+ //v->nFoldValue = VALUE_MATE;
+ v->perpetualCheckIllegal = true;
+ v->flyingGeneral = true;
+ v->xiangqiGeneral = true;
+ return v;
+ }
#ifdef LARGEBOARDS
Variant* shogi_variant() {
Variant* v = minishogi_variant_base();
"pPpPpPpPpP/PpPpPpPpPp/pPpPpPpPpP/PpPpPpPpPp/pPpPpPpPpP w 0 1";
return v;
}
+ Variant* xiangqi_variant() {
+ Variant* v = minixiangqi_variant();
+ v->maxRank = RANK_10;
+ v->maxFile = FILE_I;
+ v->add_piece(ELEPHANT, 'b', 'e');
+ v->add_piece(FERS, 'a');
+ v->startFen = "rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR w - - 0 1";
+ Bitboard white_castle = make_bitboard(SQ_D1, SQ_E1, SQ_F1,
+ SQ_D2, SQ_E2, SQ_F2,
+ SQ_D3, SQ_E3, SQ_F3);
+ Bitboard black_castle = make_bitboard(SQ_D8, SQ_E8, SQ_F8,
+ SQ_D9, SQ_E9, SQ_F9,
+ SQ_D10, SQ_E10, SQ_F10);
+ v->mobilityRegion[WHITE][KING] = white_castle;
+ v->mobilityRegion[BLACK][KING] = black_castle;
+ v->mobilityRegion[WHITE][FERS] = white_castle;
+ v->mobilityRegion[BLACK][FERS] = black_castle;
+ v->mobilityRegion[WHITE][ELEPHANT] = Rank1BB | Rank2BB | Rank3BB | Rank4BB | Rank5BB;
+ v->mobilityRegion[BLACK][ELEPHANT] = Rank6BB | Rank7BB | Rank8BB | Rank9BB | Rank10BB;
+ v->xiangqiSoldier = true;
+ return v;
+ }
#endif
} // namespace
add("shatar", shatar_variant());
add("clobber", clobber_variant());
add("breakthrough", breakthrough_variant());
+ add("minixiangqi", minixiangqi_variant());
#ifdef LARGEBOARDS
add("shogi", shogi_variant());
add("capablanca", capablanca_variant());
add("grand", grand_variant());
add("shako", shako_variant());
add("clobber10", clobber10_variant());
+ add("xiangqi", xiangqi_variant());
#endif
}
std::set<PieceType> pieceTypes = { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING };
std::string pieceToChar = " PNBRQ" + std::string(KING - QUEEN - 1, ' ') + "K" + std::string(PIECE_TYPE_NB - KING - 1, ' ')
+ " pnbrq" + std::string(KING - QUEEN - 1, ' ') + "k" + std::string(PIECE_TYPE_NB - KING - 1, ' ');
+ std::string 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 };
bool sittuyinPromotion = false;
bool gating = false;
bool seirawanGating = false;
bool cambodianMoves = false;
+ bool flyingGeneral = false;
+ bool xiangqiGeneral = false;
+ bool xiangqiSoldier = false;
// game end
int nMoveRule = 50;
int nFoldRule = 3;
int connectN = 0;
CountingRule countingRule = NO_COUNTING;
- void add_piece(PieceType pt, char c) {
+ void add_piece(PieceType pt, char c, char c2 = ' ') {
pieceToChar[make_piece(WHITE, pt)] = toupper(c);
pieceToChar[make_piece(BLACK, pt)] = tolower(c);
+ pieceToCharSynonyms[make_piece(WHITE, pt)] = toupper(c2);
+ pieceToCharSynonyms[make_piece(BLACK, pt)] = tolower(c2);
pieceTypes.insert(pt);
}
void remove_piece(PieceType pt) {
pieceToChar[make_piece(WHITE, pt)] = ' ';
pieceToChar[make_piece(BLACK, pt)] = ' ';
+ pieceToCharSynonyms[make_piece(WHITE, pt)] = ' ';
+ pieceToCharSynonyms[make_piece(BLACK, pt)] = ' ';
pieceTypes.erase(pt);
}
void reset_pieces() {
pieceToChar = std::string(PIECE_NB, ' ');
+ pieceToCharSynonyms = std::string(PIECE_NB, ' ');
pieceTypes.clear();
}
};
echo "perft testing started"
cat << EOF > perft.exp
- set timeout 30
+ set timeout 60
lassign \$argv var pos depth result
spawn ./stockfish
send "setoption name UCI_Variant value \$var\\n"
expect perft.exp chancellor startpos 4 436656 > /dev/null
expect perft.exp courier startpos 4 500337 > /dev/null
expect perft.exp grand startpos 3 259514 > /dev/null
+ expect perft.exp xiangqi startpos 4 3290240 > /dev/null
fi
rm perft.exp