cleaned up HTML in FAQ.html -- still need to work on content
[xboard.git] / backend.c
index 0e36ec2..c34724e 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -351,6 +351,8 @@ PosFlags(index)
     flags &= ~F_ALL_CASTLE_OK;\r
   case VariantGiveaway:                // [HGM] moved this case label one down: seems Giveaway does have castling on ICC!\r
     flags |= F_IGNORE_CHECK;\r
+  case VariantLosers:\r
+    flags |= F_MANDATORY_CAPTURE; //[HGM] losers: sets flag so TestLegality rejects non-capts if capts exist\r
     break;\r
   case VariantAtomic:\r
     flags |= F_IGNORE_CHECK | F_ATOMIC_CAPTURE;\r
@@ -617,6 +619,7 @@ InitBackEnd1()
     ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options\r
 \r
     GetTimeMark(&programStartTime);\r
+    srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level\r
 \r
     ClearProgramStats();\r
     programStats.ok_to_send = 1;\r
@@ -746,6 +749,8 @@ InitBackEnd1()
     first.useFEN960 = FALSE; second.useFEN960 = FALSE;\r
     first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
     /* End of new features added by Tord. */\r
+    first.fenOverride  = appData.fenOverride1;\r
+    second.fenOverride = appData.fenOverride2;\r
 \r
     /* [HGM] time odds: set factor for each machine */\r
     first.timeOdds  = appData.firstTimeOdds;\r
@@ -4253,106 +4258,7 @@ ParseOneMove(move, moveNum, moveType, fromX, fromY, toX, toY, promoChar)
     }\r
 }\r
 \r
-#if 0\r
-/* [AS] FRC game initialization */\r
-static int FindEmptySquare( Board board, int n )\r
-{\r
-    int i = 0;\r
-\r
-    while( 1 ) {\r
-        while( board[0][i] != EmptySquare ) i++;\r
-        if( n == 0 )\r
-            break;\r
-        n--;\r
-        i++;\r
-    }\r
-\r
-    return i;\r
-}\r
-\r
-static void ShuffleFRC( Board board )\r
-{\r
-    int i;\r
-\r
-    srand( time(0) );\r
-    \r
-    for( i=0; i<8; i++ ) {\r
-        board[0][i] = EmptySquare;\r
-    }\r
-\r
-    board[0][(rand() % 4)*2  ] = WhiteBishop; /* On dark square */\r
-    board[0][(rand() % 4)*2+1] = WhiteBishop; /* On lite square */\r
-    board[0][FindEmptySquare(board, rand() % 6)] = WhiteQueen;\r
-    board[0][FindEmptySquare(board, rand() % 5)] = WhiteKnight;\r
-    board[0][FindEmptySquare(board, rand() % 4)] = WhiteKnight;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[1]  = initialRights[4]  =\r
-    castlingRights[0][1] = castlingRights[0][4] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
-    initialRights[2]  = initialRights[5]  =\r
-    castlingRights[0][2] = castlingRights[0][5] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[0]  = initialRights[3]  =\r
-    castlingRights[0][0] = castlingRights[0][3] = i;\r
-\r
-    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
-        board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
-    }\r
-}\r
-\r
-static unsigned char FRC_KnightTable[10] = {\r
-    0x00, 0x01, 0x02, 0x03, 0x11, 0x12, 0x13, 0x22, 0x23, 0x33\r
-};\r
-\r
-static void SetupFRC( Board board, int pos_index )\r
-{\r
-    int i;\r
-    unsigned char knights;\r
-\r
-    /* Bring the position index into a safe range (just in case...) */\r
-    if( pos_index < 0 ) pos_index = 0;\r
-\r
-    pos_index %= 960;\r
-\r
-    /* Clear the board */\r
-    for( i=0; i<8; i++ ) {\r
-        board[0][i] = EmptySquare;\r
-    }\r
-\r
-    /* Place bishops and queen */\r
-    board[0][ (pos_index % 4)*2 + 1 ] = WhiteBishop; /* On lite square */\r
-    pos_index /= 4;\r
-    \r
-    board[0][ (pos_index % 4)*2     ] = WhiteBishop; /* On dark square */\r
-    pos_index /= 4;\r
-\r
-    board[0][ FindEmptySquare(board, pos_index % 6) ] = WhiteQueen;\r
-    pos_index /= 6;\r
-\r
-    /* Place knigths */\r
-    knights = FRC_KnightTable[ pos_index ];\r
-\r
-    board[0][ FindEmptySquare(board, knights / 16) ] = WhiteKnight;\r
-    board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;\r
-\r
-    /* Place rooks and king */\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[1]  = initialRights[4]  =\r
-    castlingRights[0][1] = castlingRights[0][4] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteKing;\r
-    initialRights[2]  = initialRights[5]  =\r
-    castlingRights[0][2] = castlingRights[0][5] = i;\r
-    board[0][ i=FindEmptySquare(board, 0) ] = WhiteRook;\r
-    initialRights[0]  = initialRights[3]  =\r
-    castlingRights[0][0] = castlingRights[0][3] = i;\r
-\r
-    /* Mirror piece placement for black */\r
-    for( i=BOARD_LEFT; i<BOARD_RGHT; i++ ) {\r
-        board[BOARD_HEIGHT-1][i] = board[0][i] + BlackPawn - WhitePawn;\r
-    }\r
-}\r
-#else\r
-// [HGM] shuffle: a more general way to suffle opening setups, applicable to arbitrry variants.\r
+// [HGM] shuffle: a general way to suffle opening setups, applicable to arbitrary variants.\r
 // All positions will have equal probability, but the current method will not provide a unique\r
 // numbering scheme for arrays that contain 3 or more pieces of the same kind.\r
 #define DARK 1\r
