mirror of
https://github.com/opelly27/Stockfish.git
synced 2026-05-20 14:27:45 +00:00
Compare commits
97 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 11763d2b7f | |||
| d99a95df29 | |||
| 0da1d6a846 | |||
| 5d94305af3 | |||
| e4fd9a2df7 | |||
| aedc6c6f1f | |||
| dae4e7df07 | |||
| 96a32eec69 | |||
| e46d3670fd | |||
| 8cd5cb930d | |||
| 2feb9d5100 | |||
| d11426c777 | |||
| e96f56adfa | |||
| e3b03f13b3 | |||
| 54b7da120f | |||
| 3fafc9768a | |||
| 23490bd825 | |||
| 61c6a3d5a0 | |||
| 31d4f0b734 | |||
| f178f0a291 | |||
| 72ca727b38 | |||
| 2d0146fe1d | |||
| 96d0501735 | |||
| b58ad355ca | |||
| 17000d1ea0 | |||
| b09cbaebb9 | |||
| 725c504a5f | |||
| bfbfc24d07 | |||
| a55b06d3c9 | |||
| ecc19381b4 | |||
| dae2f600d6 | |||
| 4c294932e7 | |||
| c831b00544 | |||
| 5b853c9be6 | |||
| 5f8f83bc05 | |||
| 8ee3124487 | |||
| 07b45151d2 | |||
| 9e3ab9099f | |||
| f09884d72f | |||
| ab29d8df67 | |||
| 389dc0e83b | |||
| 67aac4889e | |||
| aaad48464b | |||
| 235df6a887 | |||
| 8d86c87e1e | |||
| c172af1b61 | |||
| b7c36d078b | |||
| db1b0bfa1d | |||
| 6f946f823c | |||
| 2de78ebc7b | |||
| 32934c0c8d | |||
| b4fcfed55b | |||
| dc2302b701 | |||
| 268c12bf31 | |||
| 9f943a132a | |||
| 20390fcb3c | |||
| d95b9189d8 | |||
| 9d5d69e435 | |||
| 08265aef81 | |||
| 9d1e4d041d | |||
| cff3a6d33e | |||
| 20a2ca366f | |||
| c3ba5fb9d3 | |||
| eba8925d81 | |||
| ee9f650242 | |||
| 8a0dd93c56 | |||
| 5a72ff128e | |||
| 3f63dd1023 | |||
| 62ab7e4612 | |||
| 1867785231 | |||
| bac4da70c9 | |||
| 8189ae9e1c | |||
| 1d525bb45f | |||
| da7d872eda | |||
| 49d52b8266 | |||
| 75d001addd | |||
| 7daaf03b39 | |||
| 2e778445d5 | |||
| 93bc05cf69 | |||
| a7227ac26f | |||
| bb0da595a7 | |||
| 20d7197a9b | |||
| f4758ced90 | |||
| 3c05bd70eb | |||
| 7000e100bd | |||
| 2ed22e4fc8 | |||
| 940c53c366 | |||
| 4df8651c82 | |||
| 2d4e2bc62a | |||
| d89a03cc35 | |||
| 34b1d0538b | |||
| cdf1f23bc5 | |||
| e86468cc63 | |||
| e7bad2687a | |||
| 3f610e2b13 | |||
| 6cc11272e2 | |||
| a28c17ecb6 |
+26
-7
@@ -71,8 +71,8 @@ void benchmark(const std::string& commandLine) {
|
||||
|
||||
std::istringstream csVal(commandLine);
|
||||
std::istringstream csStr(commandLine);
|
||||
std::string ttSize, threads, fileName;
|
||||
int val, secsPerPos;
|
||||
std::string ttSize, threads, fileName, limitType;
|
||||
int val, secsPerPos, maxDepth, maxNodes;
|
||||
|
||||
csStr >> ttSize;
|
||||
csVal >> val;
|
||||
@@ -95,8 +95,18 @@ void benchmark(const std::string& commandLine) {
|
||||
set_option_value("Use Search Log", "true");
|
||||
set_option_value("Search Log Filename", "bench.txt");
|
||||
|
||||
csVal >> secsPerPos;
|
||||
csVal >> val;
|
||||
csVal >> fileName;
|
||||
csVal >> limitType;
|
||||
|
||||
secsPerPos = maxDepth = maxNodes = 0;
|
||||
|
||||
if (limitType == "time")
|
||||
secsPerPos = val * 1000;
|
||||
else if (limitType == "depth")
|
||||
maxDepth = val;
|
||||
else
|
||||
maxNodes = val;
|
||||
|
||||
std::vector<std::string> positions;
|
||||
|
||||
@@ -121,12 +131,21 @@ void benchmark(const std::string& commandLine) {
|
||||
for (int i = 0; i < 16; i++)
|
||||
positions.push_back(std::string(BenchmarkPositions[i]));
|
||||
|
||||
int startTime = get_system_time();
|
||||
std::vector<std::string>::iterator it;
|
||||
for (it = positions.begin(); it != positions.end(); ++it)
|
||||
int cnt = 1;
|
||||
int64_t totalNodes = 0;
|
||||
for (it = positions.begin(); it != positions.end(); ++it, ++cnt)
|
||||
{
|
||||
Move moves[1] = {MOVE_NONE};
|
||||
int dummy[2] = {0, 0};
|
||||
Move moves[1] = {MOVE_NONE};
|
||||
int dummy[2] = {0, 0};
|
||||
Position pos(*it);
|
||||
think(pos, true, false, 0, dummy, dummy, 0, 0, 0, secsPerPos * 1000, moves);
|
||||
std::cout << "\nProcessing position " << cnt << '/' << positions.size() << std::endl << std::endl;
|
||||
think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves);
|
||||
totalNodes += nodes_searched();
|
||||
}
|
||||
std::cout << "\nProcessing time (ms) " << get_system_time() - startTime << std::endl
|
||||
<< "Nodes searched " << totalNodes << std::endl
|
||||
<< "Press any key to exit" << std::endl;
|
||||
std::cin >> fileName;
|
||||
}
|
||||
|
||||
+4
-2
@@ -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];
|
||||
|
||||
+15
-2
@@ -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.
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+85
-40
@@ -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.
|
||||
|
||||
@@ -1120,13 +1172,7 @@ namespace {
|
||||
|
||||
ev = apply_scale_factor(ev, sf[(ev > Value(0) ? WHITE : BLACK)]);
|
||||
|
||||
// Linearized sigmoid interpolator
|
||||
int sph = int(ph);
|
||||
sph -= (64 - sph) / 4;
|
||||
sph = Min(PHASE_MIDGAME, Max(PHASE_ENDGAME, sph));
|
||||
|
||||
Value result = Value(int((mv * sph + ev * (128 - sph)) / 128));
|
||||
|
||||
Value result = Value(int((mv * ph + ev * (128 - ph)) / 128));
|
||||
return Value(int(result) & ~(GrainSize - 1));
|
||||
}
|
||||
|
||||
@@ -1162,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");
|
||||
|
||||
+5
-3
@@ -71,16 +71,18 @@ int main(int argc, char *argv[]) {
|
||||
// Process command line arguments
|
||||
if (argc >= 2 && string(argv[1]) == "bench")
|
||||
{
|
||||
if (argc < 4 || argc > 6)
|
||||
if (argc < 4 || argc > 7)
|
||||
{
|
||||
std::cout << "Usage: glaurung bench <hash size> <threads> "
|
||||
<< "[time = 60s] [fen positions file = default]"
|
||||
<< "[time = 60s] [fen positions file = default] "
|
||||
<< "[time, depth or node limited = time]"
|
||||
<< std::endl;
|
||||
exit(0);
|
||||
}
|
||||
string time = argc > 4 ? argv[4] : "60";
|
||||
string fen = argc > 5 ? argv[5] : "default";
|
||||
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen);
|
||||
string lim = argc > 6 ? argv[6] : "time";
|
||||
benchmark(string(argv[2]) + " " + string(argv[3]) + " " + time + " " + fen + " " + lim);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
+56
-1
@@ -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();
|
||||
|
||||
+15
-14
@@ -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.01";
|
||||
const std::string EngineVersion = "1.2";
|
||||
|
||||
|
||||
////
|
||||
@@ -60,21 +61,21 @@ extern int Bioskey();
|
||||
////
|
||||
//// Debug
|
||||
////
|
||||
extern long dbg_cnt0;
|
||||
extern long dbg_cnt1;
|
||||
|
||||
inline void dbg_hit_on(bool b) { 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_cnt0++; }
|
||||
inline void dbg_after() { dbg_cnt1++; }
|
||||
|
||||
inline void dbg_mean_of(int v) { dbg_cnt0++; dbg_cnt1 += v; }
|
||||
|
||||
extern void dbg_print_hit_rate();
|
||||
extern void dbg_print_mean();
|
||||
|
||||
extern bool dbg_show_mean;
|
||||
extern bool dbg_show_hit_rate;
|
||||
|
||||
extern long dbg_cnt0;
|
||||
extern long dbg_cnt1;
|
||||
|
||||
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)
|
||||
|
||||
+5
-4
@@ -122,15 +122,16 @@ int generate_captures(const Position& pos, MoveStack* mlist) {
|
||||
Bitboard target = pos.pieces_of_color(opposite_color(us));
|
||||
MoveStack* mlist_start = mlist;
|
||||
|
||||
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
|
||||
if (us == WHITE)
|
||||
mlist = generate_pawn_captures<WHITE>(pos, mlist);
|
||||
else
|
||||
mlist = generate_pawn_captures<BLACK>(pos, mlist);
|
||||
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KING>(pos, mlist, us, target);
|
||||
return int(mlist - mlist_start);
|
||||
}
|
||||
|
||||
+92
-38
@@ -26,6 +26,7 @@
|
||||
#include <cassert>
|
||||
|
||||
#include "history.h"
|
||||
#include "evaluate.h"
|
||||
#include "movegen.h"
|
||||
#include "movepick.h"
|
||||
#include "search.h"
|
||||
@@ -44,7 +45,9 @@ namespace {
|
||||
int MainSearchPhaseIndex;
|
||||
int EvasionsPhaseIndex;
|
||||
int QsearchWithChecksPhaseIndex;
|
||||
int QsearchNoCapturesPhaseIndex;
|
||||
int QsearchWithoutChecksPhaseIndex;
|
||||
int NoMovesPhaseIndex;
|
||||
|
||||
}
|
||||
|
||||
@@ -62,28 +65,38 @@ namespace {
|
||||
/// search captures, promotions and some checks) and about how important good
|
||||
/// move ordering is at the current node.
|
||||
|
||||
MovePicker::MovePicker(const Position& p, bool pvnode, Move ttm, Move mk,
|
||||
Move k1, Move k2, Depth d) : pos(p) {
|
||||
pvNode = pvnode;
|
||||
MovePicker::MovePicker(const Position& p, bool pv, Move ttm,
|
||||
const SearchStack& ss, Depth d, EvalInfo* ei) : pos(p) {
|
||||
pvNode = pv;
|
||||
ttMove = ttm;
|
||||
mateKiller = (mk == ttm)? MOVE_NONE : mk;
|
||||
killer1 = k1;
|
||||
killer2 = k2;
|
||||
mateKiller = (ss.mateKiller == ttm)? MOVE_NONE : ss.mateKiller;
|
||||
killer1 = ss.killers[0];
|
||||
killer2 = ss.killers[1];
|
||||
depth = d;
|
||||
movesPicked = 0;
|
||||
numOfMoves = 0;
|
||||
numOfBadCaptures = 0;
|
||||
dc = p.discovered_check_candidates(p.side_to_move());
|
||||
|
||||
// With EvalInfo we are able to know how many captures are possible before
|
||||
// generating them. So avoid generating in case we know are zero.
|
||||
Color us = pos.side_to_move();
|
||||
Color them = opposite_color(us);
|
||||
bool noCaptures = ei
|
||||
&& (ei->attackedBy[us][0] & pos.pieces_of_color(them)) == 0
|
||||
&& !ei->mi->specialized_eval_exists()
|
||||
&& (pos.ep_square() == SQ_NONE)
|
||||
&& !pos.has_pawn_on_7th(us);
|
||||
|
||||
if (p.is_check())
|
||||
phaseIndex = EvasionsPhaseIndex;
|
||||
phaseIndex = EvasionsPhaseIndex;
|
||||
else if (depth > Depth(0))
|
||||
phaseIndex = MainSearchPhaseIndex;
|
||||
phaseIndex = MainSearchPhaseIndex;
|
||||
else if (depth == Depth(0))
|
||||
phaseIndex = QsearchWithChecksPhaseIndex;
|
||||
phaseIndex = (noCaptures ? QsearchNoCapturesPhaseIndex : QsearchWithChecksPhaseIndex);
|
||||
else
|
||||
phaseIndex = QsearchWithoutChecksPhaseIndex;
|
||||
phaseIndex = (noCaptures ? NoMovesPhaseIndex : QsearchWithoutChecksPhaseIndex);
|
||||
|
||||
dc = p.discovered_check_candidates(us);
|
||||
pinned = p.pinned_pieces(p.side_to_move());
|
||||
|
||||
finished = false;
|
||||
@@ -211,6 +224,8 @@ void MovePicker::score_captures() {
|
||||
// where it is possible to recapture with the hanging piece). Exchanging
|
||||
// big pieces before capturing a hanging piece probably helps to reduce
|
||||
// the subtree size.
|
||||
// While scoring captures it moves all captures with negative SEE values
|
||||
// to the badCaptures[] array.
|
||||
Move m;
|
||||
int seeValue;
|
||||
|
||||
@@ -225,8 +240,15 @@ void MovePicker::score_captures() {
|
||||
else
|
||||
moves[i].score = int(pos.midgame_value_of_piece_on(move_to(m)))
|
||||
-int(pos.type_of_piece_on(move_from(m)));
|
||||
} else
|
||||
}
|
||||
else
|
||||
{
|
||||
// Losing capture, move it to the badCaptures[] array
|
||||
assert(numOfBadCaptures < 63);
|
||||
moves[i].score = seeValue;
|
||||
badCaptures[numOfBadCaptures++] = moves[i];
|
||||
moves[i--] = moves[--numOfMoves];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,10 +270,11 @@ void MovePicker::score_noncaptures() {
|
||||
else
|
||||
hs = H.move_ordering_score(pos.piece_on(move_from(m)), m);
|
||||
|
||||
// Ensure moves in history are always sorted as first
|
||||
// Ensure history is always preferred to pst
|
||||
if (hs > 0)
|
||||
hs += 1000;
|
||||
|
||||
// pst based scoring
|
||||
moves[i].score = hs + pos.mg_pst_delta(m);
|
||||
}
|
||||
}
|
||||
@@ -270,7 +293,6 @@ void MovePicker::score_evasions() {
|
||||
} else
|
||||
moves[i].score = H.move_ordering_score(pos.piece_on(move_from(m)), m);
|
||||
}
|
||||
// FIXME try psqt also here
|
||||
}
|
||||
|
||||
void MovePicker::score_qcaptures() {
|
||||
@@ -288,8 +310,11 @@ void MovePicker::score_qcaptures() {
|
||||
}
|
||||
|
||||
|
||||
/// find_best_index() loops across the moves and returns index of
|
||||
/// the highest scored one.
|
||||
/// find_best_index() loops across the moves and returns index of
|
||||
/// the highest scored one. There is also a second version that
|
||||
/// lowers the priority of moves that attack the same square,
|
||||
/// so that if the best move that attack a square fails the next
|
||||
/// move picked attacks a different square if any, not the same one.
|
||||
|
||||
int MovePicker::find_best_index() {
|
||||
|
||||
@@ -304,14 +329,48 @@ int MovePicker::find_best_index() {
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
int MovePicker::find_best_index(Bitboard* squares, int values[]) {
|
||||
|
||||
int hs;
|
||||
Move m;
|
||||
Square to;
|
||||
int bestScore = -10000000, bestIndex = -1;
|
||||
|
||||
for (int i = movesPicked; i < numOfMoves; i++)
|
||||
{
|
||||
m = moves[i].move;
|
||||
to = move_to(m);
|
||||
|
||||
if (!bit_is_set(*squares, to))
|
||||
{
|
||||
// Init at first use
|
||||
set_bit(squares, to);
|
||||
values[to] = 0;
|
||||
}
|
||||
|
||||
hs = moves[i].score - values[to];
|
||||
if (hs > bestScore)
|
||||
{
|
||||
bestIndex = i;
|
||||
bestScore = hs;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestIndex != -1)
|
||||
{
|
||||
// Raise value of the picked square, so next attack
|
||||
// to the same square will get low priority.
|
||||
to = move_to(moves[bestIndex].move);
|
||||
values[to] += 0xB00;
|
||||
}
|
||||
return bestIndex;
|
||||
}
|
||||
|
||||
|
||||
/// MovePicker::pick_move_from_list() picks the move with the biggest score
|
||||
/// from a list of generated moves (moves[] or badCaptures[], depending on
|
||||
/// the current move generation phase). It takes care not to return the
|
||||
/// transposition table move if that has already been serched previously.
|
||||
/// While picking captures in the PH_GOOD_CAPTURES phase (i.e. while picking
|
||||
/// non-losing captures in the main search), it moves all captures with
|
||||
/// negative SEE values to the badCaptures[] array.
|
||||
|
||||
Move MovePicker::pick_move_from_list() {
|
||||
|
||||
@@ -325,23 +384,8 @@ Move MovePicker::pick_move_from_list() {
|
||||
|
||||
while (movesPicked < numOfMoves)
|
||||
{
|
||||
int bestScore = -10000000;
|
||||
bestIndex = -1;
|
||||
for (int i = movesPicked; i < numOfMoves; i++)
|
||||
{
|
||||
if (moves[i].score < 0)
|
||||
{
|
||||
// Losing capture, move it to the badCaptures[] array
|
||||
assert(numOfBadCaptures < 63);
|
||||
badCaptures[numOfBadCaptures++] = moves[i];
|
||||
moves[i--] = moves[--numOfMoves];
|
||||
}
|
||||
else if (moves[i].score > bestScore)
|
||||
{
|
||||
bestIndex = i;
|
||||
bestScore = moves[i].score;
|
||||
}
|
||||
}
|
||||
bestIndex = find_best_index();
|
||||
|
||||
if (bestIndex != -1) // Found a good capture
|
||||
{
|
||||
move = moves[bestIndex].move;
|
||||
@@ -461,8 +505,9 @@ MovePicker::MovegenPhase MovePicker::current_move_type() const {
|
||||
|
||||
/// MovePicker::init_phase_table() initializes the PhaseTable[],
|
||||
/// MainSearchPhaseIndex, EvasionPhaseIndex, QsearchWithChecksPhaseIndex
|
||||
/// and QsearchWithoutChecksPhaseIndex variables. It is only called once
|
||||
/// during program startup, and never again while the program is running.
|
||||
/// QsearchNoCapturesPhaseIndex, QsearchWithoutChecksPhaseIndex and
|
||||
/// NoMovesPhaseIndex variables. It is only called once during program
|
||||
/// startup, and never again while the program is running.
|
||||
|
||||
void MovePicker::init_phase_table() {
|
||||
|
||||
@@ -491,8 +536,17 @@ void MovePicker::init_phase_table() {
|
||||
PhaseTable[i++] = PH_QCHECKS;
|
||||
PhaseTable[i++] = PH_STOP;
|
||||
|
||||
// Quiescence search with checks only and no captures
|
||||
QsearchNoCapturesPhaseIndex = i - 1;
|
||||
PhaseTable[i++] = PH_QCHECKS;
|
||||
PhaseTable[i++] = PH_STOP;
|
||||
|
||||
// Quiescence search without checks
|
||||
QsearchWithoutChecksPhaseIndex = i - 1;
|
||||
PhaseTable[i++] = PH_QCAPTURES;
|
||||
PhaseTable[i++] = PH_STOP;
|
||||
|
||||
// Do not generate any move
|
||||
NoMovesPhaseIndex = i - 1;
|
||||
PhaseTable[i++] = PH_STOP;
|
||||
}
|
||||
|
||||
+5
-1
@@ -34,6 +34,9 @@
|
||||
//// Types
|
||||
////
|
||||
|
||||
struct EvalInfo;
|
||||
struct SearchStack;
|
||||
|
||||
/// MovePicker is a class which is used to pick one legal move at a time from
|
||||
/// the current position. It is initialized with a Position object and a few
|
||||
/// moves we have reason to believe are good. The most important method is
|
||||
@@ -60,7 +63,7 @@ public:
|
||||
PH_STOP
|
||||
};
|
||||
|
||||
MovePicker(const Position& p, bool pvnode, Move ttm, Move mk, Move k1, Move k2, Depth d);
|
||||
MovePicker(const Position& p, bool pvnode, Move ttm, const SearchStack& ss, Depth d, EvalInfo* ei = NULL);
|
||||
Move get_next_move();
|
||||
Move get_next_move(Lock &lock);
|
||||
int number_of_moves() const;
|
||||
@@ -77,6 +80,7 @@ private:
|
||||
void score_qcaptures();
|
||||
Move pick_move_from_list();
|
||||
int find_best_index();
|
||||
int find_best_index(Bitboard* squares, int values[]);
|
||||
|
||||
const Position& pos;
|
||||
Move ttMove, mateKiller, killer1, killer2;
|
||||
|
||||
+198
-201
@@ -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;
|
||||
}
|
||||
|
||||
+4
-4
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ const SquareDelta PawnPush[2] = {
|
||||
|
||||
static const char PieceChars[] = " pnbrqk";
|
||||
|
||||
char piece_type_to_char(PieceType pt, bool upcase = false) {
|
||||
char piece_type_to_char(PieceType pt, bool upcase) {
|
||||
return upcase? toupper(PieceChars[pt]) : PieceChars[pt];
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -124,7 +124,7 @@ inline SquareDelta pawn_push(Color c) {
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern char piece_type_to_char(PieceType pt, bool upcase);
|
||||
extern char piece_type_to_char(PieceType pt, bool upcase = false);
|
||||
extern PieceType piece_type_from_char(char c);
|
||||
extern bool piece_is_ok(Piece pc);
|
||||
extern bool piece_type_is_ok(PieceType pt);
|
||||
|
||||
+105
-40
@@ -31,6 +31,7 @@
|
||||
#include "movepick.h"
|
||||
#include "position.h"
|
||||
#include "psqtab.h"
|
||||
#include "san.h"
|
||||
#include "ucioption.h"
|
||||
|
||||
|
||||
@@ -38,6 +39,8 @@
|
||||
//// Variables
|
||||
////
|
||||
|
||||
extern SearchStack EmptySearchStack;
|
||||
|
||||
int Position::castleRightsMask[64];
|
||||
|
||||
Key Position::zobrist[2][8][64];
|
||||
@@ -49,6 +52,7 @@ Key Position::zobSideToMove;
|
||||
Value Position::MgPieceSquareTable[16][64];
|
||||
Value Position::EgPieceSquareTable[16][64];
|
||||
|
||||
static bool RequestPending = false;
|
||||
|
||||
////
|
||||
//// Functions
|
||||
@@ -242,7 +246,7 @@ const std::string Position::to_fen() const {
|
||||
|
||||
fen += (rank > RANK_1 ? '/' : ' ');
|
||||
}
|
||||
fen += (sideToMove == WHITE ? 'w' : 'b') + ' ';
|
||||
fen += (sideToMove == WHITE ? "w " : "b ");
|
||||
if (castleRights != NO_CASTLES)
|
||||
{
|
||||
if (can_castle_kingside(WHITE)) fen += 'K';
|
||||
@@ -263,29 +267,45 @@ const std::string Position::to_fen() const {
|
||||
|
||||
|
||||
/// Position::print() prints an ASCII representation of the position to
|
||||
/// the standard output.
|
||||
/// the standard output. If a move is given then also the san is print.
|
||||
|
||||
void Position::print() const {
|
||||
char pieceStrings[][8] =
|
||||
{"| ? ", "| P ", "| N ", "| B ", "| R ", "| Q ", "| K ", "| ? ",
|
||||
"| ? ", "|=P=", "|=N=", "|=B=", "|=R=", "|=Q=", "|=K="
|
||||
};
|
||||
void Position::print(Move m) const {
|
||||
|
||||
for(Rank rank = RANK_8; rank >= RANK_1; rank--) {
|
||||
std::cout << "+---+---+---+---+---+---+---+---+\n";
|
||||
for(File file = FILE_A; file <= FILE_H; file++) {
|
||||
Square sq = make_square(file, rank);
|
||||
Piece piece = piece_on(sq);
|
||||
if(piece == EMPTY)
|
||||
std::cout << ((square_color(sq) == WHITE)? "| " : "| . ");
|
||||
else
|
||||
std::cout << pieceStrings[piece];
|
||||
}
|
||||
std::cout << "|\n";
|
||||
static const std::string pieceLetters = " PNBRQK PNBRQK .";
|
||||
|
||||
// Check for reentrancy, as example when called from inside
|
||||
// MovePicker that is used also here in move_to_san()
|
||||
if (RequestPending)
|
||||
return;
|
||||
|
||||
RequestPending = true;
|
||||
|
||||
std::cout << std::endl;
|
||||
if (m != MOVE_NONE)
|
||||
{
|
||||
std::string col = (color_of_piece_on(move_from(m)) == BLACK ? ".." : "");
|
||||
std::cout << "Move is: " << col << move_to_san(*this, m) << std::endl;
|
||||
}
|
||||
std::cout << "+---+---+---+---+---+---+---+---+\n";
|
||||
std::cout << to_fen() << std::endl;
|
||||
std::cout << key << std::endl;
|
||||
for (Rank rank = RANK_8; rank >= RANK_1; rank--)
|
||||
{
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl;
|
||||
for (File file = FILE_A; file <= FILE_H; file++)
|
||||
{
|
||||
Square sq = make_square(file, rank);
|
||||
Piece piece = piece_on(sq);
|
||||
if (piece == EMPTY && square_color(sq) == WHITE)
|
||||
piece = NO_PIECE;
|
||||
|
||||
char col = (color_of_piece_on(sq) == BLACK ? '=' : ' ');
|
||||
std::cout << '|' << col << pieceLetters[piece] << col;
|
||||
}
|
||||
std::cout << '|' << std::endl;
|
||||
}
|
||||
std::cout << "+---+---+---+---+---+---+---+---+" << std::endl
|
||||
<< "Fen is: " << to_fen() << std::endl
|
||||
<< "Key is: " << key << std::endl;
|
||||
|
||||
RequestPending = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -655,10 +675,12 @@ bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
|
||||
|
||||
|
||||
/// Position::move_is_capture() tests whether a move from the current
|
||||
/// position is a capture.
|
||||
/// position is a capture. Move must not be MOVE_NONE.
|
||||
|
||||
bool Position::move_is_capture(Move m) const {
|
||||
|
||||
assert(m != MOVE_NONE);
|
||||
|
||||
return ( !square_is_empty(move_to(m))
|
||||
&& (color_of_piece_on(move_to(m)) == opposite_color(side_to_move()))
|
||||
)
|
||||
@@ -1560,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 {
|
||||
|
||||
@@ -1573,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
|
||||
@@ -1594,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];
|
||||
|
||||
@@ -1905,8 +1971,7 @@ bool Position::is_mate() {
|
||||
|
||||
if (is_check())
|
||||
{
|
||||
MovePicker mp = MovePicker(*this, false, MOVE_NONE, MOVE_NONE,
|
||||
MOVE_NONE, MOVE_NONE, Depth(0));
|
||||
MovePicker mp = MovePicker(*this, false, MOVE_NONE, EmptySearchStack, Depth(0));
|
||||
return mp.get_next_move() == MOVE_NONE;
|
||||
}
|
||||
return false;
|
||||
|
||||
+4
-3
@@ -128,7 +128,7 @@ public:
|
||||
// Text input/output
|
||||
void from_fen(const std::string &fen);
|
||||
const std::string to_fen() const;
|
||||
void print() const;
|
||||
void print(Move m = MOVE_NONE) const;
|
||||
|
||||
// Copying
|
||||
void copy(const Position &pos);
|
||||
@@ -174,13 +174,13 @@ public:
|
||||
// Number of pieces of each color and type
|
||||
int piece_count(Color c, PieceType pt) const;
|
||||
|
||||
// The en passant square:
|
||||
// The en passant square
|
||||
Square ep_square() const;
|
||||
|
||||
// Current king position for each color
|
||||
Square king_square(Color c) const;
|
||||
|
||||
// Castling rights.
|
||||
// Castling rights
|
||||
bool can_castle_kingside(Color c) const;
|
||||
bool can_castle_queenside(Color c) const;
|
||||
bool can_castle(Color c) const;
|
||||
@@ -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;
|
||||
|
||||
+257
-245
@@ -31,6 +31,7 @@
|
||||
#include "movepick.h"
|
||||
#include "san.h"
|
||||
|
||||
extern SearchStack EmptySearchStack;
|
||||
|
||||
////
|
||||
//// Local definitions
|
||||
@@ -50,7 +51,7 @@ namespace {
|
||||
|
||||
/// Functions
|
||||
|
||||
Ambiguity move_ambiguity(Position &pos, Move m);
|
||||
Ambiguity move_ambiguity(const Position& pos, Move m);
|
||||
const std::string time_string(int milliseconds);
|
||||
const std::string score_string(Value v);
|
||||
}
|
||||
@@ -61,206 +62,222 @@ namespace {
|
||||
////
|
||||
|
||||
/// move_to_san() takes a position and a move as input, where it is assumed
|
||||
/// that the move is a legal move from the position. The return value is
|
||||
/// that the move is a legal move from the position. The return value is
|
||||
/// a string containing the move in short algebraic notation.
|
||||
|
||||
const std::string move_to_san(Position &pos, Move m) {
|
||||
std::string str;
|
||||
const std::string move_to_san(const Position& pos, Move m) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(move_is_ok(m));
|
||||
|
||||
if(m == MOVE_NONE) {
|
||||
str = "(none)";
|
||||
return str;
|
||||
}
|
||||
else if(m == MOVE_NULL) {
|
||||
str = "(null)";
|
||||
return str;
|
||||
}
|
||||
else if(move_is_long_castle(m))
|
||||
str = "O-O-O";
|
||||
else if(move_is_short_castle(m))
|
||||
str = "O-O";
|
||||
else {
|
||||
Square from, to;
|
||||
Piece pc;
|
||||
std::string san = "";
|
||||
|
||||
from = move_from(m);
|
||||
to = move_to(m);
|
||||
pc = pos.piece_on(move_from(m));
|
||||
|
||||
str = "";
|
||||
|
||||
if(type_of_piece(pc) == PAWN) {
|
||||
if(pos.move_is_capture(m))
|
||||
str += file_to_char(square_file(move_from(m)));
|
||||
}
|
||||
else {
|
||||
str += piece_type_to_char(type_of_piece(pc), true);
|
||||
|
||||
Ambiguity amb = move_ambiguity(pos, m);
|
||||
switch(amb) {
|
||||
|
||||
case AMBIGUITY_NONE:
|
||||
break;
|
||||
|
||||
case AMBIGUITY_FILE:
|
||||
str += file_to_char(square_file(from));
|
||||
break;
|
||||
|
||||
case AMBIGUITY_RANK:
|
||||
str += rank_to_char(square_rank(from));
|
||||
break;
|
||||
|
||||
case AMBIGUITY_BOTH:
|
||||
str += square_to_string(from);
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
if (m == MOVE_NONE)
|
||||
return "(none)";
|
||||
else if (m == MOVE_NULL)
|
||||
return "(null)";
|
||||
else if (move_is_long_castle(m))
|
||||
san = "O-O-O";
|
||||
else if (move_is_short_castle(m))
|
||||
san = "O-O";
|
||||
else
|
||||
{
|
||||
Piece pc = pos.piece_on(move_from(m));
|
||||
if (type_of_piece(pc) != PAWN)
|
||||
{
|
||||
san += piece_type_to_char(type_of_piece(pc), true);
|
||||
Square from = move_from(m);
|
||||
switch (move_ambiguity(pos, m)) {
|
||||
case AMBIGUITY_NONE:
|
||||
break;
|
||||
case AMBIGUITY_FILE:
|
||||
san += file_to_char(square_file(from));
|
||||
break;
|
||||
case AMBIGUITY_RANK:
|
||||
san += rank_to_char(square_rank(from));
|
||||
break;
|
||||
case AMBIGUITY_BOTH:
|
||||
san += square_to_string(from);
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
if (pos.move_is_capture(m))
|
||||
{
|
||||
if (type_of_piece(pc) == PAWN)
|
||||
san += file_to_char(square_file(move_from(m)));
|
||||
san += "x";
|
||||
}
|
||||
san += square_to_string(move_to(m));
|
||||
if (move_promotion(m))
|
||||
{
|
||||
san += '=';
|
||||
san += piece_type_to_char(move_promotion(m), true);
|
||||
}
|
||||
}
|
||||
|
||||
if(pos.move_is_capture(m))
|
||||
str += "x";
|
||||
|
||||
str += square_to_string(move_to(m));
|
||||
|
||||
if(move_promotion(m)) {
|
||||
str += "=";
|
||||
str += piece_type_to_char(move_promotion(m), true);
|
||||
}
|
||||
}
|
||||
|
||||
// Is the move check? We don't use pos.move_is_check(m) here, because
|
||||
// Position::move_is_check doesn't detect all checks (not castling moves,
|
||||
// promotions and en passant captures).
|
||||
UndoInfo u;
|
||||
pos.do_move(m, u);
|
||||
if(pos.is_check())
|
||||
str += pos.is_mate()? "#" : "+";
|
||||
pos.undo_move(m, u);
|
||||
Position p(pos);
|
||||
p.do_move(m, u);
|
||||
if (p.is_check())
|
||||
san += p.is_mate()? "#" : "+";
|
||||
|
||||
return str;
|
||||
return san;
|
||||
}
|
||||
|
||||
|
||||
/// move_from_san() takes a position and a string as input, and tries to
|
||||
/// interpret the string as a move in short algebraic notation. On success,
|
||||
/// interpret the string as a move in short algebraic notation. On success,
|
||||
/// the move is returned. On failure (i.e. if the string is unparsable, or
|
||||
/// if the move is illegal or ambiguous), MOVE_NONE is returned.
|
||||
|
||||
Move move_from_san(Position &pos, const std::string &movestr) {
|
||||
Move move_from_san(const Position& pos, const std::string& movestr) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
|
||||
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
|
||||
MOVE_NONE, OnePly);
|
||||
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
|
||||
|
||||
// Castling moves
|
||||
if(movestr == "O-O-O") {
|
||||
Move m;
|
||||
while((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if(move_is_long_castle(m) && pos.pl_move_is_legal(m))
|
||||
return m;
|
||||
return MOVE_NONE;
|
||||
}
|
||||
else if(movestr == "O-O") {
|
||||
Move m;
|
||||
while((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if(move_is_short_castle(m) && pos.pl_move_is_legal(m))
|
||||
return m;
|
||||
return MOVE_NONE;
|
||||
}
|
||||
if (movestr == "O-O-O" || movestr == "O-O-O+")
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_long_castle(m) && pos.pl_move_is_legal(m))
|
||||
return m;
|
||||
|
||||
// Normal moves
|
||||
const char *cstr = movestr.c_str();
|
||||
const char *c;
|
||||
char *cc;
|
||||
char str[10];
|
||||
int i;
|
||||
|
||||
// Initialize str[] by making a copy of movestr with the characters
|
||||
// 'x', '=', '+' and '#' removed.
|
||||
cc = str;
|
||||
for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++)
|
||||
if(!strchr("x=+#", *c)) {
|
||||
*cc = strchr("nrq", *c)? toupper(*c) : *c;
|
||||
cc++;
|
||||
}
|
||||
*cc = '\0';
|
||||
|
||||
size_t left = 0, right = strlen(str) - 1;
|
||||
PieceType pt = NO_PIECE_TYPE, promotion;
|
||||
Square to;
|
||||
File fromFile = FILE_NONE;
|
||||
Rank fromRank = RANK_NONE;
|
||||
|
||||
// Promotion?
|
||||
if(strchr("BNRQ", str[right])) {
|
||||
promotion = piece_type_from_char(str[right]);
|
||||
right--;
|
||||
}
|
||||
else
|
||||
promotion = NO_PIECE_TYPE;
|
||||
|
||||
// Find the moving piece:
|
||||
if(left < right) {
|
||||
if(strchr("BNRQK", str[left])) {
|
||||
pt = piece_type_from_char(str[left]);
|
||||
left++;
|
||||
}
|
||||
else
|
||||
pt = PAWN;
|
||||
}
|
||||
|
||||
// Find the to square:
|
||||
if(left < right) {
|
||||
if(str[right] < '1' || str[right] > '8' ||
|
||||
str[right-1] < 'a' || str[right-1] > 'h')
|
||||
return MOVE_NONE;
|
||||
to = make_square(file_from_char(str[right-1]), rank_from_char(str[right]));
|
||||
right -= 2;
|
||||
}
|
||||
else
|
||||
else if (movestr == "O-O" || movestr == "O-O+")
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_short_castle(m) && pos.pl_move_is_legal(m))
|
||||
return m;
|
||||
|
||||
return MOVE_NONE;
|
||||
|
||||
// Find the file and/or rank of the from square:
|
||||
if(left <= right) {
|
||||
if(strchr("abcdefgh", str[left])) {
|
||||
fromFile = file_from_char(str[left]);
|
||||
left++;
|
||||
}
|
||||
if(strchr("12345678", str[left]))
|
||||
fromRank = rank_from_char(str[left]);
|
||||
}
|
||||
|
||||
// Look for a matching move:
|
||||
// Normal moves. We use a simple FSM to parse the san string.
|
||||
enum { START, TO_FILE, TO_RANK, PROMOTION_OR_CHECK, PROMOTION, CHECK, END };
|
||||
static const std::string pieceLetters = "KQRBN";
|
||||
PieceType pt = NO_PIECE_TYPE, promotion = NO_PIECE_TYPE;
|
||||
File fromFile = FILE_NONE, toFile = FILE_NONE;
|
||||
Rank fromRank = RANK_NONE, toRank = RANK_NONE;
|
||||
Square to;
|
||||
int state = START;
|
||||
|
||||
for (size_t i = 0; i < movestr.length(); i++)
|
||||
{
|
||||
char type, c = movestr[i];
|
||||
if (pieceLetters.find(c) != std::string::npos)
|
||||
type = 'P';
|
||||
else if (c >= 'a' && c <= 'h')
|
||||
type = 'F';
|
||||
else if (c >= '1' && c <= '8')
|
||||
type = 'R';
|
||||
else
|
||||
type = c;
|
||||
|
||||
switch (type) {
|
||||
case 'P':
|
||||
if (state == START)
|
||||
{
|
||||
pt = piece_type_from_char(c);
|
||||
state = TO_FILE;
|
||||
}
|
||||
else if (state == PROMOTION)
|
||||
{
|
||||
promotion = piece_type_from_char(c);
|
||||
state = (i < movestr.length() - 1) ? CHECK : END;
|
||||
}
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case 'F':
|
||||
if (state == START)
|
||||
{
|
||||
pt = PAWN;
|
||||
fromFile = toFile = file_from_char(c);
|
||||
state = TO_RANK;
|
||||
}
|
||||
else if (state == TO_FILE)
|
||||
{
|
||||
toFile = file_from_char(c);
|
||||
state = TO_RANK;
|
||||
}
|
||||
else if (state == TO_RANK && toFile != FILE_NONE)
|
||||
{
|
||||
// Previous file was for disambiguation
|
||||
fromFile = toFile;
|
||||
toFile = file_from_char(c);
|
||||
}
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case 'R':
|
||||
if (state == TO_RANK)
|
||||
{
|
||||
toRank = rank_from_char(c);
|
||||
state = (i < movestr.length() - 1) ? PROMOTION_OR_CHECK : END;
|
||||
}
|
||||
else if (state == TO_FILE && fromRank == RANK_NONE)
|
||||
{
|
||||
// It's a disambiguation rank instead of a file
|
||||
fromRank = rank_from_char(c);
|
||||
}
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case 'x': case 'X':
|
||||
if (state == TO_RANK)
|
||||
{
|
||||
// Previous file was for disambiguation, or it's a pawn capture
|
||||
fromFile = toFile;
|
||||
state = TO_FILE;
|
||||
}
|
||||
else if (state != TO_FILE)
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case '=':
|
||||
if (state == PROMOTION_OR_CHECK)
|
||||
state = PROMOTION;
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
case '+': case '#':
|
||||
if (state == PROMOTION_OR_CHECK || state == CHECK)
|
||||
state = END;
|
||||
else
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
default:
|
||||
return MOVE_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (state != END)
|
||||
return MOVE_NONE;
|
||||
|
||||
// Look for a matching move
|
||||
Move m, move = MOVE_NONE;
|
||||
to = make_square(toFile, toRank);
|
||||
int matches = 0;
|
||||
|
||||
while((m = mp.get_next_move()) != MOVE_NONE) {
|
||||
bool match = true;
|
||||
if(pos.type_of_piece_on(move_from(m)) != pt)
|
||||
match = false;
|
||||
else if(move_to(m) != to)
|
||||
match = false;
|
||||
else if(move_promotion(m) != promotion)
|
||||
match = false;
|
||||
else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m)))
|
||||
match = false;
|
||||
else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m)))
|
||||
match = false;
|
||||
if(match) {
|
||||
move = m;
|
||||
matches++;
|
||||
}
|
||||
}
|
||||
|
||||
if(matches == 1)
|
||||
return move;
|
||||
else
|
||||
return MOVE_NONE;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if ( pos.type_of_piece_on(move_from(m)) == pt
|
||||
&& move_to(m) == to
|
||||
&& move_promotion(m) == promotion
|
||||
&& (fromFile == FILE_NONE || fromFile == square_file(move_from(m)))
|
||||
&& (fromRank == RANK_NONE || fromRank == square_rank(move_from(m))))
|
||||
{
|
||||
move = m;
|
||||
matches++;
|
||||
}
|
||||
return (matches == 1 ? move : MOVE_NONE);
|
||||
}
|
||||
|
||||
|
||||
@@ -271,34 +288,31 @@ Move move_from_san(Position &pos, const std::string &movestr) {
|
||||
/// length of 80 characters. After a line break, 'startColumn' spaces are
|
||||
/// inserted at the beginning of the new line.
|
||||
|
||||
const std::string line_to_san(const Position &pos, Move line[], int startColumn,
|
||||
bool breakLines) {
|
||||
Position p = Position(pos);
|
||||
const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines) {
|
||||
|
||||
UndoInfo u;
|
||||
std::stringstream s;
|
||||
std::string moveStr;
|
||||
size_t length, maxLength;
|
||||
size_t length = 0;
|
||||
size_t maxLength = 80 - startColumn;
|
||||
Position p(pos);
|
||||
|
||||
length = 0;
|
||||
maxLength = 80 - startColumn;
|
||||
for (int i = 0; line[i] != MOVE_NONE; i++)
|
||||
{
|
||||
moveStr = move_to_san(p, line[i]);
|
||||
length += moveStr.length() + 1;
|
||||
if (breakLines && length > maxLength)
|
||||
{
|
||||
s << '\n' << std::setw(startColumn) << ' ';
|
||||
length = moveStr.length() + 1;
|
||||
}
|
||||
s << moveStr << ' ';
|
||||
|
||||
for(int i = 0; line[i] != MOVE_NONE; i++) {
|
||||
moveStr = move_to_san(p, line[i]);
|
||||
length += moveStr.length() + 1;
|
||||
if(breakLines && length > maxLength) {
|
||||
s << "\n";
|
||||
for(int j = 0; j < startColumn; j++)
|
||||
s << " ";
|
||||
length = moveStr.length() + 1;
|
||||
}
|
||||
s << moveStr << " ";
|
||||
|
||||
if(line[i] == MOVE_NULL)
|
||||
p.do_null_move(u);
|
||||
else
|
||||
p.do_move(line[i], u);
|
||||
if (line[i] == MOVE_NULL)
|
||||
p.do_null_move(u);
|
||||
else
|
||||
p.do_move(line[i], u);
|
||||
}
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
@@ -307,26 +321,26 @@ const std::string line_to_san(const Position &pos, Move line[], int startColumn,
|
||||
/// It is used to write search information to the log file (which is created
|
||||
/// when the UCI parameter "Use Search Log" is "true").
|
||||
|
||||
const std::string pretty_pv(const Position &pos, int time, int depth,
|
||||
const std::string pretty_pv(const Position& pos, int time, int depth,
|
||||
uint64_t nodes, Value score, Move pv[]) {
|
||||
std::stringstream s;
|
||||
|
||||
// Depth
|
||||
s << std::setw(2) << std::setfill(' ') << depth << " ";
|
||||
s << std::setw(2) << depth << " ";
|
||||
|
||||
// Score
|
||||
s << std::setw(8) << score_string(score);
|
||||
|
||||
// Time
|
||||
s << std::setw(8) << std::setfill(' ') << time_string(time) << " ";
|
||||
s << std::setw(8) << time_string(time) << " ";
|
||||
|
||||
// Nodes
|
||||
if(nodes < 1000000ULL)
|
||||
s << std::setw(8) << std::setfill(' ') << nodes << " ";
|
||||
else if(nodes < 1000000000ULL)
|
||||
s << std::setw(7) << std::setfill(' ') << nodes/1000ULL << 'k' << " ";
|
||||
if (nodes < 1000000ULL)
|
||||
s << std::setw(8) << nodes << " ";
|
||||
else if (nodes < 1000000000ULL)
|
||||
s << std::setw(7) << nodes/1000ULL << 'k' << " ";
|
||||
else
|
||||
s << std::setw(7) << std::setfill(' ') << nodes/1000000ULL << 'M' << " ";
|
||||
s << std::setw(7) << nodes/1000000ULL << 'M' << " ";
|
||||
|
||||
// PV
|
||||
s << line_to_san(pos, pv, 30, true);
|
||||
@@ -337,82 +351,80 @@ const std::string pretty_pv(const Position &pos, int time, int depth,
|
||||
|
||||
namespace {
|
||||
|
||||
Ambiguity move_ambiguity(Position &pos, Move m) {
|
||||
Square from, to;
|
||||
Piece pc;
|
||||
Ambiguity move_ambiguity(const Position& pos, Move m) {
|
||||
|
||||
from = move_from(m);
|
||||
to = move_to(m);
|
||||
pc = pos.piece_on(from);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Piece pc = pos.piece_on(from);
|
||||
|
||||
// King moves are never ambiguous, because there is never two kings of
|
||||
// the same color.
|
||||
if(type_of_piece(pc) == KING)
|
||||
return AMBIGUITY_NONE;
|
||||
if (type_of_piece(pc) == KING)
|
||||
return AMBIGUITY_NONE;
|
||||
|
||||
MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE,
|
||||
MOVE_NONE, OnePly);
|
||||
MovePicker mp = MovePicker(pos, false, MOVE_NONE, EmptySearchStack, OnePly);
|
||||
Move mv, moveList[8];
|
||||
int i, j, n;
|
||||
|
||||
n = 0;
|
||||
while((mv = mp.get_next_move()) != MOVE_NONE)
|
||||
if(move_to(mv) == to && pos.piece_on(move_from(mv)) == pc
|
||||
&& pos.pl_move_is_legal(mv))
|
||||
moveList[n++] = mv;
|
||||
if(n == 1)
|
||||
return AMBIGUITY_NONE;
|
||||
int n = 0;
|
||||
while ((mv = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv))
|
||||
moveList[n++] = mv;
|
||||
|
||||
j = 0;
|
||||
for(i = 0; i < n; i++)
|
||||
if(square_file(move_from(moveList[i])) == square_file(from))
|
||||
j++;
|
||||
if(j == 1)
|
||||
return AMBIGUITY_FILE;
|
||||
if (n == 1)
|
||||
return AMBIGUITY_NONE;
|
||||
|
||||
j = 0;
|
||||
for(i = 0; i < n; i++)
|
||||
if(square_rank(move_from(moveList[i])) == square_rank(from))
|
||||
j++;
|
||||
if(j == 1)
|
||||
return AMBIGUITY_RANK;
|
||||
int f = 0, r = 0;
|
||||
for (int i = 0; i < n; i++)
|
||||
{
|
||||
if (square_file(move_from(moveList[i])) == square_file(from))
|
||||
f++;
|
||||
|
||||
if (square_rank(move_from(moveList[i])) == square_rank(from))
|
||||
r++;
|
||||
}
|
||||
if (f == 1)
|
||||
return AMBIGUITY_FILE;
|
||||
|
||||
if (r == 1)
|
||||
return AMBIGUITY_RANK;
|
||||
|
||||
return AMBIGUITY_BOTH;
|
||||
}
|
||||
|
||||
|
||||
const std::string time_string(int milliseconds) {
|
||||
|
||||
std::stringstream s;
|
||||
s << std::setfill('0');
|
||||
|
||||
int hours = milliseconds / (1000 * 60 * 60);
|
||||
int minutes = (milliseconds - hours*1000*60*60) / (60*1000);
|
||||
int seconds = (milliseconds - hours*1000*60*60 - minutes*60*1000) / 1000;
|
||||
int hours = milliseconds / (1000*60*60);
|
||||
int minutes = (milliseconds - hours*1000*60*60) / (1000*60);
|
||||
int seconds = (milliseconds - hours*1000*60*60 - minutes*1000*60) / 1000;
|
||||
|
||||
if(hours)
|
||||
s << hours << ':';
|
||||
s << std::setw(2) << std::setfill('0') << minutes << ':';
|
||||
s << std::setw(2) << std::setfill('0') << seconds;
|
||||
if (hours)
|
||||
s << hours << ':';
|
||||
|
||||
s << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
|
||||
return s.str();
|
||||
}
|
||||
|
||||
|
||||
const std::string score_string(Value v) {
|
||||
|
||||
std::stringstream s;
|
||||
|
||||
if(abs(v) >= VALUE_MATE - 200) {
|
||||
if(v < 0)
|
||||
s << "-#" << (VALUE_MATE + v) / 2;
|
||||
else
|
||||
if (v >= VALUE_MATE - 200)
|
||||
s << "#" << (VALUE_MATE - v + 1) / 2;
|
||||
}
|
||||
else {
|
||||
float floatScore = float(v) / float(PawnValueMidgame);
|
||||
if(v >= 0)
|
||||
s << '+';
|
||||
s << std::setprecision(2) << std::fixed << floatScore;
|
||||
else if(v <= -VALUE_MATE + 200)
|
||||
s << "-#" << (VALUE_MATE + v) / 2;
|
||||
else
|
||||
{
|
||||
float floatScore = float(v) / float(PawnValueMidgame);
|
||||
if (v >= 0)
|
||||
s << '+';
|
||||
|
||||
s << std::setprecision(2) << std::fixed << floatScore;
|
||||
}
|
||||
return s.str();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,12 +36,9 @@
|
||||
//// Prototypes
|
||||
////
|
||||
|
||||
extern const std::string move_to_san(Position &pos, Move m);
|
||||
extern Move move_from_san(Position &pos, const std::string &str);
|
||||
extern const std::string line_to_san(const Position &pos, Move line[],
|
||||
int startColumn, bool breakLines);
|
||||
extern const std::string pretty_pv(const Position &pos, int time, int depth,
|
||||
uint64_t nodes, Value score, Move pv[]);
|
||||
|
||||
extern const std::string move_to_san(const Position& pos, Move m);
|
||||
extern Move move_from_san(const Position& pos, const std::string& str);
|
||||
extern const std::string line_to_san(const Position& pos, Move line[], int startColumn, bool breakLines);
|
||||
extern const std::string pretty_pv(const Position& pos, int time, int depth, uint64_t nodes, Value score, Move pv[]);
|
||||
|
||||
#endif // !defined(SAN_H_INCLUDED)
|
||||
|
||||
+372
-173
File diff suppressed because it is too large
Load Diff
+4
-4
@@ -25,7 +25,6 @@
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include "types.h"
|
||||
#include "depth.h"
|
||||
#include "history.h"
|
||||
#include "lock.h"
|
||||
@@ -41,6 +40,7 @@
|
||||
|
||||
const int PLY_MAX = 100;
|
||||
const int PLY_MAX_PLUS_2 = 102;
|
||||
const int KILLER_MAX = 2;
|
||||
|
||||
|
||||
////
|
||||
@@ -56,8 +56,9 @@ struct SearchStack {
|
||||
Move pv[PLY_MAX];
|
||||
Move currentMove;
|
||||
Value currentMoveCaptureValue;
|
||||
Move mateKiller, killer1, killer2;
|
||||
Move mateKiller;
|
||||
Move threatMove;
|
||||
Move killers[KILLER_MAX];
|
||||
Depth reduction;
|
||||
};
|
||||
|
||||
@@ -66,10 +67,9 @@ struct SearchStack {
|
||||
//// Global variables
|
||||
////
|
||||
|
||||
extern SearchStack EmptySearchStack;
|
||||
extern TranspositionTable TT;
|
||||
|
||||
extern int ActiveThreads;
|
||||
|
||||
extern Lock SMPLock;
|
||||
|
||||
// Perhaps better to make H local, and pass as parameter to MovePicker?
|
||||
|
||||
+11
-15
@@ -108,29 +108,25 @@ void TranspositionTable::store(const Position &pos, Value v, Depth d,
|
||||
TTEntry *tte, *replace;
|
||||
|
||||
tte = replace = first_entry(pos);
|
||||
for (int i = 0; i < 4; i++)
|
||||
for (int i = 0; i < 4; i++, tte++)
|
||||
{
|
||||
if (!(tte+i)->key()) // still empty
|
||||
{
|
||||
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
|
||||
writes++;
|
||||
return;
|
||||
}
|
||||
if ((tte+i)->key() == pos.get_key()) // overwrite old
|
||||
if (!tte->key() || tte->key() == pos.get_key()) // empty or overwrite old
|
||||
{
|
||||
if (m == MOVE_NONE)
|
||||
m = (tte+i)->move();
|
||||
m = tte->move();
|
||||
|
||||
*(tte+i) = TTEntry(pos.get_key(), v, type, d, m, generation);
|
||||
*tte = TTEntry(pos.get_key(), v, type, d, m, generation);
|
||||
return;
|
||||
}
|
||||
if ( i == 0 // already is (replace == tte+i), common case
|
||||
|| replace->generation() < (tte+i)->generation())
|
||||
else if (i == 0) // replace would be a no-op in this common case
|
||||
continue;
|
||||
|
||||
if ( replace->generation() > (tte+i)->generation()
|
||||
|| (tte+i)->depth() < replace->depth())
|
||||
replace = tte+i;
|
||||
int c1 = (replace->generation() == generation ? 2 : 0);
|
||||
int c2 = (tte->generation() == generation ? -2 : 0);
|
||||
int c3 = (tte->depth() < replace->depth() ? 1 : 0);
|
||||
|
||||
if (c1 + c2 + c3 > 0)
|
||||
replace = tte;
|
||||
}
|
||||
*replace = TTEntry(pos.get_key(), v, type, d, m, generation);
|
||||
writes++;
|
||||
|
||||
+9
-7
@@ -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,11 +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", 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));
|
||||
@@ -176,14 +176,16 @@ namespace {
|
||||
template<typename T>
|
||||
T get_option_value(const std::string& optionName) {
|
||||
|
||||
T ret;
|
||||
T ret = T();
|
||||
Options::iterator it = option_with_name(optionName);
|
||||
|
||||
if (it != options.end())
|
||||
{
|
||||
std::istringstream ss(it->currentValue);
|
||||
ss >> ret;
|
||||
}
|
||||
} else
|
||||
assert(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user