From: Fabian Fichter Date: Thu, 7 Nov 2019 22:00:25 +0000 (+0100) Subject: Support xiangqi and minixiangqi X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=e74e67453c60ae3c036c4ae92d3db92b7d09a0c0;p=fairystockfish.git Support xiangqi and minixiangqi New features: - Piece types soldier, horse, and elephant - Restriction of piece mobility - Flying general rule - Piece synonyms Closes #31. bench: 4448277 --- diff --git a/Readme.md b/Readme.md index 56e435f..4e6d993 100644 --- a/Readme.md +++ b/Readme.md @@ -5,7 +5,7 @@ [![Build Status](https://travis-ci.org/ianfab/Fairy-Stockfish.svg?branch=master)](https://travis-ci.org/ianfab/Fairy-Stockfish) [![Build Status](https://ci.appveyor.com/api/projects/status/github/ianfab/Fairy-Stockfish?branch=master&svg=true)](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. @@ -14,11 +14,12 @@ The goal of the project is to create an engine supporting a large variety of che 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) diff --git a/src/bitboard.cpp b/src/bitboard.cpp index 47c969e..cc53154 100644 --- a/src/bitboard.cpp +++ b/src/bitboard.cpp @@ -44,6 +44,8 @@ Magic RookMagicsV[SQUARE_NB]; Magic BishopMagics[SQUARE_NB]; Magic CannonMagicsH[SQUARE_NB]; Magic CannonMagicsV[SQUARE_NB]; +Magic HorseMagics[SQUARE_NB]; +Magic ElephantMagics[SQUARE_NB]; namespace { @@ -51,17 +53,21 @@ 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 #ifdef PRECOMPUTED_MAGICS @@ -72,6 +78,7 @@ namespace { template Bitboard sliding_attack(std::vector directions, Square sq, Bitboard occupied, Color c = WHITE) { + assert(MT != LAME_LEAPER); Bitboard attack = 0; @@ -97,6 +104,47 @@ namespace { 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 directions, Square s) { + Bitboard b = 0; + for (Direction d : directions) + b |= lame_leaper_path(d, s); + return b; + } + + Bitboard lame_leaper_attack(std::vector 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; + } } @@ -129,6 +177,25 @@ void Bitboards::init() { { 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) @@ -181,6 +248,9 @@ void Bitboards::init() { std::vector RookDirectionsV = { NORTH, SOUTH}; std::vector RookDirectionsH = { EAST, WEST }; std::vector BishopDirections = { NORTH_EAST, SOUTH_EAST, SOUTH_WEST, NORTH_WEST }; + std::vector 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 ElephantDirections = { 2 * NORTH_EAST, 2 * SOUTH_EAST, 2 * SOUTH_WEST, 2 * NORTH_WEST }; #ifdef PRECOMPUTED_MAGICS init_magics(RookTableH, RookMagicsH, RookDirectionsH, RookMagicHInit); @@ -188,12 +258,16 @@ void Bitboards::init() { init_magics(BishopTable, BishopMagics, BishopDirections, BishopMagicInit); init_magics(CannonTableH, CannonMagicsH, RookDirectionsH, CannonMagicHInit); init_magics(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit); + init_magics(HorseTable, HorseMagics, HorseDirections, HorseMagicInit); + init_magics(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit); #else init_magics(RookTableH, RookMagicsH, RookDirectionsH); init_magics(RookTableV, RookMagicsV, RookDirectionsV); init_magics(BishopTable, BishopMagics, BishopDirections); init_magics(CannonTableH, CannonMagicsH, RookDirectionsH); init_magics(CannonTableV, CannonMagicsV, RookDirectionsV); + init_magics(HorseTable, HorseMagics, HorseDirections); + init_magics(ElephantTable, ElephantMagics, ElephantDirections); #endif for (Color c : { WHITE, BLACK }) @@ -210,7 +284,8 @@ void Bitboards::init() { 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) @@ -220,7 +295,8 @@ void Bitboards::init() { 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(pi->sliderCapture, s, 0, c); @@ -283,7 +359,7 @@ namespace { // 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(directions, s, 0) & ~edges; + m.mask = (MT == LAME_LEAPER ? lame_leaper_path(directions, s) : sliding_attack(directions, s, 0)) & ~edges; #ifdef LARGEBOARDS m.shift = 128 - popcount(m.mask); #else @@ -299,7 +375,7 @@ namespace { b = size = 0; do { occupancy[size] = b; - reference[size] = sliding_attack(directions, s, b); + reference[size] = MT == LAME_LEAPER ? lame_leaper_attack(directions, s, b) : sliding_attack(directions, s, b); if (HasPext) m.attacks[pext(b, m.mask)] = reference[size]; diff --git a/src/bitboard.h b/src/bitboard.h index 7253bec..fdc8848 100644 --- a/src/bitboard.h +++ b/src/bitboard.h @@ -145,6 +145,8 @@ extern Magic RookMagicsV[SQUARE_NB]; 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); @@ -344,11 +346,14 @@ template constexpr const T& clamp(const T& v, const T& lo, const T& hi template 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)]; } @@ -372,10 +377,14 @@ inline Bitboard attacks_bb(Color c, PieceType pt, Square s, Bitboard occupied) { b |= rider_attacks_bb(s, occupied); if (AttackRiderTypes[pt] & RIDER_ROOK_V) b |= rider_attacks_bb(s, occupied); - if (AttackRiderTypes[pt] & RIDER_ROOK_H) - b |= rider_attacks_bb(s, occupied); + if (AttackRiderTypes[pt] & RIDER_CANNON_H) + b |= rider_attacks_bb(s, occupied); if (AttackRiderTypes[pt] & RIDER_CANNON_V) b |= rider_attacks_bb(s, occupied); + if (AttackRiderTypes[pt] & RIDER_HORSE) + b |= rider_attacks_bb(s, occupied); + if (AttackRiderTypes[pt] & RIDER_ELEPHANT) + b |= rider_attacks_bb(s, occupied); return b & PseudoAttacks[c][pt][s]; } @@ -391,6 +400,10 @@ inline Bitboard moves_bb(Color c, PieceType pt, Square s, Bitboard occupied) { b |= rider_attacks_bb(s, occupied); if (MoveRiderTypes[pt] & RIDER_CANNON_V) b |= rider_attacks_bb(s, occupied); + if (MoveRiderTypes[pt] & RIDER_HORSE) + b |= rider_attacks_bb(s, occupied); + if (MoveRiderTypes[pt] & RIDER_ELEPHANT) + b |= rider_attacks_bb(s, occupied); return b & PseudoMoves[c][pt][s]; } diff --git a/src/evaluate.cpp b/src/evaluate.cpp index f87f3b3..cffcddf 100644 --- a/src/evaluate.cpp +++ b/src/evaluate.cpp @@ -342,6 +342,9 @@ namespace { if (pos.captures_to_hand() && pos.count(Them) && pos.count(Us)) score -= KingProximity * distance(s, pos.square(Us)) * distance(s, pos.square(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 diff --git a/src/magic.h b/src/magic.h index 1fae2e3..7dd0dfe 100644 --- a/src/magic.h +++ b/src/magic.h @@ -633,6 +633,250 @@ 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 diff --git a/src/movegen.cpp b/src/movegen.cpp index d303842..b418016 100644 --- a/src/movegen.cpp +++ b/src/movegen.cpp @@ -108,7 +108,7 @@ namespace { // 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(pawnsNotOn7) & emptySquares; Bitboard b2 = pos.double_step_enabled() ? shift(b1 & TRank3BB) & emptySquares : Bitboard(0); @@ -158,7 +158,7 @@ namespace { if (pawnsOn7) { if (Type == CAPTURES) - emptySquares = ~pos.pieces() & pos.board_bb(); + emptySquares = ~pos.pieces() & pos.board_bb(Us, PAWN); if (Type == EVASIONS) emptySquares &= target; @@ -193,7 +193,7 @@ namespace { { 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; @@ -426,7 +426,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { 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(~us)]; @@ -452,6 +452,17 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { 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(us, ksq) & target; + while (b) + moveList = make_move_and_gating(pos, moveList, us, ksq, pop_lsb(&b)); + return us == WHITE ? generate_all(pos, moveList, target) + : generate_all(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. @@ -462,7 +473,7 @@ ExtMove* generate(const Position& pos, ExtMove* moveList) { } // Generate evasions for king, capture and non capture moves - Bitboard b = pos.attacks_from(us, ksq) & ~pos.pieces(us) & ~sliderAttacks & pos.board_bb(); + Bitboard b = pos.attacks_from(us, ksq) & ~pos.pieces(us) & ~sliderAttacks; while (b) moveList = make_move_and_gating(pos, moveList, us, ksq, pop_lsb(&b)); diff --git a/src/parser.cpp b/src/parser.cpp index ee7dd26..8c70e76 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -174,6 +174,9 @@ Variant* VariantParser::parse(Variant* v) { 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); diff --git a/src/pawns.cpp b/src/pawns.cpp index 8746565..35e35ed 100644 --- a/src/pawns.cpp +++ b/src/pawns.cpp @@ -96,7 +96,7 @@ namespace { // 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); diff --git a/src/piece.cpp b/src/piece.cpp index 9699517..199c861 100644 --- a/src/piece.cpp +++ b/src/piece.cpp @@ -216,9 +216,9 @@ namespace { 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; @@ -249,6 +249,25 @@ namespace { 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() { @@ -273,11 +292,14 @@ 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()); diff --git a/src/piece.h b/src/piece.h index af6e4b8..55c6505 100644 --- a/src/piece.h +++ b/src/piece.h @@ -36,6 +36,7 @@ struct PieceInfo { std::vector sliderCapture = {}; std::vector hopperQuiet = {}; std::vector hopperCapture = {}; + bool lameLeaper = false; void merge(const PieceInfo* pi); }; diff --git a/src/position.cpp b/src/position.cpp index b3ce0c4..05f597f 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -303,7 +303,7 @@ Position& Position::set(const Variant* v, const string& fenStr, bool isChess960, 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; @@ -749,7 +749,22 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c) const { 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()) @@ -883,11 +898,27 @@ bool Position::legal(Move m) const { || !(attackers_to(to, pieces() ^ to_sq(m), ~us)); } + // Flying general rule + if (var->flyingGeneral && count(us)) + { + Square s = type_of(moved_piece(m)) == KING ? to : square(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(us) || !(attackers_to(square(us), (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us) & ~SquareBB[to]); @@ -965,7 +996,7 @@ bool Position::pseudo_legal(const Move m) const { // 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) { @@ -1004,12 +1035,20 @@ bool Position::gives_check(Move m) const { 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(~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(~sideToMove), (pieces() ^ from) | to, sideToMove)) return true; diff --git a/src/position.h b/src/position.h index 19970bd..0b7a83d 100644 --- a/src/position.h +++ b/src/position.h @@ -96,8 +96,10 @@ public: Rank max_rank() const; File max_file() const; Bitboard board_bb() const; + Bitboard board_bb(Color c, PieceType pt) const; const std::set& 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 >& promotion_piece_types() const; bool sittuyin_promotion() const; @@ -134,6 +136,7 @@ public: 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; @@ -317,6 +320,11 @@ inline Bitboard Position::board_bb() 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& Position::piece_types() const { assert(var != nullptr); return var->pieceTypes; @@ -327,6 +335,11 @@ inline const std::string Position::piece_to_char() const { 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; @@ -463,7 +476,7 @@ inline Bitboard Position::drop_region(Color c) const { } 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()) @@ -527,6 +540,11 @@ inline bool Position::cambodian_moves() const { 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; @@ -748,15 +766,15 @@ inline Square Position::castling_rook_square(CastlingRights cr) const { template 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 { diff --git a/src/psqt.cpp b/src/psqt.cpp index d197f03..c34cc32 100644 --- a/src/psqt.cpp +++ b/src/psqt.cpp @@ -28,13 +28,15 @@ Value PieceValue[PHASE_NB][PIECE_NB] = { { 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 { diff --git a/src/types.h b/src/types.h index 67a664e..83f62c2 100644 --- a/src/types.h +++ b/src/types.h @@ -331,25 +331,28 @@ enum Value : int { 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 @@ -372,6 +375,8 @@ enum RiderType { 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]; diff --git a/src/variant.cpp b/src/variant.cpp index 67a0504..dcaccbf 100644 --- a/src/variant.cpp +++ b/src/variant.cpp @@ -298,7 +298,7 @@ namespace { 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'); @@ -311,7 +311,7 @@ namespace { 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; @@ -491,6 +491,35 @@ namespace { 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(); @@ -654,6 +683,28 @@ namespace { "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 @@ -709,6 +760,7 @@ void VariantMap::init() { 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()); @@ -724,6 +776,7 @@ void VariantMap::init() { add("grand", grand_variant()); add("shako", shako_variant()); add("clobber10", clobber10_variant()); + add("xiangqi", xiangqi_variant()); #endif } diff --git a/src/variant.h b/src/variant.h index a574df0..3300ede 100644 --- a/src/variant.h +++ b/src/variant.h @@ -40,7 +40,9 @@ struct Variant { std::set 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 > promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT }; bool sittuyinPromotion = false; @@ -77,6 +79,9 @@ struct Variant { 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; @@ -99,20 +104,25 @@ struct Variant { 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(); } }; diff --git a/tests/perft.sh b/tests/perft.sh index f77fe3c..8c564c9 100755 --- a/tests/perft.sh +++ b/tests/perft.sh @@ -11,7 +11,7 @@ trap 'error ${LINENO}' ERR 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" @@ -68,6 +68,7 @@ if [[ $1 == "largeboard" ]]; then 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