X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=bd641fea724a47c2012766ff35c28f13c6a11367;hb=357e76cac0dedc4522e0018b0fb49abbe879c4da;hp=25fe5f59766d072672afae0fd948753dd4ffcad8;hpb=91f35c4c0e1494e9711f26bb86f843e176c81321;p=xboard.git diff --git a/backend.c b/backend.c index 25fe5f5..bd641fe 100644 --- a/backend.c +++ b/backend.c @@ -311,23 +311,19 @@ char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */ char* safeStrCpy( char *dst, const char *src, size_t count ) -{ - /* see for example: https://buildsecurityin.us-cert.gov/bsi-rules/home/g1/854-BSI.html - * - * usage: safeStrCpy( stringA, stringB, sizeof(stringA)/sizeof(stringA[0]); - */ - +{ // [HGM] made safe + int i; assert( dst != NULL ); assert( src != NULL ); assert( count > 0 ); - strncpy( dst, src, count ); - if( dst[ count-1 ] != '\0' ) + for(i=0; i= BOARD_RGHT) return; @@ -6459,6 +6480,7 @@ void LeftClick(ClickType clickType, int xPix, int yPix) /* Second up/down in same square; just abort move */ second = 0; fromX = fromY = -1; + gatingPiece = EmptySquare; ClearHighlights(); gotPremove = 0; ClearPremoveHighlights(); @@ -6521,7 +6543,9 @@ void LeftClick(ClickType clickType, int xPix, int yPix) } // off-board moves should not be highlighted - if(x < 0 || x < 0) ClearHighlights(); + if(x < 0 || y < 0) ClearHighlights(); + + if(gatingPiece != EmptySquare) promoChoice = ToLower(PieceToChar(gatingPiece)); if (HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice)) { SetHighlights(fromX, fromY, toX, toY); @@ -7456,6 +7480,15 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. return; // [HGM] This return was missing, causing option features to be recognized as non-compliant commands! } + if (!appData.testLegality && !strncmp(message, "setup ", 6)) { // [HGM] allow first engine to define opening position + int dummy, s=6; char buf[MSG_SIZ]; + if(appData.icsActive || forwardMostMove != 0 || cps != &first || startedFromSetupPosition) return; + if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf); + ParseFEN(boards[0], &dummy, message+s); + DrawPosition(TRUE, boards[0]); + startedFromSetupPosition = TRUE; + return; + } /* [HGM] Allow engine to set up a position. Don't ask me why one would * want this, I was asked to put it in, and obliged. */ @@ -7482,13 +7515,15 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. * Look for communication commands */ if (!strncmp(message, "telluser ", 9)) { - EscapeExpand(message+9, message+9); // [HGM] esc: allow escape sequences in popup box + if(message[9] == '\\' && message[10] == '\\') + EscapeExpand(message+9, message+11); // [HGM] esc: allow escape sequences in popup box DisplayNote(message + 9); return; } if (!strncmp(message, "tellusererror ", 14)) { cps->userError = 1; - EscapeExpand(message+14, message+14); // [HGM] esc: allow escape sequences in popup box + if(message[14] == '\\' && message[15] == '\\') + EscapeExpand(message+14, message+16); // [HGM] esc: allow escape sequences in popup box DisplayError(message + 14, 0); return; } @@ -8212,7 +8247,7 @@ ParseGameHistory(game) yynewstr(game); for (;;) { yyboardindex = boardIndex; - moveType = (ChessMove) yylex(); + moveType = (ChessMove) Myylex(); switch (moveType) { case IllegalMove: /* maybe suicide chess, etc. */ if (appData.debugMode) { @@ -8378,10 +8413,6 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) if( board[toY][toX] != EmptySquare ) board[EP_STATUS] = EP_CAPTURE; - /* [HGM] In Shatranj and Courier all promotions are to Ferz */ - if((gameInfo.variant==VariantShatranj || gameInfo.variant==VariantCourier || gameInfo.variant == VariantMakruk) - && promoChar != 0) promoChar = PieceToChar(WhiteFerz); - if (fromY == DROP_RANK) { /* must be first */ piece = board[toY][toX] = (ChessSquare) fromX; @@ -8427,21 +8458,21 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) king += (int) WhiteUnicorn - (int) WhiteKing; /* Code added by Tord: */ - /* FRC castling assumed when king captures friendly rook. */ - if (board[fromY][fromX] == WhiteKing && - board[toY][toX] == WhiteRook) { + /* FRC castling assumed when king captures friendly rook. [HGM] or RxK for S-Chess */ + if (board[fromY][fromX] == WhiteKing && board[toY][toX] == WhiteRook || + board[fromY][fromX] == WhiteRook && board[toY][toX] == WhiteKing) { board[fromY][fromX] = EmptySquare; board[toY][toX] = EmptySquare; - if(toX > fromX) { + if((toX > fromX) != (piece == WhiteRook)) { board[0][BOARD_RGHT-2] = WhiteKing; board[0][BOARD_RGHT-3] = WhiteRook; } else { board[0][BOARD_LEFT+2] = WhiteKing; board[0][BOARD_LEFT+3] = WhiteRook; } - } else if (board[fromY][fromX] == BlackKing && - board[toY][toX] == BlackRook) { + } else if (board[fromY][fromX] == BlackKing && board[toY][toX] == BlackRook || + board[fromY][fromX] == BlackRook && board[toY][toX] == BlackKing) { board[fromY][fromX] = EmptySquare; board[toY][toX] = EmptySquare; - if(toX > fromX) { + if((toX > fromX) != (piece == BlackRook)) { board[BOARD_HEIGHT-1][BOARD_RGHT-2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_RGHT-3] = BlackRook; } else { board[BOARD_HEIGHT-1][BOARD_LEFT+2] = BlackKing; board[BOARD_HEIGHT-1][BOARD_LEFT+3] = BlackRook; @@ -8574,10 +8605,11 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) /* [HGM] OK, so I have written it. Holdings are stored in the */ /* penultimate board files, so they are automaticlly stored */ /* in the game history. */ - if (fromY == DROP_RANK) { + if (fromY == DROP_RANK || gameInfo.variant == VariantSChess + && promoChar && piece != WhitePawn && piece != BlackPawn) { /* Delete from holdings, by decreasing count */ /* and erasing image if necessary */ - p = (int) fromX; + p = fromY == DROP_RANK ? (int) fromX : CharToPiece(piece > BlackPawn ? ToLower(promoChar) : ToUpper(promoChar)); if(p < (int) BlackPawn) { /* white drop */ p -= (int)WhitePawn; p = PieceToNumber((ChessSquare)p); @@ -8597,7 +8629,7 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) } } if (captured != EmptySquare && gameInfo.holdingsSize > 0 - && gameInfo.variant != VariantBughouse ) { + && gameInfo.variant != VariantBughouse && gameInfo.variant != VariantSChess ) { /* [HGM] holdings: Add to holdings, if holdings exist */ if(gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat) { // [HGM] superchess: suppress flipping color of captured pieces by reverse pre-flip @@ -8641,6 +8673,9 @@ ApplyMove(fromX, fromY, toX, toY, promoChar, board) board[toY][toX] = EmptySquare; } } + if(gameInfo.variant == VariantSChess && promoChar != NULLCHAR && promoChar != '=' && piece != WhitePawn && piece != BlackPawn) { + board[fromY][fromX] = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar)); // S-Chess gating + } else if(promoChar == '+') { /* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite orinary Pawn promotion) */ board[toY][toX] = (ChessSquare) (PROMOTED piece); @@ -9665,7 +9700,7 @@ LoadGameOneMove(readAhead) } else { if (gameFileFP == NULL) return FALSE; - moveType = (ChessMove) yylex(); + moveType = (ChessMove) Myylex(); } done = FALSE; @@ -10160,7 +10195,7 @@ LoadGame(f, gameNumber, title, useList) cm = lastLoadGameStart = EndOfFile; while (gn > 0) { yyboardindex = forwardMostMove; - cm = (ChessMove) yylex(); + cm = (ChessMove) Myylex(); switch (cm) { case EndOfFile: if (cmailMsgLoaded) { @@ -10213,7 +10248,7 @@ LoadGame(f, gameNumber, title, useList) if (gn > 0) { do { yyboardindex = forwardMostMove; - cm = (ChessMove) yylex(); + cm = (ChessMove) Myylex(); } while (cm == PGNTag || cm == Comment); } break; @@ -10252,7 +10287,7 @@ LoadGame(f, gameNumber, title, useList) /* Skip any header junk before position diagram and/or move 1 */ for (;;) { yyboardindex = forwardMostMove; - cm = (ChessMove) yylex(); + cm = (ChessMove) Myylex(); if (cm == EndOfFile || cm == GNUChessGame || cm == XBoardGame) { @@ -10325,7 +10360,7 @@ LoadGame(f, gameNumber, title, useList) } yyboardindex = forwardMostMove; - cm = (ChessMove) yylex(); + cm = (ChessMove) Myylex(); /* Handle comments interspersed among the tags */ while (cm == Comment) { @@ -10335,7 +10370,7 @@ LoadGame(f, gameNumber, title, useList) p = yy_text; AppendComment(currentMove, p, FALSE); yyboardindex = forwardMostMove; - cm = (ChessMove) yylex(); + cm = (ChessMove) Myylex(); } } @@ -10412,7 +10447,7 @@ LoadGame(f, gameNumber, title, useList) } } yyboardindex = forwardMostMove; - cm = (ChessMove) yylex(); + cm = (ChessMove) Myylex(); } if (first.pr == NoProc) { @@ -10438,7 +10473,7 @@ LoadGame(f, gameNumber, title, useList) p = yy_text; AppendComment(currentMove, p, FALSE); yyboardindex = forwardMostMove; - cm = (ChessMove) yylex(); + cm = (ChessMove) Myylex(); } if ((cm == EndOfFile && lastLoadGameStart != EndOfFile ) || @@ -11847,6 +11882,37 @@ DisplayTwoMachinesTitle() } void +SettingsMenuIfReady() +{ + if (second.lastPing != second.lastPong) { + DisplayMessage("", _("Waiting for second chess program")); + ScheduleDelayedEvent(SettingsMenuIfReady, 10); // [HGM] fast: lowered from 1000 + return; + } + ThawUI(); + DisplayMessage("", ""); + SettingsPopUp(&second); +} + +int +WaitForSecond(DelayedEventCallback retry) +{ + if (second.pr == NULL) { + StartChessProgram(&second); + if (second.protocolVersion == 1) { + retry(); + } else { + /* kludge: allow timeout for initial "feature" command */ + FreezeUI(); + DisplayMessage("", _("Starting second chess program")); + ScheduleDelayedEvent(retry, FEATURE_TIMEOUT); + } + return 1; + } + return 0; +} + +void TwoMachinesEvent P((void)) { int i; @@ -11888,21 +11954,14 @@ TwoMachinesEvent P((void)) TruncateGame(); // [HGM] vari: MachineWhite and MachineBlack do this... ResurrectChessProgram(); /* in case first program isn't running */ - if (second.pr == NULL) { - StartChessProgram(&second); - if (second.protocolVersion == 1) { - TwoMachinesEventIfReady(); - } else { - /* kludge: allow timeout for initial "feature" command */ - FreezeUI(); - DisplayMessage("", _("Starting second chess program")); - ScheduleDelayedEvent(TwoMachinesEventIfReady, FEATURE_TIMEOUT); - } - return; - } + if(WaitForSecond(TwoMachinesEventIfReady)) return; DisplayMessage("", ""); InitChessProgram(&second, FALSE); SendToProgram("force\n", &second); + if(first.lastPing != first.lastPong) { // [HGM] wait till we are sure first engine has set up position + ScheduleDelayedEvent(TwoMachinesEvent, 10); + return; + } if (startedFromSetupPosition) { SendBoard(&second, backwardMostMove); if (appData.debugMode) { @@ -13328,7 +13387,7 @@ if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces); } else { commentList[index] = (char *) malloc(len + 6); // perhaps wastes 4... if(addBraces) - safeStrCpy(commentList[index], "{\n", sizeof(commentList[index])/sizeof(commentList[index][0])); + safeStrCpy(commentList[index], "{\n", 3); else commentList[index][0] = NULLCHAR; strcat(commentList[index], text); strcat(commentList[index], "\n"); @@ -13532,6 +13591,8 @@ ReceiveFromProgram(isr, closure, message, count, error) 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 && + sscanf(message, "setboard %c", &c)!=1 && sscanf(message, "setup %c", &c)!=1 && + sscanf(message, "hint: %c", &c)!=1 && sscanf(message, "pong %c", &c)!=1 && start != '#') { quote = appData.engineComments == 2 ? "# " : "### NON-COMPLIANT! ### "; print = (appData.engineComments >= 2); @@ -13807,6 +13868,20 @@ ParseOption(Option *opt, ChessProgramState *cps) if(p && (p == cps->optionSettings || p[-1] == ',')) { snprintf(buf, MSG_SIZ, "option %s", p); if(p = strstr(buf, ",")) *p = 0; + if(q = strchr(buf, '=')) switch(opt->type) { + case ComboBox: + for(n=0; nmax; n++) + if(!strcmp(((char**)opt->textValue)[n], q+1)) opt->value = n; + break; + case TextBox: + safeStrCpy(opt->textValue, q+1, MSG_SIZ - (opt->textValue - opt->name)); + break; + case Spin: + case CheckBox: + opt->value = atoi(q+1); + default: + break; + } strcat(buf, "\n"); SendToProgram(buf, cps); } @@ -13820,6 +13895,7 @@ FeatureDone(cps, val) { DelayedEventCallback cb = GetDelayedEvent(); if ((cb == InitBackEnd3 && cps == &first) || + (cb == SettingsMenuIfReady && cps == &second) || (cb == TwoMachinesEventIfReady && cps == &second)) { CancelDelayedEvent(); ScheduleDelayedEvent(cb, val ? 1 : 3600000);