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;
case VariantShatranj:
case VariantCourier:
case VariantMakruk:
+ case VariantASEAN:
case VariantGrand:
flags &= ~F_ALL_CASTLE_OK;
break;
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 }
+};
+
+ChessSquare lionArray[2][BOARD_FILES] = {
+ { WhiteRook, WhiteLion, WhiteBishop, WhiteQueen,
+ WhiteKing, WhiteBishop, WhiteKnight, WhiteRook },
+ { BlackRook, BlackLion, BlackBishop, BlackQueen,
+ BlackKing, BlackBishop, BlackKnight, BlackRook }
+};
+
#if (BOARD_FILES>=10)
ChessSquare ShogiArray[2][BOARD_FILES] = {
{ BlackRook, BlackKnight, BlackAlfil, BlackBishop, BlackMan, BlackKing,
BlackFerz, BlackWazir, BlackBishop, BlackAlfil, BlackKnight, BlackRook }
};
+ChessSquare ChuArray[6][BOARD_FILES] = {
+ { WhiteLance, WhiteUnicorn, WhiteMan, WhiteFerz, WhiteWazir, WhiteKing,
+ WhiteAlfil, WhiteWazir, WhiteFerz, WhiteMan, WhiteUnicorn, WhiteLance },
+ { BlackLance, BlackUnicorn, BlackMan, BlackFerz, BlackWazir, BlackAlfil,
+ BlackKing, BlackWazir, BlackFerz, BlackMan, BlackUnicorn, BlackLance },
+ { WhiteCannon, EmptySquare, WhiteBishop, EmptySquare, WhiteNightrider, WhiteMarshall,
+ WhiteAngel, WhiteNightrider, EmptySquare, WhiteBishop, EmptySquare, WhiteCannon },
+ { BlackCannon, EmptySquare, BlackBishop, EmptySquare, BlackNightrider, BlackAngel,
+ BlackMarshall, BlackNightrider, EmptySquare, BlackBishop, EmptySquare, BlackCannon },
+ { WhiteFalcon, WhiteSilver, WhiteRook, WhiteCardinal, WhiteDragon, WhiteLion,
+ WhiteQueen, WhiteDragon, WhiteCardinal, WhiteRook, WhiteSilver, WhiteFalcon },
+ { BlackFalcon, BlackSilver, BlackRook, BlackCardinal, BlackDragon, BlackQueen,
+ BlackLion, BlackDragon, BlackCardinal, BlackRook, BlackSilver, BlackFalcon }
+};
#else // !(BOARD_FILES>=12)
#define CourierArray CapablancaArray
+#define ChuArray CapablancaArray
#endif // !(BOARD_FILES>=12)
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)
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*
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;
case VariantCapablanca: /* [HGM] should work */
case VariantCourier: /* [HGM] initial forced moves not implemented */
case VariantShogi: /* [HGM] could still mate with pawn drop */
+ case VariantChu: /* [HGM] experimental */
case VariantKnightmate: /* [HGM] should work */
case VariantCylinder: /* [HGM] untested */
case VariantFalcon: /* [HGM] untested */
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 */
case VariantSChess: /* S-Chess, should work */
case VariantGrand: /* should work */
case VariantSpartan: /* should work */
+ case VariantLion: /* should work */
break;
}
}
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;
return retbuf;
}
+char engineVariant[MSG_SIZ];
char *variantNames[] = VARIANT_NAMES;
char *
VariantName (VariantClass v)
{
+ if(v == VariantUnknown || *engineVariant) return engineVariant;
return variantNames[v];
}
} 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));
}
char buf[MSG_SIZ];
if(moveList[moveNum][1] == '@' && moveList[moveNum][0] == '@') {
+ if(gameInfo.variant == VariantLion || gameInfo.variant == VariantChu) {
+ sprintf(buf, "%s@@@@\n", cps->useUsermove ? "usermove " : "");
+ SendToProgram(buf, cps);
+ return;
+ }
// null move in variant where engine does not understand it (for analysis purposes)
SendBoard(cps, moveNum + 1); // send position after move in stead.
return;
}
else SendToProgram(moveList[moveNum], cps);
} else
+ if(moveList[moveNum][4] == ';') { // [HGM] lion: move is double-step over intermediate square
+ snprintf(buf, MSG_SIZ, "%c%d%c%d,%c%d%c%d\n", moveList[moveNum][0], moveList[moveNum][1] - '0', // convert to two moves
+ moveList[moveNum][5], moveList[moveNum][6] - '0',
+ moveList[moveNum][5], moveList[moveNum][6] - '0',
+ moveList[moveNum][2], moveList[moveNum][3] - '0');
+ SendToProgram(buf, cps);
+ } else
if(BOARD_HEIGHT > 10) { // [HGM] big: convert ranks to double-digit where needed
if(moveList[moveNum][1] == '@' && (BOARD_HEIGHT < 16 || moveList[moveNum][0] <= 'Z')) { // drop move
if(moveList[moveNum][0]== '@') snprintf(buf, MSG_SIZ, "@@@@\n"); else
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));
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);
SendToICS(ics_type == ICS_ICC ? "tag result Game in progress\n" : "commit\n");
}
+int killX = -1, killY = -1; // [HGM] lion: used for passing e.p. capture square to MakeMove
+
void
CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[7])
{
if (promoChar == 'x' || promoChar == NULLCHAR) {
sprintf(move, "%c%c%c%c\n",
AAA + ff, ONE + rf, AAA + ft, ONE + rt);
+ if(killX >= 0 && killY >= 0) sprintf(move+4, ";%c%c\n", AAA + killX, ONE + killY);
} else {
sprintf(move, "%c%c%c%c%c\n",
AAA + ff, ONE + rf, AAA + ft, ONE + rt, promoChar);
}
-static int lastX, lastY, selectFlag, dragging;
+static int lastX, lastY, lastLeftX, lastLeftY, selectFlag, dragging;
+static ClickType lastClickType;
void
Sweep (int step)
else if(promoSweep == WhiteKing && step > 0) promoSweep = BlackKing;
if(!step) step = -1;
} while(PieceToChar(promoSweep) == '.' || PieceToChar(promoSweep) == '~' || promoSweep == pawn ||
- appData.testLegality && (promoSweep == king ||
- gameInfo.variant == VariantShogi && promoSweep != PROMOTED last && last != PROMOTED promoSweep && last != promoSweep));
+ appData.testLegality && (promoSweep == king || promoSweep == WhiteLion || promoSweep == BlackLion) ||
+ IS_SHOGI(gameInfo.variant) && promoSweep != CHUPROMOTED last && last != CHUPROMOTED promoSweep && last != promoSweep);
if(toX >= 0) {
int victim = boards[currentMove][toY][toX];
boards[currentMove][toY][toX] = promoSweep;
case WhiteNonPromotion:
case BlackNonPromotion:
case NormalMove:
+ case FirstLeg:
case WhiteCapturesEnPassant:
case BlackCapturesEnPassant:
case WhiteKingSideCastle:
if (appData.testLegality) {
return (*moveType != IllegalMove);
} else {
- return !(*fromX == *toX && *fromY == *toY) && boards[moveNum][*fromY][*fromX] != EmptySquare &&
+ return !(*fromX == *toX && *fromY == *toY && killX < 0) && boards[moveNum][*fromY][*fromX] != EmptySquare &&
+ // [HGM] lion: if this is a double move we are less critical
WhiteOnMove(moveNum) == (boards[moveNum][*fromY][*fromX] < BlackPawn);
}
InitPosition (int redraw)
{
ChessSquare (* pieces)[BOARD_FILES];
- int i, j, pawnRow, overrule,
+ int i, j, pawnRow=1, pieceRows=1, overrule,
oldx = gameInfo.boardWidth,
oldy = gameInfo.boardHeight,
oldh = gameInfo.holdingsWidth;
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;
nrCastlingRights = 0;
SetCharTable(pieceToChar, "PNBRLS...G.++++++Kpnbrls...g.++++++k");
break;
+ case VariantChu:
+ pieces = ChuArray; pieceRows = 3;
+ gameInfo.boardWidth = 12;
+ gameInfo.boardHeight = 12;
+ nrCastlingRights = 0;
+ SetCharTable(pieceToChar, "P.BRQSEXOGCATHD.VMLIFN+.++.++++++++++.+++++K"
+ "p.brqsexogcathd.vmlifn+.++.++++++++++.+++++k");
+ break;
case VariantCourier:
pieces = CourierArray;
gameInfo.boardWidth = 12;
pieces = SpartanArray;
SetCharTable(pieceToChar, "PNBRQ................K......lwg.....c...h..k");
break;
+ case VariantLion:
+ pieces = lionArray;
+ SetCharTable(pieceToChar, "PNBRQ................LKpnbrq................lk");
+ break;
case VariantFairy:
pieces = fairyArray;
SetCharTable(pieceToChar, "PNBRQFEACWMOHIJGDVLSUKpnbrqfeacwmohijgdvlsuk");
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;
+ if(gameInfo.variant == VariantChu) pawnRow = 3;
/* User pieceToChar list overrules defaults */
if(appData.pieceToCharTable != NULL)
}
}
}
+ if(gameInfo.variant == VariantChu) {
+ if(j == (BOARD_WIDTH-2)/3 || j == BOARD_WIDTH - (BOARD_WIDTH+1)/3)
+ initialPosition[pawnRow+1][j] = WhiteCobra,
+ initialPosition[BOARD_HEIGHT-pawnRow-2][j] = BlackCobra;
+ for(i=1; i<pieceRows; i++) {
+ initialPosition[i][j] = pieces[2*i][j-gameInfo.holdingsWidth];
+ initialPosition[BOARD_HEIGHT-1-i][j] = pieces[2*i+1][j-gameInfo.holdingsWidth];
+ }
+ }
if(gameInfo.variant == VariantGrand) {
if(j==BOARD_LEFT || j>=BOARD_RGHT-1) {
initialPosition[0][j] = WhiteRook;
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);
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
return FALSE;
piece = boards[currentMove][fromY][fromX];
- if(gameInfo.variant == VariantShogi) {
+ if(gameInfo.variant == VariantChu) {
+ int p = piece >= BlackPawn ? BLACK_TO_WHITE piece : piece;
+ promotionZoneSize = BOARD_HEIGHT/3;
+ highestPromotingPiece = (p >= WhiteLion || PieceToChar(piece + 22) == '.') ? WhitePawn : WhiteLion;
+ } else if(gameInfo.variant == VariantShogi) {
promotionZoneSize = BOARD_HEIGHT/3;
- highestPromotingPiece = (int)WhiteFerz;
+ highestPromotingPiece = (int)WhiteAlfil;
} else if(gameInfo.variant == VariantMakruk || gameInfo.variant == VariantGrand) {
promotionZoneSize = 3;
}
}
// 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;
}
}
// give caller the default choice even if we will not make it
*promoChoice = ToLower(PieceToChar(defaultPromoChoice));
- if(gameInfo.variant == VariantShogi) *promoChoice = (defaultPromoChoice == piece ? '=' : '+');
+ if(IS_SHOGI(gameInfo.variant)) *promoChoice = (defaultPromoChoice == piece ? '=' : '+');
if( sweepSelect && gameInfo.variant != VariantGreat
&& gameInfo.variant != VariantGrand
&& gameInfo.variant != VariantSuper) return FALSE;
gameMode == IcsPlayingBlack && WhiteOnMove(currentMove);
if(appData.testLegality && !premove) {
moveType = LegalityTest(boards[currentMove], PosFlags(currentMove),
- fromY, fromX, toY, toX, gameInfo.variant == VariantShogi ? '+' : NULLCHAR);
+ fromY, fromX, toY, toX, IS_SHOGI(gameInfo.variant) ? '+' : NULLCHAR);
if(moveType != WhitePromotion && moveType != BlackPromotion)
return FALSE;
}
}
void
+MarkByFEN(char *fen)
+{
+ int r, f;
+ if(!appData.markers || !appData.highlightDragging) return;
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) legal[r][f] = 0;
+ r=BOARD_HEIGHT-1; f=BOARD_LEFT;
+ while(*fen) {
+ int s = 0;
+ marker[r][f] = 0;
+ if(*fen == 'M') legal[r][f] = 2; else // request promotion choice
+ if(*fen >= '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);
+}
+
+static char baseMarker[BOARD_RANKS][BOARD_FILES], baseLegal[BOARD_RANKS][BOARD_FILES];
+
+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];
Markers *m = (Markers *) closure;
- if(rf == fromY && ff == fromX)
+ if(rf == fromY && ff == fromX && (killX < 0 && !(rt == rf && ft == ff) || abs(ft-killX) < 2 && abs(rt-killY) < 2))
(*m)[rt][ft] = 1 + (board[rt][ft] != EmptySquare
|| kind == WhiteCapturesEnPassant
- || kind == BlackCapturesEnPassant);
+ || kind == BlackCapturesEnPassant) + 3*(kind == FirstLeg && killX < 0);
else if(flags & F_MANDATORY_CAPTURE && board[rt][ft] != EmptySquare) (*m)[rt][ft] = 3;
}
void
MarkTargetSquares (int clear)
{
- int x, y;
- if(clear) // no reason to ever suppress clearing
- for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) marker[y][x] = 0;
- if(!appData.markers || !appData.highlightDragging || appData.icsActive && gameInfo.variant < VariantShogi ||
- !appData.testLegality || gameMode == EditPosition) return;
- if(!clear) {
+ int x, y, sum=0;
+ if(clear) { // no reason to ever suppress clearing
+ for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) sum += marker[y][x], marker[y][x] = baseMarker[y][x] = 0;
+ if(!sum) return; // nothing was cleared,no redraw needed
+ } else {
int capt = 0;
+ if(!appData.markers || !appData.highlightDragging || appData.icsActive && gameInfo.variant < VariantShogi ||
+ !appData.testLegality || gameMode == EditPosition) return;
GenLegal(boards[currentMove], PosFlags(currentMove), Mark, (void*) marker, EmptySquare);
if(PosFlags(0) & F_MANDATORY_CAPTURE) {
for(x=0; x<BOARD_WIDTH; x++) for(y=0; y<BOARD_HEIGHT; y++) if(marker[y][x]>1) capt++;
{
if(gameMode == EditPosition) return FALSE; // no promotions when editing position
// some variants have fixed promotion piece, no promotion at all, or another selection mechanism
- if(gameInfo.variant == VariantShogi || gameInfo.variant == VariantXiangqi ||
+ if(IS_SHOGI(gameInfo.variant) || 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 ||
}
void
+HoverEvent (int xPix, int yPix, int x, int y)
+{
+ static int oldX = -1, oldY = -1, oldFromX = -1, oldFromY = -1;
+ int r, f;
+ if(dragging == 2) DragPieceMove(xPix, yPix); // [HGM] lion: drag without button for second leg
+ if(!first.highlight) return;
+ if(fromX != oldFromX || fromY != oldFromY) oldX = oldY = -1; // kludge to fake entry on from-click
+ if(x == oldX && y == oldY) return; // only do something if we enter new square
+ oldFromX = fromX; oldFromY = fromY;
+ if(oldX == -1 && oldY == -1 && x == fromX && y == fromY) // record markings after from-change
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+ baseMarker[r][f] = marker[r][f], baseLegal[r][f] = legal[r][f];
+ else if(oldX != x || oldY != y) {
+ // [HGM] lift: entered new to-square; redraw arrow, and inform engine
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++)
+ marker[r][f] = baseMarker[r][f], legal[r][f] = baseLegal[r][f];
+ if((marker[y][x] == 2 || marker[y][x] == 6) && legal[y][x]) {
+ char buf[MSG_SIZ];
+ snprintf(buf, MSG_SIZ, "hover %c%d\n", x + AAA, y + ONE - '0');
+ SendToProgram(buf, &first);
+ }
+ oldX = x; oldY = y;
+// SetHighlights(fromX, fromY, x, y);
+ }
+}
+
+void ReportClick(char *action, int x, int y)
+{
+ char buf[MSG_SIZ]; // Inform engine of what user does
+ int r, f;
+ if(action[0] == 'l') // mark any target square of a lifted piece as legal to-square, clear markers
+ for(r=0; r<BOARD_HEIGHT; r++) for(f=BOARD_LEFT; f<BOARD_RGHT; f++) legal[r][f] = 1, marker[r][f] = 0;
+ if(!first.highlight || gameMode == EditPosition) return;
+ snprintf(buf, MSG_SIZ, "%s %c%d%s\n", action, x+AAA, y+ONE-'0', controlKey && action[0]=='p' ? "," : "");
+ SendToProgram(buf, &first);
+}
+
+void
LeftClick (ClickType clickType, int xPix, int yPix)
{
int x, y;
prevClickTime = lastClickTime; GetTimeMark(&lastClickTime);
if (clickType == Press) ErrorPopDown();
+ lastClickType = clickType, lastLeftX = xPix, lastLeftY = yPix; // [HGM] alien: remember state
x = EventToSquare(xPix, BOARD_WIDTH);
y = EventToSquare(yPix, BOARD_HEIGHT);
if(gameMode == AnalyzeMode && (pausing || controlKey) && first.excludeMoves) { // use pause state to exclude moves
doubleClick = TRUE; gatingPiece = boards[currentMove][y][x];
}
- fromX = x; fromY = y; toX = toY = -1;
+ fromX = x; fromY = y; toX = toY = killX = killY = -1;
if(!appData.oneClick || !OnlyMove(&x, &y, FALSE) ||
// even if only move, we treat as normal when this would trigger a promotion popup, to allow sweep selection
appData.sweepSelect && CanPromote(boards[currentMove][fromY][fromX], fromY) && originalY != y) {
/* First square */
if (OKToStartUserMove(fromX, fromY)) {
second = 0;
+ ReportClick("lift", x, y);
MarkTargetSquares(0);
if(gameMode == EditPosition && controlKey) gatingPiece = boards[currentMove][fromY][fromX];
DragPieceBegin(xPix, yPix, FALSE); dragging = 1;
fromP = boards[currentMove][fromY][fromX];
toP = boards[currentMove][y][x];
frc = gameInfo.variant == VariantFischeRandom || gameInfo.variant == VariantCapaRandom || gameInfo.variant == VariantSChess;
- if ((WhitePawn <= fromP && fromP <= WhiteKing &&
+ if( (killX < 0 || x != fromX || y != fromY) && // [HGM] lion: do not interpret igui as deselect!
+ ((WhitePawn <= fromP && fromP <= WhiteKing &&
WhitePawn <= toP && toP <= WhiteKing &&
!(fromP == WhiteKing && toP == WhiteRook && frc) &&
!(fromP == WhiteRook && toP == WhiteKing && frc)) ||
(BlackPawn <= fromP && fromP <= BlackKing &&
BlackPawn <= toP && toP <= BlackKing &&
!(fromP == BlackRook && toP == BlackKing && frc) && // allow also RxK as FRC castling
- !(fromP == BlackKing && toP == BlackRook && frc))) {
+ !(fromP == BlackKing && toP == BlackRook && frc)))) {
/* Clicked again on same color piece -- changed his mind */
second = (x == fromX && y == fromY);
+ killX = killY = -1;
if(second && gameMode == AnalyzeMode && SubtractTimeMarks(&lastClickTime, &prevClickTime) < 200) {
second = FALSE; // first double-click rather than scond click
doubleClick = first.excludeMoves; // used by UserMoveEvent to recognize exclude moves
}
promoDefaultAltered = FALSE;
MarkTargetSquares(1);
- if(!second || appData.oneClick && !OnlyMove(&x, &y, TRUE)) {
+ if(!(second && appData.oneClick && OnlyMove(&x, &y, TRUE))) {
if (appData.highlightDragging) {
SetHighlights(x, y, -1, -1);
} else {
else gatingPiece = doubleClick ? fromP : EmptySquare;
fromX = x;
fromY = y; dragging = 1;
+ ReportClick("lift", x, y);
MarkTargetSquares(0);
DragPieceBegin(xPix, yPix, FALSE);
if(appData.sweepSelect && CanPromote(piece = boards[currentMove][y][x], y)) {
if(x < BOARD_LEFT || x >= BOARD_RGHT) return;
}
- if (clickType == Release && x == fromX && y == fromY) {
+ if (clickType == Release && x == fromX && y == fromY && killX < 0) {
DragPieceEnd(xPix, yPix); dragging = 0;
if(clearFlag) {
// a deferred attempt to click-click move an empty square on top of a piece
second = sweepSelecting = 0;
fromX = fromY = -1;
gatingPiece = EmptySquare;
+ MarkTargetSquares(1);
ClearHighlights();
gotPremove = 0;
ClearPremoveHighlights();
clearFlag = 0;
+ if(gameMode != EditPosition && !appData.testLegality && !legal[y][x] && (x != killX || y != killY) && !sweepSelecting) {
+ if(dragging) DragPieceEnd(xPix, yPix), dragging = 0;
+ DisplayMessage(_("only marked squares are legal"),"");
+ DrawPosition(TRUE, NULL);
+ return; // ignore to-click
+ }
+
/* we now have a different from- and (possibly off-board) to-square */
/* Completed move */
if(!sweepSelecting) {
toX = x;
toY = y;
- } else sweepSelecting = 0; // this must be the up-click corresponding to the down-click that started the sweep
+ }
saveAnimate = appData.animate;
if (clickType == Press) {
if(x >= 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(dragging == 2) { // [HGM] lion: just turn buttonless drag into normal drag, and let release to the job
+ dragging = 1;
+ return;
+ }
+ if(x == killX && y == killY) { // second click on this square, which was selected as first-leg target
+ killX = killY = -1; // this informs us no second leg is coming, so treat as to-click without intermediate
+ } else
+ if(marker[y][x] == 5) return; // [HGM] lion: to-click on cyan square; defer action to release
+ if(legal[y][x] == 2 || HasPromotionChoice(fromX, fromY, toX, toY, &promoChoice, FALSE)) {
if(appData.sweepSelect) {
ChessSquare piece = boards[currentMove][fromY][fromX];
promoSweep = defaultPromoChoice;
- if(PieceToChar(PROMOTED piece) == '+') promoSweep = PROMOTED piece;
+ if(PieceToChar(CHUPROMOTED piece) == '+') promoSweep = CHUPROMOTED piece;
selectFlag = 0; lastX = xPix; lastY = yPix;
Sweep(0); // Pawn that is going to promote: preview promotion piece
sweepSelecting = 1;
} else {
ClearHighlights();
}
+ } else if(sweepSelecting) { // this must be the up-click corresponding to the down-click that started the sweep
+ sweepSelecting = 0;
+ if (appData.animate || appData.highlightLastMove) {
+ SetHighlights(fromX, fromY, toX, toY);
+ } else {
+ ClearHighlights();
+ }
} else {
#if 0
// [HGM] this must be done after the move is made, as with arrow it could lead to a board redraw with piece still on from square
ClearHighlights();
}
#endif
+ if(marker[y][x] == 5) { // [HGM] lion: this was the release of a to-click or drag on a cyan square
+ dragging *= 2; // flag button-less dragging if we are dragging
+ MarkTargetSquares(1);
+ if(x == killX && y == killY) killX = killY = -1; else {
+ killX = x; killY = y; //remeber this square as intermediate
+ ReportClick("put", x, y); // and inform engine
+ ReportClick("lift", x, y);
+ MarkTargetSquares(0);
+ return;
+ }
+ }
DragPieceEnd(xPix, yPix); dragging = 0;
/* Don't animate move and drag both */
appData.animate = FALSE;
// 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));
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) {
void
HandleMachineMove (char *message, ChessProgramState *cps)
{
+ static char firstLeg[20];
char machineMove[MSG_SIZ], buf1[MSG_SIZ*10], buf2[MSG_SIZ];
char realname[MSG_SIZ];
int fromX, fromY, toX, toY;
ChessMove moveType;
- char promoChar;
+ char promoChar, roar;
char *p, *pv=buf1;
int machineWhite, oldError;
char *bookHit;
}
if(cps->alphaRank) AlphaRank(machineMove, 4);
+
+ // [HGM] lion: (some very limited) support for Alien protocol
+ killX = killY = -1;
+ if(machineMove[strlen(machineMove)-1] == ',') { // move ends in coma: non-final leg of composite move
+ safeStrCpy(firstLeg, machineMove, 20); // just remember it for processing when second leg arrives
+ return;
+ } else if(firstLeg[0]) { // there was a previous leg;
+ // only support case where same piece makes two step (and don't even test that!)
+ char buf[20], *p = machineMove+1, *q = buf+1, f;
+ safeStrCpy(buf, machineMove, 20);
+ while(isdigit(*q)) q++; // find start of to-square
+ safeStrCpy(machineMove, firstLeg, 20);
+ while(isdigit(*p)) p++;
+ safeStrCpy(p, q, 20); // glue to-square of second leg to from-square of first, to process over-all move
+ sscanf(buf, "%c%d", &f, &killY); killX = f - AAA; killY -= ONE - '0'; // pass intermediate square to MakeMove in global
+ firstLeg[0] = NULLCHAR;
+ }
+
if (!ParseOneMove(machineMove, forwardMostMove, &moveType,
&fromX, &fromY, &toX, &toY, &promoChar)) {
/* Machine move could not be parsed; ignore it. */
snprintf(buf1, MSG_SIZ*10, _("Illegal move \"%s\" from %s machine"),
machineMove, _(cps->which));
DisplayMoveError(buf1);
- snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c) res=%d",
- machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, moveType);
+ snprintf(buf1, MSG_SIZ*10, "Xboard: Forfeit due to invalid move: %s (%c%c%c%c via %c%c) res=%d",
+ machineMove, fromX+AAA, fromY+ONE, toX+AAA, toY+ONE, killX+AAA, killY+ONE, moveType);
if (gameMode == TwoMachinesPlay) {
GameEnds(machineWhite ? BlackWins : WhiteWins,
buf1, GE_XBOARD);
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;
cps->other->maybeThinking = TRUE;
}
+ roar = (killX >= 0 && IS_LION(boards[forwardMostMove][toY][toX]));
+
ShowMove(fromX, fromY, toX, toY); /*updates currentMove*/
if (!pausing && appData.ringBellAfterMoves) {
- RingBell();
+ if(!roar) RingBell();
}
/*
}
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
- ParseFEN(boards[0], &dummy, message+s);
+ 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, FALSE);
DrawPosition(TRUE, boards[0]);
startedFromSetupPosition = TRUE;
return;
GameEnds(GameUnfinished, "Engine aborts game", GE_XBOARD);
- if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9)) {
+ if (!ParseFEN(initial_position, &blackPlaysFirst, message + 9, FALSE)) {
DisplayError(_("Bad FEN received from engine"), 0);
return ;
} else {
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
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."));
}
}
}
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;
case WhiteNonPromotion:
case BlackNonPromotion:
case NormalMove:
+ case FirstLeg:
case WhiteCapturesEnPassant:
case BlackCapturesEnPassant:
case WhiteKingSideCastle:
}
piece = board[toY][toX] = (ChessSquare) fromX;
} else {
+ ChessSquare victim;
int i;
- if( board[toY][toX] != EmptySquare )
+ if( killX >= 0 && killY >= 0 ) // [HGM] lion: Lion trampled over something
+ victim = board[killY][killX],
+ board[killY][killX] = EmptySquare,
board[EP_STATUS] = EP_CAPTURE;
+ if( board[toY][toX] != EmptySquare ) {
+ board[EP_STATUS] = EP_CAPTURE;
+ if( (fromX != toX || fromY != toY) && // not igui!
+ (captured == WhiteLion && board[fromY][fromX] != BlackLion ||
+ captured == BlackLion && board[fromY][fromX] != WhiteLion ) ) { // [HGM] lion: Chu Lion-capture rules
+ board[EP_STATUS] = EP_IRON_LION; // non-Lion x Lion: no counter-strike allowed
+ }
+ }
+
if( board[fromY][fromX] == WhiteLance || board[fromY][fromX] == BlackLance ) {
if( gameInfo.variant != VariantSuper && gameInfo.variant != VariantShogi )
board[EP_STATUS] = EP_PAWN_MOVE; // Lance is Pawn-like in most variants
board[fromY][fromX+1] = EmptySquare;
}
} else {
- board[toY][toX] = board[fromY][fromX];
+ ChessSquare piece = board[fromY][fromX]; // [HGM] lion: allow for igui (where from == to)
board[fromY][fromX] = EmptySquare;
+ board[toY][toX] = piece;
}
}
} else
if(promoChar == '+') {
/* [HGM] Shogi-style promotions, to piece implied by original (Might overwrite ordinary Pawn promotion) */
- board[toY][toX] = (ChessSquare) (PROMOTED piece);
+ board[toY][toX] = (ChessSquare) (CHUPROMOTED piece);
} else if(!appData.testLegality && promoChar != NULLCHAR && promoChar != '=') { // without legality testing, unconditionally believe promoChar
ChessSquare newPiece = CharToPiece(piece < BlackPawn ? ToUpper(promoChar) : ToLower(promoChar));
if((newPiece <= WhiteMan || newPiece >= BlackPawn && newPiece <= BlackMan) // unpromoted piece specified
board[BOARD_HEIGHT-1-k][0] = EmptySquare;
}
}
-
}
/* Updates forwardMostMove */
void
MakeMove (int fromX, int fromY, int toX, int toY, int promoChar)
{
+ int x = toX, y = toY;
+ char *s = parseList[forwardMostMove];
+ ChessSquare p = boards[forwardMostMove][toY][toX];
// forwardMostMove++; // [HGM] bare: moved downstream
+ if(killX >= 0 && killY >= 0) x = killX, y = killY; // [HGM] lion: make SAN move to intermediate square, if there is one
(void) CoordsToAlgebraic(boards[forwardMostMove],
PosFlags(forwardMostMove),
- fromY, fromX, toY, toX, promoChar,
- parseList[forwardMostMove]);
+ fromY, fromX, y, x, promoChar,
+ s);
+ if(killX >= 0 && killY >= 0)
+ sprintf(s + strlen(s), "%c%c%d", p == EmptySquare || toX == fromX && toY == fromY ? '-' : 'x', toX + AAA, toY + ONE - '0');
if(serverMoves != NULL) { /* [HGM] write moves on file for broadcasting (should be separate routine, really) */
int timeLeft; static int lastLoadFlag=0; int king, piece;
strcat(parseList[forwardMostMove - 1], "#");
break;
}
-
}
/* Updates currentMove if not pausing */
currentMove = forwardMostMove;
}
+ killX = killY = -1; // [HGM] lion: used up
+
if (instant) return;
DisplayMove(currentMove - 1);
{
/* [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 )
overruled = gameInfo.boardWidth != 8 || gameInfo.boardHeight != 8 || gameInfo.holdingsSize != 7;
if( gameInfo.variant == VariantGrand )
overruled = gameInfo.boardWidth != 10 || gameInfo.boardHeight != 10 || gameInfo.holdingsSize != 7;
+ if( gameInfo.variant == VariantChu )
+ overruled = gameInfo.boardWidth != 12 || gameInfo.boardHeight != 12 || gameInfo.holdingsSize != 0;
return overruled;
}
result, resultDetails ? resultDetails : "(null)", whosays);
}
- fromX = fromY = -1; // [HGM] abort any move the user is entering.
+ fromX = fromY = killX = killY = -1; // [HGM] abort any move the user is entering. // [HGM] lion
if(pausing) PauseEvent(); // can happen when we abort a paused game (New Game or Quit)
PlayIcsUnfinishedSound();
}
}
+ if(appData.quitNext) { ExitEvent(0); return; }
} else if (gameMode == EditGame ||
gameMode == PlayFromGameFile ||
gameMode == AnalyzeMode ||
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;
ClearPremoveHighlights();
gotPremove = FALSE;
alarmSounded = FALSE;
+ killX = killY = -1; // [HGM] lion
GameEnds(EndOfFile, NULL, GE_PLAYER);
if(appData.serverMovesName != NULL) {
if (gameMode != PlayFromGameFile && gameMode != AnalyzeFile)
return FALSE;
- if (gameMode == AnalyzeFile && currentMove > backwardMostMove) {
+ if (gameMode == AnalyzeFile && currentMove > backwardMostMove && programStats.depth) {
pvInfoList[currentMove].depth = programStats.depth;
pvInfoList[currentMove].score = programStats.score;
pvInfoList[currentMove].time = 0;
if(currentMove < forwardMostMove) AppendComment(currentMove+1, lastPV[0], 2);
+ else { // append analysis of final position as comment
+ char buf[MSG_SIZ];
+ snprintf(buf, MSG_SIZ, "{final score %+4.2f/%d}", programStats.score/100., programStats.depth);
+ AppendComment(currentMove, buf, 3); // the 3 prevents stripping of the score/depth!
+ }
+ programStats.depth = 0;
}
if (currentMove >= forwardMostMove) {
case WhiteNonPromotion:
case BlackNonPromotion:
case NormalMove:
+ case FirstLeg:
case WhiteKingSideCastle:
case WhiteQueenSideCastle:
case BlackKingSideCastle:
toX = currentMoveString[2] - AAA;
toY = currentMoveString[3] - ONE;
promoChar = currentMoveString[4];
+ if(promoChar == ';') promoChar = NULLCHAR;
break;
case WhiteDrop:
thinkOutput[0] = NULLCHAR;
MakeMove(fromX, fromY, toX, toY, promoChar);
+ killX = killY = -1; // [HGM] lion: used up
currentMove = forwardMostMove;
return TRUE;
}
for(next = WhitePawn; next<EmptySquare; next++) keys[next] = random()>>8 ^ random()<<6 ^random()<<20;
initDone = TRUE;
}
- if(lg->gameInfo.fen) ParseFEN(boards[scratch], &btm, lg->gameInfo.fen);
+ if(lg->gameInfo.fen) ParseFEN(boards[scratch], &btm, lg->gameInfo.fen, FALSE);
else CopyBoard(boards[scratch], initialPosition); // default start position
if(lg->moves) {
turn = btm + 1;
case WhiteNonPromotion:
case BlackNonPromotion:
case NormalMove:
+ case FirstLeg:
case WhiteKingSideCastle:
case WhiteQueenSideCastle:
case BlackKingSideCastle:
if (gameMode != BeginningOfGame) {
Reset(FALSE, TRUE);
}
+ killX = killY = -1; // [HGM] lion: in case we did not Reset
gameFileFP = f;
if (lastLoadGameFP != NULL && lastLoadGameFP != f) {
break;
case NormalMove:
+ case FirstLeg:
/* Only a NormalMove can be at the start of a game
* without a position diagram. */
if (lastLoadGameStart == EndOfFile ) {
if (gameInfo.fen != NULL) {
Board initial_position;
startedFromSetupPosition = TRUE;
- if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen)) {
+ if (!ParseFEN(initial_position, &blackPlaysFirst, gameInfo.fen, TRUE)) {
Reset(TRUE, TRUE);
DisplayError(_("Bad FEN position in file"), 0);
return FALSE;
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
}
if (fenMode) {
- if (!ParseFEN(initial_position, &blackPlaysFirst, line)) {
+ if (!ParseFEN(initial_position, &blackPlaysFirst, line, TRUE)) {
DisplayError(_("Bad FEN position in file"), 0);
return FALSE;
}
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);
/* 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));
}
PrintPosition(f, currentMove);
fprintf(f, "--------------]\n");
} else {
- fen = PositionToFEN(currentMove, NULL);
+ fen = PositionToFEN(currentMove, NULL, 1);
fprintf(f, "%s\n", fen);
free(fen);
}
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 */
{
char buf[MSG_SIZ];
ChessSquare piece = boards[0][y][x];
+ static Board erasedBoard, currentBoard, menuBoard, nullBoard;
+ static int lastVariant;
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");
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++) {
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;
+ }
+ DisplayMessage("Clicking clock again restores position", "");
+ if(gameInfo.variant != lastVariant) lastVariant = gameInfo.variant, CopyBoard(erasedBoard, boards[0]);
+ 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)
+ && !CompareBoards(nullBoard, erasedBoard)) {
+ CopyBoard(boards[0], erasedBoard);
+ } else
+ CopyBoard(erasedBoard, currentBoard);
+
}
}
if (gameMode == EditPosition) {
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;
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;
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;
CreateBookEvent ()
{
ListGame * lg = (ListGame *) gameList.head;
- FILE *f;
+ FILE *f, *g;
int nItem;
static int secondTime = FALSE;
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;
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;
int oldlen, len;
char *old;
-if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces); fflush(debugFP);
+if(appData.debugMode) fprintf(debugFP, "Append: in='%s' %d\n", text, addBraces);
+ if(addBraces == 3) addBraces = 0; else // force appending literally
text = GetInfoFromComment( index, text ); /* [HGM] PV time: strip PV info from comment */
CrushCRs(text);
int time = -1, sec = 0, deci;
char * s_eval = FindStr( text, "[%eval " );
char * s_emt = FindStr( text, "[%emt " );
-
+#if 0
if( s_eval != NULL || s_emt != NULL ) {
+#else
+ if(0) { // [HGM] this code is not finished, and could actually be detrimental
+#endif
/* New style */
char delim;
}
p = text;
+ if(!strncmp(p+1, "final score ", 12)) p += 12, index++; else
if(p[1] == '(') { // comment starts with PV
p = strchr(p, ')'); // locate end of PV
if(p == NULL || sep < p+5) return text;
if(sec >= 0) time = 600*time + 10*sec; else
if(deci >= 0) time = 10*time + deci; else time *= 10; // deci-sec
- score = score >= 0 ? score*100 + score_lo : score*100 - score_lo;
+ score = score > 0 || !score & p[1] != '-' ? score*100 + score_lo : score*100 - score_lo;
/* [HGM] PV time: now locate end of PV info */
while( *++sep >= '0' && *sep <= '9'); // strip depth
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)
{
/* 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;
ChessMove moveType;
// [HGM] FENedit
- if(gameMode == EditPosition && ParseFEN(board, &n, move) ) {
+ if(gameMode == EditPosition && ParseFEN(board, &n, move, TRUE) ) {
EditPositionPasteFEN(move);
return;
}
char *
-PositionToFEN (int move, char *overrideCastling)
+PositionToFEN (int move, char *overrideCastling, int moveCounts)
{
int i, j, fromX, fromY, toX, toY;
int whiteToPlay;
*p++ = '+';
piece = (ChessSquare)(DEMOTED piece);
}
- *p++ = PieceToChar(piece);
+ *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece));
if(p[-1] == '~') {
/* [HGM] flag promoted pieces as '<promoted>~' (Crazyhouse) */
p[-1] = PieceToChar((ChessSquare)(DEMOTED piece));
}
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;
}
}
- /* [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++)
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);
}
Boolean
-ParseFEN (Board board, int *blackPlaysFirst, char *fen)
+ParseFEN (Board board, int *blackPlaysFirst, char *fen, Boolean autoSize)
{
- int i, j;
+ int i, j, k, w=0;
char *p, c;
int emptycount, virgin[BOARD_FILES];
ChessSquare piece;
p = fen;
- /* [HGM] by default clear Crazyhouse holdings, if present */
- if(gameInfo.holdingsWidth) {
- for(i=0; i<BOARD_HEIGHT; i++) {
- board[i][0] = EmptySquare; /* black holdings */
- board[i][BOARD_WIDTH-1] = EmptySquare; /* white holdings */
- board[i][1] = (ChessSquare) 0; /* black counts */
- board[i][BOARD_WIDTH-2] = (ChessSquare) 0; /* white counts */
- }
- }
-
/* Piece placement data */
for (i = BOARD_HEIGHT - 1; i >= 0; i--) {
j = 0;
for (;;) {
- if (*p == '/' || *p == ' ' || (*p == '[' && i == 0) ) {
- if (*p == '/') p++;
+ if (*p == '/' || *p == ' ' || *p == '[' ) {
+ if(j > w) w = j;
emptycount = gameInfo.boardWidth - j;
while (emptycount--)
board[i][(j++)+gameInfo.holdingsWidth] = EmptySquare;
+ if (*p == '/') p++;
+ else if(autoSize) { // we stumbled unexpectedly into end of board
+ for(k=i; k<BOARD_HEIGHT; k++) { // too few ranks; shift towards bottom
+ for(j=0; j<BOARD_WIDTH; j++) board[k-i][j] = board[k][j];
+ }
+ appData.NrRanks = gameInfo.boardHeight - i; i=0;
+ }
break;
#if(BOARD_FILES >= 10)
} else if(*p=='x' || *p=='X') { /* [HGM] X means 10 */
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 */
if(*p=='+') {
piece = CharToPiece(*++p);
if(piece == EmptySquare) return FALSE; /* unknown piece */
- piece = (ChessSquare) (PROMOTED piece ); p++;
+ piece = (ChessSquare) (CHUPROMOTED piece ); p++;
if(PieceToChar(piece) != '+') return FALSE; /* unpromotable piece */
} else piece = CharToPiece(*p++);
}
while (*p == '/' || *p == ' ') p++;
+ if(autoSize) appData.NrFiles = w, InitPosition(TRUE);
+
+ /* [HGM] by default clear Crazyhouse holdings, if present */
+ if(gameInfo.holdingsWidth) {
+ for(i=0; i<BOARD_HEIGHT; i++) {
+ board[i][0] = EmptySquare; /* black holdings */
+ board[i][BOARD_WIDTH-1] = EmptySquare; /* white holdings */
+ board[i][1] = (ChessSquare) 0; /* black counts */
+ board[i][BOARD_WIDTH-2] = (ChessSquare) 0; /* white counts */
+ }
+ }
+
/* [HGM] look for Crazyhouse holdings here */
while(*p==' ') p++;
if( gameInfo.holdingsWidth && p[-1] == '/' || *p == '[') {
/* 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 {
if (fen != NULL) {
Board initial_position;
- if (!ParseFEN(initial_position, &blackPlaysFirst, fen)) {
+ if (!ParseFEN(initial_position, &blackPlaysFirst, fen, TRUE)) {
DisplayError(_("Bad FEN position in clipboard"), 0);
return ;
} else {