X-Git-Url: http://winboard.nl/cgi-bin?a=blobdiff_plain;f=moves.c;h=92c0371b83919ba08d0c6fc233be0d33f0f6515d;hb=9089c023e881715fe42fe104d24df44489cd3e35;hp=787ab57ab04f55a074909ac8f4aec91bebd3ec19;hpb=01c9a60a2a37ba6441eeacca3a81cbb3ae3d484d;p=xboard.git diff --git a/moves.c b/moves.c index 787ab57..92c0371 100644 --- a/moves.c +++ b/moves.c @@ -5,7 +5,7 @@ * Massachusetts. * * Enhancements Copyright 1992-2001, 2002, 2003, 2004, 2005, 2006, - * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc. + * 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Free Software Foundation, Inc. * * Enhancements Copyright 2005 Alessandro Scotti * @@ -71,7 +71,6 @@ int BlackPiece P((ChessSquare)); int SameColor P((ChessSquare, ChessSquare)); int PosFlags(int index); -extern signed char initialRights[BOARD_FILES]; /* [HGM] all rights enabled, set in InitPosition */ int quickFlag; char *pieceDesc[EmptySquare]; char *defaultDesc[EmptySquare] = { @@ -114,19 +113,33 @@ SameColor (ChessSquare piece1, ChessSquare piece2) #define SameColor(piece1, piece2) (piece1 < EmptySquare && piece2 < EmptySquare && (piece1 < BlackPawn) == (piece2 < BlackPawn) || piece1 == DarkSquare || piece2 == DarkSquare) #endif -char pieceToChar[] = { +unsigned char pieceToChar[EmptySquare+1] = { 'P', 'N', 'B', 'R', 'Q', 'F', 'E', 'A', 'C', 'W', 'M', 'O', 'H', 'I', 'J', 'G', 'D', 'V', 'L', 'S', 'U', 'K', 'p', 'n', 'b', 'r', 'q', 'f', 'e', 'a', 'c', 'w', 'm', 'o', 'h', 'i', 'j', 'g', 'd', 'v', 'l', 's', 'u', 'k', 'x' }; -char pieceNickName[EmptySquare]; +unsigned char pieceNickName[EmptySquare]; +int promoPartner[EmptySquare]; char PieceToChar (ChessSquare p) { - if((int)p < 0 || (int)p >= (int)EmptySquare) return('x'); /* [HGM] for safety */ - return pieceToChar[(int) p]; + int c; + if((int)p < 0 || (int)p >= (int)EmptySquare) return('?'); /* [HGM] for safety */ + c = pieceToChar[(int) p]; + if(c & 128) c = c & 63 | 64; + return c; +} + +char +PieceSuffix (ChessSquare p) +{ + int c; + if((int)p < 0 || (int)p >= (int)EmptySquare) return 0; /* [HGM] for safety */ + c = pieceToChar[(int) p]; + if(c < 128) return 0; + return SUFFIXES[c - 128 >> 6]; } int @@ -191,7 +204,7 @@ CollectPieceDescriptors () // dump all engine defined pieces, and pieces with non-standard names, // but suppress black pieces that are the same as their white counterpart ChessSquare p; - static char buf[MSG_SIZ]; + static char buf[MSG_SIZ], s[2]; char *m, c, d, *pieceName = defaultName; int len; *buf = NULLCHAR; @@ -201,22 +214,56 @@ CollectPieceDescriptors () if(gameInfo.variant == VariantXiangqi) pieceName = xqName; for(p=WhitePawn; p= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == toupper(c) - && (c != '+' || pieceToChar[DEMOTED BLACK_TO_WHITE p] == d)) { // black member of normal pair + m = pieceDesc[p]; d = (c == '+' ? pieceToChar[DEMOTED(p)] : c); + if(p >= BlackPawn && pieceToChar[BLACK_TO_WHITE p] == (c & ~32) + && (c != '+' || pieceToChar[DEMOTED(BLACK_TO_WHITE p)] == d)) {// black member of normal pair char *wm = pieceDesc[BLACK_TO_WHITE p]; if(!m && !wm || m && wm && !strcmp(wm, m)) continue; // moves as a white piece } else // white or unpaired black - if((p < BlackPawn || CharToPiece(toupper(d)) != EmptySquare) && // white or lone black + if((p < BlackPawn || CharToPiece(d & ~32) != EmptySquare) && // white or lone black !pieceDesc[p] /*&& pieceName[p] == c*/) continue; // orthodox piece known by its usual name // TODO: listing pieces because of unusual name can only be done if we have accurate Betza of all defaults if(!m) m = defaultDesc[p]; + if(!m) continue; len = strlen(buf); - snprintf(buf+len, MSG_SIZ-len, "%s%s%c:%s", len ? ";" : "", c == '+' ? "+" : "", d, m); + *s = (d > 128 ? SUFFIXES[d-128>>6] : 0); d = 64 + (d & 63); + snprintf(buf+len, MSG_SIZ-len, "%s%s%c%s:%s", len ? ";" : "", c == '+' ? "+" : "", d, s, m); } return buf; } +int +LoadPieceDesc (char *s) +{ + ChessSquare piece; + static char suf[] = SUFFIXES; + char *r, *p, *q = s; + int ok = TRUE, promoted, c; + while(q && *s) { + p = s; + q = strchr(s, ';'); + if(q) *q = 0, s = q+1; + if(*p == '+') promoted = 1, p++; else promoted = 0; + c = *p++; + if(!c) { ok = FALSE; continue; } // bad syntax + if(*p && (r = strchr(suf, *p))) c += 64*(r - suf + 1), p++; + if(*p++ != ':') { ok = FALSE; continue; } // bad syntax + if(!strcmp(p, "(null)")) continue; // handle bug in writing of XBoard 4.8.0 + piece = CharToPiece(c); + if(piece >= EmptySquare) { ok = FALSE; continue; } // non-existent piece + if(promoted) { + piece = promoPartner[piece]; + if(pieceToChar[piece] != '+') { ok = FALSE; continue; } // promoted form does not exist + } + ASSIGN(pieceDesc[piece], p); + if(piece < BlackPawn && (pieceToChar[WHITE_TO_BLACK piece] == pieceToChar[piece] + 32 || promoted)) { + ASSIGN(pieceDesc[WHITE_TO_BLACK piece], p); + } + pieceDefs = TRUE; + } + return ok; +} + // [HGM] gen: configurable move generation from Betza notation sent by engine. // Some notes about two-leg moves: GenPseudoLegal() works in two modes, depending on whether a 'kill- // square has been set: without one is generates all moves, and a global int legNr flags in bits 0 and 1 @@ -235,9 +282,14 @@ char symmetry[] = "FBNW.FFW.NKN.NW.QR....W..N"; char xStep[] = "2110.130.102.10.00....0..2"; char yStep[] = "2132.133.313.20.11....1..3"; char dirType[] = "01000104000200000260050000"; +char upgrade[] = "AFCD.BGH.JQL.NO.KW....R..Z"; +char rotate[] = "DRCA.WHG.JKL.NO.QB....F..Z"; + // alphabet "a b c d e f g h i j k l m n o p q r s t u v w x y z " int dirs1[] = { 0,0x3C,0,0,0,0xC3,0,0, 0,0,0,0xF0,0,0,0,0,0,0x0F,0 ,0,0,0 ,0,0,0,0 }; int dirs2[] = { 0,0x18,0,0,0,0x81,0,0xFF,0,0,0,0x60,0,0,0,0,0,0x06,0x66,0,0,0x99,0,0,0,0 }; +int dirs3[] = { 0,0x38,0,0,0,0x83,0,0xFF,0,0,0,0xE0,0,0,0,0,0,0x0E,0xEE,0,0,0xBB,0,0,0,0 }; +int dirs4[] = { 0,0x10,0,0,0,0x01,0,0xFF,0,0,0,0x40,0,0,0,0,0,0x04,0x44,0,0,0x11,0,0,0,0 }; int rot[][4] = { // rotation matrices for each direction { 1, 0, 0, 1 }, @@ -259,21 +311,27 @@ OK (Board board, int flags, ChessMove kind, int rf, int ff, int rt, int ft, VOID void MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle, char *desc, MoveCallback cb, VOIDSTAR cl) { - char *p = desc; - int mine, his, dir, bit, occup, i; + char buf[80], *p = desc, *atom = NULL; + int mine, his, dir, bit, occup, i, ep, promoRank = -1; + ChessMove promo= NormalMove; ChessSquare pc = board[r][f]; + if(pc == DarkSquare) return; // this is not a piece, but a 'hole' in the board if(flags & F_WHITE_ON_MOVE) his = 2, mine = 1; else his = 1, mine = 2; + if(pc == WhitePawn || pc == WhiteLance) promo = WhitePromotion, promoRank = BOARD_HEIGHT-1; else + if(pc == BlackPawn || pc == BlackLance) promo = BlackPromotion, promoRank = 0; while(*p) { // more moves to go - int expo = 1, dx, dy, x, y, mode, dirSet, retry=0, initial=0, jump=1, skip = 0; + int expo = -1, dx, dy, x, y, mode, dirSet, ds2=0, retry=0, initial=0, jump=1, skip = 0, all = 0; char *cont = NULL; - if(*p == 'i') initial = 1, desc = ++p; + while(*p == 'i') initial++, desc = ++p; while(islower(*p)) p++; // skip prefixes if(!isupper(*p)) return; // syntax error: no atom + dx = xStep[*p-'A'] - '0';// step vector of atom + dy = yStep[*p-'A'] - '0'; dirSet = 0; // build direction set based on atom symmetry switch(symmetry[*p-'A']) { case 'B': expo = 0; // bishop, slide - case 'F': // diagonal atom (degenerate 4-fold) - if(tx < 0) { // for continuation legs relative directions are orthogonal! - while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { + case 'F': all = 0xAA; // diagonal atom (degenerate 4-fold) + if(tx >= 0) goto king; // continuation legs specified in K/Q system! + while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { int b = dirs1[*desc-'a']; // use wide version if( islower(desc[1]) && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim) @@ -281,17 +339,21 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle desc += 2; } else desc++; dirSet |= b; - } - dirSet &= 0x55; if(!dirSet) dirSet = 0x55; - break; } + dirSet &= 0xAA; if(!dirSet) dirSet = 0xAA; + break; case 'R': expo = 0; // rook, slide - case 'W': // orthogonal atom (non-deg 4-fold) + case 'W': all = 0x55; // orthogonal atom (non-deg 4-fold) + if(tx >= 0) goto king; // continuation legs specified in K/Q system! while(islower(*desc) && (dirType[*desc-'a'] & ~4) != '0') dirSet |= dirs2[*desc++-'a']; dirSet &= 0x55; if(!dirSet) dirSet = 0x55; dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system break; - case 'N': // oblique atom (degenerate 8-fold) + case 'N': all = 0xFF; // oblique atom (degenerate 8-fold) + if(tx >= 0) goto king; // continuation legs specified in K/Q system! + if(*desc == 'h') { // chiral direction sets 'hr' and 'hl' + dirSet = (desc[1] == 'r' ? 0x55 : 0xAA); desc += 2; + } else while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { int b = dirs2[*desc-'a']; // when alone, use narrow version if(desc[1] == 'h') b = dirs1[*desc-'a'], desc += 2; // dirs1 is wide version @@ -305,47 +367,74 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle if(!dirSet) dirSet = 0xFF; break; case 'Q': expo = 0; // queen, slide - case 'K': // non-deg (pseudo) 8-fold - dirSet=0x55; // start with orthogonal moves - retry = 1; // and schedule the diagonal moves for later + case 'K': all = 0xFF; // non-deg (pseudo) 8-fold + king: + while(islower(*desc) && (i = dirType[*desc-'a']) != '0') { + int b = dirs4[*desc-'a']; // when alone, use narrow version + if(desc[1] == *desc) desc++; // doubling forces alone + else if(islower(desc[1]) && i < '4' + && ((i | dirType[desc[1]-'a']) & 3) == 3) { // combinable (perpendicular dim or same) + b = dirs3[*desc-'a'] & dirs3[desc[1]-'a']; // intersect wide & perp wide + desc += 2; + } else desc++; + dirSet |= b; + } + if(!dirSet) dirSet = (tx < 0 ? 0xFF // default is all directions, but in continuation leg + : all == 0xFF ? 0xEF : 0x45); // omits backward, and for 4-fold atoms also diags + dirSet = (dirSet << angle | dirSet >> 8-angle) & 255; // re-orient direction system + ds2 = dirSet & 0xAA; // extract diagonal directions + if(dirSet &= 0x55) // start with orthogonal moves, if present + retry = 1, dx = 0; // and schedule the diagonal moves for later + else dx = dy, dirSet = ds2; // if no orthogonal directions, do diagonal immediately break; // should not have direction indicators default: return; // syntax error: invalid atom } if(mine == 2 && tx < 0) dirSet = dirSet >> 4 | dirSet << 4 & 255; // invert black moves mode = 0; // build mode mask - if(*desc == 'm') mode |= 4, desc++; - if(*desc == 'c') mode |= his, desc++; - if(*desc == 'd') mode |= mine, desc++; - if(*desc == 'e') mode |= 8, desc++; - if(*desc == 'p') mode |= 32, desc++; - if(*desc == 'g') mode |= 64, desc++; - if(*desc == 'o') mode |= 128, desc++; - if(*desc == 'n') jump = 0, desc++; - while(*desc == 'j') jump++, desc++; - if(*desc == 'a') cont = ++desc; - if(!cont) { - if(!(mode & 15)) mode = his + 4; // no mode spec, use default = mc - } else { - if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through' - if(!(mode & 0x10F)) mode = his + 0x104; // and default = mcp - } - dx = xStep[*p-'A'] - '0'; // step vector of atom - dy = yStep[*p-'A'] - '0'; + if(*desc == 'm') mode |= 4, desc++; // move to empty + if(*desc == 'c') mode |= his, desc++; // capture foe + if(*desc == 'd') mode |= mine, desc++; // destroy (capture friend) + if(*desc == 'e') mode |= 8, desc++; // e.p. capture last mover + if(*desc == 't') mode |= 16, desc++; // exclude enemies as hop platform ('test') + if(*desc == 'p') mode |= 32, desc++; // hop over occupied + if(*desc == 'g') mode |= 64, desc++; // hop and toggle range + if(*desc == 'o') mode |= 128, desc++; // wrap around cylinder board + if(*desc == 'y') mode |= 512, desc++; // toggle range on empty square + if(*desc == 'n') jump = 0, desc++; // non-jumping + while(*desc == 'j') jump++, desc++; // must jump (on B,R,Q: skip first square) + if(*desc == 'a') cont = ++desc; // move again after doing what preceded it if(isdigit(*++p)) expo = atoi(p++); // read exponent if(expo > 9) p++; // allow double-digit desc = p; // this is start of next move + if(initial == 2) { if(board[r][f] != initialPosition[r-2*his+3][f]) continue; } else if(initial && (board[r][f] != initialPosition[r][f] || r == 0 && board[TOUCHED_W] & 1< 1 && dx == 0 && dy == 0) { // castling indicated by O + number - mode |= 16; dy = 1; + if(expo > 0 && dx == 0 && dy == 0) { // castling indicated by O + number + mode |= 1024; dy = 1; + } + if(expo < 0) expo = 1; // use 1 for default + if(!cont) { + if(!(mode & 15)) mode |= his + 4; // no mode spec, use default = mc + } else { + strncpy(buf, cont, 80); cont = buf; // copy next leg(s), so we can modify + atom = buf; while(islower(*atom)) atom++; // skip to atom + if(mode & 32) mode ^= 256 + 32; // in non-final legs 'p' means 'pass through' + if(mode & 64 + 512) { + mode |= 256; // and 'g' too, but converts leaper <-> slider + if(mode & 512) mode ^= 0x304; // and 'y' is m-like 'g' + *atom = upgrade[*atom-'A']; // replace atom, BRQ <-> FWK + atom[1] = atom[2] = '\0'; // make sure any old range is stripped off + if(expo == 1) atom[1] = '0'; // turn other leapers into riders + } + if(!(mode & 0x30F)) mode |= 4; // and default of this leg = m } if(dy == 1) skip = jump - 1, jump = 1; // on W & F atoms 'j' = skip first square do { for(dir=0, bit=1; dir<8; dir++, bit += bit) { // loop over directions - int i = expo, j = skip, hop = mode, vx, vy; + int i = expo, j = skip, hop = mode, vx, vy, loop = 0; if(!(bit & dirSet)) continue; // does not move in this direction - if(dy != 1) j = 0; // + if(dy != 1 || mode & 1024) j = 0; // vx = dx*rot[dir][0] + dy*rot[dir][1]; // rotate step vector vy = dx*rot[dir][2] + dy*rot[dir][3]; if(tx < 0) x = f, y = r; // start square @@ -353,17 +442,21 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle do { // traverse ray x += vx; y += vy; // step to next square if(y < 0 || y >= BOARD_HEIGHT) break; // vertically off-board: always done - if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT; else break; } - if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT; else break; } + if(x < BOARD_LEFT) { if(mode & 128) x += BOARD_RGHT - BOARD_LEFT, loop++; else break; } + if(x >= BOARD_RGHT) { if(mode & 128) x -= BOARD_RGHT - BOARD_LEFT, loop++; else break; } if(j) { j--; continue; } // skip irrespective of occupation + if(board[y][x] == DarkSquare) break; // black squares are supposed to be off board if(!jump && board[y - vy + vy/2][x - vx + vx/2] != EmptySquare) break; // blocked if(jump > 1 && board[y - vy + vy/2][x - vx + vx/2] == EmptySquare) break; // no hop - if(x == f && y == r) occup = 4; else // start square counts as empty + if(x == f && y == r && !loop) occup = 4; else // start square counts as empty (if not around cylinder!) if(board[y][x] < BlackPawn) occup = 0x101; else if(board[y][x] < EmptySquare) occup = 0x102; else occup = 4; if(cont) { // non-final leg + if(mode&16 && his&occup) occup &= 3;// suppress hopping foe in t-mode if(occup & mode) { // valid intermediate square, do continuation + char origAtom = *atom; + if(!(bit & all)) *atom = rotate[*atom - 'A']; // orth-diag interconversion to make direction valid if(occup & mode & 0x104) // no side effects, merge legs to one move MovesFromString(board, flags, f, r, x, y, dir, cont, cb, cl); if(occup & mode & 3 && (killX < 0 || killX == x && killY == y)) { // destructive first leg @@ -376,29 +469,44 @@ MovesFromString (Board board, int flags, int f, int r, int tx, int ty, int angle legNr >>= 1; } } + *atom = origAtom; // undo any interconversion } if(occup != 4) break; // occupied squares always terminate the leg continue; } if(hop & 32+64) { if(occup != 4) { if(hop & 64 && i != 1) i = 2; hop &= 31; } continue; } // hopper - if(mode & 8 && y == board[EP_RANK] && occup == 4 && board[EP_FILE] == x) { // to e.p. square + ep = board[EP_RANK]; + if(mode & 8 && occup == 4 && board[EP_FILE] == x && (y == (ep & 127) || y - vy == ep - 128)) { // to e.p. square (or 2nd e.p. square) cb(board, flags, mine == 1 ? WhiteCapturesEnPassant : BlackCapturesEnPassant, r, f, y, x, cl); } - if(mode & 16) { // castling + if(mode & 1024) { // castling i = 2; // kludge to elongate move indefinitely if(occup == 4) continue; // skip empty squares - if(x == BOARD_LEFT && board[y][x] == initialPosition[y][x]) // reached initial corner piece + if((x == BOARD_LEFT + skip || x > BOARD_LEFT + skip && vx < 0 && board[y][x-1-skip] == DarkSquare) + && board[y][x] == initialPosition[y][x]) { // reached initial corner piece + if(pc != WhiteKing && pc != BlackKing || expo == 1) { // non-royal castling (to be entered as two-leg move via 'Rook') + if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX < f) + legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f - expo, cl), legNr >>= 1; + } else cb(board, flags, mine == 1 ? WhiteQueenSideCastle : BlackQueenSideCastle, r, f, y, f - expo, cl); - if(x == BOARD_RGHT-1 && board[y][x] == initialPosition[y][x]) + } + if((x == BOARD_RGHT-1-skip || x < BOARD_RGHT-1-skip && vx > 0 && board[y][x+1+skip] == DarkSquare) + && board[y][x] == initialPosition[y][x]) { + if(pc != WhiteKing && pc != BlackKing || expo == 1) { + if(killX < 0) cb(board, flags, FirstLeg, r, f, y, x, cl); if(killX > f) + legNr <<= 1, cb(board, flags, NormalMove, r, f, y, f + expo, cl), legNr >>= 1; + } else cb(board, flags, mine == 1 ? WhiteKingSideCastle : BlackKingSideCastle, r, f, y, f + expo, cl); + } break; } - if(occup & mode) cb(board, flags, NormalMove, r, f, y, x, cl); // allowed, generate + if(mode & 16 && (board[y][x] == WhiteKing || board[y][x] == BlackKing)) break; // tame piece, cannot capture royal + if(occup & mode) cb(board, flags, y == promoRank ? promo : NormalMove, r, f, y, x, cl); // allowed, generate if(occup != 4) break; // not valid transit square } while(--i); } - dx = dy = 1; dirSet = 0x99; // prepare for diagonal moves of K,Q - } while(retry--); // and start doing them + dx = dy; dirSet = ds2; // prepare for diagonal moves of K,Q + } while(retry-- && ds2); // and start doing them if(tx >= 0) break; // don't do other atoms in continuation legs } } // next atom @@ -636,7 +744,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, if ((flags & F_WHITE_ON_MOVE) != (board[rf][ff] < BlackPawn)) continue; // [HGM] speed: wrong color m = 0; piece = board[rf][ff]; if(PieceToChar(piece) == '~') - piece = (ChessSquare) ( DEMOTED piece ); + piece = (ChessSquare) ( DEMOTED(piece) ); if(filter != EmptySquare && piece != filter) continue; if(pieceDefs && pieceDesc[piece]) { // [HGM] gen: use engine-defined moves MovesFromString(board, flags, ff, rf, -1, -1, 0, pieceDesc[piece], callback, closure); @@ -812,14 +920,14 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, /* Gold General (and all its promoted versions) . First do the */ /* diagonal forward steps, then proceed as normal Wazir */ - case SHOGI (PROMOTED WhitePawn): + case SHOGI (PROMO WhitePawn): if(gameInfo.variant == VariantShogi) goto WhiteGold; - case SHOGI (PROMOTED BlackPawn): + case SHOGI (PROMO BlackPawn): if(gameInfo.variant == VariantShogi) goto BlackGold; SlideVertical(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED WhiteKnight): + case SHOGI (PROMO WhiteKnight): if(gameInfo.variant == VariantShogi) goto WhiteGold; case SHOGI BlackDrunk: case SHOGI BlackAlfil: @@ -828,7 +936,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, StepBackward(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED BlackKnight): + case SHOGI (PROMO BlackKnight): if(gameInfo.variant == VariantShogi) goto BlackGold; case SHOGI WhiteDrunk: case SHOGI WhiteAlfil: @@ -838,15 +946,15 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, break; - case SHOGI WhiteStag: - case SHOGI BlackStag: + case SHOGI WhiteGnu: + case SHOGI BlackGnu: if(gameInfo.variant == VariantShogi) goto BlackGold; SlideVertical(board, flags, rf, ff, callback, closure); Ferz(board, flags, rf, ff, callback, closure); StepSideways(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED WhiteQueen): + case SHOGI (PROMO WhiteQueen): case SHOGI WhiteTokin: case SHOGI WhiteWazir: WhiteGold: @@ -854,7 +962,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, Wazir(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED BlackQueen): + case SHOGI (PROMO BlackQueen): case SHOGI BlackTokin: case SHOGI BlackWazir: BlackGold: @@ -1165,9 +1273,9 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, StepVertical(board, flags, rf, ff, callback, closure); break; - case SHOGI (PROMOTED WhiteFerz): + case SHOGI (PROMO WhiteFerz): if(gameInfo.variant == VariantShogi) goto WhiteGold; - case SHOGI (PROMOTED BlackFerz): + case SHOGI (PROMO BlackFerz): if(gameInfo.variant == VariantShogi) goto BlackGold; case SHOGI WhitePSword: case SHOGI BlackPSword: @@ -1203,7 +1311,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, SlideVertical(board, flags, rf, ff, callback, closure); break; - case SHOGI WhiteHorned: + case SHOGI WhiteCat: Sting(board, flags, rf, ff, 1, 0, callback, closure); callback(board, flags, NormalMove, rf, ff, rf, ff, closure); if(killX >= 0) break; @@ -1212,7 +1320,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, SlideBackward(board, flags, rf, ff, callback, closure); break; - case SHOGI BlackHorned: + case SHOGI BlackCat: Sting(board, flags, rf, ff, -1, 0, callback, closure); callback(board, flags, NormalMove, rf, ff, rf, ff, closure); if(killX >= 0) break; @@ -1221,7 +1329,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, SlideForward(board, flags, rf, ff, callback, closure); break; - case SHOGI WhiteEagle: + case SHOGI WhiteDagger: Sting(board, flags, rf, ff, 1, 1, callback, closure); Sting(board, flags, rf, ff, 1, -1, callback, closure); callback(board, flags, NormalMove, rf, ff, rf, ff, closure); @@ -1230,7 +1338,7 @@ GenPseudoLegal (Board board, int flags, MoveCallback callback, VOIDSTAR closure, SlideDiagBackward(board, flags, rf, ff, callback, closure); break; - case SHOGI BlackEagle: + case SHOGI BlackDagger: Sting(board, flags, rf, ff, -1, 1, callback, closure); Sting(board, flags, rf, ff, -1, -1, callback, closure); callback(board, flags, NormalMove, rf, ff, rf, ff, closure); @@ -1569,14 +1677,16 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant CheckTestClosure cl; ChessSquare king = flags & F_WHITE_ON_MOVE ? WhiteKing : BlackKing; ChessSquare captured = EmptySquare, ep=0, trampled=0; + int saveKill = killX; /* Suppress warnings on uninitialized variables */ if(gameInfo.variant == VariantXiangqi) king = flags & F_WHITE_ON_MOVE ? WhiteWazir : BlackWazir; if(gameInfo.variant == VariantKnightmate) king = flags & F_WHITE_ON_MOVE ? WhiteUnicorn : BlackUnicorn; - if(gameInfo.variant == VariantChu) { // strictly speaking this is not needed, as Chu officially has no check + if(gameInfo.variant == VariantChu || gameInfo.variant == VariantShogi) { // strictly speaking this is not needed, as Chu officially has no check int r, f, k = king, royals=0, prince = flags & F_WHITE_ON_MOVE ? WhiteMonarch : BlackMonarch; + if(gameInfo.variant == VariantShogi) prince -= 11; // White/BlackFalcon for(r=0; r 1) return FALSE; // no check if we have two royals (ignores double captureby Lion!) @@ -1591,7 +1701,7 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant board[rf][ft] = EmptySquare; } else { captured = board[rt][ft]; - if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; } + if(killX >= 0) { trampled = board[killY][killX]; board[killY][killX] = EmptySquare; killX = -1; } } if(rf == DROP_RANK) board[rt][ft] = ff; else { // [HGM] drop board[rt][ft] = board[rf][ff]; @@ -1599,10 +1709,11 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant } ep = board[EP_STATUS]; if( captured == WhiteLion || captured == BlackLion ) { // [HGM] lion: Chu Lion-capture rules - ChessSquare victim = killX < 0 ? EmptySquare : trampled; + ChessSquare victim = saveKill < 0 ? EmptySquare : trampled; if( (board[rt][ft] == WhiteLion || board[rt][ft] == BlackLion) && // capturer is Lion (ff - ft > 1 || ft - ff > 1 || rf - rt > 1 || rt - rf > 1) && // captures from a distance - (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn) ) // no or worthless 'bridge' + (victim == EmptySquare || victim == WhitePawn || victim == BlackPawn // no or worthless 'bridge' + || victim == WhiteCobra || victim == BlackCobra) ) // (Pawn or Go Between) board[EP_STATUS] = EP_ROYAL_LION; // on distant Lion x Lion victim must not be pseudo-legally protected } } @@ -1639,7 +1750,7 @@ CheckTest (Board board, int flags, int rf, int ff, int rt, int ft, int enPassant board[rf][ft] = captured; board[rt][ft] = EmptySquare; } else { - if(killX >= 0) board[killY][killX] = trampled; + if(saveKill >= 0) board[killY][killX = saveKill] = trampled; board[rt][ft] = captured; } board[EP_STATUS] = ep; @@ -1715,7 +1826,7 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC if(quickFlag) flags = flags & ~1 | quickFlag & 1; // [HGM] speed: in quick mode quickFlag specifies side-to-move. if(rf == DROP_RANK) return LegalDrop(board, flags, ff, rt, ft); piece = filterPiece = board[rf][ff]; - if(PieceToChar(piece) == '~') filterPiece = DEMOTED piece; + if(PieceToChar(piece) == '~') filterPiece = DEMOTED(piece); /* [HGM] Cobra and Falcon are wildcard pieces; consider all their moves legal */ /* (perhaps we should disallow moves that obviously leave us in check?) */ @@ -1755,13 +1866,17 @@ LegalityTest (Board board, int flags, int rf, int ff, int rt, int ft, int promoC if(cl.kind != NormalMove || promoChar == NULLCHAR || promoChar == '=') return cl.kind; if(promoChar != '+') return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove; - if(PieceToChar(CHUPROMOTED board[rf][ff]) != '+') return ImpossibleMove; + if(PieceToChar(CHUPROMOTED(board[rf][ff])) != '+') { + if(PieceToChar(CHUPROMOTED (board[rf][ff] < BlackPawn ? WhitePawn : BlackPawn)) != '.') + return ImpossibleMove; + } return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion; } else if(gameInfo.variant == VariantShogi) { /* [HGM] Shogi promotions. '=' means defer */ if(rf != DROP_RANK && cl.kind == NormalMove) { ChessSquare piece = board[rf][ff]; + int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8); if(promoChar == PieceToChar(BlackQueen)) promoChar = NULLCHAR; /* [HGM] Kludge */ if(promoChar == 'd' && (piece == WhiteRook || piece == BlackRook) || @@ -1773,7 +1888,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo return CharToPiece(promoChar) == EmptySquare ? ImpossibleMove : IllegalMove; else if(flags & F_WHITE_ON_MOVE) { if( (int) piece < (int) WhiteWazir && - (rf >= BOARD_HEIGHT - BOARD_HEIGHT/3 || rt >= BOARD_HEIGHT - BOARD_HEIGHT/3) ) { + (rf >= BOARD_HEIGHT - zone || rt >= BOARD_HEIGHT - zone) ) { if( (piece == WhitePawn || piece == WhiteQueen) && rt > BOARD_HEIGHT-2 || piece == WhiteKnight && rt > BOARD_HEIGHT-3) /* promotion mandatory */ cl.kind = promoChar == '=' ? IllegalMove : WhitePromotion; @@ -1781,7 +1896,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo cl.kind = promoChar == '+' ? WhitePromotion : WhiteNonPromotion; } else cl.kind = promoChar == '+' ? IllegalMove : NormalMove; } else { - if( (int) piece < (int) BlackWazir && (rf < BOARD_HEIGHT/3 || rt < BOARD_HEIGHT/3) ) { + if( (int) piece < (int) BlackWazir && (rf < zone || rt < zone) ) { if( (piece == BlackPawn || piece == BlackQueen) && rt < 1 || piece == BlackKnight && rt < 2 ) /* promotion obligatory */ cl.kind = promoChar == '=' ? IllegalMove : BlackPromotion; @@ -1798,7 +1913,7 @@ if(appData.debugMode)fprintf(debugFP,"SHOGI promoChar = %c\n", promoChar ? promo // should test if in zone, really if(gameInfo.variant == VariantChuChess && (piece == WhiteKnight || piece == BlackKnight) && HasLion(board, flags)) return IllegalMove; - if(PieceToChar(PROMOTED piece) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion; + if(PieceToChar(PROMOTED(piece)) == '+') return flags & F_WHITE_ON_MOVE ? WhitePromotion : BlackPromotion; } else if(promoChar == '=') cl.kind = IllegalMove; else // [HGM] shogi: no deferred promotion outside Shogi if (cl.kind == WhitePromotion || cl.kind == BlackPromotion) { @@ -1911,6 +2026,7 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in { register DisambiguateClosure *cl = (DisambiguateClosure *) closure; int wildCard = FALSE; ChessSquare piece = board[rf][ff]; + extern int kifu; // in parser.c // [HGM] wild: for wild-card pieces rt and rf are dummies if(piece == WhiteFalcon || piece == BlackFalcon || @@ -1919,7 +2035,7 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in if ((cl->pieceIn == EmptySquare || cl->pieceIn == board[rf][ff] || PieceToChar(board[rf][ff]) == '~' - && cl->pieceIn == (ChessSquare)(DEMOTED board[rf][ff]) + && cl->pieceIn == (ChessSquare)(DEMOTED(board[rf][ff])) ) && (cl->rfIn == -1 || cl->rfIn == rf) && (cl->ffIn == -1 || cl->ffIn == ff) && @@ -1928,6 +2044,18 @@ DisambiguateCallback (Board board, int flags, ChessMove kind, int rf, int ff, in if(cl->count && rf == cl->rf && ff == cl->ff) return; // duplicate move + if(cl->count == 1 && kifu & 0x7E && cl->rfIn == -1 && cl->ffIn == -1) { // traditional Shogi disambiguation required + int this = 1, other = 1; + if(kifu & 2) this &= (flags & 1 ? rt > rf : rt < rf), other &= (flags & 1 ? cl->rt > cl->rf : cl->rt < cl->rf); + if(kifu & 4) this &= (flags & 1 ? rt < rf : rt > rf), other &= (flags & 1 ? cl->rt < cl->rf : cl->rt > cl->rf); + if(kifu & 8) this &= (rf == rt), other &= (cl->rt == cl->rf); + if(kifu & 0x10) this &= (flags & 1 ? ft <= ff : ft >= ff), other &= (flags & 1 ? cl->ft <= cl->ff : cl->ft >= cl->ff); + if(kifu & 0x20) this &= (flags & 1 ? ft >= ff : ft <= ff), other &= (flags & 1 ? cl->ft >= cl->ff : cl->ft <= cl->ff); + if(kifu & 0x40) this &= (ft == ff), other &= (cl->ft == cl->ff); // should never be used + if(!other) cl->count--; // the old move did not satisfy the requested relative position, erase it + if(!this) return; // the current move does not satisfy the requested relative position, ignore it + } + cl->count++; if(cl->count == 1 || board[rt][ft] != EmptySquare) { // [HGM] oneclick: if multiple moves, be sure we remember capture @@ -1969,7 +2097,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) GenLegal(board, flags|F_IGNORE_CHECK, DisambiguateCallback, (VOIDSTAR) closure, closure->pieceIn); if (closure->count == 0) { /* No, it's not even that */ - if(!appData.testLegality && closure->pieceIn != EmptySquare) { + if(!appData.testLegality && !pieceDefs && closure->pieceIn != EmptySquare) { int f, r; // if there is only a single piece of the requested type on the board, use that closure->rt = closure->rtIn, closure->ft = closure->ftIn; for(r=0; rrfIn != DROP_RANK && closure->kind == NormalMove) { ChessSquare piece = closure->piece; + int zone = BOARD_HEIGHT/3 + (BOARD_HEIGHT == 8); if (c == 'd' && (piece == WhiteRook || piece == BlackRook) || c == 'h' && (piece == WhiteBishop || piece == BlackBishop) || c == 'g' && (piece <= WhiteFerz || piece <= BlackFerz && piece >= BlackPawn) ) @@ -2025,7 +2154,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) if(c != NULLCHAR && c != '+' && c != '=') closure->kind = IllegalMove; // otherwise specifying a piece is illegal else if(flags & F_WHITE_ON_MOVE) { if( (int) piece < (int) WhiteWazir && - (closure->rf >= BOARD_HEIGHT-(BOARD_HEIGHT/3) || closure->rt >= BOARD_HEIGHT-(BOARD_HEIGHT/3)) ) { + (closure->rf >= BOARD_HEIGHT-zone || closure->rt >= BOARD_HEIGHT-zone) ) { if( (piece == WhitePawn || piece == WhiteQueen) && closure->rt > BOARD_HEIGHT-2 || piece == WhiteKnight && closure->rt > BOARD_HEIGHT-3) /* promotion mandatory */ closure->kind = c == '=' ? IllegalMove : WhitePromotion; @@ -2033,7 +2162,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) closure->kind = c == '+' ? WhitePromotion : WhiteNonPromotion; } else closure->kind = c == '+' ? IllegalMove : NormalMove; } else { - if( (int) piece < (int) BlackWazir && (closure->rf < BOARD_HEIGHT/3 || closure->rt < BOARD_HEIGHT/3) ) { + if( (int) piece < (int) BlackWazir && (closure->rf < zone || closure->rt < zone) ) { if( (piece == BlackPawn || piece == BlackQueen) && closure->rt < 1 || piece == BlackKnight && closure->rt < 2 ) /* promotion obligatory */ closure->kind = c == '=' ? IllegalMove : BlackPromotion; @@ -2048,8 +2177,10 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) if (closure->kind == WhitePromotion || closure->kind == BlackPromotion) { if(c == NULLCHAR) { // missing promoChar on mandatory promotion; use default for variant if(gameInfo.variant == VariantShatranj || gameInfo.variant == VariantCourier || - gameInfo.variant == VariantMakruk || gameInfo.variant == VariantASEAN) + gameInfo.variant == VariantMakruk) c = PieceToChar(BlackFerz); + else if(gameInfo.variant == VariantASEAN) + c = PieceToChar(BlackRook); else if(gameInfo.variant == VariantGreat) c = PieceToChar(BlackMan); else if(gameInfo.variant == VariantGrand) @@ -2060,7 +2191,7 @@ Disambiguate (Board board, int flags, DisambiguateClosure *closure) else if(c == 'l' && gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove; } else if (c == '+') { // '+' outside shogi, check if pieceToCharTable enabled it ChessSquare p = closure->piece; - if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED p) != '+') + if(p > WhiteMan && p < BlackPawn || p > BlackMan || PieceToChar(PROMOTED(p)) != '+') closure->kind = ImpossibleMove; // used on non-promotable piece else if(gameInfo.variant == VariantChuChess && HasLion(board, flags)) closure->kind = IllegalMove; } else if (c != NULLCHAR) closure->kind = IllegalMove; @@ -2105,7 +2236,7 @@ CoordsToAlgebraicCallback (Board board, int flags, ChessMove kind, int rf, int f if ((rt == cl->rt && ft == cl->ft || rt == rf && ft == ff) && // [HGM] null move matches any toSquare (board[rf][ff] == cl->piece || PieceToChar(board[rf][ff]) == '~' && - (ChessSquare) (DEMOTED board[rf][ff]) == cl->piece) + (ChessSquare) (DEMOTED(board[rf][ff])) == cl->piece) ) { if (rf == cl->rf) { if (ff == cl->ff) { @@ -2149,7 +2280,7 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p if (promoChar == 'x') promoChar = NULLCHAR; piece = board[rf][ff]; - if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED piece); + if(PieceToChar(piece)=='~') piece = (ChessSquare)(DEMOTED(piece)); switch (piece) { case WhitePawn: @@ -2241,13 +2372,13 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p cl.kind = IllegalMove; cl.rank = cl.file = cl.either = 0; c = PieceToChar(piece) ; - GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); // [HGM] speed + GenLegal(board, flags, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); // [HGM] speed if (cl.kind == IllegalMove && !(flags&F_IGNORE_CHECK)) { /* Generate pretty moves for moving into check, but still return IllegalMove. */ - GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED piece)); + GenLegal(board, flags|F_IGNORE_CHECK, CoordsToAlgebraicCallback, (VOIDSTAR) &cl, c!='~' ? piece : (DEMOTED(piece))); if (cl.kind == IllegalMove) break; cl.kind = IllegalMove; } @@ -2259,10 +2390,11 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p */ if( c == '~' || c == '+') { /* [HGM] print nonexistent piece as its demoted version */ - piece = (ChessSquare) (DEMOTED piece - 11*(gameInfo.variant == VariantChu)); + piece = (ChessSquare) (CHUDEMOTED(piece)); } if(c=='+') *outp++ = c; *outp++ = ToUpper(PieceToChar(piece)); + if(*outp = PieceSuffix(piece)) outp++; if (cl.file || (cl.either && !cl.rank)) { *outp++ = ff + AAA; @@ -2316,7 +2448,10 @@ CoordsToAlgebraic (Board board, int flags, int rf, int ff, int rt, int ft, int p int r, f; for(r=0; r