reindent some functions
[xboard.git] / backend.c
index 96e898a..6095cc6 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -235,6 +235,8 @@ extern char installDir[MSG_SIZ];
 
 extern int tinyLayout, smallLayout;
 ChessProgramStats programStats;
+char lastPV[2][2*MSG_SIZ]; /* [HGM] pv: last PV in thinking output of each engine */
+int endPV = -1;
 static int exiting = 0; /* [HGM] moved to top */
 static int setboardSpoiledMachineBlack = 0 /*, errorExitFlag = 0*/;
 int startedFromPositionFile = FALSE; Board filePosition;       /* [HGM] loadPos */
@@ -247,6 +249,7 @@ int lastSavedGame; /* [HGM] save: ID of game */
 char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
 extern int chatCount;
 int chattingPartner;
+char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */
 
 /* States for ics_getting_history */
 #define H_FALSE 0
@@ -353,6 +356,7 @@ PosFlags(index)
   case VariantNoCastle:
   case VariantShatranj:
   case VariantCourier:
+  case VariantMakruk:
     flags &= ~F_ALL_CASTLE_OK;
     break;
   default:
@@ -486,10 +490,10 @@ ChessSquare  KnightmateArray[2][BOARD_FILES] = {
 };
 
 ChessSquare fairyArray[2][BOARD_FILES] = { /* [HGM] Queen side differs from King side */
-    { WhiteCannon, WhiteNightrider, WhiteAlfil, WhiteQueen,
+    { WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
         WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
-    { BlackCannon, BlackNightrider, BlackAlfil, BlackQueen,
-       BlackKing, BlackBishop, BlackKnight, BlackRook }
+    { BlackLance, BlackAlfil, BlackMarshall, BlackAngel,
+       BlackKing, BlackMarshall, BlackAlfil, BlackLance }
 };
 
 ChessSquare ShatranjArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */
@@ -499,6 +503,13 @@ ChessSquare ShatranjArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatr
         BlackFerz, BlackAlfil, BlackKnight, BlackRook }
 };
 
+ChessSquare makrukArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */
+    { WhiteRook, WhiteKnight, WhiteMan, WhiteKing,
+        WhiteFerz, WhiteMan, WhiteKnight, WhiteRook },
+    { BlackRook, BlackKnight, BlackMan, BlackFerz,
+        BlackKing, BlackMan, BlackKnight, BlackRook }
+};
+
 
 #if (BOARD_FILES>=10)
 ChessSquare ShogiArray[2][BOARD_FILES] = {
@@ -754,7 +765,7 @@ InitBackEnd1()
     /* [HGM] time odds: set factor for each machine */
     first.timeOdds  = appData.firstTimeOdds;
     second.timeOdds = appData.secondTimeOdds;
-    { int norm = 1;
+    { float norm = 1;
         if(appData.timeOddsMode) {
             norm = first.timeOdds;
             if(norm > second.timeOdds) norm = second.timeOdds;
@@ -885,6 +896,7 @@ InitBackEnd1()
       case VariantAtomic:     /* should work except for win condition */
       case Variant3Check:     /* should work except for win condition */
       case VariantShatranj:   /* should work except for all win conditions */
+      case VariantMakruk:     /* should work except for daw countdown */
       case VariantBerolina:   /* might work if TestLegality is off */
       case VariantCapaRandom: /* should work */
       case VariantJanus:      /* should work */
@@ -1080,198 +1092,267 @@ InitBackEnd2()
 void
 InitBackEnd3 P((void))
 {
-    GameMode initialMode;
-    char buf[MSG_SIZ];
-    int err;
-
-    InitChessProgram(&first, startedFromSetupPosition);
-
-
-    if (appData.icsActive) {
-#ifdef WIN32
-        /* [DM] Make a console window if needed [HGM] merged ifs */
-        ConsoleCreate();
-#endif
-       err = establish();
-       if (err != 0) {
-           if (*appData.icsCommPort != NULLCHAR) {
-               sprintf(buf, _("Could not open comm port %s"),
-                       appData.icsCommPort);
-           } else {
-               snprintf(buf, sizeof(buf), _("Could not connect to host %s, port %s"),
-                       appData.icsHost, appData.icsPort);
+  GameMode initialMode;
+  char buf[MSG_SIZ];
+  int err;
+  
+  InitChessProgram(&first, startedFromSetupPosition);
+  
+  
+  if (appData.icsActive) 
+    {
+      err = establish();
+      if (err != 0) 
+       {
+         if (*appData.icsCommPort != NULLCHAR) 
+           {
+             sprintf(buf, _("Could not open comm port %s"),
+                     appData.icsCommPort);
            }
-           DisplayFatalError(buf, err, 1);
-           return;
+         else 
+           {
+             snprintf(buf, sizeof(buf), _("Could not connect to host %s, port %s"),
+                      appData.icsHost, appData.icsPort);
+           }
+         DisplayFatalError(buf, err, 1);
+         return;
        }
-       SetICSMode();
-       telnetISR =
-         AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
-       fromUserISR =
-         AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
-    } else if (appData.noChessProgram) {
-       SetNCPMode();
-    } else {
-       SetGNUMode();
+      SetICSMode();
+      telnetISR =
+       AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
+      fromUserISR =
+       AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
     }
-
-    if (*appData.cmailGameName != NULLCHAR) {
-       SetCmailMode();
-       OpenLoopback(&cmailPR);
-       cmailISR =
-         AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
+  else if (appData.noChessProgram) 
+    {
+      SetNCPMode();
+    } 
+  else 
+    {
+      SetGNUMode();
     }
-
-    ThawUI();
-    DisplayMessage("", "");
-    if (StrCaseCmp(appData.initialMode, "") == 0) {
+  
+  if (*appData.cmailGameName != NULLCHAR) 
+    {
+      SetCmailMode();
+      OpenLoopback(&cmailPR);
+      cmailISR =
+       AddInputSource(cmailPR, FALSE, CmailSigHandlerCallBack, &cmailISR);
+    }
+  
+  ThawUI();
+  DisplayMessage("", "");
+  if (StrCaseCmp(appData.initialMode, "") == 0) 
+    {
       initialMode = BeginningOfGame;
-    } else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "TwoMachines") == 0) 
+    {
       initialMode = TwoMachinesPlay;
-    } else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "AnalyzeFile") == 0) 
+    {
       initialMode = AnalyzeFile;
-    } else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "Analysis") == 0) 
+    {
       initialMode = AnalyzeMode;
-    } else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "MachineWhite") == 0) 
+    {
       initialMode = MachinePlaysWhite;
-    } else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "MachineBlack") == 0) 
+    {
       initialMode = MachinePlaysBlack;
-    } else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "EditGame") == 0) 
+    {
       initialMode = EditGame;
-    } else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "EditPosition") == 0) 
+    {
       initialMode = EditPosition;
-    } else if (StrCaseCmp(appData.initialMode, "Training") == 0) {
+    } 
+  else if (StrCaseCmp(appData.initialMode, "Training") == 0) 
+    {
       initialMode = Training;
-    } else {
+    } 
+  else 
+    {
       sprintf(buf, _("Unknown initialMode %s"), appData.initialMode);
       DisplayFatalError(buf, 0, 2);
       return;
     }
