changes from Alessandro Scotti from 20051231
[xboard.git] / backend.c
index f28d863..d545851 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -1,6 +1,6 @@
 /*
  * backend.c -- Common back end for X and Windows NT versions of
- * XBoard $Id$
+ * XBoard $Id: backend.c,v 2.6 2003/11/28 09:37:36 mann Exp $
  *
  * Copyright 1991 by Digital Equipment Corporation, Maynard, Massachusetts.
  * Enhancements Copyright 1992-2001 Free Software Foundation, Inc.
  *
  * See the file ChangeLog for a revision history.  */
 
+/* [AS] Also useful here for debugging */
+#ifdef WIN32
+#include <windows.h>
+
+#define DoSleep( n ) if( (n) != 0 ) Sleep( (n) );
+
+#else
+
+#define DoSleep( n )
+
+#endif
+
 #include "config.h"
 
+#include <assert.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <errno.h>
@@ -203,6 +216,8 @@ void InitBackEnd3 P((void));
 void FeatureDone P((ChessProgramState* cps, int val));
 void InitChessProgram P((ChessProgramState *cps));
 
+void GetInfoFromComment( int, char * );
+
 extern int tinyLayout, smallLayout;
 static ChessProgramStats programStats;
 
@@ -245,6 +260,35 @@ static ChessProgramStats programStats;
 #define TN_SGA  0003
 #define TN_PORT 23
 
+/* [AS] */
+static char * safeStrCpy( char * dst, const char * src, size_t count )
+{
+    assert( dst != NULL );
+    assert( src != NULL );
+    assert( count > 0 );
+
+    strncpy( dst, src, count );
+    dst[ count-1 ] = '\0';
+    return dst;
+}
+
+static char * safeStrCat( char * dst, const char * src, size_t count )
+{
+    size_t  dst_len;
+
+    assert( dst != NULL );
+    assert( src != NULL );
+    assert( count > 0 );
+
+    dst_len = strlen(dst);
+
+    assert( count > dst_len ); /* Buffer size must be greater than current length */
+
+    safeStrCpy( dst + dst_len, src, count - dst_len );
+
+    return dst;
+}
+
 /* Fake up flags for now, as we aren't keeping track of castling
    availability yet */
 int
@@ -275,6 +319,15 @@ PosFlags(index)
 
 FILE *gameFileFP, *debugFP;
 
+/*
+    [AS] Note: sometimes, the sscanf() function is used to parse the input
+    into a fixed-size buffer. Because of this, we must be prepared to
+    receive strings as long as the size of the input buffer, which is currently
+    set to 4K for Windows and 8K for the rest.
+    So, we must either allocate sufficiently large buffers here, or
+    reduce the size of the input buffer in the input reading part.
+*/
+
 char cmailMove[CMAIL_MAX_GAMES][MOVE_LEN], cmailMsg[MSG_SIZ];
 char bookOutput[MSG_SIZ*10], thinkOutput[MSG_SIZ*10], lastHint[MSG_SIZ];
 char thinkOutput1[MSG_SIZ*10];
@@ -317,12 +370,17 @@ InputSourceRef telnetISR = NULL, fromUserISR = NULL, cmailISR = NULL;
 GameMode gameMode = BeginningOfGame;
 char moveList[MAX_MOVES][MOVE_LEN], parseList[MAX_MOVES][MOVE_LEN * 2];
 char *commentList[MAX_MOVES], *cmailCommentList[CMAIL_MAX_GAMES];
+ChessProgramStats_Move pvInfoList[MAX_MOVES]; /* [AS] Info about engine thinking */
+int hiddenThinkOutputState = 0; /* [AS] */
+int adjudicateLossThreshold = 0; /* [AS] Automatic adjudication */
+int adjudicateLossPlies = 6;
 char white_holding[64], black_holding[64];
 TimeMark lastNodeCountTime;
 long lastNodeCount=0;
 int have_sent_ICS_logon = 0;
 int movesPerSession;
 long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;
+long timeControl_2; /* [AS] Allow separate time controls */
 long timeRemaining[2][MAX_MOVES];
 int matchGame = 0;
 TimeMark programStartTime;
@@ -446,6 +504,15 @@ InitBackEnd1()
        appData.zippyTalk = appData.zippyPlay = FALSE;
     }
 
+    /* [AS] Initialize pv info list */
+    {
+        int i;
+
+        for( i=0; i<MAX_MOVES; i++ ) {
+            pvInfoList[i].depth = 0;
+        }
+    }
+
     /*
      * Parse timeControl resource
      */
@@ -472,6 +539,9 @@ InitBackEnd1()
        }
     }
     
+    /* [AS] Adjudication threshold */
+    adjudicateLossThreshold = appData.adjudicateLossThreshold;
+
     first.which = "first";
     second.which = "second";
     first.maybeThinking = second.maybeThinking = FALSE;
@@ -523,6 +593,13 @@ InitBackEnd1()
     first.analyzing = second.analyzing = FALSE;
     first.initDone = second.initDone = FALSE;
 
