Implement drop restrictions for Shogi
authorH.G.Muller <hgm@hgm-xboard.(none)>
Wed, 28 Dec 2016 19:39:23 +0000 (20:39 +0100)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Wed, 28 Dec 2016 19:39:23 +0000 (20:39 +0100)
The various routines for dropping pieces now repect the rules that
pieces should not be dropped where they have no moves.

dropper.c

index d755acf..8087bf5 100644 (file)
--- a/dropper.c
+++ b/dropper.c
@@ -58,7 +58,7 @@ int moveNr;              // part of game state; incremented by MakeMove
 #define promoGain (rawGain + 1)
 
 int pvStack[MAXPLY*MAXPLY/2];
-int nrRanks, nrFiles, specials, pinCodes, maxDrop, moveSP, pawn, *pvPtr = pvStack, boardEnd, searchNr;
+int nrRanks, nrFiles, specials, pinCodes, maxDrop, moveSP, pawn, lanceMask, *pvPtr = pvStack, boardEnd, perpLoses, searchNr;
 int rawInts[21*22], pieceValues[96], pieceCode[96];
 signed char   rawChar[32*22], steps[512];
 unsigned char rawByte[91*22], firstDir[64], rawBulk[98], handSlot[97], promoCode[96], aVal[64], vVal[64], rawLocation[96+23];
@@ -316,19 +316,20 @@ euroFEN[]  = "r w",
 
 // promotion codes for unpromoted pieces. Will be ANDed with Z_WHITE or Z_BLACK to fill promoCode[] table
 
-#define Z_FIDE     1    /* piece has promotion choice   */
+#define Z_LAST     1    /* first or last rank           */
 #define Z_2ND   0x66    /* must promote on last 2 ranks */
 #define Z_MUST  0x78    /* must promote in entire zone  */
 #define Z_WHITE 0x2B    /* bits applying to white zone  */
 #define Z_BLACK 0x55    /* bits applying to black zone  */
-#define Z_LAST  0x80    /* ???? */
 #define Z_DOUBLE 0x80   /* where Pawns have double step */
 
-chessProms[16] = { Z_FIDE|Z_MUST },
+chessProms[16] = { Z_MUST },
 shogiProms[16] = { Z_MUST, Z_2ND, COLOR, 0, Z_MUST, Z_MUST, Z_2ND },
 toriProms[16]  = { Z_MUST, 0, 0, 0, 0, Z_MUST },
 waProms[16]    = { Z_MUST, Z_2ND, Z_MUST, Z_MUST, Z_MUST, COLOR, COLOR, Z_MUST, Z_MUST, Z_MUST, COLOR, Z_MUST, COLOR, Z_MUST };
 
