inline Bitboard& operator|=(Bitboard& b, Square s) { return b |= square_bb(s); }
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
+inline Bitboard operator-( Bitboard b, Square s) { return b & ~square_bb(s); }
+inline Bitboard& operator-=(Bitboard& b, Square s) { return b &= ~square_bb(s); }
+
+ inline Bitboard operator&(Square s, Bitboard b) { return b & s; }
+ inline Bitboard operator|(Square s, Bitboard b) { return b | s; }
+ inline Bitboard operator^(Square s, Bitboard b) { return b ^ s; }
+
+ inline Bitboard operator|(Square s, Square s2) { return square_bb(s) | square_bb(s2); }
+
constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
}
inline Square msb(Bitboard b) {
assert(b);
- return Square(63 ^ __builtin_clzll(b));
+#ifdef LARGEBOARDS
+ if (b >> 64)
- return Square(SQUARE_BIT_MASK ^ __builtin_clzll(b >> 64));
- return Square(SQUARE_BIT_MASK ^ (__builtin_clzll(b) + 64));
++ return Square(int(SQUARE_BIT_MASK) ^ __builtin_clzll(b >> 64));
++ return Square(int(SQUARE_BIT_MASK) ^ (__builtin_clzll(b) + 64));
+#else
- return Square(SQUARE_BIT_MASK ^ __builtin_clzll(b));
++ return Square(int(SQUARE_BIT_MASK) ^ __builtin_clzll(b));
+#endif
}
#elif defined(_MSC_VER) // MSVC
&& relative_rank(weakSide, pos.square<KING>(strongSide)) >= RANK_4
&& relative_rank(weakSide, rsq) == RANK_3
&& ( pos.pieces(weakSide, PAWN)
- & pos.attacks_from<KING>(weakSide, kingSq)
- & pos.attacks_from<PAWN>(strongSide, rsq)))
- & pos.attacks_from<KING>(kingSq)
++ & pos.attacks_from<KING>(kingSq, weakSide)
+ & pos.attacks_from<PAWN>(rsq, strongSide)))
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
if ( ksq == blockSq1
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
- || (pos.attacks_from<BISHOP>(weakSide, blockSq2) & pos.pieces(weakSide, BISHOP))
- || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
++ || (pos.attacks_from<BISHOP>(blockSq2, weakSide) & pos.pieces(weakSide, BISHOP))
|| distance<Rank>(psq1, psq2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1
- || (pos.attacks_from<BISHOP>(weakSide, blockSq1) & pos.pieces(weakSide, BISHOP))))
- || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(weakSide, BISHOP))))
++ || (pos.attacks_from<BISHOP>(blockSq1, weakSide) & pos.pieces(weakSide, BISHOP))))
return SCALE_FACTOR_DRAW;
else
return SCALE_FACTOR_NONE;
// King needs to get close to promoting pawn to prevent knight from blocking.
// Rules for this are very tricky, so just approximate.
- if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(weakSide, bishopSq))
- if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq))
++ if (forward_file_bb(strongSide, pawnSq) & pos.attacks_from<BISHOP>(bishopSq, weakSide))
return ScaleFactor(distance(weakKingSq, pawnSq));
return SCALE_FACTOR_NONE;
constexpr Score PassedRank[RANK_NB] = {
S(0, 0), S(10, 28), S(17, 33), S(15, 41), S(62, 72), S(168, 177), S(276, 260)
};
+
+ // OutpostRank[Rank] contains a bonus according to the rank of the outpost
+ constexpr Score OutpostRank[RANK_NB] = {
+ S(0, 0), S(0, 0), S(0, 0), S(28, 18), S(30, 24), S(32, 19)
+ };
+ // KingProximity contains a penalty according to distance from king
+ constexpr Score KingProximity = S(1, 3);
+
// Assorted bonuses and penalties
constexpr Score BishopPawns = S( 3, 7);
constexpr Score CorneredBishop = S( 50, 50);
void Evaluation<T>::initialize() {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ constexpr Direction Up = pawn_push(Us);
+ constexpr Direction Down = -Up;
- constexpr Bitboard LowRanks = (Us == WHITE ? Rank2BB | Rank3BB : Rank7BB | Rank6BB);
+ Bitboard LowRanks = rank_bb(relative_rank(Us, RANK_2, pos.max_rank())) | rank_bb(relative_rank(Us, RANK_3, pos.max_rank()));
- const Square ksq = pos.square<KING>(Us);
+ const Square ksq = pos.count<KING>(Us) ? pos.square<KING>(Us) : SQ_NONE;
Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
// Squares occupied by those pawns, by our king or queen or controlled by
// enemy pawns are excluded from the mobility area.
- mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them));
+ if (pos.must_capture())
+ mobilityArea[Us] = AllSquares;
+ else
+ mobilityArea[Us] = ~(b | pos.pieces(Us, KING, QUEEN) | pe->pawn_attacks(Them) | shift<Down>(pos.pieces(Them, SHOGI_PAWN)));
// Initialize attackedBy[] for king and pawns
- attackedBy[Us][KING] = pos.count<KING>(Us) ? pos.attacks_from<KING>(Us, ksq) : Bitboard(0);
- attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
++ attackedBy[Us][KING] = pos.count<KING>(Us) ? pos.attacks_from<KING>(ksq, Us) : Bitboard(0);
attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
- attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
- attackedBy2[Us] = dblAttackByPawn | (attackedBy[Us][KING] & attackedBy[Us][PAWN]);
+ attackedBy[Us][SHOGI_PAWN] = shift<Up>(pos.pieces(Us, SHOGI_PAWN));
+ attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN] | attackedBy[Us][SHOGI_PAWN];
+ attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN])
+ | (attackedBy[Us][KING] & attackedBy[Us][SHOGI_PAWN])
+ | (attackedBy[Us][PAWN] & attackedBy[Us][SHOGI_PAWN])
+ | dblAttackByPawn;
// Init our king safety tables
- kingRing[Us] = attackedBy[Us][KING];
- if (pos.count<KING>(Us) && relative_rank(Us, ksq, pos.max_rank()) == RANK_1)
- kingRing[Us] |= shift<Up>(kingRing[Us]);
-
- if (pos.count<KING>(Us) && file_of(ksq) == pos.max_file())
- kingRing[Us] |= shift<WEST>(kingRing[Us]);
-
- else if (pos.count<KING>(Us) && file_of(ksq) == FILE_A)
- kingRing[Us] |= shift<EAST>(kingRing[Us]);
- Square s = make_square(clamp(file_of(ksq), FILE_B, FILE_G),
- clamp(rank_of(ksq), RANK_2, RANK_7));
- kingRing[Us] = s | PseudoAttacks[KING][s];
++ if (!pos.count<KING>(Us))
++ kingRing[Us] = Bitboard(0);
++ else
++ {
++ Square s = make_square(clamp(file_of(ksq), FILE_B, File(pos.max_file() - 1)),
++ clamp(rank_of(ksq), RANK_2, Rank(pos.max_rank() - 1)));
++ kingRing[Us] = s | PseudoAttacks[Us][KING][s];
++ }
kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
// Evaluation::pieces() scores pieces of a given color and type
- template<Tracing T> template<Color Us, PieceType Pt>
- Score Evaluation<T>::pieces() {
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::pieces(PieceType Pt) {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
+ constexpr Direction Down = -pawn_push(Us);
constexpr Bitboard OutpostRanks = (Us == WHITE ? Rank4BB | Rank5BB | Rank6BB
: Rank5BB | Rank4BB | Rank3BB);
- const Square* pl = pos.squares<Pt>(Us);
+ const Square* pl = pos.squares(Us, Pt);
Bitboard b, bb;
Score score = SCORE_ZERO;
int kingFlankAttacks = popcount(b1) + popcount(b2);
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
- + 185 * popcount(kingRing[Us] & weak)
+ + kingAttackersCountInHand[Them] * kingAttackersWeight[Them]
+ + kingAttackersCount[Them] * kingAttackersWeightInHand[Them]
- + 69 * kingAttacksCount[Them] * (2 + 8 * pos.check_counting() + pos.captures_to_hand()) / 2
+ + 185 * popcount(kingRing[Us] & weak) * (1 + pos.captures_to_hand() + pos.check_counting())
- - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- - 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
+ 148 * popcount(unsafeChecks)
+ 98 * popcount(pos.blockers_for_king(Us))
- + 69 * kingAttacksCount[Them]
++ + 69 * kingAttacksCount[Them] * (2 + 8 * pos.check_counting() + pos.captures_to_hand()) / 2
+ + 3 * kingFlankAttacks * kingFlankAttacks / 8
+ + mg_value(mobility[Them] - mobility[Us])
- - 873 * !pos.count<QUEEN>(Them)
+ - 873 * !(pos.major_pieces(Them) || pos.captures_to_hand() || pos.xiangqi_general()) / (1 + pos.check_counting())
+ - 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
+ - 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
- 6 * mg_value(score) / 8
- + mg_value(mobility[Them] - mobility[Us])
- + 3 * kingFlankAttacks * kingFlankAttacks / 8
- 7;
// Transform the kingDanger units into a Score, and subtract it from the evaluation
Square s = pos.square<QUEEN>(Them);
safe = mobilityArea[Us] & ~stronglyProtected;
- b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(Us, s);
- b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(s);
++ b = attackedBy[Us][KNIGHT] & pos.attacks_from<KNIGHT>(s, Us);
score += KnightOnQueen * popcount(b & safe);
- b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(Us, s))
- | (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(Us, s));
- b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s))
- | (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(s));
++ b = (attackedBy[Us][BISHOP] & pos.attacks_from<BISHOP>(s, Us))
++ | (attackedBy[Us][ROOK ] & pos.attacks_from<ROOK >(s, Us));
score += SliderOnQueen * popcount(b & safe & attackedBy2[Us]);
}
Score Evaluation<T>::passed() const {
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
+ constexpr Direction Up = pawn_push(Us);
auto king_proximity = [&](Color c, Square s) {
- return std::min(distance(pos.square<KING>(c), s), 5);
+ return pos.count<KING>(c) ? std::min(distance(pos.square<KING>(c), s), 5) : 5;
};
Bitboard b, bb, squaresToQueen, unsafeSquares;
assert(!(pos.pieces(Them, PAWN) & forward_file_bb(Us, s + Up)));
- int r = relative_rank(Us, s);
+ int r = std::max(RANK_8 - std::max(pos.promotion_rank() - relative_rank(Us, s, pos.max_rank()), 0), 0);
- File f = file_of(s);
Score bonus = PassedRank[r];
|| (pos.pieces(PAWN) & (s + Up)))
bonus = bonus / 2;
- score += bonus - PassedFile * std::min(f, File(pos.max_file() - f));
- score += bonus - PassedFile * map_to_queenside(file_of(s));
++ score += bonus - PassedFile * std::min(file_of(s), File(pos.max_file() - file_of(s)));
+ }
+
+ // Scale by maximum promotion piece value
+ Value maxMg = VALUE_ZERO, maxEg = VALUE_ZERO;
+ for (PieceType pt : pos.promotion_piece_types())
+ {
+ maxMg = std::max(maxMg, PieceValue[MG][pt]);
+ maxEg = std::max(maxEg, PieceValue[EG][pt]);
+ }
+ score = make_score(mg_value(score) * int(maxMg - PawnValueMg) / (QueenValueMg - PawnValueMg),
+ eg_value(score) * int(maxEg - PawnValueEg) / (QueenValueEg - PawnValueEg));
+
+ // Score passed shogi pawns
+ const Square* pl = pos.squares(Us, SHOGI_PAWN);
+ Square s;
+
+ PieceType pt = pos.promoted_piece_type(SHOGI_PAWN);
+ if (pt != NO_PIECE_TYPE)
+ {
+ while ((s = *pl++) != SQ_NONE)
+ {
+ if ((pos.pieces(Them, SHOGI_PAWN) & forward_file_bb(Us, s)) || relative_rank(Us, s, pos.max_rank()) == pos.max_rank())
+ continue;
+
+ Square blockSq = s + Up;
+ int d = std::max(pos.promotion_rank() - relative_rank(Us, s, pos.max_rank()), 1);
+ d += !!(attackedBy[Them][ALL_PIECES] & ~attackedBy2[Us] & blockSq);
+ score += make_score(PieceValue[MG][pt], PieceValue[EG][pt]) / (4 * d * d);
+ }
}
if (T)
&& pos.non_pawn_material() == 2 * BishopValueMg)
sf = 16 + 4 * pe->passed_count();
else
- sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide));
+ sf = std::min(sf, 36 + (pos.opposite_bishops() ? 2 : 7) * (pos.count<PAWN>(strongSide) + pos.count<SOLDIER>(strongSide)));
- sf = std::max(0, sf - (pos.rule50_count() - 12) / 4 );
+ sf = std::max(0, sf - (pos.rule50_count() - 12) / 4);
}
return ScaleFactor(sf);
// Compute some compile time parameters relative to the white side
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
- constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
- constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
- constexpr Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
- constexpr Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
+ constexpr Direction Up = pawn_push(Us);
++ constexpr Direction Down = -pawn_push(Us);
constexpr Direction UpRight = (Us == WHITE ? NORTH_EAST : SOUTH_WEST);
constexpr Direction UpLeft = (Us == WHITE ? NORTH_WEST : SOUTH_EAST);
- const Square ksq = pos.square<KING>(Them);
++ const Square ksq = pos.count<KING>(Them) ? pos.square<KING>(Them) : SQ_NONE;
++
++ Bitboard TRank8BB = pos.mandatory_pawn_promotion() ? rank_bb(relative_rank(Us, pos.promotion_rank(), pos.max_rank()))
++ : promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank());
++ Bitboard TRank7BB = shift<Down>(TRank8BB);
+ // Define squares a pawn can pass during a double step
+ Bitboard TRank3BB = rank_bb(relative_rank(Us, Rank(pos.double_step_rank() + 1), pos.max_rank()));
+ if (pos.first_rank_double_steps())
+ TRank3BB |= rank_bb(relative_rank(Us, RANK_2, pos.max_rank()));
+
Bitboard emptySquares;
- Bitboard TRank8BB = pos.mandatory_pawn_promotion() ? rank_bb(relative_rank(Us, pos.promotion_rank(), pos.max_rank()))
- : promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank());
- Bitboard TRank7BB = shift<Down>(TRank8BB);
Bitboard pawnsOn7 = pos.pieces(Us, PAWN) & TRank7BB;
- Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & ~TRank7BB;
+ Bitboard pawnsNotOn7 = pos.pieces(Us, PAWN) & (pos.mandatory_pawn_promotion() ? ~TRank7BB : AllSquares);
Bitboard enemies = (Type == EVASIONS ? pos.pieces(Them) & target:
Type == CAPTURES ? target : pos.pieces(Them));
b2 &= target;
}
- if (Type == QUIET_CHECKS)
+ if (Type == QUIET_CHECKS && pos.count<KING>(Them))
{
- Square ksq = pos.square<KING>(Them);
-
- b1 &= pos.attacks_from<PAWN>(Them, ksq);
- b2 &= pos.attacks_from<PAWN>(Them, ksq);
+ b1 &= pos.attacks_from<PAWN>(ksq, Them);
+ b2 &= pos.attacks_from<PAWN>(ksq, Them);
// Add pawn pushes which give discovered check. This is possible only
// if the pawn is not on the same file as the enemy king, because we
constexpr bool Checks = Type == QUIET_CHECKS; // Reduce template instantations
moveList = generate_pawn_moves<Us, Type>(pos, moveList, target);
- moveList = generate_moves<KNIGHT, Checks>(pos, moveList, Us, target);
- moveList = generate_moves<BISHOP, Checks>(pos, moveList, Us, target);
- moveList = generate_moves< ROOK, Checks>(pos, moveList, Us, target);
- moveList = generate_moves< QUEEN, Checks>(pos, moveList, Us, target);
-
- if (Type != QUIET_CHECKS && Type != EVASIONS)
+ for (PieceType pt = PieceType(PAWN + 1); pt < KING; ++pt)
+ moveList = generate_moves<Checks>(pos, moveList, Us, pt, target);
+ // generate drops
+ if (pos.piece_drops() && Type != CAPTURES && pos.count_in_hand(Us, ALL_PIECES))
+ for (PieceType pt = PAWN; pt <= KING; ++pt)
+ moveList = generate_drops<Us, Checks>(pos, moveList, pt, target & ~pos.pieces(~Us));
+
+ if (Type != QUIET_CHECKS && Type != EVASIONS && pos.count<KING>(Us))
{
Square ksq = pos.square<KING>(Us);
- Bitboard b = pos.attacks_from<KING>(Us, ksq) & target;
- Bitboard b = pos.attacks_from<KING>(ksq) & target;
++ Bitboard b = pos.attacks_from<KING>(ksq, Us) & target;
while (b)
- *moveList++ = make_move(ksq, pop_lsb(&b));
+ moveList = make_move_and_gating<NORMAL>(pos, moveList, Us, ksq, pop_lsb(&b));
if (Type != CAPTURES && pos.can_castle(CastlingRights(OO | OOO)))
{
Color us = pos.side_to_move();
Square ksq = pos.square<KING>(us);
Bitboard sliderAttacks = 0;
- Bitboard sliders = pos.checkers() & ~pos.pieces(KNIGHT, PAWN);
+ Bitboard sliders = pos.checkers();
+
+ // Consider all evasion moves for special pieces
+ if (sliders & (pos.pieces(CANNON, BANNER) | pos.pieces(HORSE, ELEPHANT)))
+ {
+ Bitboard target = pos.board_bb() & ~pos.pieces(us);
- Bitboard b = pos.attacks_from<KING>(us, ksq) & target;
++ Bitboard b = pos.attacks_from<KING>(ksq, us) & target;
+ while (b)
+ moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
+ return us == WHITE ? generate_all<WHITE, EVASIONS>(pos, moveList, target)
+ : generate_all<BLACK, EVASIONS>(pos, moveList, target);
+ }
// Find all the squares attacked by slider checkers. We will remove them from
// the king evasions in order to skip known illegal moves, which avoids any
}
// Generate evasions for king, capture and non capture moves
- Bitboard b = pos.attacks_from<KING>(us, ksq) & ~pos.pieces(us) & ~sliderAttacks;
- Bitboard b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
++ Bitboard b = pos.attacks_from<KING>(ksq, us) & ~pos.pieces(us) & ~sliderAttacks;
while (b)
- *moveList++ = make_move(ksq, pop_lsb(&b));
+ moveList = make_move_and_gating<NORMAL>(pos, moveList, us, ksq, pop_lsb(&b));
if (more_than_one(pos.checkers()))
return moveList; // Double check, only a king move can save the day
// A pawn is backward when it is behind all pawns of the same color on
// the adjacent files and cannot safely advance.
- backward = !(neighbours & forward_ranks_bb(Them, s + Up))
- && (leverPush | blocked);
+ backward = is_ok(s + Up)
+ && !(neighbours & forward_ranks_bb(Them, s + Up))
- && (stoppers & (leverPush | blocked));
++ && (stoppers & blocked);
// Compute additional span if pawn is not backward nor blocked
if (!backward && !blocked)
{
// We have already handled promotion moves, so destination
// cannot be on the 8th/1st rank.
- if ((Rank8BB | Rank1BB) & to)
+ if (mandatory_pawn_promotion() && rank_of(to) == relative_rank(us, promotion_rank(), max_rank()))
return false;
- if ( !(attacks_from<PAWN>(us, from) & pieces(~us) & to) // Not a capture
+ if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture
&& !((from + pawn_push(us) == to) && empty(to)) // Not a single push
&& !( (from + 2 * pawn_push(us) == to) // Not a double push
- && (rank_of(from) == relative_rank(us, RANK_2))
+ && (rank_of(from) == relative_rank(us, double_step_rank(), max_rank())
+ || (first_rank_double_steps() && rank_of(from) == relative_rank(us, RANK_1, max_rank())))
&& empty(to)
- && empty(to - pawn_push(us))))
+ && empty(to - pawn_push(us))
+ && double_step_enabled()))
return false;
}
- else if (!(attacks_from(type_of(pc), from) & to))
+ else if (!((capture(m) ? attacks_from(us, type_of(pc), from) : moves_from(us, type_of(pc), from)) & to))
return false;
// Evasions generator already takes care to avoid some kind of illegal moves
if (type_of(pc) == PAWN)
{
// Set en-passant square if the moved pawn can be captured
- if ( (int(to) ^ int(from)) == 16
+ if ( std::abs(int(to) - int(from)) == 2 * NORTH
+ && relative_rank(us, rank_of(from), max_rank()) == double_step_rank()
- && (attacks_from<PAWN>(us, to - pawn_push(us)) & pieces(them, PAWN)))
+ && (attacks_from<PAWN>(to - pawn_push(us), us) & pieces(them, PAWN)))
{
st->epSquare = to - pawn_push(us);
k ^= Zobrist::enpassant[file_of(st->epSquare)];
// Attacks to/from a given square
Bitboard attackers_to(Square s) const;
+ Bitboard attackers_to(Square s, Color c) const;
Bitboard attackers_to(Square s, Bitboard occupied) const;
- Bitboard attacks_from(PieceType pt, Square s) const;
- template<PieceType> Bitboard attacks_from(Square s) const;
+ Bitboard attackers_to(Square s, Bitboard occupied, Color c) const;
+ Bitboard attacks_from(Color c, PieceType pt, Square s) const;
- template<PieceType> Bitboard attacks_from(Color c, Square s) const;
+ template<PieceType> Bitboard attacks_from(Square s, Color c) const;
- Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const;
+ Bitboard moves_from(Color c, PieceType pt, Square s) const;
+ Bitboard slider_blockers(Bitboard sliders, Square s, Bitboard& pinners, Color c) const;
// Properties of moves
bool legal(Move m) const;
}
template<PieceType Pt>
- inline Bitboard Position::attacks_from(Color c, Square s) const {
-inline Bitboard Position::attacks_from(Square s) const {
- static_assert(Pt != PAWN, "Pawn attacks need color");
-
- return Pt == BISHOP || Pt == ROOK ? attacks_bb<Pt>(s, byTypeBB[ALL_PIECES])
- : Pt == QUEEN ? attacks_from<ROOK>(s) | attacks_from<BISHOP>(s)
- : PseudoAttacks[Pt][s];
++inline Bitboard Position::attacks_from(Square s, Color c) const {
+ return attacks_bb(c, Pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, Pt);
}
-template<>
-inline Bitboard Position::attacks_from<PAWN>(Square s, Color c) const {
- return PawnAttacks[c][s];
+inline Bitboard Position::attacks_from(Color c, PieceType pt, Square s) const {
+ return attacks_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
}
-inline Bitboard Position::attacks_from(PieceType pt, Square s) const {
- return attacks_bb(pt, s, byTypeBB[ALL_PIECES]);
+inline Bitboard Position::moves_from(Color c, PieceType pt, Square s) const {
+ return moves_bb(c, pt, s, byTypeBB[ALL_PIECES]) & board_bb(c, pt);
}
inline Bitboard Position::attackers_to(Square s) const {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
- Bitboard fromTo = square_bb(from) ^ square_bb(to);
- Bitboard fromTo = from | to;
++ Bitboard fromTo = square_bb(from) ^ to; // from == to needs to cancel out
byTypeBB[ALL_PIECES] ^= fromTo;
byTypeBB[type_of(pc)] ^= fromTo;
byColorBB[color_of(pc)] ^= fromTo;
{ S(-4,-38), S(10,-18), S( 6,-12), S( 8, 1) },
{ S(-5,-50), S( 6,-27), S(10,-24), S( 8, -8) },
{ S(-2,-75), S(-2,-52), S( 1,-43), S(-2,-36) }
- },
- { // King
+ }
+};
+
+constexpr Score KingBonus[RANK_NB][int(FILE_NB) / 2] = {
- { S(272, 0), S(325, 41), S(273, 80), S(190, 93) },
- { S(277, 57), S(305, 98), S(241,138), S(183,131) },
- { S(198, 86), S(253,138), S(168,165), S(120,173) },
- { S(169,103), S(191,152), S(136,168), S(108,169) },
- { S(145, 98), S(176,166), S(112,197), S( 69,194) },
- { S(122, 87), S(159,164), S( 85,174), S( 36,189) },
- { S( 87, 40), S(120, 99), S( 64,128), S( 25,141) },
- { S( 64, 5), S( 87, 60), S( 49, 75), S( 0, 75) }
+ { S(271, 1), S(327, 45), S(270, 85), S(192, 76) },
+ { S(278, 53), S(303,100), S(230,133), S(174,135) },
+ { S(195, 88), S(258,130), S(169,169), S(120,175) },
+ { S(164,103), S(190,156), S(138,172), S( 98,172) },
+ { S(154, 96), S(179,166), S(105,199), S( 70,199) },
+ { S(123, 92), S(145,172), S( 81,184), S( 31,191) },
+ { S( 88, 47), S(120,121), S( 65,116), S( 33,131) },
+ { S( 59, 11), S( 89, 59), S( 45, 73), S( -1, 78) }
- }
};
constexpr Score PBonus[RANK_NB][FILE_NB] =
// search to overwrite a previous full search TT value, so we use a different
// position key in case of an excluded move.
excludedMove = ss->excludedMove;
- posKey = pos.key() ^ Key(excludedMove << 16); // Isn't a very good hash
+ posKey = pos.key() ^ (Key(excludedMove) << 16); // Isn't a very good hash
tte = TT.probe(posKey, ttHit);
- ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
+ ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = rootNode ? thisThread->rootMoves[thisThread->pvIdx].pv[0]
: ttHit ? tte->move() : MOVE_NONE;
ttPv = PvNode || (ttHit && tte->is_pv());
}
// Step 11. Internal iterative deepening (~2 Elo)
- if (depth >= 7 && !ttMove)
+ if (depth >= (7 - 2 * pos.captures_to_hand()) && !ttMove)
{
- search<NT>(pos, ss, alpha, beta, depth - 7, cutNode);
+ search<NT>(pos, ss, alpha, beta, depth - (7 - 2 * pos.captures_to_hand()), cutNode);
tte = TT.probe(posKey, ttHit);
- ttValue = ttHit ? value_from_tt(tte->value(), ss->ply) : VALUE_NONE;
+ ttValue = ttHit ? value_from_tt(tte->value(), ss->ply, pos.rule50_count()) : VALUE_NONE;
ttMove = ttHit ? tte->move() : MOVE_NONE;
}
if ( !captureOrPromotion
&& !givesCheck
- && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg))
+ && (!pos.must_capture() || !pos.attackers_to(to_sq(move), ~us))
- && (!pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg || pos.count<ALL_PIECES>(us) == pos.count<PAWN>(us)))
++ && (!PvNode || !pos.advanced_pawn_push(move) || pos.non_pawn_material(~us) > BishopValueMg || pos.count<ALL_PIECES>(us) == pos.count<PAWN>(us)))
{
- // Move count based pruning
- if (moveCountPruning)
- continue;
-
// Reduced depth of the next LMR search
int lmrDepth = std::max(newDepth - reduction(improving, depth, moveCount), 0);
if (!moveCount)
bestValue = excludedMove ? alpha
- : inCheck ? mated_in(ss->ply) : VALUE_DRAW;
+ : inCheck ? pos.checkmate_value(ss->ply) : pos.stalemate_value(ss->ply);
- else if (bestMove)
- {
- // Quiet best move: update move sorting heuristics
- if (!pos.capture_or_promotion(bestMove))
- update_quiet_stats(pos, ss, bestMove, quietsSearched, quietCount,
- stat_bonus(depth + (bestValue > beta + PawnValueMg)));
-
- update_capture_stats(pos, bestMove, capturesSearched, captureCount, stat_bonus(depth + 1));
- // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted
- if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0]))
- && !priorCapture)
- update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + 1));
+ else if (bestMove)
+ update_all_stats(pos, ss, bestMove, bestValue, beta, prevSq,
+ quietsSearched, quietCount, capturesSearched, captureCount, depth);
- }
// Bonus for prior countermove that caused the fail low
else if ( (depth >= 3 || PvNode)
&& !priorCapture)
}
- // update_continuation_histories() updates histories of the move pairs formed
- // by moves at ply -1, -2, and -4 with current move.
+ // update_all_stats() updates stats at the end of search() when a bestMove is found
- void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
+ void update_all_stats(const Position& pos, Stack* ss, Move bestMove, Value bestValue, Value beta, Square prevSq,
+ Move* quietsSearched, int quietCount, Move* capturesSearched, int captureCount, Depth depth) {
- for (int i : {1, 2, 4, 6})
- if (is_ok((ss-i)->currentMove))
- (*(ss-i)->continuationHistory)[history_slot(pc)][to] << bonus;
- }
+ int bonus1, bonus2;
+ Color us = pos.side_to_move();
+ Thread* thisThread = pos.this_thread();
+ CapturePieceToHistory& captureHistory = thisThread->captureHistory;
+ Piece moved_piece = pos.moved_piece(bestMove);
+ PieceType captured = type_of(pos.piece_on(to_sq(bestMove)));
+
+ bonus1 = stat_bonus(depth + 1);
+ bonus2 = bestValue > beta + PawnValueMg ? bonus1 // larger bonus
+ : stat_bonus(depth); // smaller bonus
+
+ if (!pos.capture_or_promotion(bestMove))
+ {
+ update_quiet_stats(pos, ss, bestMove, bonus2);
+
+ // Decrease all the non-best quiet moves
+ for (int i = 0; i < quietCount; ++i)
+ {
+ thisThread->mainHistory[us][from_to(quietsSearched[i])] << -bonus2;
+ update_continuation_histories(ss, pos.moved_piece(quietsSearched[i]), to_sq(quietsSearched[i]), -bonus2);
+ }
+ }
+ else
+ captureHistory[moved_piece][to_sq(bestMove)][captured] << bonus1;
+ // Extra penalty for a quiet TT or main killer move in previous ply when it gets refuted
+ if ( ((ss-1)->moveCount == 1 || ((ss-1)->currentMove == (ss-1)->killers[0]))
+ && !pos.captured_piece())
+ update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -bonus1);
- // update_capture_stats() updates move sorting heuristics when a new capture best move is found
+ // Decrease all the non-best capture moves
+ for (int i = 0; i < captureCount; ++i)
+ {
+ moved_piece = pos.moved_piece(capturesSearched[i]);
+ captured = type_of(pos.piece_on(to_sq(capturesSearched[i])));
+ captureHistory[moved_piece][to_sq(capturesSearched[i])][captured] << -bonus1;
+ }
+ }
- void update_capture_stats(const Position& pos, Move move,
- Move* captures, int captureCount, int bonus) {
- CapturePieceToHistory& captureHistory = pos.this_thread()->captureHistory;
- Piece moved_piece = pos.moved_piece(move);
- PieceType captured = type_of(pos.piece_on(to_sq(move)));
+ // update_continuation_histories() updates histories of the move pairs formed
+ // by moves at ply -1, -2, and -4 with current move.
- if (pos.capture_or_promotion(move))
- captureHistory[moved_piece][to_sq(move)][captured] << bonus;
+ void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
- // Decrease all the other played capture moves
- for (int i = 0; i < captureCount; ++i)
- {
- moved_piece = pos.moved_piece(captures[i]);
- captured = type_of(pos.piece_on(to_sq(captures[i])));
- captureHistory[moved_piece][to_sq(captures[i])][captured] << -bonus;
- }
+ for (int i : {1, 2, 4, 6})
+ if (is_ok((ss-i)->currentMove))
- (*(ss-i)->continuationHistory)[pc][to] << bonus;
++ (*(ss-i)->continuationHistory)[history_slot(pc)][to] << bonus;
}
for (Thread* th : *this)
{
- th->shuffleExts = th->nodes = th->tbHits = th->nmpMinPly = 0;
+ th->nodes = th->tbHits = th->nmpMinPly = 0;
th->rootDepth = th->completedDepth = 0;
th->rootMoves = rootMoves;
- th->rootPos.set(pos.fen(), pos.is_chess960(), &setupStates->back(), th);
+ th->rootPos.set(pos.variant(), pos.fen(), pos.is_chess960(), &setupStates->back(), th);
}
setupStates->back() = tmp;
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + 2 * MAX_PLY,
PawnValueMg = 128, PawnValueEg = 213,
- KnightValueMg = 782, KnightValueEg = 865,
- BishopValueMg = 830, BishopValueEg = 918,
- RookValueMg = 1289, RookValueEg = 1378,
- QueenValueMg = 2529, QueenValueEg = 2687,
+ KnightValueMg = 781, KnightValueEg = 854,
+ BishopValueMg = 825, BishopValueEg = 915,
+ RookValueMg = 1276, RookValueEg = 1380,
+ QueenValueMg = 2538, QueenValueEg = 2682,
+ FersValueMg = 420, FersValueEg = 450,
+ AlfilValueMg = 330, AlfilValueEg = 300,
+ FersAlfilValueMg = 600, FersAlfilValueEg = 600,
+ SilverValueMg = 630, SilverValueEg = 630,
+ AiwokValueMg = 2500, AiwokValueEg = 2500,
+ BersValueMg = 2000, BersValueEg = 2000,
+ ArchbishopValueMg = 2200, ArchbishopValueEg = 2200,
+ ChancellorValueMg = 2300, ChancellorValueEg = 2600,
+ AmazonValueMg = 3000, AmazonValueEg = 3000,
+ KnibisValueMg = 1100, KnibisValueEg = 1200,
+ BiskniValueMg = 750, BiskniValueEg = 700,
+ KnirooValueMg = 900, KnirooValueEg = 900,
+ RookniValueMg = 900, RookniValueEg = 900,
+ ShogiPawnValueMg = 90, ShogiPawnValueEg = 100,
+ LanceValueMg = 350, LanceValueEg = 250,
+ ShogiKnightValueMg = 350, ShogiKnightValueEg = 300,
+ EuroShogiKnightValueMg = 400, EuroShogiKnightValueEg = 400,
+ GoldValueMg = 640, GoldValueEg = 640,
+ DragonHorseValueMg = 1500, DragonHorseValueEg = 1500,
+ ClobberPieceValueMg = 300, ClobberPieceValueEg = 300,
+ BreakthroughPieceValueMg = 300, BreakthroughPieceValueEg = 300,
+ ImmobilePieceValueMg = 100, ImmobilePieceValueEg = 100,
+ CannonPieceValueMg = 800, CannonPieceValueEg = 700,
+ SoldierValueMg = 150, SoldierValueEg = 300,
+ HorseValueMg = 500, HorseValueEg = 800,
+ ElephantValueMg = 300, ElephantValueEg = 300,
+ BannerValueMg = 3500, BannerValueEg = 3500,
+ WazirValueMg = 400, WazirValueEg = 400,
+ CommonerValueMg = 700, CommonerValueEg = 900,
+ CentaurValueMg = 1500, CentaurValueEg = 1500,
MidgameLimit = 15258, EndgameLimit = 3915
};