+    /* New features added by Tord: */
+    first.useFEN960 = FALSE; second.useFEN960 = FALSE;
+    first.useOOCastle = TRUE; second.useOOCastle = TRUE;
+    /* End of new features added by Tord. */
+    first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */
+    second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */
+
     if (appData.firstProtocolVersion > PROTOVER ||
        appData.firstProtocolVersion < 1) {
       char buf[MSG_SIZ];
@@ -586,7 +663,7 @@ InitBackEnd1()
       switch (variant) {
       case VariantBughouse:     /* need four players and two boards */
       case VariantKriegspiel:   /* need to hide pieces and move details */
-      case VariantFischeRandom: /* castling doesn't work, shuffle not done */
+      /* case VariantFischeRandom: (Fabien: moved below) */
        sprintf(buf, "Variant %s supported only in ICS mode", appData.variant);
        DisplayFatalError(buf, 0, 2);
        return;
@@ -609,6 +686,7 @@ InitBackEnd1()
       case VariantNormal:     /* definitely works! */
       case VariantWildCastle: /* pieces not automatically shuffled */
       case VariantNoCastle:   /* pieces not automatically shuffled */
+      case VariantFischeRandom: /* Fabien: pieces not automatically shuffled */
       case VariantCrazyhouse: /* holdings not shown,
                                 offboard interposition not understood */
       case VariantLosers:     /* should work except for win condition,
@@ -626,12 +704,73 @@ InitBackEnd1()
     }
 }
 
+int NextIntegerFromString( char ** str, long * value )
+{
+    int result = -1;
+    char * s = *str;
+
+    while( *s == ' ' || *s == '\t' ) {
+        s++;
+    }
+
+    *value = 0;
+
+    if( *s >= '0' && *s <= '9' ) {
+        while( *s >= '0' && *s <= '9' ) {
+            *value = *value * 10 + (*s - '0');
+            s++;
+        }
+
+        result = 0;
+    }
+
+    *str = s;
+
+    return result;
+}
+
+int NextTimeControlFromString( char ** str, long * value )
+{
+    long temp;
+    int result = NextIntegerFromString( str, &temp );
+
+    if( result == 0 ) {
+        *value = temp * 60; /* Minutes */
+        if( **str == ':' ) {
+            (*str)++;
+            result = NextIntegerFromString( str, &temp );
+            *value += temp; /* Seconds */
+        }
+    }
+
+    return result;
+}
+
+int GetTimeControlForWhite()
+{
+    int result = timeControl;
+
+    return result;
+}
+
+int GetTimeControlForBlack()
+{
+    int result = timeControl;
+
+    if( timeControl_2 > 0 ) {
+        result = timeControl_2;
+    }
+
+    return result;
+}
+
 int
 ParseTimeControl(tc, ti, mps)
      char *tc;
      int ti;
      int mps;
 {
+#if 0
     int matched, min, sec;
 
     matched = sscanf(tc, "%d:%d", &min, &sec);
@@ -642,6 +781,38 @@ ParseTimeControl(tc, ti, mps)
     } else {
        return FALSE;
     }
+#else
+    long tc1;
+    long tc2;
+
+    if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {
+        return FALSE;
+    }
+
+    if( *tc == '/' ) {
+        /* Parse second time control */
+        tc++;
+
+        if( NextTimeControlFromString( &tc, &tc2 ) != 0 ) {
+            return FALSE;
+        }
+
+        if( tc2 == 0 ) {
+            return FALSE;
+        }
+
+        timeControl_2 = tc2 * 1000;
+    }
+    else {
+        timeControl_2 = 0;
+    }
+
+    if( tc1 == 0 ) {
+        return FALSE;
+    }
+
+    timeControl = tc1 * 1000;
+#endif
 
     if (ti >= 0) {
        timeIncrement = ti * 1000;  /* convert to ms */
@@ -2725,10 +2896,12 @@ ParseBoard12(string)
        gameInfo.white = StrSave(white);
        gameInfo.black = StrSave(black);
        timeControl = basetime * 60 * 1000;
+        timeControl_2 = 0;
        timeIncrement = increment * 1000;
        movesPerSession = 0;
        gameInfo.timeControl = TimeControlTagValue();
        gameInfo.variant = StringToVariant(gameInfo.event);
+        gameInfo.outOfBook = NULL;
        
        /* Do we have the ratings? */
        if (strcmp(player1Name, white) == 0 &&
@@ -3064,7 +3237,25 @@ SendMoveToProgram(moveNum, cps)
       }
       SendToProgram(buf, cps);
     } else {
-      SendToProgram(moveList[moveNum], cps);
+      /* Added by Tord: Send castle moves in "O-O" in FRC games if required by
+       * the engine. It would be nice to have a better way to identify castle
+       * moves here. */
+      if(gameInfo.variant == VariantFischeRandom && cps->useOOCastle) {
+       int fromX = moveList[moveNum][0] - 'a';
+       int fromY = moveList[moveNum][1] - '1';
+       int toX = moveList[moveNum][2] - 'a';
+       int toY = moveList[moveNum][3] - '1';
+       if((boards[currentMove][fromY][fromX] == WhiteKing
+           && boards[currentMove][toY][toX] == WhiteRook)
+          || (boards[currentMove][fromY][fromX] == BlackKing
+              && boards[currentMove][toY][toX] == BlackRook)) {
+         if(toX > fromX) SendToProgram("O-O\n", cps);
+         else SendToProgram("O-O-O\n", cps);
+       }
+       else SendToProgram(moveList[moveNum], cps);
+      }
+      else SendToProgram(moveList[moveNum], cps);
+      /* End of additions by Tord */
     }
 }
 
@@ -3085,12 +3276,20 @@ SendMoveToICS(moveType, fromX, fromY, toX, toY)
       case BlackKingSideCastle:
       case WhiteQueenSideCastleWild:
       case BlackQueenSideCastleWild:
+      /* PUSH Fabien */
+      case WhiteHSideCastleFR:
+      case BlackHSideCastleFR:
+      /* POP Fabien */
        sprintf(user_move, "o-o\n");
        break;
       case WhiteQueenSideCastle:
       case BlackQueenSideCastle:
       case WhiteKingSideCastleWild:
       case BlackKingSideCastleWild:
+      /* PUSH Fabien */
+      case WhiteASideCastleFR:
+      case BlackASideCastleFR:
+      /* POP Fabien */
        sprintf(user_move, "o-o-o\n");
        break;
       case WhitePromotionQueen:
@@ -3190,6 +3389,12 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
       case WhiteQueenSideCastleWild:
       case BlackKingSideCastleWild:
       case BlackQueenSideCastleWild:
+      /* Code added by Tord: */
+      case WhiteHSideCastleFR:
+      case WhiteASideCastleFR:
+      case BlackHSideCastleFR:
+      case BlackASideCastleFR:
+      /* End of code added by Tord */
       case IllegalMove:                /* bug or odd chess variant */
        *fromX = currentMoveString[0] - 'a';
        *fromY = currentMoveString[1] - '1';
@@ -3236,12 +3441,107 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     }
 }
 
