Implement castling
authorH.G.Muller <hgm@hgm-xboard.(none)>
Wed, 28 Dec 2016 16:40:27 +0000 (17:40 +0100)
committerH.G.Muller <hgm@hgm-xboard.(none)>
Wed, 28 Dec 2016 16:50:53 +0000 (17:50 +0100)
MoveGen() now also generates castlings. Legality is tested by the kludge
of replacing the Rook by a second King during the (early) move generation
in the next ply. MakeMove() is refactored to make sure both King and Rook
victim are saved. In addition promotion is now implemented by OR'ing the
promoInc into the piece code,tomake the engine resistent to promoting an
already-promoted piece (e.g. due to hash-move mixup).

dropper.c

index 29f632c..0510ee8 100644 (file)
--- a/dropper.c
+++ b/dropper.c
@@ -474,7 +474,7 @@ printf("# variant %d: %s\n", v, variants[v].name);
        }
     }
     sqr2file[CK_DOUBLE] = sqr2file[CK_NONE] = 13; // for decoding hashed checker
-#define CASTLE(R,F,RT,KT,RF) toDecode[R*22+F] = RT; dropType[R*22+F] = KT; zoneTab[R*22+F] = RF; promoInc[R*22+F] = 3-31; sqr2file[R*22+F] = 11;
+#define CASTLE(R,F,RT,KT,RF) toDecode[R*22+F] = RT; dropType[R*22+F] = KT; zoneTab[R*22+F] = RF; promoInc[R*22+F] = 0; sqr2file[R*22+F] = 11;
     if(v == 0) for(f=0; f<11; f++) { // In Chess we re-assign other ranks to under-promotions in zone
        for(r=1; r<7; r++) toDecode[r*22+11+f] = f + (r < 4 ? 0 : 7*22); // redirect to last rank
        promoInc[1*22+11+f] = promoInc[6*22+11+f] += 1;
@@ -496,7 +496,7 @@ printf("# variant %d: %s\n", v, variants[v].name);
        CASTLE(8, 21, 3, 2, 0) // Q-side
        CASTLE(9, 21, 7*22+3, 7*22+2, 7*22+0)
        spoiler[0*22+0] = 4;  // initial King and Rook squares marked as castling-right spoilers
-       spoiler[0*22+0] = 5;
+       spoiler[0*22+4] = 5;
        spoiler[0*22+7] = 1;
        spoiler[7*22+0] = 8;
        spoiler[7*22+4] = 10;
@@ -678,6 +678,7 @@ typedef struct {   // move stack sectioning
     int drops;     // index of first quiet drop
     int stage;     // stage of move generation (0=hash/capt/prom, 1=killer/noncapt, 2=check-drops, 3=quiet drops)
     int late;      // start of late moves
+    int castlings; // end of list of board moves without castlings
     int epSqr;
     int checker;
 } MoveStack;
@@ -730,7 +731,7 @@ Dump (char *s)
 {int i; printf("%s\n",s); for(i=0; i<ply; i++) printf(" {%x} %s", deprec[i], MoveToText(path[i])); PrintDBoard("board", board, "   ", 11); exit(1); }
 
 int
-MoveGen (int stm, MoveStack *m)
+MoveGen (int stm, MoveStack *m, int castle)
 {   // generate all board moves, return 1 if King capture found amongst those
     int r, f;
     m->firstMove = m->nonCapts =  m->late = moveSP; m->stage = 0;
@@ -787,6 +788,16 @@ MoveGen (int stm, MoveStack *m)
     }
     m->unsorted = m->firstMove; m->drops = moveSP;
 
+    r = location[stm+31]; m->castlings = moveSP; f = 0;
+    if(!(stm >> 5 & castle)) { // K-side castling
+       f++;
+       if(board[r+1] == 0 && board[r+2] == 0) f += 2, moveStack[moveSP++] = r << 8 | 8*22+10 + 22*(stm == BLACK);
+    }
+    if(!(stm >> 3 & castle)) { // Q-side castling
+       f++;
+       if(board[r-1] == 0 && board[r-2] == 0 && board[r-3] == 0) f += 2, moveStack[moveSP++] = r << 8 | 8*22+21 + 22*(stm == BLACK);
+    }
+
     return 0;
 }
 
