Eelco de Groot

Joined: 12 Mar 2006 Posts: 2596 Location: Groningen
|
Post subject: Re: Stockfish - material balance/imbalance evaluation Posted: Thu May 06, 2010 1:19 pm |
|
|
| Ralph Stoesser wrote: |
| Eelco de Groot wrote: |
...
I take it you have information about blocked pawns in the dynamic evaluation Ralph? I was wondering if computing the colour of pawnsquares is costly to perform? I do believe I do something like colour detection of passed pawns and Bishops, but not for blocked pawns. And not sure I'm doing it efficiently.
Stockfish may be difficult to improve, but it is always possible! Do you have any plans for making your own version of Stockfish public Ralph
Regards, Eelco |
For this test I simply took all pawns (temporarily) blocked by an opponent pawn.
In pawns.cpp, PawnInfoTable::evaluate_pawns(), for every pawn I calculated
| Code: |
bool blocked = theirPawns & file_bb(f) & rank_bb(r + (Us == WHITE ? 1 : -1))
if (blocked)
set_bit(&(pi->blockedPawns[Us]), s);
|
Maybe not that efficient, but simple to implement. Then in evaluate() pi->blockedPawns[Us] & WhiteSquaresBB is the bitboard of our white colored blocked pawns, pi->blockedPawns[Us] & BlackSquaresBB are our black colored blocked pawns.
No I don't plan to make my own version. I just fiddle around with SF code. |
Hi Ralph,
Thanks very much for your code example! You don't have to show your code of course, but thanks for the ideas! Blocked pawns is also in Rainbow Serpent, but mainly tried for determining lower chances of a win, at least in theory but there were some bugs as I believe I posted.
For myself I try to not look too much at the Stockfish code for a while because it gets in the way of other things I should be doing. Temporary break, at least that is the deal. But your BlackSquaresBB and WhiteSquaresBB idea looks really cool! There was already a version of blockedPawns in Rainbow Serpent but I have not really tested if everything works. I don't know if my version of pawns.cpp fits in this post, let's try. The inlining looks reasonably okay, and it seems to fit so here is my version. Rainbow Serpent header and some other changes to backward pawns mainly, my version of blockedPawns, different scoring of candidate pawns. I don't expect everything to be bugfree here, sorry for any mistakes, as I am no real programmer there must be some things that can be sped up:
| Code: |
/*
Rainbow Serpent, a UCI chess playing engine based on Stockfish 1.7.1
which in turn is derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Copyright source changes from Stockfish 1.7.1 (C) 2010 Eelco de Groot
Rainbow Serpent 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.
Rainbow Serpent 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
////
//// Includes
////
#include <cassert>
#include <cstring>
#include "bitcount.h"
#include "pawns.h"
#include "position.h"
////
//// Local definitions
////
namespace {
/// Constants and variables
#define S(mg, eg) make_score(mg, eg)
// Doubled pawn penalty by file
const Score DoubledPawnPenalty[8] = {
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 file
const Score IsolatedPawnPenalty[8] = {
S(25, 30), S(36, 35), S(40, 35), S(40, 35),
S(40, 35), S(40, 35), S(36, 35), S(25, 30)
};
// Backward pawn penalty by file
const Score BackwardPawnPenalty[8] = {
S(20, 35), S(29, 38), S(33, 40), S(33, 40),
S(33, 40), S(33, 40), S(29, 38), S(20, 35)
};
// Pawn chain membership bonus by file
const Score ChainBonus[8] = {
S(11,4), S(13,4), S(13,4), S(14,4),
S(14,4), S(13,4), S(13,4), S(11,4)
};
// Candidate passed pawn bonus by rank
const Score CandidateBonus[8] = {
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
S(34,68), S(80,170), S(0, 0), S( 0, 0)
};
// Pawn storm tables for positions with opposite castling
const int QStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
-22,-22,-22,-14,-6, 0, 0, 0,
-6,-10,-10,-10,-6, 0, 0, 0,
4, 12, 16, 12, 4, 0, 0, 0,
16, 23, 23, 16, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
23, 31, 31, 23, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
const int KStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,-10,-19,-28,-33,-33,
0, 0, 0,-10,-15,-19,-24,-24,
0, 0, 0, 0, 1, 1, 1, 1,
0, 0, 0, 0, 1, 10, 19, 19,
0, 0, 0, 0, 1, 19, 31, 27,
0, 0, 0, 0, 0, 22, 31, 22,
0, 0, 0, 0, 0, 0, 0, 0
};
// Pawn storm open file bonuses by file
const int16_t KStormOpenFileBonus[8] = { 31, 31, 18, 0, 0, 0, 0, 0 };
const int16_t QStormOpenFileBonus[8] = { 0, 0, 0, 0, 0, 26, 42, 26 };
// Pawn storm lever bonuses by file
const int StormLeverBonus[8] = { -8, -8, -13, 0, 0, -13, -8, -8 };
}
////
//// Functions
////
/// Constructor
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
size = numOfEntries;
entries = new PawnInfo[size];
if (!entries)
{
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl;
Application::exit_with_failure();
}
}
/// Destructor
PawnInfoTable::~PawnInfoTable() {
delete [] entries;
}
/// PawnInfo::clear() resets to zero the PawnInfo entry. Note that
/// kingSquares[] is initialized to SQ_NONE instead.
void PawnInfo::clear() {
memset(this, 0, sizeof(PawnInfo));
kingSquares[WHITE] = kingSquares[BLACK] = SQ_NONE;
}
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes
/// a PawnInfo 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.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
int index = int(key & (size - 1));
PawnInfo* pi = entries + index;
// If pi->key matches the position's pawn hash key, it means that we
// have analysed this pawn structure before, and we can simply return
// the information we found the last time instead of recomputing it.
if (pi->key == key)
return pi;
// Clear the PawnInfo object, and set the key
pi->clear();
pi->key = key;
// Calculate pawn attacks
Bitboard whitePawns = pos.pieces(PAWN, WHITE);
Bitboard blackPawns = pos.pieces(PAWN, BLACK);
pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
// Evaluate pawns for both colors
pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
return pi;
}
/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color
template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) {
Square s;
File f;
Rank r;
bool passed, isolated, doubled, chain, backward, candidate, blocked, indirectlyBlocked, open, attackedBlocksquare;
int bonus, candidatesupportcount;
Score value = make_score(0, 0);
const Square* ptr = pos.piece_list_begin(Us, PAWN);
const Color Them = (Us == WHITE ? BLACK : WHITE);
// Initialize pawn storm scores by giving bonuses for open files
for (f = FILE_A; f <= FILE_H; f++)
if (!(ourPawns & file_bb(f)))
{
pi->ksStormValue[Us] += KStormOpenFileBonus[f];
pi->qsStormValue[Us] += QStormOpenFileBonus[f];
pi->halfOpenFiles[Us] |= (1 << f);
}
// Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE)
{
f = square_file(s);
r = square_rank(s);
Square blockSq = s + pawn_push(Us);
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
// Pawn blocked by enemy pawn?
blocked = (!pos.square_is_empty(blockSq) && bit_is_set(theirPawns, blockSq));
indirectlyBlocked = blocked;
// Pawn on half-open file?
open = !blocked && !(theirPawns & file_bb(f));
// Passed, isolated or doubled pawn?
passed = open && Position::pawn_is_passed(theirPawns, Us, s);
isolated = Position::pawn_is_isolated(ourPawns, s);
doubled = Position::pawn_is_doubled(ourPawns, Us, s);
// Square in front attacked by enemy pawn?
attackedBlocksquare = pos.attacks_from<PAWN>(blockSq, Us) & theirPawns;
// We calculate kingside and queenside pawn storm
// scores for both colors. These are used when evaluating
// middle game positions with opposite side castling.
//
// Each pawn is given a base score given by a piece square table
// (KStormTable[] or QStormTable[]). Pawns which seem to have good
// chances of creating an open file by exchanging itself against an
// enemy pawn on an adjacent file gets an additional bonus.
// Kingside pawn storms
bonus = KStormTable[relative_square(Us, s)];
if (f >= FILE_F)
{
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
while (b)
{
// Give a bonus according to the distance of the nearest enemy pawn
Square s2 = pop_1st_bit(&b);
int v = StormLeverBonus[f] - 2 * square_distance(s, s2);
// If enemy pawn has no pawn beside itself is particularly vulnerable.
// Big bonus, especially against a weakness on the rook file
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
v *= (square_file(s2) == FILE_H ? 4 : 2);
bonus += v;
}
}
pi->ksStormValue[Us] += bonus;
// Queenside pawn storms
bonus = QStormTable[relative_square(Us, s)];
if (f <= FILE_C)
{
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileABB | FileBBB | FileCBB);
while (b)
{
// Give a bonus according to the distance of the nearest enemy pawn
Square s2 = pop_1st_bit(&b);
int v = StormLeverBonus[f] - 4 * square_distance(s, s2);
// If enemy pawn has no pawn beside itself is particularly vulnerable.
// Big bonus, especially against a weakness on the rook file
if (!(theirPawns & neighboring_files_bb(s2) & rank_bb(s2)))
v *= (square_file(s2) == FILE_A ? 4 : 2);
bonus += v;
}
}
pi->qsStormValue[Us] += bonus;
// Member of a pawn chain (but not the backward one)? We could speed up
// the test a little by introducing an array of masks indexed by color
// and square for doing the test, but because everything is hashed,
// it probably won't make any noticeable difference.
chain = ourPawns
& neighboring_files_bb(f)
& (rank_bb(r) | rank_bb(r - (Us == WHITE ? 1 : -1)));
// Test for candidate passed pawn
candidate = false;
candidatesupportcount = 0;
if (!blocked && !passed && open)
{
candidatesupportcount = count_1s_max_15(neighboring_files_bb(f) & (behind_bb(Us, r) | rank_bb(r)) & ourPawns)
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(Us, r) & theirPawns);
candidate = candidatesupportcount >= 0;
}
// Test for backward pawn
//
// If the pawn is passed, isolated, a candidate or member of a pawn chain
// it cannot be backward. If there are friendly pawns behind on neighboring files
// the pawn can not be backward either.
// [EdG: If the pawn can capture an enemy pawn the situation
// seems unclear to me also because we do not know who has the move,
// at least that is my assumption. I count the pawn backward for now
// so have removed the en prise test]
if ( (passed | candidate | isolated | chain)
|| (ourPawns & behind_bb(Us, r) & neighboring_files_bb(f))
/*|| (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 neighboring files. We now check whether the pawn is
// backward by looking in the forward direction on the neighboring
// files, and seeing whether we meet a friendly or an enemy pawn first.
Bitboard b = pos.attacks_from<PAWN>(s, Us);
backward = true;
if (!blocked && (b & ourPawns))
{
if (Us == WHITE)
{
if ((relative_rank(Us, s) >= RANK_4) || !((b | (b << 8)) & theirPawns)) backward = false;
}
else
{
if ((relative_rank(Us, s) >= RANK_4) || !((b | (b >> 8)) & theirPawns)) backward = false;
}
if (backward
&& !open
&& (count_1s_max_15(b & ourPawns) == 2)
&& (count_1s_max_15((Us == WHITE ? (b << 8) : (b >> 8)) & theirPawns) == 2))
indirectlyBlocked = true;
// This is maybe not watertight but just an indicator that
// a breakthrough will be difficult. Expensive calculation
}
}
// In order to prevent doubled passed pawns from receiving a too big
// bonus, only the frontmost passed pawn on each file is considered as
// a true passed pawn.
if (passed && (ourPawns & squares_in_front_of(Us, s)))
passed = false;
// Score this pawn
if (passed)
set_bit(&(pi->passedPawns), s);
if (blocked || indirectlyBlocked)
set_bit(&(pi->blockedPawns), s);
if (isolated)
{
value -= IsolatedPawnPenalty[f];
if (!(theirPawns & file_bb(f)))
{
value -= IsolatedPawnPenalty[f] / 2;
if (doubled)
value -= IsolatedPawnPenalty[f] / 4;
}
}
if (doubled)
value -= DoubledPawnPenalty[f];
if (backward)
{
value -= BackwardPawnPenalty[f];
if (blocked)
value -= make_score(relative_rank(Them, s) * 2, relative_rank(Them, s));
if (open)
value -= (BackwardPawnPenalty[f] / 2) - make_score(0, relative_rank(Us, s) * 2);
}
if (chain)
value += ChainBonus[f];
if (candidate)
{
value += CandidateBonus[relative_rank(Us, s)];
if (!chain && (attackedBlocksquare || (pos.attacks_from<PAWN>(s, Us) & theirPawns)))
value -= CandidateBonus[relative_rank(Us, s)]/4;
if (candidatesupportcount > 0)
{
candidatesupportcount = (candidatesupportcount * relative_rank(Us, s) * 10)
+ ((int(chain) + int(pos.non_pawn_material(Them) == Value(0))) * 4 * relative_rank(Us, s));
value += make_score(candidatesupportcount, candidatesupportcount * 2);
}
}
}
return value;
}
/// PawnInfo::updateShelter calculates and caches king shelter. It is called
/// only when king square changes, about 20% of total get_king_shelter() calls.
int PawnInfo::updateShelter(const Position& pos, Color c, Square ksq) {
unsigned shelter = 0;
Bitboard pawns = pos.pieces(PAWN, c) & this_and_neighboring_files_bb(ksq);
unsigned r = ksq & (7 << 3);
for (int i = 1, k = (c ? -8 : 8); i < 4; i++)
{
r += k;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (128 >> i);
}
kingSquares[c] = ksq;
kingShelters[c] = shelter;
return shelter;
}
|
Eelco _________________ Debugging is twice as hard as writing the code in the first
place. Therefore, if you write the code as cleverly as possible, you
are, by definition, not smart enough to debug it.
-- Brian W. Kernighan |
|