+/* [AS] FRC game initialization */
+static int FindEmptySquare( Board board, int n )
+{
+    int i = 0;
+
+    while( 1 ) {
+        while( board[0][i] != EmptySquare ) i++;
+        if( n == 0 )
+            break;
+        n--;
+        i++;
+    }
+
+    return i;
+}
+
+static void ShuffleFRC( Board board )
+{
+    int i;
+
+    srand( time(0) );
+
+    for( i=0; i<8; i++ ) {
+        board[0][i] = EmptySquare;
+    }
+
+    board[0][(rand() % 4)*2  ] = WhiteBishop; /* On dark square */
+    board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */
+    board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen;
+    board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight;
+    board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight;
+    board[0][FindEmptySquare(board, 0)] = WhiteRook;
+    board[0][FindEmptySquare(board, 0)] = WhiteKing;
+    board[0][FindEmptySquare(board, 0)] = WhiteRook;
+
+    for( i=0; i<8; i++ ) {
+        board[7][i] = board[0][i] + BlackPawn - WhitePawn;
+    }
+}
+
+static unsigned char FRC_KnightTable[10] = {
+    0x00, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x22, 0x23, 0x33
+};
+
+static void SetupFRC( Board board, int pos_index )
+{
+    int i;
+    unsigned char knights;
+
+    /* Bring the position index into a safe range (just in case...) */
+    if( pos_index < 0 ) pos_index = 0;
+
+    pos_index %= 960;
+
+    /* Clear the board */
+    for( i=0; i<8; i++ ) {
+        board[0][i] = EmptySquare;
+    }
+
+    /* Place bishops and queen */
+    board[0][ (pos_index % 4)*2 + 1 ] = WhiteBishop; /* On lite square */
+    pos_index /= 4;
+
+    board[0][ (pos_index % 4)*2     ] = WhiteBishop; /* On dark square */
+    pos_index /= 4;
+
+    board[0][ FindEmptySquare(board, pos_index % 6) ] = WhiteQueen;
+    pos_index /= 6;
+
+    /* Place knigths */
+    knights = FRC_KnightTable[ pos_index ];
+
+    board[0][ FindEmptySquare(board, knights / 16) ] = WhiteKnight;
+    board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;
+
+    /* Place rooks and king */
+    board[0][ FindEmptySquare(board, 0) ] = WhiteRook;
+    board[0][ FindEmptySquare(board, 0) ] = WhiteKing;
+    board[0][ FindEmptySquare(board, 0) ] = WhiteRook;
+
+    /* Mirror piece placement for black */
+    for( i=0; i<8; i++ ) {
+        board[7][i] = board[0][i] + BlackPawn - WhitePawn;
+    }
+}
 
 void
 InitPosition(redraw)
      int redraw;
 {
     currentMove = forwardMostMove = backwardMostMove = 0;
+
+    /* [AS] Initialize pv info list */
+    {
+        int i;
+
+        for( i=0; i<MAX_MOVES; i++ ) {
+            pvInfoList[i].depth = 0;
+        }
+    }
+
     switch (gameInfo.variant) {
     default:
       CopyBoard(boards[0], initialPosition);
@@ -3260,11 +3560,17 @@ InitPosition(redraw)
       break;
     case VariantFischeRandom:
       CopyBoard(boards[0], initialPosition);
-      /* !!shuffle according to FR rules */
+      if( appData.defaultFrcPosition < 0 ) {
+        ShuffleFRC( boards[0] );
+      }
+      else {
+        SetupFRC( boards[0], appData.defaultFrcPosition );
+      }
       break;
     }
+
     if (redraw)
-      DrawPosition(FALSE, boards[currentMove]);
+      DrawPosition(TRUE, boards[currentMove]);
 }
 
 void
@@ -3275,7 +3581,7 @@ SendBoard(cps, moveNum)
     char message[MSG_SIZ];
     
     if (cps->useSetboard) {
-      char* fen = PositionToFEN(moveNum);
+      char* fen = PositionToFEN(moveNum, cps->useFEN960);
       sprintf(message, "setboard %s\n", fen);
       SendToProgram(message, cps);
       free(fen);
@@ -3728,6 +4034,16 @@ FinishMove(moveType, fromX, fromY, toX, toY, promoChar)
   }
 }
 
+void SendProgramStatsToFrontend( ChessProgramState * cps )
+{
+    SetProgramStats( cps == &first ? 0 : 1,
+        programStats.depth,
+        programStats.nodes,
+        programStats.score,
+        programStats.time,
+        programStats.movelist );
+}
+
 void
 HandleMachineMove(message, cps)
      char *message;
@@ -3771,11 +4087,9 @@ HandleMachineMove(message, cps)
     /*
      * Look for machine move.
      */