@@ -4361,7 +4267,7 @@ static void SetupFRC( Board board, int pos_index )
 \r
 int squaresLeft[4];\r
 int piecesLeft[(int)BlackPawn];\r
-u64 seed, nrOfShuffles;\r
+int seed, nrOfShuffles;\r
 \r
 void GetPositionNumber()\r
 {      // sets global variable seed\r
@@ -4369,7 +4275,6 @@ void GetPositionNumber()
 \r
        seed = appData.defaultFrcPosition;\r
        if(seed < 0) { // randomize based on time for negative FRC position numbers\r
-               srandom(time(0)); \r
                for(i=0; i<50; i++) seed += random();\r
                seed = random() ^ random() >> 8 ^ random() << 8;\r
                if(seed<0) seed = -seed;\r
@@ -4508,8 +4413,6 @@ void SetUpShuffle(Board board, int number)
        if(number >= 0) appData.defaultFrcPosition %= nrOfShuffles; // normalize\r
 }\r
 \r
-#endif\r
-\r
 int SetCharTable( char *table, const char * map )\r
 /* [HGM] moved here from winboard.c because of its general usefulness */\r
 /*       Basically a safe strcpy that uses the last character as King */\r
@@ -4627,11 +4530,6 @@ InitPosition(redraw)
       break;\r
     case VariantTwoKings:\r
       pieces = twoKingsArray;\r
-      nrCastlingRights = 8;                 /* add rights for second King */\r
-      castlingRights[0][6] = initialRights[2] = 5;\r
-      castlingRights[0][7] = initialRights[5] = 5;\r
-      castlingRank[6] = 0;\r
-      castlingRank[7] = BOARD_HEIGHT-1;\r
       break;\r
     case VariantCapaRandom:\r
       shuffleOpenings = TRUE;\r
@@ -4797,8 +4695,8 @@ InitPosition(redraw)
 \r
      if(gameInfo.variant == VariantSuper) Prelude(initialPosition);\r
      if(gameInfo.variant == VariantGreat) { // promotion commoners\r
-       initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-1] = WhiteMan;\r
-       initialPosition[PieceToNumber(WhiteMan)][BOARD_RGHT-2] = 9;\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-1] = WhiteMan;\r
+       initialPosition[PieceToNumber(WhiteMan)][BOARD_WIDTH-2] = 9;\r
        initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][0] = BlackMan;\r
        initialPosition[BOARD_HEIGHT-1-PieceToNumber(WhiteMan)][1] = 9;\r
      }\r