-
-    if (appData.matchMode) {
-       /* Set up machine vs. machine match */
-       if (appData.noChessProgram) {
-           DisplayFatalError(_("Can't have a match with no chess programs"),
-                             0, 2);
-           return;
+  
+  if (appData.matchMode) 
+    {
+      /* Set up machine vs. machine match */
+      if (appData.noChessProgram) 
+       {
+         DisplayFatalError(_("Can't have a match with no chess programs"),
+                           0, 2);
+         return;
        }
-       matchMode = TRUE;
-       matchGame = 1;
-       if (*appData.loadGameFile != NULLCHAR) {
-           int index = appData.loadGameIndex; // [HGM] autoinc
-           if(index<0) lastIndex = index = 1;
-           if (!LoadGameFromFile(appData.loadGameFile,
-                                 index,
-                                 appData.loadGameFile, FALSE)) {
-               DisplayFatalError(_("Bad game file"), 0, 1);
-               return;
+      matchMode = TRUE;
+      matchGame = 1;
+      if (*appData.loadGameFile != NULLCHAR) 
+       {
+         int index = appData.loadGameIndex; // [HGM] autoinc
+         if(index<0) lastIndex = index = 1;
+         if (!LoadGameFromFile(appData.loadGameFile,
+                               index,
+                               appData.loadGameFile, FALSE)) 
+           {
+             DisplayFatalError(_("Bad game file"), 0, 1);
+             return;
            }
-       } else if (*appData.loadPositionFile != NULLCHAR) {
-           int index = appData.loadPositionIndex; // [HGM] autoinc
-           if(index<0) lastIndex = index = 1;
-           if (!LoadPositionFromFile(appData.loadPositionFile,
-                                     index,
-                                     appData.loadPositionFile)) {
-               DisplayFatalError(_("Bad position file"), 0, 1);
-               return;
+       } 
+      else if (*appData.loadPositionFile != NULLCHAR) 
+       {
+         int index = appData.loadPositionIndex; // [HGM] autoinc
+         if(index<0) lastIndex = index = 1;
+         if (!LoadPositionFromFile(appData.loadPositionFile,
+                                   index,
+                                   appData.loadPositionFile)) 
+           {
+             DisplayFatalError(_("Bad position file"), 0, 1);
+             return;
            }
        }
-       TwoMachinesEvent();
-    } else if (*appData.cmailGameName != NULLCHAR) {
-       /* Set up cmail mode */
-       ReloadCmailMsgEvent(TRUE);
-    } else {
-       /* Set up other modes */
-       if (initialMode == AnalyzeFile) {
-         if (*appData.loadGameFile == NULLCHAR) {
-           DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);
-           return;
-         }
+      TwoMachinesEvent();
+    } 
+  else if (*appData.cmailGameName != NULLCHAR) 
+    {
+      /* Set up cmail mode */
+      ReloadCmailMsgEvent(TRUE);
+    } 
+  else 
+    {
+      /* Set up other modes */
+      if (initialMode == AnalyzeFile) 
+       {
+         if (*appData.loadGameFile == NULLCHAR) 
+           {
+             DisplayFatalError(_("AnalyzeFile mode requires a game file"), 0, 1);
+             return;
+           }
        }
-       if (*appData.loadGameFile != NULLCHAR) {
-           (void) LoadGameFromFile(appData.loadGameFile,
-                                   appData.loadGameIndex,
-                                   appData.loadGameFile, TRUE);
-       } else if (*appData.loadPositionFile != NULLCHAR) {
-           (void) LoadPositionFromFile(appData.loadPositionFile,
-                                       appData.loadPositionIndex,
-                                       appData.loadPositionFile);
-            /* [HGM] try to make self-starting even after FEN load */
-            /* to allow automatic setup of fairy variants with wtm */
-            if(initialMode == BeginningOfGame && !blackPlaysFirst) {
-                gameMode = BeginningOfGame;
-                setboardSpoiledMachineBlack = 1;
-            }
-            /* [HGM] loadPos: make that every new game uses the setup */
-            /* from file as long as we do not switch variant          */
-            if(!blackPlaysFirst) {
-                startedFromPositionFile = TRUE;
-                CopyBoard(filePosition, boards[0]);
+      if (*appData.loadGameFile != NULLCHAR) 
+       {
+         (void) LoadGameFromFile(appData.loadGameFile,
+                                 appData.loadGameIndex,
+                                 appData.loadGameFile, TRUE);
+       } 
+      else if (*appData.loadPositionFile != NULLCHAR) 
+       {
+         (void) LoadPositionFromFile(appData.loadPositionFile,
+                                     appData.loadPositionIndex,
+                                     appData.loadPositionFile);
+         /* [HGM] try to make self-starting even after FEN load */
+         /* to allow automatic setup of fairy variants with wtm */
+         if(initialMode == BeginningOfGame && !blackPlaysFirst) 
+           {
+             gameMode = BeginningOfGame;
+             setboardSpoiledMachineBlack = 1;
             }
+         /* [HGM] loadPos: make that every new game uses the setup */
+         /* from file as long as we do not switch variant          */
+         if(!blackPlaysFirst) 
+           {
+             startedFromPositionFile = TRUE;
+             CopyBoard(filePosition, boards[0]);
+           }
        }
-       if (initialMode == AnalyzeMode) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);
-           return;
-         }
+      if (initialMode == AnalyzeMode) 
+       {
+         if (appData.noChessProgram) 
+           {
+             DisplayFatalError(_("Analysis mode requires a chess engine"), 0, 2);
+             return;
+           }
+         if (appData.icsActive) 
+           {
+             DisplayFatalError(_("Analysis mode does not work with ICS mode"),0,2);
+             return;
+           }
          AnalyzeModeEvent();
-       } else if (initialMode == AnalyzeFile) {
+       } 
+      else if (initialMode == AnalyzeFile) 
+       {
          appData.showThinking = TRUE; // [HGM] thinking: moved out of ShowThinkingEvent
          ShowThinkingEvent();
          AnalyzeFileEvent();
          AnalysisPeriodicEvent(1);
-       } else if (initialMode == MachinePlaysWhite) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("MachineWhite mode requires a chess engine"),
-                             0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),
-                             0, 2);
-           return;
-         }
+       } 
+      else if (initialMode == MachinePlaysWhite) 
+       {
+         if (appData.noChessProgram) 
+           {
+             DisplayFatalError(_("MachineWhite mode requires a chess engine"),
+                               0, 2);
+             return;
+           }
+         if (appData.icsActive) 
+           {
+             DisplayFatalError(_("MachineWhite mode does not work with ICS mode"),
+                               0, 2);
+             return;
+           }
          MachineWhiteEvent();
-       } else if (initialMode == MachinePlaysBlack) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("MachineBlack mode requires a chess engine"),
-                             0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),
-                             0, 2);
-           return;
-         }
+       } 
+      else if (initialMode == MachinePlaysBlack) 
+       {
+         if (appData.noChessProgram) 
+           {
+             DisplayFatalError(_("MachineBlack mode requires a chess engine"),
+                               0, 2);
+             return;
+           }
+         if (appData.icsActive) 
+           {
+             DisplayFatalError(_("MachineBlack mode does not work with ICS mode"),
+                               0, 2);
+             return;
+           }
          MachineBlackEvent();
-       } else if (initialMode == TwoMachinesPlay) {
-         if (appData.noChessProgram) {
-           DisplayFatalError(_("TwoMachines mode requires a chess engine"),
-                             0, 2);
-           return;
-         }
-         if (appData.icsActive) {
-           DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),
-                             0, 2);
-           return;
-         }
+       } 
+      else if (initialMode == TwoMachinesPlay) 
+       {
+         if (appData.noChessProgram) 
+           {
+             DisplayFatalError(_("TwoMachines mode requires a chess engine"),
+                               0, 2);
+             return;
+           }
+         if (appData.icsActive) 
+           {
+             DisplayFatalError(_("TwoMachines mode does not work with ICS mode"),
+                               0, 2);
+             return;
+           }
          TwoMachinesEvent();
-       } else if (initialMode == EditGame) {
+       } 
+      else if (initialMode == EditGame) 
+       {
          EditGameEvent();
-       } else if (initialMode == EditPosition) {
+       } 
+      else if (initialMode == EditPosition) 
+       {
          EditPositionEvent();
-       } else if (initialMode == Training) {
-         if (*appData.loadGameFile == NULLCHAR) {
-           DisplayFatalError(_("Training mode requires a game file"), 0, 2);
-           return;
-         }
+       } 
+      else if (initialMode == Training) 
+       {
+         if (*appData.loadGameFile == NULLCHAR) 
+           {
+             DisplayFatalError(_("Training mode requires a game file"), 0, 2);
+             return;
+           }
          TrainingEvent();
        }
     }
