2008-08-31 23:59:13 -06:00
|
|
|
/*
|
2008-10-19 10:56:28 -06:00
|
|
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
|
|
|
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
|
2016-01-02 02:43:25 -07:00
|
|
|
Copyright (C) 2008-2015 Marco Costalba, Joona Kiiski, Tord Romstad
|
2018-11-19 03:18:21 -07:00
|
|
|
Copyright (C) 2015-2019 Marco Costalba, Joona Kiiski, Gary Linscott, Tord Romstad
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2008-10-19 10:56:28 -06:00
|
|
|
Stockfish is free software: you can redistribute it and/or modify
|
2008-08-31 23:59:13 -06:00
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2009-01-07 06:26:58 -07:00
|
|
|
|
2008-10-19 10:56:28 -06:00
|
|
|
Stockfish is distributed in the hope that it will be useful,
|
2008-08-31 23:59:13 -06:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2009-01-07 06:26:58 -07:00
|
|
|
|
2008-08-31 23:59:13 -06:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
2011-04-09 12:50:56 -06:00
|
|
|
#include "bitboard.h"
|
2008-08-31 23:59:13 -06:00
|
|
|
#include "pawns.h"
|
2009-03-18 08:36:03 -06:00
|
|
|
#include "position.h"
|
2014-12-30 02:31:50 -07:00
|
|
|
#include "thread.h"
|
2008-08-31 23:59:13 -06:00
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2012-03-28 23:04:44 -06:00
|
|
|
#define V Value
|
2009-11-13 09:23:21 -07:00
|
|
|
#define S(mg, eg) make_score(mg, eg)
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2018-06-02 09:41:37 -06:00
|
|
|
// Pawn penalties
|
2018-09-03 04:46:05 -06:00
|
|
|
constexpr Score Backward = S( 9, 24);
|
|
|
|
constexpr Score Doubled = S(11, 56);
|
|
|
|
constexpr Score Isolated = S( 5, 15);
|
2016-01-16 14:34:29 -07:00
|
|
|
|
2019-03-25 13:04:14 -06:00
|
|
|
// Connected pawn bonus
|
2019-04-11 08:38:53 -06:00
|
|
|
constexpr int Connected[RANK_NB] = { 0, 13, 17, 24, 59, 96, 171 };
|
2016-01-16 14:34:29 -07:00
|
|
|
|
2018-04-23 01:48:53 -06:00
|
|
|
// 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.
|
2018-07-15 06:30:59 -06:00
|
|
|
constexpr Value ShelterStrength[int(FILE_NB) / 2][RANK_NB] = {
|
2018-08-17 02:19:32 -06:00
|
|
|
{ V( -6), V( 81), V( 93), V( 58), V( 39), V( 18), V( 25) },
|
|
|
|
{ V(-43), V( 61), V( 35), V(-49), V(-29), V(-11), V( -63) },
|
|
|
|
{ V(-10), V( 75), V( 23), V( -2), V( 32), V( 3), V( -45) },
|
|
|
|
{ V(-39), V(-13), V(-29), V(-52), V(-48), V(-67), V(-166) }
|
2016-09-23 11:28:34 -06:00
|
|
|
};
|
2012-03-26 05:52:10 -06:00
|
|
|
|
2018-05-24 10:46:38 -06:00
|
|
|
// Danger of enemy pawns moving toward our king by [distance from edge][rank].
|
2018-06-02 09:41:37 -06:00
|
|
|
// RANK_1 = 0 is used for files where the enemy has no pawn, or their pawn
|
2018-05-24 10:46:38 -06:00
|
|
|
// is behind our king.
|
|
|
|
constexpr Value UnblockedStorm[int(FILE_NB) / 2][RANK_NB] = {
|
2018-07-15 06:30:59 -06:00
|
|
|
{ V( 89), V(107), V(123), V(93), V(57), V( 45), V( 51) },
|
|
|
|
{ V( 44), V(-18), V(123), V(46), V(39), V( -7), V( 23) },
|
|
|
|
{ V( 4), V( 52), V(162), V(37), V( 7), V(-14), V( -2) },
|
|
|
|
{ V(-10), V(-14), V( 90), V(15), V( 2), V( -7), V(-16) }
|
2016-09-23 11:28:34 -06:00
|
|
|
};
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2012-03-28 23:04:44 -06:00
|
|
|
#undef S
|
|
|
|
#undef V
|
2012-12-22 03:21:06 -07:00
|
|
|
|
|
|
|
template<Color Us>
|
2013-06-16 02:51:17 -06:00
|
|
|
Score evaluate(const Position& pos, Pawns::Entry* e) {
|
2012-12-22 03:21:06 -07:00
|
|
|
|
2018-03-18 16:38:58 -06:00
|
|
|
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
|
|
|
constexpr Direction Up = (Us == WHITE ? NORTH : SOUTH);
|
2012-12-22 03:21:06 -07:00
|
|
|
|
2018-12-11 05:47:56 -07:00
|
|
|
Bitboard b, neighbours, stoppers, doubled, support, phalanx;
|
Rework the "unsupported" penalty into a "supported" bonus
A pawn (according to all the searched positions of a bench run) is not supported 85% of the time,
(in current master it is either isolated, backward or "unsupported").
So it made sense to try moving the S(17, 8) "unsupported" penalty value into the base pawn value hoping for a more representative pawn value, and accordingly
a) adjust backward and isolated so that they stay more or less the same as master
b) increase the mg connected bonus in the supported case by S(17, 0) and let the Connected formula find a suitable eg value according to rank.
Tested as a simplification SPRT(-3, 1)
Passed STC
http://tests.stockfishchess.org/tests/view/5970dbd30ebc5916ff649dd6
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 19613 W: 3663 L: 3540 D: 12410
Passed LTC
http://tests.stockfishchess.org/tests/view/597137780ebc5916ff649de3
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 24721 W: 3306 L: 3191 D: 18224
Bench: 5581946
Closes #1179
2017-08-01 19:36:33 -06:00
|
|
|
Bitboard lever, leverPush;
|
2012-12-22 03:21:06 -07:00
|
|
|
Square s;
|
2017-01-19 10:16:23 -07:00
|
|
|
bool opposed, backward;
|
2014-12-27 02:47:21 -07:00
|
|
|
Score score = SCORE_ZERO;
|
2015-08-04 01:00:52 -06:00
|
|
|
const Square* pl = pos.squares<PAWN>(Us);
|
2012-12-22 03:21:06 -07:00
|
|
|
|
2017-06-11 15:31:15 -06:00
|
|
|
Bitboard ourPawns = pos.pieces( Us, PAWN);
|
2013-06-16 02:51:17 -06:00
|
|
|
Bitboard theirPawns = pos.pieces(Them, PAWN);
|
|
|
|
|
2017-09-16 06:07:41 -06:00
|
|
|
e->passedPawns[Us] = e->pawnAttacksSpan[Us] = e->weakUnopposed[Us] = 0;
|
2016-09-23 11:28:34 -06:00
|
|
|
e->kingSquares[Us] = SQ_NONE;
|
2018-02-21 14:31:38 -07:00
|
|
|
e->pawnAttacks[Us] = pawn_attacks_bb<Us>(ourPawns);
|
2013-06-16 02:51:17 -06:00
|
|
|
|
2012-12-22 03:21:06 -07:00
|
|
|
// Loop through all pawns of the current color and score each pawn
|
|
|
|
while ((s = *pl++) != SQ_NONE)
|
|
|
|
{
|
|
|
|
assert(pos.piece_on(s) == make_piece(Us, PAWN));
|
|
|
|
|
2014-10-06 05:26:30 -06:00
|
|
|
File f = file_of(s);
|
2019-04-11 08:38:53 -06:00
|
|
|
Rank r = relative_rank(Us, s);
|
2012-12-22 03:21:06 -07:00
|
|
|
|
2015-11-14 07:30:50 -07:00
|
|
|
e->pawnAttacksSpan[Us] |= pawn_attack_span(Us, s);
|
|
|
|
|
2015-02-24 03:33:40 -07:00
|
|
|
// Flag the pawn
|
2017-06-22 22:03:58 -06:00
|
|
|
opposed = theirPawns & forward_file_bb(Us, s);
|
2019-02-08 02:36:03 -07:00
|
|
|
stoppers = theirPawns & passed_pawn_span(Us, s);
|
2017-04-28 21:33:30 -06:00
|
|
|
lever = theirPawns & PawnAttacks[Us][s];
|
|
|
|
leverPush = theirPawns & PawnAttacks[Us][s + Up];
|
2017-04-11 11:50:24 -06:00
|
|
|
doubled = ourPawns & (s - Up);
|
2016-04-08 12:05:36 -06:00
|
|
|
neighbours = ourPawns & adjacent_files_bb(f);
|
|
|
|
phalanx = neighbours & rank_bb(s);
|
2018-12-11 05:47:56 -07:00
|
|
|
support = neighbours & rank_bb(s - Up);
|
2016-04-08 12:05:36 -06:00
|
|
|
|
2018-05-06 01:42:49 -06:00
|
|
|
// A pawn is backward when it is behind all pawns of the same color
|
|
|
|
// on the adjacent files and cannot be safely advanced.
|
Drop the lever condition for backwards
We can view the patch version as adding some "undermining bonus" for
level pawns, when the defending side can not easily avoid the exchange
by advancing her pawn.
• Case 1) White b2,c3, Black a3,b3:
Black is breaking through, b2 deserves a penalty
• Case 2) White b2,c3, Black a3,c4:
if b2xa3 then White ends up with a weak pawn on a3
and probably a weak pawn on c3 too.
In either case, White can still not safely play b2-b3 and make a
phalanx with c3, which is the essence of a backward pawn definition.
Passed STC in SPRT[0, 4]:
LLR: -2.96 (-2.94,2.94) [0.00,4.00]
Total: 131169 W: 26523 L: 26199 D: 78447
http://tests.stockfishchess.org/tests/view/5aefa4d50ebc5902a409a151
ELO 1.19 [-0.38,2.88] (95%)
Passed LTC in SPRT[-3, 1]:
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 24824 W: 3732 L: 3617 D: 17475
http://tests.stockfishchess.org/tests/view/5af04d3f0ebc5902a88b2e55
ELO 1.27 [-1.21,3.70] (95%)
Closes https://github.com/official-stockfish/Stockfish/pull/1584
How to continue from there?
There were some promising tests a couple of months ago about adding
a lever condition for king danger in evaluate.cpp, maybe it would
be time to re-try this after all the recent changes in pawns.cpp
Bench: 4773882
2018-05-08 03:00:51 -06:00
|
|
|
backward = !(ourPawns & pawn_attack_span(Them, s + Up))
|
|
|
|
&& (stoppers & (leverPush | (s + Up)));
|
2012-12-22 03:21:06 -07:00
|
|
|
|
|
|
|
// Passed pawns will be properly scored in evaluation because we need
|
2017-01-19 10:16:23 -07:00
|
|
|
// full attack info to evaluate them. Include also not passed pawns
|
|
|
|
// which could become passed after one or two pawn pushes when are
|
|
|
|
// not attacked more times than defended.
|
|
|
|
if ( !(stoppers ^ lever ^ leverPush)
|
2019-03-24 10:37:38 -06:00
|
|
|
&& (support || !more_than_one(lever))
|
2019-01-01 06:10:26 -07:00
|
|
|
&& popcount(phalanx) >= popcount(leverPush))
|
2012-12-22 03:21:06 -07:00
|
|
|
e->passedPawns[Us] |= s;
|
|
|
|
|
2019-04-11 08:38:53 -06:00
|
|
|
else if (stoppers == square_bb(s + Up) && r >= RANK_5)
|
2017-05-09 05:50:05 -06:00
|
|
|
{
|
2018-12-11 05:47:56 -07:00
|
|
|
b = shift<Up>(support) & ~theirPawns;
|
2017-05-09 05:50:05 -06:00
|
|
|
while (b)
|
|
|
|
if (!more_than_one(theirPawns & PawnAttacks[Us][pop_lsb(&b)]))
|
2017-05-07 21:38:03 -06:00
|
|
|
e->passedPawns[Us] |= s;
|
2017-05-09 05:50:05 -06:00
|
|
|
}
|
2017-05-07 21:38:03 -06:00
|
|
|
|
2012-12-22 03:21:06 -07:00
|
|
|
// Score this pawn
|
2018-12-11 05:47:56 -07:00
|
|
|
if (support | phalanx)
|
2019-03-25 13:04:14 -06:00
|
|
|
{
|
2019-04-11 08:38:53 -06:00
|
|
|
int v = (phalanx ? 3 : 2) * Connected[r];
|
2019-03-25 13:04:14 -06:00
|
|
|
v = 17 * popcount(support) + (v >> (opposed + 1));
|
|
|
|
score += make_score(v, v * (r - 2) / 4);
|
|
|
|
}
|
Rework the "unsupported" penalty into a "supported" bonus
A pawn (according to all the searched positions of a bench run) is not supported 85% of the time,
(in current master it is either isolated, backward or "unsupported").
So it made sense to try moving the S(17, 8) "unsupported" penalty value into the base pawn value hoping for a more representative pawn value, and accordingly
a) adjust backward and isolated so that they stay more or less the same as master
b) increase the mg connected bonus in the supported case by S(17, 0) and let the Connected formula find a suitable eg value according to rank.
Tested as a simplification SPRT(-3, 1)
Passed STC
http://tests.stockfishchess.org/tests/view/5970dbd30ebc5916ff649dd6
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 19613 W: 3663 L: 3540 D: 12410
Passed LTC
http://tests.stockfishchess.org/tests/view/597137780ebc5916ff649de3
LLR: 2.95 (-2.94,2.94) [-3.00,1.00]
Total: 24721 W: 3306 L: 3191 D: 18224
Bench: 5581946
Closes #1179
2017-08-01 19:36:33 -06:00
|
|
|
else if (!neighbours)
|
2017-09-16 06:07:41 -06:00
|
|
|
score -= Isolated, e->weakUnopposed[Us] += !opposed;
|
2015-03-29 01:24:17 -06:00
|
|
|
|
2015-03-28 17:30:46 -06:00
|
|
|
else if (backward)
|
2017-09-16 06:07:41 -06:00
|
|
|
score -= Backward, e->weakUnopposed[Us] += !opposed;
|
2015-03-29 01:24:17 -06:00
|
|
|
|
2018-12-11 05:47:56 -07:00
|
|
|
if (doubled && !support)
|
2017-05-09 05:50:05 -06:00
|
|
|
score -= Doubled;
|
2012-12-22 03:21:06 -07:00
|
|
|
}
|
|
|
|
|
2014-12-27 02:47:21 -07:00
|
|
|
return score;
|
2012-12-22 03:21:06 -07:00
|
|
|
}
|
2013-06-16 02:51:17 -06:00
|
|
|
|
|
|
|
} // namespace
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2012-12-22 03:21:06 -07:00
|
|
|
namespace Pawns {
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-12-30 02:31:50 -07:00
|
|
|
/// 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
|
|
|
|
/// have to recompute all when the same pawns configuration occurs again.
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-12-30 02:31:50 -07:00
|
|
|
Entry* probe(const Position& pos) {
|
2008-12-09 08:46:10 -07:00
|
|
|
|
2011-12-25 03:50:59 -07:00
|
|
|
Key key = pos.pawn_key();
|
2014-12-30 02:31:50 -07:00
|
|
|
Entry* e = pos.this_thread()->pawnsTable[key];
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2012-03-31 05:15:57 -06:00
|
|
|
if (e->key == key)
|
|
|
|
return e;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2012-03-31 05:15:57 -06:00
|
|
|
e->key = key;
|
2018-02-20 09:10:37 -07:00
|
|
|
e->scores[WHITE] = evaluate<WHITE>(pos, e);
|
|
|
|
e->scores[BLACK] = evaluate<BLACK>(pos, e);
|
|
|
|
|
2012-03-31 05:15:57 -06:00
|
|
|
return e;
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
2009-07-24 04:16:18 -06:00
|
|
|
|
|
|
|
|
2018-04-23 01:48:53 -06:00
|
|
|
/// Entry::evaluate_shelter() calculates the shelter bonus and the storm
|
|
|
|
/// penalty for a king, looking at the king file and the two closest files.
|
2012-03-28 23:04:44 -06:00
|
|
|
|
2012-03-26 05:52:10 -06:00
|
|
|
template<Color Us>
|
2018-04-23 01:48:53 -06:00
|
|
|
Value Entry::evaluate_shelter(const Position& pos, Square ksq) {
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2018-04-18 12:03:37 -06:00
|
|
|
constexpr Color Them = (Us == WHITE ? BLACK : WHITE);
|
2018-05-02 05:20:47 -06:00
|
|
|
constexpr Direction Down = (Us == WHITE ? SOUTH : NORTH);
|
|
|
|
constexpr Bitboard BlockRanks = (Us == WHITE ? Rank1BB | Rank2BB : Rank8BB | Rank7BB);
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2018-07-21 00:17:27 -06:00
|
|
|
Bitboard b = pos.pieces(PAWN) & ~forward_ranks_bb(Them, ksq);
|
2013-07-25 14:23:15 -06:00
|
|
|
Bitboard ourPawns = b & pos.pieces(Us);
|
2012-03-28 04:44:41 -06:00
|
|
|
Bitboard theirPawns = b & pos.pieces(Them);
|
2018-04-23 01:48:53 -06:00
|
|
|
|
2018-08-17 02:19:32 -06:00
|
|
|
Value safety = (shift<Down>(theirPawns) & (FileABB | FileHBB) & BlockRanks & ksq) ?
|
|
|
|
Value(374) : Value(5);
|
2018-05-01 15:50:23 -06:00
|
|
|
|
2019-03-31 02:48:27 -06:00
|
|
|
File center = clamp(file_of(ksq), FILE_B, FILE_G);
|
2017-12-04 09:52:31 -07:00
|
|
|
for (File f = File(center - 1); f <= File(center + 1); ++f)
|
2012-03-28 04:44:41 -06:00
|
|
|
{
|
2013-10-24 12:34:11 -06:00
|
|
|
b = ourPawns & file_bb(f);
|
2018-12-11 05:47:56 -07:00
|
|
|
Rank ourRank = b ? relative_rank(Us, backmost_sq(Us, b)) : RANK_1;
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2017-06-22 22:03:58 -06:00
|
|
|
b = theirPawns & file_bb(f);
|
2018-12-11 05:47:56 -07:00
|
|
|
Rank theirRank = b ? relative_rank(Us, frontmost_sq(Them, b)) : RANK_1;
|
2014-01-10 19:00:17 -07:00
|
|
|
|
2017-12-04 09:52:31 -07:00
|
|
|
int d = std::min(f, ~f);
|
2018-05-10 07:46:13 -06:00
|
|
|
safety += ShelterStrength[d][ourRank];
|
2018-11-19 23:45:00 -07:00
|
|
|
safety -= (ourRank && (ourRank == theirRank - 1)) ? 66 * (theirRank == RANK_3)
|
2018-05-24 10:46:38 -06:00
|
|
|
: UnblockedStorm[d][theirRank];
|
2012-03-26 05:52:10 -06:00
|
|
|
}
|
2012-03-28 04:44:41 -06:00
|
|
|
|
|
|
|
return safety;
|
2012-03-26 05:52:10 -06:00
|
|
|
}
|
2011-04-09 12:50:56 -06:00
|
|
|
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2014-04-13 05:35:58 -06:00
|
|
|
/// Entry::do_king_safety() calculates a bonus for king safety. It is called only
|
|
|
|
/// when king square changes, which is about 20% of total king_safety() calls.
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2011-04-09 12:50:56 -06:00
|
|
|
template<Color Us>
|
2018-11-10 20:49:13 -07:00
|
|
|
Score Entry::do_king_safety(const Position& pos) {
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2018-11-10 20:49:13 -07:00
|
|
|
Square ksq = pos.square<KING>(Us);
|
2012-03-28 23:04:44 -06:00
|
|
|
kingSquares[Us] = ksq;
|
2018-12-11 05:47:56 -07:00
|
|
|
castlingRights[Us] = pos.castling_rights(Us);
|
2015-03-07 02:03:19 -07:00
|
|
|
int minKingPawnDistance = 0;
|
2012-06-17 02:03:05 -06:00
|
|
|
|
|
|
|
Bitboard pawns = pos.pieces(Us, PAWN);
|
|
|
|
if (pawns)
|
2018-08-20 10:29:46 -06:00
|
|
|
while (!(DistanceRingBB[ksq][++minKingPawnDistance] & pawns)) {}
|
2011-04-09 12:50:56 -06:00
|
|
|
|
2018-04-23 01:48:53 -06:00
|
|
|
Value bonus = evaluate_shelter<Us>(pos, ksq);
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2013-12-01 02:25:10 -07:00
|
|
|
// If we can castle use the bonus after the castling if it is bigger
|
2018-10-14 13:50:31 -06:00
|
|
|
if (pos.can_castle(Us | KING_SIDE))
|
2018-04-23 01:48:53 -06:00
|
|
|
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_G1)));
|
2012-03-28 04:44:41 -06:00
|
|
|
|
2018-10-14 13:50:31 -06:00
|
|
|
if (pos.can_castle(Us | QUEEN_SIDE))
|
2018-04-23 01:48:53 -06:00
|
|
|
bonus = std::max(bonus, evaluate_shelter<Us>(pos, relative_square(Us, SQ_C1)));
|
2012-03-26 05:52:10 -06:00
|
|
|
|
2015-03-07 02:03:19 -07:00
|
|
|
return make_score(bonus, -16 * minKingPawnDistance);
|
2011-04-09 12:50:56 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Explicit template instantiation
|
2018-11-10 20:49:13 -07:00
|
|
|
template Score Entry::do_king_safety<WHITE>(const Position& pos);
|
|
|
|
template Score Entry::do_king_safety<BLACK>(const Position& pos);
|
2012-12-22 03:21:06 -07:00
|
|
|
|
|
|
|
} // namespace Pawns
|