1
0
Fork 0
mirror of https://github.com/sockspls/badfish synced 2025-07-12 12:09:14 +00:00

Merge branch 'master' into clusterMergeMaster5

This commit is contained in:
Joost VandeVondele 2019-05-01 08:23:22 +02:00
commit 85327828c9
21 changed files with 186 additions and 231 deletions

View file

@ -55,6 +55,9 @@ script:
- make clean && make -j2 ARCH=x86-64 optimize=no debug=yes build && ../tests/signature.sh $benchref
- make clean && make -j2 ARCH=x86-32 optimize=no debug=yes build && ../tests/signature.sh $benchref
- make clean && make -j2 ARCH=x86-32 build && ../tests/signature.sh $benchref
# Verify bench number is ONE_PLY independent by doubling its value
- sed -i'.bak' 's/.*\(ONE_PLY = [0-9]*\),.*/\1 * 2,/g' types.h
- make clean && make -j2 ARCH=x86-64 build && ../tests/signature.sh $benchref
#
# Check perft and reproducible search

View file

@ -490,7 +490,7 @@ config-sanity:
@echo "Testing config sanity. If this fails, try 'make help' ..."
@echo ""
@test "$(debug)" = "yes" || test "$(debug)" = "no"
@test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "no"
@test "$(sanitize)" = "undefined" || test "$(sanitize)" = "thread" || test "$(sanitize)" = "address" || test "$(sanitize)" = "no"
@test "$(optimize)" = "yes" || test "$(optimize)" = "no"
@test "$(arch)" = "any" || test "$(arch)" = "x86_64" || test "$(arch)" = "i386" || \
test "$(arch)" = "ppc64" || test "$(arch)" = "ppc" || test "$(arch)" = "armv7"

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <numeric>
#include <vector>

View file

