Allow arbitrary nesting of sub-variations in PGN input
[xboard.git] / backend.c
index 16fdc62..01d447e 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -4377,7 +4377,7 @@ ParseBoard12(string)
            strcat(parseList[moveNum - 1], " ");
            strcat(parseList[moveNum - 1], elapsed_time);
            /* currentMoveString is set as a side-effect of ParseOneMove */
-           if(gameInfo.variant == VariantShogi && currentMoveString[4]) currentMoveString[4] = '+';
+           if(gameInfo.variant == VariantShogi && currentMoveString[4]) currentMoveString[4] = '^';
            safeStrCpy(moveList[moveNum - 1], currentMoveString, sizeof(moveList[moveNum - 1])/sizeof(moveList[moveNum - 1][0]));
            strcat(moveList[moveNum - 1], "\n");
 
@@ -4884,9 +4884,6 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
      int *fromX, *fromY, *toX, *toY;
      char *promoChar;
 {
-    if (appData.debugMode) {
-        fprintf(debugFP, "move to parse: %s\n", move);
-    }
     *moveType = yylexstr(moveNum, move, yy_textstr, sizeof yy_textstr);
 
     switch (*moveType) {
@@ -5598,7 +5595,8 @@ SendBoard(cps, moveNum)
       /* Kludge to set black to move, avoiding the troublesome and now
        * deprecated "black" command.
        */
-      if (!WhiteOnMove(moveNum)) SendToProgram("a2a3\n", cps);
+      if (!WhiteOnMove(moveNum)) // [HGM] but better a deprecated command than an illegal move...
+        SendToProgram(boards[0][1][BOARD_LEFT] == WhitePawn ? "a2a3\n" : "black\n", cps);
 
       SendToProgram("edit\n", cps);
       SendToProgram("#\n", cps);
@@ -5738,7 +5736,7 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
              gameMode == IcsPlayingBlack &&  WhiteOnMove(currentMove);
     if(appData.testLegality && !premove) {
        moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
-                       fromY, fromX, toY, toX, NULLCHAR);
+                       fromY, fromX, toY, toX, gameInfo.variant == VariantShogi ? '+' : NULLCHAR);
        if(moveType != WhitePromotion && moveType  != BlackPromotion)
            return FALSE;
     }
@@ -7459,6 +7457,15 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
       return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands!
     }
 
+    if (!appData.testLegality && !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position
+      int dummy, s=6; char buf[MSG_SIZ];
+      if(appData.icsActive || forwardMostMove != 0 || cps != &first || startedFromSetupPosition) return;
+      if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf);
+      ParseFEN(boards[0], &dummy, message+s);
+      DrawPosition(TRUE, boards[0]);
+      startedFromSetupPosition = TRUE;
+      return;
+    }
     /* [HGM] Allow engine to set up a position. Don't ask me why one would
      * want this, I was asked to put it in, and obliged.
      */
@@ -8215,7 +8222,7 @@ ParseGameHistory(game)
     yynewstr(game);
     for (;;) {
        yyboardindex = boardIndex;
-       moveType = (ChessMove) yylex();
+       moveType = (ChessMove) Myylex();
        switch (moveType) {
          case IllegalMove:             /* maybe suicide chess, etc. */
   if (appData.debugMode) {
@@ -8381,10 +8388,6 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
       if( board[toY][toX] != EmptySquare )
            board[EP_STATUS] = EP_CAPTURE;
 
-  /* [HGM] In Shatranj and Courier all promotions are to Ferz */
-  if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier || gameInfo.variant == VariantMakruk)
-       && promoChar != 0) promoChar = PieceToChar(WhiteFerz);
-
   if (fromY == DROP_RANK) {
        /* must be first */
         piece = board[toY][toX] = (ChessSquare) fromX;
@@ -8647,8 +8650,8 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
     if(promoChar == '+') {
         /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite orinary Pawn promotion) */
         board[toY][toX] = (ChessSquare) (PROMOTED piece);
-    } else if(!appData.testLegality) { // without legality testing, unconditionally believe promoChar
-        board[toY][toX] = CharToPiece(promoChar);
+    } else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
+        board[toY][toX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
     }
     if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)
                && promoChar != NULLCHAR && gameInfo.holdingsSize) {
@@ -9668,7 +9671,7 @@ LoadGameOneMove(readAhead)
     } else {
       if (gameFileFP == NULL)
          return FALSE;
-      moveType = (ChessMove) yylex();
+      moveType = (ChessMove) Myylex();
     }
 
     done = FALSE;
