0% found this document useful (0 votes)
115 views9 pages

Pawns

The document describes the evaluation of pawn structures in chess positions by a chess engine called Stockfish. It defines various penalties and bonuses for different pawn characteristics and configurations. It also calculates a king safety bonus based on factors such as pawn shelter and potential enemy pawn storms.

Uploaded by

alphabeta1729
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
115 views9 pages

Pawns

The document describes the evaluation of pawn structures in chess positions by a chess engine called Stockfish. It defines various penalties and bonuses for different pawn characteristics and configurations. It also calculates a king safety bonus based on factors such as pawn shelter and potential enemy pawn storms.

Uploaded by

alphabeta1729
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 9

/*

Stockfish, a UCI chess playing engine derived from Glaurung


2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2014 Marco Costalba, Joona Kiiski, Tord
Romstad
Stockfish is free software: you can redistribute it and/or
modify
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.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty

of

MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


the
GNU General Public License for more details.

See

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 <algorithm>
#include <cassert>
#include
#include
#include
#include

"bitboard.h"
"bitcount.h"
"pawns.h"
"position.h"

namespace {
#define V Value
#define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by file
const Score Doubled[FILE_NB] = {
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
S(23, 48), S(23, 48), S(20, 48), S(13, 43) };
// Isolated pawn penalty by opposed flag and file

const Score Isolated[2][FILE_NB] =


{ S(37, 45), S(54, 52), S(60, 52),
S(60, 52), S(60, 52), S(54, 52),
{ S(25, 30), S(36, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35),

{
S(60,
S(37,
S(40,
S(25,

// Backward pawn penalty by opposed flag


const Score Backward[2][FILE_NB] = {
{ S(30, 42), S(43, 46), S(49, 46), S(49,
S(49, 46), S(49, 46), S(43, 46), S(30,
{ S(20, 28), S(29, 31), S(33, 31), S(33,
S(33, 31), S(33, 31), S(29, 31), S(20,

52),
45) },
35),
30) } };
and file
46),
42) },
31),
28) } };

// Connected pawn bonus by file and rank (initialized by


formula)
Score Connected[FILE_NB][RANK_NB];
// Candidate passed pawn bonus by rank
const Score CandidatePassed[RANK_NB] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(83,166), S(0, 0), S( 0, 0) };
// Bonus for file distance of the two outermost pawns
const Score PawnsFileSpan = S(0, 15);
// Unsupported pawn penalty
const Score UnsupportedPawnPenalty = S(20, 10);
// Weakness of our pawn shelter in front of the king indexed
by [rank]
const Value ShelterWeakness[RANK_NB] =
{ V(100), V(0), V(27), V(73), V(92), V(101), V(101) };
// Danger of enemy pawns moving toward our king indexed by
// [no friendly pawn | pawn unblocked | pawn blocked][rank
of enemy pawn]
const Value StormDanger[3][RANK_NB] = {
{ V( 0), V(64), V(128), V(51), V(26) },
{ V(26), V(32), V( 96), V(38), V(20) },
{ V( 0), V( 0), V(160), V(25), V(13) } };
// Max bonus for king safety. Corresponds to start position
with all the pawns
// in front of the king and no enemy pawn on the horizon.
const Value MaxSafetyBonus = V(263);
#undef S

#undef V
template<Color Us>
Score evaluate(const Position& pos, Pawns::Entry* e) {
const
const
const
const

Color
Square
Square
Square

Them
Up
Right
Left

=
=
=
=

(Us
(Us
(Us
(Us

==
==
==
==

WHITE
WHITE
WHITE
WHITE

?
?
?
?

BLACK
DELTA_N
DELTA_NE
DELTA_NW

:
:
:
:

WHITE);
DELTA_S);
DELTA_SW);
DELTA_SE);

Bitboard b, p, doubled;
Square s;
File f;
bool passed, isolated, opposed, connected, backward,
candidate, unsupported;
Score value = SCORE_ZERO;
const Square* pl = pos.list<PAWN>(Us);
Bitboard ourPawns = pos.pieces(Us, PAWN);
Bitboard theirPawns = pos.pieces(Them, PAWN);
e->passedPawns[Us] = e->candidatePawns[Us] = 0;
e->kingSquares[Us] = SQ_NONE;
e->semiopenFiles[Us] = 0xFF;
e->pawnAttacks[Us] = shift_bb<Right>(ourPawns) |
shift_bb<Left>(ourPawns);
e->pawnsOnSquares[Us][BLACK] = popcount<Max15>(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)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = file_of(s);
// This file cannot be semi-open
e->semiopenFiles[Us] &= ~(1 << f);
// Previous rank
p = rank_bb(s - pawn_push(Us));
// Our rank plus previous one
b = rank_bb(s) | p;

one).
p);

// Flag the pawn as passed, isolated, doubled,


// unsupported or connected (but not the backward
connected
=
ourPawns
unsupported = !(ourPawns

& adjacent_files_bb(f) & b;


& adjacent_files_bb(f) &

isolated
doubled
opposed
passed

&
&
&
&

= !(ourPawns
=
ourPawns
=
theirPawns
= !(theirPawns

adjacent_files_bb(f));
forward_bb(Us, s);
forward_bb(Us, s);
passed_pawn_mask(Us, s));

// Test for backward pawn.


// If the pawn is passed, isolated, or connected it
cannot be
// backward. If there are friendly pawns behind on
adjacent files
// or if it can capture an enemy pawn it cannot be
backward either.
if (
(passed | isolated | connected)
|| (ourPawns & pawn_attack_span(Them, s))
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns))
backward = false;
else
{
// We now know that there are no friendly pawns
beside or behind this
// pawn on adjacent files. We now check whether
the pawn is
// backward by looking in the forward direction on
the adjacent
// files, and picking the closest pawn there.
b = pawn_attack_span(Us, s) & (ourPawns |
theirPawns);
b = pawn_attack_span(Us, s) &
rank_bb(backmost_sq(Us, b));
// If we have an enemy pawn in the same or next
rank, the pawn is
// backward because it cannot advance without
being captured.
backward = (b | shift_bb<Up>(b)) & theirPawns;
}
assert(opposed | passed | (pawn_attack_span(Us, s) &
theirPawns));

// A not-passed pawn is a candidate to become passed,


if it is free to
// advance and if the number of friendly pawns beside
or behind this
// pawn on adjacent files is higher than or equal to
the number of
// enemy pawns in the forward direction on the
adjacent files.
candidate =
!(opposed | passed | backward |
isolated)
&& (b = pawn_attack_span(Them, s +
pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >=
popcount<Max15>(pawn_attack_span(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation
because we need
// full attack info to evaluate passed pawns. Only the
frontmost passed
// pawn on each file is considered a true passed pawn.
if (passed && !doubled)
e->passedPawns[Us] |= s;
// Score this pawn
if (isolated)
value -= Isolated[opposed][f];
if (unsupported && !isolated)
value -= UnsupportedPawnPenalty;
if (doubled)
value -= Doubled[f] / rank_distance(s,
lsb(doubled));
if (backward)
value -= Backward[opposed][f];
if (connected)
value += Connected[f][relative_rank(Us, s)];
if (candidate)
{
value += CandidatePassed[relative_rank(Us, s)];

if (!doubled)
e->candidatePawns[Us] |= s;

}
//
give a
//
pawns.
if
{
}
}

In endgame it's better to have pawns on both wings. So


bonus according
to file distance between left and right outermost
(pos.count<PAWN>(Us) > 1)
b = e->semiopenFiles[Us] ^ 0xFF;
value += PawnsFileSpan * int(msb(b) - lsb(b));

return value;

} // namespace
namespace Pawns {
/// init() initializes some tables by formula instead of hardcoding their values
void init() {
const int bonusesByFile[8] = { 1, 3, 3, 4, 4, 3, 3, 1 };
int bonus;
for (Rank r = RANK_1; r <
for (File f = FILE_A;
{
bonus = r * (r-1)
+ 1);
Connected[f][r] =
}
}

RANK_8; ++r)
f <= FILE_H; ++f)
* (r-2) + bonusesByFile[f] * (r/2
make_score(bonus, bonus);

/// probe() takes a position object as input, computes a Entry


object, and returns
/// a pointer to it. The result is also stored in a hash
table, so we don't have
/// to recompute everything when the same pawn structure
occurs again.
Entry* probe(const Position& pos, Table& entries) {
Key key = pos.pawn_key();

Entry* e = entries[key];
if (e->key == key)
return e;
e->key = key;
e->value = evaluate<WHITE>(pos, e) - evaluate<BLACK>(pos,
e);
return e;
}
/// Entry::shelter_storm() calculates shelter and storm
penalties for the file
/// the king is on, as well as the two adjacent files.
template<Color Us>
Value Entry::shelter_storm(const Position& pos, Square ksq) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
static const Bitboard MiddleEdges = (FileABB | FileHBB) &
(Rank2BB | Rank3BB);
Value safety = MaxSafetyBonus;
Bitboard b = pos.pieces(PAWN) & (in_front_bb(Us,
rank_of(ksq)) | rank_bb(ksq));
Bitboard ourPawns = b & pos.pieces(Us);
Bitboard theirPawns = b & pos.pieces(Them);
Rank rkUs, rkThem;
File kf = std::max(FILE_B, std::min(FILE_G, file_of(ksq)));
for (File f = kf - File(1); f <= kf + File(1); ++f)
{
b = ourPawns & file_bb(f);
rkUs = b ? relative_rank(Us, backmost_sq(Us, b)) :
RANK_1;
b = theirPawns & file_bb(f);
rkThem = b ? relative_rank(Us, frontmost_sq(Them, b)) :
RANK_1;
if (

else

(MiddleEdges & make_square(f, rkThem))


&& file_of(ksq) == f
&& relative_rank(Us, ksq) == rkThem - 1)
safety += 200;
safety -= ShelterWeakness[rkUs]

+ StormDanger[rkUs == RANK_1 ? 0 : rkThem ==


rkUs + 1 ? 2 : 1][rkThem];
}
}

return safety;

/// 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.
template<Color Us>
Score Entry::do_king_safety(const Position& pos, Square ksq) {
kingSquares[Us] = ksq;
castlingRights[Us] = pos.can_castle(Us);
minKPdistance[Us] = 0;
Bitboard pawns = pos.pieces(Us, PAWN);
if (pawns)
while (!(DistanceRingsBB[ksq][minKPdistance[Us]++] &
pawns)) {}
if (relative_rank(Us, ksq) > RANK_4)
return make_score(0, -16 * minKPdistance[Us]);
Value bonus = shelter_storm<Us>(pos, ksq);
// If we can castle use the bonus after the castling if it
is bigger
if (pos.can_castle(MakeCastling<Us, KING_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos,
relative_square(Us, SQ_G1)));
if (pos.can_castle(MakeCastling<Us, QUEEN_SIDE>::right))
bonus = std::max(bonus, shelter_storm<Us>(pos,
relative_square(Us, SQ_C1)));
}

return make_score(bonus, -16 * minKPdistance[Us]);

// Explicit template instantiation


template Score Entry::do_king_safety<WHITE>(const Position&
pos, Square ksq);
template Score Entry::do_king_safety<BLACK>(const Position&

pos, Square ksq);


} // namespace Pawns

You might also like