constexpr Value SpaceThreshold = Value(12222);
// KingAttackWeights[PieceType] contains king attack weights by piece type
- constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10 };
+ constexpr int KingAttackWeights[PIECE_TYPE_NB] = { 0, 0, 81, 52, 44, 10, 40 };
- // Penalties for enemy's safe checks
- constexpr int QueenSafeCheck = 772;
- constexpr int RookSafeCheck = 1084;
- constexpr int BishopSafeCheck = 645;
- constexpr int KnightSafeCheck = 792;
-
- constexpr int OtherSafeCheck = 600;
+ // SafeCheck[PieceType][single/multiple] contains safe check bonus by piece type,
+ // higher if multiple safe checks are possible for that piece type.
+ constexpr int SafeCheck[][2] = {
- {}, {}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119}
++ {}, {600, 600}, {792, 1283}, {645, 967}, {1084, 1897}, {772, 1119}, {600, 900}
+ };
#define S(mg, eg) make_score(mg, eg)
S( 77,147), S( 79,150), S( 93,151), S(108,168), S(108,168), S(108,171),
S(110,182), S(114,182), S(114,192), S(116,219) }
};
+ constexpr Score MaxMobility = S(150, 200);
+ constexpr Score DropMobility = S(10, 10);
+ // KingProtector[knight/bishop] contains penalty for each distance unit to own king
+ constexpr Score KingProtector[] = { S(8, 9), S(6, 9) };
+
+ // Outpost[knight/bishop] contains bonuses for each knight or bishop occupying a
+ // pawn protected square on rank 4 to 6 which is also safe from a pawn attack.
+ constexpr Score Outpost[] = { S(56, 36), S(30, 23) };
+
+ // PassedRank[Rank] contains a bonus according to the rank of a passed pawn
+ 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)
+ };
+
// RookOnFile[semiopen/open] contains bonuses for each rook when there is
// no (friendly) pawn on the rook file.
constexpr Score RookOnFile[] = { S(19, 7), S(48, 29) };
constexpr Score WeakQueen = S( 56, 15);
++ // Variant and fairy piece bonuses
++ constexpr Score KingProximity = S(2, 4);
++ constexpr Score EndgameKingProximity = S(0, 10);
++ constexpr Score ConnectedSoldier = S(20, 20);
++
++ constexpr int VirtualCheck = 600;
++
#undef S
// Evaluation class computes and stores attacks tables and other working data
if (b & kingRing[Them])
{
kingAttackersCount[Us]++;
- kingAttackersWeight[Us] += KingAttackWeights[std::min(int(Pt), QUEEN + 1)];
- kingAttackersWeight[Us] += KingAttackWeights[Pt];
++ kingAttackersWeight[Us] += KingAttackWeights[std::min(Pt, FAIRY_PIECES)];
kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
}
else if (Pt == BISHOP && (attacks_bb<BISHOP>(s, pos.pieces(PAWN)) & kingRing[Them]))
score += BishopOnKingRing;
+ if (Pt > QUEEN)
+ b = (b & pos.pieces()) | (pos.moves_from(Us, Pt, s) & ~pos.pieces() & pos.board_bb());
+
int mob = popcount(b & mobilityArea[Us]);
- mobility[Us] += MobilityBonus[Pt - 2][mob];
+ if (Pt <= QUEEN)
+ mobility[Us] += MobilityBonus[Pt - 2][mob];
+ else
+ mobility[Us] += MaxMobility * (mob - 2) / (8 + mob);
+
+ // Piece promotion bonus
+ if (pos.promoted_piece_type(Pt) != NO_PIECE_TYPE)
+ {
+ if (promotion_zone_bb(Us, pos.promotion_rank(), pos.max_rank()) & (b | s))
+ score += make_score(PieceValue[MG][pos.promoted_piece_type(Pt)] - PieceValue[MG][Pt],
+ PieceValue[EG][pos.promoted_piece_type(Pt)] - PieceValue[EG][Pt]) / 10;
+ }
+ else if (pos.piece_demotion() && pos.unpromoted_piece_on(s))
+ score -= make_score(PieceValue[MG][Pt] - PieceValue[MG][pos.unpromoted_piece_on(s)],
+ PieceValue[EG][Pt] - PieceValue[EG][pos.unpromoted_piece_on(s)]) / 4;
+ else if (pos.captures_to_hand() && pos.unpromoted_piece_on(s))
+ score += make_score(PieceValue[MG][Pt] - PieceValue[MG][pos.unpromoted_piece_on(s)],
+ PieceValue[EG][Pt] - PieceValue[EG][pos.unpromoted_piece_on(s)]) / 8;
+
+ // Penalty if the piece is far from the kings in drop variants
+ if ((pos.captures_to_hand() || pos.two_boards()) && pos.count<KING>(Them) && pos.count<KING>(Us))
+ score -= KingProximity * distance(s, pos.square<KING>(Us)) * distance(s, pos.square<KING>(Them));
+
+ else if (pos.count<KING>(Us) && (Pt == FERS || Pt == SILVER))
+ score -= EndgameKingProximity * (distance(s, pos.square<KING>(Us)) - 2);
+
+ if (Pt == SOLDIER && (pos.pieces(Us, SOLDIER) & rank_bb(s) & adjacent_files_bb(s)))
- score += make_score(20, 20);
++ score += ConnectedSoldier;
if (Pt == BISHOP || Pt == KNIGHT)
{
score += MinorBehindPawn;
// Penalty if the piece is far from the king
+ if (pos.count<KING>(Us))
- score -= (Pt == KNIGHT ? KnightKingProtector
- : BishopKingProtector) * distance(pos.square<KING>(Us), s);
+ score -= KingProtector[Pt == BISHOP] * distance(pos.square<KING>(Us), s);
if (Pt == BISHOP)
{
return score;
}
+ // Evaluation::hand() scores pieces of a given color and type in hand
+ template<Tracing T> template<Color Us>
+ Score Evaluation<T>::hand(PieceType pt) {
+
+ constexpr Color Them = ~Us;
+
+ Score score = SCORE_ZERO;
+
+ if (pos.count_in_hand(Us, pt))
+ {
+ Bitboard b = pos.drop_region(Us, pt) & ~pos.pieces() & (~attackedBy2[Them] | attackedBy[Us][ALL_PIECES]);
+ if ((b & kingRing[Them]) && pt != SHOGI_PAWN)
+ {
+ kingAttackersCountInHand[Us] += pos.count_in_hand(Us, pt);
- kingAttackersWeightInHand[Us] += KingAttackWeights[std::min(int(pt), QUEEN + 1)] * pos.count_in_hand(Us, pt);
++ kingAttackersWeightInHand[Us] += KingAttackWeights[std::min(pt, FAIRY_PIECES)] * pos.count_in_hand(Us, pt);
+ kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
+ }
+ Bitboard theirHalf = pos.board_bb() & ~forward_ranks_bb(Them, relative_rank(Them, Rank((pos.max_rank() - 1) / 2), pos.max_rank()));
+ mobility[Us] += DropMobility * popcount(b & theirHalf & ~attackedBy[Them][ALL_PIECES]);
+ if (pos.promoted_piece_type(pt) != NO_PIECE_TYPE && pos.drop_promoted())
+ score += make_score(std::max(PieceValue[MG][pos.promoted_piece_type(pt)] - PieceValue[MG][pt], VALUE_ZERO),
+ std::max(PieceValue[EG][pos.promoted_piece_type(pt)] - PieceValue[EG][pt], VALUE_ZERO)) / 4 * pos.count_in_hand(Us, pt);
+ if (pos.enclosing_drop())
+ mobility[Us] += make_score(500, 500) * popcount(b);
+
+ // Reduce score if there is a deficit of gates
+ if (pos.seirawan_gating() && !pos.piece_drops() && pos.count_in_hand(Us, ALL_PIECES) > popcount(pos.gates(Us)))
+ score -= make_score(200, 900) / pos.count_in_hand(Us, ALL_PIECES) * (pos.count_in_hand(Us, ALL_PIECES) - popcount(pos.gates(Us)));
+
+ if (pt == SHOGI_PAWN && !pos.shogi_doubled_pawn())
+ score -= make_score(50, 20) * std::max(pos.count_with_hand(Us, SHOGI_PAWN) - pos.max_file() - 1, 0);
+ }
+
+ return score;
+ }
// Evaluation::king() assigns bonuses and penalties to a king of a given color
b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
- // Enemy rooks checks
- rookChecks = b1 & attackedBy[Them][ROOK] & safe;
- if (rookChecks)
- kingDanger += SafeCheck[ROOK][more_than_one(rookChecks)];
- else
- unsafeChecks |= b1 & attackedBy[Them][ROOK];
-
- // Enemy queen safe checks: count them only if the checks are from squares from
- // which opponent cannot give a rook check, because rook checks are more valuable.
- queenChecks = (b1 | b2) & attackedBy[Them][QUEEN] & safe
- & ~(attackedBy[Us][QUEEN] | rookChecks);
- if (queenChecks)
- kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)];
-
- // Enemy bishops checks: count them only if they are from squares from which
- // opponent cannot give a queen check, because queen checks are more valuable.
- bishopChecks = b2 & attackedBy[Them][BISHOP] & safe
- & ~queenChecks;
- if (bishopChecks)
- kingDanger += SafeCheck[BISHOP][more_than_one(bishopChecks)];
+ std::function <Bitboard (Color, PieceType)> get_attacks = [this](Color c, PieceType pt) {
+ return attackedBy[c][pt] | (pos.piece_drops() && pos.count_in_hand(c, pt) ? pos.drop_region(c, pt) & ~pos.pieces() : Bitboard(0));
+ };
+ for (PieceType pt : pos.piece_types())
+ {
+ switch (pt)
+ {
+ case QUEEN:
+ // Enemy queen safe checks: we count them only if they are from squares from
+ // which we can't give a rook check, because rook checks are more valuable.
+ queenChecks = (b1 | b2)
+ & get_attacks(Them, QUEEN)
+ & pos.board_bb()
+ & safe
+ & ~attackedBy[Us][QUEEN]
+ & ~(b1 & attackedBy[Them][ROOK]);
+
+ if (queenChecks)
- kingDanger += more_than_one(queenChecks) ? QueenSafeCheck * 145/100
- : QueenSafeCheck;
++ kingDanger += SafeCheck[QUEEN][more_than_one(queenChecks)];
+ break;
+ case ROOK:
+ case BISHOP:
+ case KNIGHT:
+ knightChecks = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & pos.board_bb();
+ if (knightChecks & safe)
- kingDanger += pt == ROOK ? RookSafeCheck * (more_than_one(knightChecks & safe) ? 175 : 100) / 100
- : pt == BISHOP ? BishopSafeCheck * (more_than_one(knightChecks & safe) ? 150 : 100) / 100
- : KnightSafeCheck * (more_than_one(knightChecks & safe) ? 162 : 100) / 100;
++ kingDanger += SafeCheck[pt][more_than_one(knightChecks & safe)];
+ else
+ unsafeChecks |= knightChecks;
+ break;
+ case PAWN:
+ if (pos.piece_drops() && pos.count_in_hand(Them, pt))
+ {
+ pawnChecks = attacks_bb(Us, pt, ksq, pos.pieces()) & ~pos.pieces() & pos.board_bb();
+ if (pawnChecks & safe)
- kingDanger += OtherSafeCheck;
++ kingDanger += SafeCheck[PAWN][more_than_one(pawnChecks & safe)];
+ else
+ unsafeChecks |= pawnChecks;
+ }
+ break;
+ case SHOGI_PAWN:
+ case KING:
+ break;
+ default:
+ otherChecks = attacks_bb(Us, pt, ksq, pos.pieces()) & get_attacks(Them, pt) & pos.board_bb();
+ if (otherChecks & safe)
- kingDanger += OtherSafeCheck * (more_than_one(otherChecks & safe) ? 3 : 2) / 2;
++ kingDanger += SafeCheck[FAIRY_PIECES][more_than_one(otherChecks & safe)];
+ else
+ unsafeChecks |= otherChecks;
+ }
+ }
- else
- unsafeChecks |= b2 & attackedBy[Them][BISHOP];
+ // Virtual piece drops
+ if (pos.two_boards() && pos.piece_drops())
+ {
+ for (PieceType pt : pos.piece_types())
+ if (!pos.count_in_hand(Them, pt) && (attacks_bb(Us, pt, ksq, pos.pieces()) & safe & pos.drop_region(Them, pt) & ~pos.pieces()))
+ {
- kingDanger += OtherSafeCheck * 500 / (500 + PieceValue[MG][pt]);
++ kingDanger += VirtualCheck * 500 / (500 + PieceValue[MG][pt]);
+ // Presumably a mate threat
+ if (!(attackedBy[Us][KING] & ~(attackedBy[Them][ALL_PIECES] | pos.pieces(Us))))
+ kingDanger += 2000;
+ }
+ }
- // Enemy knights checks
- knightChecks = attacks_bb<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
- if (knightChecks & safe)
- kingDanger += SafeCheck[KNIGHT][more_than_one(knightChecks & safe)];
- else
- unsafeChecks |= knightChecks;
+ if (pos.check_counting())
+ kingDanger += kingDanger * 7 / (3 + pos.checks_remaining(Them));
+
+ Square s = file_of(ksq) == FILE_A ? ksq + EAST : file_of(ksq) == pos.max_file() ? ksq + WEST : ksq;
+ Bitboard kingFlank = pos.max_file() == FILE_H ? KingFlank[file_of(ksq)] : file_bb(s) | adjacent_files_bb(s);
// Find the squares that opponent attacks in our king flank, the squares
// which they attack twice in that flank, and the squares that we defend.
- b1 = attackedBy[Them][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
+ b1 = attackedBy[Them][ALL_PIECES] & kingFlank & Camp;
b2 = b1 & attackedBy2[Them];
- b3 = attackedBy[Us][ALL_PIECES] & KingFlank[file_of(ksq)] & Camp;
+ b3 = attackedBy[Us][ALL_PIECES] & kingFlank & Camp;
- int kingFlankAttack = popcount(b1) + popcount(b2);
+ int kingFlankAttack = popcount(b1) + popcount(b2);
int kingFlankDefense = popcount(b3);
kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]