Support xiangqi and minixiangqi
authorFabian Fichter <ianfab@users.noreply.github.com>
Thu, 7 Nov 2019 22:00:25 +0000 (23:00 +0100)
committerFabian Fichter <ianfab@users.noreply.github.com>
Fri, 8 Nov 2019 14:50:08 +0000 (15:50 +0100)
New features:
- Piece types soldier, horse, and elephant
- Restriction of piece mobility
- Flying general rule
- Piece synonyms

Closes #31.

bench: 4448277

17 files changed:
Readme.md
src/bitboard.cpp
src/bitboard.h
src/evaluate.cpp
src/magic.h
src/movegen.cpp
src/parser.cpp
src/pawns.cpp
src/piece.cpp
src/piece.h
src/position.cpp
src/position.h
src/psqt.cpp
src/types.h
src/variant.cpp
src/variant.h
tests/perft.sh

index 56e435f..4e6d993 100644 (file)
--- 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)
index 47c969e..cc53154 100644 (file)
@@ -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 <MovementType MT>
 #ifdef PRECOMPUTED_MAGICS
@@ -72,6 +78,7 @@ namespace {
 
   template <MovementType MT>
   Bitboard sliding_attack(std::vector<Direction> 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<Direction> directions, Square s) {
+    Bitboard b = 0;
+    for (Direction d : directions)
+        b |= lame_leaper_path(d, s);
+    return b;
+  }
+
+  Bitboard lame_leaper_attack(std::vector<Direction> directions, Square s, Bitboard occupied) {
+    Bitboard b = 0;
+    for (Direction d : directions)
+    {
+        Square to = s + d;
+        if (is_ok(to) && distance(s, to) < 4 && !(lame_leaper_path(d, s) & occupied))
+            b |= to;
+    }
+    return b;
+  }
 }
 
 
@@ -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<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);
@@ -188,12 +258,16 @@ void Bitboards::init() {
   init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections, BishopMagicInit);
   init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH, CannonMagicHInit);
   init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV, CannonMagicVInit);
+  init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections, HorseMagicInit);
+  init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections, ElephantMagicInit);
 #else
   init_magics<RIDER>(RookTableH, RookMagicsH, RookDirectionsH);
   init_magics<RIDER>(RookTableV, RookMagicsV, RookDirectionsV);
   init_magics<RIDER>(BishopTable, BishopMagics, BishopDirections);
   init_magics<HOPPER>(CannonTableH, CannonMagicsH, RookDirectionsH);
   init_magics<HOPPER>(CannonTableV, CannonMagicsV, RookDirectionsV);
+  init_magics<LAME_LEAPER>(HorseTable, HorseMagics, HorseDirections);
+  init_magics<LAME_LEAPER>(ElephantTable, ElephantMagics, ElephantDirections);
 #endif
 
   for (Color c : { WHITE, BLACK })
