Adapt Chu-Shogi pieceToCharTable to new piece order
[xboard.git] / backend.c
index 8831176..99f752e 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -192,7 +192,6 @@ void GameEnds P((ChessMove result, char *resultDetails, int whosays));
 void EditPositionDone P((Boolean fakeRights));
 void PrintOpponents P((FILE *fp));
 void PrintPosition P((FILE *fp, int move));
-void StartChessProgram P((ChessProgramState *cps));
 void SendToProgram P((char *message, ChessProgramState *cps));
 void SendMoveToProgram P((int moveNum, ChessProgramState *cps));
 void ReceiveFromProgram P((InputSourceRef isr, VOIDSTAR closure,
@@ -516,7 +515,7 @@ AppData appData;
 Board boards[MAX_MOVES];
 /* [HGM] Following 7 needed for accurate legality tests: */
 signed char  castlingRank[BOARD_FILES]; // and corresponding ranks
-signed char  initialRights[BOARD_FILES];
+unsigned char initialRights[BOARD_FILES];
 int   nrCastlingRights; // For TwoKings, or to implement castling-unknown status
 int   initialRulePlies, FENrulePlies;
 FILE  *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
@@ -4890,13 +4889,13 @@ ParseBoard12 (char *string)
                   if(fromY == DROP_RANK && k==toY && j==toX) continue; // dropped pieces always stand for themselves
                   old = (k==toY && j==toX) ? boards[moveNum-1][fromY][fromX] : boards[moveNum-1][k][j]; // trace back mover
                   if(old == new) continue;
-                  if(old == PROMOTED new) boards[moveNum][k][j] = old; // prevent promoted pieces to revert to primordial ones
+                  if(old == PROMOTED(new)) boards[moveNum][k][j] = old;// prevent promoted pieces to revert to primordial ones
                   else if(new == WhiteWazir || new == BlackWazir) {
                       if(old < WhiteCannon || old >= BlackPawn && old < BlackCannon)
-                           boards[moveNum][k][j] = PROMOTED old; // choose correct type of Gold in promotion
+                           boards[moveNum][k][j] = PROMOTED(old); // choose correct type of Gold in promotion
                       else boards[moveNum][k][j] = old; // preserve type of Gold
                   } else if((old == WhitePawn || old == BlackPawn) && new != EmptySquare) // Pawn promotions (but not e.p.capture!)
-                      boards[moveNum][k][j] = PROMOTED new; // use non-primordial representation of chosen piece
+                      boards[moveNum][k][j] = PROMOTED(new); // use non-primordial representation of chosen piece
               }
          } else {
            /* Move from ICS was illegal!?  Punt. */
@@ -5381,12 +5380,22 @@ int dragging;
 static ClickType lastClickType;
 
 int
+PieceInString (char *s, ChessSquare piece)
+{
+  char *p, ID = ToUpper(PieceToChar(piece)), suffix = PieceSuffix(piece);
+  while((p = strchr(s, ID))) {
+    if(!suffix || p[1] == suffix) return TRUE;
+    s = p;
+  }
+  return FALSE;
+}
+
+int
 Partner (ChessSquare *p)
 { // change piece into promotion partner if one shogi-promotes to the other
-  int stride = gameInfo.variant == VariantChu ? 22 : 11;
-  ChessSquare partner;
-  partner = (*p/stride & 1 ? *p - stride : *p + stride);
+  ChessSquare partner = promoPartner[*p];
   if(PieceToChar(*p) != '+' && PieceToChar(partner) != '+') return 0;
+  if(PieceToChar(*p) == '+') partner = boards[currentMove][fromY][fromX];
   *p = partner;
   return 1;
 }
@@ -5411,7 +5420,7 @@ Sweep (int step)
        if(!step) step = -1;
     } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' ||
            !toggleFlag && PieceToChar(promoSweep) == '+' || // skip promoted versions of other
-           promoRestrict[0] ? !strchr(promoRestrict, ToUpper(PieceToChar(promoSweep))) : // if choice set available, use it 
+           promoRestrict[0] ? !PieceInString(promoRestrict, promoSweep) : // if choice set available, use it 
            promoSweep == pawn ||
            appData.testLegality && (promoSweep == king || gameInfo.variant != VariantChuChess &&
             (promoSweep == WhiteLion || promoSweep == BlackLion)));