@ -18,8 +18,8 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <bitset>
#include <algorithm>
#include "bitboard.h"
#include "misc.h"
@ -27,13 +27,10 @@
uint8_t PopCnt16[1 << 16];
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard ForwardRanksBB[COLOR_NB][RANK_NB];
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
Bitboard DistanceRingBB[SQUARE_NB][8];
Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
Bitboard SquareBB[SQUARE_NB];
Bitboard KingFlank[FILE_NB] = {
QueenSide ^ FileDBB, QueenSide, QueenSide,
@ -85,10 +82,7 @@ void Bitboards::init() {
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
{
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
DistanceRingBB[s1][SquareDistance[s1][s2]] |= s2;
}
int steps[][5] = { {}, { 7, 9 }, { 6, 10, 15, 17 }, {}, {}, {}, { 1, 7, 8, 9 } };
@ -122,10 +116,7 @@ void Bitboards::init() {
for (PieceType pt : { BISHOP, ROOK })
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
if (PseudoAttacks[pt][s1] & s2)
{
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
BetweenBB[s1][s2] = attacks_bb(pt, s1, SquareBB[s2]) & attacks_bb(pt, s2, SquareBB[s1]);
}
}
}

View file

@ -68,13 +68,11 @@ constexpr Bitboard Center = (FileDBB | FileEBB) & (Rank4BB | Rank5BB);
extern uint8_t PopCnt16[1 << 16];
extern uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
extern Bitboard SquareBB[SQUARE_NB];
extern Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
extern Bitboard LineBB[SQUARE_NB][SQUARE_NB];
extern Bitboard DistanceRingBB[SQUARE_NB][8];
extern Bitboard PseudoAttacks[PIECE_TYPE_NB][SQUARE_NB];
extern Bitboard PawnAttacks[COLOR_NB][SQUARE_NB];
extern Bitboard KingFlank[FILE_NB];
extern Bitboard SquareBB[SQUARE_NB];
/// Magic holds all magic bitboards relevant data for a single square
@ -102,34 +100,19 @@ struct Magic {
extern Magic RookMagics[SQUARE_NB];
extern Magic BishopMagics[SQUARE_NB];
inline Bitboard square_bb(Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return SquareBB[s];
}
/// Overloads of bitwise operators between a Bitboard and a Square for testing
/// whether a given bit is set in a bitboard, and for setting and clearing bits.
inline Bitboard operator&(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b & SquareBB[s];
}
inline Bitboard operator|(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b | SquareBB[s];
}
inline Bitboard operator^(Bitboard b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^ SquareBB[s];
}
inline Bitboard& operator|=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b |= SquareBB[s];
}
inline Bitboard& operator^=(Bitboard& b, Square s) {
assert(s >= SQ_A1 && s <= SQ_H8);
return b ^= SquareBB[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|=(Bitboard& b, Square s) { return b |= square_bb(s); }
inline Bitboard& operator^=(Bitboard& b, Square s) { return b ^= square_bb(s); }
constexpr bool more_than_one(Bitboard b) {
return b & (b - 1);
@ -200,13 +183,12 @@ inline Bitboard adjacent_files_bb(File f) {
}
/// between_bb() returns a bitboard representing all the squares between the two
/// given ones. For instance, between_bb(SQ_C4, SQ_F7) returns a bitboard with
/// the bits for square d5 and e6 set. If s1 and s2 are not on the same rank,
/// file or diagonal, 0 is returned.
/// between_bb() returns squares that are linearly between the given squares
/// If the given squares are not on a same file/rank/diagonal, return 0.
inline Bitboard between_bb(Square s1, Square s2) {
return BetweenBB[s1][s2];
return LineBB[s1][s2] & ( (AllSquares << (s1 + (s1 < s2)))
^(AllSquares << (s2 + !(s1 < s2))));
}
@ -254,15 +236,16 @@ inline bool aligned(Square s1, Square s2, Square s3) {
/// distance() functions return the distance between x and y, defined as the
/// number of steps for a king in x to reach y. Works with squares, ranks, files.
/// number of steps for a king in x to reach y.
template<typename T> inline int distance(T x, T y) { return std::abs(x - y); }
template<typename T1 = Square> inline int distance(Square x, Square y);
template<> inline int distance<File>(Square x, Square y) { return std::abs(file_of(x) - file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return std::abs(rank_of(x) - rank_of(y)); }
template<> inline int distance<Square>(Square x, Square y) { return SquareDistance[x][y]; }
template<typename T1, typename T2> inline int distance(T2 x, T2 y);
template<> inline int distance<File>(Square x, Square y) { return distance(file_of(x), file_of(y)); }
template<> inline int distance<Rank>(Square x, Square y) { return distance(rank_of(x), rank_of(y)); }
template<class T> constexpr const T& clamp(const T& v, const T& lo, const T& hi) {
return v < lo ? lo : v > hi ? hi : v;
}
/// attacks_bb() returns a bitboard representing all the squares attacked by a
/// piece of type Pt (bishop or rook) placed on 's'.

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "bitboard.h"
@ -77,10 +76,7 @@ namespace {
if (file_of(pos.square<PAWN>(strongSide)) >= FILE_E)
sq = Square(sq ^ 7); // Mirror SQ_H1 -> SQ_A1
if (strongSide == BLACK)
sq = ~sq;
return sq;
return strongSide == WHITE ? sq : ~sq;
}
} // namespace
@ -286,18 +282,18 @@ Value Endgame<KQKR>::operator()(const Position& pos) const {
}
/// KNN vs KP. Simply push the opposing king to the corner.
/// KNN vs KP. Simply push the opposing king to the corner
template<>
Value Endgame<KNNKP>::operator()(const Position& pos) const {
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
assert(verify_material(pos, strongSide, 2 * KnightValueMg, 0));
assert(verify_material(pos, weakSide, VALUE_ZERO, 1));
Value result = 2 * KnightValueEg
- PawnValueEg
+ PushToEdges[pos.square<KING>(weakSide)];
Value result = 2 * KnightValueEg
- PawnValueEg
+ PushToEdges[pos.square<KING>(weakSide)];
return strongSide == pos.side_to_move() ? result : -result;
return strongSide == pos.side_to_move() ? result : -result;
}
@ -639,8 +635,6 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
Square ksq = pos.square<KING>(weakSide);
Square psq1 = pos.squares<PAWN>(strongSide)[0];
Square psq2 = pos.squares<PAWN>(strongSide)[1];
Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2;
if (relative_rank(strongSide, psq1) > relative_rank(strongSide, psq2))
@ -674,7 +668,7 @@ ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(weakSide, BISHOP))
|| distance(r1, r2) >= 2))
|| distance<Rank>(psq1, psq2) >= 2))
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cstring> // For std::memset
#include <iomanip>
@ -229,6 +228,8 @@ namespace {
const Square ksq = pos.square<KING>(Us);
Bitboard dblAttackByPawn = pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
// Find our pawns that are blocked or on the first two ranks
Bitboard b = pos.pieces(Us, PAWN) & (shift<Down>(pos.pieces()) | LowRanks);
@ -240,7 +241,8 @@ namespace {
attackedBy[Us][KING] = pos.attacks_from<KING>(ksq);
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];
attackedBy2[Us] = (attackedBy[Us][KING] & attackedBy[Us][PAWN])
| dblAttackByPawn;
// Init our king safety tables
kingRing[Us] = attackedBy[Us][KING];
@ -257,7 +259,7 @@ namespace {
kingAttacksCount[Them] = kingAttackersWeight[Them] = 0;
// Remove from kingRing[] the squares defended by two pawns
kingRing[Us] &= ~pawn_double_attacks_bb<Us>(pos.pieces(Us, PAWN));
kingRing[Us] &= ~dblAttackByPawn;
}
@ -326,7 +328,7 @@ namespace {
// bishop, bigger when the center files are blocked with pawns.
Bitboard blocked = pos.pieces(Us, PAWN) & shift<Down>(pos.pieces());
score -= BishopPawns * pe->pawns_on_same_color_squares(Us, s)
score -= BishopPawns * pos.pawns_on_same_color_squares(Us, s)
* (1 + popcount(blocked & CenterFiles));
// Bonus for bishop on a long diagonal which can "see" both center squares
@ -356,8 +358,8 @@ namespace {
score += RookOnPawn * popcount(pos.pieces(Them, PAWN) & PseudoAttacks[ROOK][s]);
// Bonus for rook on an open or semi-open file
if (pe->semiopen_file(Us, file_of(s)))
score += RookOnFile[bool(pe->semiopen_file(Them, file_of(s)))];
if (pos.semiopen_file(Us, file_of(s)))
score += RookOnFile[bool(pos.semiopen_file(Them, file_of(s)))];
// Penalty when trapped by the king, even more if the king cannot castle
else if (mob <= 3)
@ -465,15 +467,16 @@ namespace {
+ 69 * kingAttacksCount[Them]
+ 185 * popcount(kingRing[Us] & weak)
- 100 * bool(attackedBy[Us][KNIGHT] & attackedBy[Us][KING])
- 35 * bool(attackedBy[Us][BISHOP] & attackedBy[Us][KING])
+ 150 * popcount(pos.blockers_for_king(Us) | unsafeChecks)
- 873 * !pos.count<QUEEN>(Them)
- 6 * mg_value(score) / 8
+ mg_value(mobility[Them] - mobility[Us])
+ 5 * kingFlankAttacks * kingFlankAttacks / 16
- 25;
- 7;
// Transform the kingDanger units into a Score, and subtract it from the evaluation
if (kingDanger > 0)
if (kingDanger > 100)
score -= make_score(kingDanger * kingDanger / 4096, kingDanger / 16);
// Penalty when our king is on a pawnless flank
@ -718,7 +721,7 @@ namespace {
int bonus = popcount(safe) + popcount(behind & safe);
int weight = pos.count<ALL_PIECES>(Us)
- 2 * popcount(pe->semiopenFiles[WHITE] & pe->semiopenFiles[BLACK]);
- (16 - pos.count<PAWN>()) / 4;
Score score = make_score(bonus * weight * weight / 16, 0);
@ -743,12 +746,12 @@ namespace {
&& (pos.pieces(PAWN) & KingSide);
// Compute the initiative bonus for the attacking side
int complexity = 9 * pe->pawn_asymmetry()
int complexity = 9 * pe->passed_count()
+ 11 * pos.count<PAWN>()
+ 9 * outflanking
+ 18 * pawnsOnBothFlanks
+ 49 * !pos.non_pawn_material()
-121 ;
-103 ;
// Now apply the bonus: note that we find the attacking side by extracting
// the sign of the endgame value, and that we carefully cap the bonus so
@ -776,7 +779,7 @@ namespace {
if ( pos.opposite_bishops()
&& pos.non_pawn_material(WHITE) == BishopValueMg
&& pos.non_pawn_material(BLACK) == BishopValueMg)
sf = 8 + 4 * pe->pawn_asymmetry();
sf = 16 + 4 * pe->passed_count();
else
sf = std::min(40 + (pos.opposite_bishops() ? 2 : 7) * pos.count<PAWN>(strongSide), sf);

View file

@ -44,7 +44,6 @@ int main(int argc, char* argv[]) {
Position::init();
Bitbases::init();
Search::init();
Pawns::init();
Threads.set(Options["Threads"]);
Search::clear(); // After threads are up

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm> // For std::min
#include <cassert>
#include <cstring> // For std::memset
@ -70,14 +69,12 @@ namespace {
bool is_KBPsK(const Position& pos, Color us) {
return pos.non_pawn_material(us) == BishopValueMg
&& pos.count<BISHOP>(us) == 1
&& pos.count<PAWN >(us) >= 1;
}
bool is_KQKRPs(const Position& pos, Color us) {
return !pos.count<PAWN>(us)
&& pos.non_pawn_material(us) == QueenValueMg
&& pos.count<QUEEN>(us) == 1
&& pos.count<ROOK>(~us) == 1
&& pos.count<PAWN>(~us) >= 1;
}
@ -132,7 +129,7 @@ Entry* probe(const Position& pos) {
Value npm_w = pos.non_pawn_material(WHITE);
Value npm_b = pos.non_pawn_material(BLACK);
Value npm = std::max(EndgameLimit, std::min(npm_w + npm_b, MidgameLimit));
Value npm = clamp(npm_w + npm_b, EndgameLimit, MidgameLimit);
// Map total non-pawn material into [PHASE_ENDGAME, PHASE_MIDGAME]
e->gamePhase = Phase(((npm - EndgameLimit) * PHASE_MIDGAME) / (MidgameLimit - EndgameLimit));
@ -152,9 +149,9 @@ Entry* probe(const Position& pos) {
// OK, we didn't find any special evaluation function for the current material
// configuration. Is there a suitable specialized scaling function?
const EndgameBase<ScaleFactor>* sf;
const auto* sf = pos.this_thread()->endgames.probe<ScaleFactor>(key);
if ((sf = pos.this_thread()->endgames.probe<ScaleFactor>(key)) != nullptr)
if (sf)
{
e->scalingFunction[sf->strongSide] = sf; // Only strong color assigned
return e;

View file

@ -53,7 +53,7 @@ struct HashTable {
Entry* operator[](Key key) { return &table[(uint32_t)key & (Size - 1)]; }
private:
std::vector<Entry> table = std::vector<Entry>(Size);
std::vector<Entry> table = std::vector<Entry>(Size); // Allocate on the heap
};

View file

@ -139,12 +139,12 @@ Move MovePicker::select(Pred filter) {
if (T == Best)
std::swap(*cur, *std::max_element(cur, endMoves));
move = *cur++;
if (*cur != ttMove && filter())
return *cur++;
if (move != ttMove && filter())
return move;
cur++;
}
return move = MOVE_NONE;
return MOVE_NONE;
}
/// MovePicker::next_move() is the most important method of the MovePicker class. It
@ -174,10 +174,10 @@ top:
case GOOD_CAPTURE:
if (select<Best>([&](){
return pos.see_ge(move, Value(-55 * (cur-1)->value / 1024)) ?
return pos.see_ge(*cur, Value(-55 * cur->value / 1024)) ?
// Move losing capture to endBadCaptures to be tried later
true : (*endBadCaptures++ = move, false); }))
return move;
true : (*endBadCaptures++ = *cur, false); }))
return *(cur - 1);
// Prepare the pointers to loop over the refutations array
cur = std::begin(refutations);
@ -192,10 +192,10 @@ top:
/* fallthrough */
case REFUTATION:
if (select<Next>([&](){ return move != MOVE_NONE
&& !pos.capture(move)
&& pos.pseudo_legal(move); }))
return move;
if (select<Next>([&](){ return *cur != MOVE_NONE
&& !pos.capture(*cur)
&& pos.pseudo_legal(*cur); }))
return *(cur - 1);
++stage;
/* fallthrough */
@ -210,10 +210,10 @@ top:
case QUIET:
if ( !skipQuiets
&& select<Next>([&](){return move != refutations[0]
&& move != refutations[1]
&& move != refutations[2];}))
return move;
&& select<Next>([&](){return *cur != refutations[0].move
&& *cur != refutations[1].move
&& *cur != refutations[2].move;}))
return *(cur - 1);
// Prepare the pointers to loop over the bad captures
cur = moves;
@ -237,12 +237,12 @@ top:
return select<Best>([](){ return true; });
case PROBCUT:
return select<Best>([&](){ return pos.see_ge(move, threshold); });
return select<Best>([&](){ return pos.see_ge(*cur, threshold); });
case QCAPTURE:
if (select<Best>([&](){ return depth > DEPTH_QS_RECAPTURES
|| to_sq(move) == recaptureSquare; }))
return move;
|| to_sq(*cur) == recaptureSquare; }))
return *(cur - 1);
// If we did not find any move and we do not try checks, we have finished
if (depth != DEPTH_QS_CHECKS)

View file

@ -142,7 +142,6 @@ private:
Move ttMove;
ExtMove refutations[3], *cur, *endMoves, *endBadCaptures;
int stage;
Move move;
Square recaptureSquare;
Value threshold;
Depth depth;

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "bitboard.h"
@ -36,8 +35,8 @@ namespace {
constexpr Score Doubled = S(11, 56);
constexpr Score Isolated = S( 5, 15);
// Connected pawn bonus by opposed, phalanx, #support and rank
Score Connected[2][2][3][RANK_NB];
// Connected pawn bonus
constexpr int Connected[RANK_NB] = { 0, 13, 17, 24, 59, 96, 171 };
// Strength of pawn shelter for our king by [distance from edge][rank].
// RANK_1 = 0 is used for files where we have no pawn, or pawn is behind our king.
@ -78,11 +77,8 @@ namespace {
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
e->semiopenFiles[Us] = 0xFF;
e->kingSquares[Us] = SQ_NONE;
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount(ourPawns & DarkSquares);
e->pawnsOnSquares[Us][WHITE] = pos.count<PAWN>(Us) - e->pawnsOnSquares[Us][BLACK];
// Loop through all pawns of the current color and score each pawn
while ((s = *pl++) != SQ_NONE)
@ -90,8 +86,8 @@ namespace {
assert(pos.piece_on(s) == make_piece(Us, PAWN));
File f = file_of(s);
Rank r = relative_rank(Us, s);
e->semiopenFiles[Us] &= ~(1 << f);
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
// Flag the pawn
@ -114,12 +110,11 @@ namespace {
// which could become passed after one or two pawn pushes when are
// not attacked more times than defended.
if ( !(stoppers ^ lever ^ leverPush)
&& popcount(support) >= popcount(lever) - 1
&& (support || !more_than_one(lever))
&& popcount(phalanx) >= popcount(leverPush))
e->passedPawns[Us] |= s;
else if ( stoppers == SquareBB[s + Up]
&& relative_rank(Us, s) >= RANK_5)
else if (stoppers == square_bb(s + Up) && r >= RANK_5)
{
b = shift<Up>(support) & ~theirPawns;
while (b)
@ -129,8 +124,11 @@ namespace {
// Score this pawn
if (support | phalanx)
score += Connected[opposed][bool(phalanx)][popcount(support)][relative_rank(Us, s)];
{
int v = (phalanx ? 3 : 2) * Connected[r];
v = 17 * popcount(support) + (v >> (opposed + 1));
score += make_score(v, v * (r - 2) / 4);
}
else if (!neighbours)
score -= Isolated, e->weakUnopposed[Us] += !opposed;
@ -148,27 +146,6 @@ namespace {
namespace Pawns {
/// Pawns::init() initializes some tables needed by evaluation. Instead of using
/// hard-coded tables, when makes sense, we prefer to calculate them with a formula
/// to reduce independent parameters and to allow easier tuning and better insight.
void init() {
static constexpr int Seed[RANK_NB] = { 0, 13, 24, 18, 65, 100, 175, 330 };
for (int opposed = 0; opposed <= 1; ++opposed)
for (int phalanx = 0; phalanx <= 1; ++phalanx)
for (int support = 0; support <= 2; ++support)
for (Rank r = RANK_2; r < RANK_8; ++r)
{
int v = 17 * support;
v += (Seed[r] + (phalanx ? (Seed[r + 1] - Seed[r]) / 2 : 0)) >> opposed;
Connected[opposed][phalanx][support][r] = make_score(v, v * (r - 2) / 4);
}
}
/// Pawns::probe() looks up the current position's pawns configuration in
/// the pawns hash table. It returns a pointer to the Entry if the position
/// is found. Otherwise a new Entry is computed and stored there, so we don't
@ -185,8 +162,6 @@ Entry* probe(const Position& pos) {
e->key = key;
e->scores[WHITE] = evaluate<WHITE>(pos, e);
e->scores[BLACK] = evaluate<BLACK>(pos, e);
e->asymmetry = popcount( (e->passedPawns[WHITE] | e->passedPawns[BLACK])
| (e->semiopenFiles[WHITE] ^ e->semiopenFiles[BLACK]));
return e;
}
@ -209,7 +184,7 @@ Value Entry::evaluate_shelter(const Position& pos, Square ksq) {
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
Value(374) : Value(5);
File center = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
File center = clamp(file_of(ksq), FILE_B, FILE_G);
for (File f = File(center - 1); f <= File(center + 1); ++f)
{
b = ourPawns & file_bb(f);
@ -237,11 +212,15 @@ Score Entry::do_king_safety(const Position& pos) {
Square ksq = pos.square<KING>(Us);
kingSquares[Us] = ksq;
castlingRights[Us] = pos.castling_rights(Us);
int minKingPawnDistance = 0;
Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns)
while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {}
int minPawnDist = pawns ? 8 : 0;
if (pawns & PseudoAttacks[KING][ksq])
minPawnDist = 1;
else while (pawns)
minPawnDist = std::min(minPawnDist, distance(ksq, pop_lsb(&pawns)));
Value bonus = evaluate_shelter<Us>(pos, ksq);
@ -252,7 +231,7 @@ Score Entry::do_king_safety(const Position& pos) {
if (pos.can_castle(Us | QUEEN_SIDE))
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)));
return make_score(bonus, -16 * minKingPawnDistance);
return make_score(bonus, -16 * minPawnDist);
}
// Explicit template instantiation

View file

@ -38,15 +38,7 @@ struct Entry {
Bitboard passed_pawns(Color c) const { return passedPawns[c]; }
Bitboard pawn_attacks_span(Color c) const { return pawnAttacksSpan[c]; }
int weak_unopposed(Color c) const { return weakUnopposed[c]; }
int pawn_asymmetry() const { return asymmetry; }
int semiopen_file(Color c, File f) const {
return semiopenFiles[c] & (1 << f);
}
int pawns_on_same_color_squares(Color c, Square s) const {
return pawnsOnSquares[c][bool(DarkSquares & s)];
}
int passed_count() const { return popcount(passedPawns[WHITE] | passedPawns[BLACK]); };
template<Color Us>
Score king_safety(const Position& pos) {
@ -69,14 +61,11 @@ struct Entry {
Score kingSafety[COLOR_NB];
int weakUnopposed[COLOR_NB];
int castlingRights[COLOR_NB];
int semiopenFiles[COLOR_NB];
int pawnsOnSquares[COLOR_NB][COLOR_NB]; // [color][light/dark squares]
int asymmetry;
};
typedef HashTable<Entry, 16384> Table;
void init();
Entry* probe(const Position& pos);
} // namespace Pawns

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cstddef> // For offsetof()
#include <cstring> // For std::memset, std::memcmp
@ -341,13 +340,8 @@ void Position::set_castling_right(Color c, Square rfrom) {
Square kto = relative_square(c, cs == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(c, cs == KING_SIDE ? SQ_F1 : SQ_D1);
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); ++s)
if (s != kfrom && s != rfrom)
castlingPath[cr] |= s;
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); ++s)
if (s != kfrom && s != rfrom)
castlingPath[cr] |= s;
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto) | rto | kto)
& ~(square_bb(kfrom) | rfrom);
}
@ -629,7 +623,7 @@ bool Position::pseudo_legal(const Move m) const {
{
// We have already handled promotion moves, so destination
// cannot be on the 8th/1st rank.
if (rank_of(to) == relative_rank(us, RANK_8))
if ((Rank8BB | Rank1BB) & to)
return false;
if ( !(attacks_from<PAWN>(from, us) & pieces(~us) & to) // Not a capture

View file

@ -95,6 +95,7 @@ public:
template<PieceType Pt> int count() const;
template<PieceType Pt> const Square* squares(Color c) const;
template<PieceType Pt> Square square(Color c) const;
int semiopen_file(Color c, File f) const;
// Castling
int castling_rights(Color c) const;
@ -128,6 +129,7 @@ public:
// Piece specific
bool pawn_passed(Color c, Square s) const;
bool opposite_bishops() const;
int pawns_on_same_color_squares(Color c, Square s) const;
// Doing and undoing moves
void do_move(Move m, StateInfo& newSt);
@ -260,6 +262,10 @@ inline Square Position::ep_square() const {
return st->epSquare;
}
inline int Position::semiopen_file(Color c, File f) const {
return !(pieces(c, PAWN) & file_bb(f));
}
inline bool Position::can_castle(CastlingRight cr) const {
return st->castlingRights & cr;
}
@ -318,6 +324,10 @@ inline bool Position::advanced_pawn_push(Move m) const {
&& relative_rank(sideToMove, from_sq(m)) > RANK_4;
}
inline int Position::pawns_on_same_color_squares(Color c, Square s) const {
return popcount(pieces(c, PAWN) & ((DarkSquares & s) ? DarkSquares : ~DarkSquares));
}
inline Key Position::key() const {
return st->key;
}
@ -413,7 +423,7 @@ inline void Position::move_piece(Piece pc, Square from, Square to) {
// index[from] is not updated and becomes stale. This works as long as index[]
// is accessed just by known occupied squares.
Bitboard fromTo = SquareBB[from] ^ SquareBB[to];
Bitboard fromTo = square_bb(from) | square_bb(to);
byTypeBB[ALL_PIECES] ^= fromTo;
byTypeBB[type_of(pc)] ^= fromTo;
byColorBB[color_of(pc)] ^= fromTo;

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring> // For std::memset
@ -69,10 +68,10 @@ namespace {
}
// Reductions lookup table, initialized at startup
int Reductions[64]; // [depth or moveNumber]
int Reductions[MAX_MOVES]; // [depth or moveNumber]
template <bool PvNode> Depth reduction(bool i, Depth d, int mn) {
int r = Reductions[std::min(d / ONE_PLY, 63)] * Reductions[std::min(mn, 63)] / 1024;
int r = Reductions[d / ONE_PLY] * Reductions[mn] / 1024;
return ((r + 512) / 1024 + (!i && r > 1024) - PvNode) * ONE_PLY;
}
@ -86,11 +85,10 @@ namespace {
return d > 17 ? 0 : 29 * d * d + 138 * d - 134;
}
// Add a small random component to draw evaluations to keep search dynamic
// and to avoid 3fold-blindness.
// Add a small random component to draw evaluations to avoid 3fold-blindness
Value value_draw(Depth depth, Thread* thisThread) {
return depth < 4 ? VALUE_DRAW
: VALUE_DRAW + Value(2 * (thisThread->nodes.load(std::memory_order_relaxed) % 2) - 1);
return depth < 4 * ONE_PLY ? VALUE_DRAW
: VALUE_DRAW + Value(2 * (thisThread->nodes & 1) - 1);
}
// Skill structure is used to implement strength limit
@ -117,13 +115,6 @@ namespace {
void update_quiet_stats(const Position& pos, Stack* ss, Move move, Move* quiets, int quietCount, int bonus);
void update_capture_stats(const Position& pos, Move move, Move* captures, int captureCount, int bonus);
inline bool gives_check(const Position& pos, Move move) {
Color us = pos.side_to_move();
return type_of(move) == NORMAL && !(pos.blockers_for_king(~us) & pos.pieces(us))
? pos.check_squares(type_of(pos.moved_piece(move))) & to_sq(move)
: pos.gives_check(move);
}
// perft() is our utility to verify move generation. All the leaf nodes up
// to the given depth are generated and counted, and the sum is returned.
template<bool Root>
@ -157,7 +148,7 @@ namespace {
void Search::init() {
for (int i = 1; i < 64; ++i)
for (int i = 1; i < MAX_MOVES; ++i)
Reductions[i] = int(1024 * std::log(i) / std::sqrt(1.95));
}
@ -171,12 +162,12 @@ void Search::clear() {
Time.availableNodes = 0;
TT.clear();
Threads.clear();
Tablebases::init(Options["SyzygyPath"]); // Free up mapped files
Tablebases::init(Options["SyzygyPath"]); // Free mapped files
}
/// MainThread::search() is called by the main thread when the program receives
/// the UCI 'go' command. It searches from the root position and outputs the "bestmove".
/// MainThread::search() is started when the program receives the UCI 'go'
/// command. It searches from the root position and outputs the "bestmove".
void MainThread::search() {
@ -203,8 +194,11 @@ void MainThread::search() {
else
{
for (Thread* th : Threads)
{
th->bestMoveChanges = 0;
if (th != this)
th->start_searching();
}
Thread::search(); // Let's start searching!
}
@ -235,8 +229,9 @@ void MainThread::search() {
if (Limits.npmsec)
Time.availableNodes += Limits.inc[us] - Cluster::nodes_searched();
// Check if there are threads with a better score than main thread
Thread* bestThread = this;
// Check if there are threads with a better score than main thread
if ( Options["MultiPV"] == 1
&& !Limits.depth
&& !Skill(Options["Skill Level"]).enabled()
@ -310,9 +305,9 @@ void MainThread::search() {
void Thread::search() {
// To allow access to (ss-5) up to (ss+2), the stack must be oversized.
// To allow access to (ss-7) up to (ss+2), the stack must be oversized.
// The former is needed to allow update_continuation_histories(ss-1, ...),
// which accesses its argument at ss-4, also near the root.
// which accesses its argument at ss-6, also near the root.
// The latter is needed for statScores and killer initialization.
Stack stack[MAX_PLY+10], *ss = stack+7;
Move pv[MAX_PLY+1];
@ -320,7 +315,7 @@ void Thread::search() {
Move lastBestMove = MOVE_NONE;
Depth lastBestMoveDepth = DEPTH_ZERO;
MainThread* mainThread = (this == Threads.main() ? Threads.main() : nullptr);
double timeReduction = 1.0;
double timeReduction = 1, totBestMoveChanges = 0;
Color us = rootPos.side_to_move();
std::memset(ss-7, 0, 10 * sizeof(Stack));
@ -331,9 +326,6 @@ void Thread::search() {
bestValue = delta = alpha = -VALUE_INFINITE;
beta = VALUE_INFINITE;
if (mainThread)
mainThread->bestMoveChanges = 0;
size_t multiPV = Options["MultiPV"];
Skill skill(Options["Skill Level"]);
@ -354,7 +346,7 @@ void Thread::search() {
: Options["Analysis Contempt"] == "Black" && us == WHITE ? -ct
: ct;
// In evaluate.cpp the evaluation is from the white point of view
// Evaluation score is from the white point of view
contempt = (us == WHITE ? make_score(ct, ct / 2)
: -make_score(ct, ct / 2));
@ -365,7 +357,7 @@ void Thread::search() {
{
// Age out PV variability metric
if (mainThread)
mainThread->bestMoveChanges *= 0.517;
totBestMoveChanges /= 2;
// Save the last iteration's scores before first PV line is searched and
@ -504,15 +496,20 @@ void Thread::search() {
&& !Threads.stop
&& !mainThread->stopOnPonderhit)
{
double fallingEval = (306 + 9 * (mainThread->previousScore - bestValue)) / 581.0;
fallingEval = std::max(0.5, std::min(1.5, fallingEval));
double fallingEval = (314 + 9 * (mainThread->previousScore - bestValue)) / 581.0;
fallingEval = clamp(fallingEval, 0.5, 1.5);
// If the bestMove is stable over several iterations, reduce time accordingly
timeReduction = lastBestMoveDepth + 10 * ONE_PLY < completedDepth ? 1.95 : 1.0;
double reduction = std::pow(mainThread->previousTimeReduction, 0.528) / timeReduction;
// Use part of the gained time from a previous stable move for the current move
double bestMoveInstability = 1.0 + mainThread->bestMoveChanges;
for (Thread* th : Threads)
{
totBestMoveChanges += th->bestMoveChanges;
th->bestMoveChanges = 0;
}
double bestMoveInstability = 1 + totBestMoveChanges / Threads.size();
// Stop the search if we have only one legal move, or if available time elapsed
if ( rootMoves.size() == 1
@ -578,7 +575,7 @@ namespace {
Key posKey;
Move ttMove, move, excludedMove, bestMove;
Depth extension, newDepth;
Value bestValue, value, ttValue, eval, maxValue, pureStaticEval;
Value bestValue, value, ttValue, eval, maxValue;
bool ttHit, ttPv, inCheck, givesCheck, improving;
bool captureOrPromotion, doFullDepthSearch, moveCountPruning, ttCapture;
Piece movedPiece;
@ -624,8 +621,7 @@ namespace {
assert(0 <= ss->ply && ss->ply < MAX_PLY);
(ss+1)->ply = ss->ply + 1;
ss->currentMove = (ss+1)->excludedMove = bestMove = MOVE_NONE;
ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
(ss+1)->excludedMove = bestMove = MOVE_NONE;
(ss+2)->killers[0] = (ss+2)->killers[1] = MOVE_NONE;
Square prevSq = to_sq((ss-1)->currentMove);
@ -647,6 +643,15 @@ namespace {
: ttHit ? tte->move() : MOVE_NONE;
ttPv = (ttHit && tte->is_pv()) || (PvNode && depth > 4 * ONE_PLY);
// If position has been searched at higher depths and we are shuffling,
// return value_draw.
if ( pos.rule50_count() > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
&& ss->ply > 36 - 6 * (pos.count<ALL_PIECES>() > 14)
&& ttHit
&& tte->depth() > depth
&& pos.count<PAWN>() > 0)
return VALUE_DRAW;
// At non-PV nodes we check for an early TT cutoff
if ( !PvNode
&& ttHit
@ -663,9 +668,8 @@ namespace {
if (!pos.capture_or_promotion(ttMove))
update_quiet_stats(pos, ss, ttMove, nullptr, 0, stat_bonus(depth));
// 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())
// Extra penalty for early quiet moves of the previous ply
if ((ss-1)->moveCount <= 2 && !pos.captured_piece())
update_continuation_histories(ss-1, pos.piece_on(prevSq), prevSq, -stat_bonus(depth + ONE_PLY));
}
// Penalty for a quiet ttMove that fails low
@ -734,16 +738,16 @@ namespace {
// Step 6. Static evaluation of the position
if (inCheck)
{
ss->staticEval = eval = pureStaticEval = VALUE_NONE;
ss->staticEval = eval = VALUE_NONE;
improving = false;
goto moves_loop; // Skip early pruning when in check
}
else if (ttHit)
{
// Never assume anything on values stored in TT
ss->staticEval = eval = pureStaticEval = tte->eval();
ss->staticEval = eval = tte->eval();
if (eval == VALUE_NONE)
ss->staticEval = eval = pureStaticEval = evaluate(pos);
ss->staticEval = eval = evaluate(pos);
// Can ttValue be used as a better position evaluation?
if ( ttValue != VALUE_NONE
@ -756,15 +760,14 @@ namespace {
{
int bonus = -(ss-1)->statScore / 512;
pureStaticEval = evaluate(pos);
ss->staticEval = eval = pureStaticEval + bonus;
ss->staticEval = eval = evaluate(pos) + bonus;
}
else
ss->staticEval = eval = pureStaticEval = -(ss-1)->staticEval + 2 * Eval::Tempo;
ss->staticEval = eval = -(ss-1)->staticEval + 2 * Eval::Tempo;
Cluster::save(thisThread, tte,
posKey, VALUE_NONE, ttPv, BOUND_NONE, DEPTH_NONE, MOVE_NONE,
pureStaticEval);
eval);
}
// Step 7. Razoring (~2 Elo)
@ -788,7 +791,7 @@ namespace {
&& (ss-1)->currentMove != MOVE_NULL
&& (ss-1)->statScore < 23200
&& eval >= beta
&& pureStaticEval >= beta - 36 * depth / ONE_PLY + 225
&& ss->staticEval >= beta - 36 * depth / ONE_PLY + 225
&& !excludedMove
&& pos.non_pawn_material(us)
&& (ss->ply >= thisThread->nmpMinPly || us != thisThread->nmpColor))
@ -820,7 +823,7 @@ namespace {
// Do verification search at high depths, with null move pruning disabled
// for us, until ply exceeds nmpMinPly.
thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / 4;
thisThread->nmpMinPly = ss->ply + 3 * (depth-R) / (4 * ONE_PLY);
thisThread->nmpColor = us;
Value v = search<NonPV>(pos, ss, beta-1, beta, depth-R, false);
@ -871,8 +874,7 @@ namespace {
}
// Step 11. Internal iterative deepening (~2 Elo)
if ( depth >= 8 * ONE_PLY
&& !ttMove)
if (depth >= 8 * ONE_PLY && !ttMove)
{
search<NT>(pos, ss, alpha, beta, depth - 7 * ONE_PLY, cutNode);
@ -886,6 +888,7 @@ moves_loop: // When in check, search starts from here
const PieceToHistory* contHist[] = { (ss-1)->continuationHistory, (ss-2)->continuationHistory,
nullptr, (ss-4)->continuationHistory,
nullptr, (ss-6)->continuationHistory };
Move countermove = thisThread->counterMoves[pos.piece_on(prevSq)][prevSq];
MovePicker mp(pos, ttMove, depth, &thisThread->mainHistory,
@ -893,8 +896,8 @@ moves_loop: // When in check, search starts from here
contHist,
countermove,
ss->killers);
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
value = bestValue; // Workaround a bogus 'uninitialized' warning under gcc
moveCountPruning = false;
ttCapture = ttMove && pos.capture_or_promotion(ttMove);
@ -927,7 +930,7 @@ moves_loop: // When in check, search starts from here
extension = DEPTH_ZERO;
captureOrPromotion = pos.capture_or_promotion(move);
movedPiece = pos.moved_piece(move);
givesCheck = gives_check(pos, move);
givesCheck = pos.gives_check(move);
// Step 13. Extensions (~70 Elo)
@ -947,8 +950,9 @@ moves_loop: // When in check, search starts from here
&& pos.legal(move))
{
Value singularBeta = ttValue - 2 * depth / ONE_PLY;
Depth halfDepth = depth / (2 * ONE_PLY) * ONE_PLY; // ONE_PLY invariant
ss->excludedMove = move;
value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, depth / 2, cutNode);
value = search<NonPV>(pos, ss, singularBeta - 1, singularBeta, halfDepth, cutNode);
ss->excludedMove = MOVE_NONE;
if (value < singularBeta)
@ -972,6 +976,20 @@ moves_loop: // When in check, search starts from here
else if (type_of(move) == CASTLING)
extension = ONE_PLY;
// Shuffle extension
else if ( PvNode
&& pos.rule50_count() > 18
&& ss->ply > 18
&& depth < 3 * ONE_PLY
&& ss->ply < 3 * thisThread->rootDepth / ONE_PLY) // To avoid infinite loops
extension = ONE_PLY;
// Passed pawn extension
else if ( move == ss->killers[0]
&& pos.advanced_pawn_push(move)
&& pos.pawn_passed(us, to_sq(move)))
extension = ONE_PLY;
// Calculate new depth for this move
newDepth = depth - ONE_PLY + extension;
@ -981,7 +999,7 @@ moves_loop: // When in check, search starts from here
&& bestValue > VALUE_MATED_IN_MAX_PLY)
{
// Skip quiet moves if movecount exceeds our FutilityMoveCount threshold
moveCountPruning = moveCount >= futility_move_count(improving,depth / ONE_PLY);
moveCountPruning = moveCount >= futility_move_count(improving, depth / ONE_PLY);
if ( !captureOrPromotion
&& !givesCheck
@ -992,7 +1010,8 @@ moves_loop: // When in check, search starts from here
continue;
// Reduced depth of the next LMR search
int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO) / ONE_PLY;
int lmrDepth = std::max(newDepth - reduction<PvNode>(improving, depth, moveCount), DEPTH_ZERO);
lmrDepth /= ONE_PLY;
// Countermoves based pruning (~20 Elo)
if ( lmrDepth < 3 + ((ss-1)->statScore > 0 || (ss-1)->moveCount == 1)
@ -1137,8 +1156,8 @@ moves_loop: // When in check, search starts from here
// We record how often the best move has been changed in each
// iteration. This information is used for time management: When
// the best move changes frequently, we allocate some more time.
if (moveCount > 1 && thisThread == Threads.main())
++static_cast<MainThread*>(thisThread)->bestMoveChanges;
if (moveCount > 1)
++thisThread->bestMoveChanges;
}
else
// All other moves but the PV are set to the lowest value: this
@ -1225,7 +1244,7 @@ moves_loop: // When in check, search starts from here
posKey, value_to_tt(bestValue, ss->ply), ttPv,
bestValue >= beta ? BOUND_LOWER :
PvNode && bestMove ? BOUND_EXACT : BOUND_UPPER,
depth, bestMove, pureStaticEval);
depth, bestMove, ss->staticEval);
assert(bestValue > -VALUE_INFINITE && bestValue < VALUE_INFINITE);
@ -1264,8 +1283,7 @@ moves_loop: // When in check, search starts from here
Thread* thisThread = pos.this_thread();
(ss+1)->ply = ss->ply + 1;
ss->currentMove = bestMove = MOVE_NONE;
ss->continuationHistory = &thisThread->continuationHistory[NO_PIECE][0];
bestMove = MOVE_NONE;
inCheck = pos.checkers();
moveCount = 0;
@ -1355,7 +1373,7 @@ moves_loop: // When in check, search starts from here
{
assert(is_ok(move));
givesCheck = gives_check(pos, move);
givesCheck = pos.gives_check(move);
moveCount++;

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm> // For std::count
#include <cassert>
#include "movegen.h"
@ -32,7 +31,7 @@ ThreadPool Threads; // Global object
/// Thread constructor launches the thread and waits until it goes to sleep
/// in idle_loop(). Note that 'searching' and 'exit' should be alredy set.
/// in idle_loop(). Note that 'searching' and 'exit' should be already set.
Thread::Thread(size_t n) : idx(n), stdThread(&Thread::idle_loop, this) {

View file

@ -64,7 +64,7 @@ public:
size_t pvIdx, pvLast;
int selDepth, nmpMinPly;
Color nmpColor;
std::atomic<uint64_t> nodes, tbHits, TTsaves;
std::atomic<uint64_t> nodes, tbHits, TTsaves, bestMoveChanges;
Position rootPos;
Search::RootMoves rootMoves;
@ -93,7 +93,7 @@ struct MainThread : public Thread {
void search() override;
void check_time();
double bestMoveChanges, previousTimeReduction;
double previousTimeReduction;
Value previousScore;
int callsCnt;
bool stopOnPonderhit;

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cfloat>
#include <cmath>

View file

@ -18,7 +18,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include <ostream>
#include <sstream>