-    if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 &&
-        strcmp(buf2, "...") == 0) ||
-       (sscanf(message, "%s %s", buf1, machineMove) == 2 &&
-        strcmp(buf1, "move") == 0)) {
-
+    if ((sscanf(message, "%s %s %s", buf1, buf2, machineMove) == 3 && strcmp(buf2, "...") == 0) ||
+       (sscanf(message, "%s %s", buf1, machineMove) == 2 && strcmp(buf1, "move") == 0))
+    {
         /* This method is only useful on engines that support ping */
         if (cps->lastPing != cps->lastPong) {
          if (gameMode == BeginningOfGame) {
@@ -3872,8 +4186,53 @@ HandleMachineMove(message, cps)
        strcat(machineMove, "\n");
        strcpy(moveList[forwardMostMove], machineMove);
     
+        /* [AS] Save move info and clear stats for next move */
+        pvInfoList[ forwardMostMove ].score = programStats.score;
+        pvInfoList[ forwardMostMove ].depth = programStats.depth;
+        pvInfoList[ forwardMostMove ].time = -1;
+        ClearProgramStats();
+        thinkOutput[0] = NULLCHAR;
+        hiddenThinkOutputState = 0;
+
        MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/
     
+        /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */
+        if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) {
+            int count = 0;
+
+            while( count < adjudicateLossPlies ) {
+                int score = pvInfoList[ forwardMostMove - count - 1 ].score;
+
+                if( count & 1 ) {
+                    score = -score; /* Flip score for winning side */
+                }
+
+                if( score > adjudicateLossThreshold ) {
+                    break;
+                }
+
+                count++;
+            }
+
+            if( count >= adjudicateLossPlies ) {
+               ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+
+                GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins,
+                    "Xboard adjudication",
+                    GE_XBOARD );
+
+                return;
+            }
+        }
+
+        if( appData.adjudicateDrawMoves > 0 && forwardMostMove > (2*appData.adjudicateDrawMoves) ) {
+           ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+
+            GameEnds( GameIsDrawn, "Xboard adjudication: long game", GE_XBOARD );
+
+            return;
+        }
+
        if (gameMode == TwoMachinesPlay) {
            if (cps->other->sendTime) {
                SendTimeRemaining(cps->other,
@@ -3891,15 +4250,18 @@ HandleMachineMove(message, cps)
        }
 
        ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
+
        if (!pausing && appData.ringBellAfterMoves) {
            RingBell();
        }
+
        /* 
         * Reenable menu items that were disabled while
         * machine was thinking
         */
        if (gameMode != TwoMachinesPlay)
            SetUserThinkingEnables();
+
        return;
     }
 
@@ -4315,7 +4677,7 @@ HandleMachineMove(message, cps)
     /*
      * Look for thinking output
      */
-    if (appData.showThinking) {
+    if ( appData.showThinking) {
        int plylev, mvleft, mvtot, curscore, time;
        char mvname[MOVE_LEN];
        unsigned long nodes;
@@ -4337,8 +4699,7 @@ HandleMachineMove(message, cps)
          case AnalyzeFile:
            break;
          case TwoMachinesPlay:
-           if ((cps->twoMachinesColor[0] == 'w') !=
-               WhiteOnMove(forwardMostMove)) {
+           if ((cps->twoMachinesColor[0] == 'w') != WhiteOnMove(forwardMostMove)) {
                ignore = TRUE;
            }
            break;
@@ -4355,6 +4716,15 @@ HandleMachineMove(message, cps)
                if (plyext != ' ' && plyext != '\t') {
                    time *= 100;
                }
+
+                /* [AS] Negate score if machine is playing black and reporting absolute scores */
+                if( cps->scoreIsAbsolute &&
+                    ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) )
+                {
+                    curscore = -curscore;
+                }
+
+
                programStats.depth = plylev;
                programStats.nodes = nodes;
                programStats.time = time;
@@ -4369,10 +4739,8 @@ HandleMachineMove(message, cps)
                                "PV is too long; using the first %d bytes.\n",
                                sizeof(programStats.movelist) - 1);
                    }
-                   strncpy(programStats.movelist, buf1,
-                           sizeof(programStats.movelist));
-                   programStats.movelist[sizeof(programStats.movelist) - 1]
-                     = NULLCHAR;
+
+                    safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
                } else {
                    sprintf(programStats.movelist, " no PV\n");
                }
@@ -4389,16 +4757,34 @@ HandleMachineMove(message, cps)
                    programStats.line_is_book = 0;
                }
                  
-               sprintf(thinkOutput, "[%d]%c%+.2f %s%s%s",
+                SendProgramStatsToFrontend( cps );
+
+                /*
+                    [AS] Protect the thinkOutput buffer from overflow... this
+                    is only useful if buf1 hasn't overflowed first!
+                */
+               sprintf(thinkOutput, "[%d]%c%+.2f %s%s",
                        plylev, 
                        (gameMode == TwoMachinesPlay ?
                         ToUpper(cps->twoMachinesColor[0]) : ' '),
                        ((double) curscore) / 100.0,
                        prefixHint ? lastHint : "",
-                       prefixHint ? " " : "", buf1);
+                       prefixHint ? " " : "" );
 
-               if (currentMove == forwardMostMove ||
-                   gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
+                if( buf1[0] != NULLCHAR ) {
+                    unsigned max_len = sizeof(thinkOutput) - strlen(thinkOutput) - 1;
+
+                    if( strlen(buf1) > max_len ) {
+                       if( appData.debugMode) {
+                           fprintf(debugFP,"PV is too long for thinkOutput, truncating.\n");
+                        }
+                        buf1[max_len+1] = '\0';
+                    }
+
+                    strcat( thinkOutput, buf1 );
+                }
+
+               if (currentMove == forwardMostMove || gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
                    DisplayMove(currentMove - 1);
                    DisplayAnalysis();
                }
@@ -4423,8 +4809,9 @@ HandleMachineMove(message, cps)
                   isn't searching, so stats won't change) */
                programStats.line_is_book = 1;
                  
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
-                   gameMode == AnalyzeFile) {
+                SendProgramStatsToFrontend( cps );
+
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {
                    DisplayMove(currentMove - 1);
                    DisplayAnalysis();
                }
@@ -4447,6 +4834,9 @@ HandleMachineMove(message, cps)
                programStats.nr_moves = mvtot;
                strcpy(programStats.move_name, mvname);
                programStats.ok_to_send = 1;
+
+                SendProgramStatsToFrontend( cps );
+
                DisplayAnalysis();
                return;
 
@@ -4462,14 +4852,25 @@ HandleMachineMove(message, cps)
 
            } else if (thinkOutput[0] != NULLCHAR &&
                       strncmp(message, "    ", 4) == 0) {
+                unsigned message_len;
+
                p = message;
                while (*p && *p == ' ') p++;
+
+                message_len = strlen( p );
+
+                /* [AS] Avoid buffer overflow */
+                if( sizeof(thinkOutput) - strlen(thinkOutput) - 1 > message_len ) {
                strcat(thinkOutput, " ");
                strcat(thinkOutput, p);
+                }
+
+                if( sizeof(programStats.movelist) - strlen(programStats.movelist) - 1 > message_len ) {
                strcat(programStats.movelist, " ");
                strcat(programStats.movelist, p);
-               if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
-                   gameMode == AnalyzeFile) {
+                }
+
+               if (currentMove == forwardMostMove || gameMode==AnalyzeMode || gameMode == AnalyzeFile) {
                    DisplayMove(currentMove - 1);
                    DisplayAnalysis();
                }
@@ -4543,6 +4944,12 @@ ParseGameHistory(game)
          case WhiteQueenSideCastleWild:
          case BlackKingSideCastleWild:
          case BlackQueenSideCastleWild:
+          /* PUSH Fabien */
+          case WhiteHSideCastleFR:
+          case WhiteASideCastleFR:
+          case BlackHSideCastleFR:
+          case BlackASideCastleFR:
+          /* POP Fabien */
          case IllegalMove:             /* maybe suicide chess, etc. */
            fromX = currentMoveString[0] - 'a';
            fromY = currentMoveString[1] - '1';
@@ -4663,6 +5070,30 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board)
        board[toY][toX] = (ChessSquare) fromX;
     } else if (fromX == toX && fromY == toY) {
        return;
+    }
+
+    /* Code added by Tord: */
+    /* FRC castling assumed when king captures friendly rook. */
+    else if (board[fromY][fromX] == WhiteKing &&
+            board[toY][toX] == WhiteRook) {
+      board[fromY][fromX] = EmptySquare;
+      board[toY][toX] = EmptySquare;
+      if(toX > fromX) {
+       board[0][6] = WhiteKing; board[0][5] = WhiteRook;
+      } else {
+       board[0][2] = WhiteKing; board[0][3] = WhiteRook;
+      }
+    } else if (board[fromY][fromX] == BlackKing &&
+              board[toY][toX] == BlackRook) {
+      board[fromY][fromX] = EmptySquare;
+      board[toY][toX] = EmptySquare;
+      if(toX > fromX) {
+       board[7][6] = BlackKing; board[7][5] = BlackRook;
+      } else {
+       board[7][2] = BlackKing; board[7][3] = BlackRook;
+      }
+    /* End of code added by Tord */
+
     } else if (fromY == 0 && fromX == 4
        && board[fromY][fromX] == WhiteKing
        && toY == 0 && toX == 6) {
@@ -4854,6 +5285,7 @@ ShowMove(fromX, fromY, toX, toY)
     }
 
     if (instant) return;
