Implement LionChess
authorH.G. Muller <h.g.muller@hccnet.nl>
Mon, 30 Sep 2013 20:14:15 +0000 (22:14 +0200)
committerH.G. Muller <h.g.muller@hccnet.nl>
Sun, 22 Dec 2013 22:31:28 +0000 (23:31 +0100)
Add legality testing for Lions, so it ca be played with legality testing on.
Cyan markers are now also used in autonomous highlighting, so that double-
moving is triggered: A new move type FirstLeg instructs the marker callback
to use cyan on Lion contact captures. Igui required a special test, to make
XBoard realize the fial square is empty. Implement Lion-capture rules.
Forbid Pawn promotion to Lion: The Lion is skipped during the promotion
sweep (and it was not in the promotion popp anyway). Chu should not suffer
from this, as the piece used as promoted Kylin is not a real Lion (although
it looks like one). King is not considered a protector in Mighty-Lion Chess
This rule was added to allow trading Lions in the late end-game,
to prevent the game from being too drawish.

backend.c
common.h
dialogs.c
moves.c
moves.h

index 5774c4d..001f970 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -566,6 +566,13 @@ ChessSquare aseanArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj
         BlackKing, BlackMan, BlackKnight, BlackRook }
 };
 
+ChessSquare  lionArray[2][BOARD_FILES] = {
+    { WhiteRook, WhiteLion, WhiteBishop, WhiteQueen,
+       WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
+    { BlackRook, BlackLion, BlackBishop, BlackQueen,
+       BlackKing, BlackBishop, BlackKnight, BlackRook }
+};
+
 
 #if (BOARD_FILES>=10)
 ChessSquare ShogiArray[2][BOARD_FILES] = {
@@ -1195,6 +1202,7 @@ InitBackEnd1 ()
       case VariantSChess:     /* S-Chess, should work */
       case VariantGrand:      /* should work */
       case VariantSpartan:    /* should work */
+      case VariantLion:       /* should work */
        break;
       }
     }
@@ -5235,7 +5243,7 @@ UploadGameEvent ()
     SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n");
 }
 
-static int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
+int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
 
 void
 CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7])
@@ -5289,7 +5297,7 @@ Sweep (int step)
        else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
        if(!step) step = -1;
     } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
-           appData.testLegality && promoSweep == king ||
+           appData.testLegality && (promoSweep == king || promoSweep == WhiteLion || promoSweep == BlackLion) ||
            IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED promoSweep && last != promoSweep);
     if(toX >= 0) {
        int victim = boards[currentMove][toY][toX];
@@ -6031,6 +6039,10 @@ InitPosition (int redraw)
       pieces = SpartanArray;
       SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k");
       break;
+    case VariantLion:
+      pieces = lionArray;
+      SetCharTable(pieceToChar, "PNBRQ................LKpnbrq................lk");
+      break;
     case VariantFairy:
       pieces = fairyArray;
       SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk");
@@ -7106,10 +7118,10 @@ Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VO
 {
     typedef char Markers[BOARD_RANKS][BOARD_FILES];
     Markers *m = (Markers *) closure;
-    if(rf == fromY && ff == fromX)
+    if(rf == fromY && ff == fromX && (killX < 0 && !(rt == rf && ft == ff) || abs(ft-killX) < 2 && abs(rt-killY) < 2))
        (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
                         || kind == WhiteCapturesEnPassant
-                        || kind == BlackCapturesEnPassant);
+                        || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0);
     else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
 }
 
