Support Janggi (Korean chess)
authorFabian Fichter <ianfab@users.noreply.github.com>
Sun, 29 Mar 2020 21:36:27 +0000 (23:36 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Mon, 30 Mar 2020 16:20:40 +0000 (18:20 +0200)
https://en.wikipedia.org/wiki/Janggi

Closes #40.

bench: 4801778

15 files changed:
Readme.md
src/bitboard.cpp
src/bitboard.h
src/magic.h
src/movegen.cpp
src/parser.cpp
src/piece.cpp
src/position.cpp
src/position.h
src/psqt.cpp
src/types.h
src/variant.cpp
src/variant.h
src/variants.ini
tests/perft.sh

index 95856a2..d6f012c 100644 (file)
--- a/Readme.md
+++ b/Readme.md
@@ -16,6 +16,7 @@ The games currently supported besides chess are listed below. Fairy-Stockfish ca
 ### 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)
index 0489409..5efe8c8 100644 (file)
@@ -46,6 +46,7 @@ Magic CannonMagicsH[SQUARE_NB];
 Magic CannonMagicsV[SQUARE_NB];
 Magic HorseMagics[SQUARE_NB];
 Magic ElephantMagics[SQUARE_NB];
+Magic JanggiElephantMagics[SQUARE_NB];
 
 namespace {
 
@@ -57,6 +58,7 @@ 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
@@ -65,6 +67,7 @@ namespace {
   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 };
@@ -172,6 +175,18 @@ const std::string Bitboards::pretty(Bitboard b) {
 
 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)
   {
@@ -181,51 +196,53 @@ void Bitboards::init() {
       {
           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;
       }
   }
@@ -244,14 +261,6 @@ void Bitboards::init() {
       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);
@@ -260,6 +269,7 @@ void Bitboards::init() {
   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);
@@ -268,6 +278,7 @@ void Bitboards::init() {
   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 })
index 8c6d434..3e8f05a 100644 (file)
@@ -147,6 +147,7 @@ extern Magic CannonMagicsH[SQUARE_NB];
 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);
@@ -353,13 +354,14 @@ 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
-         || 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)];
 }
@@ -391,6 +393,8 @@ inline Bitboard attacks_bb(Color c, PieceType pt, Square s, Bitboard 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];
 }
 
@@ -410,6 +414,8 @@ inline Bitboard moves_bb(Color c, PieceType pt, Square s, Bitboard occupied) {
       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];
 }
 
index 478f3f7..d24fe46 100644 (file)
       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
 
index efea6ae..38dc282 100644 (file)
@@ -268,6 +268,11 @@ namespace {
         // 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);
@@ -341,6 +346,10 @@ namespace {
         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))
@@ -384,6 +393,28 @@ namespace {
         }
     }
 
+    // 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;
   }
 
@@ -464,7 +495,7 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* 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())
index 01d9051..b8b9ebd 100644 (file)
@@ -241,6 +241,8 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     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);
@@ -254,6 +256,7 @@ Variant* VariantParser<DoCheck>::parse(Variant* v) {
     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);
index 82f938c..258959f 100644 (file)
@@ -298,6 +298,14 @@ namespace {
       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";
@@ -320,6 +328,21 @@ namespace {
       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";
@@ -371,9 +394,11 @@ void PieceMap::init() {
   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());
index 08d1336..ab44621 100644 (file)
@@ -499,6 +499,7 @@ void Position::set_check_info(StateInfo* si) const {
       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;
 }
 
 
@@ -740,7 +741,7 @@ Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners
 /// 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())
@@ -758,6 +759,8 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c) const {
                       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);
       }
@@ -770,6 +773,20 @@ Bitboard Position::attackers_to(Square s, Bitboard occupied, Color c) const {
           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];
 
@@ -856,6 +873,14 @@ bool Position::legal(Move m) const {
   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.
@@ -897,11 +922,13 @@ bool Position::legal(Move m) const {
             || !(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;
   }
 
@@ -913,10 +940,16 @@ bool Position::legal(Move m) const {
   // 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]);
 }
 
 
@@ -994,10 +1027,14 @@ bool Position::pseudo_legal(const Move m) const {
   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)
       {
@@ -1041,17 +1078,24 @@ bool Position::gives_check(Move m) const {
       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?
@@ -1059,6 +1103,19 @@ bool Position::gives_check(Move m) const {
       && 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:
@@ -1138,7 +1195,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
   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);
@@ -1420,7 +1477,8 @@ void Position::undo_move(Move m) {
   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
@@ -1881,6 +1939,21 @@ bool Position::is_immediate_game_end(Value& result, int ply) const {
           }
       }
   }
+  // 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"])
   {
index 31a9a63..b458136 100644 (file)
@@ -62,6 +62,7 @@ struct StateInfo {
   Bitboard   checkSquares[PIECE_TYPE_NB];
   bool       capturedpromoted;
   bool       shak;
+  bool       bikjang;
   int        repetition;
 };
 
@@ -140,6 +141,8 @@ public:
   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
@@ -203,6 +206,7 @@ public:
   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;
@@ -569,6 +573,16 @@ inline bool Position::cambodian_moves() 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;
@@ -838,6 +852,10 @@ inline Bitboard Position::attackers_to(Square s, Color c) const {
   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;
 }
index a483ea4..b805247 100644 (file)
@@ -30,14 +30,14 @@ Value PieceValue[PHASE_NB][PIECE_NB] = {
     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 }
 };
 
index d3b7662..c643ca6 100644 (file)
@@ -338,9 +338,11 @@ enum Value : int {
   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,
@@ -356,7 +358,8 @@ enum PieceType {
   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,
 
@@ -382,8 +385,9 @@ enum RiderType {
   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];
@@ -726,7 +730,7 @@ constexpr PieceType in_hand_piece_type(Move m) {
 }
 
 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) {
index 3dfcf4a..c1fe12e 100644 (file)
@@ -850,6 +850,35 @@ namespace {
         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
@@ -933,6 +962,7 @@ void VariantMap::init() {
     add("xiangqi", xiangqi_variant());
     add("manchu", manchu_variant());
     add("supply", supply_variant());
+    add("janggi", janggi_variant());
 #endif
 }
 
index 0ee06d3..682d38e 100644 (file)
@@ -86,6 +86,8 @@ struct Variant {
   bool gating = false;
   bool seirawanGating = false;
   bool cambodianMoves = false;
+  Bitboard diagonalLines = 0;
+  bool passOnStalemate = false;
   bool makpongRule = false;
   bool flyingGeneral = false;
   bool xiangqiSoldier = false;
@@ -100,6 +102,7 @@ struct Variant {
   Value checkmateValue = -VALUE_MATE;
   bool shogiPawnDropMateIllegal = false;
   bool shatarMateRule = false;
+  bool bikjangRule = false;
   Value bareKingValue = VALUE_NONE;
   Value extinctionValue = VALUE_NONE;
   bool bareKingMove = false;
index 399f0ed..f481323 100644 (file)
 # 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)
@@ -224,6 +229,11 @@ dropChecks = 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 = -
index 1baba94..f1eab99 100755 (executable)
@@ -74,6 +74,8 @@ if [[ $1 == "largeboard" ]]; then
   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