#else
+#include <sys/file.h>
#define DoSleep( n ) if( (n) >= 0) sleep(n)
#define SLASH '/'
{
char buf[MSG_SIZ];
+ if(moveList[moveNum][1] == '@' && moveList[moveNum][0] == '@') {
+ // null move in variant where engine does not understand it (for analysis purposes)
+ SendBoard(cps, moveNum + 1); // send position after move in stead.
+ return;
+ }
if (cps->useUsermove) {
SendToProgram("usermove ", cps);
}
} else
if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
if(moveList[moveNum][1] == '@' && (BOARD_HEIGHT < 16 || moveList[moveNum][0] <= 'Z')) { // drop move
+ if(moveList[moveNum][0]== '@') snprintf(buf, MSG_SIZ, "@@@@\n"); else
snprintf(buf, MSG_SIZ, "%c@%c%d%s", moveList[moveNum][0],
moveList[moveNum][2], moveList[moveNum][3] - '0', moveList[moveNum]+4);
} else
char move[7];
{
if (rf == DROP_RANK) {
+ if(ff == EmptySquare) sprintf(move, "@@@@\n"); else // [HGM] pass
sprintf(move, "%c@%c%c\n",
ToUpper(PieceToChar((ChessSquare) ff)), AAA + ft, ONE + rt);
} else {
(int)from_piece < (int)BlackPawn; /* [HGM] can be > King! */
switch (gameMode) {
- case PlayFromGameFile:
case AnalyzeFile:
case TwoMachinesPlay:
case EndOfGame:
}
break;
+ case PlayFromGameFile:
+ if(!shiftKey || !appData.variations) return FALSE; // [HGM] allow starting variation in this mode
case EditGame:
if (!white_piece && WhiteOnMove(currentMove)) {
DisplayMoveError(_("It is White's turn"));
}
if (currentMove != forwardMostMove && gameMode != AnalyzeMode
&& gameMode != EditGame // [HGM] vari: treat as AnalyzeMode
+ && gameMode != PlayFromGameFile // [HGM] as EditGame, with protected main line
&& gameMode != AnalyzeFile && gameMode != Training) {
DisplayMoveError(_("Displayed position is not current"));
return FALSE;
*/
switch (gameMode) {
- case PlayFromGameFile:
case AnalyzeFile:
case TwoMachinesPlay:
case EndOfGame:
}
break;
+ case PlayFromGameFile:
+ if(!shiftKey ||!appData.variations) return; // [HGM] only variations
case EditGame:
case IcsExamining:
case BeginningOfGame:
/* [HGM] always test for legality, to get promotion info */
moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
fromY, fromX, toY, toX, promoChar);
+
+ if(fromY == DROP_RANK && fromX == EmptySquare && (gameMode == AnalyzeMode || gameMode == EditGame)) moveType = NormalMove;
+
/* [HGM] but possibly ignore an IllegalMove result */
if (appData.testLegality) {
if (moveType == IllegalMove || moveType == ImpossibleMove) {
/* Ok, now we know that the move is good, so we can kill
the previous line in Analysis Mode */
- if ((gameMode == AnalyzeMode || gameMode == EditGame)
+ if ((gameMode == AnalyzeMode || gameMode == EditGame || gameMode == PlayFromGameFile && appData.variations && shiftKey)
&& currentMove < forwardMostMove) {
if(appData.variations && shiftKey) PushTail(currentMove, forwardMostMove); // [HGM] vari: save tail of game
else forwardMostMove = currentMove;
// [HGM] book: if program might be playing, let it use book
bookHit = SendMoveToBookUser(forwardMostMove-1, &first, FALSE);
first.maybeThinking = TRUE;
+ } else if(fromY == DROP_RANK && fromX == EmptySquare) {
+ if(!first.useSetboard) SendToProgram("undo\n", &first); // kludge to change stm in engines that do not support setboard
+ SendBoard(&first, currentMove+1);
} else SendMoveToProgram(forwardMostMove-1, &first);
if (currentMove == cmailOldMove + 1) {
cmailMoveType[lastLoadGameNumber - 1] = CMAIL_MOVE;
MarkTargetSquares(int clear)
{
int x, y;
- if(!appData.markers || !appData.highlightDragging ||
+ if(!appData.markers || !appData.highlightDragging || appData.icsActive && gameInfo.variant < VariantShogi ||
!appData.testLegality || gameMode == EditPosition) return;
if(clear) {
for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) marker[y][x] = 0;
toX = xSqr; toY = ySqr; lastX = x, lastY = y;
if(flipView) toX = BOARD_WIDTH - 1 - toX; else toY = BOARD_HEIGHT - 1 - toY;
NextPiece(0);
- return -2;
+ return 2; // grab
case IcsObserving:
if(!appData.icsEngineAnalyze) return -1;
case IcsPlayingWhite:
oldEP = (signed char)board[EP_STATUS];
board[EP_STATUS] = EP_NONE;
- if( board[toY][toX] != EmptySquare )
- board[EP_STATUS] = EP_CAPTURE;
-
if (fromY == DROP_RANK) {
/* must be first */
+ if(fromX == EmptySquare) { // [HGM] pass: empty drop encodes null move; nothing to change.
+ board[EP_STATUS] = EP_CAPTURE; // null move considered irreversible
+ return;
+ }
piece = board[toY][toX] = (ChessSquare) fromX;
} else {
int i;
+ if( board[toY][toX] != EmptySquare )
+ board[EP_STATUS] = EP_CAPTURE;
+
if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) {
if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi )
board[EP_STATUS] = EP_PAWN_MOVE; // Lance is Pawn-like in most variants
redraw, init, gameMode);
}
CleanupTail(); // [HGM] vari: delete any stored variations
+ CommentPopDown(); // [HGM] make sure no comments to the previous game keep hanging on
pausing = pauseExamInvalid = FALSE;
startedFromSetupPosition = blackPlaysFirst = FALSE;
firstMove = TRUE;
if (currentMove >= forwardMostMove) {
if(gameMode == AnalyzeFile) { ExitAnalyzeMode(); SendToProgram("force\n", &first); }
- gameMode = EditGame;
- ModeHighlight();
+// gameMode = EndOfGame;
+// ModeHighlight();
/* [AS] Clear current move marker at the end of a game */
/* HistorySet(parseList, backwardMostMove, forwardMostMove, -1); */
if (matchMode || appData.timeDelay == 0) {
ToEndEvent();
- gameMode = EditGame;
- ModeHighlight();
} else if (appData.timeDelay > 0) {
AutoPlayGameLoop();
}
if (gameMode == EditPosition || gameMode == IcsExamining) {
if(!appData.pieceMenu && blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0);
SetBlackToPlayEvent();
- } else if (gameMode == EditGame || shiftKey) {
+ } else if ((gameMode == AnalyzeMode || gameMode == EditGame) && !blackFlag && WhiteOnMove(currentMove)) {
+ UserMoveEvent((int)EmptySquare, DROP_RANK, 0, 0, 0); // [HGM] multi-move: if not out of time, enters null move
+ } else if (shiftKey) {
AdjustClock(which, -1);
} else if (gameMode == IcsPlayingWhite ||
gameMode == MachinePlaysBlack) {
if (gameMode == EditPosition || gameMode == IcsExamining) {
if(!appData.pieceMenu && !blackPlaysFirst) EditPositionMenuEvent(ClearBoard, 0, 0);
SetWhiteToPlayEvent();
- } else if (gameMode == EditGame || shiftKey) {
+ } else if ((gameMode == AnalyzeMode || gameMode == EditGame) && !whiteFlag && !WhiteOnMove(currentMove)) {
+ UserMoveEvent((int)EmptySquare, DROP_RANK, 0, 0, 0); // [HGM] multi-move
+ } else if (shiftKey) {
AdjustClock(which, -1);
} else if (gameMode == IcsPlayingBlack ||
gameMode == MachinePlaysWhite) {
if (gameMode == EditGame || gameMode==AnalyzeMode ||
gameMode == PlayFromGameFile || gameMode == AnalyzeFile) {
while (currentMove > target) {
+ if(moveList[currentMove-1][1] == '@' && moveList[currentMove-1][0] == '@') {
+ // null move cannot be undone. Reload program with move history before it.
+ int i;
+ for(i=target; i>backwardMostMove; i--) { // seek back to start or previous null move
+ if(moveList[i-1][1] == '@' && moveList[i-1][0] == '@') break;
+ }
+ SendBoard(&first, i);
+ for(currentMove=i; currentMove<target; currentMove++) SendMoveToProgram(currentMove, &first);
+ break;
+ }
SendToProgram("undo\n", &first);
currentMove--;
}
char *text;
{
char title[MSG_SIZ];
- char buf[8000]; // comment can be long!
- int score, depth;
if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
safeStrCpy(title, "Comment", sizeof(title)/sizeof(title[0]));
WhiteOnMove(moveNumber) ? " " : ".. ",
parseList[moveNumber]);
}
- // [HGM] PV info: display PV info together with (or as) comment
- if(moveNumber >= 0 && (depth = pvInfoList[moveNumber].depth) > 0) {
- if(text == NULL) text = "";
- score = pvInfoList[moveNumber].score;
- snprintf(buf,sizeof(buf)/sizeof(buf[0]), "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,
- depth, (pvInfoList[moveNumber].time+50)/100, text);
- text = buf;
- }
if (text != NULL && (appData.autoDisplayComment || commentUp))
CommentPopUp(title, text);
}
}
// [HGM] vari: routines for shelving variations
+Boolean modeRestore = FALSE;
void
PushInner(int firstMove, int lastMove)
PushInner(firstMove, lastMove);
if(storedGames == 1) GreyRevert(FALSE);
+ if(gameMode == PlayFromGameFile) gameMode = EditGame, modeRestore = TRUE;
}
void
int i, j, nrMoves;
char buf[8000], moveBuf[20];
- storedGames--;
- ToNrEvent(savedFirst[storedGames]); // sets currentMove
+ ToNrEvent(savedFirst[storedGames-1]); // sets currentMove
+ storedGames--; // do this after ToNrEvent, to make sure HistorySet will refresh entire game after PopInner returns
nrMoves = savedLast[storedGames] - currentMove;
if(annotate) {
int cnt = 10;
CommentPopDown(); // make sure no stale variation comments to the destroyed line can remain open
PopInner(annotate);
+ if(currentMove < forwardMostMove) ForwardEvent(); else
+ HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
- if(storedGames == 0) GreyRevert(TRUE);
+ if(storedGames == 0) { GreyRevert(TRUE); if(modeRestore) modeRestore = FALSE, gameMode = PlayFromGameFile; }
return TRUE;
}
char *p = text, *start = NULL, *end = NULL, wait = NULLCHAR;
int level = 0, move;
- if(gameMode != EditGame && gameMode != AnalyzeMode) return;
+ if(gameMode != EditGame && gameMode != AnalyzeMode && gameMode != PlayFromGameFile) return;
// first find outermost bracketing variation
while(*p) { // hope I got this right... Non-nesting {} and [] can screen each other and nesting ()
if(!wait) { // while inside [] pr {}, ignore everyting except matching closing ]}