@@ -7488,6 +7500,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
          MarkTargetSquares(1);
          if(x == killX && y == killY) killX = killY = -1; else {
            killX = x; killY = y;     //remeber this square as intermediate
+           MarkTargetSquares(0);
            ReportClick("put", x, y); // and inform engine
            ReportClick("lift", x, y);
            return;
@@ -9639,14 +9652,22 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
        }
         piece = board[toY][toX] = (ChessSquare) fromX;
   } else {
+      ChessSquare victim;
       int i;
 
       if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something
+           victim = board[killY][killX],
            board[killY][killX] = EmptySquare,
            board[EP_STATUS] = EP_CAPTURE;
 
-      if( board[toY][toX] != EmptySquare )
+      if( board[toY][toX] != EmptySquare ) {
            board[EP_STATUS] = EP_CAPTURE;
+           if( (fromX != toX || fromY != toY) && // not igui!
+               (captured == WhiteLion && board[fromY][fromX] != BlackLion ||
+                captured == BlackLion && board[fromY][fromX] != WhiteLion   ) ) { // [HGM] lion: Chu Lion-capture rules
+               board[EP_STATUS] = EP_IRON_LION; // non-Lion x Lion: no counter-strike allowed
+           }
+      }
 
       if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) {
            if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi )
@@ -9932,7 +9953,6 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                board[BOARD_HEIGHT-1-k][0] = EmptySquare;
        }
     }
-
 }
 
 /* Updates forwardMostMove */