+
     DisplayMove(currentMove - 1);
     DrawPosition(FALSE, boards[currentMove]);
     DisplayBothClocks();
@@ -4987,6 +5419,23 @@ NextMatchGame P((void))
     TwoMachinesEventIfReady();
 }
 
+void UserAdjudicationEvent( int result )
+{
+    ChessMove gameResult = GameIsDrawn;
+
+    if( result > 0 ) {
+        gameResult = WhiteWins;
+    }
+    else if( result < 0 ) {
+        gameResult = BlackWins;
+    }
+
+    if( gameMode == TwoMachinesPlay ) {
+        GameEnds( gameResult, "User adjudication", GE_XBOARD );
+    }
+}
+
+
 void
 GameEnds(result, resultDetails, whosays)
      ChessMove result;
@@ -5166,7 +5615,9 @@ GameEnds(result, resultDetails, whosays)
     
        if (first.pr != NoProc) {
            ExitAnalyzeMode();
+            DoSleep( appData.delayBeforeQuit );
            SendToProgram("quit\n", &first);
+            DoSleep( appData.delayAfterQuit );
            DestroyChildProcess(first.pr, first.useSigterm);
        }
        first.pr = NoProc;
@@ -5189,7 +5640,9 @@ GameEnds(result, resultDetails, whosays)
        second.isr = NULL;
     
        if (second.pr != NoProc) {
+            DoSleep( appData.delayBeforeQuit );
            SendToProgram("quit\n", &second);
+            DoSleep( appData.delayAfterQuit );
            DestroyChildProcess(second.pr, second.useSigterm);
        }
        second.pr = NoProc;
@@ -5383,6 +5836,10 @@ AutoPlayOneMove()
     if (currentMove >= forwardMostMove) {
       gameMode = EditGame;
       ModeHighlight();
+
+      /* [AS] Clear current move marker at the end of a game */
+      /* HistorySet(parseList, backwardMostMove, forwardMostMove, -1); */
+
       return FALSE;
     }
     
@@ -5396,6 +5853,9 @@ AutoPlayOneMove()
     } else {
        fromX = moveList[currentMove][0] - 'a';
        fromY = moveList[currentMove][1] - '1';
+
+        HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove); /* [AS] */
+
        AnimateMove(boards[currentMove], fromX, fromY, toX, toY);
 
        if (appData.highlightLastMove) {
@@ -5475,6 +5935,12 @@ LoadGameOneMove(readAhead)
       case WhiteQueenSideCastleWild:
       case BlackKingSideCastleWild:
       case BlackQueenSideCastleWild:
+      /* PUSH Fabien */
+      case WhiteHSideCastleFR:
+      case WhiteASideCastleFR:
+      case BlackHSideCastleFR:
+      case BlackASideCastleFR:
+      /* POP Fabien */
        if (appData.debugMode)
          fprintf(debugFP, "Parsed %s into %s\n", yy_text, currentMoveString);
        fromX = currentMoveString[0] - 'a';
@@ -6111,10 +6577,12 @@ LoadGame(f, gameNumber, title, useList)
          gameInfo.variant = StringToVariant(gameInfo.event);
        }
        if (!matchMode) {
+          if( appData.autoDisplayTags ) {
          tags = PGNTags(&gameInfo);
          TagsPopUp(tags, CmailMsg());
          free(tags);
        }
+       }
     } else {
        /* Make something up, but don't display it now */
        SetGameInfo();
@@ -6516,6 +6984,80 @@ SavePart(str)
 
 #define PGN_MAX_LINE 75
 
+#define PGN_SIDE_WHITE  0
+#define PGN_SIDE_BLACK  1
+
+static int FindFirstMoveOutOfBook( int side )
+{
+    int result = -1;
+
+    if( backwardMostMove == 0 && ! startedFromSetupPosition) {
+        int index = backwardMostMove;
+        int has_book_hit = 0;
+
+        if( (index % 2) != side ) {
+            index++;
+        }
+
+        while( index < forwardMostMove ) {
+            /* Check to see if engine is in book */
+            int depth = pvInfoList[index].depth;
+            int score = pvInfoList[index].score;
+            int in_book = 0;
+
+            if( depth == 0 ) {
+                in_book = 1; /* Yace */
+            }
+            if( score == 0 ) {
+                if( depth <= 1 || depth == 63 /* Zappa */ ) {
+                    in_book = 1;
+                }
+            }
+
+            has_book_hit += in_book;
+
+            if( ! in_book ) {
+                result = index;
+
+                break;
+            }
+
+            index += 2;
+        }
+    }
+
+    return result;
+}
+
+void GetOutOfBookInfo( char * buf )
+{
+    int oob[2];
+    int i;
+    int offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
+
+    oob[0] = FindFirstMoveOutOfBook( PGN_SIDE_WHITE );
+    oob[1] = FindFirstMoveOutOfBook( PGN_SIDE_BLACK );
+
+    *buf = '\0';
+
+    if( oob[0] >= 0 || oob[1] >= 0 ) {
+        for( i=0; i<2; i++ ) {
+            int idx = oob[i];
+
+            if( idx >= 0 ) {
+                if( i > 0 && oob[0] >= 0 ) {
+                    strcat( buf, "   " );
+                }
+
+                sprintf( buf+strlen(buf), "%d%s. ", (idx - offset)/2 + 1, idx & 1 ? ".." : "" );
+                sprintf( buf+strlen(buf), "%s%.2f",
+                    pvInfoList[idx].score >= 0 ? "+" : "",
+                    pvInfoList[idx].score / 100.0 );
+            }
+        }
+    }
+}
+
 /* Save game in PGN style and close the file */
 int
 SaveGamePGN(f)
@@ -6526,24 +7068,38 @@ SaveGamePGN(f)
     char *movetext;
     char numtext[32];
     int movelen, numlen, blank;
+    char move_buffer[100]; /* [AS] Buffer for move+PV info */
     
+    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
+
     tm = time((time_t *) NULL);
     
     PrintPGNTags(f, &gameInfo);
     
     if (backwardMostMove > 0 || startedFromSetupPosition) {
-        char *fen = PositionToFEN(backwardMostMove);
+        char *fen = PositionToFEN(backwardMostMove, 1);
         fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);
        fprintf(f, "\n{--------------\n");
        PrintPosition(f, backwardMostMove);
        fprintf(f, "--------------}\n");
         free(fen);
-    } else {
+    }
+    else {
+        /* [AS] Out of book annotation */
+        if( appData.saveOutOfBookInfo ) {
+            char buf[64];
+
+            GetOutOfBookInfo( buf );
+
+            if( buf[0] != '\0' ) {
+                fprintf( f, "[%s \"%s\"]\n", PGN_OUT_OF_BOOK, buf );
+            }
+        }
+
        fprintf(f, "\n");
     }
 
     i = backwardMostMove;
