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;
FILE *serverMoves = NULL; // next two for broadcasting (/serverMoves option)
int loadFlag = 0;
int shuffleOpenings;
+int mute; // mute all sounds
ChessSquare FIDEArray[2][BOARD_SIZE] = {
{ WhiteRook, WhiteKnight, WhiteBishop, WhiteQueen,
}
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
+ int channel;
if (appData.debugMode) {
if (!error) {
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_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)
+ if(buf_len == 0 || buf[buf_len-1] != ' ')
+ buf[buf_len++] = ' '; // add space (assumes ICS does not break lines within word)
}
}
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
+ channel = -1;
+ if(started == STARTED_NONE && (looking_at(buf, &i, "* tells you:") || looking_at(buf, &i, "* says:") ||
+ looking_at(buf, &i, "* whispers:") ||
+ looking_at(buf, &i, "*(*):") && (sscanf(star_match[1], "%d", &channel),1) ||
+ looking_at(buf, &i, "*(*)(*):") && sscanf(star_match[2], "%d", &channel) == 1 )) {
+ int p;
+ sscanf(star_match[0], "%[^(]", talker+1); // strip (C) or (U) off ICS handle
+ chattingPartner = -1;
+
+ if(channel >= 0) // channel broadcast; look if there is a chatbox for this channel
+ for(p=0; p<MAX_CHAT; p++) {
+ if(channel == atoi(chatPartner[p])) {
+ talker[0] = '['; strcat(talker, "]");
+ chattingPartner = p; break;
+ }
+ } else
+ 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;
break;
}
SendToICS(user_move);
+ if(appData.keepAlive) // [HGM] alive: schedule sending of dummy 'date' command
+ ScheduleDelayedEvent(KeepAlive, appData.keepAlive*60*1000);
}
void
oldh = gameInfo.holdingsWidth,
oldv = gameInfo.variant;
- currentMove = forwardMostMove = backwardMostMove = 0;
if(appData.icsActive) shuffleOpenings = FALSE; // [HGM] shuffle: in ICS mode, only shuffle on ICS request
/* [AS] Initialize pv info list [HGM] and game status */
char lastLoadGameTitle[MSG_SIZ], lastLoadPositionTitle[MSG_SIZ];
ChessMove lastLoadGameStart = (ChessMove) 0;
-
ChessMove
-UserMoveTest(fromX, fromY, toX, toY, promoChar)
+UserMoveTest(fromX, fromY, toX, toY, promoChar, captureOwn)
int fromX, fromY, toX, toY;
int promoChar;
+ Boolean captureOwn;
{
ChessMove moveType;
ChessSquare pdown, pup;
if (fromX < 0 || fromY < 0) return ImpossibleMove;
- if ((fromX == toX) && (fromY == toY)) {
- 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: this code seems to exist for filtering out some obviously illegal premoves */
+ /* 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 &&
+ 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 == 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 ImpossibleMove;
+ 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
moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
epStatus[currentMove], castlingRights[currentMove],
fromY, fromX, toY, toX, promoChar);
-
/* [HGM] but possibly ignore an IllegalMove result */
if (appData.testLegality) {
if (moveType == IllegalMove || moveType == ImpossibleMove) {
FinishMove if the first part succeeded. Calls that do not need
to do anything in between, can call this routine the old way.
*/
- ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar);
+ ChessMove moveType = UserMoveTest(fromX, fromY, toX, toY, promoChar, FALSE);
if(appData.debugMode) fprintf(debugFP, "moveType 4 = %d, promochar = %x\n", moveType, promoChar);
- if(moveType != ImpossibleMove)
+ if(moveType == AmbiguousMove)
+ DrawPosition(FALSE, boards[currentMove]);
+ else if(moveType != ImpossibleMove && moveType != Comment)
FinishMove(moveType, fromX, fromY, toX, toY, promoChar);
}
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
}
#endif
if (pausing) PauseEvent();
+ if(appData.forceIllegal) {
+ // [HGM] illegal: machine refused move; force position after move into it
+ SendToProgram("force\n", cps);
+ if(!cps->useSetboard) { // hideous kludge on kludge, because SendBoard sucks.
+ // we have a real problem now, as SendBoard will use the a2a3 kludge
+ // when black is to move, while there might be nothing on a2 or black
+ // might already have the move. So send the board as if white has the move.
+ // But first we must change the stm of the engine, as it refused the last move
+ SendBoard(cps, 0); // always kludgeless, as white is to move on boards[0]
+ if(WhiteOnMove(forwardMostMove)) {
+ SendToProgram("a7a6\n", cps); // for the engine black still had the move
+ SendBoard(cps, forwardMostMove); // kludgeless board
+ } else {
+ SendToProgram("a2a3\n", cps); // for the engine white still had the move
+ CopyBoard(boards[forwardMostMove+1], boards[forwardMostMove]);
+ SendBoard(cps, forwardMostMove+1); // kludgeless board
+ }
+ } else SendBoard(cps, forwardMostMove); // FEN case, also sets stm properly
+ if(gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack ||
+ gameMode == TwoMachinesPlay)
+ SendToProgram("go\n", cps);
+ return;
+ } else
if (gameMode == PlayFromGameFile) {
/* Stop reading this game file */
gameMode = EditGame;
0, 1);
return;
}
- SwitchClocks();
- timeRemaining[0][forwardMostMove+1] = whiteTimeRemaining;
- timeRemaining[1][forwardMostMove+1] = blackTimeRemaining;
if (commentList[forwardMostMove+1] != NULL) {
free(commentList[forwardMostMove+1]);
commentList[forwardMostMove+1] = NULL;
ApplyMove(fromX, fromY, toX, toY, promoChar, boards[forwardMostMove+1],
castlingRights[forwardMostMove+1], &epStatus[forwardMostMove+1]);
forwardMostMove++; // [HGM] bare: moved to after ApplyMove, to make sure clock interrupt finds complete board
+ SwitchClocks(); // uses forwardMostMove, so must be done after incrementing it !
+ timeRemaining[0][forwardMostMove] = whiteTimeRemaining;
+ timeRemaining[1][forwardMostMove] = blackTimeRemaining;
gameInfo.result = GameUnfinished;
if (gameInfo.resultDetails != NULL) {
free(gameInfo.resultDetails);
while(*r && *r != ',') r++; // path info is everything upto next ';' or end of string
c = *r; *r = 0; // temporarily null-terminate path info
*--q = 0; // strip of trailig ':' from name
- sprintf(buf, "egtbpath %s %s\n", name+1, s);
+ sprintf(buf, "egtpath %s %s\n", name+1, s);
*r = c;
SendToProgram(buf,cps); // send egtbpath command for this format
}
gameMode = BeginningOfGame;
ModeHighlight();
if(appData.icsActive) gameInfo.variant = VariantNormal;
+ currentMove = forwardMostMove = backwardMostMove = 0;
InitPosition(redraw);
for (i = 0; i < MAX_MOVES; i++) {
if (commentList[i] != NULL) {
SetMachineThinkingEnables();
first.maybeThinking = TRUE;
StartClocks();
+ firstMove = FALSE;
if (appData.autoFlipView && !flipView) {
flipView = !flipView;
sscanf(message, "resign%c", &c)!=1 && sscanf(message, "feature %c", &c)!=1 &&
sscanf(message, "error %c", &c)!=1 && sscanf(message, "illegal %c", &c)!=1 &&
sscanf(message, "tell%c", &c)!=1 && sscanf(message, "0-1 %c", &c)!=1 &&
- sscanf(message, "1-0 %c", &c)!=1 && sscanf(message, "1/2-1/2 %c", &c)!=1 && start != '#')
+ sscanf(message, "1-0 %c", &c)!=1 && sscanf(message, "1/2-1/2 %c", &c)!=1 &&
+ sscanf(message, "pong %c", &c)!=1 && start != '#')
{ quote = "# "; print = (appData.engineComments == 2); }
message[0] = start; // restore original message
}