else
sf = 22 + 3 * pos.count<ALL_PIECES>(strongSide);
}
+ else if ( pos.non_pawn_material(WHITE) == RookValueMg
+ && pos.non_pawn_material(BLACK) == RookValueMg
+ && !pe->passed_pawns(strongSide)
+ && pos.count<PAWN>(strongSide) - pos.count<PAWN>(~strongSide) <= 1
+ && bool(KingSide & pos.pieces(strongSide, PAWN)) != bool(QueenSide & pos.pieces(strongSide, PAWN))
+ && (attacks_bb<KING>(pos.square<KING>(~strongSide)) & pos.pieces(~strongSide, PAWN)))
+ sf = 36;
+ else if (pos.count<QUEEN>() == 1)
+ sf = 37 + 3 * (pos.count<QUEEN>(WHITE) == 1 ? pos.count<BISHOP>(BLACK) + pos.count<KNIGHT>(BLACK)
+ : pos.count<BISHOP>(WHITE) + pos.count<KNIGHT>(WHITE));
else
- sf = std::min(sf, 36 + 7 * pos.count<PAWN>(strongSide));
+ sf = std::min(sf, 36 + 7 * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide)));
}
// Interpolate between the middlegame and (scaled by 'sf') endgame score
}
// Update castling rights if needed
- if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
+ if (type_of(m) != DROP && st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
{
- int cr = castlingRightsMask[from] | castlingRightsMask[to];
- k ^= Zobrist::castling[st->castlingRights & cr];
- st->castlingRights &= ~cr;
+ k ^= Zobrist::castling[st->castlingRights];
+ st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]);
+ k ^= Zobrist::castling[st->castlingRights];
}
+ // Flip enclosed pieces
+ st->flippedPieces = 0;
+ if (flip_enclosed_pieces() && !is_pass(m))
+ {
+ // Find end of rows to be flipped
+ if (flip_enclosed_pieces() == REVERSI)
+ {
+ Bitboard b = attacks_bb(us, QUEEN, to, board_bb() & ~pieces(~us)) & ~PseudoAttacks[us][KING][to] & pieces(us);
+ while(b)
+ st->flippedPieces |= between_bb(to, pop_lsb(&b));
+ }
+ else
+ {
+ assert(flip_enclosed_pieces() == ATAXX);
+ st->flippedPieces = PseudoAttacks[us][KING][to] & pieces(~us);
+ }
+
+ // Flip pieces
+ Bitboard to_flip = st->flippedPieces;
+ while(to_flip)
+ {
+ Square s = pop_lsb(&to_flip);
+ Piece flipped = piece_on(s);
+ Piece resulting = ~flipped;
+
+ // remove opponent's piece
+ remove_piece(s);
+ k ^= Zobrist::psq[flipped][s];
+ st->materialKey ^= Zobrist::psq[flipped][pieceCount[flipped]];
+ st->nonPawnMaterial[them] -= PieceValue[MG][flipped];
+
+ // add our piece
+ put_piece(resulting, s);
+ k ^= Zobrist::psq[resulting][s];
+ st->materialKey ^= Zobrist::psq[resulting][pieceCount[resulting]-1];
+ st->nonPawnMaterial[us] += PieceValue[MG][resulting];
+ }
+ }
+
// Move the piece. The tricky Chess960 castling is handled earlier
- if (type_of(m) != CASTLING)
+ if (type_of(m) == DROP)
+ {
+ drop_piece(make_piece(us, in_hand_piece_type(m)), pc, to);
+ st->materialKey ^= Zobrist::psq[pc][pieceCount[pc]-1];
+ if (type_of(pc) != PAWN)
+ st->nonPawnMaterial[us] += PieceValue[MG][pc];
+ // Set castling rights for dropped king or rook
+ if (castling_dropped_piece() && rank_of(to) == castling_rank(us))
+ {
+ if (type_of(pc) == KING && file_of(to) == FILE_E)
+ {
+ Bitboard castling_rooks = pieces(us, castling_rook_piece())
+ & rank_bb(castling_rank(us))
+ & (file_bb(FILE_A) | file_bb(max_file()));
+ while (castling_rooks)
+ set_castling_right(us, pop_lsb(&castling_rooks));
+ }
+ else if (type_of(pc) == castling_rook_piece())
+ {
+ if ( (file_of(to) == FILE_A || file_of(to) == max_file())
+ && piece_on(make_square(FILE_E, castling_rank(us))) == make_piece(us, KING))
+ set_castling_right(us, to);
+ }
+ }
+ }
+ else if (type_of(m) != CASTLING)
move_piece(from, to);
// If the moving piece is a pawn do some special extra work
}
bool use_time_management() const {
- return !(mate | movetime | depth | nodes | perft | infinite);
+ return time[WHITE] || time[BLACK];
}
- std::vector<Move> searchmoves;
+ std::vector<Move> searchmoves, banmoves;
TimePoint time[COLOR_NB], inc[COLOR_NB], npmsec, movetime, startTime;
int movestogo, depth, mate, perft, infinite;
int64_t nodes;
}
template<MoveType T>
-constexpr Move make(Square from, Square to, PieceType pt = KNIGHT) {
- return Move(T + ((pt - KNIGHT) << 12) + (from << 6) + to);
+constexpr Move make_gating(Square from, Square to, PieceType pt, Square gate) {
+ return Move((gate << (2 * SQUARE_BITS + MOVE_TYPE_BITS + PIECE_TYPE_BITS)) + (pt << (2 * SQUARE_BITS + MOVE_TYPE_BITS)) + T + (from << SQUARE_BITS) + to);
+}
+
+constexpr PieceType dropped_piece_type(Move m) {
+ return PieceType((m >> (2 * SQUARE_BITS + MOVE_TYPE_BITS)) & (PIECE_TYPE_NB - 1));
+}
+
+constexpr PieceType in_hand_piece_type(Move m) {
+ return PieceType((m >> (2 * SQUARE_BITS + MOVE_TYPE_BITS + PIECE_TYPE_BITS)) & (PIECE_TYPE_NB - 1));
+}
+
+inline bool is_ok(Move m) {
+ return from_sq(m) != to_sq(m) || type_of(m) == PROMOTION || type_of(m) == SPECIAL; // Catch MOVE_NULL and MOVE_NONE
}
-constexpr bool is_ok(Move m) {
- return from_sq(m) != to_sq(m); // Catch MOVE_NULL and MOVE_NONE
+inline int dist(Direction d) {
+ return std::abs(d % NORTH) < NORTH / 2 ? std::max(std::abs(d / NORTH), int(std::abs(d % NORTH)))
+ : std::max(std::abs(d / NORTH) + 1, int(NORTH - std::abs(d % NORTH)));
}
+ /// Based on a congruential pseudo random number generator
+ constexpr Key make_key(uint64_t seed) {
+ return seed * 6364136223846793005ULL + 1442695040888963407ULL;
+ }
+
#endif // #ifndef TYPES_H_INCLUDED
#include "tune.h" // Global visibility to tuning setup
<< "\nNodes/second : " << 1000 * nodes / elapsed << endl;
}
+ // The win rate model returns the probability (per mille) of winning given an eval
+ // and a game-ply. The model fits rather accurately the LTC fishtest statistics.
+ int win_rate_model(Value v, int ply) {
+
+ // The model captures only up to 240 plies, so limit input (and rescale)
+ double m = std::min(240, ply) / 64.0;
+
+ // Coefficients of a 3rd order polynomial fit based on fishtest data
+ // for two parameters needed to transform eval to the argument of a
+ // logistic function.
+ double as[] = {-8.24404295, 64.23892342, -95.73056462, 153.86478679};
+ double bs[] = {-3.37154371, 28.44489198, -56.67657741, 72.05858751};
+ double a = (((as[0] * m + as[1]) * m + as[2]) * m) + as[3];
+ double b = (((bs[0] * m + bs[1]) * m + bs[2]) * m) + bs[3];
+
+ // Transform eval to centipawns with limited range
+ double x = Utility::clamp(double(100 * v) / PawnValueEg, -1000.0, 1000.0);
+
+ // Return win rate in per mille (rounded to nearest)
+ return int(0.5 + 1000 / (1 + std::exp((a - x) / b)));
+ }
+
+ // load() is called when engine receives the "load" command.
+ // The function reads variant configuration files.
+
+ void load(istringstream& is) {
+
+ string token;
+ while (is >> token)
+ Options["VariantPath"] = token;
+ }
+
+ // check() is called when engine receives the "check" command.
+ // The function reads variant configuration files and validates them.
+
+ void check(istringstream& is) {
+
+ string token;
+ while (is >> token)
+ variants.parse<true>(token);
+ }
+
} // namespace
}
+ /// UCI::wdl() report WDL statistics given an evaluation and a game ply, based on
+ /// data gathered for fishtest LTC games.
+
+ string UCI::wdl(Value v, int ply) {
+
+ stringstream ss;
+
+ int wdl_w = win_rate_model( v, ply);
+ int wdl_l = win_rate_model(-v, ply);
+ int wdl_d = 1000 - wdl_w - wdl_l;
+ ss << " wdl " << wdl_w << " " << wdl_d << " " << wdl_l;
+
+ return ss.str();
+ }
+
+
/// UCI::square() converts a Square to a string in algebraic notation (g1, a7, etc.)
-std::string UCI::square(Square s) {
- return std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
+std::string UCI::square(const Position& pos, Square s) {
+#ifdef LARGEBOARDS
+ if (Options["Protocol"] == "usi")
+ return rank_of(s) < RANK_10 ? std::string{ char('1' + pos.max_file() - file_of(s)), char('a' + pos.max_rank() - rank_of(s)) }
+ : std::string{ char('0' + (pos.max_file() - file_of(s) + 1) / 10),
+ char('0' + (pos.max_file() - file_of(s) + 1) % 10),
+ char('a' + pos.max_rank() - rank_of(s)) };
+ else if ((Options["Protocol"] == "xboard" || Options["Protocol"] == "ucci") && pos.max_rank() == RANK_10)
+ return std::string{ char('a' + file_of(s)), char('0' + rank_of(s)) };
+ else
+ return rank_of(s) < RANK_10 ? std::string{ char('a' + file_of(s)), char('1' + (rank_of(s) % 10)) }
+ : std::string{ char('a' + file_of(s)), char('0' + ((rank_of(s) + 1) / 10)),
+ char('0' + ((rank_of(s) + 1) % 10)) };
+#else
+ return Options["Protocol"] == "usi" ? std::string{ char('1' + pos.max_file() - file_of(s)), char('a' + pos.max_rank() - rank_of(s)) }
+ : std::string{ char('a' + file_of(s)), char('1' + rank_of(s)) };
+#endif
+}
+
+/// UCI::dropped_piece() generates a piece label string from a Move.
+
+string UCI::dropped_piece(const Position& pos, Move m) {
+ assert(type_of(m) == DROP);
+ if (dropped_piece_type(m) == pos.promoted_piece_type(in_hand_piece_type(m)))
+ // Dropping as promoted piece
+ return std::string{'+', pos.piece_to_char()[in_hand_piece_type(m)]};
+ else
+ return std::string{pos.piece_to_char()[dropped_piece_type(m)]};
}
void init(OptionsMap&);
void loop(int argc, char* argv[]);
std::string value(Value v);
-std::string square(Square s);
-std::string move(Move m, bool chess960);
+std::string square(const Position& pos, Square s);
+std::string dropped_piece(const Position& pos, Move m);
+std::string move(const Position& pos, Move m);
std::string pv(const Position& pos, Depth depth, Value alpha, Value beta);
+ std::string wdl(Value v, int ply);
Move to_move(const Position& pos, std::string& str);
} // namespace UCI