Implement Betza t modifier for hop-own
[xboard.git] / moves.c
diff --git a/moves.c b/moves.c
index 787ab57..bc5f415 100644 (file)
--- a/moves.c
+++ b/moves.c
@@ -235,9 +235,13 @@ char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N";
 char xStep[]    = "2110.130.102.10.00....0..2";
 char yStep[]    = "2132.133.313.20.11....1..3";
 char dirType[]  = "01000104000200000260050000";
+char upgrade[]  = "AKCD.QGH.JQL.NO.KK....Q..Z";
+
 //  alphabet   "a b    c d e f    g h    i j k l    m n o p q r    s    t u v    w x y z "
 int dirs1[] = { 0,0x3C,0,0,0,0xC3,0,0,   0,0,0,0xF0,0,0,0,0,0,0x0F,0   ,0,0,0   ,0,0,0,0 };
 int dirs2[] = { 0,0x18,0,0,0,0x81,0,0xFF,0,0,0,0x60,0,0,0,0,0,0x06,0x66,0,0,0x99,0,0,0,0 };
+int dirs3[] = { 0,0x38,0,0,0,0x83,0,0xFF,0,0,0,0xE0,0,0,0,0,0,0x0E,0xEE,0,0,0xBB,0,0,0,0 };
+int dirs4[] = { 0,0x10,0,0,0,0x01,0,0xFF,0,0,0,0x40,0,0,0,0,0,0x04,0x44,0,0,0x11,0,0,0,0 };
 
 int rot[][4] = { // rotation matrices for each direction
   { 1, 0, 0, 1 },
@@ -259,15 +263,17 @@ OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOID
 void
 MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl)
 {
-    char *p = desc;
+    char buf[80], *p = desc;
     int mine, his, dir, bit, occup, i;
     if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2;
     while(*p) {                  // more moves to go
-       int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0, jump=1, skip = 0;
+       int expo = 1, dx, dy, x, y, mode, dirSet, ds2, retry=0, initial=0, jump=1, skip = 0;
        char *cont = NULL;
        if(*p == 'i') initial = 1, desc = ++p;
        while(islower(*p)) p++;  // skip prefixes
        if(!isupper(*p)) return; // syntax error: no atom
+       dx = xStep[*p-'A'] - '0';// step vector of atom
+       dy = yStep[*p-'A'] - '0';
        dirSet = 0;              // build direction set based on atom symmetry
        switch(symmetry[*p-'A']) {
          case 'B': expo = 0;    // bishop, slide
@@ -282,7 +288,7 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                        } else desc++;
                        dirSet |= b;
                      }
-                     dirSet &= 0x55; if(!dirSet) dirSet = 0x55;
+                     dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA;
                      break;
                    }
          case 'R': expo = 0;    // rook, slide
@@ -292,7 +298,8 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                    dirSet = (dirSet << angle | dirSet >> 8-angle) & 255;   // re-orient direction system
                    break;
          case 'N':              // oblique atom (degenerate 8-fold)
-                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                   if(tx < 0) { // for continuation legs relative directions are non-degenerate!
+                     while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
                        int b = dirs2[*desc-'a']; // when alone, use narrow version
                        if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version
                        else if(*desc == desc[1] || islower(desc[1]) && i < '4'
@@ -301,36 +308,45 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                            desc += 2;
                        } else desc++;
                        dirSet |= b;
+                     }
+                     if(!dirSet) dirSet = 0xFF;
+                     break;
                    }
-                   if(!dirSet) dirSet = 0xFF;
-                   break;
          case 'Q': expo = 0;    // queen, slide
          case 'K':              // non-deg (pseudo) 8-fold
-                   dirSet=0x55; // start with orthogonal moves
-                   retry = 1;   // and schedule the diagonal moves for later
+                   while(islower(*desc) && (i = dirType[*desc-'a']) != '0') {
+                       int b = dirs4[*desc-'a'];    // when alone, use narrow version
+                       if(desc[1] == *desc) desc++; // doubling forces alone
+                       else if(islower(desc[1]) && i < '4'
+                               && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same)
+                           b = dirs3[*desc-'a'] & dirs3[desc[1]-'a'];      // intersect wide & perp wide
+                           desc += 2;
+                       } else desc++;
+                       dirSet |= b;
+                   }
+                   if(!dirSet) dirSet = 0xFF;
+                   dirSet = (dirSet << angle | dirSet >> 8-angle) & 255;   // re-orient direction system
+                   ds2 = dirSet & 0xAA;          // extract diagonal directions
+                   if(dirSet &= 0x55)            // start with orthogonal moves, if present
+                        retry = 1;               // and schedule the diagonal moves for later
+                   else dx = 1, dirSet = ds2;    // if no orthogonal directions, do diagonal immediately
                    break;       // should not have direction indicators
          default:  return;      // syntax error: invalid atom
        }
        if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255;   // invert black moves
        mode = 0;                // build mode mask