@@ -4857,7 +4755,7 @@ SendBoard(cps, moveNum)
     char message[MSG_SIZ];\r
     \r
     if (cps->useSetboard) {\r
-      char* fen = PositionToFEN(moveNum, cps->useFEN960);\r
+      char* fen = PositionToFEN(moveNum, cps->fenOverride);\r
       sprintf(message, "setboard %s\n", fen);\r
       SendToProgram(message, cps);\r
       free(fen);\r
@@ -5815,8 +5713,13 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                 if(appData.checkMates) {\r
                    SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                    ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                   GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate",\r
-                       GE_XBOARD );\r
+                   if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantSuicide\r
+                                                        || gameInfo.variant == VariantGiveaway) // [HGM] losers:\r
+                       GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, // stalemated side wins!\r
+                               "Xboard adjudication: Stalemate", GE_XBOARD );\r
+                   else\r
+                       GameEnds( GameIsDrawn, "Xboard adjudication: Stalemate", GE_XBOARD );\r
+                   return;\r
                }\r
                break;\r
              case MT_CHECKMATE:\r
@@ -5824,9 +5727,10 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                 if(appData.checkMates) {\r
                    SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
                    ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                   GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
-                   "Xboard adjudication: Checkmate", \r
-                   GE_XBOARD );\r
+                   GameEnds( WhiteOnMove(forwardMostMove) != (gameInfo.variant == VariantLosers) // [HGM] losers:\r
+                            ? BlackWins : WhiteWins,            // reverse the result ( A!=1 is !A for a boolean)\r
+                            "Xboard adjudication: Checkmate", GE_XBOARD );\r
+                   return;\r
                }\r
                break;\r
            }\r
@@ -5834,7 +5738,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
            if( appData.testLegality )\r
            {   /* [HGM] Some more adjudications for obstinate engines */\r
                int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0,\r
-                    NrWQ=0, NrBQ=0, NrW=0, bishopsColor = 0,\r
+                    NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0,\r
                     NrPieces=0, NrPawns=0, PawnAdvance=0, i, j;\r
                static int moveCount = 6;\r
 \r
@@ -5845,6 +5749,9 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
 \r
                    switch((int) p)\r
                    {   /* count B,N,R and other of each side */\r
+                        case WhiteKing:\r
+                        case BlackKing:\r
+                            NrK++; break; // [HGM] atomic: count Kings\r
                         case WhiteKnight:\r
                              NrWN++; break;\r
                         case WhiteBishop:\r
@@ -5881,6 +5788,45 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                    }\r
                 }\r
 \r
+               if(gameInfo.variant == VariantAtomic && NrK < 2) {\r
+                   // [HGM] atomic: stm must have lost his King on previous move, as destroying own K is illegal\r
+                    epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as if stm is checkmated\r
+                    if(appData.checkMates) {\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( WhiteOnMove(forwardMostMove) ? BlackWins : WhiteWins, \r
+                                                       "Xboard adjudication: King destroyed", GE_XBOARD );\r
+                         return;\r
+                    }\r
+               }\r
+\r
+               /* Bare King in Shatranj (loses) or Losers (wins) */\r
+                if( NrW == 1 || NrPieces - NrW == 1) {\r
+                  if( gameInfo.variant == VariantLosers) { // [HGM] losers: bare King wins (stm must have it first)\r
+                    epStatus[forwardMostMove] = EP_STALEMATE; // kludge to make position claimable as win\r
+                    if(appData.checkMates) {\r
+                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
+                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                         GameEnds( WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins, \r
+                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
+                         return;\r
+                    }\r
+                 } else\r
+                  if( gameInfo.variant == VariantShatranj && --bare < 0)\r
+                  {    /* bare King */\r
+                       epStatus[forwardMostMove] = EP_CHECKMATE; // make claimable as win for stm\r
+                       if(appData.checkMates) {\r
+                           /* but only adjudicate if adjudication enabled */\r
+                           SendMoveToProgram(forwardMostMove-1, cps->other); // make sure opponent gets move\r
+                           ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+                           GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, \r
+                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
+                           return;\r
+                       }\r
+                 }\r
+                } else bare = 1;\r
+\r
+\r
                 if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && \r
                                     gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible\r
                        (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 ||\r
@@ -5900,20 +5846,6 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h
                      }\r
                 }\r
 \r
