X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsan.cpp;h=0429830c37c7baa6fca2429511fda84749fbf5a7;hp=f20060e84f0dcb3ac900df3c52a44ab32228614f;hb=e1ed67aacbe7fb4b462b9141d3137bed0a3ea70b;hpb=08265aef81d63b1ccf22e1e1818d0edb64e0528e diff --git a/src/san.cpp b/src/san.cpp index f20060e8..0429830c 100644 --- a/src/san.cpp +++ b/src/san.cpp @@ -1,7 +1,7 @@ /* Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Copyright (C) 2004-2008 Tord Romstad (Glaurung author) - Copyright (C) 2008 Marco Costalba + Copyright (C) 2008-2009 Marco Costalba Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ #include "movepick.h" #include "san.h" -extern SearchStack EmptySearchStack; +using std::string; //// //// Local definitions @@ -52,8 +52,8 @@ namespace { /// Functions Ambiguity move_ambiguity(const Position& pos, Move m); - const std::string time_string(int milliseconds); - const std::string score_string(Value v); + const string time_string(int milliseconds); + const string score_string(Value v); } @@ -62,79 +62,71 @@ namespace { //// /// move_to_san() takes a position and a move as input, where it is assumed -/// that the move is a legal move from the position. The return value is +/// that the move is a legal move from the position. The return value is /// a string containing the move in short algebraic notation. -const std::string move_to_san(const Position& pos, Move m) { +const string move_to_san(const Position& pos, Move m) { assert(pos.is_ok()); assert(move_is_ok(m)); - std::string san = ""; + Square from, to; + PieceType pt; + + from = move_from(m); + to = move_to(m); + pt = type_of_piece(pos.piece_on(move_from(m))); + + string san = ""; if (m == MOVE_NONE) return "(none)"; else if (m == MOVE_NULL) return "(null)"; - else if (move_is_long_castle(m)) + else if (move_is_long_castle(m) || (int(to - from) == -2 && pt == KING)) san = "O-O-O"; - else if (move_is_short_castle(m)) + else if (move_is_short_castle(m) || (int(to - from) == 2 && pt == KING)) san = "O-O"; else { - Square from = move_from(m); - Square to = move_to(m); - Piece pc = pos.piece_on(move_from(m)); - - if (type_of_piece(pc) == PAWN) + if (pt != PAWN) { - if (pos.move_is_capture(m)) - san += file_to_char(square_file(move_from(m))); - } - else - { - san += piece_type_to_char(type_of_piece(pc), true); - + san += piece_type_to_char(pt, true); switch (move_ambiguity(pos, m)) { - case AMBIGUITY_NONE: break; - case AMBIGUITY_FILE: san += file_to_char(square_file(from)); break; - case AMBIGUITY_RANK: san += rank_to_char(square_rank(from)); break; - case AMBIGUITY_BOTH: san += square_to_string(from); break; - default: assert(false); } } - if (pos.move_is_capture(m)) + { + if (pt == PAWN) + san += file_to_char(square_file(move_from(m))); san += "x"; - + } san += square_to_string(move_to(m)); - if (move_promotion(m)) { san += '='; san += piece_type_to_char(move_promotion(m), true); } } - // Is the move check? We don't use pos.move_is_check(m) here, because // Position::move_is_check doesn't detect all checks (not castling moves, // promotions and en passant captures). - UndoInfo u; + StateInfo st; Position p(pos); - p.do_move(m, u); + p.do_move(m, st); if (p.is_check()) san += p.is_mate()? "#" : "+"; @@ -143,18 +135,18 @@ const std::string move_to_san(const Position& pos, Move m) { /// move_from_san() takes a position and a string as input, and tries to -/// interpret the string as a move in short algebraic notation. On success, +/// interpret the string as a move in short algebraic notation. On success, /// the move is returned. On failure (i.e. if the string is unparsable, or /// if the move is illegal or ambiguous), MOVE_NONE is returned. -Move move_from_san(const Position& pos, const std::string& movestr) { +Move move_from_san(const Position& pos, const string& movestr) { assert(pos.is_ok()); - MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly); + MovePicker mp = MovePicker(pos, false, MOVE_NONE, OnePly); // Castling moves - if (movestr == "O-O-O") + if (movestr == "O-O-O" || movestr == "O-O-O+") { Move m; while ((m = mp.get_next_move()) != MOVE_NONE) @@ -163,104 +155,134 @@ Move move_from_san(const Position& pos, const std::string& movestr) { return MOVE_NONE; } - else if (movestr == "O-O") + else if (movestr == "O-O" || movestr == "O-O+") { Move m; while ((m = mp.get_next_move()) != MOVE_NONE) - if(move_is_short_castle(m) && pos.pl_move_is_legal(m)) + if (move_is_short_castle(m) && pos.pl_move_is_legal(m)) return m; return MOVE_NONE; } - // Normal moves - const char *cstr = movestr.c_str(); - const char *c; - char *cc; - char str[10]; - int i; - - // Initialize str[] by making a copy of movestr with the characters - // 'x', '=', '+' and '#' removed. - cc = str; - for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++) - if(!strchr("x=+#", *c)) { - *cc = strchr("nrq", *c)? toupper(*c) : *c; - cc++; - } - *cc = '\0'; - - size_t left = 0, right = strlen(str) - 1; - PieceType pt = NO_PIECE_TYPE, promotion; + // Normal moves. We use a simple FSM to parse the san string. + enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END }; + static const string pieceLetters = "KQRBN"; + PieceType pt = NO_PIECE_TYPE, promotion = NO_PIECE_TYPE; + File fromFile = FILE_NONE, toFile = FILE_NONE; + Rank fromRank = RANK_NONE, toRank = RANK_NONE; Square to; - File fromFile = FILE_NONE; - Rank fromRank = RANK_NONE; + int state = START; - // Promotion? - if(strchr("BNRQ", str[right])) { - promotion = piece_type_from_char(str[right]); - right--; - } - else - promotion = NO_PIECE_TYPE; - - // Find the moving piece: - if(left < right) { - if(strchr("BNRQK", str[left])) { - pt = piece_type_from_char(str[left]); - left++; - } - else - pt = PAWN; + for (size_t i = 0; i < movestr.length(); i++) + { + char type, c = movestr[i]; + if (pieceLetters.find(c) != string::npos) + type = 'P'; + else if (c >= 'a' && c <= 'h') + type = 'F'; + else if (c >= '1' && c <= '8') + type = 'R'; + else + type = c; + + switch (type) { + case 'P': + if (state == START) + { + pt = piece_type_from_char(c); + state = TO_FILE; + } + else if (state == PROMOTION) + { + promotion = piece_type_from_char(c); + state = (i < movestr.length() - 1) ? CHECK : END; + } + else + return MOVE_NONE; + break; + case 'F': + if (state == START) + { + pt = PAWN; + fromFile = toFile = file_from_char(c); + state = TO_RANK; + } + else if (state == TO_FILE) + { + toFile = file_from_char(c); + state = TO_RANK; + } + else if (state == TO_RANK && toFile != FILE_NONE) + { + // Previous file was for disambiguation + fromFile = toFile; + toFile = file_from_char(c); + } + else + return MOVE_NONE; + break; + case 'R': + if (state == TO_RANK) + { + toRank = rank_from_char(c); + state = (i < movestr.length() - 1) ? PROMOTION_OR_CHECK : END; + } + else if (state == TO_FILE && fromRank == RANK_NONE) + { + // It's a disambiguation rank instead of a file + fromRank = rank_from_char(c); + } + else + return MOVE_NONE; + break; + case 'x': case 'X': + if (state == TO_RANK) + { + // Previous file was for disambiguation, or it's a pawn capture + fromFile = toFile; + state = TO_FILE; + } + else if (state != TO_FILE) + return MOVE_NONE; + break; + case '=': + if (state == PROMOTION_OR_CHECK) + state = PROMOTION; + else + return MOVE_NONE; + break; + case '+': case '#': + if (state == PROMOTION_OR_CHECK || state == CHECK) + state = END; + else + return MOVE_NONE; + break; + default: + return MOVE_NONE; + break; + } } - // Find the to square: - if(left < right) { - if(str[right] < '1' || str[right] > '8' || - str[right-1] < 'a' || str[right-1] > 'h') + if (state != END) return MOVE_NONE; - to = make_square(file_from_char(str[right-1]), rank_from_char(str[right])); - right -= 2; - } - else - return MOVE_NONE; - - // Find the file and/or rank of the from square: - if(left <= right) { - if(strchr("abcdefgh", str[left])) { - fromFile = file_from_char(str[left]); - left++; - } - if(strchr("12345678", str[left])) - fromRank = rank_from_char(str[left]); - } - // Look for a matching move: + // Look for a matching move Move m, move = MOVE_NONE; + to = make_square(toFile, toRank); int matches = 0; - while((m = mp.get_next_move()) != MOVE_NONE) { - bool match = true; - if(pos.type_of_piece_on(move_from(m)) != pt) - match = false; - else if(move_to(m) != to) - match = false; - else if(move_promotion(m) != promotion) - match = false; - else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m))) - match = false; - else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m))) - match = false; - if(match) { - move = m; - matches++; - } - } - - if(matches == 1) - return move; - else - return MOVE_NONE; + while ((m = mp.get_next_move()) != MOVE_NONE) + if ( pos.type_of_piece_on(move_from(m)) == pt + && move_to(m) == to + && move_promotion(m) == promotion + && (fromFile == FILE_NONE || fromFile == square_file(move_from(m))) + && (fromRank == RANK_NONE || fromRank == square_rank(move_from(m)))) + { + move = m; + matches++; + } + return (matches == 1 ? move : MOVE_NONE); } @@ -271,34 +293,31 @@ Move move_from_san(const Position& pos, const std::string& movestr) { /// length of 80 characters. After a line break, 'startColumn' spaces are /// inserted at the beginning of the new line. -const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) { +const string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) { - Position p = Position(pos); - UndoInfo u; + StateInfo st; std::stringstream s; - std::string moveStr; - size_t length, maxLength; - - length = 0; - maxLength = 80 - startColumn; - - for(int i = 0; line[i] != MOVE_NONE; i++) { - moveStr = move_to_san(p, line[i]); - length += moveStr.length() + 1; - if(breakLines && length > maxLength) { - s << "\n"; - for(int j = 0; j < startColumn; j++) - s << " "; - length = moveStr.length() + 1; - } - s << moveStr << " "; + string moveStr; + size_t length = 0; + size_t maxLength = 80 - startColumn; + Position p(pos); - if(line[i] == MOVE_NULL) - p.do_null_move(u); - else - p.do_move(line[i], u); - } + for (int i = 0; line[i] != MOVE_NONE; i++) + { + moveStr = move_to_san(p, line[i]); + length += moveStr.length() + 1; + if (breakLines && length > maxLength) + { + s << '\n' << std::setw(startColumn) << ' '; + length = moveStr.length() + 1; + } + s << moveStr << ' '; + if (line[i] == MOVE_NULL) + p.do_null_move(st); + else + p.do_move(line[i], st); + } return s.str(); } @@ -307,26 +326,26 @@ const std::string line_to_san(const Position& pos, Move line[], int startColumn, /// It is used to write search information to the log file (which is created /// when the UCI parameter "Use Search Log" is "true"). -const std::string pretty_pv(const Position& pos, int time, int depth, - uint64_t nodes, Value score, Move pv[]) { +const string pretty_pv(const Position& pos, int time, int depth, + uint64_t nodes, Value score, Move pv[]) { std::stringstream s; // Depth - s << std::setw(2) << std::setfill(' ') << depth << " "; + s << std::setw(2) << depth << " "; // Score s << std::setw(8) << score_string(score); // Time - s << std::setw(8) << std::setfill(' ') << time_string(time) << " "; + s << std::setw(8) << time_string(time) << " "; // Nodes - if(nodes < 1000000ULL) - s << std::setw(8) << std::setfill(' ') << nodes << " "; - else if(nodes < 1000000000ULL) - s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " "; + if (nodes < 1000000ULL) + s << std::setw(8) << nodes << " "; + else if (nodes < 1000000000ULL) + s << std::setw(7) << nodes/1000ULL << 'k' << " "; else - s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " "; + s << std::setw(7) << nodes/1000000ULL << 'M' << " "; // PV s << line_to_san(pos, pv, 30, true); @@ -348,7 +367,7 @@ namespace { if (type_of_piece(pc) == KING) return AMBIGUITY_NONE; - MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly); + MovePicker mp = MovePicker(pos, false, MOVE_NONE, OnePly); Move mv, moveList[8]; int n = 0; @@ -378,38 +397,39 @@ namespace { } - const std::string time_string(int milliseconds) { + const string time_string(int milliseconds) { + std::stringstream s; + s << std::setfill('0'); - int hours = milliseconds / (1000 * 60 * 60); - int minutes = (milliseconds - hours*1000*60*60) / (60*1000); - int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000; + int hours = milliseconds / (1000*60*60); + int minutes = (milliseconds - hours*1000*60*60) / (1000*60); + int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000; - if(hours) - s << hours << ':'; - s << std::setw(2) << std::setfill('0') << minutes << ':'; - s << std::setw(2) << std::setfill('0') << seconds; + if (hours) + s << hours << ':'; + s << std::setw(2) << minutes << ':' << std::setw(2) << seconds; return s.str(); } - const std::string score_string(Value v) { + const string score_string(Value v) { + std::stringstream s; - if(abs(v) >= VALUE_MATE - 200) { - if(v < 0) - s << "-#" << (VALUE_MATE + v) / 2; - else + if (v >= VALUE_MATE - 200) s << "#" << (VALUE_MATE - v + 1) / 2; - } - else { - float floatScore = float(v) / float(PawnValueMidgame); - if(v >= 0) - s << '+'; - s << std::setprecision(2) << std::fixed << floatScore; + else if(v <= -VALUE_MATE + 200) + s << "-#" << (VALUE_MATE + v) / 2; + else + { + float floatScore = float(v) / float(PawnValueMidgame); + if (v >= 0) + s << '+'; + + s << std::setprecision(2) << std::fixed << floatScore; } return s.str(); } - }