-    offset = backwardMostMove & (~1L); /* output move numbers start at 1 */
     linelen = 0;
     newblock = TRUE;
 
@@ -6585,6 +7141,17 @@ SaveGamePGN(f)
 
        /* Get move */
        movetext = SavePart(parseList[i]);
+
+        /* [AS] Add PV info if present */
+        if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {
+            sprintf( move_buffer, "%s {%s%.2f/%d}",
+                movetext,
+                pvInfoList[i].score >= 0 ? "+" : "",
+                pvInfoList[i].score / 100.0,
+                pvInfoList[i].depth );
+            movetext = move_buffer;
+        }
+
        movelen = strlen(movetext);
 
        /* Print move */
@@ -6746,7 +7313,7 @@ SavePosition(f, dummy, dummy2)
        PrintPosition(f, currentMove);
        fprintf(f, "--------------]\n");
     } else {
-       fen = PositionToFEN(currentMove);
+       fen = PositionToFEN(currentMove, 1);
        fprintf(f, "%s\n", fen);
        free(fen);
     }
@@ -7132,12 +7699,17 @@ ExitEvent(status)
     /* Kill off chess programs */
     if (first.pr != NoProc) {
        ExitAnalyzeMode();
+
+        DoSleep( appData.delayBeforeQuit );
        SendToProgram("quit\n", &first);
-       DestroyChildProcess(first.pr, first.useSigterm);
+        DoSleep( appData.delayAfterQuit );
+       DestroyChildProcess(first.pr, 10 /* [AS] first.useSigterm */ );
     }
     if (second.pr != NoProc) {
+        DoSleep( appData.delayBeforeQuit );
        SendToProgram("quit\n", &second);
-       DestroyChildProcess(second.pr, second.useSigterm);
+        DoSleep( appData.delayAfterQuit );
+       DestroyChildProcess(second.pr, 10 /* [AS] second.useSigterm */ );
     }
     if (first.isr != NULL) {
        RemoveInputSource(first.isr);
@@ -7752,6 +8324,7 @@ EditPositionDone()
     gameMode = EditGame;
     ModeHighlight();
     HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
+    ClearHighlights(); /* [AS] */
 }
 
 /* Pause for `ms' milliseconds */
@@ -8259,6 +8832,8 @@ void
 BackwardInner(target)
      int target;
 {
+    int full_redraw = TRUE; /* [AS] Was FALSE, had to change it! */
+
     if (appData.debugMode)
        fprintf(debugFP, "BackwardInner(%d), current %d, forward %d\n",
                target, currentMove, forwardMostMove);
@@ -8266,7 +8841,7 @@ BackwardInner(target)
     if (gameMode == EditPosition) return;
     if (currentMove <= backwardMostMove) {
        ClearHighlights();
-       DrawPosition(FALSE, boards[currentMove]);
+       DrawPosition(full_redraw, boards[currentMove]);
        return;
     }
     if (gameMode == PlayFromGameFile && !pausing)
@@ -8307,7 +8882,7 @@ BackwardInner(target)
     }
     DisplayBothClocks();
     DisplayMove(currentMove - 1);
