void BackwardInner P((int target));
void ForwardInner P((int target));
void GameEnds P((ChessMove result, char *resultDetails, int whosays));
-void EditPositionDone P((void));
+void EditPositionDone P((Boolean fakeRights));
void PrintOpponents P((FILE *fp));
void PrintPosition P((FILE *fp, int move));
void StartChessProgram P((ChessProgramState *cps));
void ResurrectChessProgram P((void));
void DisplayComment P((int moveNumber, char *text));
void DisplayMove P((int moveNumber));
-void DisplayAnalysis P((void));
void ParseGameHistory P((char *game));
void ParseBoard12 P((char *string));
+void KeepAlive P((void));
void StartClocks P((void));
void SwitchClocks P((void));
void StopClocks P((void));
int whiteNPS, blackNPS; /* [HGM] nps: for easily making clocks aware of NPS */
VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */
+Boolean connectionAlive;/* [HGM] alive: ICS connection status from probing */
int opponentKibitzes;
int lastSavedGame; /* [HGM] save: ID of game */
char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options
GetTimeMark(&programStartTime);
- srand(programStartTime.ms); // [HGM] book: makes sure random is unpredictabe to msec level
+ srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level
ClearProgramStats();
programStats.ok_to_send = 1;
/* [HGM] time odds: set factor for each machine */
first.timeOdds = appData.firstTimeOdds;
second.timeOdds = appData.secondTimeOdds;
- { int norm = 1;
+ { float norm = 1;
if(appData.timeOddsMode) {
norm = first.timeOdds;
if(norm > second.timeOdds) norm = second.timeOdds;
fprintf(debugFP, "%s\n", programVersion);
}
+ set_cont_sequence(appData.wrapContSeq);
if (appData.matchGames > 0) {
appData.matchMode = TRUE;
} else if (appData.matchMode) {
AddInputSource(icsPR, FALSE, read_from_ics, &telnetISR);
fromUserISR =
AddInputSource(NoProc, FALSE, read_from_player, &fromUserISR);
+ if(appData.keepAlive) // [HGM] alive: schedule sending of dummy 'date' command
+ ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
} else if (appData.noChessProgram) {
SetNCPMode();
} else {
void
KeepAlive()
{ // [HGM] alive: periodically send dummy (date) command to ICS to prevent time-out
+ if(!connectionAlive) DisplayFatalError("No response from ICS", 0, 1);
+ connectionAlive = FALSE; // only sticks if no response to 'date' command.
SendToICS("date\n");
if(appData.keepAlive) ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
}
ChessSquare piece;
if(gameInfo.holdingsWidth < 2) return;
+ if(gameInfo.variant != VariantBughouse && board[BOARD_SIZE-1][BOARD_SIZE-2])
+ return; // prevent overwriting by pre-board holdings
if( (int)lowestPiece >= BlackPawn ) {
holdingsColumn = 0;
board[holdingsStartRow+j*direction][holdingsColumn] = piece;
board[holdingsStartRow+j*direction][countsColumn]++;
}
-
}
VariantSwitch(Board board, VariantClass newVariant)
{
int newHoldingsWidth, newWidth = 8, newHeight = 8, i, j;
+ Board oldBoard;
startedFromPositionFile = FALSE;
if(gameInfo.variant == newVariant) return;
case VariantSuper:
newHoldingsWidth = 2;
gameInfo.holdingsSize = 8;
- return;
+ break;
case VariantGothic:
case VariantCapablanca:
case VariantCapaRandom:
gameInfo.holdingsWidth = newHoldingsWidth;
gameInfo.variant = newVariant;
InitDrawingSizes(-2, 0);
+ } else gameInfo.variant = newVariant;
+ CopyBoard(oldBoard, board); // remember correctly formatted board
InitPosition(FALSE); /* this sets up board[0], but also other stuff */
- } else { gameInfo.variant = newVariant; InitPosition(FALSE); }
-
- DrawPosition(TRUE, boards[currentMove]);
+ DrawPosition(TRUE, currentMove ? boards[currentMove] : oldBoard);
}
static int loggedOn = FALSE;
char gs_kind[MSG_SIZ];
static char player1Name[128] = "";
static char player2Name[128] = "";
+static char cont_seq[] = "\n\\ ";
static int player1Rating = -1;
static int player2Rating = -1;
/*----------------------------*/
int count;
int error;
{
-#define BUF_SIZE 8192
+#define BUF_SIZE (16*1024) /* overflowed at 8K with "inchannel 1" on FICS? */
#define STARTED_NONE 0
#define STARTED_MOVES 1
#define STARTED_BOARD 2
static int firstTime = TRUE, intfSet = FALSE;
static ColorClass prevColor = ColorNormal;
static int savingComment = FALSE;
+ static int cmatch = 0; // continuation sequence match
+ char *bp;
char str[500];
int i, oldi;
int buf_len;
char talker[MSG_SIZ]; // [HGM] chat
int channel;
+ connectionAlive = TRUE; // [HGM] alive: I think, therefore I am...
+
if (appData.debugMode) {
if (!error) {
fprintf(debugFP, "<ICS: ");
buf[i] = buf[leftover_start + i];
}
- /* Copy in new characters, removing nulls and \r's */
- buf_len = leftover_len;
- for (i = 0; i < count; i++) {
- if (data[i] != NULLCHAR && data[i] != '\r')
- buf[buf_len++] = data[i];
- if(!appData.noJoin && buf_len >= 5 && buf[buf_len-5]=='\n' && buf[buf_len-4]=='\\' &&
- buf[buf_len-3]==' ' && buf[buf_len-2]==' ' && buf[buf_len-1]==' ') {
- buf_len -= 5; // [HGM] ICS: join continuation line of Lasker 2.2.3 server with previous
- if(buf_len == 0 || buf[buf_len-1] != ' ')
- buf[buf_len++] = ' '; // add space (assumes ICS does not break lines within word)
- }
- }
+ /* copy new characters into the buffer */
+ bp = buf + leftover_len;
+ buf_len=leftover_len;
+ for (i=0; i<count; i++)
+ {
+ // ignore these
+ if (data[i] == '\r')
+ continue;
+
+ // join lines split by ICS?
+ if (!appData.noJoin)
+ {
+ /*
+ Joining just consists of finding matches against the
+ continuation sequence, and discarding that sequence
+ if found instead of copying it. So, until a match
+ fails, there's nothing to do since it might be the
+ complete sequence, and thus, something we don't want
+ copied.
+ */
+ if (data[i] == cont_seq[cmatch])
+ {
+ cmatch++;
+ if (cmatch == strlen(cont_seq))
+ {
+ cmatch = 0; // complete match. just reset the counter
+
+ /*
+ it's possible for the ICS to not include the space
+ at the end of the last word, making our [correct]
+ join operation fuse two separate words. the server
+ does this when the space occurs at the width setting.
+ */
+ if (!buf_len || buf[buf_len-1] != ' ')
+ {
+ *bp++ = ' ';
+ buf_len++;
+ }
+ }
+ continue;
+ }
+ else if (cmatch)
+ {
+ /*
+ match failed, so we have to copy what matched before
+ falling through and copying this character. In reality,
+ this will only ever be just the newline character, but
+ it doesn't hurt to be precise.
+ */
+ strncpy(bp, cont_seq, cmatch);
+ bp += cmatch;
+ buf_len += cmatch;
+ cmatch = 0;
+ }
+ }
+
+ // copy this char
+ *bp++ = data[i];
+ buf_len++;
+ }
buf[buf_len] = NULLCHAR;
next_out = leftover_len;
sprintf(str,
"/set-quietly interface %s\n/set-quietly style 12\n",
programVersion);
- if (!appData.noJoin)
- strcat(str, "/set-quietly wrap 0\n");
} else if (ics_type == ICS_CHESSNET) {
sprintf(str, "/style 12\n");
} else {
#ifdef WIN32
strcat(str, "$iset nohighlight 1\n");
#endif
- if (!appData.noJoin)
- strcat(str, "$iset nowrap 1\n");
strcat(str, "$iset lock 1\n$style 12\n");
}
SendToICS(str);
currentMove = forwardMostMove;
ClearHighlights();/*!!could figure this out*/
flipView = appData.flipView;
- DrawPosition(FALSE, boards[currentMove]);
+ DrawPosition(TRUE, boards[currentMove]);
DisplayBothClocks();
sprintf(str, "%s vs. %s",
gameInfo.white, gameInfo.black);
strncmp(why, "Continuing ", 11) == 0) {
gs_gamenum = gamenum;
strcpy(gs_kind, strchr(why, ' ') + 1);
+ VariantSwitch(boards[currentMove], StringToVariant(gs_kind)); // [HGM] variantswitch: even before we get first board
#if ZIPPY
if (appData.zippyPlay) {
ZippyGameStart(whitename, blackname);
if (currentMove == 0 &&
gameMode == IcsPlayingWhite &&
appData.premoveWhite) {
- sprintf(str, "%s%s\n", ics_prefix,
- appData.premoveWhiteText);
+ sprintf(str, "%s\n", appData.premoveWhiteText);
if (appData.debugMode)
fprintf(debugFP, "Sending premove:\n");
SendToICS(str);
} else if (currentMove == 1 &&
gameMode == IcsPlayingBlack &&
appData.premoveBlack) {
- sprintf(str, "%s%s\n", ics_prefix,
- appData.premoveBlackText);
+ sprintf(str, "%s\n", appData.premoveBlackText);
if (appData.debugMode)
fprintf(debugFP, "Sending premove:\n");
SendToICS(str);
/* Usually suppress following prompt */
if (!(forwardMostMove == 0 && gameMode == IcsExamining)) {
+ while(looking_at(buf, &i, "\n")); // [HGM] skip empty lines
if (looking_at(buf, &i, "*% ")) {
savingComment = FALSE;
}
* to move the position two files to the right to
* create room for them!
*/
- VariantSwitch(boards[currentMove], VariantCrazyhouse); /* temp guess */
+ VariantClass newVariant;
+ switch(gameInfo.boardWidth) { // base guess on board width
+ case 9: newVariant = VariantShogi; break;
+ case 10: newVariant = VariantGreat; break;
+ default: newVariant = VariantCrazyhouse; break;
+ }
+ VariantSwitch(boards[currentMove], newVariant); /* temp guess */
/* Get a move list just to see the header, which
will tell us whether this is really bug or zh */
if (ics_getting_history == H_FALSE) {
white_holding[strlen(white_holding)-1] = NULLCHAR;
black_holding[strlen(black_holding)-1] = NULLCHAR;
/* [HGM] copy holdings to board holdings area */
- CopyHoldings(boards[currentMove], white_holding, WhitePawn);
- CopyHoldings(boards[currentMove], black_holding, BlackPawn);
+ CopyHoldings(boards[forwardMostMove], white_holding, WhitePawn);
+ CopyHoldings(boards[forwardMostMove], black_holding, BlackPawn);
+ boards[forwardMostMove][BOARD_SIZE-1][BOARD_SIZE-2] = 1; // flag holdings as set
#if ZIPPY
if (appData.zippyPlay && first.initDone) {
ZippyHoldings(white_holding, black_holding,
}
/* Suppress following prompt */
if (looking_at(buf, &i, "*% ")) {
+ if(strchr(star_match[0], 7)) SendToPlayer("\007", 1); // Bell(); // FICS fuses bell for next board with prompt in zh captures
savingComment = FALSE;
}
next_out = i;
char promoChar;
int ranks=1, files=0; /* [HGM] ICS80: allow variable board size */
char *bookHit = NULL; // [HGM] book
+ Boolean weird = FALSE, reqFlag = FALSE;
fromX = fromY = toX = toY = -1;
while(i < 199 && (string[i] != ' ' || string[i+2] != ' ')) {
if(string[i] == ' ') { ranks++; files = 0; }
else files++;
+ if(!strchr(" -pnbrqkPNBRQK" , string[i])) weird = TRUE; // test for fairies
i++;
}
for(j = 0; j <i; j++) board_chars[j] = string[j];
ics_getting_history = H_FALSE;
return;
}
+
+ if (gameInfo.boardHeight != ranks || gameInfo.boardWidth != files ||
+ weird && (int)gameInfo.variant <= (int)VariantShogi) {
+ /* [HGM] We seem to have switched variant unexpectedly
+ * Try to guess new variant from board size
+ */
+ VariantClass newVariant = VariantFairy; // if 8x8, but fairies present
+ if(ranks == 8 && files == 10) newVariant = VariantCapablanca; else
+ if(ranks == 10 && files == 9) newVariant = VariantXiangqi; else
+ if(ranks == 8 && files == 12) newVariant = VariantCourier; else
+ if(ranks == 9 && files == 9) newVariant = VariantShogi; else
+ if(!weird) newVariant = VariantNormal;
+ VariantSwitch(boards[currentMove], newVariant); /* temp guess */
+ /* Get a move list just to see the header, which
+ will tell us whether this is really bug or zh */
+ if (ics_getting_history == H_FALSE) {
+ ics_getting_history = H_REQUESTED; reqFlag = TRUE;
+ sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
+ SendToICS(str);
+ }
+ }
/* Take action if this is the first board of a new game, or of a
different game than is currently being displayed. */
/* Forget the old game and get the history (if any) of the new one */
if (gameMode != BeginningOfGame) {
- Reset(FALSE, TRUE);
+ Reset(TRUE, TRUE);
}
newGame = TRUE;
if (appData.autoRaiseBoard) BoardToTop();
prevMove = -3;
if (gamenum == -1) {
newGameMode = IcsIdle;
- } else if (moveNum > 0 && newGameMode != IcsIdle &&
- appData.getMoveList) {
+ } else if ((moveNum > 0 || newGameMode == IcsObserving) && newGameMode != IcsIdle &&
+ appData.getMoveList && !reqFlag) {
/* Need to get game history */
ics_getting_history = H_REQUESTED;
sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
timeIncrement = increment * 1000;
movesPerSession = 0;
gameInfo.timeControl = TimeControlTagValue();
- VariantSwitch(board, StringToVariant(gameInfo.event) );
+ VariantSwitch(boards[currentMove], StringToVariant(gameInfo.event) );
if (appData.debugMode) {
fprintf(debugFP, "ParseBoard says variant = '%s'\n", gameInfo.event);
fprintf(debugFP, "recognized as %s\n", VariantName(gameInfo.variant));
}
}
CopyBoard(boards[moveNum], board);
+ boards[moveNum][BOARD_SIZE-1][BOARD_SIZE-2] = 0; // [HGM] indicate holdings not set
if (moveNum == 0) {
startedFromSetupPosition =
!CompareBoards(board, initialPosition);
for(k=BOARD_LEFT; k<BOARD_RGHT; k++)
if(board[BOARD_HEIGHT-1][k] == bKing)
initialRights[5] = castlingRights[moveNum][5] = k;
+ if(gameInfo.variant == VariantTwoKings) {
+ // In TwoKings looking for a King does not work, so always give castling rights to a King on e1/e8
+ if(board[0][4] == wKing) initialRights[2] = castlingRights[moveNum][2] = 4;
+ if(board[BOARD_HEIGHT-1][4] == bKing) initialRights[5] = castlingRights[moveNum][5] = 4;
+ }
} else { int r;
r = castlingRights[moveNum][0] = initialRights[0];
if(board[0][r] != WhiteRook) castlingRights[moveNum][0] = -1;
/* Update currentMove and known move number limits */
newMove = newGame || moveNum > forwardMostMove;
- /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */
- if (!newGame && appData.icsEngineAnalyze && moveNum < forwardMostMove) {
- takeback = forwardMostMove - moveNum;
- for (i = 0; i < takeback; i++) {
- if (appData.debugMode) fprintf(debugFP, "take back move\n");
- SendToProgram("undo\n", &first);
- }
- }
-
if (newGame) {
forwardMostMove = backwardMostMove = currentMove = moveNum;
if (gameMode == IcsExamining && moveNum == 0) {
}
} else if (moveNum == forwardMostMove + 1 || moveNum == forwardMostMove
|| (moveNum < forwardMostMove && moveNum >= backwardMostMove)) {
+#if ZIPPY
+ /* [DM] If we found takebacks during icsEngineAnalyze try send to engine */
+ /* [HGM] applied this also to an engine that is silently watching */
+ if (appData.zippyPlay && moveNum < forwardMostMove && first.initDone &&
+ (gameMode == IcsObserving || gameMode == IcsExamining) &&
+ gameInfo.variant == currentlyInitializedVariant) {
+ takeback = forwardMostMove - moveNum;
+ for (i = 0; i < takeback; i++) {
+ if (appData.debugMode) fprintf(debugFP, "take back move\n");
+ SendToProgram("undo\n", &first);
+ }
+ }
+#endif
+
forwardMostMove = moveNum;
if (!pausing || currentMove > forwardMostMove)
currentMove = forwardMostMove;
forwardMostMove = pauseExamForwardMostMove;
return;
}
- forwardMostMove = backwardMostMove = currentMove = moveNum;
if (gameMode == IcsExamining && moveNum > 0 && appData.getMoveList) {
+#if ZIPPY
+ if(appData.zippyPlay && forwardMostMove > 0 && first.initDone) {
+ // [HGM] when we will receive the move list we now request, it will be
+ // fed to the engine from the first move on. So if the engine is not
+ // in the initial position now, bring it there.
+ InitChessProgram(&first, 0);
+ }
+#endif
ics_getting_history = H_REQUESTED;
sprintf(str, "%smoves %d\n", ics_prefix, gamenum);
SendToICS(str);
}
+ forwardMostMove = backwardMostMove = currentMove = moveNum;
}
/* Update the clocks */
if (appData.testLegality) {
return (*moveType != IllegalMove);
} else {
- return !(fromX == fromY && toX == toY);
+ return !(*fromX == *toX && *fromY == *toY);
}
case WhiteDrop:
break;
case VariantFairy:
pieces = fairyArray;
- SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVSLUKpnbrqfeacwmohijgdvsluk");
+ SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk");
break;
case VariantGreat:
pieces = GreatArray;
}
int
-IsPromotion(fromX, fromY, toX, toY)
- int fromX, fromY, toX, toY;
+HasPromotionChoice(int fromX, int fromY, int toX, int toY, char *promoChoice)
{
+ /* [HGM] rewritten IsPromotion to only flag promotions that offer a choice */
/* [HGM] add Shogi promotions */
int promotionZoneSize=1, highestPromotingPiece = (int)WhitePawn;
ChessSquare piece;
+ ChessMove moveType;
+ Boolean premove;
+
+ if(fromX < BOARD_LEFT || fromX >= BOARD_RGHT) return FALSE; // drop
+ if(toX < BOARD_LEFT || toX >= BOARD_RGHT) return FALSE; // move into holdings
+
+ if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi || // no promotions
+ !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) // invalid move
+ return FALSE;
- if(gameMode == EditPosition || gameInfo.variant == VariantXiangqi ||
- !(fromX >=0 && fromY >= 0 && toX >= 0 && toY >= 0) ) return FALSE;
- /* [HGM] Note to self: line above also weeds out drops */
piece = boards[currentMove][fromY][fromX];
if(gameInfo.variant == VariantShogi) {
promotionZoneSize = 3;
- highestPromotingPiece = (int)WhiteKing;
- /* [HGM] Should be Silver = Ferz, really, but legality testing is off,
- and if in normal chess we then allow promotion to King, why not
- allow promotion of other piece in Shogi? */
+ highestPromotingPiece = (int)WhiteFerz;
}
+
+ // next weed out all moves that do not touch the promotion zone at all
if((int)piece >= BlackPawn) {
if(toY >= promotionZoneSize && fromY >= promotionZoneSize)
return FALSE;
if( toY < BOARD_HEIGHT - promotionZoneSize &&
fromY < BOARD_HEIGHT - promotionZoneSize) return FALSE;
}
- return ( (int)piece <= highestPromotingPiece );
+
+ if( (int)piece > highestPromotingPiece ) return FALSE; // non-promoting piece
+
+ // weed out mandatory Shogi promotions
+ if(gameInfo.variant == VariantShogi) {
+ if(piece >= BlackPawn) {
+ if(toY == 0 && piece == BlackPawn ||
+ toY == 0 && piece == BlackQueen ||
+ toY <= 1 && piece == BlackKnight) {
+ *promoChoice = '+';
+ return FALSE;
+ }
+ } else {
+ if(toY == BOARD_HEIGHT-1 && piece == WhitePawn ||
+ toY == BOARD_HEIGHT-1 && piece == WhiteQueen ||
+ toY >= BOARD_HEIGHT-2 && piece == WhiteKnight) {
+ *promoChoice = '+';
+ return FALSE;
+ }
+ }
+ }
+
+ // weed out obviously illegal Pawn moves
+ if(appData.testLegality && (piece == WhitePawn || piece == BlackPawn) ) {
+ if(toX > fromX+1 || toX < fromX-1) return FALSE; // wide
+ if(piece == WhitePawn && toY != fromY+1) return FALSE; // deep
+ if(piece == BlackPawn && toY != fromY-1) return FALSE; // deep
+ if(fromX != toX && gameInfo.variant == VariantShogi) return FALSE;
+ // note we are not allowed to test for valid (non-)capture, due to premove
+ }
+
+ // we either have a choice what to promote to, or (in Shogi) whether to promote
+ if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier) {
+ *promoChoice = PieceToChar(BlackFerz); // no choice
+ return FALSE;
+ }
+ if(appData.alwaysPromoteToQueen) { // predetermined
+ if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantLosers)
+ *promoChoice = PieceToChar(BlackKing); // in Suicide Q is the last thing we want
+ else *promoChoice = PieceToChar(BlackQueen);
+ return FALSE;
+ }
+
+ // suppress promotion popup on illegal moves that are not premoves
+ premove = gameMode == IcsPlayingWhite && !WhiteOnMove(currentMove) ||
+ gameMode == IcsPlayingBlack && WhiteOnMove(currentMove);
+ if(appData.testLegality && !premove) {
+ moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
+ epStatus[currentMove], castlingRights[currentMove],
+ fromY, fromX, toY, toX, NULLCHAR);
+ if(moveType != WhitePromotionQueen && moveType != BlackPromotionQueen &&
+ moveType != WhitePromotionKnight && moveType != BlackPromotionKnight)
+ return FALSE;
+ }
+
+ return TRUE;
}
int
ChessMove moveType;
ChessSquare pdown, pup;
- if (fromX < 0 || fromY < 0) return ImpossibleMove;
-
- /* [HGM] suppress all moves into holdings area and guard band */
- if( toX < BOARD_LEFT || toX >= BOARD_RGHT || toY < 0 )
- return ImpossibleMove;
-
- /* [HGM] <sameColor> moved to here from winboard.c */
- /* note: capture of own piece can be legal as drag-drop premove. For click-click it is selection of new piece. */
- pdown = boards[currentMove][fromY][fromX];
- pup = boards[currentMove][toY][toX];
- if ( gameMode != EditPosition && !captureOwn &&
- (WhitePawn <= pdown && pdown < BlackPawn &&
- WhitePawn <= pup && pup < BlackPawn ||
- BlackPawn <= pdown && pdown < EmptySquare &&
- BlackPawn <= pup && pup < EmptySquare
- ) && !((gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom) &&
- (pup == WhiteRook && pdown == WhiteKing && fromY == 0 && toY == 0||
- pup == BlackRook && pdown == BlackKing && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1 ||
- pup == WhiteKing && pdown == WhiteRook && fromY == 0 && toY == 0|| // also allow RxK
- pup == BlackKing && pdown == BlackRook && fromY == BOARD_HEIGHT-1 && toY == BOARD_HEIGHT-1 )
- ) )
- return Comment;
-
/* Check if the user is playing in turn. This is complicated because we
let the user "pick up" a piece before it is his turn. So the piece he
tried to pick up may have been captured by the time he puts it down!
return ImpossibleMove;
}
+ if(toX < 0 || toY < 0) return ImpossibleMove;
+ pdown = boards[currentMove][fromY][fromX];
+ pup = boards[currentMove][toY][toX];
+
/* [HGM] If move started in holdings, it means a drop */
if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {
if( pup != EmptySquare ) return ImpossibleMove;
return ImpossibleMove;
}
}
-if(appData.debugMode) fprintf(debugFP, "moveType 3 = %d, promochar = %x\n", moveType, promoChar);
+
return moveType;
/* [HGM] <popupFix> in stead of calling FinishMove directly, this
function is made into one that returns an OK move type if FinishMove
/*char*/int promoChar;
{
char *bookHit = 0;
-if(appData.debugMode) fprintf(debugFP, "moveType 5 = %d, promochar = %x\n", moveType, promoChar);
+
if((gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) && promoChar != NULLCHAR) {
// [HGM] superchess: suppress promotions to non-available piece
int k = PieceToNumber(CharToPiece(ToUpper(promoChar)));
move type in caller when we know the move is a legal promotion */
if(moveType == NormalMove && promoChar)
moveType = PromoCharToMoveType(WhiteOnMove(currentMove), promoChar);
-if(appData.debugMode) fprintf(debugFP, "moveType 1 = %d, promochar = %x\n", moveType, promoChar);
+
/* [HGM] convert drag-and-drop piece drops to standard form */
- if( fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) {
+ if( (fromX == BOARD_LEFT-2 || fromX == BOARD_RGHT+1) && fromY != DROP_RANK ){
moveType = WhiteOnMove(currentMove) ? WhiteDrop : BlackDrop;
if(appData.debugMode) fprintf(debugFP, "Drop move %d, curr=%d, x=%d,y=%d, p=%d\n",
moveType, currentMove, fromX, fromY, boards[currentMove][fromY][fromX]);
-// fromX = boards[currentMove][fromY][fromX];
// holdings might not be sent yet in ICS play; we have to figure out which piece belongs here
if(fromX == 0) fromY = BOARD_HEIGHT-1 - fromY; // black holdings upside-down
fromX = fromX ? WhitePawn : BlackPawn; // first piece type in selected holdings
}
/* [HGM] <popupFix> The following if has been moved here from
- UserMoveEvent(). Because it seemed to belon here (why not allow
+ UserMoveEvent(). Because it seemed to belong here (why not allow
piece drops in training games?), and because it can only be
performed after it is known to what we promote. */
if (gameMode == Training) {
}
ModeHighlight();
}
-if(appData.debugMode) fprintf(debugFP, "moveType 2 = %d, promochar = %x\n", moveType, promoChar);
+
/* Relay move to ICS or chess engine */
if (appData.icsActive) {
if (gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack ||
FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
}
+void LeftClick(ClickType clickType, int xPix, int yPix)
+{
+ int x, y;
+ Boolean saveAnimate;
+ static int second = 0, promotionChoice = 0;
+ char promoChoice = NULLCHAR;
+
+ if (clickType == Press) ErrorPopDown();
+
+ x = EventToSquare(xPix, BOARD_WIDTH);
+ y = EventToSquare(yPix, BOARD_HEIGHT);
+ if (!flipView && y >= 0) {
+ y = BOARD_HEIGHT - 1 - y;
+ }
+ if (flipView && x >= 0) {
+ x = BOARD_WIDTH - 1 - x;
+ }
+
+ if(promotionChoice) { // we are waiting for a click to indicate promotion piece
+ if(clickType == Release) return; // ignore upclick of click-click destination
+ promotionChoice = FALSE; // only one chance: if click not OK it is interpreted as cancel
+ if(appData.debugMode) fprintf(debugFP, "promotion click, x=%d, y=%d\n", x, y);
+ if(gameInfo.holdingsWidth &&
+ (WhiteOnMove(currentMove)
+ ? x == BOARD_WIDTH-1 && y < gameInfo.holdingsSize && y > 0
+ : x == 0 && y >= BOARD_HEIGHT - gameInfo.holdingsSize && y < BOARD_HEIGHT-1) ) {
+ // click in right holdings, for determining promotion piece
+ ChessSquare p = boards[currentMove][y][x];
+ if(appData.debugMode) fprintf(debugFP, "square contains %d\n", (int)p);
+ if(p != EmptySquare) {
+ FinishMove(NormalMove, fromX, fromY, toX, toY, ToLower(PieceToChar(p)));
+ fromX = fromY = -1;
+ return;
+ }
+ }
+ DrawPosition(FALSE, boards[currentMove]);
+ return;
+ }
+
+ /* [HGM] holdings: next 5 lines: ignore all clicks between board and holdings */
+ if(clickType == Press
+ && ( x == BOARD_LEFT-1 || x == BOARD_RGHT
+ || x == BOARD_LEFT-2 && y < BOARD_HEIGHT-gameInfo.holdingsSize
+ || x == BOARD_RGHT+1 && y >= gameInfo.holdingsSize) )
+ return;
+
+ if (fromX == -1) {
+ if (clickType == Press) {
+ /* First square */
+ if (OKToStartUserMove(x, y)) {
+ fromX = x;
+ fromY = y;
+ second = 0;
+ DragPieceBegin(xPix, yPix);
+ if (appData.highlightDragging) {
+ SetHighlights(x, y, -1, -1);
+ }
+ }
+ }
+ return;
+ }
+
+ /* fromX != -1 */
+ if (clickType == Press && gameMode != EditPosition) {
+ ChessSquare fromP;
+ ChessSquare toP;
+ int frc;
+
+ // ignore off-board to clicks
+ if(y < 0 || x < 0) return;
+
+ /* Check if clicking again on the same color piece */
+ fromP = boards[currentMove][fromY][fromX];
+ toP = boards[currentMove][y][x];
+ frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom;
+ if ((WhitePawn <= fromP && fromP <= WhiteKing &&
+ WhitePawn <= toP && toP <= WhiteKing &&
+ !(fromP == WhiteKing && toP == WhiteRook && frc) &&
+ !(fromP == WhiteRook && toP == WhiteKing && frc)) ||
+ (BlackPawn <= fromP && fromP <= BlackKing &&
+ BlackPawn <= toP && toP <= BlackKing &&
+ !(fromP == BlackRook && toP == BlackKing && frc) && // allow also RxK as FRC castling
+ !(fromP == BlackKing && toP == BlackRook && frc))) {
+ /* Clicked again on same color piece -- changed his mind */
+ second = (x == fromX && y == fromY);
+ if (appData.highlightDragging) {
+ SetHighlights(x, y, -1, -1);
+ } else {
+ ClearHighlights();
+ }
+ if (OKToStartUserMove(x, y)) {
+ fromX = x;
+ fromY = y;
+ DragPieceBegin(xPix, yPix);
+ }
+ return;
+ }
+ // ignore clicks on holdings
+ if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
+ }
+
+ if (clickType == Release && x == fromX && y == fromY) {
+ DragPieceEnd(xPix, yPix);
+ if (appData.animateDragging) {
+ /* Undo animation damage if any */
+ DrawPosition(FALSE, NULL);
+ }
+ if (second) {
+ /* Second up/down in same square; just abort move */
+ second = 0;
+ fromX = fromY = -1;
+ ClearHighlights();
+ gotPremove = 0;
+ ClearPremoveHighlights();
+ } else {
+ /* First upclick in same square; start click-click mode */
+ SetHighlights(x, y, -1, -1);
+ }
+ return;
+ }
+
+ /* we now have a different from- and (possibly off-board) to-square */
+ /* Completed move */
+ toX = x;
+ toY = y;
+ saveAnimate = appData.animate;
+ if (clickType == Press) {
+ /* Finish clickclick move */
+ if (appData.animate || appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ } else {
+ ClearHighlights();
+ }
+ } else {
+ /* Finish drag move */
+ if (appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ } else {
+ ClearHighlights();
+ }
+ DragPieceEnd(xPix, yPix);
+ /* Don't animate move and drag both */
+ appData.animate = FALSE;
+ }
+
+ // moves into holding are invalid for now (later perhaps allow in EditPosition)
+ if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) {
+ ClearHighlights();
+ fromX = fromY = -1;
+ DrawPosition(TRUE, NULL);
+ return;
+ }
+
+ // off-board moves should not be highlighted
+ if(x < 0 || x < 0) ClearHighlights();
+
+ if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) {
+ SetHighlights(fromX, fromY, toX, toY);
+ if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) {
+ // [HGM] super: promotion to captured piece selected from holdings
+ ChessSquare p = boards[currentMove][fromY][fromX], q = boards[currentMove][toY][toX];
+ promotionChoice = TRUE;
+ // kludge follows to temporarily execute move on display, without promoting yet
+ boards[currentMove][fromY][fromX] = EmptySquare; // move Pawn to 8th rank
+ boards[currentMove][toY][toX] = p;
+ DrawPosition(FALSE, boards[currentMove]);
+ boards[currentMove][fromY][fromX] = p; // take back, but display stays
+ boards[currentMove][toY][toX] = q;
+ DisplayMessage("Click in holdings to choose piece", "");
+ return;
+ }
+ PromotionPopUp();
+ } else {
+ UserMoveEvent(fromX, fromY, toX, toY, promoChoice);
+ if (!appData.highlightLastMove || gotPremove) ClearHighlights();
+ if (gotPremove) SetPremoveHighlights(fromX, fromY, toX, toY);
+ fromX = fromY = -1;
+ }
+ appData.animate = saveAnimate;
+ if (appData.animate || appData.animateDragging) {
+ /* Undo animation damage if needed */
+ DrawPosition(FALSE, NULL);
+ }
+}
+
void SendProgramStatsToFrontend( ChessProgramState * cps, ChessProgramStats * cpstats )
{
// char * hint = lastHint;
} else moveCount = 6;
}
}
-
- if (appData.debugMode) { int i;
- fprintf(debugFP, "repeat test fmm=%d bmm=%d ep=%d, reps=%d\n",
- forwardMostMove, backwardMostMove, epStatus[backwardMostMove],
- appData.drawRepeats);
- for( i=forwardMostMove; i>=backwardMostMove; i-- )
- fprintf(debugFP, "%d ep=%d\n", i, epStatus[i]);
-
- }
/* Check for rep-draws */
count = 0;
DisplayError(_("Bad FEN received from engine"), 0);
return ;
} else {
- Reset(FALSE, FALSE);
+ Reset(TRUE, FALSE);
CopyBoard(boards[0], initial_position);
initialRulePlies = FENrulePlies;
epStatus[0] = FENepStatus;
/* [AS] Negate score if machine is playing black and reporting absolute scores */
if( cps->scoreIsAbsolute &&
- ((gameMode == MachinePlaysBlack) || (gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b')) )
+ ( gameMode == MachinePlaysBlack ||
+ gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b' ||
+ gameMode == IcsPlayingBlack || // [HGM] also add other situations where engine should report black POV
+ (gameMode == AnalyzeMode || gameMode == AnalyzeFile || gameMode == IcsObserving && appData.icsEngineAnalyze) &&
+ !WhiteOnMove(currentMove)
+ ) )
{
curscore = -curscore;
}
if(cps->nps == 0) ticklen = 10*time; // use engine reported time
else ticklen = (1000. * u64ToDouble(nodes)) / cps->nps; // convert node count to time
- if(WhiteOnMove(forwardMostMove))
+ if(WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysWhite ||
+ gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'w'))
whiteTimeRemaining = timeRemaining[0][forwardMostMove] - ticklen;
- else blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;
+ if(!WhiteOnMove(forwardMostMove) && (gameMode == MachinePlaysBlack ||
+ gameMode == TwoMachinesPlay && cps->twoMachinesColor[0] == 'b'))
+ blackTimeRemaining = timeRemaining[1][forwardMostMove] - ticklen;
}
/* Buffer overflow protection */
if (strlen(buf1) >= sizeof(programStats.movelist)
&& appData.debugMode) {
fprintf(debugFP,
- "PV is too long; using the first %d bytes.\n",
- sizeof(programStats.movelist) - 1);
+ "PV is too long; using the first %u bytes.\n",
+ (unsigned) sizeof(programStats.movelist) - 1);
}
safeStrCpy( programStats.movelist, buf1, sizeof(programStats.movelist) );
if (currentMove == forwardMostMove || gameMode == AnalyzeMode
|| gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
DisplayMove(currentMove - 1);
- DisplayAnalysis();
}
return;
if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
DisplayMove(currentMove - 1);
- DisplayAnalysis();
}
return;
} else if (sscanf(message,"stat01: %d " u64Display " %d %d %d %s",
SendProgramStatsToFrontend( cps, &programStats );
- DisplayAnalysis();
return;
} else if (strncmp(message,"++",2) == 0) {
if (currentMove == forwardMostMove || gameMode==AnalyzeMode ||
gameMode == AnalyzeFile || appData.icsEngineAnalyze) {
DisplayMove(currentMove - 1);
- DisplayAnalysis();
}
return;
}
p = (int) fromX;
if(p < (int) BlackPawn) { /* white drop */
p -= (int)WhitePawn;
+ p = PieceToNumber((ChessSquare)p);
if(p >= gameInfo.holdingsSize) p = 0;
- if(--board[p][BOARD_WIDTH-2] == 0)
+ if(--board[p][BOARD_WIDTH-2] <= 0)
board[p][BOARD_WIDTH-1] = EmptySquare;
+ if((int)board[p][BOARD_WIDTH-2] < 0)
+ board[p][BOARD_WIDTH-2] = 0;
} else { /* black drop */
p -= (int)BlackPawn;
+ p = PieceToNumber((ChessSquare)p);
if(p >= gameInfo.holdingsSize) p = 0;
- if(--board[BOARD_HEIGHT-1-p][1] == 0)
+ if(--board[BOARD_HEIGHT-1-p][1] <= 0)
board[BOARD_HEIGHT-1-p][0] = EmptySquare;
+ if((int)board[BOARD_HEIGHT-1-p][1] < 0)
+ board[BOARD_HEIGHT-1-p][1] = 0;
}
}
if (captured != EmptySquare && gameInfo.holdingsSize > 0
board[BOARD_HEIGHT-1-p][0] = WHITE_TO_BLACK captured;
}
}
-
} else if (gameInfo.variant == VariantAtomic) {
if (captured != EmptySquare) {
int y, x;
void
NextMatchGame P((void))
{
- int index; /* [HGM] autoinc: step lod index during match */
+ int index; /* [HGM] autoinc: step load index during match */
Reset(FALSE, TRUE);
if (*appData.loadGameFile != NULLCHAR) {
index = appData.loadGameIndex;
/* [AS] Add PV info if present */
if( i >= 0 && appData.saveExtendedInfoInPGN && pvInfoList[i].depth > 0 ) {
/* [HGM] add time */
- char buf[MSG_SIZ]; int seconds = 0;
+ char buf[MSG_SIZ]; int seconds;
- if(i >= backwardMostMove) {
- if(WhiteOnMove(i))
- seconds = timeRemaining[0][i] - timeRemaining[0][i+1]
- + GetTimeQuota(i/2) / (1000*WhitePlayer()->timeOdds);
- else
- seconds = timeRemaining[1][i] - timeRemaining[1][i+1]
- + GetTimeQuota(i/2) / (1000*WhitePlayer()->other->timeOdds);
- }
- seconds = (seconds+50)/100; // deci-seconds, rounded to nearest
+ seconds = (pvInfoList[i].time+5)/10; // deci-seconds, rounded to nearest
if( seconds <= 0) buf[0] = 0; else
if( seconds < 30 ) sprintf(buf, " %3.1f%c", seconds/10., 0); else {
int dummy;
char *dummy2;
{
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving
if (appData.oldSaveStyle)
return SaveGameOldStyle(f);
{
time_t tm;
char *fen;
-
+
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
if (appData.oldSaveStyle) {
tm = time((time_t *) NULL);
EditGameEvent();
if (gameMode == EditPosition)
- EditPositionDone();
+ EditPositionDone(TRUE);
if (!WhiteOnMove(currentMove)) {
DisplayError(_("It is not White's turn"), 0);
EditGameEvent();
if (gameMode == EditPosition)
- EditPositionDone();
+ EditPositionDone(TRUE);
if (WhiteOnMove(currentMove)) {
DisplayError(_("It is not Black's turn"), 0);
if (gameMode != EditGame) return;
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case AnalyzeMode:
case AnalyzeFile:
strcpy(bookMove, "move ");
strcat(bookMove, bookHit);
- HandleMachineMove(bookMove, &first);
+ savedMessage = bookMove; // args for deferred call
+ savedState = onmove;
+ ScheduleDelayedEvent(DeferredBookMove, 1);
}
}
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case AnalyzeMode:
}
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case AnalyzeMode:
case AnalyzeFile:
SendToProgram("exit\n", &first);
first.analyzing = FALSE;
}
- EngineOutputPopDown();
thinkOutput[0] = NULLCHAR;
}
void
-EditPositionDone()
+EditPositionDone(Boolean fakeRights)
{
int king = gameInfo.variant == VariantKnightmate ? WhiteUnicorn : WhiteKing;
startedFromSetupPosition = TRUE;
InitChessProgram(&first, FALSE);
- castlingRights[0][2] = castlingRights[0][5] = BOARD_WIDTH>>1;
- if(boards[0][0][BOARD_WIDTH>>1] == king) {
- castlingRights[0][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? 0 : -1;
- castlingRights[0][0] = boards[0][0][BOARD_RGHT-1] == WhiteRook ? BOARD_RGHT-1 : -1;
- } else castlingRights[0][2] = -1;
- if(boards[0][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == WHITE_TO_BLACK king) {
- castlingRights[0][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? 0 : -1;
- castlingRights[0][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : -1;
- } else castlingRights[0][5] = -1;
+ if(fakeRights)
+ { /* don't do this if we just pasted FEN */
+ castlingRights[0][2] = castlingRights[0][5] = BOARD_WIDTH>>1;
+ if(boards[0][0][BOARD_WIDTH>>1] == king)
+ {
+ castlingRights[0][1] = boards[0][0][BOARD_LEFT] == WhiteRook ? 0 : -1;
+ castlingRights[0][0] = boards[0][0][BOARD_RGHT-1] == WhiteRook ? BOARD_RGHT-1 : -1;
+ }
+ else
+ castlingRights[0][2] = -1;
+ if(boards[0][BOARD_HEIGHT-1][BOARD_WIDTH>>1] == WHITE_TO_BLACK king)
+ {
+ castlingRights[0][4] = boards[0][BOARD_HEIGHT-1][BOARD_LEFT] == BlackRook ? 0 : -1;
+ castlingRights[0][3] = boards[0][BOARD_HEIGHT-1][BOARD_RGHT-1] == BlackRook ? BOARD_RGHT-1 : -1;
+ }
+ else
+ castlingRights[0][5] = -1;
+ }
SendToProgram("force\n", &first);
if (blackPlaysFirst) {
strcpy(moveList[0], "");
ToStartEvent()
{
if (gameMode == AnalyzeMode || gameMode == AnalyzeFile) {
- /* to optimze, we temporarily turn off analysis mode while we undo
+ /* to optimize, we temporarily turn off analysis mode while we undo
* all the moves. Otherwise we get analysis output after each undo.
*/
if (first.analysisSupport) {
}
break;
case EditPosition:
- EditPositionDone();
+ EditPositionDone(TRUE);
break;
case TwoMachinesPlay:
return;
} else {
gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
}
- gameInfo.resultDetails = buf;
+ gameInfo.resultDetails = StrSave(buf);
}
DisplayFatalError(buf, error, 1);
}
} else {
gameInfo.result = cps->twoMachinesColor[0]=='w' ? BlackWins : WhiteWins;
}
- gameInfo.resultDetails = buf;
+ gameInfo.resultDetails = StrSave(buf);
}
RemoveInputSource(cps->isr);
DisplayFatalError(buf, 0, 1);
/* [HGM] translate opponent's time by time-odds factor */
otime = (otime * cps->other->timeOdds) / cps->timeOdds;
if (appData.debugMode) {
- fprintf(debugFP, "time odds: %d %d \n", cps->timeOdds, cps->other->timeOdds);
+ fprintf(debugFP, "time odds: %f %f \n", cps->timeOdds, cps->other->timeOdds);
}
if (time <= 0) time = 1;
/* unknown feature: complain and skip */
q = p;
while (*q && *q != '=') q++;
- sprintf(buf, "rejected %.*s\n", q-p, p);
+ sprintf(buf, "rejected %.*s\n", (int)(q-p), p);
SendToProgram(buf, cps);
p = q;
if (*p == '=') {
appData.periodicUpdates=newState;
/* Display type changes, so update it now */
- DisplayAnalysis();
+// DisplayAnalysis();
/* Get the ball rolling again... */
if (newState) {
int newState;
{
if (newState == appData.ponderNextMove) return;
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
if (newState) {
SendToProgram("hard\n", &first);
if (gameMode == TwoMachinesPlay) {
{
char buf[MSG_SIZ];
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
sprintf(buf, "%s%s %d\n", (option ? "option ": ""), command, value);
SendToProgram(buf, &first);
if (gameMode == TwoMachinesPlay) {
if (oldState == newState) return;
oldState = newState;
- if (gameMode == EditPosition) EditPositionDone();
+ if (gameMode == EditPosition) EditPositionDone(TRUE);
if (oldState) {
SendToProgram("post\n", &first);
if (gameMode == TwoMachinesPlay) {
}
void
-DisplayAnalysisText(text)
- char *text;
-{
- if (gameMode == AnalyzeMode || gameMode == AnalyzeFile
- || appData.icsEngineAnalyze)
- {
- EngineOutputPopUp();
- }
-}
-
-static int
-only_one_move(str)
- char *str;
-{
- while (*str && isspace(*str)) ++str;
- while (*str && !isspace(*str)) ++str;
- if (!*str) return 1;
- while (*str && isspace(*str)) ++str;
- if (!*str) return 1;
- return 0;
-}
-
-void
-DisplayAnalysis()
-{
- char buf[MSG_SIZ];
- char lst[MSG_SIZ / 2];
- double nps;
- static char *xtra[] = { "", " (--)", " (++)" };
- int h, m, s, cs;
-
- if (programStats.time == 0) {
- programStats.time = 1;
- }
-
- if (programStats.got_only_move) {
- safeStrCpy(buf, programStats.movelist, sizeof(buf));
- } else {
- safeStrCpy( lst, programStats.movelist, sizeof(lst));
-
- nps = (u64ToDouble(programStats.nodes) /
- ((double)programStats.time /100.0));
-
- cs = programStats.time % 100;
- s = programStats.time / 100;
- h = (s / (60*60));
- s = s - h*60*60;
- m = (s/60);
- s = s - m*60;
-
- if (programStats.moves_left > 0 && appData.periodicUpdates) {
- if (programStats.move_name[0] != NULLCHAR) {
- sprintf(buf, "depth=%d %d/%d(%s) %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- programStats.nr_moves-programStats.moves_left,
- programStats.nr_moves, programStats.move_name,
- ((float)programStats.score)/100.0, lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- (u64)programStats.nodes, (int)nps, h, m, s, cs);
- } else {
- sprintf(buf, "depth=%d %d/%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- programStats.nr_moves-programStats.moves_left,
- programStats.nr_moves, ((float)programStats.score)/100.0,
- lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- (u64)programStats.nodes, (int)nps, h, m, s, cs);
- }
- } else {
- sprintf(buf, "depth=%d %+.2f %s%s\nNodes: " u64Display " NPS: %d\nTime: %02d:%02d:%02d.%02d",
- programStats.depth,
- ((float)programStats.score)/100.0,
- lst,
- only_one_move(lst)?
- xtra[programStats.got_fail] : "",
- (u64)programStats.nodes, (int)nps, h, m, s, cs);
- }
- }
- DisplayAnalysisText(buf);
-}
-
-void
DisplayComment(moveNumber, text)
int moveNumber;
char *text;
char title[MSG_SIZ];
char buf[8000]; // comment can be long!
int score, depth;
-
- if( appData.autoDisplayComment ) {
- if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
- strcpy(title, "Comment");
- } else {
- sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
- 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;
- sprintf(buf, "%s%.2f/%d %d\n%s", score>0 ? "+" : "", score/100.,
- depth, (pvInfoList[moveNumber].time+50)/100, text);
- text = buf;
- }
- } else title[0] = 0;
-
- if (text != NULL)
+
+ if (moveNumber < 0 || parseList[moveNumber][0] == NULLCHAR) {
+ strcpy(title, "Comment");
+ } else {
+ sprintf(title, "Comment on %d.%s%s", moveNumber / 2 + 1,
+ 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;
+ sprintf(buf, "%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);
}
*p++ = ' ';
if(q = overrideCastling) { // [HGM] FRC: override castling & e.p fields for non-compliant engines
- while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' ';
+ while(*p++ = *q++); if(q != overrideCastling+1) p[-1] = ' '; else --p;
} else {
if(nrCastlingRights) {
q = p;
} /* assume possible unless obviously impossible */
if(initialRights[0]>=0 && board[castlingRank[0]][initialRights[0]] != WhiteRook) FENcastlingRights[0] = -1;
if(initialRights[1]>=0 && board[castlingRank[1]][initialRights[1]] != WhiteRook) FENcastlingRights[1] = -1;
- if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1;
+ if(initialRights[2]>=0 && board[castlingRank[2]][initialRights[2]] != WhiteUnicorn
+ && board[castlingRank[2]][initialRights[2]] != WhiteKing) FENcastlingRights[2] = -1;
if(initialRights[3]>=0 && board[castlingRank[3]][initialRights[3]] != BlackRook) FENcastlingRights[3] = -1;
if(initialRights[4]>=0 && board[castlingRank[4]][initialRights[4]] != BlackRook) FENcastlingRights[4] = -1;
- if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1;
+ if(initialRights[5]>=0 && board[castlingRank[5]][initialRights[5]] != BlackUnicorn
+ && board[castlingRank[5]][initialRights[5]] != BlackKing) FENcastlingRights[5] = -1;
FENrulePlies = 0;
while(*p==' ') p++;
if(board[BOARD_HEIGHT-1][i] == BlackKing) blackKingFile = i;
if(board[0 ][i] == WhiteKing) whiteKingFile = i;
}
+ if(gameInfo.variant == VariantTwoKings || gameInfo.variant == VariantKnightmate)
+ whiteKingFile = blackKingFile = BOARD_WIDTH >> 1; // scanning fails in these variants
+ if(whiteKingFile<0 || board[0][whiteKingFile]!=WhiteUnicorn
+ && board[0][whiteKingFile]!=WhiteKing) whiteKingFile = -1;
+ if(blackKingFile<0 || board[BOARD_HEIGHT-1][blackKingFile]!=BlackUnicorn
+ && board[BOARD_HEIGHT-1][blackKingFile]!=BlackKing) blackKingFile = -1;
switch(c) {
case'K':
- for(i=BOARD_RGHT-1; board[0][i]!=WhiteRook && i>whiteKingFile; i--);
+ for(i=BOARD_RGHT-1; i>whiteKingFile && board[0][i]!=WhiteRook; i--);
FENcastlingRights[0] = i != whiteKingFile ? i : -1;
FENcastlingRights[2] = whiteKingFile;
break;
FENcastlingRights[2] = whiteKingFile;
break;
case'k':
- for(i=BOARD_RGHT-1; board[BOARD_HEIGHT-1][i]!=BlackRook && i>blackKingFile; i--);
+ for(i=BOARD_RGHT-1; i>blackKingFile && board[BOARD_HEIGHT-1][i]!=BlackRook; i--);
FENcastlingRights[3] = i != blackKingFile ? i : -1;
FENcastlingRights[5] = blackKingFile;
break;
}
}
}
+ for(i=0; i<nrCastlingRights; i++)
+ if(FENcastlingRights[i] >= 0) initialRights[i] = FENcastlingRights[i];
if (appData.debugMode) {
fprintf(debugFP, "FEN castling rights:");
for(i=0; i<nrCastlingRights; i++)
for( i=0; i<nrCastlingRights; i++ )
castlingRights[0][i] = FENcastlingRights[i];
}
- EditPositionDone();
+ EditPositionDone(FALSE);
DisplayBothClocks();
DrawPosition(FALSE, boards[currentMove]);
}
}
}
+
+static char cseq[12] = "\\ ";
+
+Boolean set_cont_sequence(char *new_seq)
+{
+ int len;
+ Boolean ret;
+
+ // handle bad attempts to set the sequence
+ if (!new_seq)
+ return 0; // acceptable error - no debug
+
+ len = strlen(new_seq);
+ ret = (len > 0) && (len < sizeof(cseq));
+ if (ret)
+ strcpy(cseq, new_seq);
+ else if (appData.debugMode)
+ fprintf(debugFP, "Invalid continuation sequence \"%s\" (maximum length is: %u)\n", new_seq, (unsigned) sizeof(cseq)-1);
+ return ret;
+}
+
+/*
+ reformat a source message so words don't cross the width boundary. internal
+ newlines are not removed. returns the wrapped size (no null character unless
+ included in source message). If dest is NULL, only calculate the size required
+ for the dest buffer. lp argument indicats line position upon entry, and it's
+ passed back upon exit.
+*/
+int wrap(char *dest, char *src, int count, int width, int *lp)
+{
+ int len, i, ansi, cseq_len, line, old_line, old_i, old_len, clen;
+
+ cseq_len = strlen(cseq);
+ old_line = line = *lp;
+ ansi = len = clen = 0;
+
+ for (i=0; i < count; i++)
+ {
+ if (src[i] == '\033')
+ ansi = 1;
+
+ // if we hit the width, back up
+ if (!ansi && (line >= width) && src[i] != '\n' && src[i] != ' ')
+ {
+ // store i & len in case the word is too long
+ old_i = i, old_len = len;
+
+ // find the end of the last word
+ while (i && src[i] != ' ' && src[i] != '\n')
+ {
+ i--;
+ len--;
+ }
+
+ // word too long? restore i & len before splitting it
+ if ((old_i-i+clen) >= width)
+ {
+ i = old_i;
+ len = old_len;
+ }
+
+ // extra space?
+ if (i && src[i-1] == ' ')
+ len--;
+
+ if (src[i] != ' ' && src[i] != '\n')
+ {
+ i--;
+ if (len)
+ len--;
+ }
+
+ // now append the newline and continuation sequence
+ if (dest)
+ dest[len] = '\n';
+ len++;
+ if (dest)
+ strncpy(dest+len, cseq, cseq_len);
+ len += cseq_len;
+ line = cseq_len;
+ clen = cseq_len;
+ continue;
+ }
+
+ if (dest)
+ dest[len] = src[i];
+ len++;
+ if (!ansi)
+ line++;
+ if (src[i] == '\n')
+ line = 0;
+ if (src[i] == 'm')
+ ansi = 0;
+ }
+ if (dest && appData.debugMode)
+ {
+ fprintf(debugFP, "wrap(count:%d,width:%d,line:%d,len:%d,*lp:%d,src: ",
+ count, width, line, len, *lp);
+ show_bytes(debugFP, src, count);
+ fprintf(debugFP, "\ndest: ");
+ show_bytes(debugFP, dest, len);
+ fprintf(debugFP, "\n");
+ }
+ *lp = dest ? line : old_line;
+
+ return len;
+}