@@ -913,19 +924,20 @@ MakeMove (StackFrame *f, int move)
     f->fromPiece = board[f->fromSqr];                                   // occupant or (for drops) complemented holdings count
     if(f->checker != CK_NONE && NonEvade(f)) return 0;                  // abort if move did not evade existing check
     f->mutation = (f->fromPiece >> 7) | f->fromPiece;                   // occupant or (for drops) -1
-    f->toPiece = f->mutation + promoInc[to] + dropType[f->fromSqr];     // (possibly promoted) occupant or (for drops) piece to drop
-    f->savePiece = board[f->toSqr];                                    // replacement victim
+    f->toPiece = f->mutation + dropType[f->fromSqr] | promoInc[to];     // (possibly promoted) occupant or (for drops) piece to drop
+    f->victim = board[f->captSqr];                                     // for now this is the replacement victim
     f->newEval = f->pstEval; f->newKey  = f->hashKey;                  // start building new key and eval
     f->epSqr = 255;
     f->rookSqr = sqr2file[f->toSqr] + (pawnCount - board);              // normally (i.e. when not castling) use for pawnCount
     f->rook = board[f->rookSqr];                                       // save and update Pawn occupancy
-    board[f->rookSqr] = f->rook + pawnBulk[f->toPiece] - pawnBulk[f->mutation] - pawnBulk[f->savePiece]; // assumes all on same file!
+    board[f->rookSqr] = f->rook + pawnBulk[f->toPiece] - pawnBulk[f->mutation] - pawnBulk[f->victim]; // assumes all on same file!
 //printf("f=%02x t=%02x fp=%02x tp=%02x sp=%02x mut=%02x ep=%02x\n", f->fromSqr, f->toSqr, f->fromPiece, f->toPiece, f->savePiece, f->mutation, f->epSqr);
     if(to >= specials) { // treat special moves for Chess
        if(sqr2file[to] > 11) {                                         // e.p. capture, shift capture square
 //printf("# e.p. %02x\n", to);
            f->captSqr = toDecode[to-11];                               // use to-codes from double pushes, which happen to be what we need
            f->victim  = board[f->captSqr];
+           f->savePiece = board[f->toSqr];
            board[f->captSqr] = 0;                                      // e.p. is only case with toSqr != captSqr where we have to clear captSqr
        } else if(sqr2file[to] < 8) {                                   // double push
            int xpawn = f->toPiece ^ COLOR;                             // enemy Pawn
@@ -933,21 +945,22 @@ MakeMove (StackFrame *f, int move)
               board[f->toSqr - 1] == xpawn ) {
                f->epSqr = (f->fromSqr + f->toSqr) >> 1;                // set e.p. rights
            }
-           f->victim = 0;                                              // for key and pst update
        } else { // castling. at this point we are set up to 'promote' a King to Rook (so the check tests sees the Rook, and UnMake restores location[K])
            f->rookSqr = zoneTab[to];                                   // Rook from-square
            f->rook = board[f->rookSqr];                                // arrange Rook to be put back on UnMake (pawnCount is never modified in chess)
            board[f->rookSqr] = 0;                                      // and remove it
-           f->newEval -= PST[f->toPiece][f->rookSqr];
-           f->newKey  -= KEY(f->toPiece, f->rookSqr);
+           f->newEval -= PST[f->rook][f->rookSqr];
+           f->newKey  -= KEY(f->rook, f->rookSqr);
            f->captSqr = dropType[to];                                  // this tabulates to-square of the King
-           f->victim  = board[f->captSqr];                             // should be 0, but who knows?
+           f->savePiece = f->victim;                                   // now toSqr and captSqr are different, make sure the piece that was on toSqr goes back there in UnMake
+           f->victim = board[f->captSqr];                              // should be 0, but who knows?
+           f->toPiece = f->rook;                                       // make sure Rook (or whatever was in corner) will be placed on toSqr
            board[f->captSqr] = f->mutation;                            // place the King
-           f->newEval += PST[f->mutation][f->captSqr];
+           f->newEval += PST[f->mutation][f->captSqr] + 50;            // add 50 cP castling bonus
            f->newKey  += KEY(f->mutation, f->captSqr);
            location[f->mutation] = f->captSqr;                         // be sure King location stays known
        }
-    } else f->victim = f->savePiece;                                   // for normal moves replacement victim counts
+    }
     board[f->fromSqr] = f->fromPiece - f->mutation;                     // 0 or (for drops) decremented count
     board[f->toSqr]   = f->toPiece;
     board[handSlot[f->victim]]--; // put victim in holdings
