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);
{
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;
}
if(c == '^' || c == '-') { // has specified partner
int p;
for(p=0; p<EmptySquare; p++) if(table[p] == partner[i]) break;
- if(c == '^') table[i] = '+';
if(p < EmptySquare) {
if(promoPartner[promoPartner[p]] == p) promoPartner[promoPartner[p]] = promoPartner[p]; // divorce old partners
if(promoPartner[promoPartner[i]] == i) promoPartner[promoPartner[i]] = promoPartner[i];
promoPartner[p] = i, promoPartner[i] = p; // and marry this couple
}
+ if(c == '-') autoProm[i] = autoProm[p] = '-', c = '^';
+ if(c == '^') table[i] = '+';
} else if(c == '*') {
table[i] = partner[i];
promoPartner[i] = (i < BlackPawn ? WhiteTokin : BlackTokin); // promotes to Tokin
initialPosition[EP_STATUS] = EP_NONE;
initialPosition[TOUCHED_W] = initialPosition[TOUCHED_B] = 0;
SetCharTableEsc(pieceToChar, "PNBRQ...........Kpnbrq...........k", SUFFIXES);
+ for(i=0; i<EmptySquare; i++) autoProm[i] = 0;
if(startVariant == gameInfo.variant) // [HGM] nicks: enable nicknames in original variant
SetCharTable(pieceNickName, appData.pieceNickNames);
else SetCharTable(pieceNickName, "............");
if(gameInfo.variant == VariantKnightmate)
king += (int) WhiteUnicorn - (int) WhiteKing;
+ if(autoProm[piece]) {
+ board[toY][toX] = (autoProm[piece] != '!' || board[toY][toX] != EmptySquare ? CHUPROMOTED(piece) : piece);
+ board[fromY][fromX] = EmptySquare;
+ } else
if(pieceDesc[piece] && killX >= 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;
/* 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;
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)
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;
else { *p++ = '0' + emptycount/10; *p++ = '0' + emptycount%10; }
emptycount = 0;
}
- if(PieceToChar(piece) == '+') {
+ c = PieceToChar(piece);
+ if(c == '+') {
/* [HGM] write promoted pieces as '+<unpromoted>' (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 '<promoted>~' (Crazyhouse) */
'x' };
unsigned char pieceNickName[EmptySquare];
int promoPartner[EmptySquare];
+unsigned char autoProm[EmptySquare];
char
PieceToChar (ChessSquare p)
}
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
((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
}
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
((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) {
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 ||
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
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')
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)) {
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!
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;
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