-               /* Shatranj baring rule */\r
-                if( gameInfo.variant == VariantShatranj && (NrW == 1 || NrPieces - NrW == 1) )\r
-                {    /* bare King */\r
-\r
-                     if(--bare < 0 && appData.checkMates) {\r
-                         /* but only adjudicate them if adjudication enabled */\r
-                        SendMoveToProgram(forwardMostMove-1, cps->other); /* make sure opponent gets to see move */\r
-                         ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
-                         GameEnds( NrW > 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, \r
-                                                       "Xboard adjudication: Bare king", GE_XBOARD );\r
-                         return;\r
-                     }\r
-                } else bare = 1;\r
-\r
                 /* Then some trivial draws (only adjudicate, cannot be claimed) */\r
                 if(NrPieces == 4 && \r
                    (   NrWR == 1 && NrBR == 1 /* KRKR */\r
@@ -7799,20 +7731,35 @@ GameEnds(result, resultDetails, whosays)
         if(gameMode == TwoMachinesPlay && appData.testClaims) {\r
            if(appData.testLegality && whosays >= GE_ENGINE1 ) {\r
                 char claimer;\r
+               ChessMove trueResult = (ChessMove) -1;\r
 \r
                 claimer = whosays == GE_ENGINE1 ?      /* color of claimer */\r
                                             first.twoMachinesColor[0] :\r
                                             second.twoMachinesColor[0] ;\r
-                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) &&\r
-                    (result == WhiteWins && claimer == 'w' ||\r
-                     result == BlackWins && claimer == 'b'   ) ) {\r
-               if (appData.debugMode) {\r
-                    fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
-                       result, epStatus[forwardMostMove], forwardMostMove);\r
+\r
+               // [HGM] losers: because the logic is becoming a bit hairy, determine true result first\r
+               if(epStatus[forwardMostMove] == EP_CHECKMATE) {\r
+                   /* [HGM] verify: engine mate claims accepted if they were flagged */\r
+                   trueResult = WhiteOnMove(forwardMostMove) != (gameInfo.variant == VariantLosers)\r
+                       ? BlackWins : WhiteWins; // [HGM] losers: reverse the result in VariantLosers!\r
+               } else\r
+               if(epStatus[forwardMostMove] == EP_STALEMATE) {\r
+                   trueResult = GameIsDrawn; // default; in variants where stalemate loses, Status is CHECKMATE\r
+                   if(gameInfo.variant == VariantGiveaway || gameInfo.variant == VariantSuicide || \r
+                      gameInfo.variant == VariantLosers)  // [HGM] losers: in giveaway variants stalemate wins\r
+                       trueResult = WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins;\r
                }\r
-                      /* [HGM] verify: engine mate claims accepted if they were flagged */\r
-                     if(epStatus[forwardMostMove] != EP_CHECKMATE &&\r
-                        result != (WhiteOnMove(forwardMostMove) ? WhiteWins : BlackWins)) {\r
+\r
+               // now verify win claims, but not in drop games, as we don't understand those yet\r
+                if( (gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper\r
+                                                || gameInfo.variant == VariantGreat) &&\r
+                    (result == WhiteWins && claimer == 'w' ||\r
+                     result == BlackWins && claimer == 'b'   ) ) { // case to verify: engine claims own win\r
+                     if (appData.debugMode) {\r
+                       fprintf(debugFP, "result=%d sp=%d move=%d\n",\r
+                               result, epStatus[forwardMostMove], forwardMostMove);\r
+                     }\r
+                     if(result != trueResult) {\r
                              sprintf(buf, "False win claim: '%s'", resultDetails);\r
                              result = claimer == 'w' ? BlackWins : WhiteWins;\r
                              resultDetails = buf;\r
@@ -7832,7 +7779,9 @@ GameEnds(result, resultDetails, whosays)
            }\r
            /* [HGM] bare: don't allow bare King to win */\r
            if((gameInfo.holdingsWidth == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat)\r
-                        && result != GameIsDrawn)\r
+              && gameInfo.variant != VariantLosers && gameInfo.variant != VariantGiveaway \r
+              && gameInfo.variant != VariantSuicide // [HGM] losers: except in losers, of course...\r
+              && result != GameIsDrawn)\r
            {   int i, j, k=0, color = (result==WhiteWins ? (int)WhitePawn : (int)BlackPawn);\r
                for(j=BOARD_LEFT; j<BOARD_RGHT; j++) for(i=0; i<BOARD_HEIGHT; i++) {\r
                        int p = (int)boards[forwardMostMove][i][j] - color;\r
@@ -9537,7 +9486,7 @@ SaveGamePGN(f)
     PrintPGNTags(f, &gameInfo);\r
     \r
     if (backwardMostMove > 0 || startedFromSetupPosition) {\r
-        char *fen = PositionToFEN(backwardMostMove, 1);\r
+        char *fen = PositionToFEN(backwardMostMove, NULL);\r
         fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen);\r
        fprintf(f, "\n{--------------\n");\r
        PrintPosition(f, backwardMostMove);\r
@@ -9810,7 +9759,7 @@ SavePosition(f, dummy, dummy2)
        PrintPosition(f, currentMove);\r
        fprintf(f, "--------------]\n");\r
     } else {\r
-       fen = PositionToFEN(currentMove, 1);\r
+       fen = PositionToFEN(currentMove, NULL);\r
        fprintf(f, "%s\n", fen);\r
        free(fen);\r
     }\r
@@ -12401,8 +12350,9 @@ ParseOption(Option *opt, ChessProgramState *cps)
        } else return FALSE;\r
        *p = 0; // terminate option name\r
        // now look if the command-line options define a setting for this engine option.\r
-       p = strstr(cps->optionSettings, opt->name);\r
-       if(p == cps->optionSettings || p[-1] == ',') {\r
+       if(cps->optionSettings && cps->optionSettings[0])\r
+           p = strstr(cps->optionSettings, opt->name); else p = NULL;\r
+       if(p && (p == cps->optionSettings || p[-1] == ',')) {\r
                sprintf(buf, "option %s", p);\r
                if(p = strstr(buf, ",")) *p = 0;\r
                strcat(buf, "\n");\r
@@ -13345,9 +13295,9 @@ PGNDate()
 \r
 \r
 char *\r
-PositionToFEN(move, useFEN960)\r
+PositionToFEN(move, overrideCastling)\r
      int move;\r
-     int useFEN960;\r
+     char *overrideCastling;\r
 {\r
     int i, j, fromX, fromY, toX, toY;\r
     int whiteToPlay;\r
@@ -13422,6 +13372,9 @@ PositionToFEN(move, useFEN960)
     *p++ = whiteToPlay ? 'w' : 'b';\r
     *p++ = ' ';\r
 \r
+  if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines\r
+    while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' ';\r
+  } else {\r
   if(nrCastlingRights) {\r
      q = p;\r
      if(gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) {\r
@@ -13479,6 +13432,7 @@ PositionToFEN(move, useFEN960)
     }\r
     *p++ = ' ';\r
   }\r
+  }\r
 \r
     /* [HGM] find reversible plies */\r
     {   int i = 0, j=move;\r