X-Git-Url: https://git.sesse.net/?p=stockfish;a=blobdiff_plain;f=src%2Fposition.cpp;h=918d50e505e0d1e9ab8cb4b5b15f11c6d38d81ec;hp=41e5eea25ab9ab89d2ae620a772d3f65b90cf4cf;hb=8a7876d48d4360d14d918c1ff444b5d6eb0382de;hpb=142874b058895eae65154553a9ab0606f1662111 diff --git a/src/position.cpp b/src/position.cpp index 41e5eea2..918d50e5 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -25,25 +25,21 @@ #include "bitcount.h" #include "movegen.h" -#include "notation.h" #include "position.h" #include "psqtab.h" #include "rkiss.h" #include "thread.h" #include "tt.h" +#include "uci.h" using std::string; -static const string PieceToChar(" PNBRQK pnbrqk"); - CACHE_LINE_ALIGNMENT Value PieceValue[PHASE_NB][PIECE_NB] = { { VALUE_ZERO, PawnValueMg, KnightValueMg, BishopValueMg, RookValueMg, QueenValueMg }, { VALUE_ZERO, PawnValueEg, KnightValueEg, BishopValueEg, RookValueEg, QueenValueEg } }; -static Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; - namespace Zobrist { Key psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; @@ -57,6 +53,9 @@ Key Position::exclusion_key() const { return st->key ^ Zobrist::exclusion;} namespace { +const string PieceToChar(" PNBRQK pnbrqk"); +Score psq[COLOR_NB][PIECE_TYPE_NB][SQUARE_NB]; + // min_attacker() is a helper function used by see() to locate the least // valuable attacker for the side to move, remove the attacker we just found // from the bitboards and scan for new X-ray attacks behind it. @@ -226,7 +225,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) { incremented after Black's move. */ - char col, row, token; + unsigned char col, row, token; size_t idx; Square sq = SQ_A8; std::istringstream ss(fenStr); @@ -274,7 +273,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) { for (rsq = relative_square(c, SQ_A1); type_of(piece_on(rsq)) != ROOK; ++rsq) {} else if (token >= 'A' && token <= 'H') - rsq = File(token - 'A') | relative_rank(c, RANK_1); + rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1)); else continue; @@ -286,7 +285,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) { if ( ((ss >> col) && (col >= 'a' && col <= 'h')) && ((ss >> row) && (row == '3' || row == '6'))) { - st->epSquare = File(col - 'a') | Rank(row - '1'); + st->epSquare = make_square(File(col - 'a'), Rank(row - '1')); if (!(attackers_to(st->epSquare) & pieces(sideToMove, PAWN))) st->epSquare = SQ_NONE; @@ -297,7 +296,7 @@ void Position::set(const string& fenStr, bool isChess960, Thread* th) { // Convert from fullmove starting from 1 to ply starting from 0, // handle also common incorrect FEN with fullmove = 0. - gamePly = std::max(2 * (gamePly - 1), 0) + int(sideToMove == BLACK); + gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK); chess960 = isChess960; thisThread = th; @@ -388,85 +387,87 @@ const string Position::fen() const { int emptyCnt; std::ostringstream ss; - for (Rank rank = RANK_8; rank >= RANK_1; --rank) + for (Rank r = RANK_8; r >= RANK_1; --r) { - for (File file = FILE_A; file <= FILE_H; ++file) + for (File f = FILE_A; f <= FILE_H; ++f) { - for (emptyCnt = 0; file <= FILE_H && empty(file | rank); ++file) + for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f) ++emptyCnt; if (emptyCnt) ss << emptyCnt; - if (file <= FILE_H) - ss << PieceToChar[piece_on(file | rank)]; + if (f <= FILE_H) + ss << PieceToChar[piece_on(make_square(f, r))]; } - if (rank > RANK_1) + if (r > RANK_1) ss << '/'; } ss << (sideToMove == WHITE ? " w " : " b "); if (can_castle(WHITE_OO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | KING_SIDE)), false) : 'K'); + ss << (chess960 ? 'A' + file_of(castling_rook_square(WHITE | KING_SIDE)) : 'K'); if (can_castle(WHITE_OOO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(WHITE | QUEEN_SIDE)), false) : 'Q'); + ss << (chess960 ? 'A' + file_of(castling_rook_square(WHITE | QUEEN_SIDE)) : 'Q'); if (can_castle(BLACK_OO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | KING_SIDE)), true) : 'k'); + ss << (chess960 ? 'a' + file_of(castling_rook_square(BLACK | KING_SIDE)) : 'k'); if (can_castle(BLACK_OOO)) - ss << (chess960 ? to_char(file_of(castling_rook_square(BLACK | QUEEN_SIDE)), true) : 'q'); + ss << (chess960 ? 'a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE)) : 'q'); if (!can_castle(WHITE) && !can_castle(BLACK)) ss << '-'; - ss << (ep_square() == SQ_NONE ? " - " : " " + to_string(ep_square()) + " ") - << st->rule50 << " " << 1 + (gamePly - int(sideToMove == BLACK)) / 2; + ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::format_square(ep_square()) + " ") + << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2; return ss.str(); } -/// Position::pretty() returns an ASCII representation of the position to be -/// printed to the standard output together with the move's san notation. +/// Position::pretty() returns an ASCII representation of the position -const string Position::pretty(Move move) const { +const string Position::pretty() const { - const string dottedLine = "\n+---+---+---+---+---+---+---+---+"; - const string twoRows = dottedLine + "\n| | . | | . | | . | | . |" - + dottedLine + "\n| . | | . | | . | | . | |"; + std::ostringstream ss; - string brd = twoRows + twoRows + twoRows + twoRows + dottedLine; + ss << "\n +---+---+---+---+---+---+---+---+\n"; - for (Bitboard b = pieces(); b; ) + for (Rank r = RANK_8; r >= RANK_1; --r) { - Square s = pop_lsb(&b); - brd[513 - 68 * rank_of(s) + 4 * file_of(s)] = PieceToChar[piece_on(s)]; - } + for (File f = FILE_A; f <= FILE_H; ++f) + ss << " | " << PieceToChar[piece_on(make_square(f, r))]; - std::ostringstream ss; - - if (move) - ss << "\nMove: " << (sideToMove == BLACK ? ".." : "") - << move_to_san(*const_cast(this), move); + ss << " |\n +---+---+---+---+---+---+---+---+\n"; + } - ss << brd << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase + ss << "\nFen: " << fen() << "\nKey: " << std::hex << std::uppercase << std::setfill('0') << std::setw(16) << st->key << "\nCheckers: "; for (Bitboard b = checkers(); b; ) - ss << to_string(pop_lsb(&b)) << " "; - - ss << "\nLegal moves: "; - for (MoveList it(*this); *it; ++it) - ss << move_to_san(*const_cast(this), *it) << " "; + ss << UCI::format_square(pop_lsb(&b)) << " "; return ss.str(); } +/// Position::game_phase() calculates the game phase interpolating total non-pawn +/// material between endgame and midgame limits. + +Phase Position::game_phase() const { + + Value npm = st->npMaterial[WHITE] + st->npMaterial[BLACK]; + + npm = std::max(EndgameLimit, std::min(npm, MidgameLimit)); + + return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit)); +} + + /// Position::check_blockers() returns a bitboard of all the pieces with color /// 'c' that are blocking check on the king with color 'kingColor'. A piece /// blocks a check if removing that piece from the board would result in a @@ -664,7 +665,7 @@ bool Position::gives_check(Move m, const CheckInfo& ci) const { // the captured pawn. case ENPASSANT: { - Square capsq = file_of(to) | rank_of(from); + Square capsq = make_square(file_of(to), rank_of(from)); Bitboard b = (pieces() ^ from ^ capsq) | to; return (attacks_bb< ROOK>(ci.ksq, b) & pieces(sideToMove, QUEEN, ROOK)) @@ -805,9 +806,6 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI st->castlingRights &= ~cr; } - // Prefetch TT access as soon as we know the new hash key - prefetch((char*)TT.first_entry(k)); - // Move the piece. The tricky Chess960 castling is handled earlier if (type_of(m) != CASTLING) move_piece(from, to, us, pt); @@ -823,7 +821,7 @@ void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveI k ^= Zobrist::enpassant[file_of(st->epSquare)]; } - if (type_of(m) == PROMOTION) + else if (type_of(m) == PROMOTION) { PieceType promotion = promotion_type(m); @@ -1014,6 +1012,26 @@ void Position::undo_null_move() { } +/// Position::key_after() computes the new hash key after the given moven. Needed +/// for speculative prefetch. It doesn't recognize special moves like castling, +/// en-passant and promotions. + +Key Position::key_after(Move m) const { + + Color us = sideToMove; + Square from = from_sq(m); + Square to = to_sq(m); + PieceType pt = type_of(piece_on(from)); + PieceType captured = type_of(piece_on(to)); + Key k = st->key ^ Zobrist::side; + + if (captured) + k ^= Zobrist::psq[~us][captured][to]; + + return k ^ Zobrist::psq[us][pt][to] ^ Zobrist::psq[us][pt][from]; +} + + /// Position::see() is a static exchange evaluator: It tries to estimate the /// material gain or loss resulting from a move. @@ -1115,10 +1133,6 @@ Value Position::see(Move m) const { bool Position::is_draw() const { - if ( !pieces(PAWN) - && (non_pawn_material(WHITE) + non_pawn_material(BLACK) <= BishopValueMg)) - return true; - if (st->rule50 > 99 && (!checkers() || MoveList(*this).size())) return true; @@ -1147,9 +1161,9 @@ void Position::flip() { string f, token; std::stringstream ss(fen()); - for (Rank rank = RANK_8; rank >= RANK_1; --rank) // Piece placement + for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement { - std::getline(ss, token, rank > RANK_1 ? '/' : ' '); + std::getline(ss, token, r > RANK_1 ? '/' : ' '); f.insert(0, token + (f.empty() ? " " : "/")); } @@ -1176,9 +1190,7 @@ void Position::flip() { /// Position::pos_is_ok() performs some consistency checks for the position object. /// This is meant to be helpful when debugging. -bool Position::pos_is_ok(int* failedStep) const { - - int dummy, *step = failedStep ? failedStep : &dummy; +bool Position::pos_is_ok(int* step) const { // Which parts of the position should be verified? const bool all = false; @@ -1191,19 +1203,17 @@ bool Position::pos_is_ok(int* failedStep) const { const bool testPieceList = all || false; const bool testCastlingSquares = all || false; - if (*step = 1, sideToMove != WHITE && sideToMove != BLACK) - return false; - - if ((*step)++, piece_on(king_square(WHITE)) != W_KING) - return false; - - if ((*step)++, piece_on(king_square(BLACK)) != B_KING) - return false; + if (step) + *step = 1; - if ((*step)++, ep_square() != SQ_NONE && relative_rank(sideToMove, ep_square()) != RANK_6) + if ( (sideToMove != WHITE && sideToMove != BLACK) + || piece_on(king_square(WHITE)) != W_KING + || piece_on(king_square(BLACK)) != B_KING + || ( ep_square() != SQ_NONE + && relative_rank(sideToMove, ep_square()) != RANK_6)) return false; - if ((*step)++, testBitboards) + if (step && ++*step, testBitboards) { // The intersection of the white and black pieces must be empty if (pieces(WHITE) & pieces(BLACK)) @@ -1221,7 +1231,7 @@ bool Position::pos_is_ok(int* failedStep) const { return false; } - if ((*step)++, testState) + if (step && ++*step, testState) { StateInfo si; set_state(&si); @@ -1235,22 +1245,22 @@ bool Position::pos_is_ok(int* failedStep) const { return false; } - if ((*step)++, testKingCount) + if (step && ++*step, testKingCount) if ( std::count(board, board + SQUARE_NB, W_KING) != 1 || std::count(board, board + SQUARE_NB, B_KING) != 1) return false; - if ((*step)++, testKingCapture) + if (step && ++*step, testKingCapture) if (attackers_to(king_square(~sideToMove)) & pieces(sideToMove)) return false; - if ((*step)++, testPieceCounts) + if (step && ++*step, testPieceCounts) for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= KING; ++pt) if (pieceCount[c][pt] != popcount(pieces(c, pt))) return false; - if ((*step)++, testPieceList) + if (step && ++*step, testPieceList) for (Color c = WHITE; c <= BLACK; ++c) for (PieceType pt = PAWN; pt <= KING; ++pt) for (int i = 0; i < pieceCount[c][pt]; ++i) @@ -1258,7 +1268,7 @@ bool Position::pos_is_ok(int* failedStep) const { || index[pieceList[c][pt][i]] != i) return false; - if ((*step)++, testCastlingSquares) + if (step && ++*step, testCastlingSquares) for (Color c = WHITE; c <= BLACK; ++c) for (CastlingSide s = KING_SIDE; s <= QUEEN_SIDE; s = CastlingSide(s + 1)) { @@ -1271,6 +1281,5 @@ bool Position::pos_is_ok(int* failedStep) const { return false; } - *step = 0; return true; }