X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fsan.cpp;h=1fc8d13cfc5502c48252fb19f40bbdf942d1f818;hb=ee0afea1e57e4c39140c838b9f9f2d5c7c061e6b;hp=df169977d2b9491a995e3ffef111ffafcc82471a;hpb=5dc23121215039938a9ef4e59ae934312774571c;p=stockfish diff --git a/src/san.cpp b/src/san.cpp index df169977..1fc8d13c 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-2010 Marco Costalba, Joona Kiiski, Tord Romstad Stockfish is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -28,9 +28,10 @@ #include #include -#include "movepick.h" +#include "movegen.h" #include "san.h" +using std::string; //// //// Local definitions @@ -38,21 +39,13 @@ namespace { - /// Types - enum Ambiguity { - AMBIGUITY_NONE, - AMBIGUITY_FILE, - AMBIGUITY_RANK, - AMBIGUITY_BOTH + AMBIGUITY_NONE, AMBIGUITY_FILE, AMBIGUITY_RANK, AMBIGUITY_BOTH }; - - /// Functions - - Ambiguity move_ambiguity(Position &pos, Move m); - const std::string time_string(int milliseconds); - const std::string score_string(Value v); + Ambiguity move_ambiguity(const Position& pos, Move m); + const string time_string(int milliseconds); + const string score_string(Value v); } @@ -60,207 +53,124 @@ namespace { //// Functions //// -/// 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 -/// a string containing the move in short algebraic notation. +/// move_to_uci() converts a move to a string in coordinate notation +/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we +/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in +/// Chess960 mode. -const std::string move_to_san(Position &pos, Move m) { - std::string str; +const std::string move_to_uci(Move m, bool chess960) { - assert(pos.is_ok()); - assert(move_is_ok(m)); + std::string promotion; + Square from = move_from(m); + Square to = move_to(m); - if(m == MOVE_NONE) { - str = "(none)"; - return str; - } - else if(m == MOVE_NULL) { - str = "(null)"; - return str; - } - else if(move_is_long_castle(m)) - str = "O-O-O"; - else if(move_is_short_castle(m)) - str = "O-O"; - else { - Square from, to; - Piece pc; - - from = move_from(m); - to = move_to(m); - pc = pos.piece_on(move_from(m)); - - str = ""; - - if(type_of_piece(pc) == PAWN) { - if(pos.move_is_capture(m)) - str += file_to_char(square_file(move_from(m))); - } - else { - str += piece_type_to_char(type_of_piece(pc), true); + if (m == MOVE_NONE) + return "(none)"; - Ambiguity amb = move_ambiguity(pos, m); - switch(amb) { + if (m == MOVE_NULL) + return "0000"; - case AMBIGUITY_NONE: - break; + if (move_is_short_castle(m) && !chess960) + return from == SQ_E1 ? "e1g1" : "e8g8"; - case AMBIGUITY_FILE: - str += file_to_char(square_file(from)); - break; + if (move_is_long_castle(m) && !chess960) + return from == SQ_E1 ? "e1c1" : "e8c8"; - case AMBIGUITY_RANK: - str += rank_to_char(square_rank(from)); - break; + if (move_is_promotion(m)) + promotion = char(tolower(piece_type_to_char(move_promotion_piece(m)))); - case AMBIGUITY_BOTH: - str += square_to_string(from); - break; + return square_to_string(from) + square_to_string(to) + promotion; +} - default: - assert(false); - } - } - if(pos.move_is_capture(m)) - str += "x"; +/// move_from_uci() takes a position and a string representing a move in +/// simple coordinate notation and returns an equivalent Move. - str += square_to_string(move_to(m)); +Move move_from_uci(const Position& pos, const std::string& str) { - if(move_promotion(m)) { - str += "="; - str += piece_type_to_char(move_promotion(m), true); - } - } + MoveStack mlist[MOVES_MAX]; + MoveStack* last = generate(pos, mlist); - // 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; - pos.do_move(m, u); - if(pos.is_check()) - str += pos.is_mate()? "#" : "+"; - pos.undo_move(m, u); + for (MoveStack* cur = mlist; cur != last; cur++) + if (str == move_to_uci(cur->move, pos.is_chess960())) + return cur->move; - return str; + return MOVE_NONE; } -/// 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, -/// 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_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 +/// a string containing the move in short algebraic notation. + +const string move_to_san(Position& pos, Move m) { -Move move_from_san(Position &pos, const std::string &movestr) { assert(pos.is_ok()); + assert(move_is_ok(m)); - MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE, - MOVE_NONE, OnePly); + string san; + Square from = move_from(m); + Square to = move_to(m); + PieceType pt = type_of_piece(pos.piece_on(from)); - // Castling moves - if(movestr == "O-O-O") { - Move m; - while((m = mp.get_next_move()) != MOVE_NONE) - if(move_is_long_castle(m) && pos.move_is_legal(m)) - return m; - return MOVE_NONE; - } - else if(movestr == "O-O") { - Move m; - while((m = mp.get_next_move()) != MOVE_NONE) - if(move_is_short_castle(m) && pos.move_is_legal(m)) - return m; - return MOVE_NONE; - } + if (m == MOVE_NONE) + return "(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; - Square to; - File fromFile = FILE_NONE; - Rank fromRank = RANK_NONE; - - // Promotion? - if(strchr("BNRQ", str[right])) { - promotion = piece_type_from_char(str[right]); - right--; - } + if (m == MOVE_NULL) + return "(null)"; + + if (move_is_long_castle(m)) + san = "O-O-O"; + else if (move_is_short_castle(m)) + san = "O-O"; else - promotion = NO_PIECE_TYPE; + { + if (pt != PAWN) + { + san += piece_type_to_char(pt); + + 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); + } + } - // Find the moving piece: - if(left < right) { - if(strchr("BNRQK", str[left])) { - pt = piece_type_from_char(str[left]); - left++; - } - else - pt = PAWN; - } + if (pos.move_is_capture(m)) + { + if (pt == PAWN) + san += file_to_char(square_file(from)); - // Find the to square: - if(left < right) { - if(str[right] < '1' || str[right] > '8' || - str[right-1] < 'a' || str[right-1] > 'h') - return MOVE_NONE; - to = make_square(file_from_char(str[right-1]), rank_from_char(str[right])); - right -= 2; - } - else - return MOVE_NONE; + san += 'x'; + } + san += square_to_string(to); - // 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]); + if (move_is_promotion(m)) + { + san += '='; + san += piece_type_to_char(move_promotion_piece(m)); + } } - // Look for a matching move: - Move m, move = MOVE_NONE; - 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++; - } - } + // The move gives check ? We don't use pos.move_is_check() here + // because we need to test for mate after the move is done. + StateInfo st; + pos.do_move(m, st); + if (pos.is_check()) + san += pos.is_mate() ? "#" : "+"; + pos.undo_move(m); - if(matches == 1) - return move; - else - return MOVE_NONE; + return san; } @@ -271,34 +181,31 @@ Move move_from_san(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) { - Position p = Position(pos); - UndoInfo u; +const string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) { + + 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, pos.thread()); + + for (Move* m = line; *m != MOVE_NONE; m++) + { + moveStr = move_to_san(p, *m); + 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(u); - else - p.do_move(line[i], u); + if (*m == MOVE_NULL) + p.do_null_move(st); + else + p.do_move(*m, st); } - return s.str(); } @@ -307,26 +214,31 @@ 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, + Value score, ValueType type, Move pv[]) { + + const int64_t K = 1000; + const int64_t M = 1000000; + std::stringstream s; // Depth - s << std::setw(2) << std::setfill(' ') << depth << " "; + s << std::setw(2) << depth << " "; // Score - s << std::setw(8) << score_string(score); + s << (type == VALUE_TYPE_LOWER ? ">" : type == VALUE_TYPE_UPPER ? "<" : " ") + << std::setw(7) << 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 (pos.nodes_searched() < M) + s << std::setw(8) << pos.nodes_searched() / 1 << " "; + else if (pos.nodes_searched() < K * M) + s << std::setw(7) << pos.nodes_searched() / K << "K "; else - s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " "; + s << std::setw(7) << pos.nodes_searched() / M << "M "; // PV s << line_to_san(pos, pv, 30, true); @@ -337,82 +249,77 @@ const std::string pretty_pv(const Position &pos, int time, int depth, namespace { - Ambiguity move_ambiguity(Position &pos, Move m) { - Square from, to; - Piece pc; - - from = move_from(m); - to = move_to(m); - pc = pos.piece_on(from); - - // King moves are never ambiguous, because there is never two kings of - // the same color. - if(type_of_piece(pc) == KING) - return AMBIGUITY_NONE; - - MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE, - MOVE_NONE, OnePly); - Move mv, moveList[8]; - int i, j, n; - - n = 0; - while((mv = mp.get_next_move()) != MOVE_NONE) - if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc - && pos.move_is_legal(mv)) - moveList[n++] = mv; - if(n == 1) - return AMBIGUITY_NONE; - - j = 0; - for(i = 0; i < n; i++) - if(square_file(move_from(moveList[i])) == square_file(from)) - j++; - if(j == 1) - return AMBIGUITY_FILE; - - j = 0; - for(i = 0; i < n; i++) - if(square_rank(move_from(moveList[i])) == square_rank(from)) - j++; - if(j == 1) - return AMBIGUITY_RANK; - - return AMBIGUITY_BOTH; + Ambiguity move_ambiguity(const Position& pos, Move m) { + + MoveStack mlist[MOVES_MAX], *last; + Move candidates[8]; + Square from = move_from(m); + Square to = move_to(m); + Piece pc = pos.piece_on(from); + int matches = 0, f = 0, r = 0; + + // If there is only one piece 'pc' then move cannot be ambiguous + if (pos.piece_count(pos.side_to_move(), type_of_piece(pc)) == 1) + return AMBIGUITY_NONE; + + // Collect all legal moves of piece 'pc' with destination 'to' + last = generate(pos, mlist); + for (MoveStack* cur = mlist; cur != last; cur++) + if (move_to(cur->move) == to && pos.piece_on(move_from(cur->move)) == pc) + candidates[matches++] = cur->move; + + if (matches == 1) + return AMBIGUITY_NONE; + + for (int i = 0; i < matches; i++) + { + if (square_file(move_from(candidates[i])) == square_file(from)) + f++; + + if (square_rank(move_from(candidates[i])) == square_rank(from)) + r++; + } + + return f == 1 ? AMBIGUITY_FILE : r == 1 ? AMBIGUITY_RANK : AMBIGUITY_BOTH; } - const std::string time_string(int milliseconds) { + const string time_string(int millisecs) { + + const int MSecMinute = 1000 * 60; + const int MSecHour = 1000 * 60 * 60; + 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 = millisecs / MSecHour; + int minutes = (millisecs - hours * MSecHour) / MSecMinute; + int seconds = (millisecs - hours * MSecHour - minutes * MSecMinute) / 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(); } - }