void ParseFeatures P((char* args, ChessProgramState *cps));\r
void InitBackEnd3 P((void));\r
void FeatureDone P((ChessProgramState* cps, int val));\r
-void InitChessProgram P((ChessProgramState *cps));\r
+void InitChessProgram P((ChessProgramState *cps, int setup));\r
\r
-void GetInfoFromComment( int, char * );\r
+char *GetInfoFromComment( int, char * ); // [HGM] PV time: returns stripped comment\r
\r
extern int tinyLayout, smallLayout;\r
static ChessProgramStats programStats;\r
+static int exiting = 0; /* [HGM] moved to top */\r
+static int setboardSpoiledMachineBlack = 0, errorExitFlag = 0;\r
+extern int startedFromPositionFile;\r
+int startedFromPositionFile = FALSE; Board filePosition; /* [HGM] loadPos */\r
+char endingGame = 0; /* [HGM] crash: flag to prevent recursion of GameEnds() */\r
\r
/* States for ics_getting_history */\r
#define H_FALSE 0\r
}\r
\r
/* Fake up flags for now, as we aren't keeping track of castling\r
- availability yet */\r
+ availability yet. [HGM] Change of logic: the flag now only\r
+ indicates the type of castlings allowed by the rule of the game.\r
+ The actual rights themselves are maintained in the array\r
+ castlingRights, as part of the game history, and are not probed\r
+ by this function.\r
+ */\r
int\r
PosFlags(index)\r
{\r
case VariantKriegspiel:\r
flags |= F_KRIEGSPIEL_CAPTURE;\r
break;\r
+/* case VariantCapaRandom: */\r
+ case VariantFischeRandom:\r
+ flags |= F_FRC_TYPE_CASTLING; /* [HGM] enable this through flag */\r
case VariantNoCastle:\r
flags &= ~F_ALL_CASTLE_OK;\r
break;\r
int movesPerSession;\r
long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement;\r
long timeControl_2; /* [AS] Allow separate time controls */\r
+char *fullTimeControlString = NULL; /* [HGM] secondary TC: merge of MPS, TC and inc */\r
long timeRemaining[2][MAX_MOVES];\r
int matchGame = 0;\r
TimeMark programStartTime;\r
char epStatus[MAX_MOVES];\r
char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1\r
char castlingRank[BOARD_SIZE]; // and corresponding ranks\r
-char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE];\r
+char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];\r
int nrCastlingRights; // For TwoKings, or to implement castling-unknown status\r
int initialRulePlies, FENrulePlies;\r
char FENepStatus;\r
+FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)\r
+int loadFlag = 0; \r
\r
ChessSquare FIDEArray[2][BOARD_SIZE] = {\r
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,\r
BlackKing, BlackKing, BlackKnight, BlackRook }\r
};\r
\r
-#ifdef FAIRY\r
ChessSquare KnightmateArray[2][BOARD_SIZE] = {\r
{ WhiteRook, WhiteMan, WhiteBishop, WhiteQueen,\r
WhiteUnicorn, WhiteBishop, WhiteMan, WhiteRook },\r
};\r
\r
ChessSquare ShatranjArray[2][BOARD_SIZE] = { /* [HGM] (movGen knows about Shatranj Q and P) */\r
- { WhiteRook, WhiteKnight, WhiteAlfil, WhiteFerz,\r
- WhiteKing, WhiteAlfil, WhiteKnight, WhiteRook },\r
- { BlackRook, BlackKnight, BlackAlfil, BlackFerz,\r
- BlackKing, BlackAlfil, BlackKnight, BlackRook }\r
+ { WhiteRook, WhiteKnight, WhiteAlfil, WhiteKing,\r
+ WhiteFerz, WhiteAlfil, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackAlfil, BlackKing,\r
+ BlackFerz, BlackAlfil, BlackKnight, BlackRook }\r
};\r
\r
\r
};\r
\r
ChessSquare CapablancaArray[2][BOARD_SIZE] = {\r
- { WhiteRook, WhiteKnight, WhiteCardinal, WhiteBishop, WhiteQueen, \r
+ { WhiteRook, WhiteKnight, WhiteAngel, WhiteBishop, WhiteQueen, \r
WhiteKing, WhiteBishop, WhiteMarshall, WhiteKnight, WhiteRook },\r
- { BlackRook, BlackKnight, BlackCardinal, BlackBishop, BlackQueen, \r
+ { BlackRook, BlackKnight, BlackAngel, BlackBishop, BlackQueen, \r
BlackKing, BlackBishop, BlackMarshall, BlackKnight, BlackRook }\r
};\r
\r
#ifdef GOTHIC\r
ChessSquare GothicArray[2][BOARD_SIZE] = {\r
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen, WhiteMarshall, \r
- WhiteKing, WhiteCardinal, WhiteBishop, WhiteKnight, WhiteRook },\r
+ WhiteKing, WhiteAngel, WhiteBishop, WhiteKnight, WhiteRook },\r
{ BlackRook, BlackKnight, BlackBishop, BlackQueen, BlackMarshall, \r
- BlackKing, BlackCardinal, BlackBishop, BlackKnight, BlackRook }\r
+ BlackKing, BlackAngel, BlackBishop, BlackKnight, BlackRook }\r
};\r
#else // !GOTHIC\r
#define GothicArray CapablancaArray\r
#endif // !GOTHIC\r
\r
+#ifdef FALCON\r
+ChessSquare FalconArray[2][BOARD_SIZE] = {\r
+ { WhiteRook, WhiteKnight, WhiteBishop, WhiteLance, WhiteQueen, \r
+ WhiteKing, WhiteLance, WhiteBishop, WhiteKnight, WhiteRook },\r
+ { BlackRook, BlackKnight, BlackBishop, BlackLance, BlackQueen, \r
+ BlackKing, BlackLance, BlackBishop, BlackKnight, BlackRook }\r
+};\r
+#else // !FALCON\r
+#define FalconArray CapablancaArray\r
+#endif // !FALCON\r
+\r
#else // !(BOARD_SIZE>=10)\r
#define XiangqiPosition FIDEArray\r
#define CapablancaArray FIDEArray\r
#else // !(BOARD_SIZE>=12)\r
#define CourierArray CapablancaArray\r
#endif // !(BOARD_SIZE>=12)\r
-#endif // FAIRY\r
\r
\r
Board initialPosition;\r
programStats.nr_moves = 0;\r
programStats.moves_left = 0;\r
programStats.nodes = 0;\r
- programStats.time = 100;\r
+ programStats.time = -1; // [HGM] PGNtime: make invalid to recognize engine output\r
programStats.score = 0;\r
programStats.got_only_move = 0;\r
programStats.got_fail = 0;\r
first.useOOCastle = TRUE; second.useOOCastle = TRUE;\r
/* End of new features added by Tord. */\r
\r
+ /* [HGM] time odds: set factor for each machine */\r
+ first.timeOdds = appData.firstTimeOdds;\r
+ second.timeOdds = appData.secondTimeOdds;\r
+ { int norm = 1;\r
+ if(appData.timeOddsMode) {\r
+ norm = first.timeOdds;\r
+ if(norm > second.timeOdds) norm = second.timeOdds;\r
+ }\r
+ first.timeOdds /= norm;\r
+ second.timeOdds /= norm;\r
+ }\r
+\r
+ /* [HGM] secondary TC: how to handle sessions that do not fit in 'level'*/\r
+ first.accumulateTC = appData.firstAccumulateTC;\r
+ second.accumulateTC = appData.secondAccumulateTC;\r
+ first.maxNrOfSessions = second.maxNrOfSessions = 1;\r
+\r
+ /* [HGM] debug */\r
+ first.debug = second.debug = FALSE;\r
+\r
first.scoreIsAbsolute = appData.firstScoreIsAbsolute; /* [AS] */\r
second.scoreIsAbsolute = appData.secondScoreIsAbsolute; /* [AS] */\r
first.isUCI = appData.firstIsUCI; /* [AS] */\r
case VariantCapablanca: /* [HGM] should work */\r
case VariantCourier: /* [HGM] initial forced moves not implemented */\r
case VariantShogi: /* [HGM] drops not tested for legality */\r
- case VariantShowgi: /* [HGM] not a valid variant */\r
case VariantKnightmate: /* [HGM] should work */\r
+ case VariantCylinder: /* [HGM] untested */\r
+ case VariantFalcon: /* [HGM] untested */\r
case VariantCrazyhouse: /* holdings not shown, ([HGM] fixed that!)\r
offboard interposition not understood */\r
case VariantNormal: /* definitely works! */\r
return result;\r
}\r
\r
-int GetTimeControlForWhite()\r
-{\r
- int result = timeControl;\r
+int NextSessionFromString( char ** str, int *moves, long * tc, long *inc)\r
+{ /* [HGM] routine added to read '+moves/time' for secondary time control */\r
+ int result = -1; long temp, temp2;\r
+\r
+ if(**str != '+') return -1; // old params remain in force!\r
+ (*str)++;\r
+ if( NextTimeControlFromString( str, &temp ) ) return -1;\r
\r
+ if(**str != '/') {\r
+ /* time only: incremental or sudden-death time control */\r
+ if(**str == '+') { /* increment follows; read it */\r
+ (*str)++;\r
+ if(result = NextIntegerFromString( str, &temp2)) return -1;\r
+ *inc = temp2 * 1000;\r
+ } else *inc = 0;\r
+ *moves = 0; *tc = temp * 1000; \r
+ return 0;\r
+ } else if(temp % 60 != 0) return -1; /* moves was given as min:sec */\r
+\r
+ (*str)++; /* classical time control */\r
+ result = NextTimeControlFromString( str, &temp2);\r
+ if(result == 0) {\r
+ *moves = temp/60;\r
+ *tc = temp2 * 1000;\r
+ *inc = 0;\r
+ }\r
return result;\r
}\r
\r
-int GetTimeControlForBlack()\r
-{\r
- int result = timeControl;\r
+int GetTimeQuota(int movenr)\r
+{ /* [HGM] get time to add from the multi-session time-control string */\r
+ int moves=1; /* kludge to force reading of first session */\r
+ long time, increment;\r
+ char *s = fullTimeControlString;\r
\r
- if( timeControl_2 > 0 ) {\r
- result = timeControl_2;\r
- }\r
+ if(appData.debugMode) fprintf(debugFP, "TC string = '%s'\n", fullTimeControlString);\r
+ do {\r
+ if(moves) NextSessionFromString(&s, &moves, &time, &increment);\r
+ if(appData.debugMode) fprintf(debugFP, "mps=%d tc=%d inc=%d\n", moves, (int) time, (int) increment);\r
+ if(movenr == -1) return time; /* last move before new session */\r
+ if(!moves) return increment; /* current session is incremental */\r
+ if(movenr >= 0) movenr -= moves; /* we already finished this session */\r
+ } while(movenr >= -1); /* try again for next session */\r
\r
- return result;\r
+ return 0; // no new time quota on this move\r
}\r
\r
int\r
#else\r
long tc1;\r
long tc2;\r
+ char buf[MSG_SIZ];\r
+\r
+ if(ti >= 0 && !strchr(tc, '+') && !strchr(tc, '/') ) mps = 0;\r
+ if(ti > 0) {\r
+ if(mps)\r
+ sprintf(buf, "+%d/%s+%d", mps, tc, ti);\r
+ else sprintf(buf, "+%s+%d", tc, ti);\r
+ } else {\r
+ if(mps)\r
+ sprintf(buf, "+%d/%s", mps, tc);\r
+ else sprintf(buf, "+%s", tc);\r
+ }\r
+ fullTimeControlString = StrSave(buf);\r
\r
if( NextTimeControlFromString( &tc, &tc1 ) != 0 ) {\r
return FALSE;\r
char buf[MSG_SIZ];\r
int err;\r
\r
- InitChessProgram(&first);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "From InitBackend3\n");\r
+ }\r
+ InitChessProgram(&first, startedFromSetupPosition);\r
\r
if (appData.icsActive) {\r
err = establish();\r
(void) LoadPositionFromFile(appData.loadPositionFile,\r
appData.loadPositionIndex,\r
appData.loadPositionFile);\r
+ /* [HGM] try to make self-starting even after FEN load */\r
+ /* to allow automatic setup of fairy variants with wtm */\r
+ if(initialMode == BeginningOfGame && !blackPlaysFirst) {\r
+ gameMode = BeginningOfGame;\r
+ setboardSpoiledMachineBlack = 1;\r
+ }\r
+ /* [HGM] loadPos: make that every new game uses the setup */\r
+ /* from file as long as we do not switch variant */\r
+ if(!blackPlaysFirst) { int i;\r
+ startedFromPositionFile = TRUE;\r
+ CopyBoard(filePosition, boards[0]);\r
+ for(i=0; i<BOARD_SIZE; i++) fileRights[i] = castlingRights[0][i];\r
+ }\r
}\r
if (initialMode == AnalyzeMode) {\r
if (appData.noChessProgram) {\r
gotEof = 0;\r
outCount = OutputMaybeTelnet(icsPR, message, count, &outError);\r
if (outCount < count) {\r
- DisplayFatalError("Error writing to ICS", outError, 1);\r
+ DisplayFatalError("Error writing to ICS", outError, 1);\r
}\r
} else if (count < 0) {\r
RemoveInputSource(isr);\r
v = VariantFairy;\r
break;\r
case 44:\r
- v = VariantShowgi;\r
+ v = VariantCylinder;\r
+ break;\r
+ case 45:\r
+ v = VariantFalcon;\r
break;\r
\r
case -1:\r
\r
}\r
\r
+char startBoard[MSG_SIZ]; /* [HGM] variantswitch */\r
+\r
void\r
VariantSwitch(Board board, VariantClass newVariant)\r
{\r
- int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;\r
+ int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j, oldCurrentMove = currentMove;\r
+ Board tempBoard; int saveCastling[BOARD_SIZE], saveEP;\r
+\r
+ startedFromPositionFile = FALSE;\r
if(gameInfo.variant == newVariant) return;\r
\r
/* [HGM] This routine is called each time an assignment is made to\r
* gameInfo.variant during a game, to make sure the board sizes\r
* are set to match the new variant. If that means adding or deleting\r
* holdings, we shift the playing board accordingly\r
+ * This kludge is needed because in ICS observe mode, we get boards\r
+ * of an ongoing game without knowing the variant, and learn about the\r
+ * latter only later. This can be because of the move list we requested,\r
+ * in which case the game history is refilled from the beginning anyway,\r
+ * but also when receiving holdings of a crazyhouse game. In the latter\r
+ * case we want to add those holdings to the already received position.\r
*/\r
\r
+\r
if (appData.debugMode) {\r
fprintf(debugFP, "Switch board from %s to %s\n",\r
VariantName(gameInfo.variant), VariantName(newVariant));\r
gameInfo.holdingsSize = 5; /* [HGM] prepare holdings */\r
switch(newVariant) {\r
case VariantShogi:\r
- case VariantShowgi:\r
newWidth = 9; newHeight = 9;\r
gameInfo.holdingsSize = 7;\r
case VariantBughouse:\r
gameInfo.variant = newVariant;\r
InitDrawingSizes(-2, 0);\r
\r
- if(board != boards[0]) InitPosition(FALSE);\r
-\r
- } else gameInfo.variant = newVariant;\r
+ /* [HGM] The following should definitely be solved in a better way */\r
+#if 0\r
+ CopyBoard(board, tempBoard); /* save position in case it is board[0] */\r
+ for(i=0; i<BOARD_SIZE; i++) saveCastling[i] = castlingRights[0][i];\r
+ saveEP = epStatus[0];\r
+#endif\r
+ InitPosition(FALSE); /* this sets up board[0], but also other stuff */\r
+ forwardMostMove = backwardMostMove =\r
+ currentMove = oldCurrentMove; /* InitPos reset this, but we need still to redraw it */\r
+#if 0\r
+ epStatus[0] = saveEP;\r
+ for(i=0; i<BOARD_SIZE; i++) castlingRights[0][i] = saveCastling[i];\r
+ CopyBoard(tempBoard, board); /* restore position received from ICS */\r
+#endif\r
+ } else { gameInfo.variant = newVariant; InitPosition(FALSE); }\r
}\r
\r
static int loggedOn = FALSE;\r
}\r
#endif\r
\r
+ if (appData.debugMode) { int f = forwardMostMove;\r
+ fprintf(debugFP, "ics input %d, castling = %d %d %d %d %d %d\n", f,\r
+ castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
+ }\r
if (count > 0) {\r
/* If last read ended with a partial line that we couldn't parse,\r
prepend it to the new read and try again. */\r
if (moveNum == 0) {\r
startedFromSetupPosition =\r
!CompareBoards(board, initialPosition);\r
- }\r
+ if(startedFromSetupPosition)\r
+ initialRulePlies = irrev_count; /* [HGM] 50-move counter offset */\r
+ }\r
+ /* [HGM] variantswitch: remember the last initial position parsed, */\r
+ /* because it might have been parsed in the wrong variant, so that */\r
+ /* we can re-parse it once we know the proper variant (which might */\r
+ /* have different piece assignments for the same letters). */\r
+ if(moveNum == 0) strcpy(startBoard, string);\r
+\r
+ /* [HGM] Set castling rights. Take the outermost Rooks,\r
+ to make it also work for FRC opening positions. Note that board12\r
+ is really defective for later FRC positions, as it has no way to\r
+ indicate which Rook can castle if they are on the same side of King.\r
+ For the initial position we grant rights to the outermost Rooks,\r
+ and remember thos rights, and we then copy them on positions\r
+ later in an FRC game. This means WB might not recognize castlings with\r
+ Rooks that have moved back to their original position as illegal,\r
+ but in ICS mode that is not its job anyway.\r
+ */\r
+ if(moveNum == 0 || gameInfo.variant != VariantFischeRandom)\r
+ { int i, j;\r
+\r
+ for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)\r
+ if(board[0][i] == WhiteRook) j = i;\r
+ initialRights[0] = castlingRights[moveNum][0] = (castle_ws == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+ for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)\r
+ if(board[0][i] == WhiteRook) j = i;\r
+ initialRights[1] = castlingRights[moveNum][1] = (castle_wl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+ for(i=BOARD_LEFT, j= -1; i<BOARD_RGHT; i++)\r
+ if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;\r
+ initialRights[3] = castlingRights[moveNum][3] = (castle_bs == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+ for(i=BOARD_RGHT-1, j= -1; i>=BOARD_LEFT; i--)\r
+ if(board[BOARD_HEIGHT-1][i] == BlackRook) j = i;\r
+ initialRights[4] = castlingRights[moveNum][4] = (castle_bl == 0 && gameInfo.variant != VariantFischeRandom ? -1 : j);\r
+\r
+ for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
+ if(board[0][k] == WhiteKing) initialRights[2] = castlingRights[moveNum][2] = k;\r
+ for(k=BOARD_LEFT; k<BOARD_RGHT; k++)\r
+ if(board[BOARD_HEIGHT-1][k] == BlackKing)\r
+ initialRights[5] = castlingRights[moveNum][5] = k;\r
+ } else { int r;\r
+ r = castlingRights[moveNum][0] = initialRights[0];\r
+ if(board[0][r] != WhiteRook) castlingRights[moveNum][0] = -1;\r
+ r = castlingRights[moveNum][1] = initialRights[1];\r
+ if(board[0][r] != WhiteRook) castlingRights[moveNum][1] = -1;\r
+ r = castlingRights[moveNum][3] = initialRights[3];\r
+ if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][3] = -1;\r
+ r = castlingRights[moveNum][4] = initialRights[4];\r
+ if(board[BOARD_HEIGHT-1][r] != BlackRook) castlingRights[moveNum][4] = -1;\r
+ /* wildcastle kludge: always assume King has rights */\r
+ r = castlingRights[moveNum][2] = initialRights[2];\r
+ r = castlingRights[moveNum][5] = initialRights[5];\r
+ }\r
+ /* [HGM] e.p. rights. Assume that ICS sends file number here? */\r
+ epStatus[moveNum] = double_push == -1 ? EP_NONE : double_push + BOARD_LEFT;\r
+\r
\r
if (ics_getting_history == H_GOT_REQ_HEADER ||\r
ics_getting_history == H_GOT_UNREQ_HEADER) {\r
to canonical algebraic form. */\r
if (moveNum > 0) {\r
if (appData.debugMode) {\r
+ if (appData.debugMode) { int f = forwardMostMove;\r
+ fprintf(debugFP, "parseboard %d, castling = %d %d %d %d %d %d\n", f,\r
+ castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
+ }\r
fprintf(debugFP, "accepted move %s from ICS, parse it.\n", move_str);\r
+ fprintf(debugFP, "moveNum = %d\n", moveNum);\r
fprintf(debugFP, "board = %d-%d x %d\n", BOARD_LEFT, BOARD_RGHT, BOARD_HEIGHT);\r
setbuf(debugFP, NULL);\r
}\r
ChessProgramState *cps;\r
{\r
char buf[MSG_SIZ];\r
+\r
if (cps->useUsermove) {\r
SendToProgram("usermove ", cps);\r
}\r
* the engine. It would be nice to have a better way to identify castle \r
* moves here. */\r
if(gameInfo.variant == VariantFischeRandom && cps->useOOCastle) {\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Tord's FRC castling code\n");\r
+ }\r
int fromX = moveList[moveNum][0] - AAA; \r
int fromY = moveList[moveNum][1] - ONE;\r
int toX = moveList[moveNum][2] - AAA; \r
int toY = moveList[moveNum][3] - ONE;\r
- if((boards[currentMove][fromY][fromX] == WhiteKing \r
- && boards[currentMove][toY][toX] == WhiteRook)\r
- || (boards[currentMove][fromY][fromX] == BlackKing \r
- && boards[currentMove][toY][toX] == BlackRook)) {\r
+ if((boards[moveNum][fromY][fromX] == WhiteKing \r
+ && boards[moveNum][toY][toX] == WhiteRook)\r
+ || (boards[moveNum][fromY][fromX] == BlackKing \r
+ && boards[moveNum][toY][toX] == BlackRook)) {\r
if(toX > fromX) SendToProgram("O-O\n", cps);\r
else SendToProgram("O-O-O\n", cps);\r
}\r
else SendToProgram(moveList[moveNum], cps);\r
/* End of additions by Tord */\r
}\r
+\r
+ /* [HGM] setting up the opening has brought engine in force mode! */\r
+ /* Send 'go' if we are in a mode where machine should play. */\r
+ if( (moveNum == 0 && setboardSpoiledMachineBlack && cps == &first) &&\r
+ (gameMode == TwoMachinesPlay ||\r
+#ifdef ZIPPY\r
+ gameMode == IcsPlayingBlack || gameMode == IcsPlayingWhite ||\r
+#endif\r
+ gameMode == MachinePlaysBlack || gameMode == MachinePlaysWhite) ) {\r
+ SendToProgram("go\n", cps);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "(extra)\n");\r
+ }\r
+ }\r
+ setboardSpoiledMachineBlack = 0;\r
}\r
\r
void\r
case BlackPromotionKnight:\r
case WhitePromotionKing:\r
case BlackPromotionKing:\r
-#ifdef FAIRY\r
case WhitePromotionChancellor:\r
case BlackPromotionChancellor:\r
case WhitePromotionArchbishop:\r
case BlackPromotionArchbishop:\r
-#endif\r
- sprintf(user_move, "%c%c%c%c=%c\n",\r
+ if(gameInfo.variant == VariantShatranj)\r
+ sprintf(user_move, "%c%c%c%c=%c\n",\r
+ AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
+ PieceToChar(WhiteFerz));\r
+ else\r
+ sprintf(user_move, "%c%c%c%c=%c\n",\r
AAA + fromX, ONE + fromY, AAA + toX, ONE + toY,\r
PieceToChar(PromoPiece(moveType)));\r
break;\r
if(move[1]=='*' && \r
move[2]>='0' && move[2]<='9' &&\r
move[3]>='a' && move[3]<='x' ) {\r
- move[2] = (move[2]-'1')+BOARD_LEFT + AAA;\r
- move[3] = (move[3]-'a') + ONE;\r
+ move[2] = BOARD_RGHT -1 - (move[2]-'1') + AAA;\r
+ move[3] = BOARD_HEIGHT-1 - (move[3]-'a') + ONE;\r
} else\r
if(move[0]>='0' && move[0]<='9' &&\r
move[1]>='a' && move[1]<='x' &&\r
move[2]>='0' && move[2]<='9' &&\r
move[3]>='a' && move[3]<='x' ) {\r
/* input move, Shogi -> normal */\r
-/*\r
- move[0] = BOARD_RGHT -1-(move[0]-'1') + AAA;\r
- move[1] = BOARD_HEIGHT-1-(move[1]-'a') + ONE;\r
- move[2] = BOARD_RGHT -1-(move[2]-'1') + AAA;\r
- move[3] = BOARD_HEIGHT-1-(move[3]-'a') + ONE;\r
-*/\r
- move[0] = (move[0]-'1')+BOARD_LEFT + AAA;\r
- move[1] = (move[1]-'a') + ONE;\r
- move[2] = (move[2]-'1')+BOARD_LEFT + AAA;\r
- move[3] = (move[3]-'a') + ONE;\r
+ move[0] = BOARD_RGHT -1 - (move[0]-'1') + AAA;\r
+ move[1] = BOARD_HEIGHT-1 - (move[1]-'a') + ONE;\r
+ move[2] = BOARD_RGHT -1 - (move[2]-'1') + AAA;\r
+ move[3] = BOARD_HEIGHT-1 - (move[3]-'a') + ONE;\r
} else\r
if(move[1]=='@' &&\r
move[3]>='0' && move[3]<='9' &&\r
move[2]>='a' && move[2]<='x' ) {\r
move[1] = '*';\r
- move[2] = (move[2]-AAA)-BOARD_LEFT + '1';\r
- move[3] = (move[3]-ONE) + 'a';\r
+ move[2] = BOARD_RGHT - 1 - (move[2]-AAA) + '1';\r
+ move[3] = BOARD_HEIGHT-1 - (move[3]-ONE) + 'a';\r
} else\r
if(\r
move[0]>='a' && move[0]<='x' &&\r
move[3]>='0' && move[3]<='9' &&\r
move[2]>='a' && move[2]<='x' ) {\r
/* output move, normal -> Shogi */\r
-/*\r
- move[0] = BOARD_RGHT -1-(move[0]-AAA) + '1';\r
- move[1] = BOARD_HEIGHT-1-(move[1]-ONE) + 'a';\r
- move[2] = BOARD_RGHT -1-(move[2]-AAA) + '1';\r
- move[3] = BOARD_HEIGHT-1-(move[3]-ONE) + 'a';\r
-*/\r
- move[0] = (move[0]-AAA)-BOARD_LEFT + '1';\r
- move[1] = (move[1]-ONE) + 'a';\r
- move[2] = (move[2]-AAA)-BOARD_LEFT + '1';\r
- move[3] = (move[3]-ONE) + 'a';\r
+ move[0] = BOARD_RGHT - 1 - (move[0]-AAA) + '1';\r
+ move[1] = BOARD_HEIGHT-1 - (move[1]-ONE) + 'a';\r
+ move[2] = BOARD_RGHT - 1 - (move[2]-AAA) + '1';\r
+ move[3] = BOARD_HEIGHT-1 - (move[3]-ONE) + 'a';\r
if(move[4] == PieceToChar(BlackQueen)) move[4] = '+';\r
}\r
if (appData.debugMode) {\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][FindEmptySquare(board, 0)] = WhiteRook;\r
- board[0][FindEmptySquare(board, 0)] = WhiteKing;\r
- board[0][FindEmptySquare(board, 0)] = WhiteRook;\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
board[0][ FindEmptySquare(board, knights % 16) ] = WhiteKnight;\r
\r
/* Place rooks and king */\r
- board[0][ FindEmptySquare(board, 0) ] = WhiteRook;\r
- board[0][ FindEmptySquare(board, 0) ] = WhiteKing;\r
- board[0][ FindEmptySquare(board, 0) ] = WhiteRook;\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
case VariantShatranj:\r
pieces = ShatranjArray;\r
nrCastlingRights = 0;\r
+ SetCharTable(pieceToChar, "PN.R.QB...Kpn.r.qb...k"); \r
break;\r
case VariantTwoKings:\r
pieces = twoKingsArray;\r
castlingRights[0][7] = initialRights[5] = 5;\r
castlingRank[6] = 0;\r
castlingRank[7] = BOARD_HEIGHT-1;\r
- startedFromSetupPosition = TRUE;\r
break;\r
case VariantCapablanca:\r
pieces = CapablancaArray;\r
gameInfo.boardWidth = 10;\r
- SetCharTable(pieceToChar, "PNBRQ.......AC..Kpnbrq.......ac..k"); \r
+ SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \r
break;\r
case VariantGothic:\r
pieces = GothicArray;\r
gameInfo.boardWidth = 10;\r
- SetCharTable(pieceToChar, "PNBRQ.......AC..Kpnbrq.......ac..k"); \r
+ SetCharTable(pieceToChar, "PNBRQ..ACKpnbrq..ack"); \r
+ break;\r
+ case VariantFalcon:\r
+ pieces = FalconArray;\r
+ gameInfo.boardWidth = 10;\r
+ SetCharTable(pieceToChar, "PNBRQ.............FKpnbrq.............fk"); \r
break;\r
case VariantXiangqi:\r
pieces = XiangqiArray;\r
gameInfo.boardWidth = 9;\r
gameInfo.boardHeight = 10;\r
nrCastlingRights = 0;\r
- SetCharTable(pieceToChar, "PH.R.AKE.C.......ph.r.ake.c......."); \r
+ SetCharTable(pieceToChar, "PH.R.AE..K.C.ph.r.ae..k.c."); \r
break;\r
case VariantShogi:\r
pieces = ShogiArray;\r
gameInfo.boardHeight = 9;\r
gameInfo.holdingsSize = 7;\r
nrCastlingRights = 0;\r
- SetCharTable(pieceToChar, "PNBRLSG...++++++Kpnbrlsg...++++++k"); \r
- break;\r
- case VariantShowgi:\r
- pieces = ShogiArray;\r
- gameInfo.boardWidth = 9;\r
- gameInfo.boardHeight = 9;\r
- gameInfo.holdingsSize = 7;\r
- nrCastlingRights = 0;\r
- for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
- SetCharTable(pieceToChar, "PNBRQFWEMOUHACG.Kpnbrlsgpnbrls...k"); \r
+ SetCharTable(pieceToChar, "PNBRLS...G.++++++Kpnbrls...g.++++++k"); \r
break;\r
case VariantCourier:\r
pieces = CourierArray;\r
gameInfo.boardWidth = 12;\r
nrCastlingRights = 0;\r
- SetCharTable(pieceToChar, "PNBR.FWEM.......Kpnbr.fwem.......k"); \r
+ SetCharTable(pieceToChar, "PNBR.FE..WMKpnbr.fe..wmk"); \r
for(i=0; i<BOARD_SIZE; i++) initialRights[i] = -1;\r
break;\r
case VariantKnightmate:\r
pieces = KnightmateArray;\r
- SetCharTable(pieceToChar, "P.BRQ...M.K......p.brq...m.k......"); \r
+ SetCharTable(pieceToChar, "P.BRQ.....M.........K.p.brq.....m.........k."); \r
break;\r
case VariantFairy:\r
pieces = fairyArray;\r
- SetCharTable(pieceToChar, "PNBRQFWEMOUHACGSKpnbrqfwemouhacgsk"); \r
- startedFromSetupPosition = TRUE;\r
+ SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk"); \r
break;\r
case VariantCrazyhouse:\r
case VariantBughouse:\r
pieces = FIDEArray;\r
- SetCharTable(pieceToChar, "PNBRQ......~~~~.Kpnbrq......~~~~.k"); \r
+ SetCharTable(pieceToChar, "PNBRQ.......~~~~Kpnbrq.......~~~~k"); \r
gameInfo.holdingsSize = 5;\r
break;\r
case VariantWildCastle:\r
}\r
initialPosition[BOARD_HEIGHT-1][j] = pieces[1][j-gameInfo.holdingsWidth];\r
}\r
- if( (gameInfo.variant == VariantShogi\r
- ||gameInfo.variant == VariantShowgi\r
- ) && !overrule ) {\r
+ if( (gameInfo.variant == VariantShogi) && !overrule ) {\r
+\r
j=BOARD_LEFT+1;\r
initialPosition[1][j] = WhiteBishop;\r
initialPosition[BOARD_HEIGHT-2][j] = BlackRook;\r
else {\r
SetupFRC( initialPosition, appData.defaultFrcPosition );\r
}\r
+ startedFromSetupPosition = TRUE;\r
+ } else if(startedFromPositionFile) {\r
+ /* [HGM] loadPos: use PositionFile for every new game */\r
+ CopyBoard(initialPosition, filePosition);\r
+ for(i=0; i<nrCastlingRights; i++)\r
+ castlingRights[0][i] = initialRights[i] = fileRights[i];\r
+ startedFromSetupPosition = TRUE;\r
}\r
\r
CopyBoard(boards[0], initialPosition);\r
oldy != gameInfo.boardHeight ||\r
oldh != gameInfo.holdingsWidth\r
#ifdef GOTHIC\r
- || oldv == VariantGothic ||\r
+ || oldv == VariantGothic || // For licensing popups\r
gameInfo.variant == VariantGothic\r
#endif\r
+#ifdef FALCON\r
+ || oldv == VariantFalcon ||\r
+ gameInfo.variant == VariantFalcon\r
+#endif\r
)\r
InitDrawingSizes(-2 ,0);\r
\r
if ((int) *bp < (int) BlackPawn) {\r
sprintf(message, "%c%c%c\n", PieceToChar(*bp), \r
AAA + j, ONE + i);\r
+ if(message[0] == '+' || message[0] == '~') {\r
+ sprintf(message, "%c%c%c+\n",\r
+ PieceToChar((ChessSquare)(DEMOTED *bp)),\r
+ AAA + j, ONE + i);\r
+ }\r
+ if(appData.alphaRank) {\r
+ message[1] = BOARD_RGHT - 1 - j + '1';\r
+ message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
+ }\r
SendToProgram(message, cps);\r
}\r
}\r
&& ((int) *bp >= (int) BlackPawn)) {\r
sprintf(message, "%c%c%c\n", ToUpper(PieceToChar(*bp)),\r
AAA + j, ONE + i);\r
+ if(message[0] == '+' || message[0] == '~') {\r
+ sprintf(message, "%c%c%c+\n",\r
+ PieceToChar((ChessSquare)(DEMOTED *bp)),\r
+ AAA + j, ONE + i);\r
+ }\r
+ if(appData.alphaRank) {\r
+ message[1] = BOARD_RGHT - 1 - j + '1';\r
+ message[2] = BOARD_HEIGHT - 1 - i + 'a';\r
+ }\r
SendToProgram(message, cps);\r
}\r
}\r
\r
SendToProgram(".\n", cps);\r
}\r
+ setboardSpoiledMachineBlack = 0; /* [HGM] assume WB 4.2.7 already solves this after sending setboard */\r
}\r
\r
int\r
(WhitePawn <= pdown && pdown < BlackPawn &&\r
WhitePawn <= pup && pup < BlackPawn ||\r
BlackPawn <= pdown && pdown < EmptySquare &&\r
- BlackPawn <= pup && pup < EmptySquare) )\r
+ BlackPawn <= pup && pup < EmptySquare \r
+ ) && !(gameInfo.variant == VariantFischeRandom &&\r
+ (pup == WhiteRook && pdown == WhiteKing && fromY == 0 && toY == 0||\r
+ pup == BlackRook && pdown == BlackKing && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1 ) \r
+ ) )\r
return ImpossibleMove;\r
\r
/* Check if the user is playing in turn. This is complicated because we\r
}\r
\r
/* [HGM] <popupFix> The following if has been moved here from\r
- UserMoveEnevt(). Because it seemed to belon here (why not allow\r
+ UserMoveEvent(). Because it seemed to belon here (why not allow\r
piece drops in training games?), and because it can only be\r
performed after it is known to what we promote. */\r
if (gameMode == Training) {\r
while (*message == '\007') message++;\r
\r
/*\r
+ * [HGM] engine debug message: ignore lines starting with '#' character\r
+ */\r
+ if(cps->debug && *message == '#') return;\r
+\r
+ /*\r
* Look for book output\r
*/\r
if (cps == &first && bookRequested) {\r
return;\r
}\r
\r
+ if (appData.debugMode) { int f = forwardMostMove;\r
+ fprintf(debugFP, "machine move %d, castling = %d %d %d %d %d %d\n", f,\r
+ castlingRights[f][0],castlingRights[f][1],castlingRights[f][2],castlingRights[f][3],castlingRights[f][4],castlingRights[f][5]);\r
+ }\r
AlphaRank(machineMove, 4);\r
if (!ParseOneMove(machineMove, forwardMostMove, &moveType,\r
&fromX, &fromY, &toX, &toY, &promoChar)) {\r
switch(moveType) {\r
case WhiteASideCastleFR:\r
case BlackASideCastleFR:\r
- toY++;\r
+ toX+=2;\r
currentMoveString[2]++;\r
break;\r
case WhiteHSideCastleFR:\r
case BlackHSideCastleFR:\r
- toY--;\r
+ toX--;\r
currentMoveString[2]--;\r
break;\r
}\r
/* [AS] Save move info and clear stats for next move */\r
pvInfoList[ forwardMostMove ].score = programStats.score;\r
pvInfoList[ forwardMostMove ].depth = programStats.depth;\r
- pvInfoList[ forwardMostMove ].time = -1;\r
+ pvInfoList[ forwardMostMove ].time = programStats.time; // [HGM] PGNtime: take time from engine stats\r
ClearProgramStats();\r
thinkOutput[0] = NULLCHAR;\r
hiddenThinkOutputState = 0;\r
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,\r
+ NrWQ=0, NrBQ=0, bishopsColor = 0,\r
NrPieces=0, NrPawns=0, PawnAdvance=0, i, j, k;\r
static int moveCount;\r
\r
case WhiteKnight:\r
NrWN++; break;\r
case WhiteBishop:\r
+ bishopsColor |= 1 << ((i^j)&1);\r
NrWB++; break;\r
case BlackKnight:\r
- NrWN++; break;\r
+ NrBN++; break;\r
case BlackBishop:\r
+ bishopsColor |= 1 << ((i^j)&1);\r
NrBB++; break;\r
case WhiteRook:\r
NrWR++; break;\r
case BlackRook:\r
NrBR++; break;\r
case WhiteQueen:\r
- NrWR++; break;\r
+ NrWQ++; break;\r
case BlackQueen:\r
- NrBR++; break;\r
+ NrBQ++; break;\r
case EmptySquare: \r
break;\r
case BlackPawn:\r
NrPieces += (p != EmptySquare);\r
}\r
\r
- if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2 )\r
- { /* KBK, KNK or KK */\r
+ if( NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || NrPieces == 2\r
+ || NrPieces == 4 && NrBB+NrWB==2 && bishopsColor != 3)\r
+ { /* KBK, KNK, KK of KBKB with like Bishops */\r
\r
/* always flag draws, for judging claims */\r
epStatus[forwardMostMove] = EP_INSUF_DRAW;\r
return;\r
}\r
} else moveCount = 6;\r
-\r
+#if 0\r
if (appData.debugMode) { int i;\r
fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",\r
forwardMostMove, backwardMostMove, epStatus[backwardMostMove],\r
fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]);\r
\r
}\r
+#endif\r
/* Check for rep-draws */\r
count = 0;\r
for(k = forwardMostMove-2;\r
epStatus[k+2] <= EP_NONE && epStatus[k+1] <= EP_NONE;\r
k-=2)\r
{ int rights=0;\r
+#if 0\r
if (appData.debugMode) {\r
fprintf(debugFP, " loop\n");\r
}\r
+#endif\r
if(CompareBoards(boards[k], boards[forwardMostMove])) {\r
+#if 0\r
if (appData.debugMode) {\r
fprintf(debugFP, "match\n");\r
}\r
+#endif\r
/* compare castling rights */\r
if( castlingRights[forwardMostMove][2] != castlingRights[k][2] &&\r
(castlingRights[k][0] >= 0 || castlingRights[k][1] >= 0) )\r
castlingRights[forwardMostMove][4] != castlingRights[k][4] )\r
rights++;\r
}\r
+#if 0\r
if (appData.debugMode) {\r
for(i=0; i<nrCastlingRights; i++)\r
fprintf(debugFP, " (%d,%d)", castlingRights[forwardMostMove][i], castlingRights[k][i]);\r
if (appData.debugMode) {\r
fprintf(debugFP, " %d %d\n", rights, k);\r
}\r
+#endif\r
if( rights == 0 && ++count > appData.drawRepeats-2\r
&& appData.drawRepeats > 1) {\r
/* adjudicate after user-specified nr of repeats */\r
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
GameEnds( GameIsDrawn, "Xboard adjudication: 50-move rule", GE_XBOARD );\r
return;\r
- }\r
+ }\r
+\r
+ /* if draw offer is pending, treat it as a draw claim\r
+ * when draw condition present, to allow engines a way to\r
+ * claim draws before making their move to avoid a race\r
+ * condition occurring after their move\r
+ */\r
+ if( cps->other->offeredDraw || cps->offeredDraw ) {\r
+ char *p = NULL;\r
+ if(epStatus[forwardMostMove] == EP_RULE_DRAW)\r
+ p = "Draw claim: 50-move rule";\r
+ if(epStatus[forwardMostMove] == EP_REP_DRAW)\r
+ p = "Draw claim: 3-fold repetition";\r
+ if(epStatus[forwardMostMove] == EP_INSUF_DRAW)\r
+ p = "Draw claim: insufficient mating material";\r
+ if( p != NULL ) {\r
+ GameEnds( GameIsDrawn, p, GE_XBOARD );\r
+ ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/\r
+ return;\r
+ }\r
+ }\r
+\r
}\r
\r
\r
}\r
\r
if (gameMode == TwoMachinesPlay) {\r
+ /* [HGM] relaying draw offers moved to after reception of move */\r
+ /* and interpreting offer as claim if it brings draw condition */\r
+ if (cps->offeredDraw == 1 && cps->other->sendDrawOffers) {\r
+ SendToProgram("draw\n", cps->other);\r
+ }\r
if (cps->other->sendTime) {\r
SendTimeRemaining(cps->other,\r
cps->other->twoMachinesColor[0] == 'w');\r
if (gameMode == TwoMachinesPlay) {\r
if (cps->other->offeredDraw) {\r
GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD);\r
- } else {\r
+ /* [HGM] in two-machine mode we delay relaying draw offer */\r
+ /* until after we also have move, to see if it is really claim */\r
+ }\r
+#if 0\r
+ else {\r
if (cps->other->sendDrawOffers) {\r
SendToProgram("draw\n", cps->other);\r
}\r
}\r
+#endif\r
} else if (gameMode == MachinePlaysWhite ||\r
gameMode == MachinePlaysBlack) {\r
if (userOfferedDraw) {\r
int promoChar;\r
Board board;\r
{\r
- ChessSquare captured = board[toY][toX], piece; int p;\r
+ ChessSquare captured = board[toY][toX], piece, king; int p;\r
+\r
+ /* [HGM] compute & store e.p. status and castling rights for new position */\r
+ /* if we are updating a board for which those exist (i.e. in boards[]) */\r
+ if((p = ((int)board - (int)boards[0])/((int)boards[1]-(int)boards[0])) < MAX_MOVES && p > 0)\r
+ { int i, j;\r
+\r
+ epStatus[p] = EP_NONE;\r
+\r
+ if( board[toY][toX] != EmptySquare ) \r
+ epStatus[p] = EP_CAPTURE; \r
+\r
+ if( board[fromY][fromX] == WhitePawn ) {\r
+ epStatus[p] = EP_PAWN_MOVE; \r
+ if( toY-fromY==2 &&\r
+ (toX>BOARD_LEFT && board[toY][toX-1] == BlackPawn ||\r
+ toX<BOARD_RGHT-1 && board[toY][toX+1] == BlackPawn ) )\r
+ epStatus[p] = toX;\r
+ } else \r
+ if( board[fromY][fromX] == BlackPawn ) {\r
+ epStatus[p] = EP_PAWN_MOVE; \r
+ if( toY-fromY== -2 &&\r
+ (toX>BOARD_LEFT && board[toY][toX-1] == WhitePawn ||\r
+ toX<BOARD_RGHT-1 && board[toY][toX+1] == WhitePawn ) )\r
+ epStatus[p] = toX;\r
+ }\r
+\r
+ for(i=0; i<nrCastlingRights; i++) {\r
+ castlingRights[p][i] = castlingRights[p-1][i];\r
+ if(castlingRights[p][i] == fromX && castlingRank[i] == fromY ||\r
+ castlingRights[p][i] == toX && castlingRank[i] == toY \r
+ ) castlingRights[p][i] = -1; // revoke for moved or captured piece\r
+ }\r
+\r
+ }\r
\r
/* [HGM] In Shatranj and Courier all promotions are to Ferz */\r
if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier)\r
- && promoChar != 0) promoChar = 'F';\r
+ && promoChar != 0) promoChar = PieceToChar(WhiteFerz);\r
\r
if (fromX == toX && fromY == toY) return;\r
\r
if (fromY == DROP_RANK) {\r
/* must be first */\r
- board[toY][toX] = (ChessSquare) fromX;\r
+ piece = board[toY][toX] = (ChessSquare) fromX;\r
} else {\r
piece = board[fromY][fromX]; /* [HGM] remember, for Shogi promotion */\r
+ king = piece < (int) BlackPawn ? WhiteKing : BlackKing; /* [HGM] Knightmate simplify testing for castling */\r
+ if(gameInfo.variant == VariantKnightmate)\r
+ king += (int) WhiteUnicorn - (int) WhiteKing;\r
\r
/* Code added by Tord: */\r
/* FRC castling assumed when king captures friendly rook. */\r
}\r
/* End of code added by Tord */\r
\r
- } else if (initialPosition[fromY][fromX] == WhiteKing\r
- && board[fromY][fromX] == WhiteKing\r
+ } else if (board[fromY][fromX] == king\r
+ && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
&& toY == fromY && toX > fromX+1) {\r
board[fromY][fromX] = EmptySquare;\r
- board[toY][toX] = WhiteKing;\r
+ board[toY][toX] = king;\r
+ board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
board[fromY][BOARD_RGHT-1] = EmptySquare;\r
- board[toY][toX-1] = WhiteRook;\r
- } else if (initialPosition[fromY][fromX] == WhiteKing\r
- && board[fromY][fromX] == WhiteKing\r
+ } else if (board[fromY][fromX] == king\r
+ && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
&& toY == fromY && toX < fromX-1) {\r
board[fromY][fromX] = EmptySquare;\r
- board[toY][toX] = WhiteKing;\r
+ board[toY][toX] = king;\r
+ board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
board[fromY][BOARD_LEFT] = EmptySquare;\r
- board[toY][toX+1] = WhiteRook;\r
- } else if (fromY == 0 && fromX == 3\r
- && board[fromY][fromX] == WhiteKing\r
- && toY == 0 && toX == 5) {\r
- board[fromY][fromX] = EmptySquare;\r
- board[toY][toX] = WhiteKing;\r
- board[fromY][7] = EmptySquare;\r
- board[toY][4] = WhiteRook;\r
- } else if (fromY == 0 && fromX == 3\r
- && board[fromY][fromX] == WhiteKing\r
- && toY == 0 && toX == 1) {\r
- board[fromY][fromX] = EmptySquare;\r
- board[toY][toX] = WhiteKing;\r
- board[fromY][0] = EmptySquare;\r
- board[toY][2] = WhiteRook;\r
} else if (board[fromY][fromX] == WhitePawn\r
&& toY == BOARD_HEIGHT-1\r
&& gameInfo.variant != VariantXiangqi\r
board[fromY][fromX] = EmptySquare;\r
} else if ((fromY == BOARD_HEIGHT-4)\r
&& (toX != fromX)\r
+ && gameInfo.variant != VariantXiangqi\r
&& (board[fromY][fromX] == WhitePawn)\r
&& (board[toY][toX] == EmptySquare)) {\r
board[fromY][fromX] = EmptySquare;\r
board[toY][toX] = WhitePawn;\r
captured = board[toY - 1][toX];\r
board[toY - 1][toX] = EmptySquare;\r
- } else if (initialPosition[fromY][fromX] == BlackKing\r
- && board[fromY][fromX] == BlackKing\r
+ } else if (board[fromY][fromX] == king\r
+ && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
&& toY == fromY && toX > fromX+1) {\r
board[fromY][fromX] = EmptySquare;\r
- board[toY][toX] = BlackKing;\r
+ board[toY][toX] = king;\r
+ board[toY][toX-1] = board[fromY][BOARD_RGHT-1];\r
board[fromY][BOARD_RGHT-1] = EmptySquare;\r
- board[toY][toX-1] = BlackRook;\r
- } else if (initialPosition[fromY][fromX] == BlackKing\r
- && board[fromY][fromX] == BlackKing\r
+ } else if (board[fromY][fromX] == king\r
+ && fromX != BOARD_LEFT && fromX != BOARD_RGHT-1 // [HGM] cylinder */\r
&& toY == fromY && toX < fromX-1) {\r
board[fromY][fromX] = EmptySquare;\r
- board[toY][toX] = BlackKing;\r
+ board[toY][toX] = king;\r
+ board[toY][toX+1] = board[fromY][BOARD_LEFT];\r
board[fromY][BOARD_LEFT] = EmptySquare;\r
- board[toY][toX+1] = BlackRook;\r
} else if (fromY == 7 && fromX == 3\r
&& board[fromY][fromX] == BlackKing\r
&& toY == 7 && toX == 5) {\r
board[fromY][fromX] = EmptySquare;\r
} else if ((fromY == 3)\r
&& (toX != fromX)\r
+ && gameInfo.variant != VariantXiangqi\r
&& (board[fromY][fromX] == BlackPawn)\r
&& (board[toY][toX] == EmptySquare)) {\r
board[fromY][fromX] = EmptySquare;\r
{\r
forwardMostMove++;\r
\r
+ if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting */\r
+ int timeLeft; static int lastLoadFlag=0; int king, piece;\r
+ piece = boards[forwardMostMove-1][fromY][fromX];\r
+ king = piece < (int) BlackPawn ? WhiteKing : BlackKing;\r
+ if(gameInfo.variant == VariantKnightmate)\r
+ king += (int) WhiteUnicorn - (int) WhiteKing;\r
+ if(forwardMostMove == 1) {\r
+ if(blackPlaysFirst) \r
+ fprintf(serverMoves, "%s;", second.tidy);\r
+ fprintf(serverMoves, "%s;", first.tidy);\r
+ if(!blackPlaysFirst) \r
+ fprintf(serverMoves, "%s;", second.tidy);\r
+ } else fprintf(serverMoves, loadFlag|lastLoadFlag ? ":" : ";");\r
+ lastLoadFlag = loadFlag;\r
+ // print base move\r
+ fprintf(serverMoves, "%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+toY);\r
+ // print castling suffix\r
+ if( toY == fromY && piece == king ) {\r
+ if(toX-fromX > 1)\r
+ fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_RGHT-1, ONE+fromY, AAA+toX-1,ONE+toY);\r
+ if(fromX-toX >1)\r
+ fprintf(serverMoves, ":%c%c:%c%c", AAA+BOARD_LEFT, ONE+fromY, AAA+toX+1,ONE+toY);\r
+ }\r
+ // e.p. suffix\r
+ if( (boards[forwardMostMove-1][fromY][fromX] == WhitePawn ||\r
+ boards[forwardMostMove-1][fromY][fromX] == BlackPawn ) &&\r
+ boards[forwardMostMove-1][toY][toX] == EmptySquare\r
+ && fromX != toX )\r
+ fprintf(serverMoves, ":%c%c:%c%c", AAA+fromX, ONE+fromY, AAA+toX, ONE+fromY);\r
+ // promotion suffix\r
+ if(promoChar != NULLCHAR)\r
+ fprintf(serverMoves, ":%c:%c%c", promoChar, AAA+toX, ONE+toY);\r
+ if(!loadFlag) {\r
+ fprintf(serverMoves, "/%d/%d",\r
+ pvInfoList[forwardMostMove-1].depth, pvInfoList[forwardMostMove-1].score);\r
+ if(forwardMostMove & 1) timeLeft = whiteTimeRemaining/1000;\r
+ else timeLeft = blackTimeRemaining/1000;\r
+ fprintf(serverMoves, "/%d", timeLeft);\r
+ }\r
+ fflush(serverMoves);\r
+ }\r
+\r
if (forwardMostMove >= MAX_MOVES) {\r
DisplayFatalError("Game too long; increase MAX_MOVES and recompile",\r
0, 1);\r
commentList[forwardMostMove] = NULL;\r
}\r
CopyBoard(boards[forwardMostMove], boards[forwardMostMove - 1]);\r
- /* [HGM] compute & store e.p. status and castling rights for new position */\r
- { int i, j;\r
-\r
- epStatus[forwardMostMove] = EP_NONE;\r
-\r
- if( boards[forwardMostMove][toY][toX] != EmptySquare ) \r
- epStatus[forwardMostMove] = EP_CAPTURE; \r
-\r
- if( boards[forwardMostMove][fromY][fromX] == WhitePawn ) {\r
- epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
- if( toY-fromY==2 &&\r
- (toX>BOARD_LEFT && boards[forwardMostMove][toY][toX-1] == BlackPawn ||\r
- toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == BlackPawn ) )\r
- epStatus[forwardMostMove] = toX;\r
- } else \r
- if( boards[forwardMostMove][fromY][fromX] == BlackPawn ) {\r
- epStatus[forwardMostMove] = EP_PAWN_MOVE; \r
- if( toY-fromY== -2 &&\r
- (toX>BOARD_LEFT && boards[forwardMostMove][toY][toX-1] == WhitePawn ||\r
- toX<BOARD_RGHT-1 && boards[forwardMostMove][toY][toX+1] == WhitePawn ) )\r
- epStatus[forwardMostMove] = toX;\r
- }\r
-\r
- for(i=0; i<nrCastlingRights; i++) {\r
- castlingRights[forwardMostMove][i] = castlingRights[forwardMostMove-1][i];\r
- if(castlingRights[forwardMostMove][i] == fromX && castlingRank[i] == fromY ||\r
- castlingRights[forwardMostMove][i] == toX && castlingRank[i] == toY \r
- ) castlingRights[forwardMostMove][i] = -1; // revoke for moved or captured piece\r
-\r
-\r
-\r
- }\r
-\r
- }\r
ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove]);\r
gameInfo.result = GameUnfinished;\r
if (gameInfo.resultDetails != NULL) {\r
\r
\r
void\r
-InitChessProgram(cps)\r
+InitChessProgram(cps, setup)\r
ChessProgramState *cps;\r
+ int setup; /* [HGM] needed to setup FRC opening position */\r
{\r
- char buf[MSG_SIZ], *b; int overruled;\r
+ char buf[MSG_SIZ], b[MSG_SIZ]; int overruled;\r
if (appData.noChessProgram) return;\r
hintRequested = FALSE;\r
bookRequested = FALSE;\r
DisplayFatalError(buf, 0, 1);\r
return;\r
}\r
- b = buf;\r
+\r
/* [HGM] make prefix for non-standard board size. Awkward testing... */\r
overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
if( gameInfo.variant == VariantXiangqi )\r
overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 9 || gameInfo.holdingsSize != 7;\r
if( gameInfo.variant == VariantBughouse || gameInfo.variant == VariantCrazyhouse )\r
overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 5;\r
- if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantGothic )\r
+ if( gameInfo.variant == VariantCapablanca || gameInfo.variant == VariantGothic || gameInfo.variant == VariantFalcon )\r
overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
if( gameInfo.variant == VariantCourier )\r
overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0;\r
\r
if(overruled) {\r
- if (cps->protocolVersion != 1 && StrStr(cps->variants, "boardsize") == NULL) {\r
- sprintf(buf, "Board size %dx%d+%d not supported by %s",\r
- gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy);\r
- DisplayFatalError(buf, 0, 1);\r
- return;\r
+ sprintf(b, "%dx%d+%d_%s", gameInfo.boardWidth, gameInfo.boardHeight, \r
+ gameInfo.holdingsSize, VariantName(gameInfo.variant)); // cook up sized variant name\r
+ /* [HGM] varsize: try first if this defiant size variant is specifically known */\r
+ if(StrStr(cps->variants, b) == NULL) { \r
+ // specific sized variant not known, check if general sizing allowed\r
+ if (cps->protocolVersion != 1) { // for protocol 1 we cannot check and hope for the best\r
+ if(StrStr(cps->variants, "boardsize") == NULL) {\r
+ sprintf(buf, "Board size %dx%d+%d not supported by %s",\r
+ gameInfo.boardWidth, gameInfo.boardHeight, gameInfo.holdingsSize, cps->tidy);\r
+ DisplayFatalError(buf, 0, 1);\r
+ return;\r
+ }\r
+ /* [HGM] here we really should compare with the maximum supported board size */\r
+ }\r
}\r
- /* [HGM] here we really should compare with the maximum supported board size */\r
- sprintf(buf, "%dx%d+%d_", gameInfo.boardWidth,\r
- gameInfo.boardHeight, gameInfo.holdingsSize );\r
- while(*b++ != '_');\r
- }\r
- sprintf(b, "variant %s\n", VariantName(gameInfo.variant));\r
+ } else sprintf(b, "%s", VariantName(gameInfo.variant));\r
+ sprintf(buf, "variant %s\n", b);\r
SendToProgram(buf, cps);\r
+ /* [HGM] send opening position in FRC to first engine */\r
+ if(setup /* cps == &first && gameInfo.variant == VariantFischeRandom */) {\r
+ SendToProgram("force\n", cps);\r
+ SendBoard(cps, 0);\r
+ /* engine is now in force mode! Set flag to wake it up after first move. */\r
+ setboardSpoiledMachineBlack = 1;\r
+ }\r
}\r
if (cps->sendICS) {\r
sprintf(buf, "ics %s\n", appData.icsActive ? appData.icsHost : "-");\r
int isIcsGame;\r
char buf[MSG_SIZ];\r
\r
+ if(endingGame) return; /* [HGM] crash: forbid recursion */\r
+ endingGame = 1;\r
+\r
if (appData.debugMode) {\r
fprintf(debugFP, "GameEnds(%d, %s, %d)\n",\r
result, resultDetails ? resultDetails : "(null)", whosays);\r
}\r
\r
- if (appData.icsActive && whosays == (GE_ENGINE || whosays >= GE_ENGINE1)) {\r
+ if (appData.icsActive && (whosays == GE_ENGINE || whosays >= GE_ENGINE1)) {\r
/* If we are playing on ICS, the server decides when the\r
game is over, but the engine can offer to draw, claim \r
a draw, or resign. \r
}\r
}\r
#endif\r
- return;\r
+ endingGame = 0; /* [HGM] crash */\r
+ return;\r
}\r
\r
/* If we're loading the game from a file, stop */\r
}\r
\r
/* Cancel draw offers */\r
- first.offeredDraw = second.offeredDraw = 0;\r
+ first.offeredDraw = second.offeredDraw = 0;\r
\r
/* If this is an ICS game, only ICS can really say it's done;\r
if not, anyone can. */\r
result = claimer == 'w' ? BlackWins : WhiteWins;\r
resultDetails = buf;\r
} else\r
- if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS ) {\r
+ if( result == GameIsDrawn && epStatus[forwardMostMove] > EP_DRAWS\r
+ && (forwardMostMove <= backwardMostMove ||\r
+ epStatus[forwardMostMove-1] > EP_DRAWS ||\r
+ (claimer=='b')==(forwardMostMove&1))\r
+ ) {\r
/* Draw that was not flagged by Xboard is false */\r
sprintf(buf, "False draw claim: '%s'", resultDetails);\r
result = claimer == 'w' ? BlackWins : WhiteWins;\r
/* (Claiming a loss is accepted no questions asked!) */\r
}\r
\r
+ if(serverMoves != NULL && !loadFlag) { char c = '=';\r
+ if(result==WhiteWins) c = '+';\r
+ if(result==BlackWins) c = '-';\r
+ if(resultDetails != NULL)\r
+ fprintf(serverMoves, ";%c;%s\n", c, resultDetails);\r
+ }\r
if (appData.debugMode) {\r
fprintf(debugFP, "GameEnds(%d, %s, %d) after test\n",\r
result, resultDetails ? resultDetails : "(null)", whosays);\r
gameInfo.result = result;\r
gameInfo.resultDetails = StrSave(resultDetails);\r
\r
+ /* display last move only if game was not loaded from file */\r
+ if ((whosays != GE_FILE) && (currentMove == forwardMostMove))\r
+ DisplayMove(currentMove - 1);\r
+ \r
+ if (forwardMostMove != 0) {\r
+ if (gameMode != PlayFromGameFile && gameMode != EditGame) {\r
+ if (*appData.saveGameFile != NULLCHAR) {\r
+ SaveGameToFile(appData.saveGameFile, TRUE);\r
+ } else if (appData.autoSaveGames) {\r
+ AutoSaveGame();\r
+ }\r
+ if (*appData.savePositionFile != NULLCHAR) {\r
+ SavePositionToFile(appData.savePositionFile);\r
+ }\r
+ }\r
+ }\r
+\r
/* Tell program how game ended in case it is learning */\r
+ /* [HGM] Moved this to after saving the PGN, just in case */\r
+ /* engine died and we got here through time loss. In that */\r
+ /* case we will get a fatal error writing the pipe, which */\r
+ /* would otherwise lose us the PGN. */\r
+ /* [HGM] crash: not needed anymore, but doesn't hurt; */\r
+ /* output during GameEnds should never be fatal anymore */\r
if (gameMode == MachinePlaysWhite ||\r
gameMode == MachinePlaysBlack ||\r
gameMode == TwoMachinesPlay ||\r
SendToProgram(buf, &second);\r
}\r
}\r
-\r
- /* display last move only if game was not loaded from file */\r
- if ((whosays != GE_FILE) && (currentMove == forwardMostMove))\r
- DisplayMove(currentMove - 1);\r
- \r
- if (forwardMostMove != 0) {\r
- if (gameMode != PlayFromGameFile && gameMode != EditGame) {\r
- if (*appData.saveGameFile != NULLCHAR) {\r
- SaveGameToFile(appData.saveGameFile, TRUE);\r
- } else if (appData.autoSaveGames) {\r
- AutoSaveGame();\r
- }\r
- if (*appData.savePositionFile != NULLCHAR) {\r
- SavePositionToFile(appData.savePositionFile);\r
- }\r
- }\r
- }\r
}\r
\r
if (appData.icsActive) {\r
if (appData.noChessProgram) {\r
gameMode = nextGameMode;\r
ModeHighlight();\r
- return;\r
+ endingGame = 0; /* [HGM] crash */\r
+ return;\r
}\r
\r
if (first.reuse) {\r
if(appData.matchPause>10000 || appData.matchPause<10)\r
appData.matchPause = 10000; /* [HGM] make pause adjustable */\r
ScheduleDelayedEvent(NextMatchGame, appData.matchPause);\r
+ endingGame = 0; /* [HGM] crash */\r
return;\r
} else {\r
char buf[MSG_SIZ];\r
ExitAnalyzeMode();\r
gameMode = nextGameMode;\r
ModeHighlight();\r
+ endingGame = 0; /* [HGM] crash */\r
}\r
\r
/* Assumes program was just initialized (initString sent).\r
SendToProgram("force\n", cps);\r
if (startedFromSetupPosition) {\r
SendBoard(cps, backwardMostMove);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "feedMoves\n");\r
+ }\r
}\r
for (i = backwardMostMove; i < upto; i++) {\r
SendMoveToProgram(i, cps);\r
if (appData.noChessProgram || first.pr != NoProc) return;\r
\r
StartChessProgram(&first);\r
- InitChessProgram(&first);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "From ResurrectChessProgram\n");\r
+ }\r
+ InitChessProgram(&first, FALSE);\r
FeedMovesToProgram(&first, currentMove);\r
\r
if (!first.sendTime) {\r
fprintf(debugFP, "Reset(%d, %d) from gameMode %d\n",\r
redraw, init, gameMode);\r
}\r
-\r
pausing = pauseExamInvalid = FALSE;\r
startedFromSetupPosition = blackPlaysFirst = FALSE;\r
firstMove = TRUE;\r
alarmSounded = FALSE;\r
\r
GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+ if(appData.serverMovesName != NULL) {\r
+ /* [HGM] prepare to make moves file for broadcasting */\r
+ clock_t t = clock();\r
+ if(serverMoves != NULL) fclose(serverMoves);\r
+ serverMoves = fopen(appData.serverMovesName, "r");\r
+ if(serverMoves != NULL) {\r
+ fclose(serverMoves);\r
+ /* delay 15 sec before overwriting, so all clients can see end */\r
+ while(clock()-t < appData.serverPause*CLOCKS_PER_SEC);\r
+ }\r
+ serverMoves = fopen(appData.serverMovesName, "w");\r
+ }\r
+\r
ExitAnalyzeMode();\r
gameMode = BeginningOfGame;\r
ModeHighlight();\r
if (first.pr == NULL) {\r
StartChessProgram(&first);\r
}\r
- if (init) InitChessProgram(&first);\r
+ if (init) {\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "From Reset\n");\r
+ }\r
+InitChessProgram(&first, startedFromSetupPosition);}\r
DisplayTitle("");\r
DisplayMessage("", "");\r
HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);\r
SendMoveToProgram(currentMove++, &first);\r
DisplayBothClocks();\r
DrawPosition(FALSE, boards[currentMove]);\r
- if (commentList[currentMove] != NULL) {\r
- DisplayComment(currentMove - 1, commentList[currentMove]);\r
- }\r
+ // [HGM] PV info: always display, routine tests if empty\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
return TRUE;\r
}\r
\r
if (appData.matchMode || (appData.timeDelay == 0 && !pausing)) {\r
DrawPosition(FALSE, boards[currentMove]);\r
DisplayBothClocks();\r
- if (!appData.matchMode && commentList[currentMove] != NULL)\r
+ if (!appData.matchMode) // [HGM] PV info: routine tests if empty\r
DisplayComment(currentMove - 1, commentList[currentMove]);\r
}\r
(void) StopLoadGameTimer();\r
int numPGNTags = 0;\r
int err;\r
GameMode oldGameMode;\r
+ VariantClass oldVariant = gameInfo.variant; /* [HGM] PGNvariant */\r
\r
if (appData.debugMode) \r
fprintf(debugFP, "LoadGame(): on entry, gameMode %d\n", gameMode);\r
\r
yynewfile(f);\r
\r
-\r
if (lg && lg->gameInfo.white && lg->gameInfo.black) {\r
sprintf(buf, "%s vs. %s", lg->gameInfo.white,\r
lg->gameInfo.black);\r
err = ParsePGNTag(yy_text, &gameInfo);\r
if (!err) numPGNTags++;\r
\r
+ /* [HGM] PGNvariant: automatically switch to variant given in PGN tag */\r
+ if(gameInfo.variant != oldVariant) {\r
+ startedFromPositionFile = FALSE; /* [HGM] loadPos: variant switch likely makes position invalid */\r
+ InitPosition(TRUE);\r
+ oldVariant = gameInfo.variant;\r
+ if (appData.debugMode) \r
+ fprintf(debugFP, "New variant %d\n", (int) oldVariant);\r
+ }\r
+\r
+\r
if (gameInfo.fen != NULL) {\r
Board initial_position;\r
startedFromSetupPosition = TRUE;\r
if (first.pr == NoProc) {\r
StartChessProgram(&first);\r
}\r
- InitChessProgram(&first);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "From LoadGame\n");\r
+ }\r
+ InitChessProgram(&first, FALSE);\r
SendToProgram("force\n", &first);\r
if (startedFromSetupPosition) {\r
SendBoard(&first, forwardMostMove);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Load Game\n");\r
+ }\r
DisplayBothClocks();\r
} \r
\r
+ /* [HGM] server: flag to write setup moves in broadcast file as one */\r
+ loadFlag = appData.suppressLoadMoves;\r
+\r
while (cm == Comment) {\r
char *p;\r
if (appData.debugMode) \r
return TRUE;\r
}\r
\r
- if (commentList[currentMove] != NULL) {\r
- if (!matchMode && (pausing || appData.timeDelay != 0)) {\r
+ // [HGM] PV info: routine tests if comment empty\r
+ if (!matchMode && (pausing || appData.timeDelay != 0)) {\r
DisplayComment(currentMove - 1, commentList[currentMove]);\r
- }\r
}\r
if (!matchMode && appData.timeDelay != 0) \r
DrawPosition(FALSE, boards[currentMove]);\r
\r
if (appData.debugMode) \r
fprintf(debugFP, "LoadGame(): on exit, gameMode %d\n", gameMode);\r
+\r
+ loadFlag = 0; /* [HGM] true game starts */\r
return TRUE;\r
}\r
\r
strcpy(lastLoadPositionTitle, title);\r
if (first.pr == NoProc) {\r
StartChessProgram(&first);\r
- InitChessProgram(&first);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "From LoadPosition\n");\r
+ }\r
+ InitChessProgram(&first, FALSE);\r
} \r
pn = positionNumber;\r
if (positionNumber < 0) {\r
DisplayError("Position not found in file", 0);\r
return FALSE;\r
}\r
+#if 0\r
switch (line[0]) {\r
case '#': case 'x':\r
default:\r
case 'P': case 'N': case 'B': case 'R': case 'Q': case 'K':\r
case '1': case '2': case '3': case '4': case '5': case '6':\r
case '7': case '8': case '9':\r
-#ifdef FAIRY\r
case 'H': case 'A': case 'M': case 'h': case 'a': case 'm':\r
case 'E': case 'F': case 'G': case 'e': case 'f': case 'g':\r
case 'C': case 'W': case 'c': case 'w': \r
-#endif\r
fenMode = TRUE;\r
break;\r
}\r
+#else\r
+ // [HGM] FEN can begin with digit, any piece letter valid in this variant, or a + for Shogi promoted pieces\r
+ fenMode = line[0] >= '0' && line[0] <= '9' || line[0] == '+' || CharToPiece(line[0]) != EmptySquare;\r
+#endif\r
\r
if (pn >= 2) {\r
if (fenMode || line[0] == '#') pn--;\r
DisplayMessage("", "White to play");\r
}\r
SendBoard(&first, forwardMostMove);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Load Position\n");\r
+ }\r
\r
if (positionNumber > 1) {\r
sprintf(line, "%s %d", title, positionNumber);\r
if(i >= backwardMostMove) {\r
/* take the time that changed */\r
seconds = timeRemaining[0][i] - timeRemaining[0][i+1];\r
- if(seconds <= 0)\r
+ if(seconds <= 0)\r
seconds = timeRemaining[1][i] - timeRemaining[1][i+1];\r
}\r
seconds /= 1000;\r
+ seconds = pvInfoList[i].time/100;\r
if (appData.debugMode) {\r
fprintf(debugFP, "times = %d %d %d %d, seconds=%d\n",\r
timeRemaining[0][i+1], timeRemaining[0][i],\r
}\r
}\r
\r
-static int exiting = 0;\r
-\r
void\r
ExitEvent(status)\r
int status;\r
if (icsPR != NoProc) {\r
DestroyChildProcess(icsPR, TRUE);\r
}\r
+#if 0\r
/* Save game if resource set and not already saved by GameEnds() */\r
- if (gameInfo.resultDetails == NULL && forwardMostMove > 0) {\r
+ if ((gameInfo.resultDetails == NULL || errorExitFlag )\r
+ && forwardMostMove > 0) {\r
if (*appData.saveGameFile != NULLCHAR) {\r
SaveGameToFile(appData.saveGameFile, TRUE);\r
} else if (appData.autoSaveGames) {\r
}\r
}\r
GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
+#else\r
+ /* [HGM] crash: leave writing PGN and position entirely to GameEnds() */\r
+ GameEnds(gameInfo.result, gameInfo.resultDetails==NULL ? "aborted" : gameInfo.resultDetails, GE_PLAYER);\r
+#endif\r
+ /* [HGM] crash: the above GameEnds() is a dud if another one was running */\r
+ /* make sure this other one finishes before killing it! */\r
+ if(endingGame) { int count = 0;\r
+ if(appData.debugMode) fprintf(debugFP, "ExitEvent() during GameEnds(), wait\n");\r
+ while(endingGame && count++ < 10) DoSleep(1);\r
+ if(appData.debugMode && endingGame) fprintf(debugFP, "GameEnds() seems stuck, proceed exiting\n");\r
+ }\r
\r
/* Kill off chess programs */\r
if (first.pr != NoProc) {\r
return;\r
}\r
DisplayMessage("", "");\r
- InitChessProgram(&second);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "From TwoMachines\n");\r
+ }\r
+ InitChessProgram(&second, FALSE);\r
SendToProgram("force\n", &second);\r
if (startedFromSetupPosition) {\r
SendBoard(&second, backwardMostMove);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "Two Machines\n");\r
+ }\r
}\r
for (i = backwardMostMove; i < forwardMostMove; i++) {\r
SendMoveToProgram(i, &second);\r
EditPositionDone()\r
{\r
startedFromSetupPosition = TRUE;\r
- InitChessProgram(&first);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "From EditPosition\n");\r
+ }\r
+ InitChessProgram(&first, FALSE);\r
SendToProgram("force\n", &first);\r
if (blackPlaysFirst) {\r
strcpy(moveList[0], "");\r
currentMove = forwardMostMove = backwardMostMove = 0;\r
}\r
SendBoard(&first, forwardMostMove);\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "EditPosDone\n");\r
+ }\r
DisplayTitle("");\r
timeRemaining[0][forwardMostMove] = whiteTimeRemaining;\r
timeRemaining[1][forwardMostMove] = blackTimeRemaining;\r
break;\r
\r
case PromotePiece:\r
- if(piece >= (int)WhitePawn && piece < (int)WhiteWazir ||\r
- piece >= (int)BlackPawn && piece < (int)BlackWazir ) {\r
+ if(piece >= (int)WhitePawn && piece < (int)WhiteMan ||\r
+ piece >= (int)BlackPawn && piece < (int)BlackMan ) {\r
selection = (ChessSquare) (PROMOTED piece);\r
- } else if(piece == EmptySquare) selection = WhiteWazir;\r
+ } else if(piece == EmptySquare) selection = WhiteSilver;\r
else selection = (ChessSquare)((int)piece - 1);\r
goto defaultlabel;\r
\r
case DemotePiece:\r
- if(piece >= (int)WhiteUnicorn && piece < (int)WhiteKing ||\r
- piece >= (int)BlackUnicorn && piece < (int)BlackKing ) {\r
+ if(piece > (int)WhiteMan && piece <= (int)WhiteKing ||\r
+ piece > (int)BlackMan && piece <= (int)BlackKing ) {\r
selection = (ChessSquare) (DEMOTED piece);\r
- } else if( piece == WhiteKing || piece == BlackKing )\r
- selection = (ChessSquare)((int)piece - (int)WhiteKing + (int)WhiteMan);\r
- else if(piece == EmptySquare) selection = BlackWazir;\r
+ } else if(piece == EmptySquare) selection = BlackSilver;\r
else selection = (ChessSquare)((int)piece + 1); \r
goto defaultlabel;\r
\r
DisplayMove(currentMove - 1);\r
DrawPosition(FALSE, boards[currentMove]);\r
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
- if (commentList[currentMove] && !matchMode && gameMode != Training) {\r
+ if ( !matchMode && gameMode != Training) { // [HGM] PV info: routine tests if empty\r
DisplayComment(currentMove - 1, commentList[currentMove]);\r
}\r
}\r
DisplayMove(currentMove - 1);\r
DrawPosition(full_redraw, boards[currentMove]);\r
HistorySet(parseList,backwardMostMove,forwardMostMove,currentMove-1);\r
- if (commentList[currentMove] != NULL) {\r
- DisplayComment(currentMove - 1, commentList[currentMove]);\r
- }\r
+ // [HGM] PV info: routine tests if comment empty\r
+ DisplayComment(currentMove - 1, commentList[currentMove]);\r
}\r
\r
void\r
p = q;\r
while (p >= prog && *p != '/' && *p != '\\') p--;\r
p++;\r
+ if(p == prog && *p == '"') p++;\r
if (q - p >= 4 && StrCaseCmp(q - 4, ".exe") == 0) q -= 4;\r
memcpy(buf, p, q - p);\r
buf[q - p] = NULLCHAR;\r
int oldlen, len;\r
char *old;\r
\r
- GetInfoFromComment( index, text );\r
+ text = GetInfoFromComment( index, text ); /* [HGM] PV time: strip PV info from comment */\r
\r
CrushCRs(text);\r
while (*text == '\n') text++;\r
}\r
\r
/* [AS] Try to extract PV info from PGN comment */\r
-void GetInfoFromComment( int index, char * text )\r
+/* [HGM] PV time: and then remove it, to prevent it appearing twice */\r
+char *GetInfoFromComment( int index, char * text )\r
{\r
+ char * sep = text;\r
+\r
if( text != NULL && index > 0 ) {\r
int score = 0;\r
int depth = 0;\r
- int time = -1;\r
+ int time = -1, sec = 0;\r
char * s_eval = FindStr( text, "[%eval " );\r
char * s_emt = FindStr( text, "[%emt " );\r
\r
\r
if( s_eval != NULL ) {\r
if( sscanf( s_eval, "%d,%d%c", &score, &depth, &delim ) != 3 ) {\r
- return;\r
+ return text;\r
}\r
\r
if( delim != ']' ) {\r
- return;\r
+ return text;\r
}\r
}\r
\r
}\r
else {\r
/* We expect something like: [+|-]nnn.nn/dd */\r
- char * sep = strchr( text, '/' );\r
int score_lo = 0;\r
\r
+ sep = strchr( text, '/' );\r
if( sep == NULL || sep < (text+4) ) {\r
- return;\r
+ return text;\r
}\r
\r
- if( sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {\r
- return;\r
+ time = -1; sec = -1;\r
+ if( sscanf( text, "%d.%d/%d %d:%d", &score, &score_lo, &depth, &time, &sec ) != 5 &&\r
+ sscanf( text, "%d.%d/%d %d", &score, &score_lo, &depth, &time ) != 4 &&\r
+ sscanf( text, "%d.%d/%d", &score, &score_lo, &depth ) != 3 ) {\r
+ return text;\r
}\r
\r
if( score_lo < 0 || score_lo >= 100 ) {\r
- return;\r
+ return text;\r
}\r
\r
+ if(sec >= 0) time = 60*time + sec;\r
score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;\r
+\r
+ /* [HGM] PV time: now locate end of PV info */\r
+ while( *++sep >= '0' && *sep <= '9'); // strip depth\r
+ if(time >= 0)\r
+ while( *++sep >= '0' && *sep <= '9'); // strip time\r
+ if(sec >= 0)\r
+ while( *++sep >= '0' && *sep <= '9'); // strip seconds\r
+ while(*sep == ' ') sep++;\r
}\r
\r
if( depth <= 0 ) {\r
- return;\r
+ return text;\r
}\r
\r
if( time < 0 ) {\r
\r
pvInfoList[index-1].depth = depth;\r
pvInfoList[index-1].score = score;\r
- pvInfoList[index-1].time = time;\r
+ pvInfoList[index-1].time = time;\r
}\r
+ return sep;\r
}\r
\r
void\r
\r
count = strlen(message);\r
outCount = OutputToProcess(cps->pr, message, count, &error);\r
- if (outCount < count && !exiting) {\r
+ if (outCount < count && !exiting \r
+ && !endingGame) { /* [HGM] crash: to not hang GameEnds() writing to deceased engines */\r
sprintf(buf, "Error writing to %s chess program", cps->which);\r
- DisplayFatalError(buf, error, 1);\r
+ if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
+ if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
+ gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
+ sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program);\r
+ } else {\r
+ gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;\r
+ }\r
+ gameInfo.resultDetails = buf;\r
+ }\r
+ DisplayFatalError(buf, error, 1);\r
}\r
}\r
\r
sprintf(buf,\r
"Error: %s chess program (%s) exited unexpectedly",\r
cps->which, cps->program);\r
+ if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */\r
+ if(epStatus[forwardMostMove] <= EP_DRAWS) {\r
+ gameInfo.result = GameIsDrawn; /* [HGM] accept exit as draw claim */\r
+ sprintf(buf, "%s program exits in draw position (%s)", cps->which, cps->program);\r
+ } else {\r
+ gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;\r
+ }\r
+ gameInfo.resultDetails = buf;\r
+ }\r
RemoveInputSource(cps->isr);\r
DisplayFatalError(buf, 0, 1);\r
} else {\r
\r
DisplayFatalError(buf, error, 1);\r
}\r
- GameEnds((ChessMove) 0, NULL, GE_PLAYER);\r
return;\r
}\r
\r
long tc;\r
{\r
char buf[MSG_SIZ];\r
- int seconds = (tc / 1000) % 60;\r
+ int seconds;\r
\r
if( timeControl_2 > 0 ) {\r
if( (gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b') ) {\r
tc = timeControl_2;\r
}\r
}\r
+ tc /= cps->timeOdds; /* [HGM] time odds: apply before telling engine */\r
+ inc /= cps->timeOdds;\r
+ st /= cps->timeOdds;\r
+\r
+ seconds = (tc / 1000) % 60; /* [HGM] displaced to after applying odds */\r
\r
if (st > 0) {\r
/* Set exact time per move, normally using st command */\r
}\r
}\r
\r
+ChessProgramState *WhitePlayer()\r
+/* [HGM] return pointer to 'first' or 'second', depending on who plays white */\r
+{\r
+ if(gameMode == TwoMachinesPlay && first.twoMachinesColor[0] == 'b')\r
+ return &second;\r
+ return &first;\r
+}\r
+\r
void\r
SendTimeRemaining(cps, machineWhite)\r
ChessProgramState *cps;\r
time = blackTimeRemaining / 10;\r
otime = whiteTimeRemaining / 10;\r
}\r
+ /* [HGM] translate opponent's time by time-odds factor */\r
+ otime = (otime * cps->other->timeOdds) / cps->timeOdds;\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "time odds: %d %d \n", cps->timeOdds, cps->other->timeOdds);\r
+ }\r
+\r
if (time <= 0) time = 1;\r
if (otime <= 0) otime = 1;\r
\r
if (BoolFeature(&p, "ics", &cps->sendICS, cps)) continue;\r
if (BoolFeature(&p, "name", &cps->sendName, cps)) continue;\r
if (BoolFeature(&p, "pause", &val, cps)) continue; /* unused at present */\r
+ if (BoolFeature(&p, "debug", &cps->debug, cps)) continue;\r
+ if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue;\r
if (IntFeature(&p, "done", &val, cps)) {\r
FeatureDone(cps, val);\r
continue;\r
char *text;\r
{\r
char title[MSG_SIZ];\r
+ char buf[8000]; // comment can be long!\r
+ int score, depth;\r
\r
if( appData.autoDisplayComment ) {\r
if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {\r
WhiteOnMove(moveNumber) ? " " : ".. ",\r
parseList[moveNumber]);\r
}\r
-\r
+ } else title[0] = 0;\r
+\r
+ // [HGM] PV info: display PV info together with (or as) comment\r
+ if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {\r
+ if(text == NULL) text = ""; \r
+ score = pvInfoList[moveNumber].score;\r
+ sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,\r
+ depth, pvInfoList[moveNumber].time, text);\r
+ CommentPopUp(title, buf);\r
+ } else\r
+ if (text != NULL)\r
CommentPopUp(title, text);\r
- }\r
}\r
\r
/* This routine sends a ^C interrupt to gnuchess, to awaken it if it\r
if (!appData.clockMode || appData.icsActive ||\r
gameMode == PlayFromGameFile || forwardMostMove == 0) return;\r
\r
- if (timeIncrement >= 0) {\r
- if (WhiteOnMove(forwardMostMove)) {\r
- blackTimeRemaining += timeIncrement;\r
- } else {\r
- whiteTimeRemaining += timeIncrement;\r
- }\r
- }\r
/*\r
- * add time to clocks when time control is achieved\r
+ * add time to clocks when time control is achieved ([HGM] now also used fot increment)\r
*/\r
- if (movesPerSession) {\r
- switch ((forwardMostMove + 1) % (movesPerSession * 2)) {\r
- case 0:\r
+ if ( !WhiteOnMove(forwardMostMove) )\r
/* White made time control */\r
- whiteTimeRemaining += GetTimeControlForWhite();\r
- break;\r
- case 1:\r
+ whiteTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)\r
+ /* [HGM] time odds: correct new time quota for time odds! */\r
+ / WhitePlayer()->timeOdds;\r
+ else\r
/* Black made time control */\r
- blackTimeRemaining += GetTimeControlForBlack();\r
- break;\r
- default:\r
- break;\r
- }\r
- }\r
+ blackTimeRemaining += GetTimeQuota((forwardMostMove-1)/2)\r
+ / WhitePlayer()->other->timeOdds;\r
}\r
\r
void\r
(void) StopClockTimer();\r
if (appData.icsActive) {\r
whiteTimeRemaining = blackTimeRemaining = 0;\r
- } else {\r
- whiteTimeRemaining = GetTimeControlForWhite();\r
- blackTimeRemaining = GetTimeControlForBlack();\r
+ } else { /* [HGM] correct new time quote for time odds */\r
+ whiteTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->timeOdds;\r
+ blackTimeRemaining = GetTimeQuota(-1) / WhitePlayer()->other->timeOdds;\r
}\r
if (whiteFlag || blackFlag) {\r
DisplayTitle("");\r
lastTickLength = SubtractTimeMarks(&now, &tickStartTM);\r
if (WhiteOnMove(forwardMostMove)) {\r
blackTimeRemaining -= lastTickLength;\r
+ /* [HGM] PGNtime: save time for PGN file if engine did not give it */\r
+ if(pvInfoList[forwardMostMove-1].time == -1)\r
+ pvInfoList[forwardMostMove-1].time = \r
+ (timeRemaining[1][forwardMostMove-1] - blackTimeRemaining)/10;\r
} else {\r
whiteTimeRemaining -= lastTickLength;\r
+ /* [HGM] PGNtime: save time for PGN file if engine did not give it */\r
+ if(pvInfoList[forwardMostMove-1].time == -1)\r
+ pvInfoList[forwardMostMove-1].time = \r
+ (timeRemaining[0][forwardMostMove-1] - whiteTimeRemaining)/10;\r
}\r
- /* [HGM] save time for PGN file if engine did not give it */\r
- if(pvInfoList[forwardMostMove-1].time == -1)\r
- pvInfoList[forwardMostMove-1].time = lastTickLength/100;\r
flagged = CheckFlags();\r
}\r
CheckTimeControl();\r
}\r
*(p - 1) = ' ';\r
\r
+ /* [HGM] print Crazyhouse or Shogi holdings */\r
+ if( gameInfo.holdingsWidth ) {\r
+ *(p-1) = '['; /* if we wanted to support BFEN, this could be '/' */\r
+ q = p;\r
+ for(i=0; i<gameInfo.holdingsSize; i++) { /* white holdings */\r
+ piece = boards[move][i][BOARD_WIDTH-1];\r
+ if( piece != EmptySquare )\r
+ for(j=0; j<(int) boards[move][i][BOARD_WIDTH-2]; j++)\r
+ *p++ = PieceToChar(piece);\r
+ }\r
+ for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */\r
+ piece = boards[move][BOARD_HEIGHT-i-1][0];\r
+ if( piece != EmptySquare )\r
+ for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)\r
+ *p++ = PieceToChar(piece);\r
+ }\r
+\r
+ if( q == p ) *p++ = '-';\r
+ *p++ = ']';\r
+ *p++ = ' ';\r
+ }\r
+\r
/* Active color */\r
*p++ = whiteToPlay ? 'w' : 'b';\r
*p++ = ' ';\r
\r
if(nrCastlingRights) {\r
- /* HACK: we don't keep track of castling availability, so fake it! */\r
- /* Tord! please fix with the aid of castlingRights[move][...] */\r
-\r
- /* PUSH Fabien & Tord */\r
-\r
- /* Declare all potential FRC castling rights (conservative) */\r
- /* outermost rook on each side of the king */\r
-\r
- if( gameInfo.variant == VariantFischeRandom ) {\r
- int fk, fr;\r
-\r
- q = p;\r
+ q = p;\r
+ if(gameInfo.variant == VariantFischeRandom) {\r
+ /* [HGM] write directly from rights */\r
+ if(castlingRights[move][2] >= 0 &&\r
+ castlingRights[move][0] >= 0 )\r
+ *p++ = castlingRights[move][0] + AAA + 'A' - 'a';\r
+ if(castlingRights[move][2] >= 0 &&\r
+ castlingRights[move][1] >= 0 )\r
+ *p++ = castlingRights[move][1] + AAA + 'A' - 'a';\r
+ if(castlingRights[move][5] >= 0 &&\r
+ castlingRights[move][3] >= 0 )\r
+ *p++ = castlingRights[move][3] + AAA;\r
+ if(castlingRights[move][5] >= 0 &&\r
+ castlingRights[move][4] >= 0 )\r
+ *p++ = castlingRights[move][4] + AAA;\r
+ } else {\r
\r
- /* White castling rights */\r
-\r
- for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-1; fk++) {\r
-\r
- if (boards[move][0][fk] == WhiteKing) {\r
-\r
- for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */\r
- if (boards[move][0][fr] == WhiteRook) {\r
- *p++ = useFEN960 ? 'A' + fr : 'K';\r
- break;\r
- }\r
- }\r
-\r
- for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */\r
- if (boards[move][0][fr] == WhiteRook) {\r
- *p++ = useFEN960 ? 'A' + fr : 'Q';\r
- break;\r
- }\r
- }\r
- }\r
- }\r
-\r
- /* Black castling rights */\r
-\r
- for (fk = BOARD_LEFT+1; fk < BOARD_RGHT-1; fk++) {\r
-\r
- if (boards[move][BOARD_HEIGHT-1][fk] == BlackKing) {\r
-\r
- for (fr = BOARD_RGHT-1; fr > fk; fr--) { /* H side */\r
- if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
- *p++ = useFEN960 ? 'a' + fr : 'k';\r
- break;\r
- }\r
- }\r
-\r
- for (fr = BOARD_LEFT; fr < fk; fr++) { /* A side */\r
- if (boards[move][BOARD_HEIGHT-1][fr] == BlackRook) {\r
- *p++ = useFEN960 ? 'a' + fr : 'q';\r
- break;\r
- }\r
- }\r
- }\r
- }\r
-\r
- if (q == p) *p++ = '-'; /* No castling rights */\r
- *p++ = ' ';\r
- }\r
- else {\r
- q = p;\r
-\r
-#ifdef OLDCASTLINGCODE\r
- if (boards[move][0][BOARD_WIDTH>>1] == WhiteKing) {\r
- if (boards[move][0][BOARD_RGHT-1] == WhiteRook) *p++ = 'K';\r
- if (boards[move][0][BOARD_LEFT] == WhiteRook) *p++ = 'Q';\r
- }\r
- if (boards[move][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == BlackKing) {\r
- if (boards[move][BOARD_HEIGHT-1][BOARD_HEIGHT-1] == BlackRook) *p++ = 'k';\r
- if (boards[move][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook) *p++ = 'q';\r
- } \r
-#else\r
/* [HGM] write true castling rights */\r
if( nrCastlingRights == 6 ) {\r
if(castlingRights[move][0] == BOARD_RGHT-1 &&\r
if(castlingRights[move][4] == BOARD_LEFT &&\r
castlingRights[move][5] >= 0 ) *p++ = 'q';\r
}\r
-#endif\r
- if (q == p) *p++ = '-';\r
- *p++ = ' ';\r
- }\r
-\r
- /* POP Fabien & Tord */\r
+ }\r
+ if (q == p) *p++ = '-'; /* No castling rights */\r
+ *p++ = ' ';\r
}\r
\r
if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi &&\r
*p++ = ' ';\r
}\r
\r
- /* [HGM] print Crazyhouse or Shogi holdings */\r
- if( gameInfo.holdingsWidth ) {\r
- q = p;\r
- for(i=0; i<gameInfo.holdingsSize; i++) { /* white holdings */\r
- piece = boards[move][i][BOARD_WIDTH-1];\r
- if( piece != EmptySquare )\r
- for(j=0; j<(int) boards[move][i][BOARD_WIDTH-2]; j++)\r
- *p++ = PieceToChar(piece);\r
- }\r
- for(i=0; i<gameInfo.holdingsSize; i++) { /* black holdings */\r
- piece = boards[move][BOARD_HEIGHT-i-1][0];\r
- if( piece != EmptySquare )\r
- for(j=0; j<(int) boards[move][BOARD_HEIGHT-i-1][1]; j++)\r
- *p++ = PieceToChar(piece);\r
- }\r
-\r
- if( q == p ) *p++ = '-';\r
- *p++ = ' ';\r
- }\r
-\r
/* [HGM] find reversible plies */\r
{ int i = 0, j=move;\r
\r
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {\r
j = 0;\r
for (;;) {\r
- if (*p == '/' || *p == ' ') {\r
- if (*p == '/') p++;\r
+ if (*p == '/' || *p == ' ' || (*p == '[' && i == 0) ) {\r
+ if (*p == '/') p++;\r
emptycount = gameInfo.boardWidth - j;\r
while (emptycount--)\r
board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;\r
}\r
while (*p == '/' || *p == ' ') p++;\r
\r
+ /* [HGM] look for Crazyhouse holdings here */\r
+ while(*p==' ') p++;\r
+ if( gameInfo.holdingsWidth && p[-1] == '/' || *p == '[') {\r
+ if(*p == '[') p++;\r
+ if(*p == '-' ) *p++; /* empty holdings */ else {\r
+ if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */\r
+ /* if we would allow FEN reading to set board size, we would */\r
+ /* have to add holdings and shift the board read so far here */\r
+ while( (piece = CharToPiece(*p) ) != EmptySquare ) {\r
+ *p++;\r
+ if((int) piece >= (int) BlackPawn ) {\r
+ i = (int)piece - (int)BlackPawn;\r
+ if( i >= BOARD_HEIGHT ) return FALSE;\r
+ board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */\r
+ board[BOARD_HEIGHT-1-i][1]++; /* black counts */\r
+ } else {\r
+ i = (int)piece - (int)WhitePawn;\r
+ if( i >= BOARD_HEIGHT ) return FALSE;\r
+ board[i][BOARD_WIDTH-1] = piece; /* white holdings */\r
+ board[i][BOARD_WIDTH-2]++; /* black holdings */\r
+ }\r
+ }\r
+ }\r
+ if(*p == ']') *p++;\r
+ }\r
+\r
+ while(*p == ' ') p++;\r
+\r
/* Active color */\r
switch (*p++) {\r
case 'w':\r
/* set defaults in case FEN is incomplete */\r
FENepStatus = EP_UNKNOWN;\r
for(i=0; i<nrCastlingRights; i++ ) {\r
- FENcastlingRights[i] = initialRights[i];\r
+ FENcastlingRights[i] =\r
+ gameInfo.variant == VariantFischeRandom ? -1 : initialRights[i];\r
} /* assume possible unless obviously impossible */\r
if(initialRights[0]>=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1;\r
if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1;\r
FENcastlingRights[i] = -1;\r
}\r
}\r
- while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-') {\r
- switch(*p++) {\r
+ while(*p=='K' || *p=='Q' || *p=='k' || *p=='q' || *p=='-' ||\r
+ gameInfo.variant == VariantFischeRandom &&\r
+ ( *p >= 'a' && *p < 'a' + gameInfo.boardWidth) ||\r
+ ( *p >= 'A' && *p < 'A' + gameInfo.boardWidth) ) {\r
+ char c = *p++; int whiteKingFile=-1, blackKingFile=-1;\r
+\r
+ for(i=BOARD_LEFT; i<BOARD_RGHT; i++) {\r
+ if(board[BOARD_HEIGHT-1][i] == BlackKing) blackKingFile = i;\r
+ if(board[0 ][i] == WhiteKing) whiteKingFile = i;\r
+ }\r
+ switch(c) {\r
case'K':\r
- FENcastlingRights[0] = BOARD_RGHT-1;\r
- FENcastlingRights[2] = BOARD_WIDTH>>1;\r
+ for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--);\r
+ FENcastlingRights[0] = i != whiteKingFile ? i : -1;\r
+ FENcastlingRights[2] = whiteKingFile;\r
break;\r
case'Q':\r
- FENcastlingRights[1] = BOARD_LEFT;\r
- FENcastlingRights[2] = BOARD_WIDTH>>1;\r
+ for(i=BOARD_LEFT; board[0][i]!=WhiteRook && i<whiteKingFile; i++);\r
+ FENcastlingRights[1] = i != whiteKingFile ? i : -1;\r
+ FENcastlingRights[2] = whiteKingFile;\r
break;\r
case'k':\r
- FENcastlingRights[3] = BOARD_RGHT-1;\r
- FENcastlingRights[5] = BOARD_WIDTH>>1;\r
+ for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--);\r
+ FENcastlingRights[3] = i != blackKingFile ? i : -1;\r
+ FENcastlingRights[5] = blackKingFile;\r
break;\r
case'q':\r
- FENcastlingRights[4] = BOARD_LEFT;\r
- FENcastlingRights[5] = BOARD_WIDTH>>1;\r
+ for(i=BOARD_LEFT; board[BOARD_HEIGHT-1][i]!=BlackRook && i<blackKingFile; i++);\r
+ FENcastlingRights[4] = i != blackKingFile ? i : -1;\r
+ FENcastlingRights[5] = blackKingFile;\r
+ case '-':\r
break;\r
- /* Tord! FRC! */\r
+ default: /* FRC castlings */\r
+ if(c >= 'a') { /* black rights */\r
+ for(i=BOARD_LEFT; i<BOARD_RGHT; i++)\r
+ if(board[BOARD_HEIGHT-1][i] == BlackKing) break;\r
+ if(i == BOARD_RGHT) break;\r
+ FENcastlingRights[5] = i;\r
+ c -= AAA;\r
+ if(board[BOARD_HEIGHT-1][c] < BlackPawn ||\r
+ board[BOARD_HEIGHT-1][c] >= BlackKing ) break;\r
+ if(c > i)\r
+ FENcastlingRights[3] = c;\r
+ else\r
+ FENcastlingRights[4] = c;\r
+ } else { /* white rights */\r
+ for(i=BOARD_LEFT; i<BOARD_RGHT; i++)\r
+ if(board[0][i] == WhiteKing) break;\r
+ if(i == BOARD_RGHT) break;\r
+ FENcastlingRights[2] = i;\r
+ c -= AAA - 'a' + 'A';\r
+ if(board[0][c] >= WhiteKing) break;\r
+ if(c > i)\r
+ FENcastlingRights[0] = c;\r
+ else\r
+ FENcastlingRights[1] = c;\r
+ }\r
}\r
}\r
+ if (appData.debugMode) {\r
+ fprintf(debugFP, "FEN castling rights:");\r
+ for(i=0; i<nrCastlingRights; i++)\r
+ fprintf(debugFP, " %d", FENcastlingRights[i]);\r
+ fprintf(debugFP, "\n");\r
+ }\r
\r
while(*p==' ') p++;\r
}\r
}\r
}\r
\r
- /* [HGM] look for Crazyhouse holdings here */\r
- while(*p==' ') p++;\r
- if( gameInfo.holdingsWidth ) {\r
- if(*p == '-' ) *p++; /* empty holdings */ else {\r
- if( !gameInfo.holdingsWidth ) return FALSE; /* no room to put holdings! */\r
- /* if we would allow FEN reading to set board size, we would */\r
- /* have to add holdings and shift the board read so far here */\r
- while( (piece = CharToPiece(*p) ) != EmptySquare ) {\r
- *p++;\r
- if((int) piece >= (int) BlackPawn ) {\r
- i = (int)piece - (int)BlackPawn;\r
- if( i >= BOARD_HEIGHT ) return FALSE;\r
- board[BOARD_HEIGHT-1-i][0] = piece; /* black holdings */\r
- board[BOARD_HEIGHT-1-i][1]++; /* black counts */\r
- } else {\r
- i = (int)piece - (int)WhitePawn;\r
- if( i >= BOARD_HEIGHT ) return FALSE;\r
- board[i][BOARD_WIDTH-1] = piece; /* white holdings */\r
- board[i][BOARD_WIDTH-2]++; /* black holdings */\r
- }\r
- }\r
- }\r
- }\r
-\r
\r
if(sscanf(p, "%d", &i) == 1) {\r
FENrulePlies = i; /* 50-move ply counter */\r