X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=backend.c;h=aea15b58bda448bee0a5cfbdf826beef25be693f;hb=90cc6b8204d671bde9f59382c94729271c3d5e23;hp=b43a970a39d0b4d87e84a8f94155c0cac93493b7;hpb=515cd3f7b2ca2d5ba4450fc8bd8576c7602fe41d;p=xboard.git diff --git a/backend.c b/backend.c index b43a970..aea15b5 100644 --- a/backend.c +++ b/backend.c @@ -269,6 +269,7 @@ char chatPartner[MAX_CHAT][MSG_SIZ]; /* [HGM] chat: list of chatting partners */ extern int chatCount; int chattingPartner; char marker[BOARD_RANKS][BOARD_FILES]; /* [HGM] marks for target squares */ +char legal[BOARD_RANKS][BOARD_FILES]; /* [HGM] legal target squares */ char lastMsg[MSG_SIZ]; ChessSquare pieceSweep = EmptySquare; ChessSquare promoSweep = EmptySquare, defaultPromoChoice; @@ -388,6 +389,7 @@ PosFlags (index) case VariantShatranj: case VariantCourier: case VariantMakruk: + case VariantASEAN: case VariantGrand: flags &= ~F_ALL_CASTLE_OK; break; @@ -557,6 +559,13 @@ ChessSquare makrukArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatran BlackKing, BlackMan, BlackKnight, BlackRook } }; +ChessSquare aseanArray[2][BOARD_FILES] = { /* [HGM] (movGen knows about Shatranj Q and P) */ + { WhiteRook, WhiteKnight, WhiteMan, WhiteFerz, + WhiteKing, WhiteMan, WhiteKnight, WhiteRook }, + { BlackRook, BlackKnight, BlackMan, BlackFerz, + BlackKing, BlackMan, BlackKnight, BlackRook } +}; + #if (BOARD_FILES>=10) ChessSquare ShogiArray[2][BOARD_FILES] = { @@ -817,6 +826,7 @@ InitEngine (ChessProgramState *cps, int n) cps->scoreIsAbsolute = appData.scoreIsAbsolute[n]; /* [AS] */ cps->isUCI = appData.isUCI[n]; /* [AS] */ cps->hasOwnBookUCI = appData.hasOwnBookUCI[n]; /* [AS] */ + cps->highlight = 0; if (appData.protocolVersion[n] > PROTOVER || appData.protocolVersion[n] < 1) @@ -915,7 +925,7 @@ char *insert, *wbOptions; // point in ChessProgramNames were we should insert ne void Load (ChessProgramState *cps, int i) { - char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ]; + char *p, *q, buf[MSG_SIZ], command[MSG_SIZ], buf2[MSG_SIZ], buf3[MSG_SIZ], jar; if(engineLine && engineLine[0]) { // an engine was selected from the combo box snprintf(buf, MSG_SIZ, "-fcp %s", engineLine); SwapEngines(i); // kludge to parse -f* / -first* like it is -s* / -second* @@ -939,11 +949,13 @@ Load (ChessProgramState *cps, int i) p[-1] = SLASH; if(SLASH == '/' && p - engineName > 1) *(p -= 2) = '.'; // for XBoard use ./exeName as command after split! } else { ASSIGN(appData.directory[i], "."); } + jar = (strstr(p, ".jar") == p + strlen(p) - 4); if(params[0]) { if(strchr(p, ' ') && !strchr(p, '"')) snprintf(buf2, MSG_SIZ, "\"%s\"", p), p = buf2; // quote if it contains spaces snprintf(command, MSG_SIZ, "%s %s", p, params); p = command; } + if(jar) { snprintf(buf3, MSG_SIZ, "java -jar %s", p); p = buf3; } ASSIGN(appData.chessProgram[i], p); appData.isUCI[i] = isUCI; appData.protocolVersion[i] = v1 ? 1 : PROTOVER; @@ -1158,6 +1170,7 @@ InitBackEnd1 () case Variant3Check: /* should work except for win condition */ case VariantShatranj: /* should work except for all win conditions */ case VariantMakruk: /* should work except for draw countdown */ + case VariantASEAN : /* should work except for draw countdown */ case VariantBerolina: /* might work if TestLegality is off */ case VariantCapaRandom: /* should work */ case VariantJanus: /* should work */ @@ -1485,7 +1498,7 @@ MatchEvent (int mode) NextTourneyGame(-1, &dummy); ReserveGame(-1, 0); if(nextGame <= appData.matchGames) { - DisplayNote(_("You restarted an already completed tourney\nOne more cycle will now be added to it\nGames commence in 10 sec")); + DisplayNote(_("You restarted an already completed tourney.\nOne more cycle will now be added to it.\nGames commence in 10 sec.")); matchMode = mode; ScheduleDelayedEvent(NextMatchGame, 10000); return; @@ -1995,10 +2008,12 @@ StripHighlight (char *s) return retbuf; } +char engineVariant[MSG_SIZ]; char *variantNames[] = VARIANT_NAMES; char * VariantName (VariantClass v) { + if(v == VariantUnknown || *engineVariant) return engineVariant; return variantNames[v]; } @@ -3014,8 +3029,10 @@ read_from_ics (InputSourceRef isr, VOIDSTAR closure, char *data, int count, int } else { char tmp[MSG_SIZ]; if(gameMode == IcsObserving) // restore original ICS messages + /* TRANSLATORS: to 'kibitz' is to send a message to all players and the game observers */ snprintf(tmp, MSG_SIZ, "%s kibitzes: %s", star_match[0], parse); else + /* TRANSLATORS: to 'kibitz' is to send a message to all players and the game observers */ snprintf(tmp, MSG_SIZ, _("your opponent kibitzes: %s"), parse); SendToPlayer(tmp, strlen(tmp)); } @@ -5090,7 +5107,8 @@ SendMoveToICS (ChessMove moveType, int fromX, int fromY, int toX, int toY, char break; case WhitePromotion: case BlackPromotion: - if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk) + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || + gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) snprintf(user_move, MSG_SIZ, "%c%c%c%c=%c\n", AAA + fromX, ONE + fromY, AAA + toX, ONE + toY, PieceToChar(WhiteFerz)); @@ -5154,7 +5172,7 @@ UploadGameEvent () SendToICS(ics_prefix); SendToICS(buf); if(startedFromSetupPosition || backwardMostMove != 0) { - fen = PositionToFEN(backwardMostMove, NULL); + fen = PositionToFEN(backwardMostMove, NULL, 1); if(ics_type == ICS_ICC) { // on ICC we can simply send a complete FEN to set everything snprintf(buf, MSG_SIZ,"loadfen %s\n", fen); SendToICS(buf); @@ -5227,7 +5245,8 @@ ProcessICSInitScript (FILE *f) } -static int lastX, lastY, selectFlag, dragging; +static int lastX, lastY, lastLeftX, lastLeftY, selectFlag, dragging; +static ClickType lastClickType; void Sweep (int step) @@ -5899,9 +5918,13 @@ InitPosition (int redraw) case VariantMakruk: pieces = makrukArray; nrCastlingRights = 0; - startedFromSetupPosition = TRUE; SetCharTable(pieceToChar, "PN.R.M....SKpn.r.m....sk"); break; + case VariantASEAN: + pieces = aseanArray; + nrCastlingRights = 0; + SetCharTable(pieceToChar, "PN.R.Q....BKpn.r.q....bk"); + break; case VariantTwoKings: pieces = twoKingsArray; break; @@ -6030,7 +6053,7 @@ InitPosition (int redraw) pawnRow = gameInfo.boardHeight - 7; /* seems to work in all common variants */ if(pawnRow < 1) pawnRow = 1; - if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) pawnRow = 2; + if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN || gameInfo.variant == VariantGrand) pawnRow = 2; /* User pieceToChar list overrules defaults */ if(appData.pieceToCharTable != NULL) @@ -6139,7 +6162,7 @@ SendBoard (ChessProgramState *cps, int moveNum) char message[MSG_SIZ]; if (cps->useSetboard) { - char* fen = PositionToFEN(moveNum, cps->fenOverride); + char* fen = PositionToFEN(moveNum, cps->fenOverride, 1); snprintf(message, MSG_SIZ,"setboard %s\n", fen); SendToProgram(message, cps); free(fen); @@ -6322,7 +6345,8 @@ ChessSquare DefaultPromoChoice (int white) { ChessSquare result; - if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk) + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || + gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) result = WhiteFerz; // no choice else if(gameInfo.variant == VariantSuicide || gameInfo.variant == VariantGiveaway) result= WhiteKing; // in Suicide Q is the last thing we want @@ -6407,7 +6431,8 @@ HasPromotionChoice (int fromX, int fromY, int toX, int toY, char *promoChoice, i } // we either have a choice what to promote to, or (in Shogi) whether to promote - if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || gameInfo.variant == VariantMakruk) { + if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || + gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) { *promoChoice = PieceToChar(BlackFerz); // no choice return FALSE; } @@ -6998,6 +7023,39 @@ FinishMove (ChessMove moveType, int fromX, int fromY, int toX, int toY, int prom } void +MarkByFEN(char *fen) +{ + int r, f; + if(!appData.markers || !appData.highlightDragging) return; + for(r=0; r= 'A' && *fen <= 'Z') legal[r][f] = 1; else + if(*fen >= 'a' && *fen <= 'z') *fen += 'A' - 'a'; + if(*fen == '/' && f > BOARD_LEFT) f = BOARD_LEFT, r--; else + if(*fen == 'T') marker[r][f++] = 0; else + if(*fen == 'Y') marker[r][f++] = 1; else + if(*fen == 'G') marker[r][f++] = 3; else + if(*fen == 'B') marker[r][f++] = 4; else + if(*fen == 'C') marker[r][f++] = 5; else + if(*fen == 'M') marker[r][f++] = 6; else + if(*fen == 'W') marker[r][f++] = 7; else + if(*fen == 'D') marker[r][f++] = 8; else + if(*fen == 'R') marker[r][f++] = 2; else { + while(*fen <= '9' && *fen >= '0') s = 10*s + *fen++ - '0'; + f += s; fen -= s>0; + } + while(f >= BOARD_RGHT) f -= BOARD_RGHT - BOARD_LEFT, r--; + if(r < 0) break; + fen++; + } + DrawPosition(TRUE, NULL); +} + +void Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOIDSTAR closure) { typedef char Markers[BOARD_RANKS][BOARD_FILES]; @@ -7012,13 +7070,14 @@ Mark (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VO void MarkTargetSquares (int clear) { - int x, y; - if(clear) // no reason to ever suppress clearing - for(x=0; x1) capt++; @@ -7053,7 +7112,7 @@ CanPromote (ChessSquare piece, int y) if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantSuper || gameInfo.variant == VariantGreat || gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || - gameInfo.variant == VariantMakruk) return FALSE; + gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) return FALSE; return (piece == BlackPawn && y == 1 || piece == WhitePawn && y == BOARD_HEIGHT-2 || piece == BlackLance && y == 1 || @@ -7061,6 +7120,39 @@ CanPromote (ChessSquare piece, int y) } void +HoverEvent (int hiX, int hiY, int x, int y) +{ + static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES]; + int r, f; + if(!first.highlight) return; + if(hiX == -1 && hiY == -1 && x == fromX && y == fromY) // record markings + for(r=0; r= BOARD_LEFT && x < BOARD_RGHT) clearFlag = 1; // and defer click-click move of empty-square to up-click return; } - if(HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) { + if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) { if(appData.sweepSelect) { ChessSquare piece = boards[currentMove][fromY][fromX]; promoSweep = defaultPromoChoice; @@ -7348,6 +7451,7 @@ LeftClick (ClickType clickType, int xPix, int yPix) // off-board moves should not be highlighted if(x < 0 || y < 0) ClearHighlights(); + else ReportClick("put", x, y); if(gatingPiece != EmptySquare && gameInfo.variant == VariantSChess) promoChoice = ToLower(PieceToChar(gatingPiece)); @@ -8013,6 +8117,7 @@ SendMoveToBookUser (int moveNr, ChessProgramState *cps, int initial) SendToProgram("force\n", cps); cps->bookSuspend = TRUE; // flag indicating it has to be restarted } + if(bookHit) setboardSpoiledMachineBlack = FALSE; // suppress 'go' in SendMoveToProgram if(!initial) SendMoveToProgram(moveNr, cps); // with hit on initial position there is no move // now arrange restart after book miss if(bookHit) { @@ -8289,6 +8394,17 @@ FakeBookMove: // [HGM] book: we jump here to simulate machine moves after book h MakeMove(fromX, fromY, toX, toY, promoChar);/*updates forwardMostMove*/ + /* Test suites abort the 'game' after one move */ + if(*appData.finger) { + static FILE *f; + char *fen = PositionToFEN(backwardMostMove, NULL, 0); // no counts in EPD + if(!f) f = fopen(appData.finger, "w"); + if(f) fprintf(f, "%s bm %s;\n", fen, parseList[backwardMostMove]), fflush(f); + else { DisplayFatalError("Bad output file", errno, 0); return; } + free(fen); + GameEnds(GameUnfinished, NULL, GE_XBOARD); + } + /* [AS] Adjudicate game if needed (note: remember that forwardMostMove now points past the last move) */ if( gameMode == TwoMachinesPlay && adjudicateLossThreshold != 0 && forwardMostMove >= adjudicateLossPlies ) { int count = 0; @@ -8430,13 +8546,24 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. } if (!strncmp(message, "setup ", 6) && - (!appData.testLegality || gameInfo.variant == VariantFairy || NonStandardBoardSize()) + (!appData.testLegality || gameInfo.variant == VariantFairy || gameInfo.variant == VariantUnknown || NonStandardBoardSize()) ) { // [HGM] allow first engine to define opening position - int dummy, s=6; char buf[MSG_SIZ]; + int dummy, w, h, hand, s=6; char buf[MSG_SIZ], varName[MSG_SIZ]; if(appData.icsActive || forwardMostMove != 0 || cps != &first) return; + *buf = NULLCHAR; if(sscanf(message, "setup (%s", buf) == 1) s = 8 + strlen(buf), buf[s-9] = NULLCHAR, SetCharTable(pieceToChar, buf); if(startedFromSetupPosition) return; - if(sscanf(message+s, "%dx%d+%d", &dummy, &dummy, &dummy) == 3) while(message[s] && message[s++] != ' '); // for compatibility with Alien Edition + dummy = sscanf(message+s, "%dx%d+%d_%s", &w, &h, &hand, varName); + if(dummy >= 3) { + while(message[s] && message[s++] != ' '); + if(BOARD_HEIGHT != h || BOARD_WIDTH != w + 4*(hand != 0) || gameInfo.holdingsSize != hand || + dummy == 4 && gameInfo.variant != StringToVariant(varName) ) { // engine wants to change board format or variant + appData.NrFiles = w; appData.NrRanks = h; appData.holdingsSize = hand; + if(dummy == 4) gameInfo.variant = StringToVariant(varName); // parent variant + InitPosition(1); // calls InitDrawingSizes to let new parameters take effect + if(*buf) SetCharTable(pieceToChar, buf); // do again, for it was spoiled by InitPosition + } + } ParseFEN(boards[0], &dummy, message+s); DrawPosition(TRUE, boards[0]); startedFromSetupPosition = TRUE; @@ -8552,6 +8679,28 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if (sscanf(message, "pong %d", &cps->lastPong) == 1) { return; } + if(!strncmp(message, "highlight ", 10)) { + if(appData.testLegality && appData.markers) return; + MarkByFEN(message+10); // [HGM] alien: allow engine to mark board squares + return; + } + if(!strncmp(message, "click ", 6)) { + char f, c=0; int x, y; // [HGM] alien: allow engine to finish user moves (i.e. engine-driven one-click moving) + if(appData.testLegality || !appData.oneClick) return; + sscanf(message+6, "%c%d%c", &f, &y, &c); + x = f - 'a' + BOARD_LEFT, y -= ONE - '0'; + if(flipView) x = BOARD_WIDTH-1 - x; else y = BOARD_HEIGHT-1 - y; + x = x*squareSize + (x+1)*lineGap + squareSize/2; + y = y*squareSize + (y+1)*lineGap + squareSize/2; + f = first.highlight; first.highlight = 0; // kludge to suppress lift/put in response to own clicks + if(lastClickType == Press) // if button still down, fake release on same square, to be ready for next click + LeftClick(Release, lastLeftX, lastLeftY); + controlKey = (c == ','); + LeftClick(Press, x, y); + LeftClick(Release, x, y); + first.highlight = f; + return; + } /* * If the move is illegal, cancel it and redraw the board. * Also deal with other error cases. Matching is rather loose @@ -8892,7 +9041,7 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. DisplayInformation(_("Machine accepts your draw offer")); GameEnds(GameIsDrawn, "Draw agreed", GE_XBOARD); } else { - DisplayInformation(_("Machine offers a draw\nSelect Action / Draw to agree")); + DisplayInformation(_("Machine offers a draw.\nSelect Action / Draw to accept.")); } } } @@ -8971,7 +9120,10 @@ if(appData.debugMode) fprintf(debugFP, "nodes = %d, %lld\n", (int) programStats. if(f = fopen(buf, "w")) { // export PV to applicable PV file fprintf(f, "%5.2f/%-2d %s", curscore/100., plylev, pv); fclose(f); - } else DisplayError(_("failed writing PV"), 0); + } + else + /* TRANSLATORS: PV = principal variation, the variation the chess engine thinks is the best for everyone */ + DisplayError(_("failed writing PV"), 0); } tempStats.depth = plylev; @@ -9858,6 +10010,7 @@ NonStandardBoardSize () { /* [HGM] Awkward testing. Should really be a table */ int overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 0; + if( gameInfo.variant == VariantUnknown || *engineVariant) return 0; // engine-defined name never needs prefix if( gameInfo.variant == VariantXiangqi ) overruled = gameInfo.boardWidth != 9 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 0; if( gameInfo.variant == VariantShogi ) @@ -10834,6 +10987,7 @@ GameEnds (ChessMove result, char *resultDetails, int whosays) PlayIcsUnfinishedSound(); } } + if(appData.quitNext) { ExitEvent(0); return; } } else if (gameMode == EditGame || gameMode == PlayFromGameFile || gameMode == AnalyzeMode || @@ -11093,6 +11247,7 @@ Reset (int redraw, int init) lastHint[0] = NULLCHAR; ClearGameInfo(&gameInfo); gameInfo.variant = StringToVariant(appData.variant); + if(gameInfo.variant == VariantNormal && strcmp(appData.variant, "normal")) gameInfo.variant = VariantUnknown; ics_user_moved = ics_clock_paused = FALSE; ics_getting_history = H_FALSE; ics_gamenum = -1; @@ -12454,6 +12609,15 @@ LoadGame (FILE *f, int gameNumber, char *title, int useList) AnalyzeFileEvent(); } + if(gameInfo.result == GameUnfinished && gameInfo.resultDetails && appData.clockMode) { + long int w, b; // [HGM] adjourn: restore saved clock times + char *p = strstr(gameInfo.resultDetails, "(Clocks:"); + if(p && sscanf(p+8, "%ld,%ld", &w, &b) == 2) { + timeRemaining[0][forwardMostMove] = whiteTimeRemaining = 1000*w + 500; + timeRemaining[1][forwardMostMove] = blackTimeRemaining = 1000*b + 500; + } + } + if(creatingBook) return TRUE; if (!matchMode && pos > 0) { ToNrEvent(pos); // [HGM] no autoplay if selected on position @@ -12818,7 +12982,7 @@ SaveGamePGN (FILE *f) if(appData.numberTag && matchMode) fprintf(f, "[Number \"%d\"]\n", nextGame+1); // [HGM] number tag if (backwardMostMove > 0 || startedFromSetupPosition) { - char *fen = PositionToFEN(backwardMostMove, NULL); + char *fen = PositionToFEN(backwardMostMove, NULL, 1); fprintf(f, "[FEN \"%s\"]\n[SetUp \"1\"]\n", fen); fprintf(f, "\n{--------------\n"); PrintPosition(f, backwardMostMove); @@ -12955,8 +13119,11 @@ SaveGamePGN (FILE *f) /* Print result */ if (gameInfo.resultDetails != NULL && gameInfo.resultDetails[0] != NULLCHAR) { - fprintf(f, "{%s} %s\n\n", gameInfo.resultDetails, - PGNResult(gameInfo.result)); + char buf[MSG_SIZ], *p = gameInfo.resultDetails; + if(gameInfo.result == GameUnfinished && appData.clockMode && + (gameMode == MachinePlaysWhite || gameMode == MachinePlaysBlack || gameMode == TwoMachinesPlay)) // [HGM] adjourn: save clock settings + snprintf(buf, MSG_SIZ, "%s (Clocks: %ld, %ld)", p, whiteTimeRemaining/1000, blackTimeRemaining/1000), p = buf; + fprintf(f, "{%s} %s\n\n", p, PGNResult(gameInfo.result)); } else { fprintf(f, "%s\n\n", PGNResult(gameInfo.result)); } @@ -13087,7 +13254,7 @@ SavePosition (FILE *f, int dummy, char *dummy2) PrintPosition(f, currentMove); fprintf(f, "--------------]\n"); } else { - fen = PositionToFEN(currentMove, NULL); + fen = PositionToFEN(currentMove, NULL, 1); fprintf(f, "%s\n", fen); free(fen); } @@ -13989,7 +14156,7 @@ TwoMachinesEvent P((void)) case MachinePlaysWhite: case MachinePlaysBlack: if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) { - DisplayError(_("Wait until your turn,\nor select Move Now"), 0); + DisplayError(_("Wait until your turn,\nor select 'Move Now'."), 0); return; } /* fall through */ @@ -14426,11 +14593,14 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) { char buf[MSG_SIZ]; ChessSquare piece = boards[0][y][x]; + static Board erasedBoard, currentBoard, menuBoard; if (gameMode != EditPosition && gameMode != IcsExamining) return; switch (selection) { case ClearBoard: + CopyBoard(currentBoard, boards[0]); + CopyBoard(menuBoard, initialPosition); if (gameMode == IcsExamining && ics_type == ICS_FICS) { SendToICS(ics_prefix); SendToICS("bsetup clear\n"); @@ -14438,6 +14608,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) SendToICS(ics_prefix); SendToICS("clearboard\n"); } else { + int nonEmpty = 0; for (x = 0; x < BOARD_WIDTH; x++) { ChessSquare p = EmptySquare; if(x == BOARD_LEFT-1 || x == BOARD_RGHT) p = (ChessSquare) 0; /* [HGM] holdings */ for (y = 0; y < BOARD_HEIGHT; y++) { @@ -14448,9 +14619,30 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) SendToICS(buf); } } else { + if(boards[0][y][x] != p) nonEmpty++; boards[0][y][x] = p; } } + menuBoard[1][x] = menuBoard[BOARD_HEIGHT-2][x] = p; + } + if(gameMode != IcsExamining) { // [HGM] editpos: cycle trough boards + for(x = BOARD_LEFT; x < BOARD_RGHT; x++) { // create 'menu board' by removing duplicates + ChessSquare p = menuBoard[0][x]; + for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[0][y] == p) menuBoard[0][y] = EmptySquare; + p = menuBoard[BOARD_HEIGHT-1][x]; + for(y = x + 1; y < BOARD_RGHT; y++) if(menuBoard[BOARD_HEIGHT-1][y] == p) menuBoard[BOARD_HEIGHT-1][y] = EmptySquare; + } + if(!nonEmpty) { // asked to clear an empty board + CopyBoard(boards[0], menuBoard); + } else + if(CompareBoards(currentBoard, menuBoard)) { // asked to clear an empty board + CopyBoard(boards[0], initialPosition); + } else + if(CompareBoards(currentBoard, initialPosition) && !CompareBoards(currentBoard, erasedBoard)) { + CopyBoard(boards[0], erasedBoard); + } else + CopyBoard(erasedBoard, currentBoard); + } } if (gameMode == EditPosition) { @@ -14508,6 +14700,7 @@ EditPositionMenuEvent (ChessSquare selection, int x, int y) if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantXiangqi || gameInfo.variant == VariantCourier || + gameInfo.variant == VariantASEAN || gameInfo.variant == VariantMakruk ) selection = (ChessSquare)((int)selection - (int)WhiteQueen + (int)WhiteFerz); goto defaultlabel; @@ -15103,7 +15296,7 @@ RetractMoveEvent () case MachinePlaysWhite: case MachinePlaysBlack: if (WhiteOnMove(forwardMostMove) == (gameMode == MachinePlaysWhite)) { - DisplayError(_("Wait until your turn,\nor select Move Now"), 0); + DisplayError(_("Wait until your turn,\nor select 'Move Now'."), 0); return; } if (forwardMostMove < 2) return; @@ -15201,14 +15394,14 @@ HintEvent () switch (gameMode) { case MachinePlaysWhite: if (WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; case BeginningOfGame: case MachinePlaysBlack: if (!WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; @@ -15224,7 +15417,7 @@ void CreateBookEvent () { ListGame * lg = (ListGame *) gameList.head; - FILE *f; + FILE *f, *g; int nItem; static int secondTime = FALSE; @@ -15233,8 +15426,8 @@ CreateBookEvent () return; } - if(!secondTime && (f = fopen(appData.polyglotBook, "r"))) { - fclose(f); + if(!secondTime && (g = fopen(appData.polyglotBook, "r"))) { + fclose(g); secondTime++; DisplayNote(_("Book file exists! Try again for overwrite.")); return; @@ -15261,14 +15454,14 @@ BookEvent () switch (gameMode) { case MachinePlaysWhite: if (WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; case BeginningOfGame: case MachinePlaysBlack: if (!WhiteOnMove(forwardMostMove)) { - DisplayError(_("Wait until your turn"), 0); + DisplayError(_("Wait until your turn."), 0); return; } break; @@ -15932,6 +16125,27 @@ SendTimeRemaining (ChessProgramState *cps, int machineWhite) SendToProgram(message, cps); } +char * +EngineDefinedVariant (ChessProgramState *cps, int n) +{ // return name of n-th unknown variant that engine supports + static char buf[MSG_SIZ]; + char *p, *s = cps->variants; + if(!s) return NULL; + do { // parse string from variants feature + VariantClass v; + p = strchr(s, ','); + if(p) *p = NULLCHAR; + v = StringToVariant(s); + if(v == VariantNormal && strcmp(s, "normal") && !strstr(s, "_normal")) v = VariantUnknown; // garbage is recognized as normal + if(v == VariantUnknown) { // non-standard variant in list of engine-supported variants + if(--n < 0) safeStrCpy(buf, s, MSG_SIZ); + } + if(p) *p++ = ','; + if(n < 0) return buf; + } while(s = p); + return NULL; +} + int BoolFeature (char **p, char *name, int *loc, ChessProgramState *cps) { @@ -16146,6 +16360,7 @@ ParseFeatures (char *args, ChessProgramState *cps) /* End of additions by Tord */ /* [HGM] added features: */ + if (BoolFeature(&p, "highlight", &cps->highlight, cps)) continue; if (BoolFeature(&p, "debug", &cps->debug, cps)) continue; if (BoolFeature(&p, "nps", &cps->supportsNPS, cps)) continue; if (IntFeature(&p, "level", &cps->maxNrOfSessions, cps)) continue; @@ -17001,7 +17216,7 @@ PGNDate () char * -PositionToFEN (int move, char *overrideCastling) +PositionToFEN (int move, char *overrideCastling, int moveCounts) { int i, j, fromX, fromY, toX, toY; int whiteToPlay; @@ -17033,7 +17248,7 @@ PositionToFEN (int move, char *overrideCastling) *p++ = '+'; piece = (ChessSquare)(DEMOTED piece); } - *p++ = PieceToChar(piece); + *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece)); if(p[-1] == '~') { /* [HGM] flag promoted pieces as '~' (Crazyhouse) */ p[-1] = PieceToChar((ChessSquare)(DEMOTED piece)); @@ -17131,7 +17346,8 @@ PositionToFEN (int move, char *overrideCastling) } if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi && - gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) { + gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && + gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN ) { /* En passant target square */ if (move > backwardMostMove) { fromX = moveList[move - 1][0] - AAA; @@ -17163,9 +17379,10 @@ PositionToFEN (int move, char *overrideCastling) } } - /* [HGM] find reversible plies */ + if(moveCounts) { int i = 0, j=move; + /* [HGM] find reversible plies */ if (appData.debugMode) { int k; fprintf(debugFP, "write FEN 50-move: %d %d %d\n", initialRulePlies, forwardMostMove, backwardMostMove); for(k=backwardMostMove; k<=forwardMostMove; k++) @@ -17177,9 +17394,10 @@ PositionToFEN (int move, char *overrideCastling) if( j == backwardMostMove ) i += initialRulePlies; sprintf(p, "%d ", i); p += i>=100 ? 4 : i >= 10 ? 3 : 2; - } - /* Fullmove number */ - sprintf(p, "%d", (move / 2) + 1); + + /* Fullmove number */ + sprintf(p, "%d", (move / 2) + 1); + } else *--p = NULLCHAR; return StrSave(buf); } @@ -17221,6 +17439,8 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen) while (emptycount--) board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare; #endif + } else if (*p == '*') { + board[i][(j++)+gameInfo.holdingsWidth] = DarkSquare; p++; } else if (isdigit(*p)) { emptycount = *p++ - '0'; while(isdigit(*p)) emptycount = 10*emptycount + *p++ - '0'; /* [HGM] allow > 9 */ @@ -17415,7 +17635,8 @@ ParseFEN (Board board, int *blackPlaysFirst, char *fen) /* read e.p. field in games that know e.p. capture */ if(gameInfo.variant != VariantShogi && gameInfo.variant != VariantXiangqi && - gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && gameInfo.variant != VariantMakruk ) { + gameInfo.variant != VariantShatranj && gameInfo.variant != VariantCourier && + gameInfo.variant != VariantMakruk && gameInfo.variant != VariantASEAN ) { if(*p=='-') { p++; board[EP_STATUS] = EP_NONE; } else {