+  
+  return;
 }
 
 /*
@@ -2164,7 +2245,8 @@ read_from_ics(isr, closure, data, count, error)
     }
 
        buf[buf_len] = NULLCHAR;
-       next_out = leftover_len;
+//     next_out = leftover_len; // [HGM] should we set this to 0, and not print it in advance?
+       next_out = 0;
        leftover_start = 0;
 
        i = 0;
@@ -2319,16 +2401,16 @@ read_from_ics(isr, closure, data, count, error)
                    if(!suppressKibitz) // [HGM] kibitz
                        AppendComment(forwardMostMove, StripHighlight(parse), TRUE);
                    else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
-                       int nrDigit = 0, nrAlph = 0, i;
+                       int nrDigit = 0, nrAlph = 0, j;
                        if(parse_pos > MSG_SIZ - 30) // defuse unreasonably long input
                        { parse_pos = MSG_SIZ-30; parse[parse_pos - 1] = '\n'; }
                        parse[parse_pos] = NULLCHAR;
                        // try to be smart: if it does not look like search info, it should go to
                        // ICS interaction window after all, not to engine-output window.
-                       for(i=0; i<parse_pos; i++) { // count letters and digits
-                           nrDigit += (parse[i] >= '0' && parse[i] <= '9');
-                           nrAlph  += (parse[i] >= 'a' && parse[i] <= 'z');
-                           nrAlph  += (parse[i] >= 'A' && parse[i] <= 'Z');
+                       for(j=0; j<parse_pos; j++) { // count letters and digits
+                           nrDigit += (parse[j] >= '0' && parse[j] <= '9');
+                           nrAlph  += (parse[j] >= 'a' && parse[j] <= 'z');
+                           nrAlph  += (parse[j] >= 'A' && parse[j] <= 'Z');
                        }
                        if(nrAlph < 9*nrDigit) { // if more than 10% digit we assume search info
                            int depth=0; float score;
@@ -2338,6 +2420,7 @@ read_from_ics(isr, closure, data, count, error)
                                pvInfoList[forwardMostMove-1].score = 100*score;
                            }
                            OutputKibitz(suppressKibitz, parse);
+                           next_out = i+1; // [HGM] suppress printing in ICS window
                        } else {
                            char tmp[MSG_SIZ];
                            sprintf(tmp, _("your opponent kibitzes: %s"), parse);
@@ -2346,7 +2429,7 @@ read_from_ics(isr, closure, data, count, error)
                    }
                    started = STARTED_NONE;
                } else {
-                   /* Don't match patterns against characters in chatter */
+                   /* Don't match patterns against characters in comment */
                    i++;
                    continue;
                }
@@ -2446,9 +2529,11 @@ read_from_ics(isr, closure, data, count, error)
                        }
                        continue;
                } else
-               if(looking_at(buf, &i, "kibitzed to")) { // suppress the acknowledgements of our own autoKibitz
-                   started = STARTED_CHATTER;
-                   suppressKibitz = TRUE;
+               if(looking_at(buf, &i, "kibitzed to *\n") && atoi(star_match[0])) {
+                   // suppress the acknowledgements of our own autoKibitz
+                   SendToPlayer(star_match[0], strlen(star_match[0]));
+                   looking_at(buf, &i, "*% "); // eat prompt
+                   next_out = i;
                }
            } // [HGM] kibitz: end of patch
 
@@ -2848,7 +2933,9 @@ read_from_ics(isr, closure, data, count, error)
            if (looking_at(buf, &i, "% ") ||
                ((started == STARTED_MOVES || started == STARTED_MOVES_NOHIDE)
                 && looking_at(buf, &i, "}*"))) { char *bookHit = NULL; // [HGM] book
+               if(suppressKibitz) next_out = i;
                savingComment = FALSE;
+               suppressKibitz = 0;
                switch (started) {
                  case STARTED_MOVES:
                  case STARTED_MOVES_NOHIDE:
@@ -3145,6 +3232,7 @@ read_from_ics(isr, closure, data, count, error)
                    strncmp(why, "Continuing ", 11) == 0) {
                    gs_gamenum = gamenum;
                    strcpy(gs_kind, strchr(why, ' ') + 1);
+                   VariantSwitch(boards[currentMove], StringToVariant(gs_kind)); // [HGM] variantswitch: even before we get first board
 #if ZIPPY
                    if (appData.zippyPlay) {
                        ZippyGameStart(whitename, blackname);
@@ -3274,6 +3362,7 @@ read_from_ics(isr, closure, data, count, error)
                        while(looking_at(buf, &i, "\n")); // [HGM] skip empty lines
                        if (looking_at(buf, &i, "*% ")) {
                            savingComment = FALSE;
+                           suppressKibitz = 0;
                        }
                    }
                    next_out = i;
@@ -3343,6 +3432,7 @@ read_from_ics(isr, closure, data, count, error)
                    if (looking_at(buf, &i, "*% ")) {
                        if(strchr(star_match[0], 7)) SendToPlayer("\007", 1); // Bell(); // FICS fuses bell for next board with prompt in zh captures
                        savingComment = FALSE;
+                       suppressKibitz = 0;
                    }
                    next_out = i;
                }
@@ -3351,13 +3441,14 @@ read_from_ics(isr, closure, data, count, error)
 
            i++;                /* skip unparsed character and loop back */
        }
