X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=f7c75b68b2634426b6ffbfa54a7199cd4d2f2cd5;hb=e10b34662822bd8dc06680b6b9977966816450de;hp=bb52007006a024234dcdf8601b640ee48a7887ca;hpb=75c82871211daf66c50935b39427e78922d20a5e;p=xboard.git diff --git a/backend.c b/backend.c index bb52007..f7c75b6 100644 --- a/backend.c +++ b/backend.c @@ -132,8 +132,13 @@ extern int gettimeofday(struct timeval *, struct timezone *); # define _(s) gettext (s) # define N_(s) gettext_noop (s) #else -# define _(s) (s) -# define N_(s) s +# ifdef WIN32 +# define _(s) T_(s) +# define N_(s) s +# else +# define _(s) (s) +# define N_(s) s +# endif #endif @@ -234,6 +239,7 @@ char *ProbeBook P((int moveNr, char *book)); // [HGM] book: returns a book move char *SendMoveToBookUser P((int nr, ChessProgramState *cps, int initial)); // [HGM] book void ics_update_width P((int new_width)); extern char installDir[MSG_SIZ]; +VariantClass startVariant; /* [HGM] nicks: initial variant */ extern int tinyLayout, smallLayout; ChessProgramStats programStats; @@ -637,6 +643,7 @@ InitBackEnd1() int matched, min, sec; ShowThinkingEvent(); // [HGM] thinking: make sure post/nopost state is set according to options + startVariant = StringToVariant(appData.variant); // [HGM] nicks: remember original variant GetTimeMark(&programStartTime); srandom((programStartTime.ms + 1000*programStartTime.sec)*0x1001001); // [HGM] book: makes sure random is unpredictabe to msec level @@ -712,8 +719,8 @@ InitBackEnd1() /* [AS] Adjudication threshold */ adjudicateLossThreshold = appData.adjudicateLossThreshold; - first.which = "first"; - second.which = "second"; + first.which = _("first"); + second.which = _("second"); first.maybeThinking = second.maybeThinking = FALSE; first.pr = second.pr = NoProc; first.isr = second.isr = NULL; @@ -2204,8 +2211,8 @@ MatchSoughtLine(char *line) int DrawSeekGraph() { - if(!seekGraphUp) return FALSE; int i; + if(!seekGraphUp) return FALSE; h = BOARD_HEIGHT * (squareSize + lineGap) + lineGap; w = BOARD_WIDTH * (squareSize + lineGap) + lineGap; @@ -2836,18 +2843,9 @@ read_from_ics(isr, closure, data, count, error) /* [DM] Backup address for color zippy lines */ backup = i; #if ZIPPY - #ifdef WIN32 if (loggedOn == TRUE) if (ZippyControl(buf, &backup) || ZippyConverse(buf, &backup) || (appData.zippyPlay && ZippyMatch(buf, &backup))); - #else - if (ZippyControl(buf, &i) || - ZippyConverse(buf, &i) || - (appData.zippyPlay && ZippyMatch(buf, &i))) { - loggedOn = TRUE; - if (!appData.colorize) continue; - } - #endif #endif } // [DM] 'else { ' deleted if ( @@ -4530,7 +4528,7 @@ SendMoveToProgram(moveNum, cps) /* Send 'go' if we are in a mode where machine should play. */ if( (moveNum == 0 && setboardSpoiledMachineBlack && cps == &first) && (gameMode == TwoMachinesPlay || -#ifdef ZIPPY +#if ZIPPY gameMode == IcsPlayingBlack || gameMode == IcsPlayingWhite || #endif gameMode == MachinePlaysBlack || gameMode == MachinePlaysWhite) ) { @@ -5260,7 +5258,10 @@ InitPosition(redraw) for(i=0; i 2) return FALSE; // no trivial draws with more than 1 major + if(myPawns == 2 && nMine == 3) // KPP + return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] >= 3; + if(myPawns == 1 && nMine == 2) // KP + return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] + pCnt[BlackPawn-side] >= 1; + if(myPawns == 1 && nMine == 3 && pCnt[WhiteKnight+side]) // KHP + return majorDefense || pCnt[BlackFerz-side] + pCnt[BlackAlfil-side]*2 >= 5; + if(myPawns) return FALSE; + if(pCnt[WhiteRook+side]) + return pCnt[BlackRook-side] || + pCnt[BlackCannon-side] && (pCnt[BlackFerz-side] >= 2 || pCnt[BlackAlfil-side] >= 2) || + pCnt[BlackKnight-side] && pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] > 2 || + pCnt[BlackFerz-side] + pCnt[BlackAlfil-side] >= 4; + if(pCnt[WhiteCannon+side]) { + if(pCnt[WhiteFerz+side] + myPawns == 0) return TRUE; // Cannon needs platform + return majorDefense || pCnt[BlackAlfil-side] >= 2; + } + if(pCnt[WhiteKnight+side]) + return majorDefense || pCnt[BlackFerz-side] >= 2 || pCnt[BlackAlfil-side] + pCnt[BlackPawn-side] >= 1; + return FALSE; +} + +int +MatingPotential(int pCnt[], int side, int nMine, int nHis, int stale, int bisColor) +{ + VariantClass v = gameInfo.variant; + + if(v == VariantShogi || v == VariantCrazyhouse || v == VariantBughouse) return TRUE; // drop games always winnable + if(v == VariantShatranj) return TRUE; // always winnable through baring + if(v == VariantLosers || v == VariantSuicide || v == VariantGiveaway) return TRUE; + if(v == Variant3Check || v == VariantAtomic) return nMine > 1; // can win through checking / exploding King + + if(v == VariantXiangqi) { + int majors = 5*pCnt[BlackKnight-side] + 7*pCnt[BlackCannon-side] + 7*pCnt[BlackRook-side]; + + nMine -= pCnt[WhiteFerz+side] + pCnt[WhiteAlfil+side] + stale; // discount defensive pieces and back-rank Pawns + if(nMine + stale == 1) return (pCnt[BlackFerz-side] > 1 && pCnt[BlackKnight-side] > 0); // bare K can stalemate KHAA (!) + if(nMine > 2) return TRUE; // if we don't have P, H or R, we must have CC + if(nMine == 2 && pCnt[WhiteCannon+side] == 0) return TRUE; // We have at least one P, H or R + // if we get here, we must have KC... or KP..., possibly with additional A, E or last-rank P + if(stale) // we have at least one last-rank P plus perhaps C + return majors // KPKX + || pCnt[BlackFerz-side] && pCnt[BlackFerz-side] + pCnt[WhiteCannon+side] + stale > 2; // KPKAA, KPPKA and KCPKA + else // KCA*E* + return pCnt[WhiteFerz+side] // KCAK + || pCnt[WhiteAlfil+side] && pCnt[BlackRook-side] + pCnt[BlackCannon-side] + pCnt[BlackFerz-side] // KCEKA, KCEKX (X!=H) + || majors + (12*pCnt[BlackFerz-side] | 6*pCnt[BlackAlfil-side]) > 16; // KCKAA, KCKAX, KCKEEX, KCKEXX (XX!=HH), KCKXXX + // TO DO: cases wih an unpromoted f-Pawn acting as platform for an opponent Cannon + + } else if(pCnt[WhiteKing] == 1 && pCnt[BlackKing] == 1) { // other variants with orthodox Kings + int nBishops = pCnt[WhiteBishop+side] + pCnt[WhiteFerz+side]; + + if(nMine == 1) return FALSE; // bare King + if(nBishops && bisColor == 3) return TRUE; // There must be a second B/A/F, which can either block (his) or attack (mine) the escape square + nMine += (nBishops > 0) - nBishops; // By now all Bishops (and Ferz) on like-colored squares, so count as one + if(nMine > 2 && nMine != pCnt[WhiteAlfil+side] + 1) return TRUE; // At least two pieces, not all Alfils + // by now we have King + 1 piece (or multiple Bishops on the same color) + if(pCnt[WhiteKnight+side]) + return (pCnt[BlackKnight-side] + pCnt[BlackBishop-side] + pCnt[BlackMan-side] + + pCnt[BlackWazir-side] + pCnt[BlackSilver-side] + bisColor // KNKN, KNKB, KNKF, KNKE, KNKW, KNKM, KNKS + || nHis > 3); // be sure to cover suffocation mates in corner (e.g. KNKQCA) + if(nBishops) + return (pCnt[BlackKnight-side]); // KBKN, KFKN + if(pCnt[WhiteAlfil+side]) + return (nHis > 2); // Alfils can in general not reach a corner square, but there might be edge (suffocation) mates + if(pCnt[WhiteWazir+side]) + return (pCnt[BlackKnight-side] + pCnt[BlackWazir-side] + pCnt[BlackAlfil-side]); // KWKN, KWKW, KWKE + } + + return TRUE; +} + int Adjudicate(ChessProgramState *cps) { // [HGM] some adjudications useful with buggy engines @@ -6606,63 +6704,16 @@ Adjudicate(ChessProgramState *cps) if(gameInfo.holdingsSize == 0 || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { if( appData.testLegality ) { /* [HGM] Some more adjudications for obstinate engines */ - int NrWN=0, NrBN=0, NrWB=0, NrBB=0, NrWR=0, NrBR=0, - NrWQ=0, NrBQ=0, NrW=0, NrK=0, bishopsColor = 0, - NrPieces=0, NrPawns=0, PawnAdvance=0, i, j; + int nrW, nrB, bishopColor, staleW, staleB, nr[EmptySquare+1], i; static int moveCount = 6; ChessMove result; char *reason = NULL; /* Count what is on board. */ - for(i=0; i 1 ? WhiteWins : NrPieces - NrW > 1 ? BlackWins : GameIsDrawn, + GameEnds( nrW > 1 ? WhiteWins : nrB > 1 ? BlackWins : GameIsDrawn, "Xboard adjudication: Bare king", GE_XBOARD ); return 1; } @@ -6730,8 +6781,8 @@ Adjudicate(ChessProgramState *cps) if(gameInfo.variant == VariantLosers || gameInfo.variant == VariantGiveaway) // [HGM] losers: boards[forwardMostMove][EP_STATUS] = EP_WINS; // in these variants stalemated is always a win else if(gameInfo.variant == VariantSuicide) // in suicide it depends - boards[forwardMostMove][EP_STATUS] = NrW == NrPieces-NrW ? EP_STALEMATE : - ((NrW < NrPieces-NrW) != WhiteOnMove(forwardMostMove) ? + boards[forwardMostMove][EP_STATUS] = nrW == nrB ? EP_STALEMATE : + ((nrW < nrB) != WhiteOnMove(forwardMostMove) ? EP_CHECKMATE : EP_WINS); else if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi) boards[forwardMostMove][EP_STATUS] = EP_CHECKMATE; // and in these variants being stalemated loses @@ -6762,14 +6813,9 @@ Adjudicate(ChessProgramState *cps) } /* Next absolutely insufficient mating material. */ - if( NrPieces == 2 || gameInfo.variant != VariantXiangqi && - gameInfo.variant != VariantShatranj && // [HGM] baring will remain possible - (NrPieces == 3 && NrWN+NrBN+NrWB+NrBB == 1 || - NrPieces == NrBB+NrWB+2 && bishopsColor != 3) // [HGM] all Bishops (Ferz!) same color - || gameInfo.variant == VariantXiangqi && - (NrPieces == 3 && (NrWQ==1 && NrWB==0 && NrBB<2 || NrBQ==1 && NrBB==0 && NrWB<2) || - NrPieces == 4 && NrWQ==1 && NrBQ==1 && NrWB==0 && NrBB==0)) - { /* KBK, KNK, KK of KBKB with like Bishops */ + if(!MatingPotential(nr, WhitePawn, nrW, nrB, staleW, bishopColor) && + !MatingPotential(nr, BlackPawn, nrB, nrW, staleB, bishopColor)) + { /* includes KBK, KNK, KK of KBKB with like Bishops */ /* always flag draws, for judging claims */ boards[forwardMostMove][EP_STATUS] = EP_INSUF_DRAW; @@ -6787,12 +6833,14 @@ Adjudicate(ChessProgramState *cps) } /* Then some trivial draws (only adjudicate, cannot be claimed) */ - if(NrPieces == 4 && - ( NrWR == 1 && NrBR == 1 /* KRKR */ - || NrWQ==1 && NrBQ==1 /* KQKQ */ - || NrWN==2 || NrBN==2 /* KNNK */ - || NrWN+NrWB == 1 && NrBN+NrBB == 1 /* KBKN, KBKB, KNKN */ - ) ) { + if(gameInfo.variant == VariantXiangqi ? + SufficientDefence(nr, WhitePawn, nrW, nrB) && SufficientDefence(nr, BlackPawn, nrB, nrW) + : nrW + nrB == 4 && + ( nr[WhiteRook] == 1 && nr[BlackRook] == 1 /* KRKR */ + || nr[WhiteQueen] && nr[BlackQueen]==1 /* KQKQ */ + || nr[WhiteKnight]==2 || nr[BlackKnight]==2 /* KNNK */ + || nr[WhiteKnight]+nr[WhiteBishop] == 1 && nr[BlackKnight]+nr[BlackBishop] == 1 /* KBKN, KBKB, KNKN */ + ) ) { if(--moveCount < 0 && appData.trivialDraws && canAdjudicate) { /* if the first 3 moves do not show a tactical win, declare draw */ if(engineOpponent) { @@ -8973,7 +9021,7 @@ GameEnds(result, resultDetails, whosays) { GameMode nextGameMode; int isIcsGame; - char buf[MSG_SIZ]; + char buf[MSG_SIZ], popupRequested = 0; if(endingGame) return; /* [HGM] crash: forbid recursion */ endingGame = 1; @@ -9309,13 +9357,12 @@ GameEnds(result, resultDetails, whosays) endingGame = 0; /* [HGM] crash */ return; } else { - char buf[MSG_SIZ]; gameMode = nextGameMode; sprintf(buf, _("Match %s vs. %s: final score %d-%d-%d"), first.tidy, second.tidy, first.matchWins, second.matchWins, appData.matchGames - (first.matchWins + second.matchWins)); - DisplayFatalError(buf, 0, 0); + popupRequested++; // [HGM] crash: postpone to after resetting endingGame } } if ((gameMode == AnalyzeMode || gameMode == AnalyzeFile) && @@ -9324,6 +9371,12 @@ GameEnds(result, resultDetails, whosays) gameMode = nextGameMode; ModeHighlight(); endingGame = 0; /* [HGM] crash */ + if(popupRequested) { // [HGM] crash: this calls GameEnds recursively through ExitEvent! Make it a harmless tail recursion. + if(matchMode == TRUE) DisplayFatalError(buf, 0, 0); else { + matchMode = FALSE; appData.matchGames = matchGame = 0; + DisplayNote(buf); + } + } } /* Assumes program was just initialized (initString sent). @@ -13959,7 +14012,7 @@ DisplayMove(moveNumber) sprintf(res, " %s", PGNResult(gameInfo.result)); } else { sprintf(res, " {%s} %s", - gameInfo.resultDetails, PGNResult(gameInfo.result)); + T_(gameInfo.resultDetails), PGNResult(gameInfo.result)); } } else { res[0] = NULLCHAR; @@ -14751,7 +14804,7 @@ ParseFEN(board, blackPlaysFirst, fen) char *fen; { int i, j; - char *p; + char *p, c; int emptycount; ChessSquare piece; @@ -14844,7 +14897,12 @@ ParseFEN(board, blackPlaysFirst, fen) while(*p == ' ') p++; /* Active color */ - switch (*p++) { + c = *p++; + if(appData.colorNickNames) { + if( c == appData.colorNickNames[0] ) c = 'w'; else + if( c == appData.colorNickNames[1] ) c = 'b'; + } + switch (c) { case 'w': *blackPlaysFirst = FALSE; break;