@@ -5994,37 +6003,69 @@ ptclen (const char *s, char *escapes)
 {
     int n = 0;
     if(!*escapes) return strlen(s);
-    while(*s) n += (*s != '/' && !strchr(escapes, *s)), s++;
+    while(*s) n += (*s != '-' && *s != '^' && *s != '*' && !strchr(escapes, *s)), s++;
     return n;
 }
 
+static int pieceOrder[] = {
+  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, // P N B R Q F E A C W M
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, // O H I J G D V L S U Lion
+ 45, 23, 24, 25, 26, 27, 28, 29, 46, 31, 32, // Sword Zebra Camel Tower Wolf Dragon Duck Axe Leopard Gnu Cub
+ 44, 51, 56, 57, 58, 59, 60, 61, 62, 63, 34, // Whale Pegasus Wizard Copper Iron Viking Flag Amazon Wheel Shield Claw
+ 33, 55, 53, 42, 37, 48, 39, 40, 41, 22, 30, // +P +N =B =R +L +S +E +Ph +Kn Butterfly Hat
+ 38, 43, 35, 36, 49, 47, 52, 50, 54, 64, 65 // +V +M =H =D Princess HSword +GB HCrown Wheer Shierd King
+};
+
 int
 SetCharTableEsc (unsigned char *table, const char * map, char * escapes)
 /* [HGM] moved here from winboard.c because of its general usefulness */
 /*       Basically a safe strcpy that uses the last character as King */
 {
-    int result = FALSE; int NrPieces, offs;
+    int result = FALSE; int NrPieces;
+    unsigned char partner[EmptySquare];
 
     if( map != NULL && (NrPieces=ptclen(map, escapes)) <= (int) EmptySquare
                     && NrPieces >= 12 && !(NrPieces&1)) {
-        int i, j = 0; /* [HGM] Accept even length from 12 to 88 */
+        int i, ii, j = 0; /* [HGM] Accept even length from 12 to 88 */
 
         for( i=0; i<(int) EmptySquare; i++ ) table[i] = '.';
-        for( i=offs=0; i<NrPieces/2-1; i++ ) {
-            char *p;
-            if(map[j] == '/' && *escapes) offs = WhiteTokin - i, j++;
-            table[i + offs] = map[j++];
-            if(p = strchr(escapes, map[j])) j++, table[i + offs] += 64*(p - escapes + 1);
+        for( ii=0; ii<NrPieces/2-1; ii++ ) {
+            char *p, c=0;
+            i = pieceOrder[ii];
+            if(*escapes && (map[j] == '*' || map[j] == '-' || map[j] == '^')) c = map[j++];
+            table[i] = map[j++];
+            if(p = strchr(escapes, map[j])) j++, table[i] += 64*(p - escapes + 1);
+            if(c) partner[i] = table[i], table[i] = c;
         }
         table[(int) WhiteKing]  = map[j++];
-        for( i=offs=0; i<NrPieces/2-1; i++ ) {
-            char *p;
-            if(map[j] == '/' && *escapes) offs = WhiteTokin - i, j++;
-            table[WHITE_TO_BLACK i + offs] = map[j++];
-            if(p = strchr(escapes, map[j])) j++, table[WHITE_TO_BLACK i + offs] += 64*(p - escapes + 1);
+        for( ii=0; ii<NrPieces/2-1; ii++ ) {
+            char *p, c=0;
+            i = WHITE_TO_BLACK pieceOrder[ii];
+            if(*escapes && (map[j] == '*' || map[j] == '-' || map[j] == '^')) c = map[j++];
+            table[i] = map[j++];
+            if(p = strchr(escapes, map[j])) j++, table[i] += 64*(p - escapes + 1);
+            if(c) partner[i] = table[i], table[i] = c;
         }
         table[(int) BlackKing]  = map[j++];
 
+
+        if(*escapes) { // set up promotion pairing
+            for( i=0; i<(int) EmptySquare; i++ ) promoPartner[i] = (i%BlackPawn < 11 ? i + 11 : i%BlackPawn < 22 ? i - 11 : i); // default
+            // pieceToChar entirely filled, so we can look up specified partners
+            for(i=0; i<EmptySquare; i++) { // adjust promotion pairing
+                int c = table[i];
+                if(c == '^' || c == '-') { // has specified partner
+                    int p;
+                    for(p=0; p<EmptySquare; p++) if(table[p] == partner[i]) break;
+                    if(c == '^') table[i] = '+';
+                    if(p < EmptySquare) promoPartner[p] = i, promoPartner[i] = p; // marry them
+                } else if(c == '*') {
+                    table[i] = partner[i];
+                    promoPartner[i] = (i < BlackPawn ? WhiteTokin : BlackTokin); // promotes to Tokin
+                }
+            }
+        }
+
         result = TRUE;
     }
 
@@ -6117,7 +6158,7 @@ InitPosition (int redraw)
       initialPosition[CASTLING][i] = initialRights[i] = NoRights; /* but no rights yet */
     initialPosition[EP_STATUS] = EP_NONE;
     initialPosition[TOUCHED_W] = initialPosition[TOUCHED_B] = 0;
-    SetCharTable(pieceToChar, "PNBRQ...........Kpnbrq...........k");
+    SetCharTableEsc(pieceToChar, "PNBRQ...........Kpnbrq...........k", SUFFIXES);
     if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
          SetCharTable(pieceNickName, appData.pieceNickNames);
     else SetCharTable(pieceNickName, "............");
@@ -6210,8 +6251,8 @@ InitPosition (int redraw)
       gameInfo.boardWidth  = 12;
       gameInfo.boardHeight = 12;
       nrCastlingRights = 0;
-      SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN/+.++.++++++++++.+++++K"
-                                   "p.brqsexogcathd.vmlifn/+.++.++++++++++.+++++k", SUFFIXES);
+      SetCharTableEsc(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN^T.......^H..^A^L.........^P.^F^G..^E^X^O..^S^C^B^R^V^D^I^MK"
+                                   "p.brqsexogcathd.vmlifn^t.......^h..^a^l.........^p.^f^g..^e^x^o..^s^c^b^r^v^d^i^mk", SUFFIXES);
       break;
     case VariantCourier:
       pieces = CourierArray;
@@ -6440,7 +6481,7 @@ SendBoard (ChessProgramState *cps, int moveNum)
            else snprintf(message, MSG_SIZ, "%c%c%d\n", PieceToChar(*bp), AAA + j, ONE + i - '0');
             if(message[0] == '+' || message[0] == '~') {
              snprintf(message, MSG_SIZ,"%c%c%d+\n",
-                        PieceToChar((ChessSquare)(DEMOTED *bp)),
+                        PieceToChar((ChessSquare)(DEMOTED(*bp))),
                         AAA + j, ONE + i - '0');
             }
             if(cps->alphaRank) { /* [HGM] shogi: translate coords */
@@ -6465,7 +6506,7 @@ SendBoard (ChessProgramState *cps, int moveNum)
                     AAA + j, ONE + i - '0');
             if(message[0] == '+' || message[0] == '~') {
              snprintf(message, MSG_SIZ,"%c%c%d+\n",
-                        PieceToChar((ChessSquare)(DEMOTED *bp)),
+                        PieceToChar((ChessSquare)(DEMOTED(*bp))),
                         AAA + j, ONE + i - '0');
             }
             if(cps->alphaRank) { /* [HGM] shogi: translate coords */
@@ -6630,9 +6671,8 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i
 
     piece = boards[currentMove][fromY][fromX];
     if(gameInfo.variant == VariantChu) {
-        int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece;
         promotionZoneSize = BOARD_HEIGHT/3;
-        highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion;
+        highestPromotingPiece = (PieceToChar(piece) == '+' || PieceToChar(CHUPROMOTED(piece)) != '+') ? WhitePawn : WhiteKing;
     } else if(gameInfo.variant == VariantShogi) {
         promotionZoneSize = BOARD_HEIGHT/3 +(BOARD_HEIGHT == 8);
         highestPromotingPiece = (int)WhiteAlfil;
@@ -7035,11 +7075,9 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar)
            return;
        } else if (toX >= 0 && toY >= 0) {
            if(!appData.pieceMenu && toX == fromX && toY == fromY && boards[0][rf][ff] != EmptySquare) {
-               ChessSquare q, p = boards[0][rf][ff];
-               if(p >= BlackPawn) p = BLACK_TO_WHITE p;
-               if(CHUPROMOTED p < BlackPawn) p = q = CHUPROMOTED boards[0][rf][ff];
-               else p = CHUDEMOTED (q = boards[0][rf][ff]);
-               if(PieceToChar(q) == '+') gatingPiece = p;
+               ChessSquare p = boards[0][rf][ff];
+               if(PieceToChar(p) == '+') gatingPiece = CHUDEMOTED(p); else
+               if(PieceToChar(CHUPROMOTED(p)) =='+') gatingPiece = CHUPROMOTED(p); 
            }
            boards[0][toY][toX] = boards[0][fromY][fromX];
            if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
@@ -7055,6 +7093,7 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar)
                }
            } else
            boards[0][fromY][fromX] = gatingPiece;