-
-       if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz suppress printing in ICS interaction window
-           started != STARTED_HOLDINGS && i > next_out) {
-           SendToPlayer(&buf[next_out], i - next_out);
+       
+       if (started != STARTED_MOVES && started != STARTED_BOARD && !suppressKibitz && // [HGM] kibitz
+//         started != STARTED_HOLDINGS && i > next_out) { // [HGM] should we compare to leftover_start in stead of i?
+//         SendToPlayer(&buf[next_out], i - next_out);
+           started != STARTED_HOLDINGS && leftover_start > next_out) {
+           SendToPlayer(&buf[next_out], leftover_start - next_out);
            next_out = i;
        }
-       suppressKibitz = FALSE; // [HGM] kibitz: has done its duty in if-statement above
 
        leftover_len = buf_len - leftover_start;
        /* if buffer ends with something we couldn't parse,
@@ -3691,16 +3782,16 @@ ParseBoard12(string)
     if(moveNum == 0 || gameInfo.variant != VariantFischeRandom)
     { int i, j; ChessSquare wKing = WhiteKing, bKing = BlackKing;
 
-        for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
+        for(i=BOARD_LEFT, j=NoRights; i<BOARD_RGHT; i++)
             if(board[0][i] == WhiteRook) j = i;
         initialRights[0] = boards[moveNum][CASTLING][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
-        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
+        for(i=BOARD_RGHT-1, j=NoRights; i>=BOARD_LEFT; i--)
             if(board[0][i] == WhiteRook) j = i;
         initialRights[1] = boards[moveNum][CASTLING][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
-        for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)
+        for(i=BOARD_LEFT, j=NoRights; i<BOARD_RGHT; i++)
             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
         initialRights[3] = boards[moveNum][CASTLING][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
-        for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)
+        for(i=BOARD_RGHT-1, j=NoRights; i>=BOARD_LEFT; i--)
             if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;
         initialRights[4] = boards[moveNum][CASTLING][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? NoRights : j);
 
@@ -3710,6 +3801,11 @@ ParseBoard12(string)
         for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
             if(board[BOARD_HEIGHT-1][k] == bKing)
                 initialRights[5] = boards[moveNum][CASTLING][5] = k;
+        if(gameInfo.variant == VariantTwoKings) {
+            // In TwoKings looking for a King does not work, so always give castling rights to a King on e1/e8
+            if(board[0][4] == wKing) initialRights[2] = boards[moveNum][CASTLING][2] = 4;
+            if(board[BOARD_HEIGHT-1][4] == bKing) initialRights[5] = boards[moveNum][CASTLING][5] = 4;
+        }
     } else { int r;
         r = boards[moveNum][CASTLING][0] = initialRights[0];
         if(board[0][r] != WhiteRook) boards[moveNum][CASTLING][0] = NoRights;
@@ -4179,7 +4275,7 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
       case BlackPromotionChancellor:
       case WhitePromotionArchbishop:
       case BlackPromotionArchbishop:
-        if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier)
+        if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk)
             sprintf(user_move, "%c%c%c%c=%c\n",
                 AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,
                PieceToChar(WhiteFerz));
@@ -4358,7 +4454,8 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
        if (appData.testLegality) {
          return (*moveType != IllegalMove);
        } else {
-         return !(fromX == fromY && toX == toY);
+         return !(*fromX == *toX && *fromY == *toY) && boards[moveNum][*fromY][*fromX] != EmptySquare && 
+                       WhiteOnMove(moveNum) == (boards[moveNum][*fromY][*fromX] < BlackPawn);
        }
 
       case WhiteDrop:
@@ -4393,6 +4490,108 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     }
 }
 
+
+void
+ParsePV(char *pv)
+{ // Parse a string of PV moves, and append to current game, behind forwardMostMove
+  int fromX, fromY, toX, toY; char promoChar;
+  ChessMove moveType;
+  Boolean valid;
+  int nr = 0;
+
+  endPV = forwardMostMove;
+  do {
+    while(*pv == ' ') pv++;
+    if(*pv == '(') pv++; // first (ponder) move can be in parentheses
+    valid = ParseOneMove(pv, endPV, &moveType, &fromX, &fromY, &toX, &toY, &promoChar);
+if(appData.debugMode){
+fprintf(debugFP,"parsePV: %d %c%c%c%c '%s'\n", valid, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, pv);
+}
+    if(!valid && nr == 0 &&
+       ParseOneMove(pv, endPV-1, &moveType, &fromX, &fromY, &toX, &toY, &promoChar)){ 
+        nr++; moveType = Comment; // First move has been played; kludge to make sure we continue
+    }
+    while(*pv && *pv++ != ' '); // skip what we parsed; assume space separators
+    if(moveType == Comment) { valid++; continue; } // allow comments in PV
+    nr++;
+    if(endPV+1 > framePtr) break; // no space, truncate
+    if(!valid) break;
+    endPV++;
+    CopyBoard(boards[endPV], boards[endPV-1]);
+    ApplyMove(fromX, fromY, toX, toY, promoChar, boards[endPV]);
+    moveList[endPV-1][0] = fromX + AAA;
+    moveList[endPV-1][1] = fromY + ONE;
+    moveList[endPV-1][2] = toX + AAA;
+    moveList[endPV-1][3] = toY + ONE;
+    parseList[endPV-1][0] = NULLCHAR;
+  } while(valid);
+  currentMove = endPV;
+  if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
+  SetPremoveHighlights(moveList[currentMove-1][0]-AAA, moveList[currentMove-1][1]-ONE,
+                       moveList[currentMove-1][2]-AAA, moveList[currentMove-1][3]-ONE);
+  DrawPosition(TRUE, boards[currentMove]);
+}
+
+static int lastX, lastY;
+
+Boolean
+LoadMultiPV(int x, int y, char *buf, int index, int *start, int *end)
+{
+       int startPV;
+
+       if(index < 0 || index >= strlen(buf)) return FALSE; // sanity
+       lastX = x; lastY = y;
+       while(index > 0 && buf[index-1] != '\n') index--; // beginning of line
+       startPV = index;
+      while(buf[index] != '\n') if(buf[index++] == '\t') startPV = index;
+      index = startPV;
+       while(buf[index] && buf[index] != '\n') index++;
+       buf[index] = 0;
+       ParsePV(buf+startPV);
+       *start = startPV; *end = index-1;
+       return TRUE;
+}
+
+Boolean
+LoadPV(int x, int y)
+{ // called on right mouse click to load PV
+  int which = gameMode == TwoMachinesPlay && (WhiteOnMove(forwardMostMove) == (second.twoMachinesColor[0] == 'w'));
+  lastX = x; lastY = y;
+  ParsePV(lastPV[which]); // load the PV of the thinking engine in the boards array.
+  return TRUE;
+}
+
+void
+UnLoadPV()
+{
+  if(endPV < 0) return;
+  endPV = -1;
+  currentMove = forwardMostMove;
+  ClearPremoveHighlights();
+  DrawPosition(TRUE, boards[currentMove]);
+}
+
+void
+MovePV(int x, int y, int h)
+{ // step through PV based on mouse coordinates (called on mouse move)
+  int margin = h>>3, step = 0;
+
+  if(endPV < 0) return;
+  // we must somehow check if right button is still down (might be released off board!)
+  if(y < margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = 1; else
+  if(y > h - margin && (abs(x - lastX) > 6 || abs(y - lastY) > 6)) step = -1; else
+  if( y > lastY + 6 ) step = -1; else if(y < lastY - 6) step = 1;
+  if(!step) return;
+  lastX = x; lastY = y;
+  if(currentMove + step > endPV || currentMove + step < forwardMostMove) step = 0;
+  currentMove += step;
+  if(currentMove == forwardMostMove) ClearPremoveHighlights(); else
+  SetPremoveHighlights(moveList[currentMove-1][0]-AAA, moveList[currentMove-1][1]-ONE,
+                       moveList[currentMove-1][2]-AAA, moveList[currentMove-1][3]-ONE);
+  DrawPosition(FALSE, boards[currentMove]);
+}
+
+
 // [HGM] shuffle: a general way to suffle opening setups, applicable to arbitrary variants.
 // All positions will have equal probability, but the current method will not provide a unique
 // numbering scheme for arrays that contain 3 or more pieces of the same kind.
@@ -4516,12 +4715,12 @@ void SetUpShuffle(Board board, int number)
            // Last King gets castling rights
            while(piecesLeft[(int)WhiteUnicorn]) {
                i = put(board, WhiteUnicorn, 0, piecesLeft[(int)WhiteRook]/2, ANY);
-               initialRights[2]  = initialRights[5]  = boards[0][CASTLING][2] = boards[0][CASTLING][5] = i;
+               initialRights[2]  = initialRights[5]  = board[CASTLING][2] = board[CASTLING][5] = i;
            }
 
            while(piecesLeft[(int)WhiteKing]) {
                i = put(board, WhiteKing, 0, piecesLeft[(int)WhiteRook]/2, ANY);
-               initialRights[2]  = initialRights[5]  = boards[0][CASTLING][2] = boards[0][CASTLING][5] = i;
+               initialRights[2]  = initialRights[5]  = board[CASTLING][2] = board[CASTLING][5] = i;
            }
 
 
@@ -4536,9 +4735,9 @@ void SetUpShuffle(Board board, int number)
                if(PosFlags(0) & F_FRC_TYPE_CASTLING) { // first and last Rook get FRC castling rights
                        if(first) {
                                first=0;
-                               initialRights[1]  = initialRights[4]  = boards[0][CASTLING][1] = boards[0][CASTLING][4] = i;
+                               initialRights[1]  = initialRights[4]  = board[CASTLING][1] = board[CASTLING][4] = i;
                        }
-                       initialRights[0]  = initialRights[3]  = boards[0][CASTLING][0] = boards[0][CASTLING][3] = i;
+                       initialRights[0]  = initialRights[3]  = board[CASTLING][0] = board[CASTLING][3] = i;
                }
        }
        for(i=BOARD_LEFT; i<BOARD_RGHT; i++) { // copy black from white
@@ -4664,6 +4863,12 @@ InitPosition(redraw)
       nrCastlingRights = 0;
       SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k");
       break;
+    case VariantMakruk:
+      pieces = makrukArray;
+      nrCastlingRights = 0;
+      startedFromSetupPosition = TRUE;
+      SetCharTable(pieceToChar, "PN.R.M....SKpn.r.m....sk"); 
+      break;
     case VariantTwoKings:
       pieces = twoKingsArray;
       break;
@@ -4723,7 +4928,7 @@ InitPosition(redraw)
       break;
     case VariantFairy:
       pieces = fairyArray;
-      SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
+      SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk"); 
       break;
     case VariantGreat:
       pieces = GreatArray;
@@ -4775,6 +4980,7 @@ InitPosition(redraw)
 
     pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */
     if(pawnRow < 1) pawnRow = 1;
+    if(gameInfo.variant == VariantMakruk) pawnRow = 2;
 
     /* User pieceToChar list overrules defaults */
     if(appData.pieceToCharTable != NULL)
@@ -4961,6 +5167,8 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
     if(gameInfo.variant == VariantShogi) {
         promotionZoneSize = 3;
         highestPromotingPiece = (int)WhiteFerz;
+    } else if(gameInfo.variant == VariantMakruk) {
+        promotionZoneSize = 3;
     }
 
     // next weed out all moves that do not touch the promotion zone at all
@@ -5004,7 +5212,7 @@ HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
     }
 
     // we either have a choice what to promote to, or (in Shogi) whether to promote
