X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=265a55c249d6d43b29cfe88bba0cd274e4bdce54;hb=0d5d98e82dac22b0873aaf9fea8a1b13e1f9f349;hp=14f482927423d01ca4594b4059239e20e93274cc;hpb=96e7288ce19bb9b0e681e737c3113047a6475078;p=xboard.git diff --git a/backend.c b/backend.c index 14f4829..265a55c 100644 --- a/backend.c +++ b/backend.c @@ -450,15 +450,15 @@ int adjudicateLossPlies = 6; char white_holding[64], black_holding[64]; TimeMark lastNodeCountTime; long lastNodeCount=0; -int shiftKey; // [HGM] set by mouse handler +int shiftKey, controlKey; // [HGM] set by mouse handler int have_sent_ICS_logon = 0; int movesPerSession; int suddenDeath, whiteStartMove, blackStartMove; /* [HGM] for implementation of 'any per time' sessions, as in first part of byoyomi TC */ -long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack; +long whiteTimeRemaining, blackTimeRemaining, timeControl, timeIncrement, lastWhite, lastBlack, activePartnerTime; Boolean adjustedClock; long timeControl_2; /* [AS] Allow separate time controls */ -char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC; /* [HGM] secondary TC: merge of MPS, TC and inc */ +char *fullTimeControlString = NULL, *nextSession, *whiteTC, *blackTC, activePartner; /* [HGM] secondary TC: merge of MPS, TC and inc */ long timeRemaining[2][MAX_MOVES]; int matchGame = 0, nextGame = 0, roundNr = 0; Boolean waitingForGame = FALSE; @@ -851,7 +851,7 @@ LoadEngine () SendToProgram("force\n", savCps); DisplayMessage("", ""); if (startedFromSetupPosition) SendBoard(savCps, backwardMostMove); - for (i = backwardMostMove; i < forwardMostMove; i++) SendMoveToProgram(i, savCps); + for (i = backwardMostMove; i < currentMove; i++) SendMoveToProgram(i, savCps); ThawUI(); SetGNUMode(); } @@ -920,7 +920,7 @@ Load (ChessProgramState *cps, int i) while(q = strchr(p, SLASH)) p = q+1; if(*p== NULLCHAR) { DisplayError(_("You did not specify the engine executable"), 0); return; } if(engineDir[0] != NULLCHAR) { - ASSIGN(appData.directory[i], engineDir); + ASSIGN(appData.directory[i], engineDir); p = engineName; } else if(p != engineName) { // derive directory from engine path, when not given p[-1] = 0; ASSIGN(appData.directory[i], engineName); @@ -4244,6 +4244,7 @@ ParseBoard12 (char *string) if((gameMode == IcsPlayingWhite || gameMode == IcsPlayingBlack) && newGameMode == IcsObserving && gamenum != ics_gamenum && appData.bgObserve) { // [HGM] bughouse: don't act on alien boards while we play. Just parse the board and save it */ + int fac = strchr(elapsed_time, '.') ? 1 : 1000; char *toSqr; for (k = 0; k < ranks; k++) { for (j = 0; j < files; j++) @@ -4265,9 +4266,14 @@ ParseBoard12 (char *string) if(appData.dualBoard && !twoBoards) { twoBoards = 1; InitDrawingSizes(-2,0); } if(twoBoards) { partnerUp = 1; flipView = !flipView; } // [HGM] dual if(partnerUp) DrawPosition(FALSE, partnerBoard); - if(twoBoards) { partnerUp = 0; flipView = !flipView; } // [HGM] dual - snprintf(partnerStatus, MSG_SIZ,"W: %d:%02d B: %d:%02d (%d-%d) %c", white_time/60000, (white_time%60000)/1000, - (black_time/60000), (black_time%60000)/1000, white_stren, black_stren, to_play); + if(twoBoards) { + DisplayWhiteClock(white_time*fac, to_play == 'W'); + DisplayBlackClock(black_time*fac, to_play != 'W'); + activePartner = to_play; + activePartnerTime = to_play == 'W' ? white_time*fac : black_time*fac; + partnerUp = 0; flipView = !flipView; } // [HGM] dual + snprintf(partnerStatus, MSG_SIZ,"W: %d:%02d B: %d:%02d (%d-%d) %c", white_time*fac/60000, (white_time*fac%60000)/1000, + (black_time*fac/60000), (black_time*fac%60000)/1000, white_stren, black_stren, to_play); DisplayMessage(partnerStatus, ""); partnerBoardValid = TRUE; return; @@ -5167,6 +5173,12 @@ Sweep (int step) } while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn || appData.testLegality && (promoSweep == king || gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep)); + if(toX >= 0) { + int victim = boards[currentMove][toY][toX]; + boards[currentMove][toY][toX] = promoSweep; + DrawPosition(FALSE, boards[currentMove]); + boards[currentMove][toY][toX] = victim; + } else ChangeDragPiece(promoSweep); } @@ -6131,7 +6143,6 @@ WriteMap (int s) static void ClearMap () { - int j; safeStrCpy(exclusionHeader, "exclude: none best +tail \n", MSG_SIZ); excludePtr = 24; exCnt = 0; WriteMap(0); @@ -6163,7 +6174,7 @@ UpdateExcludeHeader (int fromY, int fromX, int toY, int toX, char promoChar, cha static int ExcludeOneMove (int fromY, int fromX, int toY, int toX, signed char promoChar, char state) { // include or exclude the given move, as specified by state ('+' or '-'), or toggle - char *p, buf[MSG_SIZ]; + char buf[MSG_SIZ]; int j, k; ChessMove moveType; if(promoChar == -1) { // kludge to indicate best move @@ -6190,7 +6201,6 @@ static void ExcludeClick (int index) { int i, j; - char buf[MSG_SIZ]; Exclusion *e = excluTab; if(index < 25) { // none, best or tail clicked if(index < 13) { // none: include all @@ -6663,7 +6673,7 @@ UserMoveEvent(int fromX, int fromY, int toX, int toY, int promoChar) if(boards[0][fromY][BOARD_WIDTH-2] == 0) boards[0][fromY][BOARD_WIDTH-1] = EmptySquare; } } else - boards[0][fromY][fromX] = EmptySquare; + boards[0][fromY][fromX] = gatingPiece; DrawPosition(FALSE, boards[currentMove]); return; } @@ -6915,11 +6925,11 @@ void MarkTargetSquares (int clear) { int x, y; + if(clear) // no reason to ever suppress clearing + for(x=0; x= BOARD_LEFT && x < BOARD_RGHT) clearFlag = 1; // and defer click-click move of empty-square to up-click return; } - if(appData.sweepSelect && HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) { + if(HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) { + if(appData.sweepSelect) { ChessSquare piece = boards[currentMove][fromY][fromX]; - DragPieceBegin(xPix, yPix, TRUE); dragging = 1; promoSweep = defaultPromoChoice; if(PieceToChar(PROMOTED piece) == '+') promoSweep = PROMOTED piece; selectFlag = 0; lastX = xPix; lastY = yPix; Sweep(0); // Pawn that is going to promote: preview promotion piece + sweepSelecting = 1; DisplayMessage("", _("Pull pawn backwards to under-promote")); - DrawPosition(FALSE, boards[currentMove]); - return; + MarkTargetSquares(1); + } + return; // promo popup appears on up-click } /* Finish clickclick move */ if (appData.animate || appData.highlightLastMove) { @@ -7205,6 +7223,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) /* Don't animate move and drag both */ appData.animate = FALSE; } + MarkTargetSquares(1); // moves into holding are invalid for now (except in EditPosition, adapting to-square) if(x >= 0 && x < BOARD_LEFT || x >= BOARD_RGHT) { @@ -7914,6 +7933,25 @@ SendMoveToBookUser (int moveNr, ChessProgramState *cps, int initial) return bookHit; // notify caller of hit, so it can take action to send move to opponent } +int +LoadError (char *errmess, ChessProgramState *cps) +{ // unloads engine and switches back to -ncp mode if it was first + if(cps->initDone) return FALSE; + cps->isr = NULL; // this should suppress further error popups from breaking pipes + DestroyChildProcess(cps->pr, 9 ); // just to be sure + cps->pr = NoProc; + if(cps == &first) { + appData.noChessProgram = TRUE; + gameMode = MachinePlaysBlack; ModeHighlight(); // kludge to unmark Machine Black menu + gameMode = BeginningOfGame; ModeHighlight(); + SetNCPMode(); + } + if(GetDelayedEvent()) CancelDelayedEvent(), ThawUI(); // [HGM] cancel remaining loading effort scheduled after feature timeout + DisplayMessage("", ""); // erase waiting message + if(errmess) DisplayError(errmess, 0); // announce reason, if given + return TRUE; +} + char *savedMessage; ChessProgramState *savedState; void @@ -7936,7 +7974,7 @@ HandleMachineMove (char *message, ChessProgramState *cps) ChessMove moveType; char promoChar; char *p, *pv=buf1; - int machineWhite; + int machineWhite, oldError; char *bookHit; if(cps == &pairing && sscanf(message, "%d-%d", &savedWhitePlayer, &savedBlackPlayer) == 2) { @@ -7950,7 +7988,7 @@ HandleMachineMove (char *message, ChessProgramState *cps) return; // Skim the pairing messages here. } - cps->userError = 0; + oldError = cps->userError; cps->userError = 0; FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book hit /* @@ -8519,18 +8557,8 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. _(cps->which), cps->program, cps->host, message); RemoveInputSource(cps->isr); if(appData.icsActive) DisplayFatalError(buf1, 0, 1); else { - cps->isr = NULL; - DestroyChildProcess(cps->pr, 9 ); // just to be sure - cps->pr = NoProc; - if(cps == &first) { - appData.noChessProgram = TRUE; - gameMode = MachinePlaysBlack; ModeHighlight(); // kludge to unmark Machine Black menu - gameMode = BeginningOfGame; ModeHighlight(); - SetNCPMode(); - } - if(GetDelayedEvent()) CancelDelayedEvent(), ThawUI(); // [HGM] cancel remaining loading effort scheduled after feature timeout - DisplayMessage("", ""); // erase waiting message - DisplayError(buf1, 0); + if(LoadError(oldError ? NULL : buf1, cps)) return; // error has then been handled by LoadError + if(!oldError) DisplayError(buf1, 0); // if reason neatly announced, suppress general error popup } return; } @@ -11514,7 +11542,7 @@ QuickCompare (Board board, int *minCounts, int *maxCounts) int QuickScan (Board board, Move *move) { // reconstruct game,and compare all positions in it - int cnt=0, stretch=0, total = MakePieceList(board, counts); + int cnt=0, stretch=0, total = MakePieceList(board, counts), delayedKing = -1; do { int piece = move->piece; int to = move->to, from = pieceList[piece]; @@ -11532,18 +11560,22 @@ QuickScan (Board board, Move *move) move++; continue; } else if(piece <= Q_BCASTL) { // castling, encoded as (Q_XCASTL, king-to) + (rook, rook-to) + int rook; piece = pieceList[piece]; // first two elements of pieceList contain King numbers from = pieceList[piece]; // so this must be King quickBoard[from] = 0; - quickBoard[to] = piece; pieceList[piece] = to; - move++; - continue; + from = pieceList[(++move)->piece]; // for FRC this has to be done here + quickBoard[from] = 0; // rook + quickBoard[to] = piece; + to = move->to; piece = move->piece; + goto aftercastle; } } if(appData.searchMode > 2) counts[pieceType[quickBoard[to]]]--; // account capture if((total -= (quickBoard[to] != 0)) < soughtTotal) return -1; // piece count dropped below what we search for quickBoard[from] = 0; + aftercastle: quickBoard[to] = piece; pieceList[piece] = to; cnt++; turn ^= 3; @@ -11558,7 +11590,7 @@ QuickScan (Board board, Move *move) if(stretch++ == 0) for(i=0; i= appData.stretch)) return cnt + 1 - stretch; - move++; + move++; delayedKing = -1; } while(1); } @@ -12142,7 +12174,7 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) AnalyzeFileEvent(); } - if (!matchMode && pos >= 0) { + if (!matchMode && pos > 0) { ToNrEvent(pos); // [HGM] no autoplay if selected on position } else if (matchMode || appData.timeDelay == 0) { @@ -13555,6 +13587,11 @@ TwoMachinesEvent P((void)) if (appData.noChessProgram) return; + if(second.protocolVersion >= 2 && !strstr(second.variants, VariantName(gameInfo.variant))) { + DisplayError("second engine does not play this", 0); + return; + } + switch (gameMode) { case TwoMachinesPlay: return; @@ -15251,10 +15288,10 @@ ReceiveFromProgram (InputSourceRef isr, VOIDSTAR closure, char *message, int cou if (count <= 0) { if (count == 0) { RemoveInputSource(cps->isr); - if(!cps->initDone) return; // [HGM] should not generate fatal error during engine load snprintf(buf, MSG_SIZ, _("Error: %s chess program (%s) exited unexpectedly"), _(cps->which), cps->program); - if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */ + if(LoadError(cps->userError ? NULL : buf, cps)) return; // [HGM] should not generate fatal error during engine load + if(gameInfo.resultDetails==NULL) { /* [HGM] crash: if game in progress, give reason for abort */ if((signed char)boards[forwardMostMove][EP_STATUS] <= EP_DRAWS) { snprintf(buf, MSG_SIZ, _("%s program exits in draw position (%s)"), _(cps->which), cps->program); if(matchMode && appData.tourneyFile[0]) { cps->pr = NoProc; GameEnds(GameIsDrawn, buf, GE_XBOARD); return; } @@ -15536,8 +15573,8 @@ ParseOption (Option *opt, ChessProgramState *cps) if(sscanf(p, " -check %d", &def) < 1) return FALSE; opt->value = (def != 0); opt->type = CheckBox; - } else if(p = strstr(opt->name, " -combo ")) { - opt->textValue = (char*) (&cps->comboList[cps->comboCnt]); // cheat with pointer type + } else if(p = strstr(opt->name, " -combo ")) { + opt->textValue = (char*) (opt->choice = &cps->comboList[cps->comboCnt]); // cheat with pointer type cps->comboList[cps->comboCnt++] = q = p+8; // holds possible choices if(*q == '*') cps->comboList[cps->comboCnt-1]++; opt->value = n = 0; @@ -15658,7 +15695,10 @@ ParseFeatures (char *args, ChessProgramState *cps) if (BoolFeature(&p, "memory", &cps->memSize, cps)) continue; 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)) { + if (StringFeature(&p, "option", buf, cps)) { + FREE(cps->option[cps->nrOptions].name); + cps->option[cps->nrOptions].name = malloc(MSG_SIZ); + safeStrCpy(cps->option[cps->nrOptions].name, buf, MSG_SIZ); if(!ParseOption(&(cps->option[cps->nrOptions++]), cps)) { // [HGM] options: add option feature snprintf(buf, MSG_SIZ, "rejected option %s\n", cps->option[--cps->nrOptions].name); SendToProgram(buf, cps); @@ -16190,6 +16230,16 @@ DecrementClocks () } if (CheckFlags()) return; + if(twoBoards) { // count down secondary board's clocks as well + activePartnerTime -= lastTickLength; + partnerUp = 1; + if(activePartner == 'W') + DisplayWhiteClock(activePartnerTime, TRUE); // the counting clock is always the highlighted one! + else + DisplayBlackClock(activePartnerTime, TRUE); + partnerUp = 0; + } + tickStartTM = now; intendedTickLength = NextTickLength(timeRemaining - fudge) + fudge; StartClockTimer(intendedTickLength);