+           ClearHighlights();
            DrawPosition(FALSE, boards[currentMove]);
            return;
        }
@@ -7718,7 +7757,7 @@ LeftClick (ClickType clickType, int xPix, int yPix)
        if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) {
          if(appData.sweepSelect) {
            promoSweep = defaultPromoChoice;
-           if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED piece) == '+') promoSweep = CHUPROMOTED piece;
+           if(gameInfo.variant != VariantChuChess && PieceToChar(CHUPROMOTED(piece)) == '+') promoSweep = CHUPROMOTED(piece);
            selectFlag = 0; lastX = xPix; lastY = yPix;
            ReportClick("put", x, y); // extra put to prompt engine for 'choice' command
            Sweep(0); // Pawn that is going to promote: preview promotion piece
@@ -7752,6 +7791,8 @@ LeftClick (ClickType clickType, int xPix, int yPix)
            ClearHighlights();
        }
 #endif
+       if(PieceToChar(CHUPROMOTED(boards[currentMove][fromY][fromX])) == '+')
+         defaultPromoChoice = CHUPROMOTED(boards[currentMove][fromY][fromX]);
        if(gameInfo.variant == VariantChuChess && piece != WhitePawn && piece != BlackPawn) defaultPromoChoice = piece;
        if(marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square
          dragging *= 2;            // flag button-less dragging if we are dragging
@@ -8605,7 +8646,7 @@ HandleMachineMove (char *message, ChessProgramState *cps)
     ChessMove moveType;
     char promoChar, roar;
     char *p, *pv=buf1;
-    int machineWhite, oldError;
+    int oldError;
     char *bookHit;
 
     if(cps == &pairing && sscanf(message, "%d-%d", &savedWhitePlayer, &savedBlackPlayer) == 2) {
@@ -8697,6 +8738,8 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 
       } else {
 
+       int machineWhite = FALSE;
+
        switch (gameMode) {
          case BeginningOfGame:
            /* Extra move from before last reset; ignore */
@@ -8778,7 +8821,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
             snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c, %c%c) res=%d",
                     machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, kill2X+AAA, kill2Y+ONE, moveType);
            if (gameMode == TwoMachinesPlay) {
-             GameEnds(machineWhite ? BlackWins : WhiteWins,
+             GameEnds(cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
                        buf1, GE_XBOARD);
            }
            return;
@@ -8796,7 +8839,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
             if(moveType == IllegalMove) {
              snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to illegal move: %s (%c%c%c%c)%c",
                         machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, 0);
-                GameEnds(machineWhite ? BlackWins : WhiteWins,
+                GameEnds(cps->twoMachinesColor[0] == 'w' ? BlackWins : WhiteWins,
                            buf1, GE_XBOARD);
                return;
            } else if(!appData.fischerCastling)
@@ -9027,7 +9070,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
     if(sscanf(message, "piece %s %s", buf2, buf1) == 2) {
       ChessSquare piece = WhitePawn;
       char *p=message+6, *q, *s = SUFFIXES, ID = *p;
-      if(*p == '+') piece = CHUPROMOTED WhitePawn, ID = *++p;
+      if(*p == '+') piece = CHUPROMOTED(WhitePawn), ID = *++p;
       if(q = strchr(s, p[1])) ID += 64*(q - s + 1), p++;
       piece += CharToPiece(ID & 255) - WhitePawn;
       if(cps != &first || appData.testLegality && *engineVariant == NULLCHAR
@@ -9046,7 +9089,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
       return;
     }
     if(sscanf(message, "choice %s", promoRestrict) == 1 && promoSweep != EmptySquare) {
-      promoSweep = PieceToChar(forwardMostMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict));
+      promoSweep = CharToPiece(currentMove&1 ? ToLower(*promoRestrict) : ToUpper(*promoRestrict));
       Sweep(0);
       return;
     }
@@ -10147,6 +10190,11 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
        board[toY][toX + (killX < fromX ? 1 : -1)] = killed;
         board[EP_STATUS] = EP_NONE; // capture was fake!
     } else
+    if(nrCastlingRights == 0 && board[toY][toX] < EmptySquare && (piece < BlackPawn) == (board[toY][toX] < BlackPawn)) {
+        board[fromY][fromX] = board[toY][toX]; // capture own will lead to swapping
+        board[toY][toX] = piece;
+        board[EP_STATUS] = EP_NONE; // capture was fake!
+    } else
     /* Code added by Tord: */
     /* FRC castling assumed when king captures friendly rook. [HGM] or RxK for S-Chess */
     if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook ||
@@ -10196,8 +10244,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                ) {
        /* white pawn promotion */
         board[toY][toX] = CharToPiece(ToUpper(promoChar));
-        if(board[toY][toX] < WhiteCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
-            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
+        if(board[toY][toX] < WhiteCannon && PieceToChar(PROMOTED(board[toY][toX])) == '~') /* [HGM] use shadow piece (if available) */
+            board[toY][toX] = (ChessSquare) (PROMOTED(board[toY][toX]));
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY >= BOARD_HEIGHT>>1)
               && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
@@ -10261,8 +10309,8 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
                ) {
        /* black pawn promotion */
        board[toY][toX] = CharToPiece(ToLower(promoChar));
-        if(board[toY][toX] < BlackCannon && PieceToChar(PROMOTED board[toY][toX]) == '~') /* [HGM] use shadow piece (if available) */
-            board[toY][toX] = (ChessSquare) (PROMOTED board[toY][toX]);
+        if(board[toY][toX] < BlackCannon && PieceToChar(PROMOTED(board[toY][toX])) == '~') /* [HGM] use shadow piece (if available) */
+            board[toY][toX] = (ChessSquare) (PROMOTED(board[toY][toX]));
        board[fromY][fromX] = EmptySquare;
     } else if ((fromY < BOARD_HEIGHT>>1)
               && (oldEP == toX || oldEP == EP_UNKNOWN || appData.testLegality || abs(toX - fromX) > 4)
@@ -10336,10 +10384,10 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
         p = (int) captured;
         if (p >= (int) BlackPawn) {
           p -= (int)BlackPawn;
-          if(DEMOTED p >= 0 && PieceToChar(p) == '+') {
+          if(DEMOTED(p) >= 0 && PieceToChar(p) == '+') {
                   /* Restore shogi-promoted piece to its original  first */
-                  captured = (ChessSquare) (DEMOTED captured);
-                  p = DEMOTED p;
+                  captured = (ChessSquare) (DEMOTED(captured));
+                  p = DEMOTED(p);
           }
           p = PieceToNumber((ChessSquare)p);
           if(p >= gameInfo.holdingsSize) { p = 0; captured = BlackPawn; }
@@ -10347,9 +10395,9 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
           board[p][BOARD_WIDTH-1] = BLACK_TO_WHITE captured;
        } else {
           p -= (int)WhitePawn;
-          if(DEMOTED p >= 0 && PieceToChar(p) == '+') {
-                  captured = (ChessSquare) (DEMOTED captured);
-                  p = DEMOTED p;
+          if(DEMOTED(p) >= 0 && PieceToChar(p) == '+') {
+                  captured = (ChessSquare) (DEMOTED(captured));
+                  p = DEMOTED(p);
           }
           p = PieceToNumber((ChessSquare)p);
           if(p >= gameInfo.holdingsSize) { p = 0; captured = WhitePawn; }
@@ -10377,13 +10425,13 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board)
     } else
     if(promoChar == '+') {
         /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */
-        board[toY][toX] = (ChessSquare) (CHUPROMOTED piece);
+        board[toY][toX] = (ChessSquare) (CHUPROMOTED(piece));
         if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight))
           board[toY][toX] = piece + WhiteLion - WhiteKnight; // adjust Knight promotions to Lion
     } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
         ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
