- os: osx
compiler: clang
env:
- - COMPILER=clang++ V='Apple LLVM 6.0' # Apple LLVM version 6.0 (clang-600.0.54) (based on LLVM 3.5svn)
+ - COMPILER=clang++ V='Apple LLVM 9.4.1' # Apple LLVM version 9.1.0 (clang-902.0.39.2)
- COMP=clang
-branches:
- only:
- - master
-
before_script:
- cd src
-## Overview
-
-[](https://travis-ci.org/official-stockfish/Stockfish)
-[](https://ci.appveyor.com/project/mcostalba/stockfish)
+## Fairy-Stockfish
+
+[](https://travis-ci.org/ianfab/Fairy-Stockfish)
+[](https://ci.appveyor.com/project/ianfab/Fairy-Stockfish)
+
+Fairy-Stockfish is a Stockfish fork designed for the support of (fairy) chess variants and to make the addition/configuration of new variants as simple and flexible as possible. The goal of the project is to create an engine supporting a large variety of chess-like games, equipped with the powerful search of Stockfish. It is complementary to Stockfish forks more specialized for certain chess variants, such as [multi-variant Stockfish](https://github.com/ddugovic/Stockfish), [Seirawan-Stockfish](https://github.com/ianfab/Seirawan-Stockfish), [Makruk-Stockfish](https://github.com/ianfab/Makruk-Stockfish), etc., supporting more variants with the tradeoff of slightly lower performance.
+
+Besides chess, the currently supported games are:
+
+**Regional and historical games**
+- [Shatranj](https://en.wikipedia.org/wiki/Shatranj), [Courier](https://en.wikipedia.org/wiki/Courier_chess)
+- [Makruk](https://en.wikipedia.org/wiki/Makruk), [ASEAN](http://hgm.nubati.net/rules/ASEAN.html), Ai-Wok
+- [Sittuyin](https://en.wikipedia.org/wiki/Sittuyin)
+- [Shatar](https://en.wikipedia.org/wiki/Shatar), [Jeson Mor](https://en.wikipedia.org/wiki/Jeson_Mor)
+- [Shogi](https://en.wikipedia.org/wiki/Shogi)
+
+**Chess variants**
+- [Capablanca](https://en.wikipedia.org/wiki/Capablanca_Chess), [Janus](https://en.wikipedia.org/wiki/Janus_Chess), [Embassy](https://en.wikipedia.org/wiki/Embassy_Chess)
+- [Chess960](https://en.wikipedia.org/wiki/Chess960), [Placement/Pre-Chess](https://www.chessvariants.com/link/placement-chess)
+- [Crazyhouse](https://en.wikipedia.org/wiki/Crazyhouse), [Loop](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Chessgi](https://en.wikipedia.org/wiki/Crazyhouse#Variations), [Pocket Knight](http://www.chessvariants.com/other.dir/pocket.html)
+- [Amazon](https://en.wikipedia.org/wiki/Amazon_(chess)), [Chigorin](https://en.wikipedia.org/wiki/Chigorin_Chess), [Almost chess](https://en.wikipedia.org/wiki/Almost_Chess), [Hoppel-Poppel](http://www.chessvariants.com/diffmove.dir/hoppel-poppel.html)
+- [Antichess](https://lichess.org/variant/antichess), [Giveaway](http://www.chessvariants.com/diffobjective.dir/giveaway.old.html), [Losers](https://www.chessclub.com/help/Wild17), [Codrus](http://www.binnewirtz.com/Schlagschach1.htm)
+- [Extinction](https://en.wikipedia.org/wiki/Extinction_chess), [Kinglet](https://en.wikipedia.org/wiki/V._R._Parton#Kinglet_Chess)
+- [King of the Hill](https://en.wikipedia.org/wiki/King_of_the_Hill_(chess)), [Racing Kings](https://en.wikipedia.org/wiki/V._R._Parton#Racing_Kings)
+- [Three-check](https://en.wikipedia.org/wiki/Three-check_chess), Five-check
+- [Los Alamos](https://en.wikipedia.org/wiki/Los_Alamos_chess)
+- [Horde](https://en.wikipedia.org/wiki/Dunsany%27s_Chess#Horde_Chess)
+
+**Shogi variants**
+- [Minishogi](https://en.wikipedia.org/wiki/Minishogi), [EuroShogi](https://en.wikipedia.org/wiki/EuroShogi), [Judkins shogi](https://en.wikipedia.org/wiki/Judkins_shogi)
+- [Kyoto shogi](https://en.wikipedia.org/wiki/Kyoto_shogi), [Microshogi](https://en.wikipedia.org/wiki/Micro_shogi)
+- [Dobutsu shogi](https://en.wikipedia.org/wiki/Dōbutsu_shōgi), [Goro goro shogi](https://en.wikipedia.org/wiki/D%C5%8Dbutsu_sh%C5%8Dgi#Variation)
+
+**Related games**
+- [Breakthrough](https://en.wikipedia.org/wiki/Breakthrough_(board_game))
+- [Clobber](https://en.wikipedia.org/wiki/Clobber)
+- [Connect4](https://en.wikipedia.org/wiki/Connect_Four), [Tic-Tac-Toe](https://en.wikipedia.org/wiki/Tic-tac-toe)
+
+See the [Fairy-Stockfish Wiki](https://github.com/ianfab/Fairy-Stockfish/wiki) for more info.
+
+## Stockfish
+### Overview
- Stockfish is a free UCI chess engine derived from Glaurung 2.1. It is
- not a complete chess program and requires some UCI-compatible GUI
- (e.g. XBoard with PolyGlot, eboard, Arena, Sigma Chess, Shredder, Chess
- Partner or Fritz) in order to be used comfortably. Read the
- documentation for your GUI of choice for information about how to use
+ [Stockfish](https://stockfishchess.org) is a free, powerful UCI chess engine
+ derived from Glaurung 2.1. It is not a complete chess program and requires a
+ UCI-compatible GUI (e.g. XBoard with PolyGlot, Scid, Cute Chess, eboard, Arena,
+ Sigma Chess, Shredder, Chess Partner or Fritz) in order to be used comfortably.
+ Read the documentation for your GUI of choice for information about how to use
Stockfish with it.
- This version of Stockfish supports up to 512 cores. The engine defaults
- to one search thread, so it is therefore recommended to inspect the value of
- the *Threads* UCI parameter, and to make sure it equals the number of CPU
- cores on your computer.
- This version of Stockfish has support for Syzygybases.
-
-
- ### Files
+ ## Files
This distribution of Stockfish consists of the following files:
return b & (b - 1);
}
+/// board_size_bb() returns a bitboard representing all the squares
+/// on a board with given size.
+
+inline Bitboard board_size_bb(File f, Rank r) {
+ return BoardSizeBB[f][r];
+}
+
inline bool opposite_colors(Square s1, Square s2) {
-
return bool(DarkSquares & s1) != bool(DarkSquares & s2);
}
// Table used to drive the king towards a corner square of the
// right color in KBN vs K endgames.
constexpr int PushToCorners[SQUARE_NB] = {
- 200, 190, 180, 170, 160, 150, 140, 130,
- 190, 180, 170, 160, 150, 140, 130, 140,
- 180, 170, 155, 140, 140, 125, 140, 150,
- 170, 160, 140, 120, 110, 140, 150, 160,
- 160, 150, 140, 110, 120, 140, 160, 170,
- 150, 140, 125, 140, 140, 155, 170, 180,
- 140, 130, 140, 150, 160, 170, 180, 190,
- 130, 140, 150, 160, 170, 180, 190, 200
+ 6400, 6080, 5760, 5440, 5120, 4800, 4480, 4160,
+ 6080, 5760, 5440, 5120, 4800, 4480, 4160, 4480,
+ 5760, 5440, 4960, 4480, 4480, 4000, 4480, 4800,
+ 5440, 5120, 4480, 3840, 3520, 4480, 4800, 5120,
+ 5120, 4800, 4480, 3520, 3840, 4480, 5120, 5440,
+ 4800, 4480, 4000, 4480, 4480, 4960, 5440, 5760,
+ 4480, 4160, 4480, 4800, 5120, 5440, 5760, 6080,
+ 4160, 4480, 4800, 5120, 5440, 5760, 6080, 6400
};
+ // Table used to drive the king towards the edge of the board
+ // in KSF vs K.
+ constexpr int PushToOpposingSideEdges[SQUARE_NB] = {
+ 30, 10, 5, 0, 0, 5, 10, 30,
+ 40, 20, 5, 0, 0, 5, 20, 40,
+ 50, 30, 10, 0, 0, 10, 30, 50,
+ 60, 40, 20, 10, 10, 20, 40, 60,
+ 70, 50, 30, 20, 20, 30, 50, 70,
+ 80, 60, 40, 30, 30, 40, 60, 80,
+ 90, 70, 60, 50, 50, 60, 70, 90,
+ 100, 90, 80, 70, 70, 80, 90, 100
+ };
+
// Tables used to drive a piece towards or away from another piece
- constexpr int PushClose[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
- constexpr int PushAway [8] = { 0, 5, 20, 40, 60, 80, 90, 100 };
+ constexpr int PushClose[FILE_NB] = { 0, 0, 100, 80, 60, 40, 20, 10 };
+ constexpr int PushAway [FILE_NB] = { 0, 5, 20, 40, 60, 80, 90, 100 };
// Pawn Rank based scaling factors used in KRPPKRP endgame
constexpr int KRPPKRPScaleFactors[RANK_NB] = { 0, 9, 10, 14, 21, 44, 0, 0 };
S(-30,-14), S(-9, -8), S( 0, 9), S( -1, 7)
};
+ // KingProximity contains a penalty according to distance from king
+ constexpr Score KingProximity = S(2, 2);
+
// Assorted bonuses and penalties
- constexpr Score BishopPawns = S( 3, 8);
- constexpr Score CloseEnemies = S( 7, 0);
+ constexpr Score BishopPawns = S( 3, 7);
+ constexpr Score CloseEnemies = S( 8, 0);
constexpr Score CorneredBishop = S( 50, 50);
- constexpr Score Hanging = S( 62, 34);
- constexpr Score KingProtector = S( 6, 7);
- constexpr Score KnightOnQueen = S( 20, 12);
- constexpr Score LongDiagonalBishop = S( 44, 0);
- constexpr Score MinorBehindPawn = S( 16, 0);
- constexpr Score PawnlessFlank = S( 18, 94);
- constexpr Score RestrictedPiece = S( 7, 6);
- constexpr Score RookOnPawn = S( 10, 28);
- constexpr Score SliderOnQueen = S( 49, 21);
- constexpr Score ThreatByKing = S( 21, 84);
- constexpr Score ThreatByPawnPush = S( 48, 42);
- constexpr Score ThreatByRank = S( 14, 3);
- constexpr Score ThreatBySafePawn = S(169, 99);
- constexpr Score TrappedRook = S( 98, 5);
- constexpr Score WeakQueen = S( 51, 10);
- constexpr Score WeakUnopposedPawn = S( 14, 20);
+ constexpr Score Hanging = S( 69, 36);
+ constexpr Score KingProtector = S( 7, 8);
+ constexpr Score KnightOnQueen = S( 16, 12);
+ constexpr Score LongDiagonalBishop = S( 45, 0);
+ constexpr Score MinorBehindPawn = S( 18, 3);
+ constexpr Score PawnlessFlank = S( 17, 95);
+ constexpr Score RestrictedPiece = S( 7, 7);
+ constexpr Score RookOnPawn = S( 10, 32);
+ constexpr Score SliderOnQueen = S( 59, 18);
+ constexpr Score ThreatByKing = S( 24, 89);
+ constexpr Score ThreatByPawnPush = S( 48, 39);
+ constexpr Score ThreatByRank = S( 13, 0);
+ constexpr Score ThreatBySafePawn = S(173, 94);
+ constexpr Score TrappedRook = S( 96, 4);
+ constexpr Score WeakQueen = S( 49, 15);
+ constexpr Score WeakUnopposedPawn = S( 12, 23);
#undef S
// 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)));
// Initialise attackedBy bitboards for kings and pawns
- attackedBy[Us][KING] = pos.attacks_from<KING>(pos.square<KING>(Us));
- attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
- attackedBy[Us][ALL_PIECES] = attackedBy[Us][KING] | attackedBy[Us][PAWN];
- attackedBy2[Us] = attackedBy[Us][KING] & attackedBy[Us][PAWN];
+ attackedBy[Us][KING] = pos.count<KING>(Us) ? pos.attacks_from<KING>(Us, pos.square<KING>(Us)) : 0;
+ attackedBy[Us][PAWN] = pe->pawn_attacks(Us);
+ 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]);
- kingRing[Us] = kingAttackersCount[Them] = 0;
+ // Init our king safety tables
+ kingRing[Us] = attackedBy[Us][KING];
- if (relative_rank(Us, pos.square<KING>(Us)) == RANK_1)
++ if (pos.count<KING>(Us) && relative_rank(Us, pos.square<KING>(Us), pos.max_rank()) == RANK_1)
+ kingRing[Us] |= shift<Up>(kingRing[Us]);
- // Init our king safety tables only if we are going to use them
- if ((pos.count<KING>(Us) && pos.non_pawn_material(Them) >= RookValueMg + KnightValueMg) || pos.captures_to_hand())
- {
- kingRing[Us] = attackedBy[Us][KING];
- if (relative_rank(Us, pos.square<KING>(Us), pos.max_rank()) == RANK_1)
- kingRing[Us] |= shift<Up>(kingRing[Us]);
-
- if (file_of(pos.square<KING>(Us)) == pos.max_file())
- kingRing[Us] |= shift<WEST>(kingRing[Us]);
-
- else if (file_of(pos.square<KING>(Us)) == FILE_A)
- kingRing[Us] |= shift<EAST>(kingRing[Us]);
- if (file_of(pos.square<KING>(Us)) == FILE_H)
++ if (pos.count<KING>(Us) && file_of(pos.square<KING>(Us)) == pos.max_file())
+ kingRing[Us] |= shift<WEST>(kingRing[Us]);
- kingRing[Us] &= pos.board_bb();
- else if (file_of(pos.square<KING>(Us)) == FILE_A)
++ else if (pos.count<KING>(Us) && file_of(pos.square<KING>(Us)) == FILE_A)
+ kingRing[Us] |= shift<EAST>(kingRing[Us]);
- kingAttackersCount[Them] = popcount(kingRing[Us] & (pe->pawn_attacks(Them) | shift<Down>(pos.pieces(Them, SHOGI_PAWN))));
- kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
- }
+ kingAttackersCount[Them] = popcount(kingRing[Us] & pe->pawn_attacks(Them));
+ kingRing[Us] &= ~double_pawn_attacks_bb<Us>(pos.pieces(Us, PAWN));
+ kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
++ kingRing[Us] &= pos.board_bb();
}
attackedBy[Us][Pt] |= b;
attackedBy[Us][ALL_PIECES] |= b;
- if (b & kingRing[Them] & ~double_pawn_attacks_bb<Them>(pos.pieces(Them, PAWN)))
+ if (b & kingRing[Them])
{
kingAttackersCount[Us]++;
- kingAttackersWeight[Us] += KingAttackWeights[Pt];
+ kingAttackersWeight[Us] += KingAttackWeights[std::min(int(Pt), QUEEN + 1)];
kingAttacksCount[Us] += popcount(b & attackedBy[Them][KING]);
}
int tropism = popcount(b1) + popcount(b2);
// Main king safety evaluation
- if ((kingAttackersCount[Them] > 1 - pos.count<QUEEN>(Them)) || pos.captures_to_hand())
+ int kingDanger = 0;
+ unsafeChecks = 0;
+
+ // Attacked squares defended at most once by our queen or king
+ weak = attackedBy[Them][ALL_PIECES]
+ & ~attackedBy2[Us]
+ & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
+
+ // Analyse the safe enemy's checks which are possible on next move
+ safe = ~pos.pieces(Them);
+ safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
+
- b1 = attacks_bb<ROOK >(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
- b2 = attacks_bb<BISHOP>(ksq, pos.pieces() ^ pos.pieces(Us, QUEEN));
-
- // Enemy queen safe checks
- if ((b1 | b2) & attackedBy[Them][QUEEN] & safe & ~attackedBy[Us][QUEEN])
- kingDanger += QueenSafeCheck;
-
- b1 &= attackedBy[Them][ROOK];
- b2 &= attackedBy[Them][BISHOP];
-
- // Enemy rooks checks
- if (b1 & safe)
- kingDanger += RookSafeCheck;
- else
- unsafeChecks |= b1;
-
- // Enemy bishops checks
- if (b2 & safe)
- kingDanger += BishopSafeCheck;
- else
- unsafeChecks |= b2;
++ std::function <Bitboard (Color, PieceType)> get_attacks = [this](Color c, PieceType pt) {
++ return attackedBy[c][pt] | (pos.captures_to_hand() && pos.count_in_hand(c, pt) ? ~pos.pieces() : 0);
++ };
++ for (PieceType pt : pos.piece_types())
+ {
- int kingDanger = 0;
- unsafeChecks = 0;
-
- // Attacked squares defended at most once by our queen or king
- weak = attackedBy[Them][ALL_PIECES]
- & ~attackedBy2[Us]
- & (~attackedBy[Us][ALL_PIECES] | attackedBy[Us][KING] | attackedBy[Us][QUEEN]);
-
- // Analyse the safe enemy's checks which are possible on next move
- safe = ~pos.pieces(Them);
- safe &= ~attackedBy[Us][ALL_PIECES] | (weak & attackedBy2[Them]);
-
- std::function <Bitboard (Color, PieceType)> get_attacks = [this](Color c, PieceType pt) {
- return attackedBy[c][pt] | (pos.captures_to_hand() && pos.count_in_hand(c, pt) ? ~pos.pieces() : 0);
- };
- for (PieceType pt : pos.piece_types())
++ switch (pt)
+ {
- switch (pt)
++ case QUEEN:
++ b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & safe & ~attackedBy[Us][QUEEN] & pos.board_bb();
++ if (b)
++ kingDanger += QueenSafeCheck;
++ break;
++ case ROOK:
++ case BISHOP:
++ case KNIGHT:
++ b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & pos.board_bb();
++ if (b & safe)
++ kingDanger += pt == ROOK ? RookSafeCheck
++ : pt == BISHOP ? BishopSafeCheck
++ : KnightSafeCheck;
++ else
++ unsafeChecks |= b;
++ break;
++ case PAWN:
++ if (pos.captures_to_hand() && pos.count_in_hand(Them, pt))
+ {
- case QUEEN:
- b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & safe & ~attackedBy[Us][QUEEN] & pos.board_bb();
- if (b)
- kingDanger += QueenSafeCheck;
- break;
- case ROOK:
- case BISHOP:
- case KNIGHT:
- b = attacks_bb(Us, pt, ksq, pos.pieces() ^ pos.pieces(Us, QUEEN)) & get_attacks(Them, pt) & pos.board_bb();
- if (b & safe)
- kingDanger += pt == ROOK ? RookSafeCheck
- : pt == BISHOP ? BishopSafeCheck
- : KnightSafeCheck;
- else
- unsafeChecks |= b;
- break;
- case PAWN:
- if (pos.captures_to_hand() && pos.count_in_hand(Them, pt))
- {
- b = attacks_bb(Us, pt, ksq, pos.pieces()) & ~pos.pieces() & pos.board_bb();
- if (b & safe)
- kingDanger += OtherSafeCheck;
- else
- unsafeChecks |= b;
- }
- break;
- case SHOGI_PAWN:
- case KING:
- break;
- default:
- b = attacks_bb(Us, pt, ksq, pos.pieces()) & get_attacks(Them, pt) & pos.board_bb();
++ b = attacks_bb(Us, pt, ksq, pos.pieces()) & ~pos.pieces() & pos.board_bb();
+ if (b & safe)
+ kingDanger += OtherSafeCheck;
+ else
+ unsafeChecks |= b;
+ }
++ break;
++ case SHOGI_PAWN:
++ case KING:
++ break;
++ default:
++ b = attacks_bb(Us, pt, ksq, pos.pieces()) & get_attacks(Them, pt) & pos.board_bb();
++ if (b & safe)
++ kingDanger += OtherSafeCheck;
++ else
++ unsafeChecks |= b;
+ }
-
- if (pos.max_check_count())
- kingDanger *= 2;
-
- // Unsafe or occupied checking squares will also be considered, as long as
- // the square is in the attacker's mobility area.
- unsafeChecks &= mobilityArea[Them];
-
- kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
- + 69 * kingAttacksCount[Them] * (1 + 2 * !!pos.max_check_count())
- + 185 * popcount(kingRing[Us] & weak) * (1 + pos.captures_to_hand() + !!pos.max_check_count())
- + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
- + tropism * tropism / 4
- - 873 * !(pos.count<QUEEN>(Them) || pos.captures_to_hand()) / (1 + !!pos.max_check_count())
- - 6 * mg_value(score) / 8
- + mg_value(mobility[Them] - mobility[Us])
- - 30;
-
- // Transform the kingDanger units into a Score, and subtract it from the evaluation
- if (kingDanger > 0)
- score -= make_score(std::min(kingDanger * kingDanger / 4096, 3000), kingDanger / 16);
+ }
- // Enemy knights checks
- b = pos.attacks_from<KNIGHT>(ksq) & attackedBy[Them][KNIGHT];
- if (b & safe)
- kingDanger += KnightSafeCheck;
- else
- unsafeChecks |= b;
++ if (pos.max_check_count())
++ kingDanger *= 2;
+
+ // Unsafe or occupied checking squares will also be considered, as long as
+ // the square is in the attacker's mobility area.
+ unsafeChecks &= mobilityArea[Them];
+
+ kingDanger += kingAttackersCount[Them] * kingAttackersWeight[Them]
- + 69 * kingAttacksCount[Them]
- + 185 * popcount(kingRing[Us] & weak)
- + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
- + tropism * tropism / 4
- - 873 * !pos.count<QUEEN>(Them)
- - 6 * mg_value(score) / 8
- + mg_value(mobility[Them] - mobility[Us])
- - 30;
++ + 69 * kingAttacksCount[Them] * (1 + 2 * !!pos.max_check_count())
++ + 185 * popcount(kingRing[Us] & weak) * (1 + pos.captures_to_hand() + !!pos.max_check_count())
++ + 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
++ + tropism * tropism / 4
++ - 873 * !(pos.count<QUEEN>(Them) || pos.captures_to_hand()) / (1 + !!pos.max_check_count())
++ - 6 * mg_value(score) / 8
++ + mg_value(mobility[Them] - mobility[Us])
++ - 30;
+
+ // Transform the kingDanger units into a Score, and subtract it from the evaluation
+ if (kingDanger > 0)
- score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
++ score -= make_score(std::min(kingDanger * kingDanger / 4096, 3000), kingDanger / 16);
+
// Penalty when our king is on a pawnless flank
if (!(pos.pieces(PAWN) & kingFlank))
score -= PawnlessFlank;
Bitboard b, weak, defended, nonPawnEnemies, stronglyProtected, safe, restricted;
Score score = SCORE_ZERO;
+ // Bonuses for variants with mandatory captures
+ if (pos.must_capture())
+ {
+ // Penalties for possible captures
+ score -= make_score(100, 100) * popcount(attackedBy[Us][ALL_PIECES] & pos.pieces(Them));
+
+ // Bonus if we threaten to force captures
+ Bitboard moves = 0, piecebb = pos.pieces(Us);
+ while (piecebb)
+ {
+ Square s = pop_lsb(&piecebb);
+ if (type_of(pos.piece_on(s)) != KING)
+ moves |= pos.moves_from(Us, type_of(pos.piece_on(s)), s);
+ }
+ score += make_score(200, 200) * popcount(attackedBy[Them][ALL_PIECES] & moves & ~pos.pieces());
+ score += make_score(200, 200) * popcount(attackedBy[Them][ALL_PIECES] & moves & ~pos.pieces() & ~attackedBy2[Us]);
+ }
+
// Non-pawn enemies
- nonPawnEnemies = pos.pieces(Them) ^ pos.pieces(Them, PAWN, SHOGI_PAWN);
- nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, PAWN);
++ nonPawnEnemies = pos.pieces(Them) & ~pos.pieces(Them, PAWN, SHOGI_PAWN);
// Squares strongly protected by the enemy, either because they defend the
// square with a pawn, or because they defend the square twice and we don't.
-- stronglyProtected = attackedBy[Them][PAWN]
++ stronglyProtected = (attackedBy[Them][PAWN] | attackedBy[Them][SHOGI_PAWN])
| (attackedBy2[Them] & ~attackedBy2[Us]);
// Non-pawn enemies, strongly protected
*moveList++ = make_move(ksq, pop_lsb(&b));
}
- if (pos.castling_enabled() && Type != CAPTURES && Type != EVASIONS && pos.can_castle(Us))
- if (Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us))
++ if (pos.castling_enabled() && Type != CAPTURES && Type != EVASIONS && pos.castling_rights(Us))
{
if (pos.is_chess960())
{
// Flag the pawn
opposed = theirPawns & forward_file_bb(Us, s);
stoppers = theirPawns & passed_pawn_mask(Us, s);
- lever = theirPawns & PawnAttacks[Us][s];
- leverPush = theirPawns & PawnAttacks[Us][s + Up];
- doubled = ourPawns & (s - Up);
+ lever = theirPawns & PseudoAttacks[Us][PAWN][s];
+ leverPush = relative_rank(Them, s, pos.max_rank()) > RANK_1 ? theirPawns & PseudoAttacks[Us][PAWN][s + Up] : 0;
+ doubled = relative_rank(Us, s, pos.max_rank()) > RANK_1 ? ourPawns & (s - Up) : 0;
neighbours = ourPawns & adjacent_files_bb(f);
phalanx = neighbours & rank_bb(s);
- supported = relative_rank(Us, s, pos.max_rank()) > RANK_1 ? neighbours & rank_bb(s - Up) : 0;
- support = neighbours & rank_bb(s - Up);
++ support = relative_rank(Us, s, pos.max_rank()) > RANK_1 ? neighbours & rank_bb(s - Up) : 0;
// A pawn is backward when it is behind all pawns of the same color
// on the adjacent files and cannot be safely advanced.
&& popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s;
- else if ( stoppers == SquareBB[s + Up]
- && relative_rank(Us, s) >= RANK_5)
+ else if ( relative_rank(Them, s, pos.max_rank()) > RANK_1
+ && stoppers == SquareBB[s + Up]
+ && relative_rank(Us, s, pos.max_rank()) >= RANK_5)
{
- b = shift<Up>(supported) & ~theirPawns;
+ b = shift<Up>(support) & ~theirPawns;
while (b)
- if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
+ if (!more_than_one(theirPawns & PseudoAttacks[Us][PAWN][pop_lsb(&b)]))
e->passedPawns[Us] |= s;
}
// Score this pawn
- if (supported | phalanx)
- score += Connected[opposed][bool(phalanx)][popcount(supported)][relative_rank(Us, s, pos.max_rank())];
+ if (support | phalanx)
- score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s)];
++ score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s, pos.max_rank())];
else if (!neighbours)
- score -= Isolated, e->weakUnopposed[Us] += !opposed;
+ score -= Isolated * (1 + 2 * pos.must_capture()), e->weakUnopposed[Us] += !opposed;
else if (backward)
score -= Backward, e->weakUnopposed[Us] += !opposed;
for (File f = File(center - 1); f <= File(center + 1); ++f)
{
b = ourPawns & file_bb(f);
- int ourRank = b ? relative_rank(Us, backmost_sq(Us, b), pos.max_rank()) : 0;
- Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
++ Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b), pos.max_rank()) : RANK_1;
b = theirPawns & file_bb(f);
- int theirRank = b ? relative_rank(Us, frontmost_sq(Them, b), pos.max_rank()) : 0;
- Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
++ Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b), pos.max_rank()) : RANK_1;
- int d = std::min(f, ~f);
- safety += ShelterStrength[d][ourRank];
+ int d = std::min(std::min(f, File(pos.max_file() - f)), FILE_D);
+ // higher weight for pawns on second rank and missing shelter in drop variants
+ safety += ShelterStrength[d][ourRank] * (1 + (pos.captures_to_hand() && ourRank <= RANK_2));
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
: UnblockedStorm[d][theirRank];
}
if (can_castle(BLACK_OOO))
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK | QUEEN_SIDE))) : 'q');
- if (!can_castle(WHITE) && !can_castle(BLACK))
+ if (!can_castle(ANY_CASTLING))
ss << '-';
- ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
- << st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
+ // check count
+ if (max_check_count())
+ ss << " " << (max_check_count() - st->checksGiven[WHITE]) << "+" << (max_check_count() - st->checksGiven[BLACK]);
+
+ // Counting limit and counting ply, or ep-square and 50-move rule counter
+ if (st->countingLimit)
+ ss << " " << st->countingLimit << " " << st->countingPly;
+ else
+ ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(*this, ep_square()) + " ")
+ << st->rule50;
+
+ ss << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
return ss.str();
}
// type on a given square a (middlegame, endgame) score pair is assigned. Table
// is defined for files A..D and white side: it is symmetric for black side and
// second half of the files.
-constexpr Score Bonus[][RANK_NB][int(FILE_NB) / 2] = {
+constexpr Score Bonus[PIECE_TYPE_NB][RANK_NB][int(FILE_NB) / 2] = {
{ },
- { // Pawn
- { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
- { S(-11,-3), S( 7, -1), S( 7, 7), S(17, 2) },
- { S(-16,-2), S( -3, 2), S( 23, 6), S(23,-1) },
- { S(-14, 7), S( -7, -4), S( 20,-8), S(24, 2) },
- { S( -5,13), S( -2, 10), S( -1,-1), S(12,-8) },
- { S(-11,16), S(-12, 6), S( -2, 1), S( 4,16) },
- { S( -2, 1), S( 20,-12), S(-10, 6), S(-2,25) }
- },
+ { },
{ // Knight
{ S(-169,-105), S(-96,-74), S(-80,-46), S(-79,-18) },
{ S( -79, -70), S(-39,-56), S(-24,-15), S( -9, 6) },
{ 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) }
- }
};
-constexpr Score PBonus[RANK_NB][FILE_NB] =\r
- { // Pawn\r
- { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },\r
- { S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) },\r
- { S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) },\r
- { S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) },\r
- { S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) },\r
- { S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) },\r
- { S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) }\r
++constexpr Score PBonus[RANK_NB][FILE_NB] =
++ { // Pawn
++ { S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0) },
++ { S( 0,-11), S( -3, -4), S( 13, -1), S( 19, -4), S( 16, 17), S( 13, 7), S( 4, 4), S( -4,-13) },
++ { S(-16, -8), S(-12, -6), S( 20, -3), S( 21, 0), S( 25,-11), S( 29, 3), S( 0, 0), S(-27, -1) },
++ { S(-11, 3), S(-17, 6), S( 11,-10), S( 21, 1), S( 32, -6), S( 19,-11), S( -5, 0), S(-14, -2) },
++ { S( 4, 13), S( 6, 7), S( -8, 3), S( 3, -5), S( 8,-15), S( -2, -1), S(-19, 9), S( -5, 13) },
++ { S( -5, 25), S(-19, 20), S( 7, 16), S( 8, 12), S( -7, 21), S( -2, 3), S(-10, -4), S(-16, 15) },
++ { S(-10, 6), S( 9, -5), S( -7, 16), S(-12, 27), S( -7, 15), S( -8, 11), S( 16, -7), S( -8, 4) }
+ };
+
#undef S
-Score psq[PIECE_NB][SQUARE_NB];
+Score psq[PIECE_NB][SQUARE_NB + 1];
// init() initializes piece-square tables: the white halves of the tables are
// copied from Bonus[] adding the piece value, then the black halves of the
Score score = make_score(PieceValue[MG][pc], PieceValue[EG][pc]);
- for (Square s = SQ_A1; s <= SQ_H8; ++s)
+ // For drop variants, halve the piece values
+ if (v->capturesToHand || !v->checking)
+ score = make_score(mg_value(score) * int(MidgameLimit / 2) / (MidgameLimit + mg_value(score)),
+ eg_value(score) * int(MidgameLimit / 2) / (MidgameLimit + eg_value(score)));
+
+ // For antichess variants, use negative piece values
+ if ( v->extinctionValue == VALUE_MATE
+ && v->extinctionPieceTypes.find(ALL_PIECES) != v->extinctionPieceTypes.end())
+ score = -score / 8;
+ else if (v->bareKingValue == VALUE_MATE)
+ score = -score / 8;
+
+ for (Square s = SQ_A1; s <= SQ_MAX; ++s)
{
- File f = std::min(file_of(s), ~file_of(s));
- psq[ pc][ s] = score + (type_of(pc) == PAWN ? PBonus[rank_of(s)][file_of(s)]
- : Bonus[pc][rank_of(s)][f]);
- psq[~pc][~s] = -psq[pc][s];
+ File f = std::max(std::min(file_of(s), File(v->maxFile - file_of(s))), FILE_A);
+ Rank r = rank_of(s);
- psq[ pc][ s] = score + ( pt == KING ? KingBonus[std::min(r, RANK_8)][std::min(f, FILE_D)]
++ psq[ pc][ s] = score + ( pt == PAWN ? PBonus[std::min(r, RANK_8)][std::min(file_of(s), FILE_H)]
++ : pt == KING ? KingBonus[std::min(r, RANK_8)][std::min(f, FILE_D)]
+ : pt <= QUEEN ? Bonus[pc][std::min(r, RANK_8)][std::min(f, FILE_D)]
+ : make_score(5, 5) * (2 * f + std::max(std::min(r, Rank(v->maxRank - r)), RANK_1) - 8));
+ psq[~pc][rank_of(s) <= v->maxRank ? relative_square(BLACK, s, v->maxRank) : s] = -psq[pc][s];
}
+ // pieces in pocket
+ psq[ pc][SQ_NONE] = score + make_score(20, 20);
+ psq[~pc][SQ_NONE] = -psq[pc][SQ_NONE];
}
}
}
// Step 7. Razoring (~2 Elo)
- if ( depth < 2 * ONE_PLY
+ if ( !rootNode // The required rootNode PV handling is not available in qsearch
+ && depth < 2 * ONE_PLY
+ && !pos.must_capture()
+ && !pos.capture_the_flag_piece()
+ && !pos.max_check_count()
- && eval <= alpha - RazorMargin)
+ && eval <= alpha - RazorMargin)
return qsearch<NT>(pos, ss, alpha, beta);
improving = ss->staticEval >= (ss-2)->staticEval
|| (ss-2)->staticEval == VALUE_NONE;
+ // Skip early pruning in case of mandatory capture
+ if (pos.must_capture() && MoveList<CAPTURES>(pos).size())
+ goto moves_loop;
+
// Step 8. Futility pruning: child node (~30 Elo)
- if ( !rootNode
+ if ( !PvNode
&& depth < 7 * ONE_PLY
- && eval - futility_margin(depth, improving) >= beta
+ && !( pos.extinction_value() == -VALUE_MATE
+ && pos.extinction_piece_types().find(ALL_PIECES) == pos.extinction_piece_types().end())
+ && (pos.checking_permitted() || !pos.capture_the_flag_piece())
+ && eval - futility_margin(depth, improving) * (1 + !!pos.max_check_count()) >= beta
&& eval < VALUE_KNOWN_WIN) // Do not return unproven wins
return eval;
// much above beta, we can (almost) safely prune the previous move.
if ( !PvNode
&& depth >= 5 * ONE_PLY
+ && (pos.pieces() ^ pos.pieces(CLOBBER_PIECE))
&& abs(beta) < VALUE_MATE_IN_MAX_PLY)
{
- Value rbeta = std::min(beta + 216 * (1 + !!pos.max_check_count() + (pos.extinction_value() != VALUE_NONE)) - 48 * improving, VALUE_INFINITE);
- MovePicker mp(pos, ttMove, rbeta - ss->staticEval, &thisThread->captureHistory);
- Value raisedBeta = std::min(beta + 216 - 48 * improving, VALUE_INFINITE);
++ Value raisedBeta = std::min(beta + 216 * (1 + !!pos.max_check_count() + (pos.extinction_value() != VALUE_NONE)) - 48 * improving, VALUE_INFINITE);
+ MovePicker mp(pos, ttMove, raisedBeta - ss->staticEval, &thisThread->captureHistory);
int probCutCount = 0;
while ( (move = mp.next_move()) != MOVE_NONE
{
if ( !captureOrPromotion
&& !givesCheck
+ && (!pos.must_capture() || !pos.attackers_to(to_sq(move), ~pos.side_to_move()))
- && (!pos.advanced_pawn_push(move) || pos.non_pawn_material() >= Value(5000)))
+ && !pos.advanced_pawn_push(move))
{
// Move count based pruning (~30 Elo)
if (moveCountPruning)