From: H.G.Muller Date: Sat, 21 Apr 2018 19:02:52 +0000 (+0200) Subject: Implement auto-promotion X-Git-Url: http://winboard.nl/cgi-bin?a=commitdiff_plain;h=c720c7e1e75f74427cef3ef2534d26c6fc9c4252;p=xboard.git Implement auto-promotion A piece that in the pieceToCharTable is marked as a promoted version of some other piece by using a '-' instead of a '^' will now define both partners as auto-promoting. This means promotion suffixes on SAN moves for them will be suppressed an ignored, while in protocol moves they will always be added. It will also be allowed to drop these pieces in promoted form. The notation for this will use the nickname of the promoted version, rather than a +X notation. --- diff --git a/backend.c b/backend.c index 05306de..2394fb6 100644 --- a/backend.c +++ b/backend.c @@ -5402,10 +5402,15 @@ void CoordsToComputerAlgebraic (int rf, int ff, int rt, int ft, char promoChar, char move[9]) { if (rf == DROP_RANK) { - if(ff == EmptySquare) sprintf(move, "@@@@\n"); else // [HGM] pass - sprintf(move, "%c@%c%c\n", - ToUpper(PieceToChar((ChessSquare) ff)), AAA + ft, ONE + rt); + if(ff == EmptySquare) sprintf(move, "@@@@\n"); else { // [HGM] pass + char p = PieceToChar((ChessSquare) ff); + if(p == '+' && pieceNickName[ff] >= 'A') p = pieceNickName[ff]; // prefer nick over +X + sprintf(move, "%c@%c%c\n", + ToUpper(p), AAA + ft, ONE + rt); + } } else { + char c = autoProm[boards[forwardMostMove][rf][ff]]; + if(c && (c == '-' || boards[forwardMostMove][rt][ft] != EmptySquare)) promoChar = '+'; if (promoChar == 'x' || promoChar == NULLCHAR) { sprintf(move, "%c%c%c%c\n", AAA + ff, ONE + rf, AAA + ft, ONE + rt); @@ -6066,7 +6071,7 @@ ptclen (const char *s, char *escapes) { int n = 0; if(!*escapes) return strlen(s); - while(*s) n += (*s != '/' && *s != '-' && *s != '^' && *s != '*' && !strchr(escapes, *s)) - 2*(*s == '='), s++; + while(*s) n += (!strchr("-*/^", *s) && !strchr(escapes, *s)) - 2*(*s == '='), s++; return n; } @@ -6114,12 +6119,13 @@ SetCharTableEsc (unsigned char *table, const char * map, char * escapes) if(c == '^' || c == '-') { // has specified partner int p; for(p=0; p= 0 && strchr(pieceDesc[piece], 'O') // Betza castling-enabled && (piece < BlackPawn ? killed < BlackPawn : killed >= BlackPawn)) { // and tramples own board[toY][toX] = piece; board[fromY][fromX] = EmptySquare; @@ -10668,6 +10679,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) /* and erasing image if necessary */ p = fromY == DROP_RANK ? (int) fromX : CharToPiece(piece > BlackPawn ? ToLower(promoChar) : ToUpper(promoChar)); if(p < (int) BlackPawn) { /* white drop */ + if(PieceToChar(p) == '+') p = CHUDEMOTED(p); // promoted drop p -= (int)WhitePawn; p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) p = 0; @@ -10677,6 +10689,7 @@ ApplyMove (int fromX, int fromY, int toX, int toY, int promoChar, Board board) board[p][BOARD_WIDTH-2] = 0; } else { /* black drop */ p -= (int)BlackPawn; + if(PieceToChar(p) == '+') p = CHUDEMOTED(p); p = PieceToNumber((ChessSquare)p); if(p >= gameInfo.holdingsSize) p = 0; if(--board[handSize-1-p][1] <= 0) @@ -18474,7 +18487,7 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) int i, j, fromX, fromY, toX, toY; int whiteToPlay, haveRights = nrCastlingRights; char buf[MSG_SIZ]; - char *p, *q; + char *p, *q, c; int emptycount; ChessSquare piece; @@ -18496,12 +18509,13 @@ PositionToFEN (int move, char *overrideCastling, int moveCounts) else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; } emptycount = 0; } - if(PieceToChar(piece) == '+') { + c = PieceToChar(piece); + if(c == '+') { /* [HGM] write promoted pieces as '+' (Shogi) */ - *p++ = '+'; - piece = (ChessSquare)(CHUDEMOTED(piece)); + if(autoProm[piece] && pieceNickName[piece] >= 'A') c = pieceNickName[piece]; else + *p++ = '+', piece = (ChessSquare)(CHUDEMOTED(piece)), c = PieceToChar(piece); } - *p++ = (piece == DarkSquare ? '*' : PieceToChar(piece)); + *p++ = (piece == DarkSquare ? '*' : c); if(*p = PieceSuffix(piece)) p++; if(p[-1] == '~') { /* [HGM] flag promoted pieces as '~' (Crazyhouse) */ diff --git a/moves.c b/moves.c index a6b066b..013f0c1 100644 --- a/moves.c +++ b/moves.c @@ -122,6 +122,7 @@ unsigned char pieceToChar[EmptySquare+1] = { 'x' }; unsigned char pieceNickName[EmptySquare]; int promoPartner[EmptySquare]; +unsigned char autoProm[EmptySquare]; char PieceToChar (ChessSquare p) @@ -790,7 +791,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, } if (rf < BOARD_HEIGHT-1 && board[rf + 1][ff] == EmptySquare) { callback(board, flags, - rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove, + rf >= BOARD_HEIGHT-1-promoRank && !autoProm[WhitePawn] ? WhitePromotion : NormalMove, rf, ff, rf + 1, ff, closure); } if (rf <= (BOARD_HEIGHT>>1)-3 && board[rf+1][ff] == EmptySquare && // [HGM] grand: also on 3rd rank on 10-board @@ -805,7 +806,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ((flags & F_KRIEGSPIEL_CAPTURE) || BlackPiece(board[rf + 1][ff + s]))) { callback(board, flags, - rf >= BOARD_HEIGHT-1-promoRank ? WhitePromotion : NormalMove, + rf >= BOARD_HEIGHT-1-promoRank && !autoProm[WhitePawn] ? WhitePromotion : NormalMove, rf, ff, rf + 1, ff + s, closure); } if (rf >= BOARD_HEIGHT+1>>1) {// [HGM] grand: 4th & 5th rank on 10-board @@ -841,7 +842,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, } if (rf > 0 && board[rf - 1][ff] == EmptySquare) { callback(board, flags, - rf <= promoRank ? BlackPromotion : NormalMove, + rf <= promoRank && !autoProm[BlackPawn] ? BlackPromotion : NormalMove, rf, ff, rf - 1, ff, closure); } if (rf >= (BOARD_HEIGHT+1>>1)+2 && board[rf-1][ff] == EmptySquare && // [HGM] grand @@ -856,7 +857,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, ((flags & F_KRIEGSPIEL_CAPTURE) || WhitePiece(board[rf - 1][ff + s]))) { callback(board, flags, - rf <= promoRank ? BlackPromotion : NormalMove, + rf <= promoRank && !autoProm[BlackPawn] ? BlackPromotion : NormalMove, rf, ff, rf - 1, ff + s, closure); } if (rf < BOARD_HEIGHT>>1) { @@ -1822,14 +1823,15 @@ HasLion (Board board, int flags) ChessMove LegalDrop (Board board, int flags, ChessSquare piece, int rt, int ft) { // [HGM] put drop legality testing in separate routine for clarity - int n; + int n, p = piece; if(appData.debugMode) fprintf(debugFP, "LegalDrop: %d @ %d,%d)\n", piece, ft, rt); if(board[rt][ft] != EmptySquare) return ImpossibleMove; // must drop to empty square - n = PieceToNumber(piece); - if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[handSize-1-n][0]) != piece) + if(PieceToChar(piece) == '+') p = CHUDEMOTED(p); + n = PieceToNumber(p); + if((gameInfo.holdingsWidth == 0 || (flags & F_WHITE_ON_MOVE ? board[n][BOARD_WIDTH-1] : board[handSize-1-n][0]) != p) && gameInfo.variant != VariantBughouse) // in bughouse we don't check for availability, because ICS doesn't always tell us return ImpossibleMove; // piece not available - if(gameInfo.variant == VariantShogi) { // in Shogi lots of drops are forbidden! + if(gameInfo.variant == VariantShogi && !autoProm[piece]) { // in Shogi lots of drops are forbidden! (but not in Kyoto/micro-) if((piece == WhitePawn || piece == WhiteQueen) && rt == BOARD_HEIGHT-1 || (piece == BlackPawn || piece == BlackQueen) && rt == 0 || piece == WhiteKnight && rt > BOARD_HEIGHT-3 || @@ -1897,6 +1899,7 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC return(IllegalMove); // [HGM] losers: if there are legal captures, non-capts are illegal if(promoChar == 'x') promoChar = NULLCHAR; // [HGM] is this ever the case? + if(autoProm[piece]) promoChar = NULLCHAR; // ignore promotion characters on auto-promoting pieces if(gameInfo.variant == VariantSChess && promoChar && promoChar != '=' && board[rf][ff] != WhitePawn && board[rf][ff] != BlackPawn) { if(board[rf][ff] < BlackPawn) { // white if(rf != 0) return IllegalMove; // must be on back rank @@ -2323,7 +2326,9 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p if (rf == DROP_RANK) { if(ff == EmptySquare) { strncpy(outp, "--",3); return NormalMove; } // [HGM] pass /* Bughouse piece drop */ - *outp++ = ToUpper(PieceToChar((ChessSquare) ff)); + c = PieceToChar((ChessSquare) ff); + if(c == '+') c = pieceNickName[ff]; // must have nickname for promote drop + *outp++ = ToUpper(c); *outp++ = '@'; *outp++ = ft + AAA; if(rt+ONE <= '9') @@ -2443,12 +2448,15 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p else "N1f3" or "N5xf7", else "Ng1f3" or "Ng5xf7". */ + if(c=='+') { + c = pieceNickName[piece]; // prefer any nick over +X notation + if(c < 'A') *outp++ = c = '+'; + } if( c == '~' || c == '+') { /* [HGM] print nonexistent piece as its demoted version */ - piece = (ChessSquare) (CHUDEMOTED(piece)); + c = PieceToChar((ChessSquare) (CHUDEMOTED(piece))); } - if(c=='+') *outp++ = c; - *outp++ = ToUpper(PieceToChar(piece)); + *outp++ = ToUpper(c); if(*outp = PieceSuffix(piece)) outp++; if (cl.file || (cl.either && !cl.rank)) { @@ -2467,6 +2475,7 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p if(rt+ONE <= '9') *outp++ = rt + ONE; else { *outp++ = (rt+ONE-'0')/10 + '0';*outp++ = (rt+ONE-'0')%10 + '0'; } + if(autoProm[piece]) promoChar = 0; // no promotion suffix for implied promotions if (IS_SHOGI(gameInfo.variant)) { /* [HGM] in Shogi non-pawns can promote */ *outp++ = promoChar; // Don't bother to correct move type, return value is never used! diff --git a/moves.h b/moves.h index 4a02a19..2148269 100644 --- a/moves.h +++ b/moves.h @@ -64,6 +64,7 @@ extern void CopyBoard P((Board to, Board from)); extern int CompareBoards P((Board board1, Board board2)); extern unsigned char pieceToChar[(int)EmptySquare+1]; extern unsigned char pieceNickName[(int)EmptySquare]; +extern unsigned char autoProm[(int)EmptySquare]; extern int promoPartner[(int)EmptySquare]; extern char *pieceDesc[(int)EmptySquare]; extern Board initialPosition; diff --git a/parser.c b/parser.c index 797a40c..ff33245 100644 --- a/parser.c +++ b/parser.c @@ -553,8 +553,10 @@ NextUnit (char **p) if(!(appData.icsActive && PieceToChar(realPiece) == '+') && // trust ICS if it moves promoted pieces piece && realPiece != cl.pieceIn) return ImpossibleMove; } else if(!separator && **p == '+') { // could be a protocol move, where bare '+' suffix means shogi-style promotion - if(realPiece < (wom ? WhiteCannon : BlackCannon) && PieceToChar(PROMOTED(realPiece)) == '+') // seems to be that + if(PieceToChar(CHUPROMOTED(realPiece)) == '+') { // seems to be that + if(!autoProm[realPiece]) **p++; else // for now ignore promosuffix on auto-promoting pieces' protocol moves currentMoveString[4] = cl.promoCharIn = *(*p)++; // append promochar after all + } } result = LegalityTest(boards[yyboardindex], PosFlags(yyboardindex), fromY, fromX, toY, toX, cl.promoCharIn); if (currentMoveString[4] == NULLCHAR) { // suppy missing mandatory promotion character