-    if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier) {
+    if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk) {
        *promoChoice = PieceToChar(BlackFerz);  // no choice
        return FALSE;
     }
@@ -5276,6 +5484,18 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn)
            return AmbiguousMove;
        } else if (toX >= 0 && toY >= 0) {
            boards[0][toY][toX] = boards[0][fromY][fromX];
+           if(fromX == BOARD_LEFT-2) { // handle 'moves' out of holdings
+               if(boards[0][fromY][0] != EmptySquare) {
+                   if(boards[0][fromY][1]) boards[0][fromY][1]--;
+                   if(boards[0][fromY][1] == 0)  boards[0][fromY][0] = EmptySquare; 
+               }
+           } else
+           if(fromX == BOARD_RGHT+1) {
+               if(boards[0][fromY][BOARD_WIDTH-1] != EmptySquare) {
+                   if(boards[0][fromY][BOARD_WIDTH-2]) boards[0][fromY][BOARD_WIDTH-2]--;
+                   if(boards[0][fromY][BOARD_WIDTH-2] == 0)  boards[0][fromY][BOARD_WIDTH-1] = EmptySquare; 
+               }
+           } else
            boards[0][fromY][fromX] = EmptySquare;
            return AmbiguousMove;
        }
@@ -5563,6 +5783,43 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar)
     FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
 }
 
