### Regional and historical games
- [Xiangqi](https://en.wikipedia.org/wiki/Xiangqi), [Manchu](https://en.wikipedia.org/wiki/Manchu_chess), [Minixiangqi](http://mlwi.magix.net/bg/minixiangqi.htm), [Supply chess](https://en.wikipedia.org/wiki/Xiangqi#Variations)
- [Shogi](https://en.wikipedia.org/wiki/Shogi), [Shogi variants](https://github.com/ianfab/Fairy-Stockfish#shogi-variants)
+- [Janggi](https://en.wikipedia.org/wiki/Janggi)
- [Makruk](https://en.wikipedia.org/wiki/Makruk), [ASEAN](http://hgm.nubati.net/rules/ASEAN.html), Makpong, Ai-Wok
- [Ouk Chatrang](https://en.wikipedia.org/wiki/Makruk#Cambodian_chess), [Kar Ouk](https://en.wikipedia.org/wiki/Makruk#Cambodian_chess)
- [Sittuyin](https://en.wikipedia.org/wiki/Sittuyin)
Magic CannonMagicsV[SQUARE_NB];
Magic HorseMagics[SQUARE_NB];
Magic ElephantMagics[SQUARE_NB];
+Magic JanggiElephantMagics[SQUARE_NB];
namespace {
Bitboard CannonTableV[0x4800]; // To store vertical cannon attacks
Bitboard HorseTable[0x500]; // To store horse attacks
Bitboard ElephantTable[0x400]; // To store elephant attacks
+ Bitboard JanggiElephantTable[0x1C000]; // To store janggi elephant attacks
#else
Bitboard RookTableH[0xA00]; // To store horizontal rook attacks
Bitboard RookTableV[0xA00]; // To store vertical rook attacks
Bitboard CannonTableV[0xA00]; // To store vertical cannon attacks
Bitboard HorseTable[0x240]; // To store horse attacks
Bitboard ElephantTable[0x1A0]; // To store elephant attacks
+ Bitboard JanggiElephantTable[0x5C00]; // To store janggi elephant attacks
#endif
enum MovementType { RIDER, HOPPER, LAME_LEAPER };
void Bitboards::init() {
+ // Piece moves
+ 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 };
+ std::vector<Direction> JanggiElephantDirections = { NORTH + 2 * NORTH_EAST, EAST + 2 * NORTH_EAST,
+ EAST + 2 * SOUTH_EAST, SOUTH + 2 * SOUTH_EAST,
+ SOUTH + 2 * SOUTH_WEST, WEST + 2 * SOUTH_WEST,
+ WEST + 2 * NORTH_WEST, NORTH + 2 * NORTH_WEST };
+
// Initialize rider types
for (PieceType pt = PAWN; pt <= KING; ++pt)
{
{
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)
+ if (std::find(HorseDirections.begin(), HorseDirections.end(), d) != HorseDirections.end())
AttackRiderTypes[pt] |= RIDER_HORSE;
- if (d == 2 * NORTH_EAST || d == 2 * SOUTH_EAST || d == 2 * SOUTH_WEST || d == 2 * NORTH_WEST)
+ if (std::find(ElephantDirections.begin(), ElephantDirections.end(), d) != ElephantDirections.end())
AttackRiderTypes[pt] |= RIDER_ELEPHANT;
+ if (std::find(JanggiElephantDirections.begin(), JanggiElephantDirections.end(), d) != JanggiElephantDirections.end())
+ AttackRiderTypes[pt] |= RIDER_JANGGI_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)
+ if (std::find(HorseDirections.begin(), HorseDirections.end(), d) != HorseDirections.end())
MoveRiderTypes[pt] |= RIDER_HORSE;
- if (d == 2 * NORTH_EAST || d == 2 * SOUTH_EAST || d == 2 * SOUTH_WEST || d == 2 * NORTH_WEST)
+ if (std::find(ElephantDirections.begin(), ElephantDirections.end(), d) != ElephantDirections.end())
MoveRiderTypes[pt] |= RIDER_ELEPHANT;
+ if (std::find(JanggiElephantDirections.begin(), JanggiElephantDirections.end(), d) != JanggiElephantDirections.end())
+ MoveRiderTypes[pt] |= RIDER_JANGGI_ELEPHANT;
}
}
for (Direction d : pi->sliderCapture)
{
- if (d == NORTH_EAST || d == SOUTH_WEST || d == NORTH_WEST || d == SOUTH_EAST)
+ if (std::find(BishopDirections.begin(), BishopDirections.end(), d) != BishopDirections.end())
AttackRiderTypes[pt] |= RIDER_BISHOP;
- if (d == EAST || d == WEST)
+ if (std::find(RookDirectionsH.begin(), RookDirectionsH.end(), d) != RookDirectionsH.end())
AttackRiderTypes[pt] |= RIDER_ROOK_H;
- if (d == NORTH || d == SOUTH)
+ if (std::find(RookDirectionsV.begin(), RookDirectionsV.end(), d) != RookDirectionsV.end())
AttackRiderTypes[pt] |= RIDER_ROOK_V;
}
for (Direction d : pi->sliderQuiet)
{
- if (d == NORTH_EAST || d == SOUTH_WEST || d == NORTH_WEST || d == SOUTH_EAST)
+ if (std::find(BishopDirections.begin(), BishopDirections.end(), d) != BishopDirections.end())
MoveRiderTypes[pt] |= RIDER_BISHOP;
- if (d == EAST || d == WEST)
+ if (std::find(RookDirectionsH.begin(), RookDirectionsH.end(), d) != RookDirectionsH.end())
MoveRiderTypes[pt] |= RIDER_ROOK_H;
- if (d == NORTH || d == SOUTH)
+ if (std::find(RookDirectionsV.begin(), RookDirectionsV.end(), d) != RookDirectionsV.end())
MoveRiderTypes[pt] |= RIDER_ROOK_V;
}
for (Direction d : pi->hopperCapture)
{
- if (d == EAST || d == WEST)
+ if (std::find(RookDirectionsH.begin(), RookDirectionsH.end(), d) != RookDirectionsH.end())
AttackRiderTypes[pt] |= RIDER_CANNON_H;
- if (d == NORTH || d == SOUTH)
+ if (std::find(RookDirectionsV.begin(), RookDirectionsV.end(), d) != RookDirectionsV.end())
AttackRiderTypes[pt] |= RIDER_CANNON_V;
}
for (Direction d : pi->hopperQuiet)
{
- if (d == EAST || d == WEST)
+ if (std::find(RookDirectionsH.begin(), RookDirectionsH.end(), d) != RookDirectionsH.end())
MoveRiderTypes[pt] |= RIDER_CANNON_H;
- if (d == NORTH || d == SOUTH)
+ if (std::find(RookDirectionsV.begin(), RookDirectionsV.end(), d) != RookDirectionsV.end())
MoveRiderTypes[pt] |= RIDER_CANNON_V;
}
}
for (Square s2 = SQ_A1; s2 <= SQ_MAX; ++s2)
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
- // Piece moves
- 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>(RookTableV, RookMagicsV, RookDirectionsV, RookMagicVInit);
init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit);
init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections, HorseMagicInit);
init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit);
+ init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections, JanggiElephantMagicInit);
#else
init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH);
init_magics<RIDER>(RookTableV, RookMagicsV, RookDirectionsV);
init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV);
init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections);
init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections);
+ init_magics<LAME_LEAPER>(JanggiElephantTable, JanggiElephantMagics, JanggiElephantDirections);
#endif
for (Color c : { WHITE, BLACK })
extern Magic CannonMagicsV[SQUARE_NB];
extern Magic HorseMagics[SQUARE_NB];
extern Magic ElephantMagics[SQUARE_NB];
+extern Magic JanggiElephantMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_MAX);
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
- || R == RIDER_HORSE || R == RIDER_ELEPHANT);
+ || R == RIDER_HORSE || R == RIDER_ELEPHANT || R == RIDER_JANGGI_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]
+ : R == RIDER_JANGGI_ELEPHANT ? JanggiElephantMagics[s]
: BishopMagics[s];
return m.attacks[m.index(occupied)];
}
b |= rider_attacks_bb<RIDER_HORSE>(s, occupied);
if (AttackRiderTypes[pt] & RIDER_ELEPHANT)
b |= rider_attacks_bb<RIDER_ELEPHANT>(s, occupied);
+ if (AttackRiderTypes[pt] & RIDER_JANGGI_ELEPHANT)
+ b |= rider_attacks_bb<RIDER_JANGGI_ELEPHANT>(s, occupied);
return b & PseudoAttacks[c][pt][s];
}
b |= rider_attacks_bb<RIDER_HORSE>(s, occupied);
if (MoveRiderTypes[pt] & RIDER_ELEPHANT)
b |= rider_attacks_bb<RIDER_ELEPHANT>(s, occupied);
+ if (MoveRiderTypes[pt] & RIDER_JANGGI_ELEPHANT)
+ b |= rider_attacks_bb<RIDER_JANGGI_ELEPHANT>(s, occupied);
return b & PseudoMoves[c][pt][s];
}
B(0x224404002004268C, 0x202500204C7D254),
B(0x290080000000, 0xA41297838F40D),
};
+ Bitboard JanggiElephantMagicInit[SQUARE_NB] = {
+ B(0xC502282200061400, 0x2D07081241D90200),
+ B(0xC502282200061400, 0x2D07081241D90200),
+ B(0x8084810022440C2, 0x81402202004),
+ B(0x80204010A800500, 0x5000021001740218),
+ B(0x8048100401208000, 0x2001000390000044),
+ B(0x202080020000000, 0x4010800010090424),
+ B(0x4081A0480073200, 0x100000A010406000),
+ B(0x4081A0480073200, 0x100000A010406000),
+ B(0x2040450004000C40, 0x8400000006302),
+ B(0x84010410018201, 0xA00A00000100000),
+ B(0x840091030C00040, 0x1400008200023400),
+ B(0x801058C0A0022, 0xC1920480010034),
+ B(0x80B4004800840800, 0x4080210A42040010),
+ B(0x400402221000445, 0x80321200408040),
+ B(0x4028142401012A00, 0x4005009000104448),
+ B(0x1440102040800220, 0x82800010A082000),
+ B(0x4100040300C00200, 0x800805100120000),
+ B(0x8200080061100, 0x2000101400000),
+ B(0x2000100410070001, 0x40818200B0900410),
+ B(0x400088020080000, 0x4A000402000CA0),
+ B(0x1402040410004000, 0x9840044504040),
+ B(0x20800088A00A0400, 0x1000020100180),
+ B(0x2001820520308201, 0x2008003404349000),
+ B(0x4004808022100, 0x8001000008081080),
+ B(0x102041041100425, 0x840400180B100104),
+ B(0x8806446000800214, 0x404402100010000),
+ B(0x8200141409C04101, 0x209030004A00D00),
+ B(0x8806004800880080, 0x1560004201000A01),
+ B(0x4200050600200090, 0x1CD0000000000421),
+ B(0x4820100022408100, 0x101404080320),
+ B(0x2A000A0A08080080, 0x1C02808000C2C0),
+ B(0x8808425040040014, 0x2021000100020),
+ B(0x5282104044A0020, 0x6B402104200008),
+ B(0x4001091040068120, 0x202000004003031),
+ B(0x4001091040068120, 0x202000004003031),
+ B(0x98040200A0214344, 0xA00300840010),
+ B(0x82508040A40808A, 0x40010000110042),
+ B(0x4400100101023, 0x450C8480040022),
+ B(0x210588880010800, 0x800A000108018102),
+ B(0x9400010144400, 0xC00010100018000),
+ B(0x20A0400100040004, 0x1242000101002040),
+ B(0x8022900040001001, 0x100000014000260),
+ B(0x51004124000A080, 0x40098400000002),
+ B(0x2158040001080022, 0x80009238401222),
+ B(0xA0103A0000802220, 0x20000200400010),
+ B(0x1101001208240, 0x100000800001064),
+ B(0x821020002090081, 0x5840D0010290280),
+ B(0x821020002090081, 0x5840D0010290280),
+ B(0x10400C1042000400, 0x4005000000440200),
+ B(0x844022008804820, 0x1000800100118000),
+ B(0x10802A9800800139, 0x4802840100842200),
+ B(0x4000A008200081, 0x4001100200402000),
+ B(0x200000008108400, 0x1000C00008080020),
+ B(0x120C11500100081, 0x440300308041100),
+ B(0x8080040080060100, 0xC00101B0040028),
+ B(0x901420A00110000, 0x8200010044700280),
+ B(0x140080080410000, 0x808040000C001001),
+ B(0x80210C0200A0008, 0x88088004600201),
+ B(0x8000004202020301, 0x2100142104002000),
+ B(0x1101011210004880, 0x8500840400000000),
+ B(0x40208802004800, 0x8080806009011240),
+ B(0x800000140408880, 0xC001018004060040),
+ B(0xC008080420500, 0x8024A10000000000),
+ B(0x2800000000400010, 0x44001C00400408),
+ B(0xA804008001200408, 0x202000020001000),
+ B(0xC08288805004080, 0x200042000800004),
+ B(0xA40A01000080012, 0x8800080042408),
+ B(0x2200100000100810, 0x800200010000100),
+ B(0x9881800004040001, 0x8058100100884004),
+ B(0x820000044020014, 0x4AA00010245012),
+ B(0x820000044020014, 0x4AA00010245012),
+ B(0x4000080240000808, 0x10100022054000),
+ B(0x5002000840101, 0x202020004000A00),
+ B(0x1188008200008402, 0x8088100020A2204),
+ B(0x304012004044080, 0x8028108818006010),
+ B(0x102210000008400, 0x1008000200380002),
+ B(0x51410E114200, 0x100C00084000000),
+ B(0x5001242320218, 0x800025000040040),
+ B(0x4008000200008190, 0x400020021000000),
+ B(0x10910022F0040, 0x450084400040001),
+ B(0x180010810000040, 0x4004100040040),
+ B(0x1088801424062010, 0x400084010030401),
+ B(0x3000120408000040, 0x10802001080A4051),
+ B(0x200008420, 0x40C0100020008804),
+ B(0x1048C000004000, 0x4220120804004000),
+ B(0x404A180000000E, 0x4C30412008110102),
+ B(0x400000404202005, 0x800808550EC40044),
+ B(0x282000200212010, 0x8001C0C102000210),
+ B(0x9012240000008100, 0x280CA04010040000),
+ B(0x2000C04001020C00, 0x2002010101042000),
+ B(0x1010000204408408, 0x8008004800E0C4A),
+ B(0x800286801000025, 0x8402401040050088),
+ B(0x40002000A0880000, 0x8400300108082086),
+ B(0x2080004404011, 0x20C080400100001),
+ B(0xB0010218100800, 0x8040200482C14103),
+ B(0x8011035000000C20, 0x4200044043200040),
+ B(0x804008000040050, 0x41044890228000),
+ B(0x80000400A0020020, 0x5308022021000000),
+ B(0x2118200000008004, 0x4141014004423D00),
+ B(0x90C0000200008040, 0x41041062000082),
+ B(0x1D000100941204, 0x12402001200420),
+ B(0x8C0040400400065, 0x22300B408100000),
+ B(0x8C0040400400065, 0x22300B408100000),
+ B(0x802802044600000, 0x1210100401030082),
+ B(0x9400488010000000, 0x8005404902040000),
+ B(0x2214020200001, 0x40102100820200),
+ B(0x2022000000800000, 0x6400440108480),
+ B(0x110000400100028, 0x24052304508004),
+ B(0x848820140010000, 0x201012500A000),
+ B(0x848820140010000, 0x201012500A000),
+ B(0x100100000000C4, 0x208004084048201),
+ B(0x100500000000290, 0x10102818208000),
+ B(0x2800414000C000, 0x20004005001301),
+ B(0x698180005101241, 0x10002014800210),
+ B(0x20000080000009, 0x440340C040),
+ B(0x1C0220200290020, 0x42100004004011C0),
+ B(0x200E620018320208, 0x440410402),
+ B(0xD04101010004024, 0x20000121104010A4),
+ B(0x220400000A80040, 0x806080020810010C),
+ B(0xA000200000000080, 0x1040801A0081208),
+ };
#undef B
#endif
// Xiangqi soldier
if (pt == SOLDIER && pos.unpromoted_soldier(us, from))
b1 &= file_bb(file_of(from));
+ if (pt == JANGGI_CANNON)
+ {
+ b1 &= ~pos.pieces(pt);
+ b1 &= attacks_bb(us, pt, from, pos.pieces() ^ pos.pieces(pt));
+ }
PieceType prom_pt = pos.promoted_piece_type(pt);
Bitboard b2 = prom_pt && (!pos.promotion_limit(prom_pt) || pos.promotion_limit(prom_pt) > pos.count(us, prom_pt)) ? b1 : Bitboard(0);
Bitboard b3 = pos.piece_demotion() && pos.is_promoted(from) ? b1 : Bitboard(0);
while (b)
moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, ksq, pop_lsb(&b));
+ // Passing move by king
+ if (pos.pass_on_stalemate())
+ *moveList++ = make<SPECIAL>(ksq, ksq);
+
if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
{
if (!pos.castling_impeded(OO) && pos.can_castle(OO))
}
}
+ // Janggi palace moves
+ if (pos.diagonal_lines())
+ {
+ Bitboard diags = pos.pieces(Us) & pos.diagonal_lines();
+ while (diags)
+ {
+ Square from = pop_lsb(&diags);
+ PieceType pt = type_of(pos.piece_on(from));
+ PieceType movePt = pt == KING ? pos.king_type() : pt;
+ Bitboard b = 0;
+ PieceType diagType = movePt == WAZIR ? FERS : movePt == SOLDIER ? PAWN : movePt == ROOK ? BISHOP : NO_PIECE_TYPE;
+ if (diagType)
+ b |= attacks_bb(Us, diagType, from, pos.pieces());
+ else if (movePt == JANGGI_CANNON)
+ // TODO: fix for longer diagonals
+ b |= attacks_bb(Us, ALFIL, from, pos.pieces()) & ~attacks_bb(Us, ELEPHANT, from, pos.pieces() ^ pos.pieces(JANGGI_CANNON));
+ b &= pos.board_bb(Us, pt) & target & pos.diagonal_lines();
+ while (b)
+ moveList = make_move_and_gating<SPECIAL>(pos, moveList, Us, from, pop_lsb(&b));
+ }
+ }
+
return moveList;
}
Bitboard sliders = pos.checkers();
// Consider all evasion moves for special pieces
- if (sliders & (pos.pieces(CANNON, BANNER) | pos.pieces(HORSE, ELEPHANT)))
+ if (sliders & (pos.pieces(CANNON, BANNER) | pos.pieces(HORSE, ELEPHANT) | pos.pieces(JANGGI_CANNON, JANGGI_ELEPHANT)))
{
Bitboard target = pos.board_bb() & ~pos.pieces(us);
Bitboard b = ( (pos.attacks_from(us, KING, ksq) & pos.pieces())
parse_attribute("gating", v->gating);
parse_attribute("seirawanGating", v->seirawanGating);
parse_attribute("cambodianMoves", v->cambodianMoves);
+ parse_attribute("diagonalLines", v->diagonalLines);
+ parse_attribute("passOnStalemate", v->passOnStalemate);
parse_attribute("makpongRule", v->makpongRule);
parse_attribute("flyingGeneral", v->flyingGeneral);
parse_attribute("xiangqiSoldier", v->xiangqiSoldier);
parse_attribute("checkmateValue", v->checkmateValue);
parse_attribute("shogiPawnDropMateIllegal", v->shogiPawnDropMateIllegal);
parse_attribute("shatarMateRule", v->shatarMateRule);
+ parse_attribute("bikjangRule", v->bikjangRule);
parse_attribute("bareKingValue", v->bareKingValue);
parse_attribute("extinctionValue", v->extinctionValue);
parse_attribute("bareKingMove", v->bareKingMove);
p->hopperCapture = {NORTH, EAST, SOUTH, WEST};
return p;
}
+ PieceInfo* janggi_cannon_piece() {
+ PieceInfo* p = new PieceInfo();
+ p->name = "janggi_cannon";
+ p->betza = "pR";
+ p->hopperQuiet = {NORTH, EAST, SOUTH, WEST};
+ p->hopperCapture = {NORTH, EAST, SOUTH, WEST};
+ return p;
+ }
PieceInfo* soldier_piece() {
PieceInfo* p = new PieceInfo();
p->name = "soldier";
p->lameLeaper = true;
return p;
}
+ PieceInfo* janggi_elephant_piece() {
+ PieceInfo* p = new PieceInfo();
+ p->name = "janggi_elephant";
+ p->betza = "nZ";
+ p->stepsQuiet = {SOUTH + 2 * SOUTH_WEST, SOUTH + 2 * SOUTH_EAST,
+ WEST + 2 * SOUTH_WEST, EAST + 2 * SOUTH_EAST,
+ WEST + 2 * NORTH_WEST, EAST + 2 * NORTH_EAST,
+ NORTH + 2 * NORTH_WEST, NORTH + 2 * NORTH_EAST};
+ p->stepsCapture = {SOUTH + 2 * SOUTH_WEST, SOUTH + 2 * SOUTH_EAST,
+ WEST + 2 * SOUTH_WEST, EAST + 2 * SOUTH_EAST,
+ WEST + 2 * NORTH_WEST, EAST + 2 * NORTH_EAST,
+ NORTH + 2 * NORTH_WEST, NORTH + 2 * NORTH_EAST};
+ p->lameLeaper = true;
+ return p;
+ }
PieceInfo* banner_piece() {
PieceInfo* p = rook_piece();
p->name = "banner";
add(BREAKTHROUGH_PIECE, breakthrough_piece());
add(IMMOBILE_PIECE, immobile_piece());
add(CANNON, cannon_piece());
+ add(JANGGI_CANNON, janggi_cannon_piece());
add(SOLDIER, soldier_piece());
add(HORSE, horse_piece());
add(ELEPHANT, elephant_piece());
+ add(JANGGI_ELEPHANT, janggi_elephant_piece());
add(BANNER, banner_piece());
add(WAZIR, wazir_piece());
add(COMMONER, commoner_piece());
si->checkSquares[pt] = ksq != SQ_NONE ? attacks_from(~sideToMove, pt, ksq) : Bitboard(0);
si->checkSquares[KING] = 0;
si->shak = si->checkersBB & (byTypeBB[KNIGHT] | byTypeBB[ROOK] | byTypeBB[BERS]);
+ si->bikjang = ksq != SQ_NONE ? bool(attacks_from(sideToMove, ROOK, ksq) & pieces(sideToMove, KING)) : false;
}
/// Position::attackers_to() computes a bitboard of all pieces which attack a
/// given square. Slider attacks use the occupied bitboard to indicate occupancy.
-Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c) const {
+Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c, Bitboard janggiCannons) const {
Bitboard b = 0;
for (PieceType pt : piece_types())
b |= s2;
}
}
+ else if (pt == JANGGI_CANNON)
+ b |= attacks_bb(~c, move_pt, s, occupied) & attacks_bb(~c, move_pt, s, occupied & ~janggiCannons) & pieces(c, JANGGI_CANNON);
else
b |= attacks_bb(~c, move_pt, s, occupied) & pieces(c, pt);
}
b |= pieces(c, FERS) & gates(c) & fers_sq;
}
+ // Janggi palace moves
+ if (diagonal_lines() & s)
+ {
+ Bitboard diags = 0;
+ if (king_type() == WAZIR)
+ diags |= attacks_bb(~c, FERS, s, occupied) & pieces(c, KING);
+ diags |= attacks_bb(~c, FERS, s, occupied) & pieces(c, WAZIR);
+ diags |= attacks_bb(~c, PAWN, s, occupied) & pieces(c, SOLDIER);
+ diags |= attacks_bb(~c, BISHOP, s, occupied) & pieces(c, ROOK);
+ // TODO: fix for longer diagonals
+ diags |= attacks_bb(~c, ALFIL, s, occupied) & ~attacks_bb(~c, ELEPHANT, s, occupied & ~janggiCannons) & pieces(c, JANGGI_CANNON);
+ b |= diags & diagonal_lines();
+ }
+
if (unpromoted_soldier(c, s))
b ^= b & pieces(SOLDIER) & ~PseudoAttacks[~c][SHOGI_PAWN][s];
if (immobility_illegal() && (type_of(m) == DROP || type_of(m) == NORMAL) && !(moves_bb(us, type_of(moved_piece(m)), to, 0) & board_bb()))
return false;
+ // Illegal passing move
+ if (pass_on_stalemate() && type_of(m) == SPECIAL && from == to && !checkers())
+ {
+ for (const auto& move : MoveList<NON_EVASIONS>(*this))
+ if (!(type_of(move) == SPECIAL && from == to) && legal(move))
+ return false;
+ }
+
// En passant captures are a tricky special case. Because they are rather
// uncommon, we do it simply by testing whether the king is attacked after
// the move is made.
|| !(attackers_to(to, pieces() ^ to_sq(m), ~us));
}
+ Bitboard occupied = (type_of(m) != DROP ? pieces() ^ from : pieces()) | to;
+
// 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))
+ if (attacks_bb(~us, ROOK, s, occupied) & pieces(~us, KING) & ~square_bb(to))
return false;
}
// 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, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us);
+ return type_of(m) == CASTLING || !attackers_to(to, occupied, ~us);
+
+ Bitboard janggiCannons = pieces(JANGGI_CANNON);
+ if (type_of(moved_piece(m)) == JANGGI_CANNON)
+ janggiCannons = (type_of(m) == DROP ? janggiCannons : janggiCannons ^ from) | to;
+ else if (janggiCannons & to)
+ janggiCannons ^= to;
// 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]);
+ return !count<KING>(us) || !(attackers_to(square<KING>(us), occupied, ~us, janggiCannons) & ~SquareBB[to]);
}
else if (!((capture(m) ? attacks_from(us, type_of(pc), from) : moves_from(us, type_of(pc), from)) & to))
return false;
+ // Janggi cannon
+ if (type_of(pc) == JANGGI_CANNON && (pieces(JANGGI_CANNON) & (between_bb(from, to) | to)))
+ return false;
+
// 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() & ~(pieces(CANNON, BANNER) | pieces(HORSE, ELEPHANT)))
+ if (checkers() & ~(pieces(CANNON, BANNER) | pieces(HORSE, ELEPHANT) | pieces(JANGGI_CANNON, JANGGI_ELEPHANT)))
{
if (type_of(pc) != KING)
{
PieceType pt = type_of(moved_piece(m));
if (AttackRiderTypes[pt] & (HOPPING_RIDERS | ASYMMETRICAL_RIDERS))
{
- if (attacks_bb(sideToMove, pt, to, (pieces() ^ from) | to) & square<KING>(~sideToMove))
+ Bitboard occupied = (type_of(m) != DROP ? pieces() ^ from : pieces()) | to;
+ if (attacks_bb(sideToMove, pt, to, occupied) & square<KING>(~sideToMove))
return true;
}
else if (st->checkSquares[pt] & to)
return true;
}
+ Bitboard janggiCannons = pieces(JANGGI_CANNON);
+ if (type_of(moved_piece(m)) == JANGGI_CANNON)
+ janggiCannons = (type_of(m) == DROP ? janggiCannons : janggiCannons ^ from) | to;
+ else if (janggiCannons & to)
+ janggiCannons ^= to;
+
// Is there a discovered check?
- if ( type_of(m) != DROP
- && ((st->blockersForKing[~sideToMove] & from) || (pieces(sideToMove, CANNON, BANNER) | pieces(HORSE, ELEPHANT)))
- && attackers_to(square<KING>(~sideToMove), (pieces() ^ from) | to, sideToMove))
+ if ( ((type_of(m) != DROP && (st->blockersForKing[~sideToMove] & from)) || pieces(sideToMove, CANNON, BANNER)
+ || pieces(HORSE, ELEPHANT) || pieces(JANGGI_CANNON, JANGGI_ELEPHANT))
+ && attackers_to(square<KING>(~sideToMove), (type_of(m) == DROP ? pieces() : pieces() ^ from) | to, sideToMove, janggiCannons))
return true;
// Is there a check by gated pieces?
&& attacks_bb(sideToMove, gating_type(m), gating_square(m), (pieces() ^ from) | to) & square<KING>(~sideToMove))
return true;
+ // Is there a check by special diagonal moves?
+ if (more_than_one(diagonal_lines() & (to | square<KING>(~sideToMove))))
+ {
+ PieceType pt = type_of(moved_piece(m));
+ PieceType diagType = pt == WAZIR ? FERS : pt == SOLDIER ? PAWN : pt == ROOK ? BISHOP : NO_PIECE_TYPE;
+ Bitboard occupied = type_of(m) == DROP ? pieces() : pieces() ^ from;
+ if (diagType && (attacks_bb(sideToMove, diagType, to, occupied) & square<KING>(~sideToMove)))
+ return true;
+ // TODO: fix for longer diagonals
+ else if (pt == JANGGI_CANNON && (attacks_bb(sideToMove, ALFIL, to, occupied) & ~attacks_bb(sideToMove, ELEPHANT, to, occupied) & square<KING>(~sideToMove)))
+ return true;
+ }
+
switch (type_of(m))
{
case NORMAL:
Piece captured = type_of(m) == ENPASSANT ? make_piece(them, PAWN) : piece_on(to);
if (to == from)
{
- assert(type_of(m) == PROMOTION && sittuyin_promotion());
+ assert((type_of(m) == PROMOTION && sittuyin_promotion()) || (type_of(m) == SPECIAL && pass_on_stalemate()));
captured = NO_PIECE;
}
st->capturedpromoted = is_promoted(to);
Square to = to_sq(m);
Piece pc = piece_on(to);
- assert(type_of(m) == DROP || empty(from) || type_of(m) == CASTLING || is_gating(m) || (type_of(m) == PROMOTION && sittuyin_promotion()));
+ assert(type_of(m) == DROP || empty(from) || type_of(m) == CASTLING || is_gating(m)
+ || (type_of(m) == PROMOTION && sittuyin_promotion()) || (type_of(m) == SPECIAL && pass_on_stalemate()));
assert(type_of(st->capturedPiece) != KING);
// Remove gated piece
}
}
}
+ // Check for bikjang rule (Janggi)
+ if (var->bikjangRule && st->pliesFromNull > 0 && st->bikjang && st->previous->bikjang)
+ {
+ // material counting
+ auto weigth_count = [this](PieceType pt, int v){ return v * (count(WHITE, pt) - count(BLACK, pt)); };
+ int materialCount = weigth_count(ROOK, 13)
+ + weigth_count(JANGGI_CANNON, 7)
+ + weigth_count(HORSE, 5)
+ + weigth_count(JANGGI_ELEPHANT, 3)
+ + weigth_count(WAZIR, 3)
+ + weigth_count(SOLDIER, 2)
+ - 1;
+ result = (sideToMove == WHITE) == (materialCount > 0) ? mate_in(ply) : mated_in(ply);
+ return true;
+ }
// Tsume mode: Assume that side with king wins when not in check
if (!count<KING>(~sideToMove) && count<KING>(sideToMove) && !checkers() && Options["TsumeMode"])
{
Bitboard checkSquares[PIECE_TYPE_NB];
bool capturedpromoted;
bool shak;
+ bool bikjang;
int repetition;
};
bool gating() const;
bool seirawan_gating() const;
bool cambodian_moves() const;
+ Bitboard diagonal_lines() const;
+ bool pass_on_stalemate() const;
bool unpromoted_soldier(Color c, Square s) const;
bool makpong() const;
// winning conditions
Bitboard attackers_to(Square s, Color c) const;
Bitboard attackers_to(Square s, Bitboard occupied) const;
Bitboard attackers_to(Square s, Bitboard occupied, Color c) const;
+ Bitboard attackers_to(Square s, Bitboard occupied, Color c, Bitboard janggiCannons) const;
Bitboard attacks_from(Color c, PieceType pt, Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
Bitboard moves_from(Color c, PieceType pt, Square s) const;
return var->cambodianMoves;
}
+inline Bitboard Position::diagonal_lines() const {
+ assert(var != nullptr);
+ return var->diagonalLines;
+}
+
+inline bool Position::pass_on_stalemate() const {
+ assert(var != nullptr);
+ return var->passOnStalemate;
+}
+
inline bool Position::unpromoted_soldier(Color c, Square s) const {
assert(var != nullptr);
return var->xiangqiSoldier && relative_rank(c, s, var->maxRank) <= RANK_5;
return attackers_to(s, byTypeBB[ALL_PIECES], c);
}
+inline Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c) const {
+ return attackers_to(s, occupied, c, byTypeBB[JANGGI_CANNON]);
+}
+
inline Bitboard Position::checkers() const {
return st->checkersBB;
}
ArchbishopValueMg, ChancellorValueMg, AmazonValueMg, KnibisValueMg, BiskniValueMg, KnirooValueMg, RookniValueMg,
ShogiPawnValueMg, LanceValueMg, ShogiKnightValueMg, EuroShogiKnightValueMg, GoldValueMg, DragonHorseValueMg,
ClobberPieceValueMg, BreakthroughPieceValueMg, ImmobilePieceValueMg,
- CannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, BannerValueMg,
+ CannonPieceValueMg, JanggiCannonPieceValueMg, SoldierValueMg, HorseValueMg, ElephantValueMg, JanggiElephantValueMg, BannerValueMg,
WazirValueMg, CommonerValueMg, CentaurValueMg },
{ VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg,
FersValueEg, AlfilValueEg, FersAlfilValueEg, SilverValueEg, AiwokValueEg, BersValueEg,
ArchbishopValueMg, ChancellorValueEg, AmazonValueEg, KnibisValueMg, BiskniValueMg, KnirooValueEg, RookniValueEg,
ShogiPawnValueEg, LanceValueEg, ShogiKnightValueEg, EuroShogiKnightValueEg, GoldValueEg, DragonHorseValueEg,
ClobberPieceValueEg, BreakthroughPieceValueEg, ImmobilePieceValueEg,
- CannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, BannerValueEg,
+ CannonPieceValueEg, JanggiCannonPieceValueEg, SoldierValueEg, HorseValueEg, ElephantValueEg, JanggiElephantValueEg, BannerValueEg,
WazirValueEg, CommonerValueEg, CentaurValueEg }
};
BreakthroughPieceValueMg = 300, BreakthroughPieceValueEg = 300,
ImmobilePieceValueMg = 100, ImmobilePieceValueEg = 100,
CannonPieceValueMg = 800, CannonPieceValueEg = 700,
+ JanggiCannonPieceValueMg = 800, JanggiCannonPieceValueEg = 600,
SoldierValueMg = 150, SoldierValueEg = 300,
HorseValueMg = 500, HorseValueEg = 800,
ElephantValueMg = 300, ElephantValueEg = 300,
+ JanggiElephantValueMg = 350, JanggiElephantValueEg = 350,
BannerValueMg = 3400, BannerValueEg = 3500,
WazirValueMg = 400, WazirValueEg = 400,
CommonerValueMg = 700, CommonerValueEg = 900,
FERS, MET = FERS, ALFIL, FERS_ALFIL, SILVER, KHON = SILVER, AIWOK, BERS, DRAGON = BERS,
ARCHBISHOP, CHANCELLOR, AMAZON, KNIBIS, BISKNI, KNIROO, ROOKNI,
SHOGI_PAWN, LANCE, SHOGI_KNIGHT, EUROSHOGI_KNIGHT, GOLD, DRAGON_HORSE,
- CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, CANNON, SOLDIER, HORSE, ELEPHANT, BANNER,
+ CLOBBER_PIECE, BREAKTHROUGH_PIECE, IMMOBILE_PIECE, CANNON, JANGGI_CANNON,
+ SOLDIER, HORSE, ELEPHANT, JANGGI_ELEPHANT, BANNER,
WAZIR, COMMONER, CENTAUR, KING,
ALL_PIECES = 0,
RIDER_CANNON_V = 1 << 4,
RIDER_HORSE = 1 << 5,
RIDER_ELEPHANT = 1 << 6,
+ RIDER_JANGGI_ELEPHANT = 1 << 7,
HOPPING_RIDERS = RIDER_CANNON_H | RIDER_CANNON_V,
- ASYMMETRICAL_RIDERS = RIDER_HORSE,
+ ASYMMETRICAL_RIDERS = RIDER_HORSE | RIDER_JANGGI_ELEPHANT,
};
extern Value PieceValue[PHASE_NB][PIECE_NB];
}
inline bool is_ok(Move m) {
- return from_sq(m) != to_sq(m) || type_of(m) == PROMOTION; // Catch MOVE_NULL and MOVE_NONE
+ return from_sq(m) != to_sq(m) || type_of(m) == PROMOTION || type_of(m) == SPECIAL; // Catch MOVE_NULL and MOVE_NONE
}
inline int dist(Direction d) {
v->blackDropRegion = v->mobilityRegion[BLACK][ELEPHANT];
return v;
}
+ // Janggi (Korean chess)
+ // https://en.wikipedia.org/wiki/Janggi
+ Variant* janggi_variant() {
+ Variant* v = xiangqi_variant();
+ v->remove_piece(FERS);
+ v->remove_piece(CANNON);
+ v->remove_piece(ELEPHANT);
+ v->add_piece(WAZIR, 'a');
+ v->add_piece(JANGGI_CANNON, 'c');
+ v->add_piece(JANGGI_ELEPHANT, 'b', 'e');
+ v->startFen = "rnba1abnr/4k4/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/4K4/RNBA1ABNR 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][WAZIR] = white_castle;
+ v->mobilityRegion[BLACK][WAZIR] = black_castle;
+ v->xiangqiSoldier = false;
+ v->flyingGeneral = false;
+ v->bikjangRule = true;
+ v->diagonalLines = make_bitboard(SQ_D1, SQ_F1, SQ_E2, SQ_D3, SQ_F3,
+ SQ_D8, SQ_F8, SQ_E9, SQ_D10, SQ_F10);
+ v->passOnStalemate = true;
+ v->nFoldValue = -VALUE_MATE;
+ v->perpetualCheckIllegal = true;
+ return v;
+ }
#endif
} // namespace
add("xiangqi", xiangqi_variant());
add("manchu", manchu_variant());
add("supply", supply_variant());
+ add("janggi", janggi_variant());
#endif
}
bool gating = false;
bool seirawanGating = false;
bool cambodianMoves = false;
+ Bitboard diagonalLines = 0;
+ bool passOnStalemate = false;
bool makpongRule = false;
bool flyingGeneral = false;
bool xiangqiSoldier = false;
Value checkmateValue = -VALUE_MATE;
bool shogiPawnDropMateIllegal = false;
bool shatarMateRule = false;
+ bool bikjangRule = false;
Value bareKingValue = VALUE_NONE;
Value extinctionValue = VALUE_NONE;
bool bareKingMove = false;
# breakthrough
# immobile (piece without moves)
# cannon
+# janggi_cannon
# soldier
# horse
# elephant
+# janggi_elephant
# banner (=rook+cannon+horse)
# wazir
# commoner (non-royal king)
# gating: maintain squares on backrank with extra rights in castling field of FEN [bool] (default: false)
# seirawanGating: allow gating of pieces in hand like in S-Chess, requires "gating = true" [bool] (default: false)
# cambodianMoves: enable special moves of cambodian chess, requires "gating = true" [bool] (default: false)
+# diagonalLines: enable special moves along diagonal for specific squares (Janggi) [Bitboard]
+# passOnStalemate: allow passing by king in case of stalemate [bool] (default: false)
# makpongRule: the king may not move away from check [bool] (default: false)
# flyingGeneral: disallow general face-off like in xiangqi [bool] (default: false)
# xiangqiSoldier: restrict soldier to shogi pawn movements on first five ranks [bool] (default: false)
# checkmateValue: result in case of checkmate [Value] (default: loss)
# shogiPawnDropMateIllegal: prohibit checkmate via shogi pawn drops [bool] (default: false)
# shatarMateRule: enable shatar mating rules [bool] (default: false)
+# bikjangRule: enable Janggi bikjang rule (facing kings) [bool] (default: false)
# bareKingValue: result when player only has a lone/bare king [Value] (default: <none>)
# extinctionValue: result when one of extinctionPieceTypes is extinct [Value] (default: <none>)
# bareKingMove: allow additional move by opponent after lone/bare king position [bool] (default: false)
whiteDropRegion = *1 *2 *3 *4 *5
blackDropRegion = *6 *7 *8 *9 *10
+# Hybrid variant of janggi and crazyhouse
+[janggihouse:janggi]
+pieceDrops = true
+capturesToHand = true
+
# Hybrid variant of antichess and losalamos
[anti-losalamos:losalamos]
king = -
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
+ expect perft.exp janggi startpos 4 948462 > /dev/null
+ expect perft.exp janggi "fen 1n1kaabn1/cr2N4/5C1c1/p1pNp3p/9/9/P1PbP1P1P/3r1p3/4A4/R1BA1KB1R b - - 0 1" 4 70254 > /dev/null
fi
rm perft.exp