Compare commits

..

44 Commits

Author SHA1 Message Date
Marco Costalba
cc3c1dc25a Stockfish 1.2 optimistic
Optimistic razoring settings. It is stronger with
most engines but weaker with someones.

The default is instead more solid and uniform with all
the opponents.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-29 12:24:34 +01:00
Marco Costalba
11763d2b7f Stockfish 1.2
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-29 12:18:49 +01:00
Marco Costalba
d99a95df29 Micro optimization in extension()
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-29 12:18:36 +01:00
Marco Costalba
0da1d6a846 Remove a gcc warning on an unused variable
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-29 12:18:23 +01:00
Marco Costalba
5d94305af3 Properly handle odd depths in razor formula
A little bit more aggressive, but should be more
in line with the depths logic.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-28 19:38:44 +01:00
Marco Costalba
e4fd9a2df7 Safer razoring formula
Add also the possibility to razor at ply one.
It is disable dby default but it seems stronger
against Stockfish itself. It is still not clear if
is stronger against other engines. By now leave
disabled.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-28 12:55:33 +01:00
Marco Costalba
aedc6c6f1f Don't silently accept an option name mismatch
With this we could have found earlier the futility
name option bug!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-28 12:37:13 +01:00
Marco Costalba
dae4e7df07 Tweak again futility margings
Lower margins near the leafs, higher at high depth.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-25 20:08:45 +01:00
Marco Costalba
96a32eec69 Expose new futility margin interface to UCI
Now futility margin it's actually a scale factor
to apply to the base ones.

No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-25 19:41:24 +01:00
Marco Costalba
e46d3670fd Tweak futility margins
Less prune at the bottom and at the middle, a bit more
at the top.

After 747 games: +215 =345 -187 +13 elo

Also introduced a vector of margins, now that start to be a lot
it is a more flexible solution.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-25 12:05:45 +01:00
Marco Costalba
8cd5cb930d Try razoring only for depth > OnePly
Because razoring verification after qsearch() cuts more
then 40% of candidates, do not waste a costly qsearch for
nodes at depth one that will be probably discarded anyway
by futility.

Also tight razoring conditions to keep dangerous false
negatives below 0,05%. Still not clear if it is enough.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-24 09:35:57 +01:00
Marco Costalba
2feb9d5100 Futility pruning till ply 6 included
Seems good:

After 796 games: +211 = 393 -192 +8 elo

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-24 09:34:09 +01:00
Marco Costalba
d11426c777 Fix a comment
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-23 12:02:10 +01:00
Marco Costalba
e96f56adfa Merge futility pruning from Glaurung 2.2
It seems much more powerful then previous one.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-23 12:01:48 +01:00
Marco Costalba
e3b03f13b3 Passed pawns: consider enemy rooks or queens from behind
Merged from Glaurung 2.2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-22 12:10:15 +01:00
Marco Costalba
54b7da120f King safety: retire rook contact check
Merged from Glaurung 2.2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-22 12:07:00 +01:00
Marco Costalba
3fafc9768a Set the 'Problem' variable only at ply == 1
Bug fix merged from Glaurung 2.2 for search_pv()

Added the same fix also to sp_search_pv() where
was missing.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-22 11:44:00 +01:00
Marco Costalba
23490bd825 Retire EvaluatePawnStorms and UseEasyMove constants
Merged from Glaurung 2.2

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 20:10:20 +01:00
Marco Costalba
61c6a3d5a0 Merge cosmetics from Glaurung 2.2
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 17:02:34 +01:00
Marco Costalba
31d4f0b734 Merge space weigth evaluation fromGlaurung 2.2
Is a new evaluation rule that gives bonus in midgame
to the side that has more space behind pawns for its
minor pieces.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 16:26:36 +01:00
Marco Costalba
f178f0a291 Merged two new endgames from Glaurung 2.2
It is two bishop against a knight and two minor
pieces against one minor piece.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 15:38:10 +01:00
Marco Costalba
72ca727b38 SEE: add support for enpassant moves
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 10:44:41 +01:00
Marco Costalba
2d0146fe1d Call poll() before to check for stopped search
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 10:44:12 +01:00
Marco Costalba
96d0501735 Less aggressive razoring
Use a margin to compare with beta so that positions
that after the verifying qsearch have gained a lot of points
are not discarded just becasue not above beta.

Also remove the second condition on depth <= OnePly, it
was too risky and added only a 2% more of pruned nodes.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-21 10:42:39 +01:00
Marco Costalba
b58ad355ca Revert: "Do not razor when in check"
It is slightly weaker after 500 games. Keep the
check on mate values.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-18 22:01:56 +01:00
Marco Costalba
17000d1ea0 Trigger of PawnEndgameExtension if capture is not a pawn
Instead of a rook.

This gives an unexpected graeat increase!

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-17 19:36:51 +01:00
Marco Costalba
b09cbaebb9 search_pv: an enpassant move is a capture
Fix the logic in search_pv and sp_search_pv

An additional issue to consider is that a castle move
is not a capture but destination square is not empty.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-16 22:20:53 +01:00
Marco Costalba
725c504a5f qsearch: take in account enpassant in futility formula
Should not change anything at ELO level but it is
the correct thing to do.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-16 22:20:42 +01:00
Marco Costalba
bfbfc24d07 qsearch: do not call evaluate when in check
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-16 22:20:18 +01:00
Marco Costalba
a55b06d3c9 Restore development versioning and LSN filtering
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:23:03 +01:00
Marco Costalba
ecc19381b4 Do not razor while in check or value is mate
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:20:03 +01:00
Marco Costalba
dae2f600d6 Do not null search when beta is a mate value
Also do not return unproven mates in null search.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:19:25 +01:00
Marco Costalba
4c294932e7 Better document null move dynamic reduction
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:18:56 +01:00
Marco Costalba
c831b00544 Introduce beta counters to order moves at ply one
Instead of number of searched nodes use the number of
opponent beta-cutoff occurred under the move subtree.

After 570 games 1+0 we have: +150 =288 -132 (+11 ELO)

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-15 22:13:42 +01:00
Marco Costalba
5b853c9be6 Debugging: move debug function definitions in misc.cpp
Also activate writing on log file.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-14 17:26:05 +01:00
Marco Costalba
5f8f83bc05 Debugging: print to file
Print debug info on log file, not only on std::cout

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-14 14:57:17 +01:00
Marco Costalba
8ee3124487 Fix two bugs in ok_to_prune() (2)
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-12 22:27:43 +01:00
Marco Costalba
07b45151d2 Disable "Null driven IID" by default
Testing is not clear. Probably we need to test
at deeper depths to have some clear results.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-11 20:39:08 +01:00
Marco Costalba
9e3ab9099f Null move only for depth > OnePly
Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-11 20:38:52 +01:00
Marco Costalba
f09884d72f Null driven IID: remove IIDMargin from see() condition
This seems to cut searched nodes also more.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 21:43:09 +01:00
Marco Costalba
ab29d8df67 Fix inflate pawns.cpp fallout
Catched counting the nodes searched at
fixed depth. A quick and reliable cross check,
expecially in inflate only patches.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 21:35:07 +01:00
Marco Costalba
389dc0e83b Add behind_bb() helper to simplify code logic
No functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 00:14:24 +01:00
Marco Costalba
67aac4889e Space inflate pawns.cpp
Hopefully no functional change.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 00:14:15 +01:00
Marco Costalba
aaad48464b Add a see() function that take only destination square
In this case firstlocates the least valuable attacker, if any,
then proceed as usual.

This will be used by next patch.

Signed-off-by: Marco Costalba <mcostalba@gmail.com>
2008-12-10 00:13:59 +01:00
16 changed files with 754 additions and 362 deletions

View File

