Support grand chess
authorFabian Fichter <ianfab@users.noreply.github.com>
Fri, 16 Aug 2019 16:15:59 +0000 (18:15 +0200)
committerFabian Fichter <ianfab@users.noreply.github.com>
Fri, 16 Aug 2019 17:32:31 +0000 (19:32 +0200)
https://en.wikipedia.org/wiki/Grand_Chess

Readme.md
src/movegen.cpp
src/position.cpp
src/position.h
src/variant.cpp
src/variant.h
tests/perft.sh

index 00b4725..5733f03 100644 (file)
--- a/Readme.md
+++ b/Readme.md
@@ -15,7 +15,8 @@ Besides chess, the currently supported games are:
 - [Shogi](https://en.wikipedia.org/wiki/Shogi)
 
 **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), [Shako](https://www.chessvariants.com/large.dir/shako.html)
+- [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)
+- [Grand](https://en.wikipedia.org/wiki/Grand_Chess), [Shako](https://www.chessvariants.com/large.dir/shako.html)
 - [Chess960](https://en.wikipedia.org/wiki/Chess960), [Placement/Pre-Chess](https://www.chessvariants.com/link/placement-chess)
 - [Crazyhouse](https://en.wikipedia.org/wiki/Crazyhouse), [Loop](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Chessgi](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Pocket Knight](http://www.chessvariants.com/other.dir/pocket.html)
 - [Seirawan](https://en.wikipedia.org/wiki/Seirawan_chess)
index 3263266..7542044 100644 (file)
@@ -48,7 +48,8 @@ namespace {
 
     if (Type == CAPTURES || Type == EVASIONS || Type == NON_EVASIONS)
         for (PieceType pt : pos.promotion_piece_types())
-            *moveList++ = make<PROMOTION>(to - D, to, pt);
+            if (!pos.promotion_limit(pt) || pos.promotion_limit(pt) > pos.count(c, pt))
+                *moveList++ = make<PROMOTION>(to - D, to, pt);
 
     return moveList;
   }
@@ -95,10 +96,11 @@ namespace {
 
     Bitboard emptySquares;
 
-    Bitboard TRank8BB = rank_bb(Us == WHITE ? pos.promotion_rank() : Rank(pos.max_rank() - pos.promotion_rank()));
+    Bitboard TRank8BB = pos.mandatory_pawn_promotion() ? rank_bb(relative_rank(Us, pos.promotion_rank(), pos.max_rank()))
+                                                       : promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank());
     Bitboard TRank7BB = shift<Down>(TRank8BB);
     Bitboard pawnsOn7    = pos.pieces(Us, PAWN) &  TRank7BB;
-    Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
+    Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & (pos.mandatory_pawn_promotion() ? ~TRank7BB : AllSquares);
 
     Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
                         Type == CAPTURES ? target : pos.pieces(Them));
index 1eede29..5a3e197 100644 (file)
@@ -914,7 +914,7 @@ bool Position::pseudo_legal(const Move m) const {
   {
       // We have already handled promotion moves, so destination
       // cannot be on the 8th/1st rank.
-      if (rank_of(to) == relative_rank(us, promotion_rank(), max_rank()))
+      if (mandatory_pawn_promotion() && (promotion_zone_bb(us, promotion_rank(), max_rank()) & to))
           return false;
 
       if (   !(attacks_from<PAWN>(us, from) & pieces(~us) & to) // Not a capture
@@ -1210,7 +1210,7 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
       {
           Piece promotion = make_piece(us, promotion_type(m));
 
-          assert(relative_rank(us, to, max_rank()) == promotion_rank() || sittuyin_promotion());
+          assert(relative_rank(us, to, max_rank()) >= promotion_rank() || sittuyin_promotion());
           assert(type_of(promotion) >= KNIGHT && type_of(promotion) < KING);
 
           remove_piece(pc, to);
@@ -1387,7 +1387,7 @@ void Position::undo_move(Move m) {
 
   if (type_of(m) == PROMOTION)
   {
-      assert(relative_rank(us, to, max_rank()) == promotion_rank() || sittuyin_promotion());
+      assert(relative_rank(us, to, max_rank()) >= promotion_rank() || sittuyin_promotion());
       assert(type_of(pc) == promotion_type(m));
       assert(type_of(pc) >= KNIGHT && type_of(pc) < KING);
 
index cef06f7..c4c6ce8 100644 (file)
@@ -101,8 +101,10 @@ public:
   Rank promotion_rank() const;
   const std::set<PieceType, std::greater<PieceType> >& promotion_piece_types() const;
   bool sittuyin_promotion() const;
+  int promotion_limit(PieceType pt) const;
   PieceType promoted_piece_type(PieceType pt) const;
   bool piece_promotion_on_capture() const;
+  bool mandatory_pawn_promotion() const;
   bool mandatory_piece_promotion() const;
   bool piece_demotion() const;
   bool endgame_eval() const;
@@ -338,6 +340,11 @@ inline bool Position::sittuyin_promotion() const {
   return var->sittuyinPromotion;
 }
 
+inline int Position::promotion_limit(PieceType pt) const {
+  assert(var != nullptr);
+  return var->promotionLimit[pt];
+}
+
 inline PieceType Position::promoted_piece_type(PieceType pt) const {
   assert(var != nullptr);
   return var->promotedPieceType[pt];
@@ -348,6 +355,11 @@ inline bool Position::piece_promotion_on_capture() const {
   return var->piecePromotionOnCapture;
 }
 
+inline bool Position::mandatory_pawn_promotion() const {
+  assert(var != nullptr);
+  return var->mandatoryPawnPromotion;
+}
+
 inline bool Position::mandatory_piece_promotion() const {
   assert(var != nullptr);
   return var->mandatoryPiecePromotion;
index 4550639..30421c4 100644 (file)
@@ -605,6 +605,27 @@ VariantMap variants; // Global object
         v->stalemateValue = -VALUE_MATE;
         return v;
     }
+    Variant* grand_variant() {
+        Variant* v = fairy_variant_base();
+        v->maxRank = RANK_10;
+        v->maxFile = FILE_J;
+        v->add_piece(ARCHBISHOP, 'a');
+        v->add_piece(CHANCELLOR, 'c');
+        v->startFen = "r8r/1nbqkcabn1/pppppppppp/10/10/10/10/PPPPPPPPPP/1NBQKCABN1/R8R w - - 0 1";
+        v->promotionPieceTypes = {ARCHBISHOP, CHANCELLOR, QUEEN, ROOK, BISHOP, KNIGHT};
+        v->promotionRank = RANK_8;
+        v->promotionLimit[ARCHBISHOP] = 1;
+        v->promotionLimit[CHANCELLOR] = 1;
+        v->promotionLimit[QUEEN] = 1;
+        v->promotionLimit[ROOK] = 2;
+        v->promotionLimit[BISHOP] = 2;
+        v->promotionLimit[KNIGHT] = 2;
+        v->mandatoryPawnPromotion = false;
+        v->immobilityIllegal = true;
+        v->doubleStepRank = RANK_3;
+        v->castling = false;
+        return v;
+    }
     Variant* shako_variant() {
         Variant* v = fairy_variant_base();
         v->maxRank = RANK_10;
@@ -688,6 +709,7 @@ void VariantMap::init() {
     add("embassy", embassy_variant());
     add("jesonmor", jesonmor_variant());
     add("courier", courier_variant());
+    add("grand", grand_variant());
     add("shako", shako_variant());
     add("clobber10", clobber10_variant());
 #endif
index 5e72c34..e5abcdd 100644 (file)
@@ -44,8 +44,10 @@ struct Variant {
   Rank promotionRank = RANK_8;
   std::set<PieceType, std::greater<PieceType> > promotionPieceTypes = { QUEEN, ROOK, BISHOP, KNIGHT };
   bool sittuyinPromotion = false;
+  uint8_t promotionLimit[PIECE_TYPE_NB] = {}; // 0 means unlimited
   PieceType promotedPieceType[PIECE_TYPE_NB] = {};
   bool piecePromotionOnCapture = false;
+  bool mandatoryPawnPromotion = true;
   bool mandatoryPiecePromotion = false;
   bool pieceDemotion = false;
   bool endgameEval = false;
index 8815c84..f77fe3c 100755 (executable)
@@ -67,6 +67,7 @@ if [[ $1 == "largeboard" ]]; then
   expect perft.exp modern startpos 4 433729 > /dev/null
   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
 fi
 
 rm perft.exp