+void
+Mark(board, flags, kind, rf, ff, rt, ft, closure)
+     Board board;
+     int flags;
+     ChessMove kind;
+     int rf, ff, rt, ft;
+     VOIDSTAR closure;
+{
+    typedef char Markers[BOARD_RANKS][BOARD_FILES];
+    Markers *m = (Markers *) closure;
+    if(rf == fromY && ff == fromX)
+       (*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
+                        || kind == WhiteCapturesEnPassant
+                        || kind == BlackCapturesEnPassant);
+    else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
+}
+
+void
+MarkTargetSquares(int clear)
+{
+  int x, y;
+  if(!appData.markers || !appData.highlightDragging || 
+     !appData.testLegality || gameMode == EditPosition) return;
+  if(clear) {
+    for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) marker[y][x] = 0;
+  } else {
+    int capt = 0;
+    GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker);
+    if(PosFlags(0) & F_MANDATORY_CAPTURE) {
+      for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
+      if(capt)
+      for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x] == 1) marker[y][x] = 0;
+    }
+  }
+  DrawPosition(TRUE, NULL);
+}
+
 void LeftClick(ClickType clickType, int xPix, int yPix)
 {
     int x, y;
@@ -5571,6 +5828,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
     char promoChoice = NULLCHAR;
 
     if (clickType == Press) ErrorPopDown();
+    MarkTargetSquares(1);
 
     x = EventToSquare(xPix, BOARD_WIDTH);
     y = EventToSquare(yPix, BOARD_HEIGHT);
@@ -5616,6 +5874,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
                fromX = x;
                fromY = y;
                second = 0;
+               MarkTargetSquares(0);
                DragPieceBegin(xPix, yPix);
                if (appData.highlightDragging) {
                    SetHighlights(x, y, -1, -1);
@@ -5656,6 +5915,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
            if (OKToStartUserMove(x, y)) {
                fromX = x;
                fromY = y;
+               MarkTargetSquares(0);
                DragPieceBegin(xPix, yPix);
            }
            return;
@@ -5708,11 +5968,30 @@ void LeftClick(ClickType clickType, int xPix, int yPix)
        appData.animate = FALSE;
     }
 
-    // moves into holding are invalid for now (later perhaps allow in EditPosition)
+    // moves into holding are invalid for now (except in EditPosition, adapting to-square)
     if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) {
+       ChessSquare piece = boards[currentMove][fromY][fromX];
+       if(gameMode == EditPosition && piece != EmptySquare &&
+          fromX >= BOARD_LEFT && fromX < BOARD_RGHT) {
+           int n;
+            
+           if(x == BOARD_LEFT-2 && piece >= BlackPawn) {
+               n = PieceToNumber(piece - (int)BlackPawn);
+               if(n > gameInfo.holdingsSize) { n = 0; piece = BlackPawn; }
+               boards[currentMove][BOARD_HEIGHT-1 - n][0] = piece;
+               boards[currentMove][BOARD_HEIGHT-1 - n][1]++;
+           } else
+           if(x == BOARD_RGHT+1 && piece < BlackPawn) {
+               n = PieceToNumber(piece);
+               if(n > gameInfo.holdingsSize) { n = 0; piece = WhitePawn; }
+               boards[currentMove][n][BOARD_WIDTH-1] = piece;
+               boards[currentMove][n][BOARD_WIDTH-2]++;
+           }
+           boards[currentMove][fromY][fromX] = EmptySquare;
+       }
        ClearHighlights();
        fromX = fromY = -1;
-       DrawPosition(TRUE, NULL);
+       DrawPosition(TRUE, boards[currentMove]);
        return;
     }
 
@@ -5769,6 +6048,8 @@ void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cp
         stats.an_move_count = cpstats->nr_moves;
     }
 
+    if(stats.pv && stats.pv[0]) strcpy(lastPV[stats.which], stats.pv); // [HGM] pv: remember last PV of each
+
     SetProgramStats( &stats );
 }
 
@@ -5830,6 +6111,8 @@ HandleMachineMove(message, cps)
     int machineWhite;
     char *bookHit;
 
+    cps->userError = 0;
+
 FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book hit
     /*
      * Kludge to ignore BEL characters
@@ -6502,6 +6785,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.
        return;
     }
     if (!strncmp(message, "tellusererror ", 14)) {
+       cps->userError = 1;
        DisplayError(message + 14, 0);
        return;
     }
@@ -7385,6 +7669,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
      Board board;
 {
   ChessSquare captured = board[toY][toX], piece, king; int p, oldEP = EP_NONE, berolina = 0;
+  int promoRank = gameInfo.variant == VariantMakruk ? 3 : 1;
 
     /* [HGM] compute & store e.p. status and castling rights for new position */
     /* we can always do that 'in place', now pointers to these rights are passed to ApplyMove */
@@ -7431,7 +7716,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
     }
 
   /* [HGM] In Shatranj and Courier all promotions are to Ferz */
-  if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier)
+  if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier || gameInfo.variant == VariantMakruk)
        && promoChar != 0) promoChar = PieceToChar(WhiteFerz);
 
   if (fromX == toX && fromY == toY) return;
@@ -7482,7 +7767,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
         board[toY][toX+1] = board[fromY][BOARD_LEFT];
         board[fromY][BOARD_LEFT] = EmptySquare;
     } else if (board[fromY][fromX] == WhitePawn
-               && toY == BOARD_HEIGHT-1
+               && toY >= BOARD_HEIGHT-promoRank
                && gameInfo.variant != VariantXiangqi
                ) {
        /* white pawn promotion */
@@ -7546,13 +7831,13 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[fromY][0] = EmptySquare;
        board[toY][2] = BlackRook;
     } else if (board[fromY][fromX] == BlackPawn
-              && toY == 0
+              && toY < promoRank
                && gameInfo.variant != VariantXiangqi
                ) {
        /* black pawn promotion */
-       board[0][toX] = CharToPiece(ToLower(promoChar));
-       if (board[0][toX] == EmptySquare) {
-           board[0][toX] = BlackQueen;
+       board[toY][toX] = CharToPiece(ToLower(promoChar));
+       if (board[toY][toX] == EmptySquare) {
+           board[toY][toX] = BlackQueen;
        }
         if(gameInfo.variant==VariantBughouse ||
            gameInfo.variant==VariantCrazyhouse) /* [HGM] use shadow piece */
@@ -7739,6 +8024,7 @@ MakeMove(fromX, fromY, toX, toY, promoChar)
                        0, 1);
       return;
     }