@@ -965,8 +978,8 @@ void
 UnMake (StackFrame *f)
 {
     board[f->rookSqr] = f->rook;      // restore either pawnCount or (after castling) Rook from-square
-    board[f->captSqr] = f->victim;    // differs from toSqr on e.p. (Pawn to-square) and castling, (King to-square) and should be cleared then
     board[f->toSqr]   = f->savePiece; // put back the regularly captured piece (for castling that captured by Rook)
+    board[f->captSqr] = f->victim;    // differs from toSqr on e.p. (Pawn to-square) and castling, (King to-square) and should be cleared then
     board[f->fromSqr] = f->fromPiece; //          and the mover
     board[handSlot[f->victim]]++;
     location[f->fromPiece] = f->fromSqr;
@@ -1004,7 +1017,7 @@ if(ply > 90) Dump("maxply");
 if(PATH)printf("%d:%d   Hash Probe %016llx\n",ply,depth,f.hashKey);
     // hash probe
     hashKeyH = f.hashKey >> 32;
-    entry = hashTable + (f.hashKey + (stm + 9849)*(m.epSqr + 51451) & hashMask);
+    entry = hashTable + (f.hashKey + (stm + 9849 + f.rights)*(m.epSqr + 51451) & hashMask);
     if(entry->lock == hashKeyH || (++entry)->lock == hashKeyH || (++entry)->lock == hashKeyH || (++entry)->lock == hashKeyH) { // 4 possible entries
        int score = entry->score, d = entry->depth;
        f.checker = entry->checker; f.checkDist = 0;
@@ -1029,8 +1042,12 @@ if(hashMove && board[hashMove>>8&255] == 0) {char s[100];sprintf(s,"bad hash mov
     } else hit = hashMove = 0, f.checker = CK_UNKNOWN;
 
     moveSP += 48;  // create space for non-captures
-    if(earlyGen) { // last moved piece was King
-       if(MoveGen(stm, &m)) { moveSP = oldSP; return INF; } // make sure we detect if he moved into check
+    if(earlyGen) { // last moved piece was King, e.p. capture or castling
+       int kingCapt;
+       if(!ff->victim) board[ff->toSqr] = stm + 31 ^ COLOR; // kludge: after castling we temporarily make Rook a second King to catch passing through check
+       kingCapt = MoveGen(stm, &m, f.rights);
+       board[ff->toSqr] = ff->toPiece; // undo kludge damage
+       if(kingCapt) { moveSP = oldSP; ff->depth = MAXPLY; return INF; } // make sure we detect if he moved into check
     }
 
     if((++nodeCount & 0xFFF) == 0) abortFlag |= TimeIsUp(3); // check time limit every 4K nodes
@@ -1069,8 +1086,9 @@ if(PATH)printf("%d:%d   {%d,%d} max=%d eval=%d check=%02x,%d,%d\n",ply,depth,alp
     }
 
     // move generation
-    if(!earlyGen) MoveGen(stm, &m); // generate moves if we had not done so yet
+    if(!earlyGen) MoveGen(stm, &m, f.rights); // generate moves if we had not done so yet
     if(hashMove) moveStack[--m.firstMove] = hashMove; // put hash move in front of list (duplicat!)
+    if(f.checker != CK_NONE) moveSP = m.drops = m.castlings; // clip off castlings when in check
 
     do { // IID loop
        int curMove, highDepth;
@@ -1268,7 +1286,7 @@ StackFrame undoInfo;
 int
 Setup (char *fen)
 { // very flaky FEN parser
-  static char castle[] = "QqKk-", startFEN[200];
+  static char castle[] = "KkQq-", startFEN[200];
   int pstEval, rights, stm = WHITE, i, p, sqr = 22*(nrRanks-1); // upper-left corner
   ClearBoard();
   if(!fen) fen = startFEN; else strcpy(startFEN, fen); // remember start position, or use remembered one if not given
@@ -1333,10 +1351,10 @@ MoveToText (int move)
 {
   static char buf[20], pieceID[] = "+nbrq";
   int promo = '\0', from = move >> 8 & 0xFF, to = move & 0xFF;
-  int inc = promoInc[to];
+  int inc = promoInc[to], castle = (nrFiles < 11 && to%11 == 10);
   to = toDecode[to];
   if(inc > 0) promo = pieceID[inc - 16]; // move is promotion
-  else if(inc < 0) to = 2*to - from;     // move is castling, and 'to' indicates Rook; calculate King to-square
+  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(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);