-    DrawPosition(FALSE, boards[currentMove]);
+    DrawPosition(full_redraw, boards[currentMove]);
     HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);
     if (commentList[currentMove] != NULL) {
        DisplayComment(currentMove - 1, commentList[currentMove]);
@@ -8399,7 +8974,7 @@ RetractMoveEvent()
        DisplayBothClocks();
        DisplayMove(currentMove - 1);
        ClearHighlights();/*!! could figure this out*/
-       DrawPosition(FALSE, boards[currentMove]);
+       DrawPosition(TRUE, boards[currentMove]); /* [AS] Changed to full redraw! */
        SendToProgram("remove\n", &first);
        /*first.maybeThinking = TRUE;*/ /* GNU Chess does not ponder here */
        break;
@@ -8633,7 +9208,7 @@ SetGameInfo()
 
     switch (gameMode) {
       case MachinePlaysWhite:
-       gameInfo.event = StrSave("Computer chess game");
+       gameInfo.event = StrSave( appData.pgnEventHeader );
        gameInfo.site = StrSave(HostName());
        gameInfo.date = PGNDate();
        gameInfo.round = StrSave("-");
@@ -8643,7 +9218,7 @@ SetGameInfo()
        break;
 
       case MachinePlaysBlack:
-       gameInfo.event = StrSave("Computer chess game");
+       gameInfo.event = StrSave( appData.pgnEventHeader );
        gameInfo.site = StrSave(HostName());
        gameInfo.date = PGNDate();
        gameInfo.round = StrSave("-");
@@ -8653,7 +9228,7 @@ SetGameInfo()
        break;
 
       case TwoMachinesPlay:
-       gameInfo.event = StrSave("Computer chess game");
+       gameInfo.event = StrSave( appData.pgnEventHeader );
        gameInfo.site = StrSave(HostName());
        gameInfo.date = PGNDate();
        if (matchGame > 0) {
@@ -8758,6 +9333,8 @@ AppendComment(index, text)
     int oldlen, len;
     char *old;
 
+    GetInfoFromComment( index, text );
+
     CrushCRs(text);
     while (*text == '\n') text++;
     len = strlen(text);
@@ -8782,6 +9359,78 @@ AppendComment(index, text)
     }
 }
 
+static char * FindStr( char * text, char * sub_text )
+{
+    char * result = strstr( text, sub_text );
+
+    if( result != NULL ) {
+        result += strlen( sub_text );
+    }
+
+    return result;
+}
+
+/* [AS] Try to extract PV info from PGN comment */
+void GetInfoFromComment( int index, char * text )
+{
+    if( text != NULL && index > 0 ) {
+        int score = 0;
+        int depth = 0;
+        int time = -1;
+        char * s_eval = FindStr( text, "[%eval " );
+        char * s_emt = FindStr( text, "[%emt " );
+
+        if( s_eval != NULL || s_emt != NULL ) {
+            /* New style */
+            char delim;
+
+            if( s_eval != NULL ) {
+                if( sscanf( s_eval, "%d,%d%c", &score, &depth, &delim ) != 3 ) {
+                    return;
+                }
+
+                if( delim != ']' ) {
+                    return;
+                }
+            }
+
+            if( s_emt != NULL ) {
+            }
+        }
+        else {
+            /* We expect something like: [+|-]nnn.nn/dd */
+            char * sep = strchr( text, '/' );
+            int score_lo = 0;
+
+            if( sep == NULL || sep < (text+4) ) {
+                return;
+            }
+
+            if( sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {
+                return;
+            }
+
+            if( score_lo < 0 || score_lo >= 100 ) {
+                return;
+            }
+
+            score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;
+        }
+
+        if( depth <= 0 ) {
+            return;
+        }
+
+        if( time < 0 ) {
+            time = -1;
+        }
+
+        pvInfoList[index-1].depth = depth;
+        pvInfoList[index-1].score = score;
+        pvInfoList[index-1].time = time;
+    }
+}
+
 void
 SendToProgram(message, cps)
      char *message;
@@ -8834,6 +9483,13 @@ ReceiveFromProgram(isr, closure, message, count, error)
                    "Error reading from %s chess program (%s)",
                    cps->which, cps->program);
            RemoveInputSource(cps->isr);
+
+            /* [AS] Program is misbehaving badly... kill it */
+            if( count == -2 ) {
+                DestroyChildProcess( cps->pr, 9 );
+                cps->pr = NoProc;
+            }
+
            DisplayFatalError(buf, error, 1);
        }
        GameEnds((ChessMove) 0, NULL, GE_PLAYER);
@@ -8865,6 +9521,12 @@ SendTimeControl(cps, mps, tc, inc, sd, st)
     char buf[MSG_SIZ];
     int seconds = (tc / 1000) % 60;
 
+    if( timeControl_2 > 0 ) {
+        if( (gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b') ) {
+            tc = timeControl_2;
+        }
+    }
+
     if (st > 0) {
       /* Set exact time per move, normally using st command */
       if (cps->stKludge) {
@@ -9055,6 +9717,10 @@ ParseFeatures(args, cps)
       FeatureDone(cps, val);
       continue;
     }
+    /* Added by Tord: */
+    if (BoolFeature(&p, "fen960", &cps->useFEN960, cps)) continue;
+    if (BoolFeature(&p, "oocastle", &cps->useOOCastle, cps)) continue;
+    /* End of additions by Tord */
 
     /* unknown feature: complain and skip */
     q = p;
@@ -9157,13 +9823,29 @@ DisplayMove(moveNumber)
     if (moveNumber == forwardMostMove - 1 || 
        gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
 
-       strcpy(cpThinkOutput, thinkOutput);
-       if (strchr(cpThinkOutput, '\n'))
+       safeStrCpy(cpThinkOutput, thinkOutput, sizeof(cpThinkOutput));
+
+        if (strchr(cpThinkOutput, '\n')) {
          *strchr(cpThinkOutput, '\n') = NULLCHAR;
+        }
     } else {
        *cpThinkOutput = NULLCHAR;
     }
 
+    /* [AS] Hide thinking from human user */
+    if( appData.hideThinkingFromHuman && gameMode != TwoMachinesPlay ) {
+        *cpThinkOutput = NULLCHAR;
+        if( thinkOutput[0] != NULLCHAR ) {
+            int i;
+
+            for( i=0; i<=hiddenThinkOutputState; i++ ) {
+                cpThinkOutput[i] = '.';
+            }
+            cpThinkOutput[i] = NULLCHAR;
+            hiddenThinkOutputState = (hiddenThinkOutputState + 1) % 3;
+        }
+    }
+
     if (moveNumber == forwardMostMove - 1 &&
        gameInfo.resultDetails != NULL) {
        if (gameInfo.resultDetails[0] == NULLCHAR) {
@@ -9214,6 +9896,7 @@ void
 DisplayAnalysis()
 {
     char buf[MSG_SIZ];
+    char lst[MSG_SIZ / 2];
     double nps;
     static char *xtra[] = { "", " (--)", " (++)" };
     int h, m, s, cs;
@@ -9223,8 +9906,10 @@ DisplayAnalysis()
     }
   
     if (programStats.got_only_move) {
-       strcpy(buf, programStats.movelist);
+       safeStrCpy(buf, programStats.movelist, sizeof(buf));
     } else {
+        safeStrCpy( lst, programStats.movelist, sizeof(lst));
+
        nps = (((double)programStats.nodes) /
               (((double)programStats.time)/100.0));
 
@@ -9241,8 +9926,8 @@ DisplayAnalysis()
                    programStats.depth,
                    programStats.nr_moves-programStats.moves_left,
                    programStats.nr_moves, programStats.move_name,
-                   ((float)programStats.score)/100.0, programStats.movelist,
-                   only_one_move(programStats.movelist)?
+                   ((float)programStats.score)/100.0, lst,
+                   only_one_move(lst)?
                    xtra[programStats.got_fail] : "",
                    programStats.nodes, (int)nps, h, m, s, cs);
          } else {
@@ -9250,8 +9935,8 @@ DisplayAnalysis()
                    programStats.depth,
                    programStats.nr_moves-programStats.moves_left,
                    programStats.nr_moves, ((float)programStats.score)/100.0,
-                   programStats.movelist,
-                   only_one_move(programStats.movelist)?
+                   lst,
+                   only_one_move(lst)?
                    xtra[programStats.got_fail] : "",
                    programStats.nodes, (int)nps, h, m, s, cs);
          }
@@ -9259,8 +9944,8 @@ DisplayAnalysis()
            sprintf(buf, "depth=%d %+.2f %s%s\nNodes: %lu NPS: %d\nTime: %02d:%02d:%02d.%02d",
                    programStats.depth,
                    ((float)programStats.score)/100.0,
-                   programStats.movelist,
-                   only_one_move(programStats.movelist)?
+                   lst,
+                   only_one_move(lst)?
                    xtra[programStats.got_fail] : "",
                    programStats.nodes, (int)nps, h, m, s, cs);
        }
@@ -9275,6 +9960,7 @@ DisplayComment(moveNumber, text)
 {
     char title[MSG_SIZ];
 
+    if( appData.autoDisplayComment ) {
     if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
        strcpy(title, "Comment");
     } else {
@@ -9284,6 +9970,7 @@ DisplayComment(moveNumber, text)
     }
 
     CommentPopUp(title, text);
+    }
 }
 
 /* This routine sends a ^C interrupt to gnuchess, to awaken it if it
@@ -9390,11 +10077,11 @@ CheckTimeControl()
       switch ((forwardMostMove + 1) % (movesPerSession * 2)) {
       case 0:
        /* White made time control */