-       if(*desc == 'm') mode |= 4, desc++;
-       if(*desc == 'c') mode |= his, desc++;
-       if(*desc == 'd') mode |= mine, desc++;
-       if(*desc == 'e') mode |= 8, desc++;
-       if(*desc == 'p') mode |= 32, desc++;
-       if(*desc == 'g') mode |= 64, desc++;
-       if(*desc == 'o') mode |= 128, desc++;
-       if(*desc == 'n') jump = 0, desc++;
-       while(*desc == 'j') jump++, desc++;
-       if(*desc == 'a') cont = ++desc;
-        if(!cont) {
-           if(!(mode & 15)) mode = his + 4;          // no mode spec, use default = mc
-       } else {
-           if(mode & 32) mode ^= 256 + 32;           // in non-final legs 'p' means 'pass through'
-           if(!(mode & 0x10F)) mode = his + 0x104;   // and default = mcp
-       }
-       dx = xStep[*p-'A'] - '0';                     // step vector of atom
-       dy = yStep[*p-'A'] - '0';
+       if(*desc == 'm') mode |= 4, desc++;           // move to empty
+       if(*desc == 'c') mode |= his, desc++;         // capture foe
+       if(*desc == 'd') mode |= mine, desc++;        // destroy (capture friend)
+       if(*desc == 'e') mode |= 8, desc++;           // e.p. capture last mover
+       if(*desc == 't') mode |= 16, desc++;          // exclude enemies as hop platform ('test')
+       if(*desc == 'p') mode |= 32, desc++;          // hop over occupied
+       if(*desc == 'g') mode |= 64, desc++;          // hop and toggle range
+       if(*desc == 'o') mode |= 128, desc++;         // wrap around cylinder board
+       if(*desc == 'y') mode |= 512, desc++;         // toggle range on empty square
+       if(*desc == 'n') jump = 0, desc++;            // non-jumping
+       while(*desc == 'j') jump++, desc++;           // must jump (on B,R,Q: skip first square)
+       if(*desc == 'a') cont = ++desc;               // move again after doing what preceded it
        if(isdigit(*++p)) expo = atoi(p++);           // read exponent
        if(expo > 9) p++;                             // allow double-digit
        desc = p;                                     // this is start of next move
@@ -338,7 +354,23 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                       r == 0              && board[TOUCHED_W] & 1<<f ||
                       r == BOARD_HEIGHT-1 && board[TOUCHED_B] & 1<<f   ) ) continue;
        if(expo > 1 && dx == 0 && dy == 0) {          // castling indicated by O + number
-           mode |= 16; dy = 1;
+           mode |= 1024; dy = 1;
+       }
+        if(!cont) {
+           if(!(mode & 15)) mode = his + 4;          // no mode spec, use default = mc
+       } else {
+           if(mode & 32) mode ^= 256 + 32;           // in non-final legs 'p' means 'pass through'
+           if(mode & 64 + 512) {
+               mode |= 256;                          // and 'g' too, but converts leaper <-> slider
+               if(mode & 512) mode ^= 0x304;         // and 'y' is m-like 'g'
+               strncpy(buf, cont, 80); cont = buf;   // copy next leg(s), so we can modify
+               while(islower(*cont)) cont++;         // skip to atom
+               *cont = upgrade[*cont-'A'];           // replace atom, BRQ <-> FWK
+               if(expo == 1) *++cont = '0';          // turn other leapers into riders 
+               *++cont = '\0';                       // make sure any old range is stripped off
+               cont = buf;                           // use modified string for continuation leg
+           }
+           if(!(mode & 0x30F)) mode = his + 0x104;   // and default = mcp
        }
        if(dy == 1) skip = jump - 1, jump = 1;        // on W & F atoms 'j' = skip first square
         do {
@@ -363,6 +395,7 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                if(board[y][x] < EmptySquare) occup = 0x102; else
                                              occup = 4;
                if(cont) {                            // non-final leg
+                 if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode
                  if(occup & mode) {                  // valid intermediate square, do continuation
                    if(occup & mode & 0x104)          // no side effects, merge legs to one move
                        MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl);
@@ -384,7 +417,7 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square
                    cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl);
                }
-               if(mode & 16) {              // castling
+               if(mode & 1024) {            // castling
                    i = 2;                   // kludge to elongate move indefinitely
                    if(occup == 4) continue; // skip empty squares
                    if(x == BOARD_LEFT   && board[y][x] == initialPosition[y][x]) // reached initial corner piece
@@ -397,8 +430,8 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle
                if(occup != 4) break; // not valid transit square
            } while(--i);
          }
-         dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q
-       } while(retry--);             // and start doing them
+         dx = dy = 1; dirSet = ds2;  // prepare for diagonal moves of K,Q
+       } while(retry-- && ds2);      // and start doing them
        if(tx >= 0) break;            // don't do other atoms in continuation legs
     }
 } // next atom