@@ -250,8 +250,8 @@ Bitboard BMask[64];
int BAttackIndex[64];
Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[64];
Bitboard ClearMaskBB[64];
Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard StepAttackBB[16][64];
Bitboard RayBB[64][8];
@@ -433,6 +433,8 @@ namespace {
// be necessary to touch any of them.
void init_masks() {
SetMaskBB[SQ_NONE] = 0ULL;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for(Square s = SQ_A1; s <= SQ_H8; s++) {
SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s];

View File

@@ -113,8 +113,8 @@ extern const Bitboard RankBB[8];
extern const Bitboard RelativeRankBB[2][8];
extern const Bitboard InFrontBB[2][8];
extern Bitboard SetMaskBB[64];
extern Bitboard ClearMaskBB[64];
extern Bitboard SetMaskBB[65];
extern Bitboard ClearMaskBB[65];
extern Bitboard StepAttackBB[16][64];
extern Bitboard RayBB[64][8];
@@ -236,6 +236,19 @@ inline Bitboard in_front_bb(Color c, Square s) {
}
/// behind_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks behind of the rank
/// (or square), from the given color's point of view.
inline Bitboard behind_bb(Color c, Rank r) {
return InFrontBB[opposite_color(c)][r];
}
inline Bitboard behind_bb(Color c, Square s) {
return in_front_bb(opposite_color(c), square_rank(s));
}
/// ray_bb() gives a bitboard representing all squares along the ray in a
/// given direction from a given square.

View File

@@ -62,6 +62,13 @@ KRKNEvaluationFunction EvaluateKNKR = KRKNEvaluationFunction(BLACK);
KQKREvaluationFunction EvaluateKQKR = KQKREvaluationFunction(WHITE);
KQKREvaluationFunction EvaluateKRKQ = KQKREvaluationFunction(BLACK);
// KBB vs KN:
KBBKNEvaluationFunction EvaluateKBBKN = KBBKNEvaluationFunction(WHITE);
KBBKNEvaluationFunction EvaluateKNKBB = KBBKNEvaluationFunction(BLACK);
// K and two minors vs K and one or two minors:
KmmKmEvaluationFunction EvaluateKmmKm = KmmKmEvaluationFunction(WHITE);
/// Scaling functions
@@ -187,6 +194,8 @@ KRKPEvaluationFunction::KRKPEvaluationFunction(Color c) : EndgameEvaluationFunct
KRKBEvaluationFunction::KRKBEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KRKNEvaluationFunction::KRKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KQKREvaluationFunction::KQKREvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KBBKNEvaluationFunction::KBBKNEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
KmmKmEvaluationFunction::KmmKmEvaluationFunction(Color c) : EndgameEvaluationFunction(c) { }
ScalingFunction::ScalingFunction(Color c) {
@@ -420,6 +429,36 @@ Value KQKREvaluationFunction::apply(const Position &pos) {
}
Value KBBKNEvaluationFunction::apply(const Position &pos) {
assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pawns() == EmptyBoardBB);
Value result = BishopValueEndgame;
Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT, 0);
// Bonus for attacking king close to defending king
result += distance_bonus(square_distance(wksq, bksq));
// Bonus for driving the defending king and knight apart
result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility
result += Value((8 - count_1s_max_15(pos.piece_attacks<KNIGHT>(nsq))) * 8);
return (strongerSide == pos.side_to_move())? result : -result;
}
Value KmmKmEvaluationFunction::apply(const Position &pos) {
return Value(0);
}
/// KBPKScalingFunction scales endgames where the stronger side has king,
/// bishop and one or more pawns. It checks for draws with rook pawns and a
/// bishop of the wrong color. If such a draw is detected, ScaleFactor(0) is
@@ -604,6 +643,16 @@ ScaleFactor KRPKRScalingFunction::apply(const Position &pos) {
- (8 * square_distance(wpsq, queeningSq) +
2 * square_distance(wksq, queeningSq)));
// If the pawn is not far advanced, and the defending king is somewhere in
// the pawn's path, it's probably a draw:
if(r <= RANK_4 && bksq > wpsq) {
if(square_file(bksq) == square_file(wpsq))
return ScaleFactor(10);
if(abs(square_file(bksq) - square_file(wpsq)) == 1
&& square_distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
}
return SCALE_FACTOR_NONE;
}

View File

@@ -99,6 +99,20 @@ public:
Value apply(const Position &pos);
};
// KBB vs KN:
class KBBKNEvaluationFunction : public EndgameEvaluationFunction {
public:
KBBKNEvaluationFunction(Color C);
Value apply(const Position &pos);
};
// K and two minors vs K and one or two minors:
class KmmKmEvaluationFunction : public EndgameEvaluationFunction {
public:
KmmKmEvaluationFunction(Color c);
Value apply(const Position &pos);
};
/// Abstract base class for all evaluation scaling functions:
@@ -205,6 +219,12 @@ extern KRKNEvaluationFunction EvaluateKRKN, EvaluateKNKR;
// KQ vs KR:
extern KQKREvaluationFunction EvaluateKQKR, EvaluateKRKQ;
// KBB vs KN:
extern KBBKNEvaluationFunction EvaluateKBBKN, EvaluateKNKBB;
// K and two minors vs K and one or two minors:
extern KmmKmEvaluationFunction EvaluateKmmKm;
// KBP vs K:
extern KBPKScalingFunction ScaleKBPK, ScaleKKBP;

View File