-       whiteTimeRemaining += timeControl;
+       whiteTimeRemaining += GetTimeControlForWhite();
        break;
       case 1:
        /* Black made time control */
-       blackTimeRemaining += timeControl;
+       blackTimeRemaining += GetTimeControlForBlack();
        break;
       default:
        break;
@@ -9498,7 +10185,8 @@ ResetClocks()
     if (appData.icsActive) {
        whiteTimeRemaining = blackTimeRemaining = 0;
     } else {
-       whiteTimeRemaining = blackTimeRemaining = timeControl;
+       whiteTimeRemaining = GetTimeControlForWhite();
+        blackTimeRemaining = GetTimeControlForBlack();
     }
     if (whiteFlag || blackFlag) {
        DisplayTitle("");
@@ -9817,8 +10505,9 @@ PGNDate()
 
 
 char *
-PositionToFEN(move)
+PositionToFEN(move, useFEN960)
      int move;
+     int useFEN960;
 {
     int i, j, fromX, fromY, toX, toY;
     int whiteToPlay;
@@ -9856,7 +10545,66 @@ PositionToFEN(move)
     *p++ = whiteToPlay ? 'w' : 'b';
     *p++ = ' ';
 
-    /* !!We don't keep track of castling availability, so fake it */
+    /* HACK: we don't keep track of castling availability, so fake it! */
+
+    /* PUSH Fabien & Tord */
+
+    /* Declare all potential FRC castling rights (conservative) */
+    /* outermost rook on each side of the king */
+
+    if( gameInfo.variant == VariantFischeRandom ) {
+       int fk, fr;
+
+       q = p;
+
+       /* White castling rights */
+
+       for (fk = 1; fk < 7; fk++) {
+
+          if (boards[move][0][fk] == WhiteKing) {
+
+             for (fr = 7; fr > fk; fr--) { /* H side */
+                if (boards[move][0][fr] == WhiteRook) {
+                   *p++ = useFEN960 ? 'A' + fr : 'K';
+                   break;
+                }
+             }
+
+             for (fr = 0; fr < fk; fr++) { /* A side */
+                if (boards[move][0][fr] == WhiteRook) {
+                   *p++ = useFEN960 ? 'A' + fr : 'Q';
+                   break;
+                }
+             }
+          }
+       }
+
+       /* Black castling rights */
+
+       for (fk = 1; fk < 7; fk++) {
+
+          if (boards[move][7][fk] == BlackKing) {
+
+             for (fr = 7; fr > fk; fr--) { /* H side */
+                if (boards[move][7][fr] == BlackRook) {
+                   *p++ = useFEN960 ? 'a' + fr : 'k';
+                   break;
+                }
+             }
+
+             for (fr = 0; fr < fk; fr++) { /* A side */
+                if (boards[move][7][fr] == BlackRook) {
+                   *p++ = useFEN960 ? 'a' + fr : 'q';
+                   break;
+                }
+             }
+          }
+       }
+
+       if (q == p) *p++ = '-'; /* No castling rights */
+       *p++ = ' ';
+    }
+    else {
     q = p;
     if (boards[move][0][4] == WhiteKing) {
        if (boards[move][0][7] == WhiteRook) *p++ = 'K';
@@ -9868,6 +10616,9 @@ PositionToFEN(move)
     }      
     if (q == p) *p++ = '-';
     *p++ = ' ';
+    }
+
+    /* POP Fabien & Tord */
 
     /* En passant target square */
     if (move > backwardMostMove) {
@@ -9889,7 +10640,7 @@ PositionToFEN(move)
        *p++ = '-';
     }
 
-    /* !!We don't keep track of halfmove clock for 50-move rule */
+    /* We don't keep track of halfmove clock for 50-move rule */
     strcpy(p, " 0 ");
     p += 3;
 
@@ -9945,6 +10696,7 @@ ParseFEN(board, blackPlaysFirst, fen)
       default:
        return FALSE;
     }
+
     /* !!We ignore the rest of the FEN notation */
     return TRUE;
 }