+int lances[] = { 1, 3, 0103, 0103, 3, 1, 1 }; // bitmap indicating piece types with drop restriction (LSB = Pawn) in various variants
+
 char *betza[] = { // piece defs for sending to GUI
   NULL, // suppresses setup command
   "",
@@ -444,6 +445,8 @@ printf("# variant %d: %s\n", v, variants[v].name);
     moves   = variants[v].dirs;
     startPos= variants[v].fen;
     codes   = variants[v].proms;
+    lanceMask = lances[v];
+    perpLoses = v; // this works for now, as only zh allows perpetuals
 
     if((p = betza[v])) { // configure GUI for this variant
        printf("setup (%s) %dx%d+%d_%s %s 0 1", ptc[v], nrFiles, nrRanks, maxDrop+1, (v == 4 ? "chu" : "shogi"), startPos);
@@ -773,8 +776,8 @@ MoveGen (int stm, MoveStack *m, int castle)
                    }
                    if(promote & stm) { // promotion is (also?) possible
                        moveStack[--m->firstMove] = (move | from << 8 | to + 11) + (vVal[victim-WHITE] + 20 << 24); // put it amongst captures
-                       if(promoCode[piece-WHITE] & Z_FIDE) { // only for FIDE Pawns
-                           moveStack[--m->firstMove] = move | from << 8 | to - 11 + (stm >> 6)*44; // under-promotion
+                       if(!perpLoses) { // only for FIDE Pawns
+                           moveStack[--m->firstMove] = move | from << 8 | to - 11 + (stm >> 6)*44; // turn previously-generated deferral into under-promotion
                            // other under-promotions could go here (Capahouse?)
                        }
                    }
@@ -804,18 +807,19 @@ MoveGen (int stm, MoveStack *m, int castle)
 void
 CheckDrops (int stm, int king)
 {
-    int i;
-    stm ^= COLOR;
-    for(i=maxDrop; i>=0; i--) {
-       int piece = stm + i, from = handSlot[piece];
+    int i, lowest = 0;
+    if(perpLoses) lowest = !!(pawnCount[sqr2file[king]] & maxBulk[stm]); // in Shogi no Pawn if file already crowded with those
+    else lowest = (stm == WHITE ? king < 2*22 : king >= 6*22);           // in zh no Pawn from back rank
+    for(i=maxDrop; i>=lowest; i--) {
+       int piece = stm + i, from = handSlot[piece^COLOR];
        if((signed char)board[from] < -1) { // piece type is in hand
            int step, dir = 2*firstDir[piece-WHITE];
            while((step = steps[dir++])) {
                int to = king, range = steps[dir++];
-               if((range & 3) == 2) break; // non-capture direction
+               if((range & 3) == 2) continue; // non-capture direction
                while(board[to-=step] == 0) {
                    moveStack[moveSP++] = to | from << 8;
-                   if((range -= 8) <= 0) break;
+                   if((range -= 8) <= 0) { if(i == 0 && (to < 22 || to >= boardEnd-22)) moveSP--; break; }
                }
            }
        }
@@ -825,12 +829,20 @@ CheckDrops (int stm, int king)
 void
 EvasionDrops (int stm, StackFrame *f)
 {
-    int i, x = f->checker, v = f->checkDir;
-    stm ^= COLOR;
+    int i, x = f->checker, v = f->checkDir, s = stm ^ COLOR;
     while(board[x+=v] == 0) { // all squares on check ray
-       i = (x < 22 || x >= 7*22); // not on last rang. *** WRONG FOR SHOGI ***
-       for(i=0; i<=maxDrop; i++) { // all droppable types
-           int piece = stm + i, from = handSlot[piece];
+       int last = maxDrop;
+       i = zoneTab[x] & Z_LAST; // Pawn not on first & last rank (OK for zh)
+       if(perpLoses) { // but it is Shogi!
+           if(!(zoneTab[x] & stm)) i = 0; // outside zone, so dropping is always allowed
+           else if(perpLoses < 5) {       // Shogi variant with Lance
+               i *= 2;                    // no Pawn, then also no Lance!
+               last += 1 - (zoneTab[x] & (Z_2ND & ~COLOR)) + (perpLoses & 4) >> 3; // on last 2 ranks trim off Knight (not in Wa)
+           }
+           if(pawnCount[sqr2file[x]] & maxBulk[stm]) i += !i; // no Pawn in pawn-crowded file
+       }
+       for(; i<=last; i++) { // all droppable types
+           int piece = s + i, from = handSlot[piece];
            if((signed char)board[from] < -1) // piece type is in hand
                moveStack[moveSP++] = from << 8 | x;
        }
@@ -840,16 +852,23 @@ EvasionDrops (int stm, StackFrame *f)
 void
 AllDrops (int stm)
 {
-    int i, start = 22, end = boardEnd-22; 
-    stm ^= COLOR;
+    int i, mask = lanceMask;
     for(i=0; i<=maxDrop; i++) {
-       int piece = stm + i, from = handSlot[piece];
+       int piece = stm + i, from = handSlot[piece^COLOR];
        if((signed char)board[from] < -1) { // piece type is in hand
-           int r, f;
-           for(r=start; r<end; r+=22) for(f=0; f<nrFiles; f++)
-               if(board[r+f] == 0) moveStack[moveSP++] = from << 8 | f + r;
+           int r, f, start = 0, end = boardEnd;
+           if(mask & 1) { // piece with drop limitation
+               int badZone = (i == 6 ? 44 : 22); // 6 is the Knight in (Judkins) Shogi
+               if(stm == BLACK || !perpLoses) start = badZone;
+               if(stm == WHITE || !perpLoses) end  -= badZone;
+           }
+           for(f=0; f<nrFiles; f++) {
+               if(i == 0 && pawnCount[f] & maxBulk[stm]) continue;
+               for(r=start; r<end; r+=22)
+                   if(board[r+f] == 0) moveStack[moveSP++] = from << 8 | f + r;
+           }
        }
-       start = 0; end = boardEnd;
+       mask >>= 1;
     }
 }
 
@@ -871,7 +890,7 @@ DiscoTest (int stm, int fromSqr, int king, StackFrame *f)
 void
 CheckTest (int stm, StackFrame *ff, StackFrame *f)
 {
-    if(ff->mutation == -2) f.checker = CK_NONE, f.checkDist = 0; else { // null move never checks
+    if(ff->mutation == -2) f->checker = CK_NONE, f->checkDist = 0; else { // null move never checks
        int king = location[stm+31]; // own King
        int vec = king - ff->toSqr;
        int match = captCode[vec] & pieceCode[ff->toPiece];
@@ -1357,7 +1376,7 @@ MoveToText (int move)
   to = toDecode[to];
   if(inc > 0) promo = pieceID[inc - 16]; // move is promotion
   else if(castle) to = 2*to - from;     // move is castling, and 'to' indicates Rook; calculate King to-square
-  if(promo == '+' && promoCode[WHITE] & Z_FIDE) promo = 'q';
+  if(promo == '+' && !perpLoses) promo = 'q';
   if(from%22 > 10) sprintf(buf, "%c@%c%d", pieces[dropType[from]-1&~COLOR], 'a'+(to%22), 1+(to/22));   // move is drop
   else sprintf(buf, "%c%d%c%d%c", 'a'+(from%22), 1+(from/22), 'a'+(to%22), 1+(to/22), promo);
   return buf;
@@ -1385,6 +1404,7 @@ ParseMove(int stm, char *s)
         case 'n': m += in; break;
         case 'b': m += 2*in; break;
         case 'r': m += 3*in; break;
+       case '=': m -= 11;   break; // no promotion after all
       }
     } else if(board[from] == stm + 31 && (f - f2 > 1 || f2 - f > 1)) { // castling
       m = 22*8+10 + (f > f2 ? 0 : 11) + 22*(r2 == 7) + (from << 8);