index f06fbcf..3e8adbe 100644 (file)
--- a/common.h
+++ b/common.h
@@ -289,7 +289,7 @@ typedef enum {
     WhitePromotion, WhiteNonPromotion,
     BlackPromotion, BlackNonPromotion,
     WhiteCapturesEnPassant, BlackCapturesEnPassant,
-    WhiteDrop, BlackDrop,
+    WhiteDrop, BlackDrop, FirstLeg,
     NormalMove, AmbiguousMove, IllegalMove, ImpossibleMove,
     WhiteWins, BlackWins, GameIsDrawn, GameUnfinished,
     GNUChessGame, XBoardGame, MoveNumberOne, Open, Close, Nothing,
@@ -353,6 +353,7 @@ typedef enum {
     VariantSChess,
     VariantGrand,
     VariantSpartan,
+    VariantLion,
     VariantUnknown       /* Catchall for other unknown variants */
 } VariantClass;
 
@@ -401,6 +402,7 @@ typedef enum {
   "seirawan",\
   "grand",\
   "spartan",\
+  "lion",\
   "unknown" \
 }
 
index 4db3da7..84aaf3a 100644 (file)
--- a/dialogs.c
+++ b/dialogs.c
@@ -447,7 +447,8 @@ static Option variantDescriptors[] = {
 { VariantXiangqi, SAME_ROW,135, NULL, (void*) &Pick, "#BFFFFF", NULL, Button, N_("xiangqi (9x10)")},
 { VariantFairy,         0, 135, NULL, (void*) &Pick, "#BFBFBF", NULL, Button, N_("fairy")},
 { VariantCourier, SAME_ROW,135, NULL, (void*) &Pick, "#BFFFBF", NULL, Button, N_("courier (12x8)")},
-{ VariantNormal,        0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_(" ")}, // dummy, to have good alignment
+//{ VariantNormal,        0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, Button, N_(" ")}, // dummy, to have good alignment
+{ VariantLion,          0, 135, NULL, (void*) &Pick, "#BFBFBF", NULL, Button, N_("mighty lion")},
 { VariantChu,    SAME_ROW, 135, NULL, (void*) &Pick, "#BFFFBF", NULL, Button, N_("chu shogi (12x12)")},
 // optional buttons for engine-defined variants
 { VariantUnknown,       0, 135, NULL, (void*) &Pick, "#FFFFFF", NULL, -1, NULL },
diff --git a/moves.c b/moves.c
index 381b188..ad68656 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -697,6 +697,16 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure,
              }
             break;
 
+            case WhiteLion:
+            case BlackLion:
+              for(rt = rf - 2; rt <= rf + 2; rt++) for(ft = ff - 2; ft <= ff + 2; ft++) {
+                if (rt < 0 || rt >= BOARD_HEIGHT || ft < BOARD_LEFT || ft >= BOARD_RGHT) continue;
+                if (!(ff == ft && rf == rt) && SameColor(board[rf][ff], board[rt][ft])) continue;
+                callback(board, flags, (rt-rf)*(rt-rf) + (ff-ft)*(ff-ft) < 3 && board[rt][ft] != EmptySquare ? FirstLeg : NormalMove,
+                         rf, ff, rt, ft, closure);
+              }
+              break;
+
            case WhiteFalcon: // [HGM] wild: for wildcards, self-capture symbolizes move to anywhere
            case BlackFalcon:
            case WhiteCobra:
@@ -728,6 +738,8 @@ GenLegalCallback (Board board, int flags, ChessMove kind, int rf, int ff, int rt
 
     if(rFilter >= 0 && rFilter != rt || fFilter >= 0 && fFilter != ft) return; // [HGM] speed: ignore moves with wrong to-square
 
+    if (board[EP_STATUS] == EP_IRON_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)) return; //[HGM] lion
+
     if (!(flags & F_IGNORE_CHECK) ) {
       int check, promo = (gameInfo.variant == VariantSpartan && kind == BlackPromotion);
       if(promo) {
@@ -972,6 +984,9 @@ CheckTestCallback (Board board, int flags, ChessMove kind, int rf, int ff, int r
        cl->check++;
        xqCheckers[rf][ff] = xqCheckers[EP_STATUS] & 1; // remember who is checking (if status == 1)
     }
+    if( board[EP_STATUS] == EP_ROYAL_LION && (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion)
+       && (gameInfo.variant != VariantLion || board[rf][ff] != WhiteKing && board[rf][ff] != BlackKing) )
+       cl->check++; // [HGM] lion: forbidden counterstrike against Lion equated to putting yourself in check
 }
 
 
@@ -987,7 +1002,7 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
 {
     CheckTestClosure cl;
     ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing;
-    ChessSquare captured = EmptySquare;
+    ChessSquare captured = EmptySquare, ep;
     /*  Suppress warnings on uninitialized variables    */
 
     if(gameInfo.variant == VariantXiangqi)
@@ -1006,6 +1021,14 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
            board[rt][ft] = board[rf][ff];
            board[rf][ff] = EmptySquare;
        }
+       ep = board[EP_STATUS];
+       if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules
+           ChessSquare victim = killX < 0 ? EmptySquare : board[killY][killX];
+           if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) &&           // capturer is Lion
+               (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) &&           // captures from a distance
+               (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge'
+                    board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected
+       }
     }
 
     /* For compatibility with ICS wild 9, we scan the board in the
@@ -1042,6 +1065,7 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant
        } else {
            board[rt][ft] = captured;
        }
+       board[EP_STATUS] = ep;
     }
 
     return cl.fking < BOARD_RGHT ? cl.check : 1000; // [HGM] atomic: return 1000 if we have no king
diff --git a/moves.h b/moves.h
index 5a7e9ae..bef3073 100644 (file)
--- a/moves.h
+++ b/moves.h
@@ -83,10 +83,12 @@ typedef void (*MoveCallback) P((Board board, int flags, ChessMove kind,
 #define F_MANDATORY_CAPTURE 0x200
 
 /* Special epfile values. [HGM] positive values are non-reversible moves! */
-#define EP_NONE (-4)           /* [HGM] Tricky! order matters:            */
-#define EP_UNKNOWN (-1)        /*       >= EP_UNKNOWN spils rep-draw      */
+#define EP_NONE (-6)           /* [HGM] Tricky! order matters:            */
+#define EP_UNKNOWN (-1)        /*       >= EP_UNKNOWN spoils rep-draw     */
 #define EP_CAPTURE (-2)        /*       <= EP_NONE is reversible move     */
 #define EP_PAWN_MOVE (-3)
+#define EP_IRON_LION (-4)
+#define EP_ROYAL_LION (-5)
 #define EP_REP_DRAW   (-15)
 #define EP_RULE_DRAW  (-14)
 #define EP_INSUF_DRAW  (-13)
@@ -172,4 +174,4 @@ ChessMove CoordsToAlgebraic P((Board board, int flags,
                               int rf, int ff, int rt, int ft,
                               int promoChar, char out[MOVE_LEN]));
 
-extern int quickFlag;
+extern int quickFlag, killX, killY;