X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=a612db1d1eb5ddc0f089ec5f0d63a958afb0cba7;hb=98aa02bda540f17f1f50be00e494efafe439b004;hp=674b14a05b6db07cf313682e5f18ef8d1a447d1c;hpb=08c9436657f0649356c3b16d8cb8cafb0a3b8c31;p=xboard.git diff --git a/backend.c b/backend.c index 674b14a..a612db1 100644 --- a/backend.c +++ b/backend.c @@ -151,7 +151,6 @@ void SendToICS P((char *s)); 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)); @@ -242,6 +241,10 @@ 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 */ 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 @@ -456,16 +459,17 @@ AppData appData; 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, @@ -1452,6 +1456,13 @@ read_from_player(isr, closure, message, count, error) } 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; { @@ -2082,6 +2093,8 @@ read_from_ics(isr, closure, data, count, error) int tkind; int backup; /* [DM] For zippy color lines */ char *p; + char talker[MSG_SIZ]; // [HGM] chat + int channel; if (appData.debugMode) { if (!error) { @@ -2109,9 +2122,11 @@ read_from_ics(isr, closure, data, count, 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) + if(buf_len == 0 || buf[buf_len-1] != ' ') + buf[buf_len++] = ' '; // add space (assumes ICS does not break lines within word) + } } buf[buf_len] = NULLCHAR; @@ -2261,6 +2276,12 @@ read_from_ics(isr, closure, data, count, error) 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 @@ -2397,6 +2418,45 @@ read_from_ics(isr, closure, data, count, error) } } // [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= BOARD_RGHT || toY < 0 ) return ImpossibleMove; /* [HGM] 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 @@ -5161,7 +5222,6 @@ UserMoveTest(fromX, fromY, toX, toY, promoChar) 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) { @@ -5377,9 +5437,11 @@ UserMoveEvent(fromX, fromY, toX, toY, promoChar) 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); } @@ -5645,7 +5707,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h 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., @@ -5653,6 +5715,7 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h (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 @@ -6314,6 +6377,29 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h } #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; @@ -7389,9 +7475,6 @@ MakeMove(fromX, fromY, toX, toY, promoChar) 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; @@ -7401,6 +7484,9 @@ MakeMove(fromX, fromY, toX, toY, promoChar) 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); @@ -7487,7 +7573,7 @@ void SendEgtPath(ChessProgramState *cps) 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 } @@ -7718,6 +7804,28 @@ void UserAdjudicationEvent( int result ) } +// [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; i1 && 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; @@ -7867,7 +7975,9 @@ GameEnds(result, resultDetails, whosays) 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) { @@ -8192,6 +8302,7 @@ Reset(redraw, init) 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) { @@ -8211,6 +8322,7 @@ Reset(redraw, init) DisplayTitle(""); DisplayMessage("", ""); HistorySet(parseList, backwardMostMove, forwardMostMove, currentMove-1); + lastSavedGame = 0; // [HGM] save: make sure next game counts as unsaved } void @@ -9701,6 +9813,7 @@ SaveGamePGN(f) } fclose(f); + lastSavedGame = GameCheckSum(); // [HGM] save: remember ID of last saved game to prevent double saving return TRUE; } @@ -9777,6 +9890,7 @@ SaveGame(f, dummy, dummy2) 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 @@ -10451,6 +10565,7 @@ MachineWhiteEvent() SetMachineThinkingEnables(); first.maybeThinking = TRUE; StartClocks(); + firstMove = FALSE; if (appData.autoFlipView && !flipView) { flipView = !flipView; @@ -10883,8 +10998,19 @@ ExitAnalyzeMode() 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], ""); @@ -12170,7 +12296,8 @@ ReceiveFromProgram(isr, closure, message, count, error) 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 } @@ -12520,7 +12647,11 @@ ParseFeatures(args, cps) 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);