+    UnLoadPV(); // [HGM] pv: if we are looking at a PV, abort this
     if (commentList[forwardMostMove+1] != NULL) {
        free(commentList[forwardMostMove+1]);
        commentList[forwardMostMove+1] = NULL;
@@ -10801,76 +11087,84 @@ MachineWhiteEvent()
 void
 MachineBlackEvent()
 {
-    char buf[MSG_SIZ];
-   char *bookHit = NULL;
-
-    if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
-       return;
-
-
-    if (gameMode == PlayFromGameFile ||
-       gameMode == TwoMachinesPlay  ||
-       gameMode == Training         ||
-       gameMode == AnalyzeMode      ||
-       gameMode == EndOfGame)
-        EditGameEvent();
-
-    if (gameMode == EditPosition) 
-        EditPositionDone(TRUE);
-
-    if (WhiteOnMove(currentMove)) {
-       DisplayError(_("It is not Black's turn"), 0);
-       return;
+  char buf[MSG_SIZ];
+  char *bookHit = NULL;
+  
+  if (appData.noChessProgram || (gameMode == MachinePlaysBlack))
+    return;
+  
+  
+  if (gameMode == PlayFromGameFile 
+      || gameMode == TwoMachinesPlay  
+      || gameMode == Training     
+      || gameMode == AnalyzeMode
+      || gameMode == EndOfGame)
+    EditGameEvent();
+  
+  if (gameMode == EditPosition) 
+    EditPositionDone(TRUE);
+  
+  if (WhiteOnMove(currentMove)) 
+    {
+      DisplayError(_("It is not Black's turn"), 0);
+      return;
     }
-
-    if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
-      ExitAnalyzeMode();
-
-    if (gameMode == EditGame || gameMode == AnalyzeMode ||
-       gameMode == AnalyzeFile)
-       TruncateGame();
-
-    ResurrectChessProgram();   /* in case it isn't running */
-    gameMode = MachinePlaysBlack;
-    pausing = FALSE;
-    ModeHighlight();
-    SetGameInfo();
-    sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
-    DisplayTitle(buf);
-    if (first.sendName) {
+  
+  if (gameMode == AnalyzeMode || gameMode == AnalyzeFile)
+    ExitAnalyzeMode();
+  
+  if (gameMode == EditGame || gameMode == AnalyzeMode 
+      || gameMode == AnalyzeFile)
+    TruncateGame();
+  
+  ResurrectChessProgram();     /* in case it isn't running */
+  gameMode = MachinePlaysBlack;
+  pausing  = FALSE;
+  ModeHighlight();
+  SetGameInfo();
+  sprintf(buf, "%s vs. %s", gameInfo.white, gameInfo.black);
+  DisplayTitle(buf);
+  if (first.sendName) 
+    {
       sprintf(buf, "name %s\n", gameInfo.white);
       SendToProgram(buf, &first);
     }
-    if (first.sendTime) {
-      if (first.useColors) {
-       SendToProgram("white\n", &first); /*gnu kludge*/
-      }
+  if (first.sendTime) 
+    {
+      if (first.useColors) 
+       {
+         SendToProgram("white\n", &first); /*gnu kludge*/
+       }
       SendTimeRemaining(&first, FALSE);
     }
-    if (first.useColors) {
+  if (first.useColors) 
+    {
       SendToProgram("black\n", &first); // [HGM] book: 'go' sent separately
     }
-    bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
-    SetMachineThinkingEnables();
-    first.maybeThinking = TRUE;
-    StartClocks();
-
-    if (appData.autoFlipView && flipView) {
+  bookHit = SendMoveToBookUser(forwardMostMove-1, &first, TRUE); // [HGM] book: send go or retrieve book move
+  SetMachineThinkingEnables();
+  first.maybeThinking = TRUE;
+  StartClocks();
+  
+  if (appData.autoFlipView && flipView) 
+    {
       flipView = !flipView;
       DrawPosition(FALSE, NULL);
       DisplayBothClocks();       // [HGM] logo: clocks might have to be exchanged;
     }
-    if(bookHit) { // [HGM] book: simulate book reply
-       static char bookMove[MSG_SIZ]; // a bit generous?
-
-       programStats.nodes = programStats.depth = programStats.time =
-       programStats.score = programStats.got_only_move = 0;
-       sprintf(programStats.movelist, "%s (xbook)", bookHit);
-
-       strcpy(bookMove, "move ");
-       strcat(bookMove, bookHit);
-       HandleMachineMove(bookMove, &first);
+  if(bookHit) 
+    { // [HGM] book: simulate book reply
+      static char bookMove[MSG_SIZ]; // a bit generous?
+      
+      programStats.nodes = programStats.depth = programStats.time 
+       = programStats.score = programStats.got_only_move = 0;
+      sprintf(programStats.movelist, "%s (xbook)", bookHit);
+      
+      strcpy(bookMove, "move ");
+      strcat(bookMove, bookHit);
+      HandleMachineMove(bookMove, &first);
     }
+  return;
 }
 
 
@@ -11366,9 +11660,20 @@ EditPositionMenuEvent(selection, x, y)
 
       case EmptySquare:
        if (gameMode == IcsExamining) {
+            if (x < BOARD_LEFT || x >= BOARD_RGHT) break; // [HGM] holdings
             sprintf(buf, "%sx@%c%c\n", ics_prefix, AAA + x, ONE + y);
            SendToICS(buf);
        } else {
+            if(x < BOARD_LEFT || x >= BOARD_RGHT) {
+                if(x == BOARD_LEFT-2) {
+                    if(y < BOARD_HEIGHT-1-gameInfo.holdingsSize) break;
+                    boards[0][y][1] = 0;
+                } else
+                if(x == BOARD_RGHT+1) {
+                    if(y >= gameInfo.holdingsSize) break;
+                    boards[0][y][BOARD_WIDTH-2] = 0;
+                } else break;
+            }
            boards[0][y][x] = EmptySquare;
            DrawPosition(FALSE, boards[0]);
        }
@@ -11394,7 +11699,8 @@ EditPositionMenuEvent(selection, x, y)
       case BlackQueen:
         if(gameInfo.variant == VariantShatranj ||
            gameInfo.variant == VariantXiangqi  ||
-           gameInfo.variant == VariantCourier    )
+           gameInfo.variant == VariantCourier  ||
+           gameInfo.variant == VariantMakruk     )
             selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz);
         goto defaultlabel;
 
@@ -11407,12 +11713,28 @@ EditPositionMenuEvent(selection, x, y)
       default:
         defaultlabel:
        if (gameMode == IcsExamining) {
+            if (x < BOARD_LEFT || x >= BOARD_RGHT) break; // [HGM] holdings
            sprintf(buf, "%s%c@%c%c\n", ics_prefix,
                     PieceToChar(selection), AAA + x, ONE + y);
            SendToICS(buf);
        } else {
+            if(x < BOARD_LEFT || x >= BOARD_RGHT) {
+                int n;
+                if(x == BOARD_LEFT-2 && selection >= BlackPawn) {
+                    n = PieceToNumber(selection - BlackPawn);
+                    if(n > gameInfo.holdingsSize) { n = 0; selection = BlackPawn; }
+                    boards[0][BOARD_HEIGHT-1-n][0] = selection;
+                    boards[0][BOARD_HEIGHT-1-n][1]++;
+                } else
+                if(x == BOARD_RGHT+1 && selection < BlackPawn) {
+                    n = PieceToNumber(selection);
+                    if(n > gameInfo.holdingsSize) { n = 0; selection = WhitePawn; }
+                    boards[0][n][BOARD_WIDTH-1] = selection;
+                    boards[0][n][BOARD_WIDTH-2]++;
+                }
+            } else
            boards[0][y][x] = selection;
-           DrawPosition(FALSE, boards[0]);
+           DrawPosition(TRUE, boards[0]);
        }
        break;
     }
@@ -12487,7 +12809,7 @@ SendToProgram(message, cps)
             }
             gameInfo.resultDetails = StrSave(buf);
         }
-        DisplayFatalError(buf, error, 1);
+        if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, error, 1); else errorExitStatus = 1;
     }
 }
 
@@ -12519,7 +12841,7 @@ ReceiveFromProgram(isr, closure, message, count, error)
                 gameInfo.resultDetails = StrSave(buf);
             }
            RemoveInputSource(cps->isr);
-           DisplayFatalError(buf, 0, 1);
+           if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, 0, 1); else errorExitStatus = 1;
        } else {
            sprintf(buf,
                    _("Error reading from %s chess program (%s)"),
@@ -12532,7 +12854,7 @@ ReceiveFromProgram(isr, closure, message, count, error)
                 cps->pr = NoProc;
             }
 
-            DisplayFatalError(buf, error, 1);
+            if(!cps->userError || !appData.popupExitMessage) DisplayFatalError(buf, error, 1); else errorExitStatus = 1;
        }
        return;
     }