@@ -10163,7 +10166,7 @@ LoadGame(f, gameNumber, title, useList)
     cm = lastLoadGameStart = EndOfFile;
     while (gn > 0) {
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
        switch (cm) {
          case EndOfFile:
            if (cmailMsgLoaded) {
@@ -10216,7 +10219,7 @@ LoadGame(f, gameNumber, title, useList)
            if (gn > 0) {
                do {
                    yyboardindex = forwardMostMove;
-                   cm = (ChessMove) yylex();
+                   cm = (ChessMove) Myylex();
                } while (cm == PGNTag || cm == Comment);
            }
            break;
@@ -10255,7 +10258,7 @@ LoadGame(f, gameNumber, title, useList)
        /* Skip any header junk before position diagram and/or move 1 */
        for (;;) {
            yyboardindex = forwardMostMove;
-           cm = (ChessMove) yylex();
+           cm = (ChessMove) Myylex();
 
            if (cm == EndOfFile ||
                cm == GNUChessGame || cm == XBoardGame) {
@@ -10328,7 +10331,7 @@ LoadGame(f, gameNumber, title, useList)
        }
 
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
 
        /* Handle comments interspersed among the tags */
        while (cm == Comment) {
@@ -10338,7 +10341,7 @@ LoadGame(f, gameNumber, title, useList)
            p = yy_text;
            AppendComment(currentMove, p, FALSE);
            yyboardindex = forwardMostMove;
-           cm = (ChessMove) yylex();
+           cm = (ChessMove) Myylex();
        }
     }
 
@@ -10415,7 +10418,7 @@ LoadGame(f, gameNumber, title, useList)
            }
        }
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
     }
 
     if (first.pr == NoProc) {
@@ -10441,7 +10444,7 @@ LoadGame(f, gameNumber, title, useList)
        p = yy_text;
        AppendComment(currentMove, p, FALSE);
        yyboardindex = forwardMostMove;
-       cm = (ChessMove) yylex();
+       cm = (ChessMove) Myylex();
     }
 
     if ((cm == EndOfFile && lastLoadGameStart != EndOfFile ) ||
@@ -11906,6 +11909,10 @@ TwoMachinesEvent P((void))
     DisplayMessage("", "");
     InitChessProgram(&second, FALSE);
     SendToProgram("force\n", &second);
+    if(first.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position
+      ScheduleDelayedEvent(TwoMachinesEvent, 10);
+      return;
+    }
     if (startedFromSetupPosition) {
        SendBoard(&second, backwardMostMove);
     if (appData.debugMode) {
@@ -13331,7 +13338,7 @@ if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces);
     } else {
        commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4...
        if(addBraces)
-         safeStrCpy(commentList[index], "{\n", sizeof(commentList[index])/sizeof(commentList[index][0]));
+         safeStrCpy(commentList[index], "{\n", 3);
        else commentList[index][0] = NULLCHAR;
        strcat(commentList[index], text);
        strcat(commentList[index], "\n");
@@ -13810,6 +13817,20 @@ ParseOption(Option *opt, ChessProgramState *cps)
        if(p && (p == cps->optionSettings || p[-1] == ',')) {
          snprintf(buf, MSG_SIZ, "option %s", p);
                if(p = strstr(buf, ",")) *p = 0;
+               if(q = strchr(buf, '=')) switch(opt->type) {
+                   case ComboBox:
+                       for(n=0; n<opt->max; n++)
+                           if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n;
+                       break;
+                   case TextBox:
+                       safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name));
+                       break;
+                   case Spin:
+                   case CheckBox:
+                       opt->value = atoi(q+1);
+                   default:
+                       break;
+               }
                strcat(buf, "\n");
                SendToProgram(buf, cps);
        }