MOVE AlgebToMove(const char *str_mov) { MOVE curr_move = 0; char square = 0; /*Pieced promoted to:*/ square = CharToPiece(str_mov[4]); curr_move += square; curr_move = curr_move << 8; /*DEST:*/ square = CharToCoordinate(str_mov[3]) + CharToCoordinate(str_mov[2]); curr_move += square; curr_move = curr_move << 8; /*ORIG:*/ square = CharToCoordinate(str_mov[1]) + CharToCoordinate(str_mov[0]); curr_move += square; return curr_move; }
int NextUnit (char **p) { // Main parser routine int coord[4], n, result, piece, i; char type[4], promoted, separator, slash, *oldp, *commentEnd, c; int wom = quickFlag ? quickFlag&1 : WhiteOnMove(yyboardindex); // ********* try white first, because it is so common ************************** if(**p == ' ' || **p == '\n' || **p == '\t') { parseStart = (*p)++; return Nothing; } if(**p == NULLCHAR) { // make sure there is something to parse if(fromString) return 0; // we are parsing string, so the end is really the end *p = inPtr = inputBuf; if(!ReadLine()) return 0; // EOF } else if(inPtr > inputBuf + PARSEBUFSIZE/2) { // buffer fills up with already parsed stuff char *q = *p, *r = inputBuf; while(*r++ = *q++); *p = inputBuf; inPtr = r - 1; } parseStart = oldp = *p; // remember where we begin // ********* attempt to recognize a SAN move in the leading non-blank text ***** piece = separator = promoted = slash = n = 0; for(i=0; i<4; i++) coord[i] = -1, type[i] = NOTHING; if(**p == '+') (*p)++, promoted++; if(**p >= 'a' && **p <= 'z' && (*p)[1]== '@') piece =*(*p)++ + 'A' - 'a'; else if(**p >= 'A' && **p <= 'Z') { piece = *(*p)++; // Note we could test for 2-byte non-ascii names here if(**p == '/') slash = *(*p)++; } while(n < 4) { if(**p >= 'a' && **p < 'x') coord[n] = *(*p)++ - 'a', type[n++] = ALPHABETIC; else if((i = Number(p)) != BADNUMBER) coord[n] = i, type[n++] = NUMERIC; else break; if(n == 2 && type[0] == type[1]) { // if two identical types, the opposite type in between must have been missing type[2] = type[1]; coord[2] = coord[1]; type[1] = NOTHING; coord[1] = -1; n++; } } // we always get here, and might have read a +, a piece, and upto 4 potential coordinates if(n <= 2) { // could be from-square or disambiguator, when -:xX follow, or drop with @ directly after piece, but also to-square if(**p == '-' || **p == ':' || **p == 'x' || **p == 'X' || // these cannot be move suffix, so to-square must follow (**p == '@' || **p == '*') && n == 0 && !promoted && piece) { // P@ must also be followed by to-square separator = *(*p)++; if(n == 1) coord[1] = coord[0]; // must be disambiguator, but we do not know which yet n = 2; while(n < 4) { // attempt to read to-square if(**p >= 'a' && **p < 'x') coord[n] = *(*p)++ - 'a', type[n++] = ALPHABETIC; else if((i = Number(p)) != BADNUMBER) coord[n] = i, type[n++] = NUMERIC; else break; } } else if((**p == '+' || **p == '=') && n == 1 && piece && type[0] == NUMERIC) { // can be traditional Xiangqi notation separator = *(*p)++; n = 2; if((i = Number(p)) != BADNUMBER) coord[n] = i, type[n++] = NUMERIC; } else if(n == 2) { // only one square mentioned, must be to-square while(n < 4) { coord[n] = coord[n-2], type[n] = type[n-2], coord[n-2] = -1, type[n-2] = NOTHING; n++; } } } else if(n == 3 && type[1] != NOTHING) { // must be hyphenless disambiguator + to-square for(i=3; i>0; i--) coord[i] = coord[i-1], type[i] = type[i-1]; // move to-square to where it belongs type[1] = NOTHING; // disambiguator goes in first two positions n = 4; } // we always get here; move must be completely read now, with to-square coord(s) at end if(n == 3) { // incomplete to-square. Could be Xiangqi traditional, or stuff like fxg if(piece && type[1] == NOTHING && type[0] == NUMERIC && type[2] == NUMERIC && (separator == '+' || separator == '=' || separator == '-')) { // Xiangqi traditional return ImpossibleMove; // for now treat as invalid } // fxg stuff, but also things like 0-0, 0-1 and 1-0 if(!piece && type[1] == NOTHING && type[0] == ALPHABETIC && type[2] == ALPHABETIC && (coord[0] != 14 || coord[2] != 14) /* reserve oo for castling! */ ) { piece = 'P'; n = 4; // kludge alert: fake full to-square } } else if(n == 1 && type[0] == NUMERIC && coord[0] > 1) { while(**p == '.') (*p)++; return Nothing; } // fast exit for move numbers if(n == 4 && type[2] != type[3] && // we have a valid to-square (kludge: type[3] can be NOTHING on fxg type move) (piece || !promoted) && // promoted indicator only valid on named piece type (type[2] == ALPHABETIC || gameInfo.variant == VariantShogi)) { // in Shogi also allow alphabetic rank DisambiguateClosure cl; int fromX, fromY, toX, toY; if(slash && (!piece || type[1] == NOTHING)) goto badMove; // slash after piece only in ICS long format if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ if(type[2] == NUMERIC) { // alpha-rank coord[2] = BOARD_RGHT - BOARD_LEFT - coord[2]; coord[3] = BOARD_HEIGHT - coord[3]; if(coord[0] >= 0) coord[0] = BOARD_RGHT - BOARD_LEFT - coord[0]; if(coord[1] >= 0) coord[1] = BOARD_HEIGHT - coord[1]; } toX = cl.ftIn = (currentMoveString[2] = coord[2] + 'a') - AAA; toY = cl.rtIn = (currentMoveString[3] = coord[3] + '0') - ONE; if(type[3] == NOTHING) cl.rtIn = -1; // for fxg type moves ask for toY disambiguation else if(toY >= BOARD_HEIGHT || toY < 0) return ImpossibleMove; // vert off-board to-square if(toX < BOARD_LEFT || toX >= BOARD_RGHT) return ImpossibleMove; if(piece) { cl.pieceIn = CharToPiece(wom ? piece : ToLower(piece)); if(cl.pieceIn == EmptySquare) return ImpossibleMove; // non-existent piece if(promoted) cl.pieceIn = (ChessSquare) (PROMOTED cl.pieceIn); } else cl.pieceIn = EmptySquare; if(separator == '@' || separator == '*') { // drop move. We only get here without from-square or promoted piece fromY = DROP_RANK; fromX = cl.pieceIn; currentMoveString[0] = piece; currentMoveString[1] = '@'; return LegalityTest(boards[yyboardindex], PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, fromY, fromX, toY, toX, NULLCHAR); } if(type[1] == NOTHING && type[0] != NOTHING) { // there is a disambiguator if(type[0] != type[2]) coord[0] = -1, type[1] = type[0], type[0] = NOTHING; // it was a rank-disambiguator } if( type[1] != type[2] && // means fromY is of opposite type as ToX, or NOTHING (type[0] == NOTHING || type[0] == type[2]) ) { // well formed fromX = (currentMoveString[0] = coord[0] + 'a') - AAA; fromY = (currentMoveString[1] = coord[1] + '0') - ONE; currentMoveString[4] = cl.promoCharIn = PromoSuffix(p); currentMoveString[5] = NULLCHAR; if(type[0] != NOTHING && type[1] != NOTHING && type[3] != NOTHING) { // fully specified. // Note that Disambiguate does not work for illegal moves, but flags them as impossible if(piece) { // check if correct piece indicated ChessSquare realPiece = boards[yyboardindex][fromY][fromX]; if(PieceToChar(realPiece) == '~') realPiece = (ChessSquare) (DEMOTED realPiece); if(!(appData.icsActive && PieceToChar(realPiece) == '+') && // trust ICS if it moves promoted pieces piece && realPiece != cl.pieceIn) return ImpossibleMove; } result = LegalityTest(boards[yyboardindex], PosFlags(yyboardindex), fromY, fromX, toY, toX, cl.promoCharIn); if (currentMoveString[4] == NULLCHAR) { // suppy missing mandatory promotion character if(result == WhitePromotion || result == BlackPromotion) { switch(gameInfo.variant) { case VariantCourier: case VariantShatranj: currentMoveString[4] = PieceToChar(BlackFerz); break; case VariantGreat: currentMoveString[4] = PieceToChar(BlackMan); break; case VariantShogi: currentMoveString[4] = '+'; break; default: currentMoveString[4] = PieceToChar(BlackQueen); } } else if(result == WhiteNonPromotion || result == BlackNonPromotion) { currentMoveString[4] = '='; } } else if(appData.testLegality && gameInfo.variant != VariantSChess && // strip off unnecessary and false promo characters !(result == WhitePromotion || result == BlackPromotion || result == WhiteNonPromotion || result == BlackNonPromotion)) currentMoveString[4] = NULLCHAR; return result; } else if(cl.pieceIn == EmptySquare) cl.pieceIn = wom ? WhitePawn : BlackPawn; cl.ffIn = type[0] == NOTHING ? -1 : coord[0] + 'a' - AAA; cl.rfIn = type[1] == NOTHING ? -1 : coord[1] + '0' - ONE; Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl); if(cl.kind == ImpossibleMove && !piece && type[1] == NOTHING // fxg5 type && toY == (wom ? 4 : 3)) { // could be improperly written e.p. cl.rtIn += wom ? 1 : -1; // shift target square to e.p. square Disambiguate(boards[yyboardindex], PosFlags(yyboardindex), &cl); if((cl.kind != WhiteCapturesEnPassant && cl.kind != BlackCapturesEnPassant)) return ImpossibleMove; // nice try, but no cigar } currentMoveString[0] = cl.ff + AAA; currentMoveString[1] = cl.rf + ONE; currentMoveString[3] = cl.rt + ONE; currentMoveString[4] = cl.promoChar; if((cl.kind == WhiteCapturesEnPassant || cl.kind == BlackCapturesEnPassant) && (Match("ep", p) || Match("e.p.", p))); return (int) cl.kind; } } badMove:// we failed to find algebraic move *p = oldp; // Next we do some common symbols where the first character commits us to things that cannot possibly be a move // ********* PGN tags ****************************************** if(**p == '[') { oldp = ++(*p); if(Match("--", p)) { // "[--" could be start of position diagram if(!Scan(']', p) && (*p)[-3] == '-' && (*p)[-2] == '-') return PositionDiagram; *p = oldp; } SkipWhite(p); if(isdigit(**p) || isalpha(**p)) { do (*p)++; while(isdigit(**p) || isalpha(**p) || **p == '+' || **p == '-' || **p == '=' || **p == '_' || **p == '#'); SkipWhite(p); if(**p == '"') { (*p)++; while(**p != '\n' && (*(*p)++ != '"'|| (*p)[-2] == '\\')); // look for unescaped quote if((*p)[-1] !='"') { *p = oldp; Scan(']', p); return Comment; } // string closing delimiter missing SkipWhite(p); if(*(*p)++ == ']') return PGNTag; } } Scan(']', p); return Comment; } // ********* SAN Castings ************************************* if(**p == 'O' || **p == 'o' || **p == '0' && !Match("00:", p)) { // exclude 00 in time stamps int castlingType = 0; if(Match("O-O-O", p) || Match("o-o-o", p) || Match("0-0-0", p) || Match("OOO", p) || Match("ooo", p) || Match("000", p)) castlingType = 2; else if(Match("O-O", p) || Match("o-o", p) || Match("0-0", p) || Match("OO", p) || Match("oo", p) || Match("00", p)) castlingType = 1; if(castlingType) { //code from old parser, collapsed for both castling types, and streamlined a bit int rf, ff, rt, ft; ChessSquare king; char promo=NULLCHAR; if(gameInfo.variant == VariantSChess) promo = PromoSuffix(p); if (yyskipmoves) return (int) AmbiguousMove; /* not disambiguated */ if (wom) { rf = 0; rt = 0; king = WhiteKing; } else { rf = BOARD_HEIGHT-1; rt = BOARD_HEIGHT-1; king = BlackKing; } ff = (BOARD_WIDTH-1)>>1; // this would be d-file if (boards[yyboardindex][rf][ff] == king) { /* ICS wild castling */ ft = castlingType == 1 ? BOARD_LEFT+1 : (gameInfo.variant == VariantJanus ? BOARD_RGHT-2 : BOARD_RGHT-3); } else { ff = BOARD_WIDTH>>1; // e-file ft = castlingType == 1 ? BOARD_RGHT-2 : BOARD_LEFT+2; } if(PosFlags(0) & F_FRC_TYPE_CASTLING) { if (wom) { ff = initialRights[2]; ft = initialRights[castlingType-1]; } else { ff = initialRights[5]; ft = initialRights[castlingType+2]; } if (appData.debugMode) fprintf(debugFP, "Parser FRC (type=%d) %d %d\n", castlingType, ff, ft); if(ff == NoRights || ft == NoRights) return ImpossibleMove; } sprintf(currentMoveString, "%c%c%c%c%c",ff+AAA,rf+ONE,ft+AAA,rt+ONE,promo); if (appData.debugMode) fprintf(debugFP, "(%d-type) castling %d %d\n", castlingType, ff, ft); return (int) LegalityTest(boards[yyboardindex], PosFlags(yyboardindex)&~F_MANDATORY_CAPTURE, // [HGM] losers: e.p.! rf, ff, rt, ft, promo); } }
int ReadFEN(const char *sFEN, BOARD *board) { /*TODO: check FEN validity.*/ int fen_pos = 0; SQUARE square = 0x70; board->wk_castle = 0; board->bk_castle = 0; board->wq_castle = 0; board->bq_castle = 0; board->w_castled = 0; board->b_castled = 0; for (char on_board = 1; on_board; fen_pos++) { char empty = 0; switch (sFEN[fen_pos]) { case ' ': on_board = 0; break; case '/': square -= 0x18; break; case '1': empty = 1; break; case '2': empty = 2; break; case '3': empty = 3; break; case '4': empty = 4; break; case '5': empty = 5; break; case '6': empty = 6; break; case '7': empty = 7; break; case '8': empty = 8; break; default: board->squares[square] = CharToPiece(sFEN[fen_pos]); if (board->squares[square] == W_KING) board->wking_pos = square; else if (board->squares[square] == B_KING) board->bking_pos = square; square++; break; } for (int i = 0; i < empty; i++) board->squares[square+i] = EMPTY; square += empty; } board->white_to_move = (sFEN[fen_pos] == 'w'); fen_pos+=2; for (char on_board = 1; on_board; fen_pos++) { /*Castle rights loop.*/ switch (sFEN[fen_pos]) { case 'K': board->wk_castle = 1; break; case 'k': board->bk_castle = 1; break; case 'Q': board->wq_castle = 1; break; case 'q': board->bq_castle = 1; break; case '-': break; default: on_board = 0; break; } } /*Store En Passant coordinates. TODO: check.*/ board->en_passant = 0x00; for (char on_board = 1; on_board; fen_pos++) { switch (sFEN[fen_pos]) { case ' ': on_board = 0; break; default: board->en_passant += CharToCoordinate(sFEN[fen_pos]); break; } } /*halfmoves and moves.*/ int rev = atoi(&sFEN[fen_pos]); while (sFEN[++fen_pos] != ' '); board->ply = 2*atoi(&sFEN[fen_pos]) - board->white_to_move - 1; board->rev_plies[board->ply] = rev; InitZobrist(board); board->zobrist_history[board->ply] = board->zobrist_key; InitMaterial(board); return fen_pos; }