@@ -52,6 +52,7 @@ namespace {
int WeightPassedPawnsMidgame = 0x100;
int WeightPassedPawnsEndgame = 0x100;
int WeightKingSafety[2] = { 0x100, 0x100 };
int WeightSpace;
// Internal evaluation weights. These are applied on top of the evaluation
// weights read from UCI parameters. The purpose is to be able to change
@@ -63,8 +64,9 @@ namespace {
const int WeightPawnStructureEndgameInternal = 0x100;
const int WeightPassedPawnsMidgameInternal = 0x100;
const int WeightPassedPawnsEndgameInternal = 0x100;
const int WeightKingSafetyInternal = 0x100;
const int WeightKingOppSafetyInternal = 0x100;
const int WeightKingSafetyInternal = 0x110;
const int WeightKingOppSafetyInternal = 0x110;
const int WeightSpaceInternal = 0x30;
// Visually better to define tables constants
typedef Value V;
@@ -203,6 +205,19 @@ namespace {
((1ULL << SQ_A8) | (1ULL << SQ_H8))
};
// The SpaceMask[color] contains area of the board which is consdered by
// the space evaluation. In the middle game, each side is given a bonus
// based on how many squares inside this area are safe and available for
// friendly minor pieces.
const Bitboard SpaceMask[2] = {
(1ULL<<SQ_C2) | (1ULL<<SQ_D2) | (1ULL<<SQ_E2) | (1ULL<<SQ_F2) |
(1ULL<<SQ_C3) | (1ULL<<SQ_D3) | (1ULL<<SQ_E3) | (1ULL<<SQ_F3) |
(1ULL<<SQ_C4) | (1ULL<<SQ_D4) | (1ULL<<SQ_E4) | (1ULL<<SQ_F4),
(1ULL<<SQ_C7) | (1ULL<<SQ_D7) | (1ULL<<SQ_E7) | (1ULL<<SQ_F7) |
(1ULL<<SQ_C6) | (1ULL<<SQ_D6) | (1ULL<<SQ_E6) | (1ULL<<SQ_F6) |
(1ULL<<SQ_C5) | (1ULL<<SQ_D5) | (1ULL<<SQ_E5) | (1ULL<<SQ_F5)
};
/// King safety constants and variables. The king safety scores are taken
/// from the array SafetyTable[]. Various little "meta-bonuses" measuring
/// the strength of the attack are added up into an integer, which is used
@@ -215,8 +230,7 @@ namespace {
const int KnightAttackWeight = 2;
// Bonuses for safe checks for each piece type.
int QueenContactCheckBonus = 4;
int RookContactCheckBonus = 2;
int QueenContactCheckBonus = 3;
int QueenCheckBonus = 2;
int RookCheckBonus = 1;
int BishopCheckBonus = 1;
@@ -246,18 +260,18 @@ namespace {
// in init_safety().
Value SafetyTable[100];
// Pawn and material hash tables, indexed by the current thread id:
// Pawn and material hash tables, indexed by the current thread id
PawnInfoTable *PawnTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
MaterialInfoTable *MaterialTable[8] = {0, 0, 0, 0, 0, 0, 0, 0};
// Sizes of pawn and material hash tables:
// Sizes of pawn and material hash tables
const int PawnTableSize = 16384;
const int MaterialTableSize = 1024;
// Array which gives the number of nonzero bits in an 8-bit integer:
uint8_t BitCount8Bit[256];
// Function prototypes:
// Function prototypes
void evaluate_knight(const Position &p, Square s, Color us, EvalInfo &ei);
void evaluate_bishop(const Position &p, Square s, Color us, EvalInfo &ei);
void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei);
@@ -270,6 +284,7 @@ namespace {
void evaluate_trapped_bishop_a1h1(const Position &pos, Square s, Color us,
EvalInfo &ei);
void evaluate_space(const Position &p, Color us, EvalInfo &ei);
inline Value apply_weight(Value v, int w);
Value scale_by_game_phase(Value mv, Value ev, Phase ph, const ScaleFactor sf[]);
@@ -407,6 +422,13 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
ei.mgValue += ei.pi->kingside_storm_value(WHITE)
- ei.pi->queenside_storm_value(BLACK);
// Evaluate space for both sides
if (ei.mi->space_weight() > 0)
{
evaluate_space(pos, WHITE, ei);
evaluate_space(pos, BLACK, ei);
}
}
// Mobility
@@ -414,7 +436,7 @@ Value evaluate(const Position &pos, EvalInfo &ei, int threadID) {
ei.egValue += apply_weight(ei.egMobility, WeightMobilityEndgame);
// If we don't already have an unusual scale factor, check for opposite
// colored bishop endgames, and use a lower scale for those:
// colored bishop endgames, and use a lower scale for those
if ( phase < PHASE_MIDGAME
&& pos.opposite_colored_bishops()
&& ( (factor[WHITE] == SCALE_FACTOR_NORMAL && ei.egValue > Value(0))
@@ -527,6 +549,7 @@ void read_weights(Color us) {
WeightKingSafety[us] = weight_option("Cowardice", WeightKingSafetyInternal);
WeightKingSafety[them] = weight_option("Aggressiveness", WeightKingOppSafetyInternal);
WeightSpace = weight_option("Space", WeightSpaceInternal);
init_safety();
}
@@ -613,7 +636,6 @@ namespace {
void evaluate_rook(const Position &p, Square s, Color us, EvalInfo &ei) {
//Bitboard b = p.rook_attacks(s);
Bitboard b = rook_attacks_bb(s, p.occupied_squares() & ~p.rooks_and_queens(us));
ei.attackedBy[us][ROOK] |= b;
@@ -744,14 +766,14 @@ namespace {
Bitboard occ = p.occupied_squares(), b, b2;
// Initialize the 'attackUnits' variable, which is used later on as an
// index to the SafetyTable[] array. The initial is based on the number
// and types of the attacking pieces, the number of attacked and
// index to the SafetyTable[] array. The initial value is based on the
// number and types of the attacking pieces, the number of attacked and
// undefended squares around the king, the square of the king, and the
// quality of the pawn shelter.
int attackUnits =
Min((ei.kingAttackersCount[them] * ei.kingAttackersWeight[them]) / 2, 25)
+ (ei.kingAdjacentZoneAttacksCount[them] + count_1s_max_15(undefended)) * 3
+ InitKingDanger[relative_square(us, s)] - shelter / 32;
+ InitKingDanger[relative_square(us, s)] - (shelter >> 5);
// Analyse safe queen contact checks
b = undefended & ei.attacked_by(them, QUEEN) & ~p.pieces_of_color(them);
@@ -797,25 +819,8 @@ namespace {
}
}
}
// Analyse safe rook contact checks:
if (RookContactCheckBonus)
{
b = undefended & ei.attacked_by(them, ROOK) & ~p.pieces_of_color(them);
if (b)
{
Bitboard attackedByOthers =
ei.attacked_by(them, PAWN) | ei.attacked_by(them, KNIGHT)
| ei.attacked_by(them, BISHOP) | ei.attacked_by(them, QUEEN);
b &= attackedByOthers;
if (b)
{
int count = count_1s_max_15(b);
attackUnits += (RookContactCheckBonus * count * (sente? 2 : 1));
}
}
}
// Analyse safe distance checks:
// Analyse safe distance checks
if (QueenCheckBonus > 0 || RookCheckBonus > 0)
{
b = p.piece_attacks<ROOK>(s) & ~p.pieces_of_color(them) & ~ei.attacked_by(us);
@@ -931,6 +936,13 @@ namespace {
b2 = squares_in_front_of(us, s);
b3 = b2 & ei.attacked_by(them);
b4 = b2 & ei.attacked_by(us);
// If there is an enemy rook or queen attacking the pawn from behind,
// add all X-ray attacks by the rook or queen.
if(bit_is_set(ei.attacked_by(them,ROOK)|ei.attacked_by(them,QUEEN),s)
&& squares_behind(us, s) & pos.rooks_and_queens(them))
b3 = b2;
if((b2 & pos.pieces_of_color(them)) == EmptyBoardBB) {
// There are no enemy pieces in the pawn's path! Are any of the
// squares in the pawn's path attacked by the enemy?
@@ -1101,14 +1113,54 @@ namespace {
}
// apply_weight applies an evaluation weight to a value.
// evaluate_space() computes the space evaluation for a given side. The
// space evaluation is a simple bonus based on the number of safe squares
// available for minor pieces on the central four files on ranks 2--4. Safe
// squares one, two or three squares behind a friendly pawn are counted
// twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table.
void evaluate_space(const Position &pos, Color us, EvalInfo &ei) {
Color them = opposite_color(us);
// Find the safe squares for our pieces inside the area defined by
// SpaceMask[us]. A square is unsafe it is attacked by an enemy
// pawn, or if it is undefended and attacked by an enemy piece.
Bitboard safeSquares =
SpaceMask[us] & ~pos.pawns(us) & ~ei.attacked_by(them, PAWN)
& ~(~ei.attacked_by(us) & ei.attacked_by(them));
// Find all squares which are at most three squares behind some friendly
// pawn.
Bitboard behindFriendlyPawns = pos.pawns(us);
if(us == WHITE) {
behindFriendlyPawns |= (behindFriendlyPawns >> 8);
behindFriendlyPawns |= (behindFriendlyPawns >> 16);
}
else {
behindFriendlyPawns |= (behindFriendlyPawns << 8);
behindFriendlyPawns |= (behindFriendlyPawns << 16);
}
int space =
count_1s_max_15(safeSquares)
+ count_1s_max_15(behindFriendlyPawns & safeSquares);
ei.mgValue += Sign[us] *
apply_weight(Value(space * ei.mi->space_weight()), WeightSpace);
}
// apply_weight() applies an evaluation weight to a value
inline Value apply_weight(Value v, int w) {
return (v*w) / 0x100;
}
// scale_by_game_phase interpolates between a middle game and an endgame
// scale_by_game_phase() interpolates between a middle game and an endgame
// score, based on game phase. It also scales the return value by a
// ScaleFactor array.
@@ -1156,7 +1208,6 @@ namespace {
void init_safety() {
QueenContactCheckBonus = get_option_value_int("Queen Contact Check Bonus");
RookContactCheckBonus = get_option_value_int("Rook Contact Check Bonus");
QueenCheckBonus = get_option_value_int("Queen Check Bonus");
RookCheckBonus = get_option_value_int("Rook Check Bonus");
BishopCheckBonus = get_option_value_int("Bishop Check Bonus");

View File

@@ -161,6 +161,22 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
mi->evaluationFunction = &EvaluateKKX;
return mi;
}
else if ( pos.pawns() == EmptyBoardBB
&& pos.rooks() == EmptyBoardBB
&& pos.queens() == EmptyBoardBB)
{
// Minor piece endgame with at least one minor piece per side,
// and no pawns.
assert(pos.knights(WHITE) | pos.bishops(WHITE));
assert(pos.knights(BLACK) | pos.bishops(BLACK));
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{
mi->evaluationFunction = &EvaluateKmmKm;
return mi;
}
}
// OK, we didn't find any special evaluation function for the current
// material configuration. Is there a suitable scaling function?
@@ -221,6 +237,18 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
}
}
// Compute the space weight
if (pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK) >=
2*QueenValueMidgame + 4*RookValueMidgame + 2*KnightValueMidgame)
{
int minorPieceCount = pos.piece_count(WHITE, KNIGHT)
+ pos.piece_count(BLACK, KNIGHT)
+ pos.piece_count(WHITE, BISHOP)
+ pos.piece_count(BLACK, BISHOP);
mi->spaceWeight = minorPieceCount * minorPieceCount;
}
// Evaluate the material balance
int sign;
@@ -309,6 +337,8 @@ EndgameFunctions::EndgameFunctions() {
add(z[W][KNIGHT][1] ^ z[B][ROOK][1], &EvaluateKNKR);
add(z[W][QUEEN][1] ^ z[B][ROOK][1], &EvaluateKQKR);
add(z[W][ROOK][1] ^ z[B][QUEEN][1], &EvaluateKRKQ);
add(z[W][BISHOP][2] ^ z[B][KNIGHT][1], &EvaluateKBBKN);
add(z[W][KNIGHT][1] ^ z[B][BISHOP][2], &EvaluateKNKBB);
add(z[W][KNIGHT][1] ^ z[W][PAWN][1], W, &ScaleKNPK);
add(z[B][KNIGHT][1] ^ z[B][PAWN][1], B, &ScaleKKNP);

View File

@@ -52,6 +52,7 @@ public:
Value mg_value() const;
Value eg_value() const;
ScaleFactor scale_factor(const Position& pos, Color c) const;
int space_weight() const;
bool specialized_eval_exists() const;
Value evaluate(const Position& pos) const;
@@ -64,6 +65,7 @@ private:
uint8_t factor[2];
EndgameEvaluationFunction* evaluationFunction;
ScalingFunction* scalingFunction[2];
uint8_t spaceWeight;
};
@@ -120,6 +122,7 @@ inline void MaterialInfo::clear() {
mgValue = egValue = 0;
factor[WHITE] = factor[BLACK] = uint8_t(SCALE_FACTOR_NORMAL);
spaceWeight = 0;
evaluationFunction = NULL;
scalingFunction[WHITE] = scalingFunction[BLACK] = NULL;
}
@@ -144,6 +147,15 @@ inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) cons
}
/// MaterialInfo::space_weight() simply returns the weight for the space
/// evaluation for this material configuration.
inline int MaterialInfo::space_weight() const {
return spaceWeight;
}
/// MaterialInfo::specialized_eval_exists decides whether there is a
/// specialized evaluation function for the current material configuration,
/// or if the normal evaluation function should be used.

View File

@@ -37,6 +37,7 @@ int gettimeofday(struct timeval * tp, struct timezone * tzp);
#endif
#include <cassert>
#include <cstdio>
#include <iomanip>
#include <iostream>
@@ -49,6 +50,9 @@ int gettimeofday(struct timeval * tp, struct timezone * tzp);
//// Variables
////
static const std::string AppName = "Stockfish";
static const std::string AppTag = "";
long dbg_cnt0 = 0;
long dbg_cnt1 = 0;
@@ -60,6 +64,42 @@ bool dbg_show_hit_rate = false;
//// Functions
////
void dbg_hit_on(bool b) {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt0++;
if (b)
dbg_cnt1++;
}
void dbg_hit_on_c(bool c, bool b) {
if (c)
dbg_hit_on(b);
}
void dbg_before() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt0++;
}
void dbg_after() {
assert(!dbg_show_mean);
dbg_show_hit_rate = true;
dbg_cnt1++;
}
void dbg_mean_of(int v) {
assert(!dbg_show_hit_rate);
dbg_cnt0++;
dbg_cnt1 += v;
}
void dbg_print_hit_rate() {
std::cout << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
@@ -73,6 +113,19 @@ void dbg_print_mean() {
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << std::endl;
}
void dbg_print_hit_rate(std::ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Hit " << dbg_cnt1
<< " hit rate (%) " << (dbg_cnt1*100)/(dbg_cnt0 ? dbg_cnt0 : 1)
<< std::endl;
}
void dbg_print_mean(std::ofstream& logFile) {
logFile << "Total " << dbg_cnt0 << " Mean "
<< (float)dbg_cnt1 / (dbg_cnt0 ? dbg_cnt0 : 1) << std::endl;
}
/// engine_name() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when the
/// program was compiled) or "Stockfish <version number>", depending on whether
@@ -90,7 +143,9 @@ const std::string engine_name() {
std::stringstream s;
std::string day = (date[4] == ' ' ? date.substr(5, 1) : date.substr(4, 2));
s << "Stockfish " << date.substr(date.length() - 2) << std::setfill('0')
std::string name = AppName + " " + AppTag + " ";
s << name << date.substr(date.length() - 2) << std::setfill('0')
<< std::setw(2) << mon << std::setw(2) << day;
return s.str();

View File

@@ -26,6 +26,7 @@
//// Includes
////
#include <fstream>
#include <string>
@@ -37,7 +38,7 @@
/// Version number. If this is left empty, the current date (in the format
/// YYMMDD) is used as a version number.
const std::string EngineVersion = "1.1a";
const std::string EngineVersion = "1.2";
////
@@ -60,21 +61,21 @@ extern int Bioskey();
////
//// Debug
////
extern bool dbg_show_mean;
extern bool dbg_show_hit_rate;
extern long dbg_cnt0;
extern long dbg_cnt1;
inline void dbg_hit_on(bool b) { dbg_show_hit_rate = true; dbg_cnt0++; if (b) dbg_cnt1++; }
inline void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
inline void dbg_before() { dbg_show_hit_rate = true; dbg_cnt0++; }
inline void dbg_after() { dbg_show_hit_rate = true; dbg_cnt1++; }
inline void dbg_mean_of(int v) { dbg_cnt0++; dbg_cnt1 += v; }
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_before();
extern void dbg_after();
extern void dbg_mean_of(int v);
extern void dbg_print_hit_rate();
extern void dbg_print_mean();
extern void dbg_print_hit_rate(std::ofstream& logFile);
extern void dbg_print_mean(std::ofstream& logFile);
#endif // !defined(MISC_H_INCLUDED)

View File

@@ -86,40 +86,37 @@ namespace {
// Candidate passed pawn bonus by rank, middle game.
const Value CandidateMidgameBonus[8] = {
Value(0), Value(12), Value(12), Value(20),
Value(40), Value(90), Value(0), Value(0)
Value( 0), Value(12), Value(12), Value(20),
Value(40), Value(90), Value( 0), Value( 0)
};
// Candidate passed pawn bonus by rank, endgame.
const Value CandidateEndgameBonus[8] = {
Value(0), Value(24), Value(24), Value(40),
Value(80), Value(180), Value(0), Value(0)
Value( 0), Value(24), Value(24), Value(40),
Value(80), Value(180), Value(0), Value( 0)
};
// Evaluate pawn storms?
const bool EvaluatePawnStorms = true;
// Pawn storm tables for positions with opposite castling:
const int QStormTable[64] = {
0, 0, 0, 0, 0, 0, 0, 0,
-22, -22, -22, -13, -4, 0, 0, 0,
-4, -9, -9, -9, -4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
-22,-22,-22,-13,-4, 0, 0, 0,
-4, -9, -9, -9,-4, 0, 0, 0,
9, 18, 22, 18, 9, 0, 0, 0,
22, 31, 31, 22, 0, 0, 0, 0,
31, 40, 40, 31, 0, 0, 0, 0,
31, 40, 40, 31, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
22, 31, 31, 22, 0, 0, 0, 0,
31, 40, 40, 31, 0, 0, 0, 0,
31, 40, 40, 31, 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, -4, -13, -22, -27, -27,
0, 0, 0, -4, -9, -13, -18, -18,
0, 0, 0, 0, 9, 9, 9, 9,
0, 0, 0, 0, 9, 18, 27, 27,
0, 0, 0, 0, 9, 27, 40, 36,
0, 0, 0, 0, 0, 31, 40, 31,
0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,-4,-13,-22,-27,-27,
0, 0, 0,-4, -9,-13,-18,-18,
0, 0, 0, 0, 9, 9, 9, 9,
0, 0, 0, 0, 9, 18, 27, 27,
0, 0, 0, 0, 9, 27, 40, 36,
0, 0, 0, 0, 0, 31, 40, 31,
0, 0, 0, 0, 0, 0, 0, 0
};
// Pawn storm open file bonuses by file:
@@ -141,14 +138,16 @@ namespace {
/// Constructor
PawnInfoTable::PawnInfoTable(unsigned numOfEntries) {
size = numOfEntries;
entries = new PawnInfo[size];
if(entries == NULL) {
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl;
exit(EXIT_FAILURE);
if (entries == NULL)
{
std::cerr << "Failed to allocate " << (numOfEntries * sizeof(PawnInfo))
<< " bytes for pawn hash table." << std::endl;
exit(EXIT_FAILURE);
}
this->clear();
clear();
}
@@ -173,6 +172,7 @@ void PawnInfoTable::clear() {
/// the same pawn structure occurs again.
PawnInfo *PawnInfoTable::get_pawn_info(const Position &pos) {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
@@ -181,207 +181,204 @@ PawnInfo *PawnInfoTable::get_pawn_info(const Position &pos) {
// 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;
// information we found the last time instead of recomputing it
if (pi->key == key)
return pi;
// Clear the PawnInfo object, and set the key:
// Clear the PawnInfo object, and set the key
pi->clear();
pi->key = key;
Value mgValue[2] = {Value(0), Value(0)};
Value egValue[2] = {Value(0), Value(0)};
// Loop through the pawns for both colors:
for(Color us = WHITE; us <= BLACK; us++) {
// Loop through the pawns for both colors
for (Color us = WHITE; us <= BLACK; us++)
{
Color them = opposite_color(us);
Bitboard ourPawns = pos.pawns(us);
Bitboard theirPawns = pos.pawns(them);
Bitboard pawns = ourPawns;
// Initialize pawn storm scores by giving bonuses for open files:
if(EvaluatePawnStorms)
for(File f = FILE_A; f <= FILE_H; f++)
if(pos.file_is_half_open(us, f)) {
pi->ksStormValue[us] += KStormOpenFileBonus[f];
pi->qsStormValue[us] += QStormOpenFileBonus[f];
// Initialize pawn storm scores by giving bonuses for open files
for (File f = FILE_A; f <= FILE_H; f++)
if (pos.file_is_half_open(us, f))
{
pi->ksStormValue[us] += KStormOpenFileBonus[f];
pi->qsStormValue[us] += QStormOpenFileBonus[f];
}
// Loop through all pawns of the current color and score each pawn:
while(pawns) {
Square s = pop_1st_bit(&pawns);
File f = square_file(s);
Rank r = square_rank(s);
bool passed, doubled, isolated, backward, chain, candidate;
int bonus;
// Loop through all pawns of the current color and score each pawn
while (pawns)
{
bool passed, doubled, isolated, backward, chain, candidate;
Square s = pop_1st_bit(&pawns);
File f = square_file(s);
Rank r = square_rank(s);
assert(pos.piece_on(s) == pawn_of_color(us));
assert(pos.piece_on(s) == pawn_of_color(us));
// The file containing the pawn is not half open:
pi->halfOpenFiles[us] &= ~(1 << f);
// The file containing the pawn is not half open
pi->halfOpenFiles[us] &= ~(1 << f);
// Passed, isolated or doubled pawn?
passed = pos.pawn_is_passed(us, s);
isolated = pos.pawn_is_isolated(us, s);
doubled = pos.pawn_is_doubled(us, s);
// Passed, isolated or doubled pawn?
passed = pos.pawn_is_passed(us, s);
isolated = pos.pawn_is_isolated(us, s);
doubled = pos.pawn_is_doubled(us, s);
if(EvaluatePawnStorms) {
// We calculate kingside and queenside pawn storm
// scores for both colors. These are used when evaluating
// middle game positions with opposite side castling.
// 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[]). This score is increased if
// (KStormTable[] or QStormTable[]). This score is increased if
// there are enemy pawns on adjacent files in front of the pawn.
// This is because we want to be able to open files against the
// enemy king, and to avoid blocking the pawn structure (e.g. white
// pawns on h6, g5, black pawns on h7, g6, f7).
// Kingside and queenside pawn storms
int KBonus = KStormTable[relative_square(us, s)];
int QBonus = QStormTable[relative_square(us, s)];
bool outPostFlag = (KBonus > 0 && (outpost_mask(us, s) & theirPawns));
bool passedFlag = (QBonus > 0 && (passed_pawn_mask(us, s) & theirPawns));
switch (f) {
case FILE_A:
QBonus += passedFlag * QBonus / 2;
break;
case FILE_B:
QBonus += passedFlag * (QBonus / 2 + QBonus / 4);
break;
case FILE_C:
QBonus += passedFlag * QBonus / 2;
break;
case FILE_F:
KBonus += outPostFlag * KBonus / 4;
break;
case FILE_G:
KBonus += outPostFlag * (KBonus / 2 + KBonus / 4);
break;
case FILE_H:
KBonus += outPostFlag * KBonus / 2;
break;
default:
break;
}
pi->ksStormValue[us] += KBonus;
pi->qsStormValue[us] += QBonus;
// 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 noticable difference.
chain = ourPawns
& neighboring_files_bb(f)
& (rank_bb(r) | rank_bb(r - (us == WHITE ? 1 : -1)));
// Test for backward pawn
//
// If the pawn is passed, isolated, or member of a pawn chain
// it cannot be backward. If can capture an enemy pawn or if
// there are friendly pawns behind on neighboring files it cannot
// be backward either.
if ( passed
|| isolated
|| chain
|| (pos.pawn_attacks(us, s) & theirPawns)
|| (ourPawns & behind_bb(us, r) & neighboring_files_bb(f)))
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;
if (us == WHITE)
{
for (b = pos.pawn_attacks(us, s); !(b & (ourPawns | theirPawns)); b <<= 8);
backward = (b | (b << 8)) & theirPawns;
}
else
{
for (b = pos.pawn_attacks(us, s); !(b & (ourPawns | theirPawns)); b >>= 8);
backward = (b | (b >> 8)) & theirPawns;
}
}
// Test for candidate passed pawn
candidate = !passed
&& pos.file_is_half_open(them, f)
&& ( 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)
>= 0);
// 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)))
{
// candidate = true;
passed = false;
}
// Score this pawn
Value mv = Value(0), ev = Value(0);
if (isolated)
{
mv -= IsolatedPawnMidgamePenalty[f];
ev -= IsolatedPawnEndgamePenalty[f];
if (pos.file_is_half_open(them, f))
{
mv -= IsolatedPawnMidgamePenalty[f] / 2;
ev -= IsolatedPawnEndgamePenalty[f] / 2;
}
}
if (doubled)
{
mv -= DoubledPawnMidgamePenalty[f];
ev -= DoubledPawnEndgamePenalty[f];
}
if (backward)
{
mv -= BackwardPawnMidgamePenalty[f];
ev -= BackwardPawnEndgamePenalty[f];
if (pos.file_is_half_open(them, f))
{
mv -= BackwardPawnMidgamePenalty[f] / 2;
ev -= BackwardPawnEndgamePenalty[f] / 2;
}
}
if (chain)
{
mv += ChainMidgameBonus[f];
ev += ChainEndgameBonus[f];
}
if (candidate)
{
mv += CandidateMidgameBonus[relative_rank(us, s)];
ev += CandidateEndgameBonus[relative_rank(us, s)];
}
mgValue[us] += mv;
egValue[us] += ev;
// Kingside pawn storms:
bonus = KStormTable[relative_square(us, s)];
if(bonus > 0 && outpost_mask(us, s) & theirPawns) {
switch(f) {
case FILE_F:
bonus += bonus / 4;
break;
case FILE_G:
bonus += bonus / 2 + bonus / 4;
break;
case FILE_H:
bonus += bonus / 2;
break;
default:
break;
}
}
pi->ksStormValue[us] += bonus;
// Queenside pawn storms:
bonus = QStormTable[relative_square(us, s)];
if(bonus > 0 && passed_pawn_mask(us, s) & theirPawns) {
switch(f) {
case FILE_A:
bonus += bonus / 2;
break;
case FILE_B:
bonus += bonus / 2 + bonus / 4;
break;
case FILE_C:
bonus += bonus / 2;
break;
default:
break;
}
}
pi->qsStormValue[us] += bonus;
}
// Member of a pawn chain? 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 noticable difference.
chain = (us == WHITE)?
(ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r-1))) :
(ourPawns & neighboring_files_bb(f) & (rank_bb(r) | rank_bb(r+1)));
// Test for backward pawn.
// If the pawn is isolated, passed, or member of a pawn chain, it cannot
// be backward:
if(passed || isolated || chain)
backward = false;
// If the pawn can capture an enemy pawn, it's not backward:
else if(pos.pawn_attacks(us, s) & theirPawns)
backward = false;
// Check for friendly pawns behind on neighboring files:
else if(ourPawns & in_front_bb(them, r) & neighboring_files_bb(f))
backward = false;
else {
// We now know that there is 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;
if(us == WHITE) {
for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b<<=8);
backward = (b | (b << 8)) & theirPawns;
}
else {
for(b=pos.pawn_attacks(us, s); !(b&(ourPawns|theirPawns)); b>>=8);
backward = (b | (b >> 8)) & theirPawns;
}
}
// Test for candidate passed pawn.
candidate =
(!passed && pos.file_is_half_open(them, f) &&
count_1s_max_15(neighboring_files_bb(f)
& (in_front_bb(them, r) | rank_bb(r))
& ourPawns)
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(us, r)
& theirPawns)
>= 0);
// 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))) {
// candidate = true;
passed = false;
}
// Score this pawn:
Value mv = Value(0), ev = Value(0);
if(isolated) {
mv -= IsolatedPawnMidgamePenalty[f];
ev -= IsolatedPawnEndgamePenalty[f];
if(pos.file_is_half_open(them, f)) {
mv -= IsolatedPawnMidgamePenalty[f] / 2;
ev -= IsolatedPawnEndgamePenalty[f] / 2;
}
}
if(doubled) {
mv -= DoubledPawnMidgamePenalty[f];
ev -= DoubledPawnEndgamePenalty[f];
}
if(backward) {
mv -= BackwardPawnMidgamePenalty[f];
ev -= BackwardPawnEndgamePenalty[f];
if(pos.file_is_half_open(them, f)) {
mv -= BackwardPawnMidgamePenalty[f] / 2;
ev -= BackwardPawnEndgamePenalty[f] / 2;
}
}
if(chain) {
mv += ChainMidgameBonus[f];
ev += ChainEndgameBonus[f];
}
if(candidate) {
mv += CandidateMidgameBonus[relative_rank(us, s)];
ev += CandidateEndgameBonus[relative_rank(us, s)];
}
mgValue[us] += mv;
egValue[us] += ev;
// If the pawn is passed, set the square of the pawn in the passedPawns
// bitboard:
if(passed)
set_bit(&(pi->passedPawns), s);
}
}
// If the pawn is passed, set the square of the pawn in the passedPawns
// bitboard
if (passed)
set_bit(&(pi->passedPawns), s);
} // while(pawns)
} // for(colors)
pi->mgValue = int16_t(mgValue[WHITE] - mgValue[BLACK]);
pi->egValue = int16_t(egValue[WHITE] - egValue[BLACK]);
return pi;
}

View File

@@ -120,10 +120,10 @@ inline bool PawnInfo::has_open_file_to_right(Color c, File f) const {
}
inline void PawnInfo::clear() {
mgValue = egValue = 0;
passedPawns = EmptyBoardBB;
ksStormValue[WHITE] = ksStormValue[BLACK] = 0;
qsStormValue[WHITE] = qsStormValue[BLACK] = 0;
Key k = key;
memset(this, 0, sizeof(PawnInfo));
key = k;
halfOpenFiles[WHITE] = halfOpenFiles[BLACK] = 0xFF;
}

View File

@@ -1582,10 +1582,16 @@ void Position::undo_null_move(const UndoInfo &u) {
/// Position::see() is a static exchange evaluator: It tries to estimate the
/// material gain or loss resulting from a move. There are two versions of
/// this function: One which takes a move as input, and one which takes a
/// 'from' and a 'to' square. The function does not yet understand promotions
/// or en passant captures.
/// material gain or loss resulting from a move. There are three versions of
/// this function: One which takes a destination square as input, one takes a
/// move, and one which takes a 'from' and a 'to' square. The function does
/// not yet understand promotions or en passant captures.
int Position::see(Square to) const {
assert(square_is_ok(to));
return see(SQ_NONE, to);
}
int Position::see(Move m) const {
@@ -1595,18 +1601,22 @@ int Position::see(Move m) const {
int Position::see(Square from, Square to) const {
// Approximate material values, with pawn = 1
// Material values
static const int seeValues[18] = {
0, 1, 3, 3, 5, 10, 100, 0, 0, 1, 3, 3, 5, 10, 100, 0, 0, 0
0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0,
0, PawnValueMidgame, KnightValueMidgame, BishopValueMidgame,
RookValueMidgame, QueenValueMidgame, QueenValueMidgame*10, 0,
0, 0
};
Bitboard attackers, occ, b;
assert(square_is_ok(from));
assert(square_is_ok(from) || from == SQ_NONE);
assert(square_is_ok(to));
// Initialize colors
Color us = color_of_piece_on(from);
Color us = (from != SQ_NONE ? color_of_piece_on(from) : opposite_color(color_of_piece_on(to)));
Color them = opposite_color(us);
// Initialize pieces
@@ -1616,15 +1626,49 @@ int Position::see(Square from, Square to) const {
// Find all attackers to the destination square, with the moving piece
// removed, but possibly an X-ray attacker added behind it.
occ = occupied_squares();
clear_bit(&occ, from);
attackers = (rook_attacks_bb(to, occ) & rooks_and_queens())
| (bishop_attacks_bb(to, occ) & bishops_and_queens())
| (piece_attacks<KNIGHT>(to) & knights())
| (piece_attacks<KING>(to) & kings())
| (pawn_attacks(WHITE, to) & pawns(BLACK))
| (pawn_attacks(BLACK, to) & pawns(WHITE));
// If the opponent has no attackers, we are finished
// Handle enpassant moves
if (ep_square() == to && type_of_piece_on(from) == PAWN)
{
assert(capture == EMPTY);
Square capQq = (side_to_move() == WHITE)? (to - DELTA_N) : (to - DELTA_S);
capture = piece_on(capQq);
assert(type_of_piece_on(capQq) == PAWN);
// Remove the captured pawn
clear_bit(&occ, capQq);
}
while (true)
{
clear_bit(&occ, from);
attackers = (rook_attacks_bb(to, occ) & rooks_and_queens())
| (bishop_attacks_bb(to, occ) & bishops_and_queens())
| (piece_attacks<KNIGHT>(to) & knights())
| (piece_attacks<KING>(to) & kings())
| (pawn_attacks(WHITE, to) & pawns(BLACK))
| (pawn_attacks(BLACK, to) & pawns(WHITE));
if (from != SQ_NONE)
break;
// If we don't have any attacker we are finished
if ((attackers & pieces_of_color(us)) == EmptyBoardBB)
return 0;
// Locate the least valuable attacker to the destination square
// and use it to initialize from square.
PieceType pt;
for (pt = PAWN; !(attackers & pieces_of_color_and_type(us, pt)); pt++)
assert(pt < KING);
from = first_1(attackers & pieces_of_color_and_type(us, pt));
piece = piece_on(from);
}
// If the opponent has no attackers we are finished
if ((attackers & pieces_of_color(them)) == EmptyBoardBB)
return seeValues[capture];

View File

@@ -253,6 +253,7 @@ public:
// Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const;
int see(Square to) const;
// Accessing hash keys
Key get_key() const;

View File

@@ -47,6 +47,23 @@ namespace {
/// Types
// The BetaCounterType class is used to order moves at ply one.
// Apart for the first one that has its score, following moves
// normally have score -VALUE_INFINITE, so are ordered according
// to the number of beta cutoffs occurred under their subtree during
// the last iteration.
struct BetaCounterType {
BetaCounterType();
void clear();
void add(Color us, Depth d, int threadID);
void read(Color us, int64_t& our, int64_t& their);
int64_t hits[THREAD_MAX][2];
};
// The RootMove class is used for moves at the root at the tree. For each
// root move, we store a score, a node count, and a PV (really a refutation
// in the case of moves which fail low).
@@ -60,6 +77,7 @@ namespace {
Value score;
int64_t nodes, cumulativeNodes;
Move pv[PLY_MAX_PLUS_2];
int64_t ourBeta, theirBeta;
};
@@ -74,6 +92,7 @@ namespace {
inline Value get_move_score(int moveNum) const;
inline void set_move_score(int moveNum, Value score);
inline void set_move_nodes(int moveNum, int64_t nodes);
inline void set_beta_counters(int moveNum, int64_t our, int64_t their);
void set_move_pv(int moveNum, const Move pv[]);
inline Move get_move_pv(int moveNum, int i) const;
inline int64_t get_move_cumulative_nodes(int moveNum) const;
@@ -107,16 +126,13 @@ namespace {
const bool UseIIDAtNonPVNodes = false;
// Use null move driven internal iterative deepening?
bool UseNullDrivenIID = true;
bool UseNullDrivenIID = false;
// Internal iterative deepening margin. At Non-PV moves, when
// UseIIDAtNonPVNodes is true, we do an internal iterative deepening search
// when the static evaluation is at most IIDMargin below beta.
const Value IIDMargin = Value(0x100);
// Use easy moves?
const bool UseEasyMove = true;
// Easy move margin. An easy move candidate must be at least this much
// better than the second best move.
const Value EasyMoveMargin = Value(0x200);
@@ -146,13 +162,14 @@ namespace {
bool UseQSearchFutilityPruning = true;
bool UseFutilityPruning = true;
// Margins for futility pruning in the quiescence search, at frontier
// nodes, and at pre-frontier nodes
Value FutilityMargin0 = Value(0x80);
Value FutilityMargin1 = Value(0x100);
Value FutilityMargin2 = Value(0x300);
// Margins for futility pruning in the quiescence search, and at frontier
// and near frontier nodes
Value FutilityMarginQS = Value(0x80);
Value FutilityMargins[6] = { Value(0x100), Value(0x200), Value(0x250),
Value(0x2A0), Value(0x340), Value(0x3A0) };
// Razoring
const bool RazorAtDepthOne = false;
Depth RazorDepth = 4*OnePly;
Value RazorMargin = Value(0x300);
@@ -177,9 +194,10 @@ namespace {
int NodesSincePoll;
int NodesBetweenPolls = 30000;
// Iteration counter
// Iteration counters
int Iteration;
bool LastIterations;
BetaCounterType BetaCounter;
// Scores and number of times the best move changed for each iteration:
Value ValueByIteration[PLY_MAX_PLUS_2];
@@ -247,6 +265,7 @@ namespace {
void update_pv(SearchStack ss[], int ply);
void sp_update_pv(SearchStack *pss, SearchStack ss[], int ply);
bool connected_moves(const Position &pos, Move m1, Move m2);
bool value_is_mate(Value value);
bool move_is_killer(Move m, const SearchStack& ss);
Depth extension(const Position &pos, Move m, bool pvNode, bool check, bool singleReply, bool mateThreat, bool* dangerous);
bool ok_to_do_nullmove(const Position &pos);
@@ -397,9 +416,10 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move,
UseQSearchFutilityPruning = get_option_value_bool("Futility Pruning (Quiescence Search)");
UseFutilityPruning = get_option_value_bool("Futility Pruning (Main Search)");
FutilityMargin0 = value_from_centipawns(get_option_value_int("Futility Margin 0"));
FutilityMargin1 = value_from_centipawns(get_option_value_int("Futility Margin 1"));
FutilityMargin2 = value_from_centipawns(get_option_value_int("Futility Margin 2"));
FutilityMarginQS = value_from_centipawns(get_option_value_int("Futility Margin (Quiescence Search)"));
int fmScale = get_option_value_int("Futility Margin Scale Factor (Main Search)");
for (int i = 0; i < 6; i++)
FutilityMargins[i] = (FutilityMargins[i] * fmScale) / 100;
RazorDepth = (get_option_value_int("Maximum Razoring Depth") + 1) * OnePly;
RazorMargin = value_from_centipawns(get_option_value_int("Razoring Margin"));
@@ -429,7 +449,6 @@ void think(const Position &pos, bool infinite, bool ponder, int side_to_move,
// Set thinking time:
int myTime = time[side_to_move];
int myIncrement = increment[side_to_move];
int oppTime = time[1 - side_to_move];
if (!movesToGo) // Sudden death time control
{
@@ -729,6 +748,12 @@ namespace {
if (UseLogFile)
{
if (dbg_show_mean)
dbg_print_mean(LogFile);
if (dbg_show_hit_rate)
dbg_print_hit_rate(LogFile);
UndoInfo u;
LogFile << "Nodes: " << nodes_searched() << std::endl
<< "Nodes/second: " << nps() << std::endl
@@ -768,6 +793,9 @@ namespace {
// are used to sort the root moves at the next iteration.
nodes = nodes_searched();
// Reset beta cut-off counters
BetaCounter.clear();
// Pick the next root move, and print the move and the move number to
// the standard output.
move = ss[0].currentMove = rml.get_move(i);
@@ -823,6 +851,11 @@ namespace {
// sort the root moves at the next iteration.
rml.set_move_nodes(i, nodes_searched() - nodes);
// Remember the beta-cutoff statistics
int64_t our, their;
BetaCounter.read(pos.side_to_move(), our, their);
rml.set_beta_counters(i, our, their);
assert(value >= -VALUE_INFINITE && value <= VALUE_INFINITE);
if (value <= alpha && i >= MultiPV)
@@ -905,16 +938,17 @@ namespace {
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
if (AbortSearch || thread_should_stop(threadID))
return Value(0);
if (depth < OnePly)
return qsearch(pos, ss, alpha, beta, Depth(0), ply, threadID);
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
init_node(pos, ss, ply, threadID);
// After init_node() that calls poll()
if (AbortSearch || thread_should_stop(threadID))
return Value(0);
if (pos.is_draw())
return VALUE_DRAW;
@@ -968,9 +1002,8 @@ namespace {
movesSearched[moveCount++] = ss[ply].currentMove = move;
if (moveIsCapture)
ss[ply].currentMoveCaptureValue = pos.midgame_value_of_piece_on(move_to(move));
else if (move_is_ep(move))
ss[ply].currentMoveCaptureValue = PawnValueMidgame;
ss[ply].currentMoveCaptureValue =
move_is_ep(move)? PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move));
else
ss[ply].currentMoveCaptureValue = Value(0);
@@ -1003,7 +1036,7 @@ namespace {
else
value = alpha + 1; // Just to trigger next condition
if (value > alpha) // Go with full depth pv search
if (value > alpha) // Go with full depth non-pv search
{
ss[ply].reduction = Depth(0);
value = -search(pos, ss, -alpha, newDepth, ply+1, true, threadID);
@@ -1041,7 +1074,9 @@ namespace {
// If we are at ply 1, and we are searching the first root move at
// ply 0, set the 'Problem' variable if the score has dropped a lot
// (from the computer's point of view) since the previous iteration:
if (Iteration >= 2 && -value <= ValueByIteration[Iteration-1] - ProblemMargin)
if ( ply == 1
&& Iteration >= 2
&& -value <= ValueByIteration[Iteration-1] - ProblemMargin)
Problem = true;
}
@@ -1073,6 +1108,7 @@ namespace {
else if (bestValue >= beta)
{
BetaCounter.add(pos.side_to_move(), depth, threadID);
Move m = ss[ply].pv[ply];
if (ok_to_history(pos, m)) // Only non capture moves are considered
{
@@ -1097,21 +1133,22 @@ namespace {
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
EvalInfo ei;
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
if (AbortSearch || thread_should_stop(threadID))
return Value(0);
if (depth < OnePly)
return qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
init_node(pos, ss, ply, threadID);
// After init_node() that calls poll()
if (AbortSearch || thread_should_stop(threadID))
return Value(0);
if (pos.is_draw())
return VALUE_DRAW;
EvalInfo ei;
if (ply >= PLY_MAX - 1)
return evaluate(pos, ei, threadID);
@@ -1139,7 +1176,9 @@ namespace {
// Null move search
if ( allowNullmove
&& depth > OnePly
&& !isCheck
&& !value_is_mate(beta)
&& ok_to_do_nullmove(pos)
&& approximateEval >= beta - NullMoveMargin)
{
@@ -1147,25 +1186,30 @@ namespace {
UndoInfo u;
pos.do_null_move(u);
int R = (depth > 7 ? 4 : 3);
int R = (depth >= 4 * OnePly ? 4 : 3); // Null move dynamic reduction
Value nullValue = -search(pos, ss, -(beta-1), depth-R*OnePly, ply+1, false, threadID);
// Check for a null capture artifact, if the value without the null capture
// is above beta then there is a good possibility that this is a cut-node.
// We will do an IID later to find a ttMove.
// is above beta then mark the node as a suspicious failed low. We will verify
// later if we are really under threat.
if ( UseNullDrivenIID
&& nullValue < beta
&& depth > 6 * OnePly
&&!value_is_mate(nullValue)
&& ttMove == MOVE_NONE
&& ss[ply + 1].currentMove != MOVE_NONE
&& pos.move_is_capture(ss[ply + 1].currentMove)
&& pos.see(ss[ply + 1].currentMove) * PawnValueMidgame + nullValue > beta - IIDMargin)
&& pos.see(ss[ply + 1].currentMove) + nullValue >= beta)
nullDrivenIID = true;
pos.undo_null_move(u);
if (nullValue >= beta)
if (value_is_mate(nullValue))
{
/* Do not return unproven mates */
}
else if (nullValue >= beta)
{
if (depth < 6 * OnePly)
return beta;
@@ -1176,9 +1220,9 @@ namespace {
return beta;
} else {
// The null move failed low, which means that we may be faced with
// some kind of threat. If the previous move was reduced, check if
// some kind of threat. If the previous move was reduced, check if
// the move that refuted the null move was somehow connected to the
// move which was reduced. If a connection is found, return a fail
// move which was reduced. If a connection is found, return a fail
// low score (which will cause the reduced move to fail high in the
// parent node, which will trigger a re-search with full depth).
if (nullValue == value_mated_in(ply + 2))
@@ -1194,11 +1238,17 @@ namespace {
}
}
// Null move search not allowed, try razoring
else if ( (approximateEval < beta - RazorMargin && depth < RazorDepth)
||(approximateEval < beta - PawnValueMidgame && depth <= OnePly))
else if ( !value_is_mate(beta)
&& approximateEval < beta - RazorMargin
&& depth < RazorDepth
&& (RazorAtDepthOne || depth > OnePly)
&& ttMove == MOVE_NONE
&& !pos.has_pawn_on_7th(pos.side_to_move()))
{
Value v = qsearch(pos, ss, beta-1, beta, Depth(0), ply, threadID);
if (v < beta)
if ( (v < beta - RazorMargin - RazorMargin / 4)
|| (depth <= 2*OnePly && v < beta - RazorMargin)
|| (depth <= OnePly && v < beta - RazorMargin / 2))
return v;
}
@@ -1213,12 +1263,15 @@ namespace {
{
// The null move failed low due to a suspicious capture. Perhaps we
// are facing a null capture artifact due to the side to move change
// and this is a cut-node. So it's a good time to search for a ttMove.
// and this position should fail high. So do a normal search with a
// reduced depth to get a good ttMove to use in the following full
// depth search.
Move tm = ss[ply].threatMove;
assert(tm != MOVE_NONE);
assert(ttMove == MOVE_NONE);
search(pos, ss, beta, Min(depth/2, depth-3*OnePly), ply, false, threadID);
search(pos, ss, beta, depth/2, ply, false, threadID);
ttMove = ss[ply].pv[ply];
ss[ply].threatMove = tm;
}
@@ -1261,15 +1314,18 @@ namespace {
&& !moveIsCapture
&& !move_promotion(move))
{
// History pruning. See ok_to_prune() definition
if ( moveCount >= 2 + int(depth)
&& ok_to_prune(pos, move, ss[ply].threatMove, depth))
continue;
if (depth < 3 * OnePly && approximateEval < beta)
// Value based pruning
if (depth < 7 * OnePly && approximateEval < beta)
{
if (futilityValue == VALUE_NONE)
futilityValue = evaluate(pos, ei, threadID)
+ (depth < 2 * OnePly ? FutilityMargin1 : FutilityMargin2);
+ FutilityMargins[int(depth)/2 - 1]
+ 32 * (depth & 1);
if (futilityValue < beta)
{
@@ -1347,6 +1403,7 @@ namespace {
TT.store(pos, value_to_tt(bestValue, ply), depth, MOVE_NONE, VALUE_TYPE_UPPER);
else
{
BetaCounter.add(pos.side_to_move(), depth, threadID);
Move m = ss[ply].pv[ply];
if (ok_to_history(pos, m)) // Only non capture moves are considered
{
@@ -1372,15 +1429,14 @@ namespace {
assert(ply >= 0 && ply < PLY_MAX);
assert(threadID >= 0 && threadID < ActiveThreads);
EvalInfo ei;
// Initialize, and make an early exit in case of an aborted search,
// an instant draw, maximum ply reached, etc.
init_node(pos, ss, ply, threadID);
// After init_node() that calls poll()
if (AbortSearch || thread_should_stop(threadID))
return Value(0);
init_node(pos, ss, ply, threadID);
if (pos.is_draw())
return VALUE_DRAW;
@@ -1390,14 +1446,16 @@ namespace {
return value_from_tt(tte->value(), ply);
// Evaluate the position statically
Value staticValue = evaluate(pos, ei, threadID);
EvalInfo ei;
bool isCheck = pos.is_check();
Value staticValue = (isCheck ? -VALUE_INFINITE : evaluate(pos, ei, threadID));
if (ply == PLY_MAX - 1)
return staticValue;
return evaluate(pos, ei, threadID);
// Initialize "stand pat score", and return it immediately if it is
// at least beta.
Value bestValue = (pos.is_check() ? -VALUE_INFINITE : staticValue);
Value bestValue = staticValue;
if (bestValue >= beta)
return bestValue;
@@ -1408,12 +1466,11 @@ namespace {
// Initialize a MovePicker object for the current position, and prepare
// to search the moves. Because the depth is <= 0 here, only captures,
// queen promotions and checks (only if depth == 0) will be generated.
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, depth, &ei);
bool pvNode = (beta - alpha != 1);
MovePicker mp = MovePicker(pos, pvNode, MOVE_NONE, EmptySearchStack, depth, isCheck ? NULL : &ei);
Move move;
int moveCount = 0;
Bitboard dcCandidates = mp.discovered_check_candidates();
bool isCheck = pos.is_check();
bool pvNode = (beta - alpha != 1);
bool enoughMaterial = pos.non_pawn_material(pos.side_to_move()) > RookValueMidgame;
// Loop through the moves until no moves remain or a beta cutoff
@@ -1438,7 +1495,8 @@ namespace {
Value futilityValue = staticValue
+ Max(pos.midgame_value_of_piece_on(move_to(move)),
pos.endgame_value_of_piece_on(move_to(move)))
+ FutilityMargin0
+ (move_is_ep(move) ? PawnValueEndgame : Value(0))
+ FutilityMarginQS
+ ei.futilityMargin;
if (futilityValue < alpha)
@@ -1641,8 +1699,11 @@ namespace {
assert(move_is_ok(move));
ss[sp->ply].currentMoveCaptureValue = move_is_ep(move)?
PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move));
if (moveIsCapture)
ss[sp->ply].currentMoveCaptureValue =
move_is_ep(move)? PawnValueMidgame : pos.midgame_value_of_piece_on(move_to(move));
else
ss[sp->ply].currentMoveCaptureValue = Value(0);
lock_grab(&(sp->lock));
int moveCount = ++sp->moves;
@@ -1723,8 +1784,10 @@ namespace {
}
// If we are at ply 1, and we are searching the first root move at
// ply 0, set the 'Problem' variable if the score has dropped a lot
// (from the computer's point of view) since the previous iteration:
if (Iteration >= 2 && -value <= ValueByIteration[Iteration-1] - ProblemMargin)
// (from the computer's point of view) since the previous iteration.
if ( sp->ply == 1
&& Iteration >= 2
&& -value <= ValueByIteration[Iteration-1] - ProblemMargin)
Problem = true;
}
lock_release(&(sp->lock));
@@ -1733,7 +1796,7 @@ namespace {
lock_grab(&(sp->lock));
// If this is the master thread and we have been asked to stop because of
// a beta cutoff higher up in the tree, stop all slave threads:
// a beta cutoff higher up in the tree, stop all slave threads.
if (sp->master == threadID && thread_should_stop(threadID))
for (int i = 0; i < ActiveThreads; i++)
if (sp->slaves[i])
@@ -1745,6 +1808,32 @@ namespace {
lock_release(&(sp->lock));
}
/// The BetaCounterType class
BetaCounterType::BetaCounterType() { clear(); }
void BetaCounterType::clear() {
for (int i = 0; i < THREAD_MAX; i++)
hits[i][WHITE] = hits[i][BLACK] = 0ULL;
}
void BetaCounterType::add(Color us, Depth d, int threadID) {
// Weighted count based on depth
hits[threadID][us] += int(d);
}
void BetaCounterType::read(Color us, int64_t& our, int64_t& their) {
our = their = 0UL;
for (int i = 0; i < THREAD_MAX; i++)
{
our += hits[i][us];
their += hits[i][opposite_color(us)];
}
}
/// The RootMove class
@@ -1764,7 +1853,7 @@ namespace {
if (score != m.score)
return (score < m.score);
return nodes <= m.nodes;
return theirBeta <= m.theirBeta;
}
/// The RootMoveList class
@@ -1827,6 +1916,11 @@ namespace {
moves[moveNum].cumulativeNodes += nodes;
}
inline void RootMoveList::set_beta_counters(int moveNum, int64_t our, int64_t their) {
moves[moveNum].ourBeta = our;
moves[moveNum].theirBeta = their;
}
void RootMoveList::set_move_pv(int moveNum, const Move pv[]) {
int j;
for(j = 0; pv[j] != MOVE_NONE; j++)
@@ -2051,6 +2145,18 @@ namespace {
}
// value_is_mate() checks if the given value is a mate one
// eventually compensated for the ply.
bool value_is_mate(Value value) {
assert(abs(value) <= VALUE_INFINITE);
return value <= value_mated_in(PLY_MAX)
|| value >= value_mate_in(PLY_MAX);
}
// move_is_killer() checks if the given move is among the
// killer moves of that ply.
@@ -2075,6 +2181,8 @@ namespace {
Depth extension(const Position &pos, Move m, bool pvNode, bool check,
bool singleReply, bool mateThreat, bool* dangerous) {
assert(m != MOVE_NONE);
Depth result = Depth(0);
*dangerous = check || singleReply || mateThreat;
@@ -2087,21 +2195,26 @@ namespace {
if (mateThreat)
result += MateThreatExtension[pvNode];
if (pos.move_is_pawn_push_to_7th(m))
if (pos.type_of_piece_on(move_from(m)) == PAWN)
{
result += PawnPushTo7thExtension[pvNode];
*dangerous = true;
}
if (pos.move_is_passed_pawn_push(m))
{
result += PassedPawnExtension[pvNode];
*dangerous = true;
if (pos.move_is_pawn_push_to_7th(m))
{
result += PawnPushTo7thExtension[pvNode];
*dangerous = true;
}
if (pos.move_is_passed_pawn_push(m))
{
result += PassedPawnExtension[pvNode];
*dangerous = true;
}
}
if ( pos.midgame_value_of_piece_on(move_to(m)) >= RookValueMidgame
if ( pos.move_is_capture(m)
&& pos.type_of_piece_on(move_to(m)) != PAWN
&& ( pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK)
- pos.midgame_value_of_piece_on(move_to(m)) == Value(0))
&& !move_promotion(m))
&& !move_promotion(m)
&& !move_is_ep(m))
{
result += PawnEndgameExtension[pvNode];
*dangerous = true;
@@ -2156,31 +2269,35 @@ namespace {
tto = move_to(threat);
// Case 1: Castling moves are never pruned.
if(move_is_castle(m))
return false;
if (move_is_castle(m))
return false;
// Case 2: Don't prune moves which move the threatened piece
if(!PruneEscapeMoves && threat != MOVE_NONE && mfrom == tto)
return false;
if (!PruneEscapeMoves && threat != MOVE_NONE && mfrom == tto)
return false;
// Case 3: If the threatened piece has value less than or equal to the
// value of the threatening piece, don't prune move which defend it.
if(!PruneDefendingMoves && threat != MOVE_NONE
&& (piece_value_midgame(pos.piece_on(tfrom))
>= piece_value_midgame(pos.piece_on(tto)))
&& pos.move_attacks_square(m, tto))
if ( !PruneDefendingMoves
&& threat != MOVE_NONE
&& pos.move_is_capture(threat)
&& ( pos.midgame_value_of_piece_on(tfrom) >= pos.midgame_value_of_piece_on(tto)
|| pos.type_of_piece_on(tfrom) == KING)
&& pos.move_attacks_square(m, tto))
return false;
// Case 4: Don't prune moves with good history.
if(!H.ok_to_prune(pos.piece_on(move_from(m)), m, d))
return false;
if (!H.ok_to_prune(pos.piece_on(move_from(m)), m, d))
return false;
// Case 5: If the moving piece in the threatened move is a slider, don't
// prune safe moves which block its ray.
if(!PruneBlockingMoves && threat != MOVE_NONE
&& piece_is_slider(pos.piece_on(tfrom))
&& bit_is_set(squares_between(tfrom, tto), mto) && pos.see(m) >= 0)
return false;
if ( !PruneBlockingMoves
&& threat != MOVE_NONE
&& piece_is_slider(pos.piece_on(tfrom))
&& bit_is_set(squares_between(tfrom, tto), mto)
&& pos.see(m) >= 0)
return false;
return true;
}

View File

@@ -25,7 +25,6 @@
//// Includes
////
#include "types.h"
#include "depth.h"
#include "history.h"
#include "lock.h"

View File

@@ -85,6 +85,7 @@ namespace {
o.push_back(Option("Pawn Structure (Endgame)", 100, 0, 200));
o.push_back(Option("Passed Pawns (Middle Game)", 100, 0, 200));
o.push_back(Option("Passed Pawns (Endgame)", 100, 0, 200));
o.push_back(Option("Space", 100, 0, 200));
o.push_back(Option("Aggressiveness", 100, 0, 200));
o.push_back(Option("Cowardice", 100, 0, 200));
o.push_back(Option("King Safety Curve", "Quadratic", COMBO));
@@ -96,8 +97,7 @@ namespace {
o.push_back(Option("King Safety X Intercept", 0, 0, 20));
o.push_back(Option("King Safety Max Slope", 30, 10, 100));
o.push_back(Option("King Safety Max Value", 500, 100, 1000));
o.push_back(Option("Queen Contact Check Bonus", 4, 0, 8));
o.push_back(Option("Rook Contact Check Bonus", 2, 0, 4));
o.push_back(Option("Queen Contact Check Bonus", 3, 0, 8));
o.push_back(Option("Queen Check Bonus", 2, 0, 4));
o.push_back(Option("Rook Check Bonus", 1, 0, 4));
o.push_back(Option("Bishop Check Bonus", 1, 0, 4));
@@ -120,12 +120,11 @@ namespace {
o.push_back(Option("Full Depth Moves (non-PV nodes)", 3, 1, 100));
o.push_back(Option("Threat Depth", 5, 0, 100));
o.push_back(Option("Selective Plies", 7, 0, 10));
o.push_back(Option("Null driven IID", true));
o.push_back(Option("Null driven IID", false));
o.push_back(Option("Futility Pruning (Main Search)", true));
o.push_back(Option("Futility Pruning (Quiescence Search)", true));
o.push_back(Option("Futility Margin 0", 50, 0, 1000));
o.push_back(Option("Futility Margin 1", 100, 0, 1000));
o.push_back(Option("Futility Margin 2", 300, 0, 1000));
o.push_back(Option("Futility Margin (Quiescence Search)", 50, 0, 1000));
o.push_back(Option("Futility Margin Scale Factor (Main Search)", 100, 0, 1000));
o.push_back(Option("Maximum Razoring Depth", 3, 0, 4));
o.push_back(Option("Razoring Margin", 300, 150, 600));
o.push_back(Option("LSN filtering", false));
@@ -184,7 +183,9 @@ namespace {
{
std::istringstream ss(it->currentValue);
ss >> ret;
}
} else
assert(false);
return ret;
}