X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fsan.cpp;h=d8e7da15a7f8b7b83931956f7a8d9beb6d432f13;hp=0e9b531003cb9c69595b84ee6540aec4d0689532;hb=1a20d727019548de69314a90ae59a3b8a76b4ae7;hpb=18cd83a38042a3993678e88bc8163ceb1cbd62b4 diff --git a/src/san.cpp b/src/san.cpp index 0e9b5310..d8e7da15 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-2009 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,8 +28,7 @@ #include #include -#include "history.h" -#include "movepick.h" +#include "movegen.h" #include "san.h" using std::string; @@ -41,14 +40,9 @@ using std::string; namespace { enum Ambiguity { - AMBIGUITY_NONE, - AMBIGUITY_FILE, - AMBIGUITY_RANK, - AMBIGUITY_BOTH + AMBIGUITY_NONE, AMBIGUITY_FILE, AMBIGUITY_RANK, AMBIGUITY_BOTH }; - const History H; // used as dummy argument for MovePicker c'tor - Ambiguity move_ambiguity(const Position& pos, Move m); const string time_string(int milliseconds); const string score_string(Value v); @@ -63,33 +57,32 @@ namespace { /// 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(const Position& pos, Move m) { +const string move_to_san(Position& pos, Move m) { assert(pos.is_ok()); assert(move_is_ok(m)); - 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 = ""; + string san; + Square from = move_from(m); + Square to = move_to(m); + PieceType pt = type_of_piece(pos.piece_on(from)); if (m == MOVE_NONE) return "(none)"; - else if (m == MOVE_NULL) + + if (m == MOVE_NULL) return "(null)"; - else if (move_is_long_castle(m) || (int(to - from) == -2 && pt == KING)) + + if (move_is_long_castle(m)) san = "O-O-O"; - else if (move_is_short_castle(m) || (int(to - from) == 2 && pt == KING)) + else if (move_is_short_castle(m)) san = "O-O"; else { if (pt != PAWN) { - san += piece_type_to_char(pt, true); + san += piece_type_to_char(pt); + switch (move_ambiguity(pos, m)) { case AMBIGUITY_NONE: break; @@ -106,27 +99,30 @@ const string move_to_san(const Position& pos, Move m) { assert(false); } } + if (pos.move_is_capture(m)) { if (pt == PAWN) - san += file_to_char(square_file(move_from(m))); - san += "x"; + san += file_to_char(square_file(from)); + + san += 'x'; } - san += square_to_string(move_to(m)); + san += square_to_string(to); + if (move_is_promotion(m)) { san += '='; - san += piece_type_to_char(move_promotion_piece(m), true); + san += piece_type_to_char(move_promotion_piece(m)); } } - // 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). + + // 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; - Position p(pos); - p.do_move(m, st); - if (p.is_check()) - san += p.is_mate()? "#" : "+"; + pos.do_move(m, st); + if (pos.is_check()) + san += pos.is_mate() ? "#" : "+"; + pos.undo_move(m); return san; } @@ -141,40 +137,43 @@ Move move_from_san(const Position& pos, const string& movestr) { assert(pos.is_ok()); - MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H); + enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END }; + static const string pieceLetters = "KQRBN"; + + MoveStack mlist[MOVES_MAX], *last; + PieceType pt = PIECE_TYPE_NONE, promotion = PIECE_TYPE_NONE; + File fromFile = FILE_NONE, toFile = FILE_NONE; + Rank fromRank = RANK_NONE, toRank = RANK_NONE; + Move move = MOVE_NONE; + Square from, to; + int matches, state = START; + + // Generate all legal moves for the given position + last = generate_moves(pos, mlist); // Castling moves if (movestr == "O-O-O" || movestr == "O-O-O+") { - Move m; - while ((m = mp.get_next_move()) != MOVE_NONE) - if (move_is_long_castle(m) && pos.pl_move_is_legal(m)) - return m; + for (MoveStack* cur = mlist; cur != last; cur++) + if (move_is_long_castle(cur->move)) + return cur->move; return MOVE_NONE; } 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)) - return m; + for (MoveStack* cur = mlist; cur != last; cur++) + if (move_is_short_castle(cur->move)) + return cur->move; return MOVE_NONE; } - // 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; - int state = START; - + // Normal moves. We use a simple FSM to parse the san string 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') @@ -194,7 +193,7 @@ Move move_from_san(const Position& pos, const string& movestr) { else if (state == PROMOTION) { promotion = piece_type_from_char(c); - state = (i < movestr.length() - 1) ? CHECK : END; + state = (i < movestr.length() - 1 ? CHECK : END); } else return MOVE_NONE; @@ -234,7 +233,8 @@ Move move_from_san(const Position& pos, const string& movestr) { else return MOVE_NONE; break; - case 'x': case 'X': + case 'x': + case 'X': if (state == TO_RANK) { // Previous file was for disambiguation, or it's a pawn capture @@ -250,7 +250,8 @@ Move move_from_san(const Position& pos, const string& movestr) { else return MOVE_NONE; break; - case '+': case '#': + case '+': + case '#': if (state == PROMOTION_OR_CHECK || state == CHECK) state = END; else @@ -265,22 +266,25 @@ Move move_from_san(const Position& pos, const string& movestr) { if (state != END) return MOVE_NONE; - // Look for a matching move - Move m, move = MOVE_NONE; + // Look for an unambiguous matching move to = make_square(toFile, toRank); - int matches = 0; - - while ((m = mp.get_next_move()) != MOVE_NONE) - if ( pos.type_of_piece_on(move_from(m)) == pt - && move_to(m) == to - && move_promotion_piece(m) == promotion - && (fromFile == FILE_NONE || fromFile == square_file(move_from(m))) - && (fromRank == RANK_NONE || fromRank == square_rank(move_from(m)))) + matches = 0; + + for (MoveStack* cur = mlist; cur != last; cur++) + { + from = move_from(cur->move); + + if ( pos.type_of_piece_on(from) == pt + && move_to(cur->move) == to + && move_promotion_piece(cur->move) == promotion + && (fromFile == FILE_NONE || fromFile == square_file(from)) + && (fromRank == RANK_NONE || fromRank == square_rank(from))) { - move = m; + move = cur->move; matches++; } - return (matches == 1 ? move : MOVE_NONE); + } + return matches == 1 ? move : MOVE_NONE; } @@ -298,23 +302,23 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool string moveStr; size_t length = 0; size_t maxLength = 80 - startColumn; - Position p(pos); + Position p(pos, pos.thread()); - for (int i = 0; line[i] != MOVE_NONE; i++) + for (Move* m = line; *m != MOVE_NONE; m++) { - moveStr = move_to_san(p, line[i]); + moveStr = move_to_san(p, *m); length += moveStr.length() + 1; if (breakLines && length > maxLength) { - s << '\n' << std::setw(startColumn) << ' '; + s << "\n" << std::setw(startColumn) << " "; length = moveStr.length() + 1; } s << moveStr << ' '; - if (line[i] == MOVE_NULL) + if (*m == MOVE_NULL) p.do_null_move(st); else - p.do_move(line[i], st); + p.do_move(*m, st); } return s.str(); } @@ -325,26 +329,30 @@ const string line_to_san(const Position& pos, Move line[], int startColumn, bool /// when the UCI parameter "Use Search Log" is "true"). const string pretty_pv(const Position& pos, int time, int depth, - uint64_t nodes, Value score, ValueType type, Move pv[]) { + Value score, ValueType type, Move pv[]) { + + const int64_t K = 1000; + const int64_t M = 1000000; + std::stringstream s; // Depth s << std::setw(2) << depth << " "; // Score - s << ((type == VALUE_TYPE_LOWER)? ">" : ((type == VALUE_TYPE_UPPER)? "<" : " ")); - s << std::setw(7) << score_string(score); + s << (type == VALUE_TYPE_LOWER ? ">" : type == VALUE_TYPE_UPPER ? "<" : " ") + << std::setw(7) << score_string(score); // Time s << std::setw(8) << time_string(time) << " "; // Nodes - if (nodes < 1000000ULL) - s << std::setw(8) << nodes << " "; - else if (nodes < 1000000000ULL) - s << std::setw(7) << 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) << nodes/1000000ULL << 'M' << " "; + s << std::setw(7) << pos.nodes_searched() / M << "M "; // PV s << line_to_san(pos, pv, 30, true); @@ -357,53 +365,50 @@ namespace { 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; - // King moves are never ambiguous, because there is never two kings of - // the same color. - if (type_of_piece(pc) == KING) + // 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; - MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H); - Move mv, moveList[8]; - - int n = 0; - while ((mv = mp.get_next_move()) != MOVE_NONE) - if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv)) - moveList[n++] = mv; + // Collect all legal moves of piece 'pc' with destination 'to' + last = generate_moves(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 (n == 1) + if (matches == 1) return AMBIGUITY_NONE; - int f = 0, r = 0; - for (int i = 0; i < n; i++) + for (int i = 0; i < matches; i++) { - if (square_file(move_from(moveList[i])) == square_file(from)) + if (square_file(move_from(candidates[i])) == square_file(from)) f++; - if (square_rank(move_from(moveList[i])) == square_rank(from)) + if (square_rank(move_from(candidates[i])) == square_rank(from)) r++; } - if (f == 1) - return AMBIGUITY_FILE; - if (r == 1) - return AMBIGUITY_RANK; - - return AMBIGUITY_BOTH; + return f == 1 ? AMBIGUITY_FILE : r == 1 ? AMBIGUITY_RANK : AMBIGUITY_BOTH; } - const 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) / (1000*60); - int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000; + int hours = millisecs / MSecHour; + int minutes = (millisecs - hours * MSecHour) / MSecMinute; + int seconds = (millisecs - hours * MSecHour - minutes * MSecMinute) / 1000; if (hours) s << hours << ':'; @@ -419,7 +424,7 @@ namespace { if (v >= VALUE_MATE - 200) s << "#" << (VALUE_MATE - v + 1) / 2; - else if(v <= -VALUE_MATE + 200) + else if (v <= -VALUE_MATE + 200) s << "-#" << (VALUE_MATE + v) / 2; else {