@@ -12677,7 +12999,7 @@ SendTimeRemaining(cps, machineWhite)
     /* [HGM] translate opponent's time by time-odds factor */
     otime = (otime * cps->other->timeOdds) / cps->timeOdds;
     if (appData.debugMode) {
-        fprintf(debugFP, "time odds: %d %d \n", cps->timeOdds, cps->other->timeOdds);
+        fprintf(debugFP, "time odds: %f %f \n", cps->timeOdds, cps->other->timeOdds);
     }
 
     if (time <= 0) time = 1;
@@ -13368,12 +13690,12 @@ DecrementClocks()
        if(whiteNPS >= 0) lastTickLength = 0;
        timeRemaining = whiteTimeRemaining -= lastTickLength;
        DisplayWhiteClock(whiteTimeRemaining - fudge,
-                         WhiteOnMove(currentMove));
+                         WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove));
     } else {
        if(blackNPS >= 0) lastTickLength = 0;
        timeRemaining = blackTimeRemaining -= lastTickLength;
        DisplayBlackClock(blackTimeRemaining - fudge,
-                         !WhiteOnMove(currentMove));
+                         !WhiteOnMove(currentMove < forwardMostMove ? currentMove : forwardMostMove));
     }
 
     if (CheckFlags()) return;
@@ -13803,7 +14125,7 @@ PositionToFEN(move, overrideCastling)
   }
 
   if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&
-     gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) {
+     gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) { 
     /* En passant target square */
     if (move > backwardMostMove) {
         fromX = moveList[move - 1][0] - AAA;
@@ -13976,12 +14298,14 @@ ParseFEN(board, blackPlaysFirst, fen)
         board[CASTLING][i] =
             gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom ? NoRights : initialRights[i];
     }   /* assume possible unless obviously impossible */
-    if(initialRights[0]>=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) board[CASTLING][0] = NoRights;
-    if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) board[CASTLING][1] = NoRights;
-    if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteKing) board[CASTLING][2] = NoRights;
-    if(initialRights[3]>=0 && board[castlingRank[3]][initialRights[3]] != BlackRook) board[CASTLING][3] = NoRights;
-    if(initialRights[4]>=0 && board[castlingRank[4]][initialRights[4]] != BlackRook) board[CASTLING][4] = NoRights;
-    if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackKing) board[CASTLING][5] = NoRights;
+    if(initialRights[0]!=NoRights && board[castlingRank[0]][initialRights[0]] != WhiteRook) board[CASTLING][0] = NoRights;
+    if(initialRights[1]!=NoRights && board[castlingRank[1]][initialRights[1]] != WhiteRook) board[CASTLING][1] = NoRights;
+    if(initialRights[2]!=NoRights && board[castlingRank[2]][initialRights[2]] != WhiteUnicorn
+                                 && board[castlingRank[2]][initialRights[2]] != WhiteKing) board[CASTLING][2] = NoRights;
+    if(initialRights[3]!=NoRights && board[castlingRank[3]][initialRights[3]] != BlackRook) board[CASTLING][3] = NoRights;
+    if(initialRights[4]!=NoRights && board[castlingRank[4]][initialRights[4]] != BlackRook) board[CASTLING][4] = NoRights;
+    if(initialRights[5]!=NoRights && board[castlingRank[5]][initialRights[5]] != BlackUnicorn
+                                 && board[castlingRank[5]][initialRights[5]] != BlackKing) board[CASTLING][5] = NoRights;
     FENrulePlies = 0;
 
     while(*p==' ') p++;
@@ -13996,12 +14320,18 @@ ParseFEN(board, blackPlaysFirst, fen)
              (gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) &&
              ( *p >= 'a' && *p < 'a' + gameInfo.boardWidth) ||
              ( *p >= 'A' && *p < 'A' + gameInfo.boardWidth)   ) {
-        char c = *p++; int whiteKingFile=-1, blackKingFile=-1;
+        char c = *p++; int whiteKingFile=NoRights, blackKingFile=NoRights;
 
         for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {
             if(board[BOARD_HEIGHT-1][i] == BlackKing) blackKingFile = i;
             if(board[0             ][i] == WhiteKing) whiteKingFile = i;
         }
+        if(gameInfo.variant == VariantTwoKings || gameInfo.variant == VariantKnightmate)
+            whiteKingFile = blackKingFile = BOARD_WIDTH >> 1; // for these variant scanning fails
+        if(whiteKingFile == NoRights || board[0][whiteKingFile] != WhiteUnicorn
+                                     && board[0][whiteKingFile] != WhiteKing) whiteKingFile = NoRights;
+        if(blackKingFile == NoRights || board[BOARD_HEIGHT-1][blackKingFile] != BlackUnicorn
+                                     && board[BOARD_HEIGHT-1][blackKingFile] != BlackKing) blackKingFile = NoRights;
         switch(c) {
           case'K':
               for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--);
@@ -14009,7 +14339,7 @@ ParseFEN(board, blackPlaysFirst, fen)
               board[CASTLING][2] = whiteKingFile;
               break;
           case'Q':
-              for(i=BOARD_LEFT; board[0][i]!=WhiteRook && i<whiteKingFile; i++);
+              for(i=BOARD_LEFT;  i<BOARD_RGHT && board[0][i]!=WhiteRook && i<whiteKingFile; i++);
               board[CASTLING][1] = i != whiteKingFile ? i : NoRights;
               board[CASTLING][2] = whiteKingFile;
               break;
@@ -14019,7 +14349,7 @@ ParseFEN(board, blackPlaysFirst, fen)
               board[CASTLING][5] = blackKingFile;
               break;
           case'q':
-              for(i=BOARD_LEFT; board[BOARD_HEIGHT-1][i]!=BlackRook && i<blackKingFile; i++);
+              for(i=BOARD_LEFT; i<BOARD_RGHT && board[BOARD_HEIGHT-1][i]!=BlackRook && i<blackKingFile; i++);
               board[CASTLING][4] = i != blackKingFile ? i : NoRights;
               board[CASTLING][5] = blackKingFile;
           case '-':
@@ -14051,6 +14381,8 @@ ParseFEN(board, blackPlaysFirst, fen)
               }
         }
       }
+      for(i=0; i<nrCastlingRights; i++)
+        if(board[CASTLING][i] != NoRights) initialRights[i] = board[CASTLING][i];
     if (appData.debugMode) {
         fprintf(debugFP, "FEN castling rights:");
         for(i=0; i<nrCastlingRights; i++)
@@ -14063,7 +14395,7 @@ ParseFEN(board, blackPlaysFirst, fen)
 
     /* read e.p. field in games that know e.p. capture */
     if(gameInfo.variant != VariantShogi    && gameInfo.variant != VariantXiangqi &&
-       gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier ) {
+       gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) { 
       if(*p=='-') {
         p++; board[EP_STATUS] = EP_NONE;
       } else {