-       if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified
-          && pieceToChar[PROMOTED newPiece] == '~') newPiece = PROMOTED newPiece; // but promoted version available
+       if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan)  // unpromoted piece specified
+          && pieceToChar[PROMOTED(newPiece)] == '~') newPiece = PROMOTED(newPiece);// but promoted version available
         board[toY][toX] = newPiece;
     }
     if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantGrand)
@@ -15373,7 +15421,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y)
       case PromotePiece:
         if(piece >= (int)WhitePawn && piece < (int)WhiteMan ||
            piece >= (int)BlackPawn && piece < (int)BlackMan   ) {
-            selection = (ChessSquare) (PROMOTED piece);
+            selection = (ChessSquare) (PROMOTED(piece));
         } else if(piece == EmptySquare) selection = WhiteSilver;
         else selection = (ChessSquare)((int)piece - 1);
         goto defaultlabel;
@@ -15381,7 +15429,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y)
       case DemotePiece:
         if(piece > (int)WhiteMan && piece <= (int)WhiteKing ||
            piece > (int)BlackMan && piece <= (int)BlackKing   ) {
-            selection = (ChessSquare) (DEMOTED piece);
+            selection = (ChessSquare) (DEMOTED(piece));
         } else if(piece == EmptySquare) selection = BlackSilver;
         else selection = (ChessSquare)((int)piece + 1);
         goto defaultlabel;