@@ -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<RIDER>(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<MT == HOPPER ? RIDER : MT>(directions, s, 0) & ~edges;
+        m.mask  = (MT == LAME_LEAPER ? lame_leaper_path(directions, s) : sliding_attack<MT == HOPPER ? RIDER : MT>(directions, s, 0)) & ~edges;
 #ifdef LARGEBOARDS
         m.shift = 128 - popcount(m.mask);
 #else
@@ -299,7 +375,7 @@ namespace {
         b = size = 0;
         do {
             occupancy[size] = b;
-            reference[size] = sliding_attack<MT>(directions, s, b);
+            reference[size] = MT == LAME_LEAPER ? lame_leaper_attack(directions, s, b) : sliding_attack<MT>(directions, s, b);
 
             if (HasPext)
                 m.attacks[pext(b, m.mask)] = reference[size];
index 7253bec..fdc8848 100644 (file)
@@ -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<class T> constexpr const T& clamp(const T& v, const T& lo, const T&  hi
 template<RiderType R>
 inline Bitboard rider_attacks_bb(Square s, Bitboard occupied) {
 
-  assert(R == RIDER_BISHOP || R == RIDER_ROOK_H || R == RIDER_ROOK_V || R == RIDER_CANNON_H || R == RIDER_CANNON_V);
+  assert(R == RIDER_BISHOP || R == RIDER_ROOK_H || R == RIDER_ROOK_V || R == RIDER_CANNON_H || R == RIDER_CANNON_V
+         || R == RIDER_HORSE || R == RIDER_ELEPHANT);
   const Magic& m =  R == RIDER_ROOK_H ? RookMagicsH[s]
                   : R == RIDER_ROOK_V ? RookMagicsV[s]
                   : R == RIDER_CANNON_H ? CannonMagicsH[s]
                   : R == RIDER_CANNON_V ? CannonMagicsV[s]
+                  : R == RIDER_HORSE ? HorseMagics[s]
+                  : R == RIDER_ELEPHANT ? ElephantMagics[s]
                   : BishopMagics[s];
   return m.attacks[m.index(occupied)];
 }
@@ -372,10 +377,14 @@ inline Bitboard attacks_bb(Color c, PieceType pt, Square s, Bitboard occupied) {
       b |= rider_attacks_bb<RIDER_ROOK_H>(s, occupied);
   if (AttackRiderTypes[pt] & RIDER_ROOK_V)
       b |= rider_attacks_bb<RIDER_ROOK_V>(s, occupied);
-  if (AttackRiderTypes[pt] & RIDER_ROOK_H)
-      b |= rider_attacks_bb<RIDER_ROOK_H>(s, occupied);
+  if (AttackRiderTypes[pt] & RIDER_CANNON_H)
+      b |= rider_attacks_bb<RIDER_CANNON_H>(s, occupied);
   if (AttackRiderTypes[pt] & RIDER_CANNON_V)
       b |= rider_attacks_bb<RIDER_CANNON_V>(s, occupied);
+  if (AttackRiderTypes[pt] & RIDER_HORSE)
+      b |= rider_attacks_bb<RIDER_HORSE>(s, occupied);
+  if (AttackRiderTypes[pt] & RIDER_ELEPHANT)
+      b |= rider_attacks_bb<RIDER_ELEPHANT>(s, occupied);
   return b & PseudoAttacks[c][pt][s];
 }
 
@@ -391,6 +400,10 @@ inline Bitboard moves_bb(Color c, PieceType pt, Square s, Bitboard occupied) {
       b |= rider_attacks_bb<RIDER_CANNON_H>(s, occupied);
   if (MoveRiderTypes[pt] & RIDER_CANNON_V)
       b |= rider_attacks_bb<RIDER_CANNON_V>(s, occupied);
+  if (MoveRiderTypes[pt] & RIDER_HORSE)
+      b |= rider_attacks_bb<RIDER_HORSE>(s, occupied);
+  if (MoveRiderTypes[pt] & RIDER_ELEPHANT)
+      b |= rider_attacks_bb<RIDER_ELEPHANT>(s, occupied);
   return b & PseudoMoves[c][pt][s];
 }
 
index f87f3b3..cffcddf 100644 (file)
@@ -342,6 +342,9 @@ namespace {
         if (pos.captures_to_hand() && pos.count<KING>(Them) && pos.count<KING>(Us))
             score -= KingProximity * distance(s, pos.square<KING>(Us)) * distance(s, pos.square<KING>(Them));
 
+        if (Pt == SOLDIER && pos.unpromoted_soldier(Us, s))
+            score -= make_score(PieceValue[MG][Pt], PieceValue[EG][Pt]) / 2;
+
         if (Pt == BISHOP || Pt == KNIGHT)
         {
             // Bonus if piece is on an outpost square or can reach one
index 1fae2e3..7dd0dfe 100644 (file)
       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
 
index d303842..b418016 100644 (file)
@@ -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<Up>(pawnsNotOn7)   & emptySquares;
         Bitboard b2 = pos.double_step_enabled() ? shift<Up>(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<QUIET_CHECKS>(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<KING>(~us)];
@@ -452,6 +452,17 @@ ExtMove* generate<EVASIONS>(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<KING>(us, ksq) & target;
+      while (b)
+          moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
+      return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
+                         : generate_all<BLACK, EVASIONS>(pos, moveList, target);
+  }
+
   // Find all the squares attacked by slider checkers. We will remove them from
   // the king evasions in order to skip known illegal moves, which avoids any
   // useless legality checks later on.
@@ -462,7 +473,7 @@ ExtMove* generate<EVASIONS>(const Position& pos, ExtMove* moveList) {
   }
 
   // Generate evasions for king, capture and non capture moves
-  Bitboard b = pos.attacks_from<KING>(us, ksq) & ~pos.pieces(us) & ~sliderAttacks & pos.board_bb();
+  Bitboard b = pos.attacks_from<KING>(us, ksq) & ~pos.pieces(us) & ~sliderAttacks;
   while (b)
       moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
 
index ee7dd26..8c70e76 100644 (file)
@@ -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);
index 8746565..35e35ed 100644 (file)
@@ -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);
index 9699517..199c861 100644 (file)
@@ -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());
index af6e4b8..55c6505 100644 (file)
@@ -36,6 +36,7 @@ struct PieceInfo {
   std::vector<Direction> sliderCapture = {};
   std::vector<Direction> hopperQuiet = {};
   std::vector<Direction> hopperCapture = {};
+  bool lameLeaper = false;
 
   void merge(const PieceInfo* pi);
 };
index b3ce0c4..05f597f 100644 (file)
@@ -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<KING>(us))
+  {
+      Square s = type_of(moved_piece(m)) == KING ? to : square<KING>(us);
+      if (attacks_bb(~us, ROOK, s, (pieces() ^ from) | to) & pieces(~us, KING) & ~square_bb(to))
+          return false;
+  }
+
+  // Xiangqi general
+  if (var->xiangqiGeneral && type_of(moved_piece(m)) == KING && !(PseudoAttacks[us][WAZIR][from] & to))
+      return false;
+
+  // Xiangqi soldier
+  if (type_of(moved_piece(m)) == SOLDIER && unpromoted_soldier(us, from) && file_of(from) != file_of(to))
+      return false;
+
   // If the moving piece is a king, check whether the destination
   // square is attacked by the opponent. Castling moves are checked
   // for legality during move generation.
   if (type_of(moved_piece(m)) == KING)
-      return type_of(m) == CASTLING || !attackers_to(to, ~us);
+      return type_of(m) == CASTLING || !attackers_to(to, (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us);
 
   // A non-king move is legal if the king is not under attack after the move.
   return !count<KING>(us) || !(attackers_to(square<KING>(us), (type_of(m) != DROP ? pieces() ^ from : pieces()) | to, ~us) & ~SquareBB[to]);
@@ -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<KING>(~sideToMove))
+              return true;
+      }
+      else if (st->checkSquares[type_of(moved_piece(m))] & to)
+          return true;
+  }
 
   // Is there a discovered check?
   if (   type_of(m) != DROP
-      && ((st->blockersForKing[~sideToMove] & from) || pieces(sideToMove, CANNON))
+      && ((st->blockersForKing[~sideToMove] & from) || pieces(sideToMove, CANNON, HORSE))
       && attackers_to(square<KING>(~sideToMove), (pieces() ^ from) | to, sideToMove))
       return true;
 
index 19970bd..0b7a83d 100644 (file)
@@ -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<PieceType>& piece_types() const;
   const std::string piece_to_char() const;
+  const std::string piece_to_char_synonyms() const;
   Rank promotion_rank() const;
   const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types() const;
   bool sittuyin_promotion() const;
@@ -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<PieceType>& 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<PieceType Pt>
 inline Bitboard Position::attacks_from(Color c, Square s) const {
-  return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]);
+  return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, Pt);
 }
 
 inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const {
-  return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]);
+  return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
 }
 
 inline Bitboard Position::moves_from(Color c, PieceType pt, Square s) const {
-  return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]);
+  return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
 }
 
 inline Bitboard Position::attackers_to(Square s) const {
index d197f03..c34cc32 100644 (file)
@@ -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 {
index 67a664e..83f62c2 100644 (file)
@@ -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];
index 67a0504..dcaccbf 100644 (file)
@@ -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
 }
 
index a574df0..3300ede 100644 (file)
@@ -40,7 +40,9 @@ struct Variant {
   std::set<PieceType> pieceTypes = { PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING };
   std::string pieceToChar =  " PNBRQ" + std::string(KING - QUEEN - 1, ' ') + "K" + std::string(PIECE_TYPE_NB - KING - 1, ' ')
                            + " pnbrq" + std::string(KING - QUEEN - 1, ' ') + "k" + std::string(PIECE_TYPE_NB - KING - 1, ' ');
+  std::string pieceToCharSynonyms = std::string(PIECE_NB, ' ');
   std::string startFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
+  Bitboard mobilityRegion[COLOR_NB][PIECE_TYPE_NB] = {};
   Rank promotionRank = RANK_8;
   std::set<PieceType, std::greater<PieceType> > promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT };
   bool sittuyinPromotion = false;
@@ -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();
   }
 };
index f77fe3c..8c564c9 100755 (executable)
@@ -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