void SendToICSDelayed P((char *s, long msdelay));
void SendMoveToICS P((ChessMove moveType, int fromX, int fromY,
int toX, int toY));
-void InitPosition P((int redraw));
void HandleMachineMove P((char *message, ChessProgramState *cps));
int AutoPlayOneMove P((void));
int LoadGameOneMove P((ChessMove readAhead));
VariantClass currentlyInitializedVariant; /* [HGM] variantswitch */
int lastIndex = 0; /* [HGM] autoinc: last game/position used in match mode */
int opponentKibitzes;
+int lastSavedGame; /* [HGM] save: ID of game */
+char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */
+extern int chatCount;
+int chattingPartner;
/* States for ics_getting_history */
#define H_FALSE 0
Board boards[MAX_MOVES];
/* [HGM] Following 7 needed for accurate legality tests: */
-char epStatus[MAX_MOVES];
-char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1
-char castlingRank[BOARD_SIZE]; // and corresponding ranks
-char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];
+signed char epStatus[MAX_MOVES];
+signed char castlingRights[MAX_MOVES][BOARD_SIZE]; // stores files for pieces with castling rights or -1
+signed char castlingRank[BOARD_SIZE]; // and corresponding ranks
+signed char initialRights[BOARD_SIZE], FENcastlingRights[BOARD_SIZE], fileRights[BOARD_SIZE];
int nrCastlingRights; // For TwoKings, or to implement castling-unknown status
int initialRulePlies, FENrulePlies;
char FENepStatus;
}
void
+KeepAlive()
+{ // [HGM] alive: periodically send dummy (date) command to ICS to prevent time-out
+ SendToICS("date\n");
+ if(appData.keepAlive) ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
+}
+
+void
SendToICS(s)
char *s;
{
int tkind;
int backup; /* [DM] For zippy color lines */
char *p;
+ char talker[MSG_SIZ]; // [HGM] chat
if (appData.debugMode) {
if (!error) {
if (data[i] != NULLCHAR && data[i] != '\r')
buf[buf_len++] = data[i];
if(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[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
buf[buf_len++] = ' '; // replace by space (assumes ICS does not break lines within word)
+ }
}
buf[buf_len] = NULLCHAR;
parse[parse_pos++] = buf[i];
if (buf[i] == '\n') {
parse[parse_pos] = NULLCHAR;
+ if(chattingPartner>=0) {
+ char mess[MSG_SIZ];
+ sprintf(mess, "%s%s", talker, parse);
+ OutputChatMessage(chattingPartner, mess);
+ chattingPartner = -1;
+ } else
if(!suppressKibitz) // [HGM] kibitz
AppendComment(forwardMostMove, StripHighlight(parse));
else { // [HGM kibitz: divert memorized engine kibitz to engine-output window
}
} // [HGM] kibitz: end of patch
+//if(appData.debugMode) fprintf(debugFP, "hunt for tell, buf = %s\n", buf+i);
+
+ // [HGM] chat: intercept tells by users for which we have an open chat window
+ if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") ||
+ looking_at(buf, &i, "* whispers:"))) {
+ int p;
+ sscanf(star_match[0], "%[^(]", talker+1); // strip (C) or (U) off ICS handle
+ chattingPartner = -1;
+ if(buf[i-3] == 'r') // whisper; look if there is a WHISPER chatbox
+ for(p=0; p<MAX_CHAT; p++) if(!strcmp("WHISPER", chatPartner[p])) {
+ talker[0] = '['; strcat(talker, "]");
+ chattingPartner = p; break;
+ }
+ if(chattingPartner<0) // if not, look if there is a chatbox for this indivdual
+ for(p=0; p<MAX_CHAT; p++) if(!strcasecmp(talker+1, chatPartner[p])) {
+ talker[0] = 0;
+ chattingPartner = p; break;
+ }
+ if(chattingPartner<0) i = oldi; else {
+ started = STARTED_COMMENT;
+ parse_pos = 0; parse[0] = NULLCHAR;
+ savingComment = TRUE;
+ suppressKibitz = TRUE;
+ }
+ } // [HGM] chat: end of patch
+
if (appData.zippyTalk || appData.zippyPlay) {
/* [DM] Backup address for color zippy lines */
backup = i;
#endif
#endif
} // [DM] 'else { ' deleted
- if (/* Don't color "message" or "messages" output */
- (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
- looking_at(buf, &i, "*. * at *:*: ") ||
- looking_at(buf, &i, "--* (*:*): ") ||
+ if (
/* Regular tells and says */
(tkind = 1, looking_at(buf, &i, "* tells you: ")) ||
looking_at(buf, &i, "* (your partner) tells you: ") ||
looking_at(buf, &i, "* says: ") ||
+ /* Don't color "message" or "messages" output */
+ (tkind = 5, looking_at(buf, &i, "*. * (*:*): ")) ||
+ looking_at(buf, &i, "*. * at *:*: ") ||
+ looking_at(buf, &i, "--* (*:*): ") ||
/* Message notifications (same color as tells) */
looking_at(buf, &i, "* has left a message ") ||
looking_at(buf, &i, "* just sent you a message:\n") ||
break;
}
SendToICS(user_move);
+ if(appData.keepAlive) // [HGM] alive: schedule sending of dummy 'date' command
+ ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
}
void
if(appData.autoKibitz && !appData.icsEngineAnalyze ) { /* [HGM] kibitz: send most-recent PV info to ICS */
char buf[3*MSG_SIZ];
- sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %1.0f knps) PV=%s\n",
+ sprintf(buf, "kibitz !!! %+.2f/%d (%.2f sec, %u nodes, %.0f knps) PV=%s\n",
programStats.score / 100.,
programStats.depth,
programStats.time / 100.,
(unsigned int)programStats.nodes / (10*abs(programStats.time) + 1.),
programStats.movelist);
SendToICS(buf);
+if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats.nodes, programStats.nodes);
}
}
#endif
}
+// [HGM] save: calculate checksum of game to make games easily identifiable
+int StringCheckSum(char *s)
+{
+ int i = 0;
+ if(s==NULL) return 0;
+ while(*s) i = i*259 + *s++;
+ return i;
+}
+
+int GameCheckSum()
+{
+ int i, sum=0;
+ for(i=backwardMostMove; i<forwardMostMove; i++) {
+ sum += pvInfoList[i].depth;
+ sum += StringCheckSum(parseList[i]);
+ sum += StringCheckSum(commentList[i]);
+ sum *= 261;
+ }
+ if(i>1 && sum==0) sum++; // make sure never zero for non-empty game
+ return sum + StringCheckSum(commentList[i]);
+} // end of save patch
+
void
GameEnds(result, resultDetails, whosays)
ChessMove result;
DisplayMove(currentMove - 1);
if (forwardMostMove != 0) {
- if (gameMode != PlayFromGameFile && gameMode != EditGame) {
+ if (gameMode != PlayFromGameFile && gameMode != EditGame
+ && lastSavedGame != GameCheckSum() // [HGM] save: suppress duplicates
+ ) {
if (*appData.saveGameFile != NULLCHAR) {
SaveGameToFile(appData.saveGameFile, TRUE);
} else if (appData.autoSaveGames) {
DisplayTitle("");
DisplayMessage("", "");
HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1);
+ lastSavedGame = 0; // [HGM] save: make sure next game counts as unsaved
}
void
}
fclose(f);
+ lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving
return TRUE;
}
char *dummy2;
{
if (gameMode == EditPosition) EditPositionDone();
+ lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving
if (appData.oldSaveStyle)
return SaveGameOldStyle(f);
else
void
EditPositionDone()
{
+ 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;
SendToProgram("force\n", &first);
if (blackPlaysFirst) {
strcpy(moveList[0], "");
if (BoolFeature(&p, "smp", &cps->maxCores, cps)) continue;
if (StringFeature(&p, "egt", &cps->egtFormats, cps)) continue;
if (StringFeature(&p, "option", &(cps->option[cps->nrOptions].name), cps)) {
- ParseOption(&(cps->option[cps->nrOptions++]), cps); // [HGM] options: add option feature
+ if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature
+ sprintf(buf, "rejected option %s\n", cps->option[--cps->nrOptions].name);
+ SendToProgram(buf, cps);
+ continue;
+ }
if(cps->nrOptions >= MAX_OPTIONS) {
cps->nrOptions--;
sprintf(buf, "%s engine has too many options\n", cps->which);