@@ -16950,6 +16998,7 @@ ParseOption (Option *opt, ChessProgramState *cps)
        char *p, *q, buf[MSG_SIZ];
        int n, min = (-1)<<31, max = 1<<31, def;
 
+       opt->target = &opt->value;   // OK for spin/slider and checkbox
        if(p = strstr(opt->name, " -spin ")) {
            if((n = sscanf(p, " -spin %d %d %d", &def, &min, &max)) < 3 ) return FALSE;
            if(max < min) max = min; // enforce consistency
@@ -16972,14 +17021,17 @@ ParseOption (Option *opt, ChessProgramState *cps)
        } else if((p = strstr(opt->name, " -string "))) {
            opt->textValue = p+9;
            opt->type = TextBox;
+           opt->target = &opt->textValue;
        } else if((p = strstr(opt->name, " -file "))) {
            // for now -file is a synonym for -string, to already provide compatibility with future polyglots
-           opt->textValue = p+7;
+           opt->target = opt->textValue = p+7;
            opt->type = FileName; // FileName;
+           opt->target = &opt->textValue;
        } else if((p = strstr(opt->name, " -path "))) {
            // for now -file is a synonym for -string, to already provide compatibility with future polyglots
-           opt->textValue = p+7;
+           opt->target = opt->textValue = p+7;
            opt->type = PathName; // PathName;
+           opt->target = &opt->textValue;
        } else if(p = strstr(opt->name, " -check ")) {
            if(sscanf(p, " -check %d", &def) < 1) return FALSE;
            opt->value = (def != 0);
@@ -17043,9 +17095,9 @@ FeatureDone (ChessProgramState *cps, int val)
       (cb == TwoMachinesEventIfReady)) {
     CancelDelayedEvent();
     ScheduleDelayedEvent(cb, val ? 1 : 3600000);
-  }
+  } else if(!val && !cps->reload) ClearOptions(cps); // let 'spurious' done=0 clear engine's option list
   cps->initDone = val;
-  if(val) cps->reload = FALSE;
+  if(val) cps->reload = FALSE,  RefreshSettingsDialog(cps, val);
 }
 
 /* Parse feature command from engine */
@@ -17987,13 +18039,13 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts)
                 if(PieceToChar(piece) == '+') {
                     /* [HGM] write promoted pieces as '+<unpromoted>' (Shogi) */
                     *p++ = '+';
-                    piece = (ChessSquare)(CHUDEMOTED piece);
+                    piece = (ChessSquare)(CHUDEMOTED(piece));
                 }
                 *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece));
                 if(*p = PieceSuffix(piece)) p++;
                 if(p[-1] == '~') {
                     /* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */
-                    p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED piece));
+                    p[-1] = PieceToChar((ChessSquare)(CHUDEMOTED(piece)));
                     *p++ = '~';
                 }
            }
@@ -18235,7 +18287,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
                     if(q = strchr(s, p[1])) p++;
                     piece = CharToPiece(c + (q ? 64*(q - s + 1) : 0));
                     if(piece == EmptySquare) return FALSE; /* unknown piece */
-                    piece = (ChessSquare) (CHUPROMOTED piece ); p++;
+                    piece = (ChessSquare) (CHUPROMOTED(piece)); p++;
                     if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */
                 } else {
                     char c = *p++;
@@ -18245,7 +18297,7 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
 
                 if(piece==EmptySquare) return FALSE; /* unknown piece */
                 if(*p == '~') { /* [HGM] make it a promoted piece for Crazyhouse */
-                    piece = (ChessSquare) (PROMOTED piece);
+                    piece = (ChessSquare) (PROMOTED(piece));
                     if(PieceToChar(piece) != '~') return FALSE; /* cannot be a promoted piece */
                     p++;
                 }