DroidFish: Updated stockfish engine to version 2.2.

This commit is contained in:
Peter Osterlund
2012-01-01 00:52:19 +00:00
parent d8782830a9
commit e00df7370c
44 changed files with 4187 additions and 5191 deletions

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,16 +21,19 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include "misc.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
using namespace Search;
static const string Defaults[] = { static const char* Defaults[] = {
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -", "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -", "8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19", "4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14", "rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 14",
"r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14", "r1bq1r1k/1pp1n1pp/1p1p4/4p2Q/4Pp2/1BNP4/PPP2PPP/3R1RK1 w - - 2 14",
@@ -43,30 +46,24 @@ static const string Defaults[] = {
"3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22", "3r1rk1/p5pp/bpp1pp2/8/q1PP1P2/b3P3/P2NQRPP/1R2B1K1 b - - 6 22",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18", "r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22", "4k2r/1pb2ppp/1p2p3/1R1p4/3P4/2r1PN2/P4PPP/1R4K1 b - - 3 22",
"3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26", "3q2k1/pb3p1p/4pbp1/2r5/PpN2N2/1P2P2P/5PP1/Q2R2K1 b - - 4 26"
""
}; };
/// benchmark() runs a simple benchmark by letting Stockfish analyze a set /// benchmark() runs a simple benchmark by letting Stockfish analyze a set
/// of positions for a given limit each. There are five parameters; the /// of positions for a given limit each. There are five parameters; the
/// transposition table size, the number of search threads that should /// transposition table size, the number of search threads that should
/// be used, the limit value spent for each position (optional, default /// be used, the limit value spent for each position (optional, default is
/// is ply 12), an optional file name where to look for positions in fen /// depth 12), an optional file name where to look for positions in fen
/// format (default are the BenchmarkPositions defined above) and the type /// format (defaults are the positions defined above) and the type of the
/// of the limit value: depth (default), time in secs or number of nodes. /// limit value: depth (default), time in secs or number of nodes.
/// The analysis is written to a file named bench.txt.
void benchmark(int argc, char* argv[]) { void benchmark(int argc, char* argv[]) {
vector<string> fenList; vector<string> fens;
SearchLimits limits; LimitsType limits;
int64_t totalNodes;
int time; int time;
int64_t nodes = 0;
// Load default positions
for (int i = 0; !Defaults[i].empty(); i++)
fenList.push_back(Defaults[i]);
// Assign default values to missing arguments // Assign default values to missing arguments
string ttSize = argc > 2 ? argv[2] : "128"; string ttSize = argc > 2 ? argv[2] : "128";
@@ -75,79 +72,64 @@ void benchmark(int argc, char* argv[]) {
string fenFile = argc > 5 ? argv[5] : "default"; string fenFile = argc > 5 ? argv[5] : "default";
string valType = argc > 6 ? argv[6] : "depth"; string valType = argc > 6 ? argv[6] : "depth";
Options["Hash"].set_value(ttSize); Options["Hash"] = ttSize;
Options["Threads"].set_value(threads); Options["Threads"] = threads;
Options["OwnBook"].set_value("false"); Options["OwnBook"] = false;
// Search should be limited by nodes, time or depth ? if (valType == "time")
if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else if (valType == "time")
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else else
limits.maxDepth = atoi(valStr.c_str()); limits.maxDepth = atoi(valStr.c_str());
// Do we need to load positions from a given FEN file ?
if (fenFile != "default") if (fenFile != "default")
{ {
string fen; string fen;
ifstream f(fenFile.c_str()); ifstream file(fenFile.c_str());
if (f.is_open()) if (!file.is_open())
{ {
fenList.clear(); cerr << "Unable to open file " << fenFile << endl;
while (getline(f, fen))
if (!fen.empty())
fenList.push_back(fen);
f.close();
}
else
{
cerr << "Unable to open FEN file " << fenFile << endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
while (getline(file, fen))
if (!fen.empty())
fens.push_back(fen);
file.close();
} }
else
fens.assign(Defaults, Defaults + 16);
// Ok, let's start the benchmark ! time = system_time();
totalNodes = 0;
time = get_system_time();
for (size_t i = 0; i < fenList.size(); i++) for (size_t i = 0; i < fens.size(); i++)
{ {
Move moves[] = { MOVE_NONE }; Position pos(fens[i], false, 0);
Position pos(fenList[i], false, 0);
cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl; cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (valType == "perft") if (valType == "perft")
{ {
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY); int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
totalNodes += cnt; cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl;
nodes += cnt;
cerr << "\nPerft " << limits.maxDepth << " nodes counted: " << cnt << endl;
} }
else else
{ {
if (!think(pos, limits, moves)) Threads.start_thinking(pos, limits, vector<Move>(), false);
break; nodes += RootPosition.nodes_searched();
totalNodes += pos.nodes_searched();
} }
} }
time = get_system_time() - time; time = system_time() - time;
cerr << "\n===============================" cerr << "\n==========================="
<< "\nTotal time (ms) : " << time << "\nTotal time (ms) : " << time
<< "\nNodes searched : " << totalNodes << "\nNodes searched : " << nodes
<< "\nNodes/second : " << (int)(totalNodes / (time / 1000.0)) << endl << endl; << "\nNodes/second : " << int(nodes / (time / 1000.0)) << endl;
// MS Visual C++ debug window always unconditionally closes when program
// exits, this is bad because we want to read results before.
#if (defined(WINDOWS) || defined(WIN32) || defined(WIN64))
cerr << "Press any key to exit" << endl;
cin >> time;
#endif
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -28,31 +28,31 @@ namespace {
RESULT_UNKNOWN, RESULT_UNKNOWN,
RESULT_INVALID, RESULT_INVALID,
RESULT_WIN, RESULT_WIN,
RESULT_LOSS,
RESULT_DRAW RESULT_DRAW
}; };
struct KPKPosition { struct KPKPosition {
Result classify_knowns(int index);
Result classify(int index, Result db[]);
private:
void from_index(int index); void from_index(int index);
bool is_legal() const; Result classify_white(const Result db[]);
bool is_immediate_draw() const; Result classify_black(const Result db[]);
bool is_immediate_win() const; Bitboard wk_attacks() const { return StepAttacksBB[W_KING][whiteKingSquare]; }
Bitboard wk_attacks() const { return StepAttacksBB[WK][whiteKingSquare]; } Bitboard bk_attacks() const { return StepAttacksBB[B_KING][blackKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[BK][blackKingSquare]; } Bitboard pawn_attacks() const { return StepAttacksBB[W_PAWN][pawnSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[WP][pawnSquare]; }
Square whiteKingSquare, blackKingSquare, pawnSquare; Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove; Color sideToMove;
}; };
// The possible pawns squares are 24, the first 4 files and ranks from 2 to 7 // The possible pawns squares are 24, the first 4 files and ranks from 2 to 7
const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq const int IndexMax = 2 * 24 * 64 * 64; // color * wp_sq * wk_sq * bk_sq = 196608
// Each uint32_t stores results of 32 positions, one per bit // Each uint32_t stores results of 32 positions, one per bit
uint32_t KPKBitbase[IndexMax / 32]; uint32_t KPKBitbase[IndexMax / 32];
Result classify_wtm(const KPKPosition& pos, const Result bb[]);
Result classify_btm(const KPKPosition& pos, const Result bb[]);
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm); int compute_index(Square wksq, Square bksq, Square wpsq, Color stm);
} }
@@ -65,64 +65,49 @@ uint32_t probe_kpk_bitbase(Square wksq, Square wpsq, Square bksq, Color stm) {
} }
void init_kpk_bitbase() { void kpk_bitbase_init() {
Result bb[IndexMax]; Result db[IndexMax];
KPKPosition pos; KPKPosition pos;
bool repeat; int index, bit, repeat = 1;
// Initialize table // Initialize table
for (int i = 0; i < IndexMax; i++) for (index = 0; index < IndexMax; index++)
{ db[index] = pos.classify_knowns(index);
pos.from_index(i);
bb[i] = !pos.is_legal() ? RESULT_INVALID
: pos.is_immediate_draw() ? RESULT_DRAW
: pos.is_immediate_win() ? RESULT_WIN : RESULT_UNKNOWN;
}
// Iterate until all positions are classified (30 cycles needed) // Iterate until all positions are classified (30 cycles needed)
do { while (repeat)
repeat = false; for (repeat = index = 0; index < IndexMax; index++)
if ( db[index] == RESULT_UNKNOWN
for (int i = 0; i < IndexMax; i++) && pos.classify(index, db) != RESULT_UNKNOWN)
if (bb[i] == RESULT_UNKNOWN) repeat = 1;
{
pos.from_index(i);
bb[i] = (pos.sideToMove == WHITE) ? classify_wtm(pos, bb)
: classify_btm(pos, bb);
if (bb[i] != RESULT_UNKNOWN)
repeat = true;
}
} while (repeat);
// Map 32 position results into one KPKBitbase[] entry // Map 32 position results into one KPKBitbase[] entry
for (int i = 0; i < IndexMax / 32; i++) for (index = 0; index < IndexMax / 32; index++)
for (int j = 0; j < 32; j++) for (bit = 0; bit < 32; bit++)
if (bb[32 * i + j] == RESULT_WIN || bb[32 * i + j] == RESULT_LOSS) if (db[32 * index + bit] == RESULT_WIN)
KPKBitbase[i] |= (1 << j); KPKBitbase[index] |= (1 << bit);
} }
namespace { namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range // A KPK bitbase index is an integer in [0, IndexMax] range
// //
// Information is mapped in this way // Information is mapped in this way
// //
// bit 0: side to move (WHITE or BLACK) // bit 0: side to move (WHITE or BLACK)
// bit 1- 6: black king square (from SQ_A1 to SQ_H8) // bit 1- 6: black king square (from SQ_A1 to SQ_H8)
// bit 7-12: white king square (from SQ_A1 to SQ_H8) // bit 7-12: white king square (from SQ_A1 to SQ_H8)
// bit 13-14: white pawn file (from FILE_A to FILE_D) // bit 13-14: white pawn file (from FILE_A to FILE_D)
// bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1) // bit 15-17: white pawn rank - 1 (from RANK_2 - 1 to RANK_7 - 1)
int compute_index(Square wksq, Square bksq, Square wpsq, Color stm) { int compute_index(Square wksq, Square bksq, Square wpsq, Color stm) {
assert(square_file(wpsq) <= FILE_D); assert(file_of(wpsq) <= FILE_D);
int p = int(square_file(wpsq)) + 4 * int(square_rank(wpsq) - 1); int p = file_of(wpsq) + 4 * (rank_of(wpsq) - 1);
int r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p; int r = stm + 2 * bksq + 128 * wksq + 8192 * p;
assert(r >= 0 && r < IndexMax); assert(r >= 0 && r < IndexMax);
@@ -131,73 +116,79 @@ namespace {
void KPKPosition::from_index(int index) { void KPKPosition::from_index(int index) {
int s = (index / 8192) % 24; int s = index >> 13;
sideToMove = Color(index & 1);
sideToMove = Color(index % 2); blackKingSquare = Square((index >> 1) & 63);
blackKingSquare = Square((index / 2) % 64); whiteKingSquare = Square((index >> 7) & 63);
whiteKingSquare = Square((index / 128) % 64); pawnSquare = make_square(File(s & 3), Rank((s >> 2) + 1));
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
} }
bool KPKPosition::is_legal() const { Result KPKPosition::classify_knowns(int index) {
from_index(index);
// Check if two pieces are on the same square
if ( whiteKingSquare == pawnSquare if ( whiteKingSquare == pawnSquare
|| whiteKingSquare == blackKingSquare || whiteKingSquare == blackKingSquare
|| blackKingSquare == pawnSquare) || blackKingSquare == pawnSquare)
return false; return RESULT_INVALID;
if (sideToMove == WHITE) // Check if a king can be captured
{ if ( bit_is_set(wk_attacks(), blackKingSquare)
if ( bit_is_set(wk_attacks(), blackKingSquare) || (bit_is_set(pawn_attacks(), blackKingSquare) && sideToMove == WHITE))
|| bit_is_set(pawn_attacks(), blackKingSquare)) return RESULT_INVALID;
return false;
}
else if (bit_is_set(bk_attacks(), whiteKingSquare))
return false;
return true;
}
bool KPKPosition::is_immediate_draw() const {
if (sideToMove == BLACK)
{
Bitboard wka = wk_attacks();
Bitboard bka = bk_attacks();
// Case 1: Stalemate
if ((bka & ~(wka | pawn_attacks())) == EmptyBoardBB)
return true;
// Case 2: King can capture pawn
if (bit_is_set(bka, pawnSquare) && !bit_is_set(wka, pawnSquare))
return true;
}
else
{
// Case 1: Stalemate (possible pawn files are only from A to D)
if ( whiteKingSquare == SQ_A8
&& pawnSquare == SQ_A7
&& (blackKingSquare == SQ_C7 || blackKingSquare == SQ_C8))
return true;
}
return false;
}
bool KPKPosition::is_immediate_win() const {
// The position is an immediate win if it is white to move and the // The position is an immediate win if it is white to move and the
// white pawn can be promoted without getting captured. // white pawn can be promoted without getting captured.
return sideToMove == WHITE if ( rank_of(pawnSquare) == RANK_7
&& square_rank(pawnSquare) == RANK_7 && sideToMove == WHITE
&& whiteKingSquare != pawnSquare + DELTA_N && whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1 && ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N)); || bit_is_set(wk_attacks(), pawnSquare + DELTA_N)))
return RESULT_WIN;
// Check for known draw positions
//
// Case 1: Stalemate
if ( sideToMove == BLACK
&& !(bk_attacks() & ~(wk_attacks() | pawn_attacks())))
return RESULT_DRAW;
// Case 2: King can capture pawn
if ( sideToMove == BLACK
&& bit_is_set(bk_attacks(), pawnSquare) && !bit_is_set(wk_attacks(), pawnSquare))
return RESULT_DRAW;
// Case 3: Black king in front of white pawn
if ( blackKingSquare == pawnSquare + DELTA_N
&& rank_of(pawnSquare) < RANK_7)
return RESULT_DRAW;
// Case 4: White king in front of pawn and black has opposition
if ( whiteKingSquare == pawnSquare + DELTA_N
&& blackKingSquare == pawnSquare + DELTA_N + DELTA_N + DELTA_N
&& rank_of(pawnSquare) < RANK_5
&& sideToMove == WHITE)
return RESULT_DRAW;
// Case 5: Stalemate with rook pawn
if ( blackKingSquare == SQ_A8
&& file_of(pawnSquare) == FILE_A)
return RESULT_DRAW;
return RESULT_UNKNOWN;
} }
Result classify_wtm(const KPKPosition& pos, const Result bb[]) { Result KPKPosition::classify(int index, Result db[]) {
// If one move leads to a position classified as RESULT_LOSS, the result from_index(index);
db[index] = (sideToMove == WHITE ? classify_white(db) : classify_black(db));
return db[index];
}
Result KPKPosition::classify_white(const Result db[]) {
// If one move leads to a position classified as RESULT_WIN, the result
// of the current position is RESULT_WIN. If all moves lead to positions // of the current position is RESULT_WIN. If all moves lead to positions
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW // classified as RESULT_DRAW, the current position is classified RESULT_DRAW
// otherwise the current position is classified as RESULT_UNKNOWN. // otherwise the current position is classified as RESULT_UNKNOWN.
@@ -208,13 +199,13 @@ namespace {
Result r; Result r;
// King moves // King moves
b = pos.wk_attacks(); b = wk_attacks();
while (b) while (b)
{ {
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
r = bb[compute_index(s, pos.blackKingSquare, pos.pawnSquare, BLACK)]; r = db[compute_index(s, blackKingSquare, pawnSquare, BLACK)];
if (r == RESULT_LOSS) if (r == RESULT_WIN)
return RESULT_WIN; return RESULT_WIN;
if (r == RESULT_UNKNOWN) if (r == RESULT_UNKNOWN)
@@ -222,26 +213,24 @@ namespace {
} }
// Pawn moves // Pawn moves
if (square_rank(pos.pawnSquare) < RANK_7) if (rank_of(pawnSquare) < RANK_7)
{ {
s = pos.pawnSquare + DELTA_N; s = pawnSquare + DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)]; r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS) if (r == RESULT_WIN)
return RESULT_WIN; return RESULT_WIN;
if (r == RESULT_UNKNOWN) if (r == RESULT_UNKNOWN)
unknownFound = true; unknownFound = true;
// Double pawn push // Double pawn push
if ( square_rank(s) == RANK_3 if (rank_of(s) == RANK_3 && r != RESULT_INVALID)
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
{ {
s += DELTA_N; s += DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)]; r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS) if (r == RESULT_WIN)
return RESULT_WIN; return RESULT_WIN;
if (r == RESULT_UNKNOWN) if (r == RESULT_UNKNOWN)
@@ -251,14 +240,12 @@ namespace {
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW; return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
} }
Result KPKPosition::classify_black(const Result db[]) {
Result classify_btm(const KPKPosition& pos, const Result bb[]) {
// If one move leads to a position classified as RESULT_DRAW, the result // If one move leads to a position classified as RESULT_DRAW, the result
// of the current position is RESULT_DRAW. If all moves lead to positions // of the current position is RESULT_DRAW. If all moves lead to positions
// classified as RESULT_WIN, the current position is classified as // classified as RESULT_WIN, the position is classified as RESULT_WIN.
// RESULT_LOSS. Otherwise, the current position is classified as // Otherwise, the current position is classified as RESULT_UNKNOWN.
// RESULT_UNKNOWN.
bool unknownFound = false; bool unknownFound = false;
Bitboard b; Bitboard b;
@@ -266,11 +253,11 @@ namespace {
Result r; Result r;
// King moves // King moves
b = pos.bk_attacks(); b = bk_attacks();
while (b) while (b)
{ {
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
r = bb[compute_index(pos.whiteKingSquare, s, pos.pawnSquare, WHITE)]; r = db[compute_index(whiteKingSquare, s, pawnSquare, WHITE)];
if (r == RESULT_DRAW) if (r == RESULT_DRAW)
return RESULT_DRAW; return RESULT_DRAW;
@@ -278,7 +265,7 @@ namespace {
if (r == RESULT_UNKNOWN) if (r == RESULT_UNKNOWN)
unknownFound = true; unknownFound = true;
} }
return unknownFound ? RESULT_UNKNOWN : RESULT_LOSS; return unknownFound ? RESULT_UNKNOWN : RESULT_WIN;
} }
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,159 +17,27 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cstring>
#include <iostream> #include <iostream>
#include "bitboard.h" #include "bitboard.h"
#include "bitcount.h" #include "bitcount.h"
#include "rkiss.h"
#if defined(IS_64BIT) Bitboard RMasks[64];
Bitboard RMagics[64];
Bitboard* RAttacks[64];
int RShifts[64];
const uint64_t BMult[64] = { Bitboard BMasks[64];
0x0440049104032280ULL, 0x1021023C82008040ULL, 0x0404040082000048ULL, Bitboard BMagics[64];
0x48C4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL, Bitboard* BAttacks[64];
0x0181011002E06040ULL, 0x9101004104200E00ULL, 0x1240848848310401ULL, int BShifts[64];
0x2000142828050024ULL, 0x00001004024D5000ULL, 0x0102044400800200ULL,
0x8108108820112000ULL, 0xA880818210C00046ULL, 0x4008008801082000ULL,
0x0060882404049400ULL, 0x0104402004240810ULL, 0x000A002084250200ULL,
0x00100B0880801100ULL, 0x0004080201220101ULL, 0x0044008080A00000ULL,
0x0000202200842000ULL, 0x5006004882D00808ULL, 0x0000200045080802ULL,
0x0086100020200601ULL, 0xA802080A20112C02ULL, 0x0080411218080900ULL,
0x000200A0880080A0ULL, 0x9A01010000104000ULL, 0x0028008003100080ULL,
0x0211021004480417ULL, 0x0401004188220806ULL, 0x00825051400C2006ULL,
0x00140C0210943000ULL, 0x0000242800300080ULL, 0x00C2208120080200ULL,
0x2430008200002200ULL, 0x1010100112008040ULL, 0x8141050100020842ULL,
0x0000822081014405ULL, 0x800C049E40400804ULL, 0x4A0404028A000820ULL,
0x0022060201041200ULL, 0x0360904200840801ULL, 0x0881A08208800400ULL,
0x0060202C00400420ULL, 0x1204440086061400ULL, 0x0008184042804040ULL,
0x0064040315300400ULL, 0x0C01008801090A00ULL, 0x0808010401140C00ULL,
0x04004830C2020040ULL, 0x0080005002020054ULL, 0x40000C14481A0490ULL,
0x0010500101042048ULL, 0x1010100200424000ULL, 0x0000640901901040ULL,
0x00000A0201014840ULL, 0x00840082AA011002ULL, 0x010010840084240AULL,
0x0420400810420608ULL, 0x8D40230408102100ULL, 0x4A00200612222409ULL,
0x0A08520292120600ULL
};
const uint64_t RMult[64] = {
0x0A8002C000108020ULL, 0x4440200140003000ULL, 0x8080200010011880ULL,
0x0380180080141000ULL, 0x1A00060008211044ULL, 0x410001000A0C0008ULL,
0x9500060004008100ULL, 0x0100024284A20700ULL, 0x0000802140008000ULL,
0x0080C01002A00840ULL, 0x0402004282011020ULL, 0x9862000820420050ULL,
0x0001001448011100ULL, 0x6432800200800400ULL, 0x040100010002000CULL,
0x0002800D0010C080ULL, 0x90C0008000803042ULL, 0x4010004000200041ULL,
0x0003010010200040ULL, 0x0A40828028001000ULL, 0x0123010008000430ULL,
0x0024008004020080ULL, 0x0060040001104802ULL, 0x00582200028400D1ULL,
0x4000802080044000ULL, 0x0408208200420308ULL, 0x0610038080102000ULL,
0x3601000900100020ULL, 0x0000080080040180ULL, 0x00C2020080040080ULL,
0x0080084400100102ULL, 0x4022408200014401ULL, 0x0040052040800082ULL,
0x0B08200280804000ULL, 0x008A80A008801000ULL, 0x4000480080801000ULL,
0x0911808800801401ULL, 0x822A003002001894ULL, 0x401068091400108AULL,
0x000004A10A00004CULL, 0x2000800640008024ULL, 0x1486408102020020ULL,
0x000100A000D50041ULL, 0x00810050020B0020ULL, 0x0204000800808004ULL,
0x00020048100A000CULL, 0x0112000831020004ULL, 0x0009000040810002ULL,
0x0440490200208200ULL, 0x8910401000200040ULL, 0x6404200050008480ULL,
0x4B824A2010010100ULL, 0x04080801810C0080ULL, 0x00000400802A0080ULL,
0x8224080110026400ULL, 0x40002C4104088200ULL, 0x01002100104A0282ULL,
0x1208400811048021ULL, 0x3201014A40D02001ULL, 0x0005100019200501ULL,
0x0101000208001005ULL, 0x0002008450080702ULL, 0x001002080301D00CULL,
0x410201CE5C030092ULL
};
const int BShift[64] = {
58, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 59, 59,
59, 59, 57, 57, 57, 57, 59, 59, 59, 59, 57, 55, 55, 57, 59, 59,
59, 59, 57, 55, 55, 57, 59, 59, 59, 59, 57, 57, 57, 57, 59, 59,
59, 59, 59, 59, 59, 59, 59, 59, 58, 59, 59, 59, 59, 59, 59, 58
};
const int RShift[64] = {
52, 53, 53, 53, 53, 53, 53, 52, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 53, 54, 54, 54, 54, 54, 54, 53,
53, 54, 54, 54, 54, 54, 54, 53, 52, 53, 53, 53, 53, 53, 53, 52
};
#else // if !defined(IS_64BIT)
const uint64_t BMult[64] = {
0x54142844C6A22981ULL, 0x710358A6EA25C19EULL, 0x704F746D63A4A8DCULL,
0xBFED1A0B80F838C5ULL, 0x90561D5631E62110ULL, 0x2804260376E60944ULL,
0x84A656409AA76871ULL, 0xF0267F64C28B6197ULL, 0x70764EBB762F0585ULL,
0x92AA09E0CFE161DEULL, 0x41EE1F6BB266F60EULL, 0xDDCBF04F6039C444ULL,
0x5A3FAB7BAC0D988AULL, 0xD3727877FA4EAA03ULL, 0xD988402D868DDAAEULL,
0x812B291AFA075C7CULL, 0x94FAF987B685A932ULL, 0x3ED867D8470D08DBULL,
0x92517660B8901DE8ULL, 0x2D97E43E058814B4ULL, 0x880A10C220B25582ULL,
0xC7C6520D1F1A0477ULL, 0xDBFC7FBCD7656AA6ULL, 0x78B1B9BFB1A2B84FULL,
0x2F20037F112A0BC1ULL, 0x657171EA2269A916ULL, 0xC08302B07142210EULL,
0x0880A4403064080BULL, 0x3602420842208C00ULL, 0x852800DC7E0B6602ULL,
0x595A3FBBAA0F03B2ULL, 0x9F01411558159D5EULL, 0x2B4A4A5F88B394F2ULL,
0x4AFCBFFC292DD03AULL, 0x4A4094A3B3F10522ULL, 0xB06F00B491F30048ULL,
0xD5B3820280D77004ULL, 0x8B2E01E7C8E57A75ULL, 0x2D342794E886C2E6ULL,
0xC302C410CDE21461ULL, 0x111F426F1379C274ULL, 0xE0569220ABB31588ULL,
0x5026D3064D453324ULL, 0xE2076040C343CD8AULL, 0x93EFD1E1738021EEULL,
0xB680804BED143132ULL, 0x44E361B21986944CULL, 0x44C60170EF5C598CULL,
0xF4DA475C195C9C94ULL, 0xA3AFBB5F72060B1DULL, 0xBC75F410E41C4FFCULL,
0xB51C099390520922ULL, 0x902C011F8F8EC368ULL, 0x950B56B3D6F5490AULL,
0x3909E0635BF202D0ULL, 0x5744F90206EC10CCULL, 0xDC59FD76317ABBC1ULL,
0x881C7C67FCBFC4F6ULL, 0x47CA41E7E440D423ULL, 0xEB0C88112048D004ULL,
0x51C60E04359AEF1AULL, 0x1AA1FE0E957A5554ULL, 0xDD9448DB4F5E3104ULL,
0xDC01F6DCA4BEBBDCULL,
};
const uint64_t RMult[64] = {
0xD7445CDEC88002C0ULL, 0xD0A505C1F2001722ULL, 0xE065D1C896002182ULL,
0x9A8C41E75A000892ULL, 0x8900B10C89002AA8ULL, 0x9B28D1C1D60005A2ULL,
0x015D6C88DE002D9AULL, 0xB1DBFC802E8016A9ULL, 0x149A1042D9D60029ULL,
0xB9C08050599E002FULL, 0x132208C3AF300403ULL, 0xC1000CE2E9C50070ULL,
0x9D9AA13C99020012ULL, 0xB6B078DAF71E0046ULL, 0x9D880182FB6E002EULL,
0x52889F467E850037ULL, 0xDA6DC008D19A8480ULL, 0x468286034F902420ULL,
0x7140AC09DC54C020ULL, 0xD76FFFFA39548808ULL, 0xEA901C4141500808ULL,
0xC91004093F953A02ULL, 0x02882AFA8F6BB402ULL, 0xAEBE335692442C01ULL,
0x0E904A22079FB91EULL, 0x13A514851055F606ULL, 0x76C782018C8FE632ULL,
0x1DC012A9D116DA06ULL, 0x3C9E0037264FFFA6ULL, 0x2036002853C6E4A2ULL,
0xE3FE08500AFB47D4ULL, 0xF38AF25C86B025C2ULL, 0xC0800E2182CF9A40ULL,
0x72002480D1F60673ULL, 0x2500200BAE6E9B53ULL, 0xC60018C1EEFCA252ULL,
0x0600590473E3608AULL, 0x46002C4AB3FE51B2ULL, 0xA200011486BCC8D2ULL,
0xB680078095784C63ULL, 0x2742002639BF11AEULL, 0xC7D60021A5BDB142ULL,
0xC8C04016BB83D820ULL, 0xBD520028123B4842ULL, 0x9D1600344AC2A832ULL,
0x6A808005631C8A05ULL, 0x604600A148D5389AULL, 0xE2E40103D40DEA65ULL,
0x945B5A0087C62A81ULL, 0x012DC200CD82D28EULL, 0x2431C600B5F9EF76ULL,
0xFB142A006A9B314AULL, 0x06870E00A1C97D62ULL, 0x2A9DB2004A2689A2ULL,
0xD3594600CAF5D1A2ULL, 0xEE0E4900439344A7ULL, 0x89C4D266CA25007AULL,
0x3E0013A2743F97E3ULL, 0x0180E31A0431378AULL, 0x3A9E465A4D42A512ULL,
0x98D0A11A0C0D9CC2ULL, 0x8E711C1ABA19B01EULL, 0x8DCDC836DD201142ULL,
0x5AC08A4735370479ULL,
};
const int BShift[64] = {
26, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 27, 27,
27, 27, 25, 25, 25, 25, 27, 27, 27, 27, 25, 23, 23, 25, 27, 27,
27, 27, 25, 23, 23, 25, 27, 27, 27, 27, 25, 25, 25, 25, 27, 27,
27, 27, 27, 27, 27, 27, 27, 27, 26, 27, 27, 27, 27, 27, 27, 26
};
const int RShift[64] = {
20, 21, 21, 21, 21, 21, 21, 20, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 21, 22, 22, 22, 22, 22, 22, 21,
21, 22, 22, 22, 22, 22, 22, 21, 20, 21, 21, 21, 21, 21, 21, 20
};
#endif // defined(IS_64BIT)
// Global bitboards definitions with static storage duration are
// automatically set to zero before enter main().
Bitboard RMask[64];
int RAttackIndex[64];
Bitboard RAttacks[0x19000];
Bitboard BMask[64];
int BAttackIndex[64];
Bitboard BAttacks[0x1480];
Bitboard SetMaskBB[65]; Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65]; Bitboard ClearMaskBB[65];
Bitboard SquaresByColorBB[2];
Bitboard FileBB[8]; Bitboard FileBB[8];
Bitboard RankBB[8]; Bitboard RankBB[8];
Bitboard NeighboringFilesBB[8]; Bitboard NeighboringFilesBB[8];
@@ -186,19 +54,18 @@ Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64]; Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256]; uint8_t BitCount8Bit[256];
int SquareDistance[64][64];
namespace { namespace {
void init_masks(); CACHE_LINE_ALIGNMENT
void init_step_attacks();
void init_pseudo_attacks(); int BSFTable[64];
void init_between_bitboards(); Bitboard RookTable[0x19000]; // Storage space for rook attacks
Bitboard index_to_bitboard(int index, Bitboard mask); Bitboard BishopTable[0x1480]; // Storage space for bishop attacks
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax); void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[],
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[], Bitboard masks[], int shifts[]);
const int shift[], const Bitboard mult[], int deltas[][2]);
} }
@@ -211,7 +78,7 @@ void print_bitboard(Bitboard b) {
{ {
std::cout << "+---+---+---+---+---+---+---+---+" << '\n'; std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
for (File f = FILE_A; f <= FILE_H; f++) for (File f = FILE_A; f <= FILE_H; f++)
std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? 'X' : ' ') << ' '; std::cout << "| " << (bit_is_set(b, make_square(f, r)) ? "X " : " ");
std::cout << "|\n"; std::cout << "|\n";
} }
@@ -225,39 +92,22 @@ void print_bitboard(Bitboard b) {
#if defined(IS_64BIT) && !defined(USE_BSFQ) #if defined(IS_64BIT) && !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
0, 1, 2, 7, 3, 13, 8, 19, 4, 25, 14, 28, 9, 34, 20, 40, 5, 17, 26,
38, 15, 46, 29, 48, 10, 31, 35, 54, 21, 50, 41, 57, 63, 6, 12, 18, 24, 27,
33, 39, 16, 37, 45, 47, 30, 53, 49, 56, 62, 11, 23, 32, 36, 44, 52, 55, 61,
22, 43, 51, 60, 42, 59, 58
};
Square first_1(Bitboard b) { Square first_1(Bitboard b) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]); return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]);
} }
Square pop_1st_bit(Bitboard* b) { Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b; Bitboard bb = *b;
*b &= (*b - 1); *b &= (*b - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]); return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]);
} }
#elif !defined(USE_BSFQ) #elif !defined(USE_BSFQ)
static CACHE_LINE_ALIGNMENT
const int BitTable[64] = {
63, 30, 3, 32, 25, 41, 22, 33, 15, 50, 42, 13, 11, 53, 19, 34, 61, 29, 2,
51, 21, 43, 45, 10, 18, 47, 1, 54, 9, 57, 0, 35, 62, 31, 40, 4, 49, 5,
52, 26, 60, 6, 23, 44, 46, 27, 56, 16, 7, 39, 48, 24, 59, 14, 12, 55, 38,
28, 58, 20, 37, 17, 36, 8
};
Square first_1(Bitboard b) { Square first_1(Bitboard b) {
b ^= (b - 1); b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32); uint32_t fold = unsigned(b) ^ unsigned(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]); return Square(BSFTable[(fold * 0x783A9B23) >> 26]);
} }
// Use type-punning // Use type-punning
@@ -284,12 +134,12 @@ Square pop_1st_bit(Bitboard* bb) {
if (u.dw.l) if (u.dw.l)
{ {
ret = Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]); ret = Square(BSFTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783A9B23) >> 26]);
u.dw.l &= (u.dw.l - 1); u.dw.l &= (u.dw.l - 1);
*bb = u.b; *bb = u.b;
return ret; return ret;
} }
ret = Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]); ret = Square(BSFTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783A9B23) >> 26]);
u.dw.h &= (u.dw.h - 1); u.dw.h &= (u.dw.h - 1);
*bb = u.b; *bb = u.b;
return ret; return ret;
@@ -298,189 +148,219 @@ Square pop_1st_bit(Bitboard* bb) {
#endif // !defined(USE_BSFQ) #endif // !defined(USE_BSFQ)
/// init_bitboards() initializes various bitboard arrays. It is called during /// bitboards_init() initializes various bitboard arrays. It is called during
/// program initialization. /// program initialization.
void init_bitboards() { void bitboards_init() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}}; for (Bitboard b = 0; b < 256; b++)
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}}; BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
init_masks(); for (Square s = SQ_A1; s <= SQ_H8; s++)
init_step_attacks(); {
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas); SetMaskBB[s] = 1ULL << s;
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas); ClearMaskBB[s] = ~SetMaskBB[s];
init_pseudo_attacks(); }
init_between_bitboards();
ClearMaskBB[SQ_NONE] = ~0ULL;
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
}
for (int f = FILE_A; f <= FILE_H; f++)
{
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
}
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++)
{
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
}
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(file_of(s));
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(file_of(s));
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
SquareDistance[s1][s2] = std::max(file_distance(s1, s2), rank_distance(s1, s2));
for (int i = 0; i < 64; i++)
if (!Is64Bit) // Matt Taylor's folding trick for 32 bit systems
{
Bitboard b = 1ULL << i;
b ^= b - 1;
b ^= b >> 32;
BSFTable[uint32_t(b * 0x783A9B23) >> 26] = i;
}
else
BSFTable[((1ULL << i) * 0x218A392CD3D5DBFULL) >> 58] = i;
int steps[][9] = { {}, { 7, 9 }, { 17, 15, 10, 6, -6, -10, -15, -17 },
{}, {}, {}, { 9, 7, -7, -9, 8, 1, -1, -8 } };
for (Color c = WHITE; c <= BLACK; c++)
for (PieceType pt = PAWN; pt <= KING; pt++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (int k = 0; steps[pt][k]; k++)
{
Square to = s + Square(c == WHITE ? steps[pt][k] : -steps[pt][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[make_piece(c, pt)][s], to);
}
init_magic_bitboards(ROOK, RAttacks, RMagics, RMasks, RShifts);
init_magic_bitboards(BISHOP, BAttacks, BMagics, BMasks, BShifts);
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, 0);
RookPseudoAttacks[s] = rook_attacks_bb(s, 0);
QueenPseudoAttacks[s] = queen_attacks_bb(s, 0);
}
for (Square s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (Square s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
{
Square delta = (s2 - s1) / square_distance(s1, s2);
for (Square s = s1 + delta; s != s2; s += delta)
set_bit(&BetweenBB[s1][s2], s);
}
} }
namespace { namespace {
// All functions below are used to precompute various bitboards during Bitboard sliding_attacks(PieceType pt, Square sq, Bitboard occupied) {
// program initialization. Some of the functions may be difficult to
// understand, but they all seem to work correctly, and it should never
// be necessary to touch any of them.
void init_masks() { Square deltas[][4] = { { DELTA_N, DELTA_E, DELTA_S, DELTA_W },
{ DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW } };
SquaresByColorBB[DARK] = 0xAA55AA55AA55AA55ULL; Bitboard attacks = 0;
SquaresByColorBB[LIGHT] = ~SquaresByColorBB[DARK]; Square* delta = (pt == ROOK ? deltas[0] : deltas[1]);
FileBB[FILE_A] = FileABB;
RankBB[RANK_1] = Rank1BB;
for (int f = FILE_B; f <= FILE_H; f++)
{
FileBB[f] = FileBB[f - 1] << 1;
RankBB[f] = RankBB[f - 1] << 8;
}
for (int f = FILE_A; f <= FILE_H; f++)
{
NeighboringFilesBB[f] = (f > FILE_A ? FileBB[f - 1] : 0) | (f < FILE_H ? FileBB[f + 1] : 0);
ThisAndNeighboringFilesBB[f] = FileBB[f] | NeighboringFilesBB[f];
}
for (int rw = RANK_7, rb = RANK_2; rw >= RANK_1; rw--, rb++)
{
InFrontBB[WHITE][rw] = InFrontBB[WHITE][rw + 1] | RankBB[rw + 1];
InFrontBB[BLACK][rb] = InFrontBB[BLACK][rb - 1] | RankBB[rb - 1];
}
SetMaskBB[SQ_NONE] = EmptyBoardBB;
ClearMaskBB[SQ_NONE] = ~SetMaskBB[SQ_NONE];
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = (1ULL << s);
ClearMaskBB[s] = ~SetMaskBB[s];
}
for (Color c = WHITE; c <= BLACK; c++)
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SquaresInFrontMask[c][s] = in_front_bb(c, s) & file_bb(s);
PassedPawnMask[c][s] = in_front_bb(c, s) & this_and_neighboring_files_bb(s);
AttackSpanMask[c][s] = in_front_bb(c, s) & neighboring_files_bb(s);
}
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)count_1s<CNT32>(b);
}
void init_step_attacks() {
const int step[][9] = {
{0},
{7,9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}, {0}, {0},
{-7,-9,0}, {17,15,10,6,-6,-10,-15,-17,0}, {0}, {0}, {0},
{9,7,-7,-9,8,1,-1,-8,0}
};
for (Square s = SQ_A1; s <= SQ_H8; s++)
for (Piece pc = WP; pc <= BK; pc++)
for (int k = 0; step[pc][k] != 0; k++)
{
Square to = s + Square(step[pc][k]);
if (square_is_ok(to) && square_distance(s, to) < 3)
set_bit(&StepAttacksBB[pc][s], to);
}
}
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax) {
int dx, dy, f, r;
int rk = sq / 8;
int fl = sq % 8;
Bitboard attacks = EmptyBoardBB;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
dx = deltas[i][0]; Square s = sq + delta[i];
dy = deltas[i][1];
f = fl + dx;
r = rk + dy;
while ( (dx == 0 || (f >= fmin && f <= fmax)) while (square_is_ok(s) && square_distance(s, s - delta[i]) == 1)
&& (dy == 0 || (r >= rmin && r <= rmax)))
{ {
attacks |= (1ULL << (f + r * 8)); set_bit(&attacks, s);
if (occupied & (1ULL << (f + r * 8))) if (bit_is_set(occupied, s))
break; break;
f += dx; s += delta[i];
r += dy;
} }
} }
return attacks; return attacks;
} }
Bitboard index_to_bitboard(int index, Bitboard mask) {
Bitboard result = EmptyBoardBB; Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) {
int sq, cnt = 0;
while (mask) Bitboard magic;
// Values s1 and s2 are used to rotate the candidate magic of a
// quantity known to be the optimal to quickly find the magics.
int s1 = booster & 63, s2 = (booster >> 6) & 63;
while (true)
{ {
sq = pop_1st_bit(&mask); magic = rk.rand<Bitboard>();
magic = (magic >> s1) | (magic << (64 - s1));
magic &= rk.rand<Bitboard>();
magic = (magic >> s2) | (magic << (64 - s2));
magic &= rk.rand<Bitboard>();
if (index & (1 << cnt++)) if (BitCount8Bit[(mask * magic) >> 56] >= 6)
result |= (1ULL << sq); return magic;
}
return result;
}
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]) {
Bitboard b, v;
int i, j, index;
for (i = index = 0; i < 64; i++)
{
attackIndex[i] = index;
mask[i] = sliding_attacks(i, 0, deltas, 1, 6, 1, 6);
j = 1 << ((CpuIs64Bit ? 64 : 32) - shift[i]);
for (int k = 0; k < j; k++)
{
b = index_to_bitboard(k, mask[i]);
v = CpuIs64Bit ? b * mult[i] : unsigned(b * mult[i] ^ (b >> 32) * (mult[i] >> 32));
attacks[index + (v >> shift[i])] = sliding_attacks(i, b, deltas, 0, 7, 0, 7);
}
index += j;
} }
} }
void init_pseudo_attacks() {
// init_magic_bitboards() computes all rook and bishop magics at startup.
// Magic bitboards are used to look up attacks of sliding pieces. As reference
// see chessprogramming.wikispaces.com/Magic+Bitboards. In particular, here we
// use the so called "fancy" approach.
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], int shifts[]) {
int MagicBoosters[][8] = { { 3191, 2184, 1310, 3618, 2091, 1308, 2452, 3996 },
{ 1059, 3608, 605, 3234, 3326, 38, 2029, 3043 } };
RKISS rk;
Bitboard occupancy[4096], reference[4096], edges, b;
int i, size, index, booster;
// attacks[s] is a pointer to the beginning of the attacks table for square 's'
attacks[SQ_A1] = (pt == ROOK ? RookTable : BishopTable);
for (Square s = SQ_A1; s <= SQ_H8; s++) for (Square s = SQ_A1; s <= SQ_H8; s++)
{ {
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB); // Board edges are not considered in the relevant occupancies
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB); edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
// Given a square 's', the mask is the bitboard of sliding attacks from
// 's' computed on an empty board. The index must be big enough to contain
// all the attacks for each possible subset of the mask and so is 2 power
// the number of 1s of the mask. Hence we deduce the size of the shift to
// apply to the 64 or 32 bits word to get the index.
masks[s] = sliding_attacks(pt, s, 0) & ~edges;
shifts[s] = (Is64Bit ? 64 : 32) - popcount<Max15>(masks[s]);
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
// store the corresponding sliding attacks bitboard in reference[].
b = size = 0;
do {
occupancy[size] = b;
reference[size++] = sliding_attacks(pt, s, b);
b = (b - masks[s]) & masks[s];
} while (b);
// Set the offset for the table of the next square. We have individual
// table sizes for each square with "Fancy Magic Bitboards".
if (s < SQ_H8)
attacks[s + 1] = attacks[s] + size;
booster = MagicBoosters[Is64Bit][rank_of(s)];
// Find a magic for square 's' picking up an (almost) random number
// until we find the one that passes the verification test.
do {
magics[s] = pick_random(masks[s], rk, booster);
memset(attacks[s], 0, size * sizeof(Bitboard));
// A good magic must map every possible occupancy to an index that
// looks up the correct sliding attack in the attacks[s] database.
// Note that we build up the database for square 's' as a side
// effect of verifying the magic.
for (i = 0; i < size; i++)
{
index = (pt == ROOK ? rook_index(s, occupancy[i])
: bishop_index(s, occupancy[i]));
if (!attacks[s][index])
attacks[s][index] = reference[i];
else if (attacks[s][index] != reference[i])
break;
}
} while (i != size);
} }
} }
void init_between_bitboards() {
Square s1, s2, s3, d;
int f, r;
for (s1 = SQ_A1; s1 <= SQ_H8; s1++)
for (s2 = SQ_A1; s2 <= SQ_H8; s2++)
if (bit_is_set(QueenPseudoAttacks[s1], s2))
{
f = file_distance(s1, s2);
r = rank_distance(s1, s2);
d = (s2 - s1) / Max(f, r);
for (s3 = s1 + d; s3 != s2; s3 += d)
set_bit(&(BetweenBB[s1][s2]), s3);
}
}
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,27 +23,6 @@
#include "types.h" #include "types.h"
const Bitboard EmptyBoardBB = 0;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
extern Bitboard SquaresByColorBB[2];
extern Bitboard FileBB[8]; extern Bitboard FileBB[8];
extern Bitboard NeighboringFilesBB[8]; extern Bitboard NeighboringFilesBB[8];
extern Bitboard ThisAndNeighboringFilesBB[8]; extern Bitboard ThisAndNeighboringFilesBB[8];
@@ -60,17 +39,15 @@ extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64]; extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64]; extern Bitboard AttackSpanMask[2][64];
extern const uint64_t RMult[64]; extern uint64_t RMagics[64];
extern const int RShift[64]; extern int RShifts[64];
extern Bitboard RMask[64]; extern Bitboard RMasks[64];
extern int RAttackIndex[64]; extern Bitboard* RAttacks[64];
extern Bitboard RAttacks[0x19000];
extern const uint64_t BMult[64]; extern uint64_t BMagics[64];
extern const int BShift[64]; extern int BShifts[64];
extern Bitboard BMask[64]; extern Bitboard BMasks[64];
extern int BAttackIndex[64]; extern Bitboard* BAttacks[64];
extern Bitboard BAttacks[0x1480];
extern Bitboard BishopPseudoAttacks[64]; extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64]; extern Bitboard RookPseudoAttacks[64];
@@ -86,11 +63,11 @@ inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & SetMaskBB[s]; return b & SetMaskBB[s];
} }
inline void set_bit(Bitboard *b, Square s) { inline void set_bit(Bitboard* b, Square s) {
*b |= SetMaskBB[s]; *b |= SetMaskBB[s];
} }
inline void clear_bit(Bitboard *b, Square s) { inline void clear_bit(Bitboard* b, Square s) {
*b &= ClearMaskBB[s]; *b &= ClearMaskBB[s];
} }
@@ -102,7 +79,7 @@ inline Bitboard make_move_bb(Square from, Square to) {
return SetMaskBB[from] | SetMaskBB[to]; return SetMaskBB[from] | SetMaskBB[to];
} }
inline void do_move_bb(Bitboard *b, Bitboard move_bb) { inline void do_move_bb(Bitboard* b, Bitboard move_bb) {
*b ^= move_bb; *b ^= move_bb;
} }
@@ -115,7 +92,7 @@ inline Bitboard rank_bb(Rank r) {
} }
inline Bitboard rank_bb(Square s) { inline Bitboard rank_bb(Square s) {
return RankBB[square_rank(s)]; return RankBB[rank_of(s)];
} }
inline Bitboard file_bb(File f) { inline Bitboard file_bb(File f) {
@@ -123,33 +100,25 @@ inline Bitboard file_bb(File f) {
} }
inline Bitboard file_bb(Square s) { inline Bitboard file_bb(Square s) {
return FileBB[square_file(s)]; return FileBB[file_of(s)];
} }
/// neighboring_files_bb takes a file or a square as input and returns a /// neighboring_files_bb takes a file as input and returns a bitboard representing
/// bitboard representing all squares on the neighboring files. /// all squares on the neighboring files.
inline Bitboard neighboring_files_bb(File f) { inline Bitboard neighboring_files_bb(File f) {
return NeighboringFilesBB[f]; return NeighboringFilesBB[f];
} }
inline Bitboard neighboring_files_bb(Square s) {
return NeighboringFilesBB[square_file(s)];
}
/// this_and_neighboring_files_bb takes a file as input and returns a bitboard
/// this_and_neighboring_files_bb takes a file or a square as input and returns /// representing all squares on the given and neighboring files.
/// a bitboard representing all squares on the given and neighboring files.
inline Bitboard this_and_neighboring_files_bb(File f) { inline Bitboard this_and_neighboring_files_bb(File f) {
return ThisAndNeighboringFilesBB[f]; return ThisAndNeighboringFilesBB[f];
} }
inline Bitboard this_and_neighboring_files_bb(Square s) {
return ThisAndNeighboringFilesBB[square_file(s)];
}
/// in_front_bb() takes a color and a rank or square as input, and returns a /// in_front_bb() takes a color and a rank or square as input, and returns a
/// bitboard representing all the squares on all ranks in front of the rank /// bitboard representing all the squares on all ranks in front of the rank
@@ -162,7 +131,7 @@ inline Bitboard in_front_bb(Color c, Rank r) {
} }
inline Bitboard in_front_bb(Color c, Square s) { inline Bitboard in_front_bb(Color c, Square s) {
return InFrontBB[c][square_rank(s)]; return InFrontBB[c][rank_of(s)];
} }
@@ -173,32 +142,35 @@ inline Bitboard in_front_bb(Color c, Square s) {
#if defined(IS_64BIT) #if defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) { FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
Bitboard b = blockers & RMask[s]; return unsigned(((occ & RMasks[s]) * RMagics[s]) >> RShifts[s]);
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
} }
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) { FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
Bitboard b = blockers & BMask[s]; return unsigned(((occ & BMasks[s]) * BMagics[s]) >> BShifts[s]);
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
} }
#else // if !defined(IS_64BIT) #else // if !defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) { FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
Bitboard b = blockers & RMask[s]; Bitboard b = occ & RMasks[s];
return RAttacks[RAttackIndex[s] + return unsigned(int(b) * int(RMagics[s]) ^ int(b >> 32) * int(RMagics[s] >> 32)) >> RShifts[s];
(unsigned(int(b) * int(RMult[s]) ^ int(b >> 32) * int(RMult[s] >> 32)) >> RShift[s])];
} }
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) { FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
Bitboard b = blockers & BMask[s]; Bitboard b = occ & BMasks[s];
return BAttacks[BAttackIndex[s] + return unsigned(int(b) * int(BMagics[s]) ^ int(b >> 32) * int(BMagics[s] >> 32)) >> BShifts[s];
(unsigned(int(b) * int(BMult[s]) ^ int(b >> 32) * int(BMult[s] >> 32)) >> BShift[s])];
} }
#endif #endif
inline Bitboard rook_attacks_bb(Square s, Bitboard occ) {
return RAttacks[s][rook_index(s, occ)];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard occ) {
return BAttacks[s][bishop_index(s, occ)];
}
inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) { inline Bitboard queen_attacks_bb(Square s, Bitboard blockers) {
return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers); return rook_attacks_bb(s, blockers) | bishop_attacks_bb(s, blockers);
} }
@@ -249,7 +221,16 @@ inline Bitboard attack_span_mask(Color c, Square s) {
inline bool squares_aligned(Square s1, Square s2, Square s3) { inline bool squares_aligned(Square s1, Square s2, Square s3) {
return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3]) return (BetweenBB[s1][s2] | BetweenBB[s1][s3] | BetweenBB[s2][s3])
& ((1ULL << s1) | (1ULL << s2) | (1ULL << s3)); & ( SetMaskBB[s1] | SetMaskBB[s2] | SetMaskBB[s3]);
}
/// same_color_squares() returns a bitboard representing all squares with
/// the same color of the given square.
inline Bitboard same_color_squares(Square s) {
return bit_is_set(0xAA55AA55AA55AA55ULL, s) ? 0xAA55AA55AA55AA55ULL
: ~0xAA55AA55AA55AA55ULL;
} }
@@ -290,6 +271,6 @@ extern Square pop_1st_bit(Bitboard* b);
extern void print_bitboard(Bitboard b); extern void print_bitboard(Bitboard b);
extern void init_bitboards(); extern void bitboards_init();
#endif // !defined(BITBOARD_H_INCLUDED) #endif // !defined(BITBOARD_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,25 +21,29 @@
#if !defined(BITCOUNT_H_INCLUDED) #if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED #define BITCOUNT_H_INCLUDED
#include <cassert>
#include "types.h" #include "types.h"
enum BitCountType { enum BitCountType {
CNT64, CNT_64,
CNT64_MAX15, CNT_64_MAX15,
CNT32, CNT_32,
CNT32_MAX15, CNT_32_MAX15,
CNT_POPCNT CNT_HW_POPCNT
}; };
/// count_1s() counts the number of nonzero bits in a bitboard. /// Determine at compile time the best popcount<> specialization according if
/// We have different optimized versions according if platform /// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or
/// is 32 or 64 bits, and to the maximum number of nonzero bits. /// use hardware popcnt instruction when available.
/// We also support hardware popcnt instruction. See Readme.txt const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
/// on how to pgo compile with popcnt support. const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
template<BitCountType> inline int count_1s(Bitboard);
/// popcount() counts the number of nonzero bits in a bitboard
template<BitCountType> inline int popcount(Bitboard);
template<> template<>
inline int count_1s<CNT64>(Bitboard b) { inline int popcount<CNT_64>(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL); b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL; b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
@@ -48,7 +52,7 @@ inline int count_1s<CNT64>(Bitboard b) {
} }
template<> template<>
inline int count_1s<CNT64_MAX15>(Bitboard b) { inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL; b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL); b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL; b *= 0x1111111111111111ULL;
@@ -56,7 +60,7 @@ inline int count_1s<CNT64_MAX15>(Bitboard b) {
} }
template<> template<>
inline int count_1s<CNT32>(Bitboard b) { inline int popcount<CNT_32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -69,7 +73,7 @@ inline int count_1s<CNT32>(Bitboard b) {
} }
template<> template<>
inline int count_1s<CNT32_MAX15>(Bitboard b) { inline int popcount<CNT_32_MAX15>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b); unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555; w -= (w >> 1) & 0x55555555;
@@ -81,17 +85,27 @@ inline int count_1s<CNT32_MAX15>(Bitboard b) {
} }
template<> template<>
inline int count_1s<CNT_POPCNT>(Bitboard b) { inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT) #if !defined(USE_POPCNT)
assert(false);
return int(b != 0); // Avoid 'b not used' warning return int(b != 0); // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER) #elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b); return _mm_popcnt_u64(b);
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
return (int)__popcnt64(b); return (int)__popcnt64(b);
#elif defined(__GNUC__)
#else
unsigned long ret; unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b)); __asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret; return ret;
#endif #endif
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#include <iostream> #include <iostream>
#include "book.h" #include "book.h"
#include "misc.h"
#include "movegen.h" #include "movegen.h"
using namespace std; using namespace std;
@@ -35,7 +36,7 @@ using namespace std;
namespace { namespace {
// Random numbers from PolyGlot, used to compute book hash keys // Random numbers from PolyGlot, used to compute book hash keys
const uint64_t Random64[781] = { const Key PolyGlotRandoms[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL, 0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL, 0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL, 0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
@@ -299,57 +300,56 @@ namespace {
0xF8D626AAAF278509ULL 0xF8D626AAAF278509ULL
}; };
// Indices to the Random64[] array // Offsets to the PolyGlotRandoms[] array of zobrist keys
const int PieceIdx = 0; const Key* ZobPiece = PolyGlotRandoms + 0;
const int CastleIdx = 768; const Key* ZobCastle = PolyGlotRandoms + 768;
const int EnPassantIdx = 772; const Key* ZobEnPassant = PolyGlotRandoms + 772;
const int TurnIdx = 780; const Key* ZobTurn = PolyGlotRandoms + 780;
// Piece offset is calculated as 64 * (PolyPiece ^ 1) where
// PolyPiece is: BP = 0, WP = 1, BN = 2, WN = 3 ... BK = 10, WK = 11
const int PieceOfs[] = { 0, 64, 192, 320, 448, 576, 704, 0,
0, 0, 128, 256, 384, 512, 640 };
// book_key() builds up a PolyGlot hash key out of a position // book_key() builds up a PolyGlot hash key out of a position
uint64_t book_key(const Position& pos) { uint64_t book_key(const Position& pos) {
// Piece offset is calculated as (64 * PolyPieceType + square), where
// PolyPieceType is: BP = 0, WP = 1, BN = 2, WN = 3 .... BK = 10, WK = 11
static const int PieceToPoly[] = { 0, 1, 3, 5, 7, 9, 11, 0, 0, 0, 2, 4, 6, 8, 10 };
uint64_t result = 0; uint64_t result = 0;
Bitboard b = pos.occupied_squares(); Bitboard b = pos.occupied_squares();
while (b) while (b)
{ {
Square s = pop_1st_bit(&b); Square s = pop_1st_bit(&b);
int p = PieceToPoly[int(pos.piece_on(s))]; result ^= ZobPiece[PieceOfs[pos.piece_on(s)] + s];
result ^= Random64[PieceIdx + 64 * p + int(s)];
} }
if (pos.can_castle_kingside(WHITE)) if (pos.can_castle(WHITE_OO))
result ^= Random64[CastleIdx + 0]; result ^= ZobCastle[0];
if (pos.can_castle_queenside(WHITE)) if (pos.can_castle(WHITE_OOO))
result ^= Random64[CastleIdx + 1]; result ^= ZobCastle[1];
if (pos.can_castle_kingside(BLACK)) if (pos.can_castle(BLACK_OO))
result ^= Random64[CastleIdx + 2]; result ^= ZobCastle[2];
if (pos.can_castle_queenside(BLACK)) if (pos.can_castle(BLACK_OOO))
result ^= Random64[CastleIdx + 3]; result ^= ZobCastle[3];
if (pos.ep_square() != SQ_NONE) if (pos.ep_square() != SQ_NONE)
result ^= Random64[EnPassantIdx + square_file(pos.ep_square())]; result ^= ZobEnPassant[file_of(pos.ep_square())];
if (pos.side_to_move() == WHITE) if (pos.side_to_move() == WHITE)
result ^= Random64[TurnIdx]; result ^= ZobTurn[0];
return result; return result;
} }
} }
/// Book c'tor. Make random number generation less deterministic, for book moves /// Book c'tor. Make random number generation less deterministic, for book moves
Book::Book() { Book::Book() : bookSize(0) {
for (int i = abs(get_system_time() % 10000); i > 0; i--) for (int i = abs(system_time() % 10000); i > 0; i--)
RKiss.rand<unsigned>(); RKiss.rand<unsigned>();
} }
@@ -362,8 +362,8 @@ Book::~Book() {
} }
/// Book::close() closes the file only if it is open, otherwise /// Book::close() closes the file only if it is open, otherwise the call fails
/// we can end up in a little mess due to how std::ifstream works. /// and the failbit internal state flag is set.
void Book::close() { void Book::close() {
@@ -371,24 +371,24 @@ void Book::close() {
bookFile.close(); bookFile.close();
bookName = ""; bookName = "";
bookSize = 0;
} }
/// Book::open() opens a book file with a given file name /// Book::open() opens a book file with a given name
void Book::open(const string& fileName) { void Book::open(const string& fileName) {
// Close old file before opening the new // Close old file before opening the new
close(); close();
bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary); bookFile.open(fileName.c_str(), ifstream::in | ifstream::binary |ios::ate);
// Silently return when asked to open a non-exsistent file // Silently return when asked to open a non-exsistent file
if (!bookFile.is_open()) if (!bookFile.is_open())
return; return;
// Get the book size in number of entries // Get the book size in number of entries, we are already at the file end
bookFile.seekg(0, ios::end);
bookSize = long(bookFile.tellg()) / sizeof(BookEntry); bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
if (!bookFile.good()) if (!bookFile.good())
@@ -402,46 +402,39 @@ void Book::open(const string& fileName) {
} }
/// Book::get_move() gets a book move for a given position. Returns /// Book::probe() gets a book move for a given position. Returns MOVE_NONE
/// MOVE_NONE if no book move is found. If findBestMove is true then /// if no book move is found. If findBest is true then returns always the
/// return always the highest rated book move. /// highest rated move otherwise chooses randomly based on the move score.
Move Book::get_move(const Position& pos, bool findBestMove) { Move Book::probe(const Position& pos, bool findBest) {
if (!bookFile.is_open() || bookSize == 0) if (!bookSize || !bookFile.is_open())
return MOVE_NONE; return MOVE_NONE;
BookEntry entry; BookEntry entry;
int bookMove = MOVE_NONE; unsigned scoresSum = 0, bestScore = 0, bookMove = 0;
unsigned score, scoresSum = 0, bestScore = 0;
uint64_t key = book_key(pos); uint64_t key = book_key(pos);
int idx = first_entry(key) - 1;
// Choose a book move among the possible moves for the given position // Choose a book move among the possible moves for the given position
for (int idx = find_entry(key); idx < bookSize; idx++) while (++idx < bookSize && (entry = read_entry(idx), entry.key == key))
{ {
entry = read_entry(idx); scoresSum += entry.count;
if (entry.key != key) // Choose book move according to its score. If a move has a very
break; // high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen.
score = entry.count; if ( RKiss.rand<unsigned>() % scoresSum < entry.count
|| (findBest && entry.count > bestScore))
if (!findBestMove)
{
// Choose book move according to its score. If a move has a very
// high score it has more probability to be choosen then a one with
// lower score. Note that first entry is always chosen.
scoresSum += score;
if (RKiss.rand<unsigned>() % scoresSum < score)
bookMove = entry.move;
}
else if (score > bestScore)
{
bestScore = score;
bookMove = entry.move; bookMove = entry.move;
}
if (entry.count > bestScore)
bestScore = entry.count;
} }
if (!bookMove)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows: // A PolyGlot book move is encoded as follows:
// //
// bit 0- 5: destination square (from 0 to 63) // bit 0- 5: destination square (from 0 to 63)
@@ -452,33 +445,31 @@ Move Book::get_move(const Position& pos, bool findBestMove) {
// book move is a promotion we have to convert to our representation, in // book move is a promotion we have to convert to our representation, in
// all other cases we can directly compare with a Move after having // all other cases we can directly compare with a Move after having
// masked out special Move's flags that are not supported by PolyGlot. // masked out special Move's flags that are not supported by PolyGlot.
int p = (bookMove >> 12) & 7; int promotion = (bookMove >> 12) & 7;
if (p) if (promotion)
bookMove = int(make_promotion_move(move_from(Move(bookMove)), bookMove = make_promotion_move(move_from(Move(bookMove)),
move_to(Move(bookMove)), PieceType(p + 1))); move_to(Move(bookMove)),
PieceType(promotion + 1));
// Verify the book move (if any) is legal // Verify the book move is legal
MoveStack mlist[MAX_MOVES]; for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
MoveStack* last = generate<MV_LEGAL>(pos, mlist); if (unsigned(ml.move() & ~(3 << 14)) == bookMove) // Mask out special flags
for (MoveStack* cur = mlist; cur != last; cur++) return ml.move();
if ((int(cur->move) & ~(3 << 14)) == bookMove) // Mask out special flags
return cur->move;
return MOVE_NONE; return MOVE_NONE;
} }
/// Book::find_entry() takes a book key as input, and does a binary search /// Book::first_entry() takes a book key as input, and does a binary search
/// through the book file for the given key. The index to the first book /// through the book file for the given key. The index to the first (leftmost)
/// entry with the same key as the input is returned. When the key is not /// book entry with the same key as the input is returned. When the key is not
/// found in the book file, bookSize is returned. /// found in the book file, bookSize is returned.
int Book::find_entry(uint64_t key) { int Book::first_entry(uint64_t key) {
int left, right, mid; int left, right, mid;
// Binary search (finds the leftmost entry) // Binary search (finds the leftmost entry with given key)
left = 0; left = 0;
right = bookSize - 1; right = bookSize - 1;
@@ -502,15 +493,17 @@ int Book::find_entry(uint64_t key) {
} }
/// Book::get_number() reads sizeof(T) chars from the file's binary byte /// Book::operator>>() reads sizeof(T) chars from the file's binary byte
/// stream and converts them in a number of type T. /// stream and converts them in a number of type T.
template<typename T> template<typename T>
void Book::get_number(T& n) { Book& Book::operator>>(T& n) {
n = 0; n = 0;
for (size_t i = 0; i < sizeof(T); i++) for (size_t i = 0; i < sizeof(T); i++)
n = (n << 8) + (T)bookFile.get(); n = T((n << 8) + bookFile.get());
return *this;
} }
@@ -526,10 +519,7 @@ BookEntry Book::read_entry(int idx) {
bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg); bookFile.seekg(idx * sizeof(BookEntry), ios_base::beg);
get_number(e.key); *this >> e.key >> e.move >> e.count >> e.learn;
get_number(e.move);
get_number(e.count);
get_number(e.learn);
if (!bookFile.good()) if (!bookFile.good())
{ {

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -23,14 +23,13 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "move.h"
#include "position.h" #include "position.h"
#include "rkiss.h" #include "rkiss.h"
// A Polyglot book is a series of "entries" of 16 bytes. All integers are /// A Polyglot book is a series of "entries" of 16 bytes. All integers are
// stored highest byte first (regardless of size). The entries are ordered /// stored highest byte first (regardless of size). The entries are ordered
// according to key. Lowest key first. /// according to key. Lowest key first.
struct BookEntry { struct BookEntry {
uint64_t key; uint64_t key;
uint16_t move; uint16_t move;
@@ -44,19 +43,19 @@ public:
~Book(); ~Book();
void open(const std::string& fileName); void open(const std::string& fileName);
void close(); void close();
Move get_move(const Position& pos, bool findBestMove); Move probe(const Position& pos, bool findBestMove);
const std::string name() const { return bookName; } const std::string name() const { return bookName; }
private: private:
template<typename T> void get_number(T& n); template<typename T> Book& operator>>(T& n);
BookEntry read_entry(int idx); BookEntry read_entry(int idx);
int find_entry(uint64_t key); int first_entry(uint64_t key);
RKISS RKiss;
std::ifstream bookFile; std::ifstream bookFile;
std::string bookName; std::string bookName;
int bookSize; int bookSize;
RKISS RKiss;
}; };
#endif // !defined(BOOK_H_INCLUDED) #endif // !defined(BOOK_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "bitcount.h" #include "bitcount.h"
@@ -59,107 +60,75 @@ namespace {
// the two kings in basic endgames. // the two kings in basic endgames.
const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 }; const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
// Penalty for big distance between king and knight for the defending king // Get the material key of a Position out of the given endgame key code
// and knight in KR vs KN endgames. // like "KBPKN". The trick here is to first forge an ad-hoc fen string
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 }; // and then let a Position object to do the work for us. Note that the
// fen string could correspond to an illegal position.
Key key(const string& code, Color c) {
// Build corresponding key code for the opposite color: "KBPKN" -> "KNKBP" assert(code.length() > 0 && code.length() < 8);
const string swap_colors(const string& keyCode) { assert(code[0] == 'K');
size_t idx = keyCode.find('K', 1); string sides[] = { code.substr(code.find('K', 1)), // Weaker
return keyCode.substr(idx) + keyCode.substr(0, idx); code.substr(0, code.find('K', 1)) }; // Stronger
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
string fen = sides[0] + char('0' + int(8 - code.length()))
+ sides[1] + "/8/8/8/8/8/8/8 w - - 0 10";
return Position(fen, false, 0).material_key();
} }
// Get the material key of a position out of the given endgame key code template<typename M>
// like "KBPKN". The trick here is to first build up a FEN string and then void delete_endgame(const typename M::value_type& p) { delete p.second; }
// let a Position object to do the work for us. Note that the FEN string
// could correspond to an illegal position.
Key mat_key(const string& keyCode) {
assert(keyCode.length() > 0 && keyCode.length() < 8);
assert(keyCode[0] == 'K');
string fen;
size_t i = 0;
// First add white and then black pieces
do fen += keyCode[i]; while (keyCode[++i] != 'K');
do fen += char(tolower(keyCode[i])); while (++i < keyCode.length());
// Add file padding and remaining empty ranks
fen += string(1, '0' + int(8 - keyCode.length())) + "/8/8/8/8/8/8/8 w - -";
// Build a Position out of the fen string and get its material key
return Position(fen, false, 0).get_material_key();
}
typedef EndgameBase<Value> EF;
typedef EndgameBase<ScaleFactor> SF;
} // namespace } // namespace
/// Endgames member definitions /// Endgames members definitions
template<> const Endgames::EFMap& Endgames::get<EF>() const { return maps.first; }
template<> const Endgames::SFMap& Endgames::get<SF>() const { return maps.second; }
Endgames::Endgames() { Endgames::Endgames() {
add<Endgame<Value, KNNK> >("KNNK"); add<KPK>("KPK");
add<Endgame<Value, KPK> >("KPK"); add<KNNK>("KNNK");
add<Endgame<Value, KBNK> >("KBNK"); add<KBNK>("KBNK");
add<Endgame<Value, KRKP> >("KRKP"); add<KRKP>("KRKP");
add<Endgame<Value, KRKB> >("KRKB"); add<KRKB>("KRKB");
add<Endgame<Value, KRKN> >("KRKN"); add<KRKN>("KRKN");
add<Endgame<Value, KQKR> >("KQKR"); add<KQKR>("KQKR");
add<Endgame<Value, KBBKN> >("KBBKN"); add<KBBKN>("KBBKN");
add<Endgame<ScaleFactor, KNPK> >("KNPK"); add<KNPK>("KNPK");
add<Endgame<ScaleFactor, KRPKR> >("KRPKR"); add<KRPKR>("KRPKR");
add<Endgame<ScaleFactor, KBPKB> >("KBPKB"); add<KBPKB>("KBPKB");
add<Endgame<ScaleFactor, KBPPKB> >("KBPPKB"); add<KBPKN>("KBPKN");
add<Endgame<ScaleFactor, KBPKN> >("KBPKN"); add<KBPPKB>("KBPPKB");
add<Endgame<ScaleFactor, KRPPKRP> >("KRPPKRP"); add<KRPPKRP>("KRPPKRP");
} }
Endgames::~Endgames() { Endgames::~Endgames() {
for (EFMap::const_iterator it = get<EF>().begin(); it != get<EF>().end(); ++it) for_each(m1.begin(), m1.end(), delete_endgame<M1>);
delete it->second; for_each(m2.begin(), m2.end(), delete_endgame<M2>);
for (SFMap::const_iterator it = get<SF>().begin(); it != get<SF>().end(); ++it)
delete it->second;
} }
template<class T> template<EndgameType E>
void Endgames::add(const string& keyCode) { void Endgames::add(const string& code) {
typedef typename T::Base F; typedef typename eg_family<E>::type T;
typedef std::map<Key, F*> M;
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(keyCode), new T(WHITE))); map((T*)0)[key(code, WHITE)] = new Endgame<E>(WHITE);
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(swap_colors(keyCode)), new T(BLACK))); map((T*)0)[key(code, BLACK)] = new Endgame<E>(BLACK);
} }
template<class T>
T* Endgames::get(Key key) const {
typename std::map<Key, T*>::const_iterator it = get<T>().find(key);
return it != get<T>().end() ? it->second : NULL;
}
// Explicit template instantiations
template EF* Endgames::get<EF>(Key key) const;
template SF* Endgames::get<SF>(Key key) const;
/// Mate with KX vs K. This function is used to evaluate positions with /// Mate with KX vs K. This function is used to evaluate positions with
/// King and plenty of material vs a lone king. It simply gives the /// King and plenty of material vs a lone king. It simply gives the
/// attacking side a bonus for driving the defending king towards the edge /// attacking side a bonus for driving the defending king towards the edge
/// of the board, and for keeping the distance between the two kings small. /// of the board, and for keeping the distance between the two kings small.
template<> template<>
Value Endgame<Value, KXK>::apply(const Position& pos) const { Value Endgame<KXK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
@@ -185,7 +154,7 @@ Value Endgame<Value, KXK>::apply(const Position& pos) const {
/// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the /// Mate with KBN vs K. This is similar to KX vs K, but we have to drive the
/// defending king towards a corner square of the right color. /// defending king towards a corner square of the right color.
template<> template<>
Value Endgame<Value, KBNK>::apply(const Position& pos) const { Value Endgame<KBNK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO); assert(pos.piece_count(weakerSide, PAWN) == VALUE_ZERO);
@@ -196,15 +165,15 @@ Value Endgame<Value, KBNK>::apply(const Position& pos) const {
Square winnerKSq = pos.king_square(strongerSide); Square winnerKSq = pos.king_square(strongerSide);
Square loserKSq = pos.king_square(weakerSide); Square loserKSq = pos.king_square(weakerSide);
Square bishopSquare = pos.piece_list(strongerSide, BISHOP, 0); Square bishopSquare = pos.piece_list(strongerSide, BISHOP)[0];
// kbnk_mate_table() tries to drive toward corners A1 or H8, // kbnk_mate_table() tries to drive toward corners A1 or H8,
// if we have a bishop that cannot reach the above squares we // if we have a bishop that cannot reach the above squares we
// mirror the kings so to drive enemy toward corners A8 or H1. // mirror the kings so to drive enemy toward corners A8 or H1.
if (opposite_color_squares(bishopSquare, SQ_A1)) if (opposite_colors(bishopSquare, SQ_A1))
{ {
winnerKSq = flop_square(winnerKSq); winnerKSq = mirror(winnerKSq);
loserKSq = flop_square(loserKSq); loserKSq = mirror(loserKSq);
} }
Value result = VALUE_KNOWN_WIN Value result = VALUE_KNOWN_WIN
@@ -217,7 +186,7 @@ Value Endgame<Value, KBNK>::apply(const Position& pos) const {
/// KP vs K. This endgame is evaluated with the help of a bitbase. /// KP vs K. This endgame is evaluated with the help of a bitbase.
template<> template<>
Value Endgame<Value, KPK>::apply(const Position& pos) const { Value Endgame<KPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
@@ -231,22 +200,22 @@ Value Endgame<Value, KPK>::apply(const Position& pos) const {
{ {
wksq = pos.king_square(WHITE); wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK); bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN, 0); wpsq = pos.piece_list(WHITE, PAWN)[0];
stm = pos.side_to_move(); stm = pos.side_to_move();
} }
else else
{ {
wksq = flip_square(pos.king_square(BLACK)); wksq = flip(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE)); bksq = flip(pos.king_square(WHITE));
wpsq = flip_square(pos.piece_list(BLACK, PAWN, 0)); wpsq = flip(pos.piece_list(BLACK, PAWN)[0]);
stm = opposite_color(pos.side_to_move()); stm = flip(pos.side_to_move());
} }
if (square_file(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)
{ {
wksq = flop_square(wksq); wksq = mirror(wksq);
bksq = flop_square(bksq); bksq = mirror(bksq);
wpsq = flop_square(wpsq); wpsq = mirror(wpsq);
} }
if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm)) if (!probe_kpk_bitbase(wksq, wpsq, bksq, stm))
@@ -254,7 +223,7 @@ Value Endgame<Value, KPK>::apply(const Position& pos) const {
Value result = VALUE_KNOWN_WIN Value result = VALUE_KNOWN_WIN
+ PawnValueEndgame + PawnValueEndgame
+ Value(square_rank(wpsq)); + Value(rank_of(wpsq));
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -265,7 +234,7 @@ Value Endgame<Value, KPK>::apply(const Position& pos) const {
/// far advanced with support of the king, while the attacking king is far /// far advanced with support of the king, while the attacking king is far
/// away. /// away.
template<> template<>
Value Endgame<Value, KRKP>::apply(const Position& pos) const { Value Endgame<KRKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -276,23 +245,23 @@ Value Endgame<Value, KRKP>::apply(const Position& pos) const {
int tempo = (pos.side_to_move() == strongerSide); int tempo = (pos.side_to_move() == strongerSide);
wksq = pos.king_square(strongerSide); wksq = pos.king_square(strongerSide);
wrsq = pos.piece_list(strongerSide, ROOK, 0); wrsq = pos.piece_list(strongerSide, ROOK)[0];
bksq = pos.king_square(weakerSide); bksq = pos.king_square(weakerSide);
bpsq = pos.piece_list(weakerSide, PAWN, 0); bpsq = pos.piece_list(weakerSide, PAWN)[0];
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
wksq = flip_square(wksq); wksq = flip(wksq);
wrsq = flip_square(wrsq); wrsq = flip(wrsq);
bksq = flip_square(bksq); bksq = flip(bksq);
bpsq = flip_square(bpsq); bpsq = flip(bpsq);
} }
Square queeningSq = make_square(square_file(bpsq), RANK_1); Square queeningSq = make_square(file_of(bpsq), RANK_1);
Value result; Value result;
// If the stronger side's king is in front of the pawn, it's a win // If the stronger side's king is in front of the pawn, it's a win
if (wksq < bpsq && square_file(wksq) == square_file(bpsq)) if (wksq < bpsq && file_of(wksq) == file_of(bpsq))
result = RookValueEndgame - Value(square_distance(wksq, bpsq)); result = RookValueEndgame - Value(square_distance(wksq, bpsq));
// If the weaker side's king is too far from the pawn and the rook, // If the weaker side's king is too far from the pawn and the rook,
@@ -303,9 +272,9 @@ Value Endgame<Value, KRKP>::apply(const Position& pos) const {
// If the pawn is far advanced and supported by the defending king, // If the pawn is far advanced and supported by the defending king,
// the position is drawish // the position is drawish
else if ( square_rank(bksq) <= RANK_3 else if ( rank_of(bksq) <= RANK_3
&& square_distance(bksq, bpsq) == 1 && square_distance(bksq, bpsq) == 1
&& square_rank(wksq) >= RANK_4 && rank_of(wksq) >= RANK_4
&& square_distance(wksq, bpsq) - tempo > 2) && square_distance(wksq, bpsq) - tempo > 2)
result = Value(80 - square_distance(wksq, bpsq) * 8); result = Value(80 - square_distance(wksq, bpsq) * 8);
@@ -322,7 +291,7 @@ Value Endgame<Value, KRKP>::apply(const Position& pos) const {
/// KR vs KB. This is very simple, and always returns drawish scores. The /// KR vs KB. This is very simple, and always returns drawish scores. The
/// score is slightly bigger when the defending king is close to the edge. /// score is slightly bigger when the defending king is close to the edge.
template<> template<>
Value Endgame<Value, KRKB>::apply(const Position& pos) const { Value Endgame<KRKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -338,7 +307,7 @@ Value Endgame<Value, KRKB>::apply(const Position& pos) const {
/// KR vs KN. The attacking side has slightly better winning chances than /// KR vs KN. The attacking side has slightly better winning chances than
/// in KR vs KB, particularly if the king and the knight are far apart. /// in KR vs KB, particularly if the king and the knight are far apart.
template<> template<>
Value Endgame<Value, KRKN>::apply(const Position& pos) const { Value Endgame<KRKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -346,14 +315,11 @@ Value Endgame<Value, KRKN>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.piece_count(weakerSide, KNIGHT) == 1);
Square defendingKSq = pos.king_square(weakerSide); const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 };
Square nSq = pos.piece_list(weakerSide, KNIGHT, 0);
int d = square_distance(defendingKSq, nSq);
Value result = Value(10)
+ MateTable[defendingKSq]
+ KRKNKingKnightDistancePenalty[d];
Square bksq = pos.king_square(weakerSide);
Square bnsq = pos.piece_list(weakerSide, KNIGHT)[0];
Value result = Value(MateTable[bksq] + penalty[square_distance(bksq, bnsq)]);
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -364,7 +330,7 @@ Value Endgame<Value, KRKN>::apply(const Position& pos) const {
/// for the defending side in the search, this is usually sufficient to be /// for the defending side in the search, this is usually sufficient to be
/// able to win KQ vs KR. /// able to win KQ vs KR.
template<> template<>
Value Endgame<Value, KQKR>::apply(const Position& pos) const { Value Endgame<KQKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 0); assert(pos.piece_count(strongerSide, PAWN) == 0);
@@ -383,18 +349,18 @@ Value Endgame<Value, KQKR>::apply(const Position& pos) const {
} }
template<> template<>
Value Endgame<Value, KBBKN>::apply(const Position& pos) const { Value Endgame<KBBKN>::operator()(const Position& pos) const {
assert(pos.piece_count(strongerSide, BISHOP) == 2); assert(pos.piece_count(strongerSide, BISHOP) == 2);
assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame); assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pieces(PAWN) == EmptyBoardBB); assert(!pos.pieces(PAWN));
Value result = BishopValueEndgame; Value result = BishopValueEndgame;
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square nsq = pos.piece_list(weakerSide, KNIGHT, 0); Square nsq = pos.piece_list(weakerSide, KNIGHT)[0];
// Bonus for attacking king close to defending king // Bonus for attacking king close to defending king
result += Value(DistanceBonus[square_distance(wksq, bksq)]); result += Value(DistanceBonus[square_distance(wksq, bksq)]);
@@ -403,7 +369,7 @@ Value Endgame<Value, KBBKN>::apply(const Position& pos) const {
result += Value(square_distance(bksq, nsq) * 32); result += Value(square_distance(bksq, nsq) * 32);
// Bonus for restricting the knight's mobility // Bonus for restricting the knight's mobility
result += Value((8 - count_1s<CNT32_MAX15>(pos.attacks_from<KNIGHT>(nsq))) * 8); result += Value((8 - popcount<Max15>(pos.attacks_from<KNIGHT>(nsq))) * 8);
return strongerSide == pos.side_to_move() ? result : -result; return strongerSide == pos.side_to_move() ? result : -result;
} }
@@ -412,22 +378,21 @@ Value Endgame<Value, KBBKN>::apply(const Position& pos) const {
/// K and two minors vs K and one or two minors or K and two knights against /// K and two minors vs K and one or two minors or K and two knights against
/// king alone are always draw. /// king alone are always draw.
template<> template<>
Value Endgame<Value, KmmKm>::apply(const Position&) const { Value Endgame<KmmKm>::operator()(const Position&) const {
return VALUE_DRAW; return VALUE_DRAW;
} }
template<> template<>
Value Endgame<Value, KNNK>::apply(const Position&) const { Value Endgame<KNNK>::operator()(const Position&) const {
return VALUE_DRAW; return VALUE_DRAW;
} }
/// KBPKScalingFunction scales endgames where the stronger side has king, /// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and
/// bishop and one or more pawns. It checks for draws with rook pawns and a /// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
/// bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_ZERO is /// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used. /// will be used.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const { ScaleFactor Endgame<KBPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -437,18 +402,18 @@ ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
// be detected even when the weaker side has some pawns. // be detected even when the weaker side has some pawns.
Bitboard pawns = pos.pieces(PAWN, strongerSide); Bitboard pawns = pos.pieces(PAWN, strongerSide);
File pawnFile = square_file(pos.piece_list(strongerSide, PAWN, 0)); File pawnFile = file_of(pos.piece_list(strongerSide, PAWN)[0]);
// All pawns are on a single rook file ? // All pawns are on a single rook file ?
if ( (pawnFile == FILE_A || pawnFile == FILE_H) if ( (pawnFile == FILE_A || pawnFile == FILE_H)
&& (pawns & ~file_bb(pawnFile)) == EmptyBoardBB) && !(pawns & ~file_bb(pawnFile)))
{ {
Square bishopSq = pos.piece_list(strongerSide, BISHOP, 0); Square bishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8)); Square queeningSq = relative_square(strongerSide, make_square(pawnFile, RANK_8));
Square kingSq = pos.king_square(weakerSide); Square kingSq = pos.king_square(weakerSide);
if ( opposite_color_squares(queeningSq, bishopSq) if ( opposite_colors(queeningSq, bishopSq)
&& abs(square_file(kingSq) - pawnFile) <= 1) && abs(file_of(kingSq) - pawnFile) <= 1)
{ {
// The bishop has the wrong color, and the defending king is on the // The bishop has the wrong color, and the defending king is on the
// file of the pawn(s) or the neighboring file. Find the rank of the // file of the pawn(s) or the neighboring file. Find the rank of the
@@ -456,12 +421,12 @@ ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
Rank rank; Rank rank;
if (strongerSide == WHITE) if (strongerSide == WHITE)
{ {
for (rank = RANK_7; (rank_bb(rank) & pawns) == EmptyBoardBB; rank--) {} for (rank = RANK_7; !(rank_bb(rank) & pawns); rank--) {}
assert(rank >= RANK_2 && rank <= RANK_7); assert(rank >= RANK_2 && rank <= RANK_7);
} }
else else
{ {
for (rank = RANK_2; (rank_bb(rank) & pawns) == EmptyBoardBB; rank++) {} for (rank = RANK_2; !(rank_bb(rank) & pawns); rank++) {}
rank = Rank(rank ^ 7); // HACK to get the relative rank rank = Rank(rank ^ 7); // HACK to get the relative rank
assert(rank >= RANK_2 && rank <= RANK_7); assert(rank >= RANK_2 && rank <= RANK_7);
} }
@@ -469,19 +434,17 @@ ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
// is placed somewhere in front of the pawn, it's a draw. // is placed somewhere in front of the pawn, it's a draw.
if ( square_distance(kingSq, queeningSq) <= 1 if ( square_distance(kingSq, queeningSq) <= 1
|| relative_rank(strongerSide, kingSq) >= rank) || relative_rank(strongerSide, kingSq) >= rank)
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
} }
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
/// KQKRPScalingFunction scales endgames where the stronger side has only /// K and queen vs K, rook and one or more pawns. It tests for fortress draws with
/// king and queen, while the weaker side has at least a rook and a pawn. /// a rook on the third rank defended by a pawn.
/// It tests for fortress draws with a rook on the third rank defended by
/// a pawn.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KQKRPs>::apply(const Position& pos) const { ScaleFactor Endgame<KQKRPs>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame); assert(pos.non_pawn_material(strongerSide) == QueenValueMidgame);
assert(pos.piece_count(strongerSide, QUEEN) == 1); assert(pos.piece_count(strongerSide, QUEEN) == 1);
@@ -496,23 +459,22 @@ ScaleFactor Endgame<ScaleFactor, KQKRPs>::apply(const Position& pos) const {
&& (pos.pieces(PAWN, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_2))) && (pos.pieces(PAWN, weakerSide) & rank_bb(relative_rank(weakerSide, RANK_2)))
&& (pos.attacks_from<KING>(kingSq) & pos.pieces(PAWN, weakerSide))) && (pos.attacks_from<KING>(kingSq) & pos.pieces(PAWN, weakerSide)))
{ {
Square rsq = pos.piece_list(weakerSide, ROOK, 0); Square rsq = pos.piece_list(weakerSide, ROOK)[0];
if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide)) if (pos.attacks_from<PAWN>(rsq, strongerSide) & pos.pieces(PAWN, weakerSide))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
/// KRPKRScalingFunction scales KRP vs KR endgames. This function knows a /// K, rook and one pawn vs K and a rook. This function knows a handful of the
/// handful of the most important classes of drawn positions, but is far /// most important classes of drawn positions, but is far from perfect. It would
/// from perfect. It would probably be a good idea to add more knowledge /// probably be a good idea to add more knowledge in the future.
/// in the future.
/// ///
/// It would also be nice to rewrite the actual code for this function, /// It would also be nice to rewrite the actual code for this function,
/// which is mostly copied from Glaurung 1.x, and not very pretty. /// which is mostly copied from Glaurung 1.x, and not very pretty.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const { ScaleFactor Endgame<KRPKR>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 1); assert(pos.piece_count(strongerSide, PAWN) == 1);
@@ -520,32 +482,32 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wksq = pos.king_square(strongerSide); Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.piece_list(strongerSide, ROOK, 0); Square wrsq = pos.piece_list(strongerSide, ROOK)[0];
Square wpsq = pos.piece_list(strongerSide, PAWN, 0); Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
Square brsq = pos.piece_list(weakerSide, ROOK, 0); Square brsq = pos.piece_list(weakerSide, ROOK)[0];
// Orient the board in such a way that the stronger side is white, and the // Orient the board in such a way that the stronger side is white, and the
// pawn is on the left half of the board. // pawn is on the left half of the board.
if (strongerSide == BLACK) if (strongerSide == BLACK)
{ {
wksq = flip_square(wksq); wksq = flip(wksq);
wrsq = flip_square(wrsq); wrsq = flip(wrsq);
wpsq = flip_square(wpsq); wpsq = flip(wpsq);
bksq = flip_square(bksq); bksq = flip(bksq);
brsq = flip_square(brsq); brsq = flip(brsq);
} }
if (square_file(wpsq) > FILE_D) if (file_of(wpsq) > FILE_D)
{ {
wksq = flop_square(wksq); wksq = mirror(wksq);
wrsq = flop_square(wrsq); wrsq = mirror(wrsq);
wpsq = flop_square(wpsq); wpsq = mirror(wpsq);
bksq = flop_square(bksq); bksq = mirror(bksq);
brsq = flop_square(brsq); brsq = mirror(brsq);
} }
File f = square_file(wpsq); File f = file_of(wpsq);
Rank r = square_rank(wpsq); Rank r = rank_of(wpsq);
Square queeningSq = make_square(f, RANK_8); Square queeningSq = make_square(f, RANK_8);
int tempo = (pos.side_to_move() == strongerSide); int tempo = (pos.side_to_move() == strongerSide);
@@ -554,31 +516,31 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
if ( r <= RANK_5 if ( r <= RANK_5
&& square_distance(bksq, queeningSq) <= 1 && square_distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5 && wksq <= SQ_H5
&& (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6))) && (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6)))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
// The defending side saves a draw by checking from behind in case the pawn // The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind. // has advanced to the 6th rank with the king behind.
if ( r == RANK_6 if ( r == RANK_6
&& square_distance(bksq, queeningSq) <= 1 && square_distance(bksq, queeningSq) <= 1
&& square_rank(wksq) + tempo <= RANK_6 && rank_of(wksq) + tempo <= RANK_6
&& (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3))) && (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3)))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
if ( r >= RANK_6 if ( r >= RANK_6
&& bksq == queeningSq && bksq == queeningSq
&& square_rank(brsq) == RANK_1 && rank_of(brsq) == RANK_1
&& (!tempo || square_distance(wksq, wpsq) >= 2)) && (!tempo || square_distance(wksq, wpsq) >= 2))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
// White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7 // White pawn on a7 and rook on a8 is a draw if black's king is on g7 or h7
// and the black rook is behind the pawn. // and the black rook is behind the pawn.
if ( wpsq == SQ_A7 if ( wpsq == SQ_A7
&& wrsq == SQ_A8 && wrsq == SQ_A8
&& (bksq == SQ_H7 || bksq == SQ_G7) && (bksq == SQ_H7 || bksq == SQ_G7)
&& square_file(brsq) == FILE_A && file_of(brsq) == FILE_A
&& (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5)) && (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
// If the defending king blocks the pawn and the attacking king is too far // If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw. // away, it's a draw.
@@ -586,14 +548,14 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
&& bksq == wpsq + DELTA_N && bksq == wpsq + DELTA_N
&& square_distance(wksq, wpsq) - tempo >= 2 && square_distance(wksq, wpsq) - tempo >= 2
&& square_distance(wksq, brsq) - tempo >= 2) && square_distance(wksq, brsq) - tempo >= 2)
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
// Pawn on the 7th rank supported by the rook from behind usually wins if the // Pawn on the 7th rank supported by the rook from behind usually wins if the
// attacking king is closer to the queening square than the defending king, // attacking king is closer to the queening square than the defending king,
// and the defending king cannot gain tempi by threatening the attacking rook. // and the defending king cannot gain tempi by threatening the attacking rook.
if ( r == RANK_7 if ( r == RANK_7
&& f != FILE_A && f != FILE_A
&& square_file(wrsq) == f && file_of(wrsq) == f
&& wrsq != queeningSq && wrsq != queeningSq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo)) && (square_distance(wksq, queeningSq) < square_distance(bksq, wrsq) + tempo))
@@ -601,7 +563,7 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
// Similar to the above, but with the pawn further back // Similar to the above, but with the pawn further back
if ( f != FILE_A if ( f != FILE_A
&& square_file(wrsq) == f && file_of(wrsq) == f
&& wrsq < wpsq && wrsq < wpsq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo) && (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + tempo)
&& (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo) && (square_distance(wksq, wpsq + DELTA_N) < square_distance(bksq, wpsq + DELTA_N) - 2 + tempo)
@@ -616,9 +578,9 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
// the pawn's path, it's probably a draw. // the pawn's path, it's probably a draw.
if (r <= RANK_4 && bksq > wpsq) if (r <= RANK_4 && bksq > wpsq)
{ {
if (square_file(bksq) == square_file(wpsq)) if (file_of(bksq) == file_of(wpsq))
return ScaleFactor(10); return ScaleFactor(10);
if ( abs(square_file(bksq) - square_file(wpsq)) == 1 if ( abs(file_of(bksq) - file_of(wpsq)) == 1
&& square_distance(wksq, bksq) > 2) && square_distance(wksq, bksq) > 2)
return ScaleFactor(24 - 2 * square_distance(wksq, bksq)); return ScaleFactor(24 - 2 * square_distance(wksq, bksq));
} }
@@ -626,19 +588,19 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
} }
/// KRPPKRPScalingFunction scales KRPP vs KRP endgames. There is only a /// K, rook and two pawns vs K, rook and one pawn. There is only a single
/// single pattern: If the stronger side has no pawns and the defending king /// pattern: If the stronger side has no passed pawns and the defending king
/// is actively placed, the position is drawish. /// is actively placed, the position is drawish.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const { ScaleFactor Endgame<KRPPKRP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == RookValueMidgame); assert(pos.non_pawn_material(strongerSide) == RookValueMidgame);
assert(pos.piece_count(strongerSide, PAWN) == 2); assert(pos.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame); assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 1); assert(pos.piece_count(weakerSide, PAWN) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN, 0); Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
Square wpsq2 = pos.piece_list(strongerSide, PAWN, 1); Square wpsq2 = pos.piece_list(strongerSide, PAWN)[1];
Square bksq = pos.king_square(weakerSide); Square bksq = pos.king_square(weakerSide);
// Does the stronger side have a passed pawn? // Does the stronger side have a passed pawn?
@@ -646,7 +608,7 @@ ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const {
|| pos.pawn_is_passed(strongerSide, wpsq2)) || pos.pawn_is_passed(strongerSide, wpsq2))
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Rank r = Max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2)); Rank r = std::max(relative_rank(strongerSide, wpsq1), relative_rank(strongerSide, wpsq2));
if ( file_distance(bksq, wpsq1) <= 1 if ( file_distance(bksq, wpsq1) <= 1
&& file_distance(bksq, wpsq2) <= 1 && file_distance(bksq, wpsq2) <= 1
@@ -665,11 +627,10 @@ ScaleFactor Endgame<ScaleFactor, KRPPKRP>::apply(const Position& pos) const {
} }
/// KPsKScalingFunction scales endgames with king and two or more pawns /// K and two or more pawns vs K. There is just a single rule here: If all pawns
/// against king. There is just a single rule here: If all pawns are on /// are on the same rook file and are blocked by the defending king, it's a draw.
/// the same rook file and are blocked by the defending king, it's a draw.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KPsK>::apply(const Position& pos) const { ScaleFactor Endgame<KPsK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.piece_count(strongerSide, PAWN) >= 2); assert(pos.piece_count(strongerSide, PAWN) >= 2);
@@ -680,34 +641,33 @@ ScaleFactor Endgame<ScaleFactor, KPsK>::apply(const Position& pos) const {
Bitboard pawns = pos.pieces(PAWN, strongerSide); Bitboard pawns = pos.pieces(PAWN, strongerSide);
// Are all pawns on the 'a' file? // Are all pawns on the 'a' file?
if ((pawns & ~FileABB) == EmptyBoardBB) if (!(pawns & ~FileABB))
{ {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1 if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( square_file(ksq) == FILE_A || ( file_of(ksq) == FILE_A
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)) && !in_front_bb(strongerSide, ksq) & pawns))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
} }
// Are all pawns on the 'h' file? // Are all pawns on the 'h' file?
else if ((pawns & ~FileHBB) == EmptyBoardBB) else if (!(pawns & ~FileHBB))
{ {
// Does the defending king block the pawns? // Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1 if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( square_file(ksq) == FILE_H || ( file_of(ksq) == FILE_H
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB)) && !in_front_bb(strongerSide, ksq) & pawns))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
/// KBPKBScalingFunction scales KBP vs KB endgames. There are two rules: /// K, bishop and a pawn vs K and a bishop. There are two rules: If the defending
/// If the defending king is somewhere along the path of the pawn, and the /// king is somewhere along the path of the pawn, and the square of the king is
/// square of the king is not of the same color as the stronger side's bishop, /// not of the same color as the stronger side's bishop, it's a draw. If the two
/// it's a draw. If the two bishops have opposite color, it's almost always /// bishops have opposite color, it's almost always a draw.
/// a draw.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const { ScaleFactor Endgame<KBPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -716,20 +676,20 @@ ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0); Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0); Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP, 0); Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
// Case 1: Defending king blocks the pawn, and cannot be driven away // Case 1: Defending king blocks the pawn, and cannot be driven away
if ( square_file(weakerKingSq) == square_file(pawnSq) if ( file_of(weakerKingSq) == file_of(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq) && ( opposite_colors(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6)) || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
// Case 2: Opposite colored bishops // Case 2: Opposite colored bishops
if (opposite_color_squares(strongerBishopSq, weakerBishopSq)) if (opposite_colors(strongerBishopSq, weakerBishopSq))
{ {
// We assume that the position is drawn in the following three situations: // We assume that the position is drawn in the following three situations:
// //
@@ -742,27 +702,27 @@ ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
// reasonably well. // reasonably well.
if (relative_rank(strongerSide, pawnSq) <= RANK_5) if (relative_rank(strongerSide, pawnSq) <= RANK_5)
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
else else
{ {
Bitboard path = squares_in_front_of(strongerSide, pawnSq); Bitboard path = squares_in_front_of(strongerSide, pawnSq);
if (path & pos.pieces(KING, weakerSide)) if (path & pos.pieces(KING, weakerSide))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path) if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
&& square_distance(weakerBishopSq, pawnSq) >= 3) && square_distance(weakerBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
} }
} }
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
/// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic /// K, bishop and two pawns vs K and bishop. It detects a few basic draws with
/// draws with opposite-colored bishops. /// opposite-colored bishops.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const { ScaleFactor Endgame<KBPPKB>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -771,28 +731,28 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, BISHOP) == 1); assert(pos.piece_count(weakerSide, BISHOP) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP, 0); Square wbsq = pos.piece_list(strongerSide, BISHOP)[0];
Square bbsq = pos.piece_list(weakerSide, BISHOP, 0); Square bbsq = pos.piece_list(weakerSide, BISHOP)[0];
if (!opposite_color_squares(wbsq, bbsq)) if (!opposite_colors(wbsq, bbsq))
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
Square ksq = pos.king_square(weakerSide); Square ksq = pos.king_square(weakerSide);
Square psq1 = pos.piece_list(strongerSide, PAWN, 0); Square psq1 = pos.piece_list(strongerSide, PAWN)[0];
Square psq2 = pos.piece_list(strongerSide, PAWN, 1); Square psq2 = pos.piece_list(strongerSide, PAWN)[1];
Rank r1 = square_rank(psq1); Rank r1 = rank_of(psq1);
Rank r2 = square_rank(psq2); Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2; Square blockSq1, blockSq2;
if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2)) if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
{ {
blockSq1 = psq1 + pawn_push(strongerSide); blockSq1 = psq1 + pawn_push(strongerSide);
blockSq2 = make_square(square_file(psq2), square_rank(psq1)); blockSq2 = make_square(file_of(psq2), rank_of(psq1));
} }
else else
{ {
blockSq1 = psq2 + pawn_push(strongerSide); blockSq1 = psq2 + pawn_push(strongerSide);
blockSq2 = make_square(square_file(psq1), square_rank(psq2)); blockSq2 = make_square(file_of(psq1), rank_of(psq2));
} }
switch (file_distance(psq1, psq2)) switch (file_distance(psq1, psq2))
@@ -800,10 +760,10 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
case 0: case 0:
// Both pawns are on the same file. Easy draw if defender firmly controls // Both pawns are on the same file. Easy draw if defender firmly controls
// some square in the frontmost pawn's path. // some square in the frontmost pawn's path.
if ( square_file(ksq) == square_file(blockSq1) if ( file_of(ksq) == file_of(blockSq1)
&& relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1) && relative_rank(strongerSide, ksq) >= relative_rank(strongerSide, blockSq1)
&& opposite_color_squares(ksq, wbsq)) && opposite_colors(ksq, wbsq))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
else else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -812,17 +772,17 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
// in front of the frontmost pawn's path, and the square diagonally behind // in front of the frontmost pawn's path, and the square diagonally behind
// this square on the file of the other pawn. // this square on the file of the other pawn.
if ( ksq == blockSq1 if ( ksq == blockSq1
&& opposite_color_squares(ksq, wbsq) && opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2 && ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide)) || (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| abs(r1 - r2) >= 2)) || abs(r1 - r2) >= 2))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2 else if ( ksq == blockSq2
&& opposite_color_squares(ksq, wbsq) && opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1 && ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide)))) || (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
else else
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
@@ -833,12 +793,11 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
} }
/// KBPKNScalingFunction scales KBP vs KN endgames. There is a single rule: /// K, bisop and a pawn vs K and knight. There is a single rule: If the defending
/// If the defending king is somewhere along the path of the pawn, and the /// king is somewhere along the path of the pawn, and the square of the king is
/// square of the king is not of the same color as the stronger side's bishop, /// not of the same color as the stronger side's bishop, it's a draw.
/// it's a draw.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KBPKN>::apply(const Position& pos) const { ScaleFactor Endgame<KBPKN>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame); assert(pos.non_pawn_material(strongerSide) == BishopValueMidgame);
assert(pos.piece_count(strongerSide, BISHOP) == 1); assert(pos.piece_count(strongerSide, BISHOP) == 1);
@@ -847,25 +806,25 @@ ScaleFactor Endgame<ScaleFactor, KBPKN>::apply(const Position& pos) const {
assert(pos.piece_count(weakerSide, KNIGHT) == 1); assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0); Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0); Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
if ( square_file(weakerKingSq) == square_file(pawnSq) if ( file_of(weakerKingSq) == file_of(pawnSq)
&& relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq) && relative_rank(strongerSide, pawnSq) < relative_rank(strongerSide, weakerKingSq)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq) && ( opposite_colors(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6)) || relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
/// KNPKScalingFunction scales KNP vs K endgames. There is a single rule: /// K, knight and a pawn vs K. There is a single rule: If the pawn is a rook pawn
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents /// on the 7th rank and the defending king prevents the pawn from advancing, the
/// the pawn from advancing, the position is drawn. /// position is drawn.
template<> template<>
ScaleFactor Endgame<ScaleFactor, KNPK>::apply(const Position& pos) const { ScaleFactor Endgame<KNPK>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame); assert(pos.non_pawn_material(strongerSide) == KnightValueMidgame);
assert(pos.piece_count(strongerSide, KNIGHT) == 1); assert(pos.piece_count(strongerSide, KNIGHT) == 1);
@@ -873,67 +832,61 @@ ScaleFactor Endgame<ScaleFactor, KNPK>::apply(const Position& pos) const {
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(weakerSide, PAWN) == 0); assert(pos.piece_count(weakerSide, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0); Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square weakerKingSq = pos.king_square(weakerSide); Square weakerKingSq = pos.king_square(weakerSide);
if ( pawnSq == relative_square(strongerSide, SQ_A7) if ( pawnSq == relative_square(strongerSide, SQ_A7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1) && square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
if ( pawnSq == relative_square(strongerSide, SQ_H7) if ( pawnSq == relative_square(strongerSide, SQ_H7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1) && square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
return SCALE_FACTOR_ZERO; return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
} }
/// KPKPScalingFunction scales KP vs KP endgames. This is done by removing /// K and a pawn vs K and a pawn. This is done by removing the weakest side's
/// the weakest side's pawn and probing the KP vs K bitbase: If the weakest /// pawn and probing the KP vs K bitbase: If the weakest side has a draw without
/// side has a draw without the pawn, she probably has at least a draw with /// the pawn, she probably has at least a draw with the pawn as well. The exception
/// the pawn as well. The exception is when the stronger side's pawn is far /// is when the stronger side's pawn is far advanced and not on a rook file; in
/// advanced and not on a rook file; in this case it is often possible to win /// this case it is often possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
template<> template<>
ScaleFactor Endgame<ScaleFactor, KPKP>::apply(const Position& pos) const { ScaleFactor Endgame<KPKP>::operator()(const Position& pos) const {
assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO); assert(pos.non_pawn_material(strongerSide) == VALUE_ZERO);
assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO); assert(pos.non_pawn_material(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1); assert(pos.piece_count(WHITE, PAWN) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1); assert(pos.piece_count(BLACK, PAWN) == 1);
Square wksq, bksq, wpsq; Square wksq = pos.king_square(strongerSide);
Color stm; Square bksq = pos.king_square(weakerSide);
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
Color stm = pos.side_to_move();
if (strongerSide == WHITE) if (strongerSide == BLACK)
{ {
wksq = pos.king_square(WHITE); wksq = flip(wksq);
bksq = pos.king_square(BLACK); bksq = flip(bksq);
wpsq = pos.piece_list(WHITE, PAWN, 0); wpsq = flip(wpsq);
stm = pos.side_to_move(); stm = flip(stm);
}
else
{
wksq = flip_square(pos.king_square(BLACK));
bksq = flip_square(pos.king_square(WHITE));
wpsq = flip_square(pos.piece_list(BLACK, PAWN, 0));
stm = opposite_color(pos.side_to_move());
} }
if (square_file(wpsq) >= FILE_E) if (file_of(wpsq) >= FILE_E)
{ {
wksq = flop_square(wksq); wksq = mirror(wksq);
bksq = flop_square(bksq); bksq = mirror(bksq);
wpsq = flop_square(wpsq); wpsq = mirror(wpsq);
} }
// If the pawn has advanced to the fifth rank or further, and is not a // If the pawn has advanced to the fifth rank or further, and is not a
// rook pawn, it's too dangerous to assume that it's at least a draw. // rook pawn, it's too dangerous to assume that it's at least a draw.
if ( square_rank(wpsq) >= RANK_5 if ( rank_of(wpsq) >= RANK_5
&& square_file(wpsq) != FILE_A) && file_of(wpsq) != FILE_A)
return SCALE_FACTOR_NONE; return SCALE_FACTOR_NONE;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a // Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// draw, it's probably at least a draw even with the pawn. // it's probably at least a draw even with the pawn.
return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_ZERO; return probe_kpk_bitbase(wksq, wpsq, bksq, stm) ? SCALE_FACTOR_NONE : SCALE_FACTOR_DRAW;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,8 +20,8 @@
#if !defined(ENDGAME_H_INCLUDED) #if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED #define ENDGAME_H_INCLUDED
#include <string>
#include <map> #include <map>
#include <string>
#include "position.h" #include "position.h"
#include "types.h" #include "types.h"
@@ -46,6 +46,7 @@ enum EndgameType {
// Scaling functions // Scaling functions
SCALE_FUNS,
KBPsK, // KB+pawns vs K KBPsK, // KB+pawns vs K
KQKRPs, // KQ vs KR+pawns KQKRPs, // KQ vs KR+pawns
@@ -60,25 +61,30 @@ enum EndgameType {
}; };
/// Some magic to detect family type of endgame from its enum value
template<bool> struct bool_to_type { typedef Value type; };
template<> struct bool_to_type<true> { typedef ScaleFactor type; };
template<EndgameType E> struct eg_family : public bool_to_type<(E > SCALE_FUNS)> {};
/// Base and derived templates for endgame evaluation and scaling functions /// Base and derived templates for endgame evaluation and scaling functions
template<typename T> template<typename T>
struct EndgameBase { struct EndgameBase {
typedef EndgameBase<T> Base;
virtual ~EndgameBase() {} virtual ~EndgameBase() {}
virtual Color color() const = 0; virtual Color color() const = 0;
virtual T apply(const Position&) const = 0; virtual T operator()(const Position&) const = 0;
}; };
template<typename T, EndgameType> template<EndgameType E, typename T = typename eg_family<E>::type>
struct Endgame : public EndgameBase<T> { struct Endgame : public EndgameBase<T> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {} explicit Endgame(Color c) : strongerSide(c), weakerSide(flip(c)) {}
Color color() const { return strongerSide; } Color color() const { return strongerSide; }
T apply(const Position&) const; T operator()(const Position&) const;
private: private:
Color strongerSide, weakerSide; Color strongerSide, weakerSide;
@@ -87,26 +93,28 @@ private:
/// Endgames class stores in two std::map the pointers to endgame evaluation /// Endgames class stores in two std::map the pointers to endgame evaluation
/// and scaling base objects. Then we use polymorphism to invoke the actual /// and scaling base objects. Then we use polymorphism to invoke the actual
/// endgame function calling its apply() method that is virtual. /// endgame function calling its operator() method that is virtual.
class Endgames { class Endgames {
typedef std::map<Key, EndgameBase<Value>* > EFMap; typedef std::map<Key, EndgameBase<Value>*> M1;
typedef std::map<Key, EndgameBase<ScaleFactor>* > SFMap; typedef std::map<Key, EndgameBase<ScaleFactor>*> M2;
M1 m1;
M2 m2;
M1& map(Value*) { return m1; }
M2& map(ScaleFactor*) { return m2; }
template<EndgameType E> void add(const std::string& code);
public: public:
Endgames(); Endgames();
~Endgames(); ~Endgames();
template<class T> T* get(Key key) const;
private: template<typename T> EndgameBase<T>* get(Key key) {
template<class T> void add(const std::string& keyCode); return map((T*)0).count(key) ? map((T*)0)[key] : NULL;
}
// Here we store two maps, for evaluate and scaling functions...
std::pair<EFMap, SFMap> maps;
// ...and here is the accessing template function
template<typename T> const std::map<Key, T*>& get() const;
}; };
#endif // !defined(ENDGAME_H_INCLUDED) #endif // !defined(ENDGAME_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <sstream> #include <sstream>
#include <algorithm>
#include "bitcount.h" #include "bitcount.h"
#include "evaluate.h" #include "evaluate.h"
@@ -44,20 +45,20 @@ namespace {
// all squares attacked by the given color. // all squares attacked by the given color.
Bitboard attackedBy[2][8]; Bitboard attackedBy[2][8];
// kingZone[color] is the zone around the enemy king which is considered // kingRing[color] is the zone around the king which is considered
// by the king safety evaluation. This consists of the squares directly // by the king safety evaluation. This consists of the squares directly
// adjacent to the king, and the three (or two, for a king on an edge file) // adjacent to the king, and the three (or two, for a king on an edge file)
// squares two ranks in front of the king. For instance, if black's king // squares two ranks in front of the king. For instance, if black's king
// is on g8, kingZone[WHITE] is a bitboard containing the squares f8, h8, // is on g8, kingRing[BLACK] is a bitboard containing the squares f8, h8,
// f7, g7, h7, f6, g6 and h6. // f7, g7, h7, f6, g6 and h6.
Bitboard kingZone[2]; Bitboard kingRing[2];
// kingAttackersCount[color] is the number of pieces of the given color // kingAttackersCount[color] is the number of pieces of the given color
// which attack a square in the kingZone of the enemy king. // which attack a square in the kingRing of the enemy king.
int kingAttackersCount[2]; int kingAttackersCount[2];
// kingAttackersWeight[color] is the sum of the "weight" of the pieces of the // kingAttackersWeight[color] is the sum of the "weight" of the pieces of the
// given color which attack a square in the kingZone of the enemy king. The // given color which attack a square in the kingRing of the enemy king. The
// weights of the individual piece types are given by the variables // weights of the individual piece types are given by the variables
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and // QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp // KnightAttackWeight in evaluate.cpp
@@ -88,7 +89,7 @@ namespace {
// //
// Values modified by Joona Kiiski // Values modified by Joona Kiiski
const Score WeightsInternal[] = { const Score WeightsInternal[] = {
S(248, 271), S(252, 259), S(46, 0), S(247, 0), S(259, 0) S(252, 344), S(216, 266), S(46, 0), S(247, 0), S(259, 0)
}; };
// MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and // MobilityBonus[PieceType][attacked] contains mobility bonuses for middle and
@@ -224,28 +225,27 @@ namespace {
}; };
// Function prototypes // Function prototypes
template<bool HasPopCnt, bool Trace> template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin); Value do_evaluate(const Position& pos, Value& margin);
template<Color Us, bool HasPopCnt> template<Color Us>
void init_eval_info(const Position& pos, EvalInfo& ei); void init_eval_info(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt, bool Trace> template<Color Us, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility); Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility);
template<Color Us, bool HasPopCnt, bool Trace> template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]); Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]);
template<Color Us> template<Color Us>
Score evaluate_threats(const Position& pos, EvalInfo& ei); Score evaluate_threats(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt> template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei); int evaluate_space(const Position& pos, EvalInfo& ei);
template<Color Us> template<Color Us>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei); Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei); Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
inline Score apply_weight(Score v, Score weight); inline Score apply_weight(Score v, Score weight);
@@ -260,22 +260,17 @@ namespace {
/// evaluate() is the main evaluation function. It always computes two /// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates /// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material. /// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) { Value evaluate(const Position& pos, Value& margin) { return do_evaluate<false>(pos, margin); }
return CpuHasPOPCNT ? do_evaluate<true, false>(pos, margin)
: do_evaluate<false, false>(pos, margin);
}
namespace { namespace {
template<bool HasPopCnt, bool Trace> template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin) { Value do_evaluate(const Position& pos, Value& margin) {
EvalInfo ei; EvalInfo ei;
Value margins[2]; Value margins[2];
Score score, mobilityWhite, mobilityBlack; Score score, mobilityWhite, mobilityBlack;
assert(pos.is_ok());
assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS); assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS);
assert(!pos.in_check()); assert(!pos.in_check());
@@ -288,7 +283,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
margins[WHITE] = margins[BLACK] = VALUE_ZERO; margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// Probe the material hash table // Probe the material hash table
ei.mi = Threads[pos.thread()].materialTable.get_material_info(pos); ei.mi = Threads[pos.thread()].materialTable.material_info(pos);
score += ei.mi->material_value(); score += ei.mi->material_value();
// If we have a specialized evaluation function for the current material // If we have a specialized evaluation function for the current material
@@ -300,23 +295,23 @@ Value do_evaluate(const Position& pos, Value& margin) {
} }
// Probe the pawn hash table // Probe the pawn hash table
ei.pi = Threads[pos.thread()].pawnTable.get_pawn_info(pos); ei.pi = Threads[pos.thread()].pawnTable.pawn_info(pos);
score += ei.pi->pawns_value(); score += ei.pi->pawns_value();
// Initialize attack and king safety bitboards // Initialize attack and king safety bitboards
init_eval_info<WHITE, HasPopCnt>(pos, ei); init_eval_info<WHITE>(pos, ei);
init_eval_info<BLACK, HasPopCnt>(pos, ei); init_eval_info<BLACK>(pos, ei);
// Evaluate pieces and mobility // Evaluate pieces and mobility
score += evaluate_pieces_of_color<WHITE, HasPopCnt, Trace>(pos, ei, mobilityWhite) score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, HasPopCnt, Trace>(pos, ei, mobilityBlack); - evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobilityBlack);
score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]); score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
// Evaluate kings after all other pieces because we need complete attack // Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation. // information when computing the king safety evaluation.
score += evaluate_king<WHITE, HasPopCnt, Trace>(pos, ei, margins) score += evaluate_king<WHITE, Trace>(pos, ei, margins)
- evaluate_king<BLACK, HasPopCnt, Trace>(pos, ei, margins); - evaluate_king<BLACK, Trace>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king // Evaluate tactical threats, we need full attack information including king
score += evaluate_threats<WHITE>(pos, ei) score += evaluate_threats<WHITE>(pos, ei)
@@ -328,12 +323,12 @@ Value do_evaluate(const Position& pos, Value& margin) {
// If one side has only a king, check whether exists any unstoppable passed pawn // If one side has only a king, check whether exists any unstoppable passed pawn
if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK)) if (!pos.non_pawn_material(WHITE) || !pos.non_pawn_material(BLACK))
score += evaluate_unstoppable_pawns<HasPopCnt>(pos, ei); score += evaluate_unstoppable_pawns(pos, ei);
// Evaluate space for both sides, only in middle-game. // Evaluate space for both sides, only in middle-game.
if (ei.mi->space_weight()) if (ei.mi->space_weight())
{ {
int s = evaluate_space<WHITE, HasPopCnt>(pos, ei) - evaluate_space<BLACK, HasPopCnt>(pos, ei); int s = evaluate_space<WHITE>(pos, ei) - evaluate_space<BLACK>(pos, ei);
score += apply_weight(make_score(s * ei.mi->space_weight(), 0), Weights[Space]); score += apply_weight(make_score(s * ei.mi->space_weight(), 0), Weights[Space]);
} }
@@ -375,9 +370,9 @@ Value do_evaluate(const Position& pos, Value& margin) {
trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility])); trace_add(MOBILITY, apply_weight(mobilityWhite, Weights[Mobility]), apply_weight(mobilityBlack, Weights[Mobility]));
trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei)); trace_add(THREAT, evaluate_threats<WHITE>(pos, ei), evaluate_threats<BLACK>(pos, ei));
trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei)); trace_add(PASSED, evaluate_passed_pawns<WHITE>(pos, ei), evaluate_passed_pawns<BLACK>(pos, ei));
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns<false>(pos, ei)); trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE, false>(pos, ei), 0); Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE>(pos, ei), 0);
Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK, false>(pos, ei), 0); Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK>(pos, ei), 0);
trace_add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space])); trace_add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
trace_add(TOTAL, score); trace_add(TOTAL, score);
TraceStream << "\nUncertainty margin: White: " << to_cp(margins[WHITE]) TraceStream << "\nUncertainty margin: White: " << to_cp(margins[WHITE])
@@ -412,7 +407,7 @@ void read_evaluation_uci_options(Color us) {
// If running in analysis mode, make sure we use symmetrical king safety. We do this // If running in analysis mode, make sure we use symmetrical king safety. We do this
// by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average. // by replacing both Weights[kingDangerUs] and Weights[kingDangerThem] by their average.
if (Options["UCI_AnalyseMode"].value<bool>()) if (Options["UCI_AnalyseMode"])
Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2; Weights[kingDangerUs] = Weights[kingDangerThem] = (Weights[kingDangerUs] + Weights[kingDangerThem]) / 2;
init_safety(); init_safety();
@@ -424,10 +419,9 @@ namespace {
// init_eval_info() initializes king bitboards for given color adding // init_eval_info() initializes king bitboards for given color adding
// pawn attacks. To be done at the beginning of the evaluation. // pawn attacks. To be done at the beginning of the evaluation.
template<Color Us, bool HasPopCnt> template<Color Us>
void init_eval_info(const Position& pos, EvalInfo& ei) { void init_eval_info(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them)); Bitboard b = ei.attackedBy[Them][KING] = pos.attacks_from<KING>(pos.king_square(Them));
@@ -437,12 +431,12 @@ namespace {
if ( pos.piece_count(Us, QUEEN) if ( pos.piece_count(Us, QUEEN)
&& pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame) && pos.non_pawn_material(Us) >= QueenValueMidgame + RookValueMidgame)
{ {
ei.kingZone[Us] = (b | (Us == WHITE ? b >> 8 : b << 8)); ei.kingRing[Them] = (b | (Us == WHITE ? b >> 8 : b << 8));
b &= ei.attackedBy[Us][PAWN]; b &= ei.attackedBy[Us][PAWN];
ei.kingAttackersCount[Us] = b ? count_1s<Max15>(b) / 2 : 0; ei.kingAttackersCount[Us] = b ? popcount<Max15>(b) / 2 : 0;
ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0; ei.kingAdjacentZoneAttacksCount[Us] = ei.kingAttackersWeight[Us] = 0;
} else } else
ei.kingZone[Us] = ei.kingAttackersCount[Us] = 0; ei.kingRing[Them] = ei.kingAttackersCount[Us] = 0;
} }
@@ -462,8 +456,8 @@ namespace {
// no minor piece which can exchange the outpost piece. // no minor piece which can exchange the outpost piece.
if (bonus && bit_is_set(ei.attackedBy[Us][PAWN], s)) if (bonus && bit_is_set(ei.attackedBy[Us][PAWN], s))
{ {
if ( pos.pieces(KNIGHT, Them) == EmptyBoardBB if ( !pos.pieces(KNIGHT, Them)
&& (SquaresByColorBB[square_color(s)] & pos.pieces(BISHOP, Them)) == EmptyBoardBB) && !(same_color_squares(s) & pos.pieces(BISHOP, Them)))
bonus += bonus + bonus / 2; bonus += bonus + bonus / 2;
else else
bonus += bonus / 2; bonus += bonus / 2;
@@ -474,7 +468,7 @@ namespace {
// evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color // evaluate_pieces<>() assigns bonuses and penalties to the pieces of a given color
template<PieceType Piece, Color Us, bool HasPopCnt, bool Trace> template<PieceType Piece, Color Us, bool Trace>
Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) { Score evaluate_pieces(const Position& pos, EvalInfo& ei, Score& mobility, Bitboard mobilityArea) {
Bitboard b; Bitboard b;
@@ -483,14 +477,12 @@ namespace {
File f; File f;
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
const BitCountType Full = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64 : CNT32;
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Square* ptr = pos.piece_list_begin(Us, Piece); const Square* pl = pos.piece_list(Us, Piece);
ei.attackedBy[Us][Piece] = EmptyBoardBB; ei.attackedBy[Us][Piece] = 0;
while ((s = *ptr++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
{ {
// Find attacked squares, including x-ray attacks for bishops and rooks // Find attacked squares, including x-ray attacks for bishops and rooks
if (Piece == KNIGHT || Piece == QUEEN) if (Piece == KNIGHT || Piece == QUEEN)
@@ -506,18 +498,18 @@ namespace {
ei.attackedBy[Us][Piece] |= b; ei.attackedBy[Us][Piece] |= b;
// King attacks // King attacks
if (b & ei.kingZone[Us]) if (b & ei.kingRing[Them])
{ {
ei.kingAttackersCount[Us]++; ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece]; ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
Bitboard bb = (b & ei.attackedBy[Them][KING]); Bitboard bb = (b & ei.attackedBy[Them][KING]);
if (bb) if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += count_1s<Max15>(bb); ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
} }
// Mobility // Mobility
mob = (Piece != QUEEN ? count_1s<Max15>(b & mobilityArea) mob = (Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: count_1s<Full >(b & mobilityArea)); : popcount<Full >(b & mobilityArea));
mobility += MobilityBonus[Piece][mob]; mobility += MobilityBonus[Piece][mob];
@@ -527,7 +519,8 @@ namespace {
score -= ThreatenedByPawnPenalty[Piece]; score -= ThreatenedByPawnPenalty[Piece];
// Bishop and knight outposts squares // Bishop and knight outposts squares
if ((Piece == BISHOP || Piece == KNIGHT) && pos.square_is_weak(s, Us)) if ( (Piece == BISHOP || Piece == KNIGHT)
&& !(pos.pieces(PAWN, Them) & attack_span_mask(Us, s)))
score += evaluate_outposts<Piece, Us>(pos, ei, s); score += evaluate_outposts<Piece, Us>(pos, ei, s);
// Queen or rook on 7th rank // Queen or rook on 7th rank
@@ -546,7 +539,7 @@ namespace {
// problem, especially when that pawn is also blocked. // problem, especially when that pawn is also blocked.
if (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1)) if (s == relative_square(Us, SQ_A1) || s == relative_square(Us, SQ_H1))
{ {
Square d = pawn_push(Us) + (square_file(s) == FILE_A ? DELTA_E : DELTA_W); Square d = pawn_push(Us) + (file_of(s) == FILE_A ? DELTA_E : DELTA_W);
if (pos.piece_on(s + d) == make_piece(Us, PAWN)) if (pos.piece_on(s + d) == make_piece(Us, PAWN))
{ {
if (!pos.square_is_empty(s + d + pawn_push(Us))) if (!pos.square_is_empty(s + d + pawn_push(Us)))
@@ -563,7 +556,7 @@ namespace {
if (Piece == ROOK) if (Piece == ROOK)
{ {
// Open and half-open files // Open and half-open files
f = square_file(s); f = file_of(s);
if (ei.pi->file_is_half_open(Us, f)) if (ei.pi->file_is_half_open(Us, f))
{ {
if (ei.pi->file_is_half_open(Them, f)) if (ei.pi->file_is_half_open(Them, f))
@@ -579,21 +572,21 @@ namespace {
ksq = pos.king_square(Us); ksq = pos.king_square(Us);
if ( square_file(ksq) >= FILE_E if ( file_of(ksq) >= FILE_E
&& square_file(s) > square_file(ksq) && file_of(s) > file_of(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s))) && (relative_rank(Us, ksq) == RANK_1 || rank_of(ksq) == rank_of(s)))
{ {
// Is there a half-open file between the king and the edge of the board? // Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_right(Us, square_file(ksq))) if (!ei.pi->has_open_file_to_right(Us, file_of(ksq)))
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2 score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0); : (TrappedRookPenalty - mob * 16), 0);
} }
else if ( square_file(ksq) <= FILE_D else if ( file_of(ksq) <= FILE_D
&& square_file(s) < square_file(ksq) && file_of(s) < file_of(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s))) && (relative_rank(Us, ksq) == RANK_1 || rank_of(ksq) == rank_of(s)))
{ {
// Is there a half-open file between the king and the edge of the board? // Is there a half-open file between the king and the edge of the board?
if (!ei.pi->has_open_file_to_left(Us, square_file(ksq))) if (!ei.pi->has_open_file_to_left(Us, file_of(ksq)))
score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2 score -= make_score(pos.can_castle(Us) ? (TrappedRookPenalty - mob * 16) / 2
: (TrappedRookPenalty - mob * 16), 0); : (TrappedRookPenalty - mob * 16), 0);
} }
@@ -619,7 +612,7 @@ namespace {
Score score = SCORE_ZERO; Score score = SCORE_ZERO;
// Enemy pieces not defended by a pawn and under our attack // Enemy pieces not defended by a pawn and under our attack
Bitboard weakEnemies = pos.pieces_of_color(Them) Bitboard weakEnemies = pos.pieces(Them)
& ~ei.attackedBy[Them][PAWN] & ~ei.attackedBy[Them][PAWN]
& ei.attackedBy[Us][0]; & ei.attackedBy[Us][0];
if (!weakEnemies) if (!weakEnemies)
@@ -643,7 +636,7 @@ namespace {
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the // evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
// pieces of a given color. // pieces of a given color.
template<Color Us, bool HasPopCnt, bool Trace> template<Color Us, bool Trace>
Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) { Score evaluate_pieces_of_color(const Position& pos, EvalInfo& ei, Score& mobility) {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
@@ -651,12 +644,12 @@ namespace {
Score score = mobility = SCORE_ZERO; Score score = mobility = SCORE_ZERO;
// Do not include in mobility squares protected by enemy pawns or occupied by our pieces // Do not include in mobility squares protected by enemy pawns or occupied by our pieces
const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces_of_color(Us)); const Bitboard mobilityArea = ~(ei.attackedBy[Them][PAWN] | pos.pieces(Us));
score += evaluate_pieces<KNIGHT, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<BISHOP, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<ROOK, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<QUEEN, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea); score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares // Sum up all attacked squares
ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT] ei.attackedBy[Us][0] = ei.attackedBy[Us][PAWN] | ei.attackedBy[Us][KNIGHT]
@@ -668,10 +661,9 @@ namespace {
// evaluate_king<>() assigns bonuses and penalties to a king of a given color // evaluate_king<>() assigns bonuses and penalties to a king of a given color
template<Color Us, bool HasPopCnt, bool Trace> template<Color Us, bool Trace>
Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) { Score evaluate_king(const Position& pos, EvalInfo& ei, Value margins[]) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard undefended, b, b1, b2, safe; Bitboard undefended, b, b1, b2, safe;
@@ -698,14 +690,14 @@ namespace {
// the number and types of the enemy's attacking pieces, the number of // the number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king, the square of the // attacked and undefended squares around our king, the square of the
// king, and the quality of the pawn shelter. // king, and the quality of the pawn shelter.
attackUnits = Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2) attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + count_1s<Max15>(undefended)) + 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ InitKingDanger[relative_square(Us, ksq)] + InitKingDanger[relative_square(Us, ksq)]
- mg_value(ei.pi->king_shelter<Us>(pos, ksq)) / 32; - mg_value(ei.pi->king_shelter<Us>(pos, ksq)) / 32;
// Analyse enemy's safe queen contact checks. First find undefended // Analyse enemy's safe queen contact checks. First find undefended
// squares around the king attacked by enemy queen... // squares around the king attacked by enemy queen...
b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces_of_color(Them); b = undefended & ei.attackedBy[Them][QUEEN] & ~pos.pieces(Them);
if (b) if (b)
{ {
// ...then remove squares not supported by another enemy piece // ...then remove squares not supported by another enemy piece
@@ -713,13 +705,13 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]); | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b) if (b)
attackUnits += QueenContactCheckBonus attackUnits += QueenContactCheckBonus
* count_1s<Max15>(b) * popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1); * (Them == pos.side_to_move() ? 2 : 1);
} }
// Analyse enemy's safe rook contact checks. First find undefended // Analyse enemy's safe rook contact checks. First find undefended
// squares around the king attacked by enemy rooks... // squares around the king attacked by enemy rooks...
b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces_of_color(Them); b = undefended & ei.attackedBy[Them][ROOK] & ~pos.pieces(Them);
// Consider only squares where the enemy rook gives check // Consider only squares where the enemy rook gives check
b &= RookPseudoAttacks[ksq]; b &= RookPseudoAttacks[ksq];
@@ -731,12 +723,12 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]); | ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b) if (b)
attackUnits += RookContactCheckBonus attackUnits += RookContactCheckBonus
* count_1s<Max15>(b) * popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1); * (Them == pos.side_to_move() ? 2 : 1);
} }
// Analyse enemy's safe distance checks for sliders and knights // Analyse enemy's safe distance checks for sliders and knights
safe = ~(pos.pieces_of_color(Them) | ei.attackedBy[Us][0]); safe = ~(pos.pieces(Them) | ei.attackedBy[Us][0]);
b1 = pos.attacks_from<ROOK>(ksq) & safe; b1 = pos.attacks_from<ROOK>(ksq) & safe;
b2 = pos.attacks_from<BISHOP>(ksq) & safe; b2 = pos.attacks_from<BISHOP>(ksq) & safe;
@@ -744,25 +736,25 @@ namespace {
// Enemy queen safe checks // Enemy queen safe checks
b = (b1 | b2) & ei.attackedBy[Them][QUEEN]; b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b) if (b)
attackUnits += QueenCheckBonus * count_1s<Max15>(b); attackUnits += QueenCheckBonus * popcount<Max15>(b);
// Enemy rooks safe checks // Enemy rooks safe checks
b = b1 & ei.attackedBy[Them][ROOK]; b = b1 & ei.attackedBy[Them][ROOK];
if (b) if (b)
attackUnits += RookCheckBonus * count_1s<Max15>(b); attackUnits += RookCheckBonus * popcount<Max15>(b);
// Enemy bishops safe checks // Enemy bishops safe checks
b = b2 & ei.attackedBy[Them][BISHOP]; b = b2 & ei.attackedBy[Them][BISHOP];
if (b) if (b)
attackUnits += BishopCheckBonus * count_1s<Max15>(b); attackUnits += BishopCheckBonus * popcount<Max15>(b);
// Enemy knights safe checks // Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe; b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b) if (b)
attackUnits += KnightCheckBonus * count_1s<Max15>(b); attackUnits += KnightCheckBonus * popcount<Max15>(b);
// To index KingDangerTable[] attackUnits must be in [0, 99] range // To index KingDangerTable[] attackUnits must be in [0, 99] range
attackUnits = Min(99, Max(0, attackUnits)); attackUnits = std::min(99, std::max(0, attackUnits));
// Finally, extract the king danger score from the KingDangerTable[] // Finally, extract the king danger score from the KingDangerTable[]
// array and subtract the score from evaluation. Set also margins[] // array and subtract the score from evaluation. Set also margins[]
@@ -812,11 +804,11 @@ namespace {
Square blockSq = s + pawn_push(Us); Square blockSq = s + pawn_push(Us);
// Adjust bonus based on kings proximity // Adjust bonus based on kings proximity
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 6 * rr); ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * rr);
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 3 * rr); ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 2 * rr);
// If blockSq is not the queening square then consider also a second push // If blockSq is not the queening square then consider also a second push
if (square_rank(blockSq) != (Us == WHITE ? RANK_8 : RANK_1)) if (rank_of(blockSq) != (Us == WHITE ? RANK_8 : RANK_1))
ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr); ebonus -= Value(square_distance(pos.king_square(Us), blockSq + pawn_push(Us)) * rr);
// If the pawn is free to advance, increase bonus // If the pawn is free to advance, increase bonus
@@ -832,7 +824,7 @@ namespace {
&& (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s))) && (squares_in_front_of(Them, s) & pos.pieces(ROOK, QUEEN, Them) & pos.attacks_from<ROOK>(s)))
unsafeSquares = squaresToQueen; unsafeSquares = squaresToQueen;
else else
unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces_of_color(Them)); unsafeSquares = squaresToQueen & (ei.attackedBy[Them][0] | pos.pieces(Them));
// If there aren't enemy attacks or pieces along the path to queen give // If there aren't enemy attacks or pieces along the path to queen give
// huge bonus. Even bigger if we protect the pawn's path. // huge bonus. Even bigger if we protect the pawn's path.
@@ -844,19 +836,15 @@ namespace {
// If yes, big bonus (but smaller than when there are no enemy attacks), // If yes, big bonus (but smaller than when there are no enemy attacks),
// if no, somewhat smaller bonus. // if no, somewhat smaller bonus.
ebonus += Value(rr * ((unsafeSquares & defendedSquares) == unsafeSquares ? 13 : 8)); ebonus += Value(rr * ((unsafeSquares & defendedSquares) == unsafeSquares ? 13 : 8));
// At last, add a small bonus when there are no *friendly* pieces
// in the pawn's path.
if (!(squaresToQueen & pos.pieces_of_color(Us)))
ebonus += Value(rr);
} }
} // rr != 0 } // rr != 0
// Increase the bonus if the passed pawn is supported by a friendly pawn // Increase the bonus if the passed pawn is supported by a friendly pawn
// on the same rank and a bit smaller if it's on the previous rank. // on the same rank and a bit smaller if it's on the previous rank.
supportingPawns = pos.pieces(PAWN, Us) & neighboring_files_bb(s); supportingPawns = pos.pieces(PAWN, Us) & neighboring_files_bb(file_of(s));
if (supportingPawns & rank_bb(s)) if (supportingPawns & rank_bb(s))
ebonus += Value(r * 20); ebonus += Value(r * 20);
else if (supportingPawns & rank_bb(s - pawn_push(Us))) else if (supportingPawns & rank_bb(s - pawn_push(Us)))
ebonus += Value(r * 12); ebonus += Value(r * 12);
@@ -866,7 +854,7 @@ namespace {
// we try the following: Increase the value for rook pawns if the // we try the following: Increase the value for rook pawns if the
// other side has no pieces apart from a knight, and decrease the // other side has no pieces apart from a knight, and decrease the
// value if the other side has a rook or queen. // value if the other side has a rook or queen.
if (square_file(s) == FILE_A || square_file(s) == FILE_H) if (file_of(s) == FILE_A || file_of(s) == FILE_H)
{ {
if (pos.non_pawn_material(Them) <= KnightValueMidgame) if (pos.non_pawn_material(Them) <= KnightValueMidgame)
ebonus += ebonus / 4; ebonus += ebonus / 4;
@@ -885,11 +873,8 @@ namespace {
// evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite // evaluate_unstoppable_pawns() evaluates the unstoppable passed pawns for both sides, this is quite
// conservative and returns a winning score only when we are very sure that the pawn is winning. // conservative and returns a winning score only when we are very sure that the pawn is winning.
template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) { Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
Bitboard b, b2, blockers, supporters, queeningPath, candidates; Bitboard b, b2, blockers, supporters, queeningPath, candidates;
Square s, blockSq, queeningSquare; Square s, blockSq, queeningSquare;
Color c, winnerSide, loserSide; Color c, winnerSide, loserSide;
@@ -902,7 +887,7 @@ namespace {
for (c = WHITE; c <= BLACK; c++) for (c = WHITE; c <= BLACK; c++)
{ {
// Skip if other side has non-pawn pieces // Skip if other side has non-pawn pieces
if (pos.non_pawn_material(opposite_color(c))) if (pos.non_pawn_material(flip(c)))
continue; continue;
b = ei.pi->passed_pawns(c); b = ei.pi->passed_pawns(c);
@@ -910,12 +895,12 @@ namespace {
while (b) while (b)
{ {
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
queeningSquare = relative_square(c, make_square(square_file(s), RANK_8)); queeningSquare = relative_square(c, make_square(file_of(s), RANK_8));
queeningPath = squares_in_front_of(c, s); queeningPath = squares_in_front_of(c, s);
// Compute plies to queening and check direct advancement // Compute plies to queening and check direct advancement
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2); movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(c, s) == RANK_2);
oppMovesToGo = square_distance(pos.king_square(opposite_color(c)), queeningSquare) - int(c != pos.side_to_move()); oppMovesToGo = square_distance(pos.king_square(flip(c)), queeningSquare) - int(c != pos.side_to_move());
pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath); pathDefended = ((ei.attackedBy[c][0] & queeningPath) == queeningPath);
if (movesToGo >= oppMovesToGo && !pathDefended) if (movesToGo >= oppMovesToGo && !pathDefended)
@@ -924,16 +909,16 @@ namespace {
// Opponent king cannot block because path is defended and position // Opponent king cannot block because path is defended and position
// is not in check. So only friendly pieces can be blockers. // is not in check. So only friendly pieces can be blockers.
assert(!pos.in_check()); assert(!pos.in_check());
assert((queeningPath & pos.occupied_squares()) == (queeningPath & pos.pieces_of_color(c))); assert((queeningPath & pos.occupied_squares()) == (queeningPath & pos.pieces(c)));
// Add moves needed to free the path from friendly pieces and retest condition // Add moves needed to free the path from friendly pieces and retest condition
movesToGo += count_1s<Max15>(queeningPath & pos.pieces_of_color(c)); movesToGo += popcount<Max15>(queeningPath & pos.pieces(c));
if (movesToGo >= oppMovesToGo && !pathDefended) if (movesToGo >= oppMovesToGo && !pathDefended)
continue; continue;
pliesToGo = 2 * movesToGo - int(c == pos.side_to_move()); pliesToGo = 2 * movesToGo - int(c == pos.side_to_move());
pliesToQueen[c] = Min(pliesToQueen[c], pliesToGo); pliesToQueen[c] = std::min(pliesToQueen[c], pliesToGo);
} }
} }
@@ -943,7 +928,7 @@ namespace {
return SCORE_ZERO; return SCORE_ZERO;
winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK); winnerSide = (pliesToQueen[WHITE] < pliesToQueen[BLACK] ? WHITE : BLACK);
loserSide = opposite_color(winnerSide); loserSide = flip(winnerSide);
// Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss? // Step 3. Can the losing side possibly create a new passed pawn and thus prevent the loss?
b = candidates = pos.pieces(PAWN, loserSide); b = candidates = pos.pieces(PAWN, loserSide);
@@ -953,7 +938,7 @@ namespace {
s = pop_1st_bit(&b); s = pop_1st_bit(&b);
// Compute plies from queening // Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(square_file(s), RANK_8)); queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
@@ -977,12 +962,12 @@ namespace {
minKingDist = kingptg = 256; minKingDist = kingptg = 256;
// Compute plies from queening // Compute plies from queening
queeningSquare = relative_square(loserSide, make_square(square_file(s), RANK_8)); queeningSquare = relative_square(loserSide, make_square(file_of(s), RANK_8));
movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2); movesToGo = rank_distance(s, queeningSquare) - int(relative_rank(loserSide, s) == RANK_2);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move()); pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// Generate list of blocking pawns and supporters // Generate list of blocking pawns and supporters
supporters = neighboring_files_bb(s) & candidates; supporters = neighboring_files_bb(file_of(s)) & candidates;
opposed = squares_in_front_of(loserSide, s) & pos.pieces(PAWN, winnerSide); opposed = squares_in_front_of(loserSide, s) & pos.pieces(PAWN, winnerSide);
blockers = passed_pawn_mask(loserSide, s) & pos.pieces(PAWN, winnerSide); blockers = passed_pawn_mask(loserSide, s) & pos.pieces(PAWN, winnerSide);
@@ -1003,7 +988,7 @@ namespace {
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{ {
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2; d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
movesToGo = Min(movesToGo, d); movesToGo = std::min(movesToGo, d);
} }
} }
@@ -1013,7 +998,7 @@ namespace {
while (b2) // This while-loop could be replaced with LSB/MSB (depending on color) while (b2) // This while-loop could be replaced with LSB/MSB (depending on color)
{ {
d = square_distance(blockSq, pop_1st_bit(&b2)) - 2; d = square_distance(blockSq, pop_1st_bit(&b2)) - 2;
movesToGo = Min(movesToGo, d); movesToGo = std::min(movesToGo, d);
} }
// If obstacle can be destroyed with an immediate pawn exchange / sacrifice, // If obstacle can be destroyed with an immediate pawn exchange / sacrifice,
@@ -1027,7 +1012,7 @@ namespace {
// Plies needed for the king to capture all the blocking pawns // Plies needed for the king to capture all the blocking pawns
d = square_distance(pos.king_square(loserSide), blockSq); d = square_distance(pos.king_square(loserSide), blockSq);
minKingDist = Min(minKingDist, d); minKingDist = std::min(minKingDist, d);
kingptg = (minKingDist + blockersCount) * 2; kingptg = (minKingDist + blockersCount) * 2;
} }
@@ -1052,10 +1037,9 @@ namespace {
// squares one, two or three squares behind a friendly pawn are counted // 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 // twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table. The aim is to improve play on game opening. // material hash table. The aim is to improve play on game opening.
template<Color Us, bool HasPopCnt> template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei) { int evaluate_space(const Position& pos, EvalInfo& ei) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
// Find the safe squares for our pieces inside the area defined by // Find the safe squares for our pieces inside the area defined by
@@ -1071,7 +1055,7 @@ namespace {
behind |= (Us == WHITE ? behind >> 8 : behind << 8); behind |= (Us == WHITE ? behind >> 8 : behind << 8);
behind |= (Us == WHITE ? behind >> 16 : behind << 16); behind |= (Us == WHITE ? behind >> 16 : behind << 16);
return count_1s<Max15>(safe) + count_1s<Max15>(behind & safe); return popcount<Max15>(safe) + popcount<Max15>(behind & safe);
} }
@@ -1104,8 +1088,8 @@ namespace {
Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) { Score weight_option(const std::string& mgOpt, const std::string& egOpt, Score internalWeight) {
// Scale option value from 100 to 256 // Scale option value from 100 to 256
int mg = Options[mgOpt].value<int>() * 256 / 100; int mg = Options[mgOpt] * 256 / 100;
int eg = Options[egOpt].value<int>() * 256 / 100; int eg = Options[egOpt] * 256 / 100;
return apply_weight(make_score(mg, eg), internalWeight); return apply_weight(make_score(mg, eg), internalWeight);
} }
@@ -1126,9 +1110,9 @@ namespace {
t[i] = Value(int(0.4 * i * i)); t[i] = Value(int(0.4 * i * i));
if (i > 0) if (i > 0)
t[i] = Min(t[i], t[i - 1] + MaxSlope); t[i] = std::min(t[i], t[i - 1] + MaxSlope);
t[i] = Min(t[i], Peak); t[i] = std::min(t[i], Peak);
} }
// Then apply the weights and get the final KingDangerTable[] array // Then apply the weights and get the final KingDangerTable[] array
@@ -1190,7 +1174,7 @@ std::string trace_evaluate(const Position& pos) {
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2); TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score)); memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<false, true>(pos, margin); do_evaluate<true>(pos, margin);
totals = TraceStream.str(); totals = TraceStream.str();
TraceStream.str(""); TraceStream.str("");

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,8 +20,9 @@
#if !defined(HISTORY_H_INCLUDED) #if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED #define HISTORY_H_INCLUDED
#include <cstring>
#include "types.h" #include "types.h"
#include <cstring>
#include <algorithm>
/// The History class stores statistics about how often different moves /// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These /// have been successful or unsuccessful during the current search. These
@@ -35,7 +36,7 @@ class History {
public: public:
void clear(); void clear();
Value value(Piece p, Square to) const; Value value(Piece p, Square to) const;
void update(Piece p, Square to, Value bonus); void add(Piece p, Square to, Value bonus);
Value gain(Piece p, Square to) const; Value gain(Piece p, Square to) const;
void update_gain(Piece p, Square to, Value g); void update_gain(Piece p, Square to, Value g);
@@ -55,7 +56,7 @@ inline Value History::value(Piece p, Square to) const {
return history[p][to]; return history[p][to];
} }
inline void History::update(Piece p, Square to, Value bonus) { inline void History::add(Piece p, Square to, Value bonus) {
if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus; if (abs(history[p][to] + bonus) < MaxValue) history[p][to] += bonus;
} }
@@ -64,7 +65,7 @@ inline Value History::gain(Piece p, Square to) const {
} }
inline void History::update_gain(Piece p, Square to, Value g) { inline void History::update_gain(Piece p, Square to, Value g) {
maxGains[p][to] = Max(g, maxGains[p][to] - 1); maxGains[p][to] = std::max(g, maxGains[p][to] - 1);
} }
#endif // !defined(HISTORY_H_INCLUDED) #endif // !defined(HISTORY_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -35,12 +35,15 @@ typedef pthread_cond_t WaitCondition;
# define cond_init(x) pthread_cond_init(x, NULL) # define cond_init(x) pthread_cond_init(x, NULL)
# define cond_signal(x) pthread_cond_signal(x) # define cond_signal(x) pthread_cond_signal(x)
# define cond_wait(x,y) pthread_cond_wait(x,y) # define cond_wait(x,y) pthread_cond_wait(x,y)
# define cond_timedwait(x,y,z) pthread_cond_timedwait(x,y,z)
#else #else
#define NOMINMAX // disable macros min() and max()
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#undef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// Default fast and race free locks and condition variables // Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS) #if !defined(OLD_LOCKS)
@@ -55,7 +58,8 @@ typedef CONDITION_VARIABLE WaitCondition;
# define cond_destroy(x) (x) # define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(x) # define cond_init(x) InitializeConditionVariable(x)
# define cond_signal(x) WakeConditionVariable(x) # define cond_signal(x) WakeConditionVariable(x)
# define cond_wait(x,y) SleepConditionVariableSRW(x, y, INFINITE,0) # define cond_wait(x,y) SleepConditionVariableSRW(x,y,INFINITE,0)
# define cond_timedwait(x,y,z) SleepConditionVariableSRW(x,y,z,0)
// Fallback solution to build for Windows XP and older versions, note that // Fallback solution to build for Windows XP and older versions, note that
// cond_wait() is racy between lock_release() and WaitForSingleObject(). // cond_wait() is racy between lock_release() and WaitForSingleObject().
@@ -71,7 +75,9 @@ typedef HANDLE WaitCondition;
# define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); } # define cond_init(x) { *x = CreateEvent(0, FALSE, FALSE, 0); }
# define cond_destroy(x) CloseHandle(*x) # define cond_destroy(x) CloseHandle(*x)
# define cond_signal(x) SetEvent(*x) # define cond_signal(x) SetEvent(*x)
# define cond_wait(x,y) { lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); } # define cond_wait(x,y) { ResetEvent(*x); lock_release(y); WaitForSingleObject(*x, INFINITE); lock_grab(y); }
# define cond_timedwait(x,y,z) { ResetEvent(*x); lock_release(y); WaitForSingleObject(*x,z); lock_grab(y); }
#endif #endif
#endif #endif

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,71 +17,41 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// To profile with callgrind uncomment following line
//#define USE_CALLGRIND
#include <cstdio>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include "bitboard.h" #include "bitboard.h"
#include "evaluate.h" #include "misc.h"
#include "position.h" #include "position.h"
#include "thread.h"
#include "search.h" #include "search.h"
#include "ucioption.h" #include "thread.h"
#ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
using namespace std; using namespace std;
extern bool execute_uci_command(const string& cmd); extern void uci_loop();
extern void benchmark(int argc, char* argv[]); extern void benchmark(int argc, char* argv[]);
extern void init_kpk_bitbase(); extern void kpk_bitbase_init();
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
// Disable IO buffering for C and C++ standard libraries bitboards_init();
setvbuf(stdin, NULL, _IONBF, 0); Position::init();
setvbuf(stdout, NULL, _IONBF, 0); kpk_bitbase_init();
cout.rdbuf()->pubsetbuf(NULL, 0); Search::init();
cin.rdbuf()->pubsetbuf(NULL, 0);
// Startup initializations
init_bitboards();
Position::init_zobrist();
Position::init_piece_square_tables();
init_kpk_bitbase();
init_search();
Threads.init(); Threads.init();
#ifdef USE_CALLGRIND cout << engine_info() << endl;
CALLGRIND_START_INSTRUMENTATION;
#endif
if (argc < 2) if (argc == 1)
{ uci_loop();
// Print copyright notice
cout << engine_name() << " by " << engine_authors() << endl;
if (CpuHasPOPCNT) else if (string(argv[1]) == "bench")
cout << "Good! CPU has hardware POPCNT." << endl;
// Wait for a command from the user, and passes this command to
// execute_uci_command() and also intercepts EOF from stdin to
// ensure that we exit gracefully if the GUI dies unexpectedly.
string cmd;
while (getline(cin, cmd) && execute_uci_command(cmd)) {}
}
else if (string(argv[1]) == "bench" && argc < 8)
benchmark(argc, argv); benchmark(argc, argv);
else else
cout << "Usage: stockfish bench [hash size = 128] [threads = 1] " cerr << "\nUsage: stockfish bench [hash size = 128] [threads = 1] "
<< "[limit = 12] [fen positions file = default] " << "[limit = 12] [fen positions file = default] "
<< "[limited by depth, time, nodes or perft = depth]" << endl; << "[limited by depth, time, nodes or perft = depth]" << endl;
Threads.exit(); Threads.exit();
return 0;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <algorithm>
#include "material.h" #include "material.h"
@@ -49,13 +50,13 @@ namespace {
// Endgame evaluation and scaling functions accessed direcly and not through // Endgame evaluation and scaling functions accessed direcly and not through
// the function maps because correspond to more then one material hash key. // the function maps because correspond to more then one material hash key.
Endgame<Value, KmmKm> EvaluateKmmKm[] = { Endgame<Value, KmmKm>(WHITE), Endgame<Value, KmmKm>(BLACK) }; Endgame<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) };
Endgame<Value, KXK> EvaluateKXK[] = { Endgame<Value, KXK>(WHITE), Endgame<Value, KXK>(BLACK) }; Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<ScaleFactor, KBPsK> ScaleKBPsK[] = { Endgame<ScaleFactor, KBPsK>(WHITE), Endgame<ScaleFactor, KBPsK>(BLACK) }; Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
Endgame<ScaleFactor, KQKRPs> ScaleKQKRPs[] = { Endgame<ScaleFactor, KQKRPs>(WHITE), Endgame<ScaleFactor, KQKRPs>(BLACK) }; Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
Endgame<ScaleFactor, KPsK> ScaleKPsK[] = { Endgame<ScaleFactor, KPsK>(WHITE), Endgame<ScaleFactor, KPsK>(BLACK) }; Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
Endgame<ScaleFactor, KPKP> ScaleKPKP[] = { Endgame<ScaleFactor, KPKP>(WHITE), Endgame<ScaleFactor, KPKP>(BLACK) }; Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution // Helper templates used to detect a given material distribution
template<Color Us> bool is_KXK(const Position& pos) { template<Color Us> bool is_KXK(const Position& pos) {
@@ -89,15 +90,15 @@ void MaterialInfoTable::init() { Base::init(); if (!funcs) funcs = new Endgames(
MaterialInfoTable::~MaterialInfoTable() { delete funcs; } MaterialInfoTable::~MaterialInfoTable() { delete funcs; }
/// MaterialInfoTable::get_material_info() takes a position object as input, /// MaterialInfoTable::material_info() takes a position object as input,
/// computes or looks up a MaterialInfo object, and returns a pointer to it. /// computes or looks up a MaterialInfo object, and returns a pointer to it.
/// If the material configuration is not already present in the table, it /// If the material configuration is not already present in the table, it
/// is stored there, so we don't have to recompute everything when the /// is stored there, so we don't have to recompute everything when the
/// same material configuration occurs again. /// same material configuration occurs again.
MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const { MaterialInfo* MaterialInfoTable::material_info(const Position& pos) const {
Key key = pos.get_material_key(); Key key = pos.material_key();
MaterialInfo* mi = probe(key); MaterialInfo* mi = probe(key);
// If mi->key matches the position's material hash key, it means that we // If mi->key matches the position's material hash key, it means that we
@@ -117,7 +118,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
// Let's look if we have a specialized evaluation function for this // Let's look if we have a specialized evaluation function for this
// particular material configuration. First we look for a fixed // particular material configuration. First we look for a fixed
// configuration one, then a generic one if previous search failed. // configuration one, then a generic one if previous search failed.
if ((mi->evaluationFunction = funcs->get<EndgameBase<Value> >(key)) != NULL) if ((mi->evaluationFunction = funcs->get<Value>(key)) != NULL)
return mi; return mi;
if (is_KXK<WHITE>(pos)) if (is_KXK<WHITE>(pos))
@@ -142,7 +143,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2 if ( pos.piece_count(WHITE, BISHOP) + pos.piece_count(WHITE, KNIGHT) <= 2
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2) && pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{ {
mi->evaluationFunction = &EvaluateKmmKm[WHITE]; mi->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
return mi; return mi;
} }
} }
@@ -154,7 +155,7 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
// scaling functions and we need to decide which one to use. // scaling functions and we need to decide which one to use.
EndgameBase<ScaleFactor>* sf; EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<EndgameBase<ScaleFactor> >(key)) != NULL) if ((sf = funcs->get<ScaleFactor>(key)) != NULL)
{ {
mi->scalingFunction[sf->color()] = sf; mi->scalingFunction[sf->color()] = sf;
return mi; return mi;
@@ -203,13 +204,13 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) const {
if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame) if (pos.piece_count(WHITE, PAWN) == 0 && npm_w - npm_b <= BishopValueMidgame)
{ {
mi->factor[WHITE] = uint8_t mi->factor[WHITE] = uint8_t
(npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(WHITE, BISHOP), 2)]); (npm_w == npm_b || npm_w < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(WHITE, BISHOP), 2)]);
} }
if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame) if (pos.piece_count(BLACK, PAWN) == 0 && npm_b - npm_w <= BishopValueMidgame)
{ {
mi->factor[BLACK] = uint8_t mi->factor[BLACK] = uint8_t
(npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[Min(pos.piece_count(BLACK, BISHOP), 2)]); (npm_w == npm_b || npm_b < RookValueMidgame ? 0 : NoPawnsSF[std::min(pos.piece_count(BLACK, BISHOP), 2)]);
} }
// Compute the space weight // Compute the space weight
@@ -253,7 +254,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
+ RedundantQueenPenalty * pieceCount[Us][QUEEN]; + RedundantQueenPenalty * pieceCount[Us][QUEEN];
// Second-degree polynomial material imbalance by Tord Romstad // Second-degree polynomial material imbalance by Tord Romstad
for (pt1 = PIECE_TYPE_NONE; pt1 <= QUEEN; pt1++) for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
{ {
pc = pieceCount[Us][pt1]; pc = pieceCount[Us][pt1];
if (!pc) if (!pc)
@@ -261,7 +262,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
v = LinearCoefficients[pt1]; v = LinearCoefficients[pt1];
for (pt2 = PIECE_TYPE_NONE; pt2 <= pt1; pt2++) for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2] v += QuadraticCoefficientsSameColor[pt1][pt2] * pieceCount[Us][pt2]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2]; + QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,6 +27,13 @@
const int MaterialTableSize = 8192; const int MaterialTableSize = 8192;
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// MaterialInfo is a class which contains various information about a /// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation, /// material configuration. It contains a material balance evaluation,
/// a function pointer to a special endgame evaluation function (which in /// a function pointer to a special endgame evaluation function (which in
@@ -61,13 +68,13 @@ private:
/// The MaterialInfoTable class represents a pawn hash table. The most important /// The MaterialInfoTable class represents a pawn hash table. The most important
/// method is get_material_info, which returns a pointer to a MaterialInfo object. /// method is material_info(), which returns a pointer to a MaterialInfo object.
class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> { class MaterialInfoTable : public SimpleHash<MaterialInfo, MaterialTableSize> {
public: public:
~MaterialInfoTable(); ~MaterialInfoTable();
void init(); void init();
MaterialInfo* get_material_info(const Position& pos) const; MaterialInfo* material_info(const Position& pos) const;
static Phase game_phase(const Position& pos); static Phase game_phase(const Position& pos);
private: private:
@@ -90,12 +97,12 @@ inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) cons
if (!scalingFunction[c]) if (!scalingFunction[c])
return ScaleFactor(factor[c]); return ScaleFactor(factor[c]);
ScaleFactor sf = scalingFunction[c]->apply(pos); ScaleFactor sf = (*scalingFunction[c])(pos);
return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf; return sf == SCALE_FACTOR_NONE ? ScaleFactor(factor[c]) : sf;
} }
inline Value MaterialInfo::evaluate(const Position& pos) const { inline Value MaterialInfo::evaluate(const Position& pos) const {
return evaluationFunction->apply(pos); return (*evaluationFunction)(pos);
} }
inline Score MaterialInfo::material_value() const { inline Score MaterialInfo::material_value() const {

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,7 +17,14 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#if !defined(_MSC_VER) #if defined(_MSC_VER)
#define _CRT_SECURE_NO_DEPRECATE
#define NOMINMAX // disable macros min() and max()
#include <windows.h>
#include <sys/timeb.h>
#else
# include <sys/time.h> # include <sys/time.h>
# include <sys/types.h> # include <sys/types.h>
@@ -26,18 +33,13 @@
# include <sys/pstat.h> # include <sys/pstat.h>
# endif # endif
#else
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <sys/timeb.h>
#endif #endif
#if !defined(NO_PREFETCH) #if !defined(NO_PREFETCH)
# include <xmmintrin.h> # include <xmmintrin.h>
#endif #endif
#include <algorithm>
#include <cassert> #include <cassert>
#include <cstdio> #include <cstdio>
#include <iomanip> #include <iomanip>
@@ -50,91 +52,69 @@
using namespace std; using namespace std;
/// Version number. If EngineVersion is left empty, then AppTag plus /// Version number. If Version is left empty, then Tag plus current
/// current date (in the format YYMMDD) is used as a version number. /// date (in the format YYMMDD) is used as a version number.
static const string AppName = "Stockfish"; static const string Version = "2.2";
static const string EngineVersion = "2.1.1"; static const string Tag = "";
static const string AppTag = "";
/// engine_name() returns the full name of the current Stockfish version. /// engine_info() returns the full name of the current Stockfish version.
/// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when /// This will be either "Stockfish YYMMDD" (where YYMMDD is the date when
/// the program was compiled) or "Stockfish <version number>", depending /// the program was compiled) or "Stockfish <version number>", depending
/// on whether the constant EngineVersion is empty. /// on whether Version is empty.
const string engine_name() { const string engine_info(bool to_uci) {
const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec"); const string months("Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec");
const string cpu64(CpuIs64Bit ? " 64bit" : ""); const string cpu64(Is64Bit ? " 64bit" : "");
const string popcnt(HasPopCnt ? " SSE4.2" : "");
if (!EngineVersion.empty())
return AppName + " " + EngineVersion + cpu64;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
string month, day, year; string month, day, year;
stringstream s, date(__DATE__); // From compiler, format is "Sep 21 2008"
date >> month >> day >> year; if (Version.empty())
{
date >> month >> day >> year;
s << setfill('0') << AppName + " " + AppTag + " " s << "Stockfish " << Tag
<< year.substr(2, 2) << setw(2) << setfill('0') << " " << year.substr(2)
<< (1 + months.find(month) / 4) << setw(2) << setw(2) << (1 + months.find(month) / 4)
<< day << cpu64; << setw(2) << day << cpu64 << popcnt;
}
else
s << "Stockfish " << Version << cpu64 << popcnt;
s << (to_uci ? "\nid author ": " by ")
<< "Tord Romstad, Marco Costalba and Joona Kiiski";
return s.str(); return s.str();
} }
/// Our brave developers! Required by UCI /// Debug functions used mainly to collect run-time statistics
const string engine_authors() { static uint64_t hits[2], means[2];
return "Tord Romstad, Marco Costalba and Joona Kiiski";
}
/// Debug stuff. Helper functions used mainly for debugging purposes
static uint64_t dbg_hit_cnt0;
static uint64_t dbg_hit_cnt1;
static uint64_t dbg_mean_cnt0;
static uint64_t dbg_mean_cnt1;
void dbg_print_hit_rate() {
if (dbg_hit_cnt0)
cout << "Total " << dbg_hit_cnt0 << " Hit " << dbg_hit_cnt1
<< " hit rate (%) " << 100 * dbg_hit_cnt1 / dbg_hit_cnt0 << endl;
}
void dbg_print_mean() {
if (dbg_mean_cnt0)
cout << "Total " << dbg_mean_cnt0 << " Mean "
<< (float)dbg_mean_cnt1 / dbg_mean_cnt0 << endl;
}
void dbg_mean_of(int v) {
dbg_mean_cnt0++;
dbg_mean_cnt1 += v;
}
void dbg_hit_on(bool b) {
dbg_hit_cnt0++;
if (b)
dbg_hit_cnt1++;
}
void dbg_hit_on(bool b) { hits[0]++; if (b) hits[1]++; }
void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); } void dbg_hit_on_c(bool c, bool b) { if (c) dbg_hit_on(b); }
void dbg_before() { dbg_hit_on(false); } void dbg_mean_of(int v) { means[0]++; means[1] += v; }
void dbg_after() { dbg_hit_on(true); dbg_hit_cnt0--; }
void dbg_print() {
if (hits[0])
cerr << "Total " << hits[0] << " Hits " << hits[1]
<< " hit rate (%) " << 100 * hits[1] / hits[0] << endl;
if (means[0])
cerr << "Total " << means[0] << " Mean "
<< (float)means[1] / means[0] << endl;
}
/// get_system_time() returns the current system time, measured in milliseconds /// system_time() returns the current system time, measured in milliseconds
int get_system_time() { int system_time() {
#if defined(_MSC_VER) #if defined(_MSC_VER)
struct _timeb t; struct _timeb t;
@@ -155,16 +135,16 @@ int cpu_count() {
#if defined(_MSC_VER) #if defined(_MSC_VER)
SYSTEM_INFO s; SYSTEM_INFO s;
GetSystemInfo(&s); GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, MAX_THREADS); return std::min(int(s.dwNumberOfProcessors), MAX_THREADS);
#else #else
# if defined(_SC_NPROCESSORS_ONLN) # if defined(_SC_NPROCESSORS_ONLN)
return Min(sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS); return std::min((int)sysconf(_SC_NPROCESSORS_ONLN), MAX_THREADS);
# elif defined(__hpux) # elif defined(__hpux)
struct pst_dynamic psd; struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1) if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1; return 1;
return Min(psd.psd_proc_cnt, MAX_THREADS); return std::min((int)psd.psd_proc_cnt, MAX_THREADS);
# else # else
return 1; return 1;
# endif # endif
@@ -173,78 +153,32 @@ int cpu_count() {
} }
/// Check for console input. Original code from Beowulf, Olithink and Greko /// timed_wait() waits for msec milliseconds. It is mainly an helper to wrap
/// conversion from milliseconds to struct timespec, as used by pthreads.
#ifndef _WIN32 void timed_wait(WaitCondition* sleepCond, Lock* sleepLock, int msec) {
int input_available() {
fd_set readfds;
struct timeval timeout;
FD_ZERO(&readfds);
FD_SET(fileno(stdin), &readfds);
timeout.tv_sec = 0; // Set to timeout immediately
timeout.tv_usec = 0;
select(16, &readfds, 0, 0, &timeout);
return (FD_ISSET(fileno(stdin), &readfds));
}
#if defined(_MSC_VER)
int tm = msec;
#else #else
struct timeval t;
struct timespec abstime, *tm = &abstime;
int input_available() { gettimeofday(&t, NULL);
static HANDLE inh = NULL; abstime.tv_sec = t.tv_sec + (msec / 1000);
static bool usePipe = false; abstime.tv_nsec = (t.tv_usec + (msec % 1000) * 1000) * 1000;
INPUT_RECORD rec[256];
DWORD nchars, recCnt;
if (!inh) if (abstime.tv_nsec > 1000000000LL)
{ {
inh = GetStdHandle(STD_INPUT_HANDLE); abstime.tv_sec += 1;
if (GetConsoleMode(inh, &nchars)) abstime.tv_nsec -= 1000000000LL;
{
SetConsoleMode(inh, nchars & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
} else
usePipe = true;
} }
// When using Standard C input functions, also check if there
// is anything in the buffer. After a call to such functions,
// the input waiting in the pipe will be copied to the buffer,
// and the call to PeekNamedPipe can indicate no input available.
// Setting stdin to unbuffered was not enough. [from Greko]
if (stdin->_cnt > 0)
return 1;
// When running under a GUI the input commands are sent to us
// directly over the internal pipe. If PeekNamedPipe() returns 0
// then something went wrong. Probably the parent program exited.
// Returning 1 will make the next call to the input function
// return EOF, where this should be catched then.
if (usePipe)
return PeekNamedPipe(inh, NULL, 0, NULL, &nchars, NULL) ? nchars : 1;
// Count the number of unread input records, including keyboard,
// mouse, and window-resizing input records.
GetNumberOfConsoleInputEvents(inh, &nchars);
// Read data from console without removing it from the buffer
if (nchars <= 0 || !PeekConsoleInput(inh, rec, Min(nchars, 256), &recCnt))
return 0;
// Search for at least one keyboard event
for (DWORD i = 0; i < recCnt; i++)
if (rec[i].EventType == KEY_EVENT)
return 1;
return 0;
}
#endif #endif
cond_timedwait(sleepCond, sleepLock, tm);
}
/// prefetch() preloads the given address in L1/L2 cache. This is a non /// prefetch() preloads the given address in L1/L2 cache. This is a non
/// blocking function and do not stalls the CPU waiting for data to be /// blocking function and do not stalls the CPU waiting for data to be
@@ -257,11 +191,11 @@ void prefetch(char*) {}
void prefetch(char* addr) { void prefetch(char* addr) {
#if defined(__INTEL_COMPILER) || defined(__ICL) # if defined(__INTEL_COMPILER) || defined(__ICL)
// This hack prevents prefetches to be optimized away by // This hack prevents prefetches to be optimized away by
// Intel compiler. Both MSVC and gcc seems not affected. // Intel compiler. Both MSVC and gcc seems not affected.
__asm__ (""); __asm__ ("");
#endif # endif
_mm_prefetch(addr, _MM_HINT_T2); _mm_prefetch(addr, _MM_HINT_T2);
_mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead _mm_prefetch(addr+64, _MM_HINT_T2); // 64 bytes ahead

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,22 +20,31 @@
#if !defined(MISC_H_INCLUDED) #if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED #define MISC_H_INCLUDED
#include <fstream>
#include <string> #include <string>
#include "lock.h"
#include "types.h" #include "types.h"
extern const std::string engine_name(); extern const std::string engine_info(bool to_uci = false);
extern const std::string engine_authors(); extern int system_time();
extern int get_system_time();
extern int cpu_count(); extern int cpu_count();
extern int input_available(); extern void timed_wait(WaitCondition*, Lock*, int);
extern void prefetch(char* addr); extern void prefetch(char* addr);
extern void dbg_hit_on(bool b); extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, 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_mean_of(int v);
extern void dbg_print_hit_rate(); extern void dbg_print();
extern void dbg_print_mean();
class Position;
extern Move move_from_uci(const Position& pos, const std::string& str);
extern const std::string move_to_uci(Move m, bool chess960);
extern const std::string move_to_san(Position& pos, Move m);
struct Log : public std::ofstream {
Log(const std::string& f = "log.txt") : std::ofstream(f.c_str(), std::ios::out | std::ios::app) {}
~Log() { if (is_open()) close(); }
};
#endif // !defined(MISC_H_INCLUDED) #endif // !defined(MISC_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,26 +19,17 @@
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <iomanip>
#include <string> #include <string>
#include <sstream>
#include "move.h"
#include "movegen.h" #include "movegen.h"
#include "search.h" #include "position.h"
using std::string; using std::string;
namespace {
const string time_string(int milliseconds);
const string score_string(Value v);
}
/// move_to_uci() converts a move to a string in coordinate notation /// move_to_uci() converts a move to a string in coordinate notation
/// (g1f3, a7a8q, etc.). The only special case is castling moves, where we /// (g1f3, a7a8q, etc.). The only special case is castling moves, where we
/// print in the e1g1 notation in normal chess mode, and in e1h1 notation in /// print in the e1g1 notation in normal chess mode, and in e1h1 notation in
/// Chess960 mode. /// Chess960 mode. Instead internally Move is coded as "king captures rook".
const string move_to_uci(Move m, bool chess960) { const string move_to_uci(Move m, bool chess960) {
@@ -52,14 +43,11 @@ const string move_to_uci(Move m, bool chess960) {
if (m == MOVE_NULL) if (m == MOVE_NULL)
return "0000"; return "0000";
if (move_is_short_castle(m) && !chess960) if (is_castle(m) && !chess960)
return from == SQ_E1 ? "e1g1" : "e8g8"; to = from + (file_of(to) == FILE_H ? Square(2) : -Square(2));
if (move_is_long_castle(m) && !chess960) if (is_promotion(m))
return from == SQ_E1 ? "e1c1" : "e8c8"; promotion = char(tolower(piece_type_to_char(promotion_piece_type(m))));
if (move_is_promotion(m))
promotion = char(tolower(piece_type_to_char(move_promotion_piece(m))));
return square_to_string(from) + square_to_string(to) + promotion; return square_to_string(from) + square_to_string(to) + promotion;
} }
@@ -71,88 +59,91 @@ const string move_to_uci(Move m, bool chess960) {
Move move_from_uci(const Position& pos, const string& str) { Move move_from_uci(const Position& pos, const string& str) {
MoveStack mlist[MAX_MOVES]; for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
MoveStack* last = generate<MV_LEGAL>(pos, mlist); if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
for (MoveStack* cur = mlist; cur != last; cur++)
if (str == move_to_uci(cur->move, pos.is_chess960()))
return cur->move;
return MOVE_NONE; return MOVE_NONE;
} }
/// move_to_san() takes a position and a move as input, where it is assumed /// 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 for the position. The return value is
/// a string containing the move in short algebraic notation. /// a string containing the move in short algebraic notation.
const string move_to_san(Position& pos, Move m) { const string move_to_san(Position& pos, Move m) {
assert(pos.is_ok());
assert(move_is_ok(m));
MoveStack mlist[MAX_MOVES];
Square from = move_from(m);
Square to = move_to(m);
PieceType pt = pos.type_of_piece_on(from);
string san;
if (m == MOVE_NONE) if (m == MOVE_NONE)
return "(none)"; return "(none)";
if (m == MOVE_NULL) if (m == MOVE_NULL)
return "(null)"; return "(null)";
if (move_is_long_castle(m)) assert(is_ok(m));
san = "O-O-O";
else if (move_is_short_castle(m)) Bitboard attackers;
san = "O-O"; bool ambiguousMove, ambiguousFile, ambiguousRank;
Square sq, from = move_from(m);
Square to = move_to(m);
PieceType pt = type_of(pos.piece_on(from));
string san;
if (is_castle(m))
san = (move_to(m) < move_from(m) ? "O-O-O" : "O-O");
else else
{ {
if (pt != PAWN) if (pt != PAWN)
{ {
san = piece_type_to_char(pt); san = piece_type_to_char(pt);
// Collect all legal moves of piece type 'pt' with destination 'to'
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
int f = 0, r = 0;
for (MoveStack* cur = mlist; cur != last; cur++)
if ( move_to(cur->move) == to
&& pos.type_of_piece_on(move_from(cur->move)) == pt)
{
if (square_file(move_from(cur->move)) == square_file(from))
f++;
if (square_rank(move_from(cur->move)) == square_rank(from))
r++;
}
assert(f > 0 && r > 0);
// Disambiguation if we have more then one piece with destination 'to' // Disambiguation if we have more then one piece with destination 'to'
if (f == 1 && r > 1) // note that for pawns is not needed because starting file is explicit.
san += file_to_char(square_file(from)); attackers = pos.attackers_to(to) & pos.pieces(pt, pos.side_to_move());
else if (f > 1 && r == 1) clear_bit(&attackers, from);
san += rank_to_char(square_rank(from)); ambiguousMove = ambiguousFile = ambiguousRank = false;
else if (f > 1 && r > 1)
san += square_to_string(from); while (attackers)
{
sq = pop_1st_bit(&attackers);
// Pinned pieces are not included in the possible sub-set
if (!pos.pl_move_is_legal(make_move(sq, to), pos.pinned_pieces()))
continue;
if (file_of(sq) == file_of(from))
ambiguousFile = true;
if (rank_of(sq) == rank_of(from))
ambiguousRank = true;
ambiguousMove = true;
}
if (ambiguousMove)
{
if (!ambiguousFile)
san += file_to_char(file_of(from));
else if (!ambiguousRank)
san += rank_to_char(rank_of(from));
else
san += square_to_string(from);
}
} }
if (pos.move_is_capture(m)) if (pos.is_capture(m))
{ {
if (pt == PAWN) if (pt == PAWN)
san += file_to_char(square_file(from)); san += file_to_char(file_of(from));
san += 'x'; san += 'x';
} }
san += square_to_string(to); san += square_to_string(to);
if (move_is_promotion(m)) if (is_promotion(m))
{ {
san += '='; san += '=';
san += piece_type_to_char(move_promotion_piece(m)); san += piece_type_to_char(promotion_piece_type(m));
} }
} }
@@ -166,93 +157,3 @@ const string move_to_san(Position& pos, Move m) {
return san; return san;
} }
/// pretty_pv() creates a human-readable string from a position and a PV.
/// It is used to write search information to the log file (which is created
/// when the UCI parameter "Use Search Log" is "true").
const string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]) {
const int64_t K = 1000;
const int64_t M = 1000000;
const int startColumn = 28;
const size_t maxLength = 80 - startColumn;
const string lf = string("\n") + string(startColumn, ' ');
StateInfo state[PLY_MAX_PLUS_2], *st = state;
Move* m = pv;
string san;
std::stringstream s;
size_t length = 0;
// First print depth, score, time and searched nodes...
s << std::setw(2) << depth
<< std::setw(8) << score_string(score)
<< std::setw(8) << time_string(time);
if (pos.nodes_searched() < M)
s << std::setw(8) << pos.nodes_searched() / 1 << " ";
else if (pos.nodes_searched() < K * M)
s << std::setw(7) << pos.nodes_searched() / K << "K ";
else
s << std::setw(7) << pos.nodes_searched() / M << "M ";
// ...then print the full PV line in short algebraic notation
while (*m != MOVE_NONE)
{
san = move_to_san(pos, *m);
length += san.length() + 1;
if (length > maxLength)
{
length = san.length() + 1;
s << lf;
}
s << san << ' ';
pos.do_move(*m++, *st++);
}
// Restore original position before to leave
while (m != pv) pos.undo_move(*--m);
return s.str();
}
namespace {
const string time_string(int millisecs) {
const int MSecMinute = 1000 * 60;
const int MSecHour = 1000 * 60 * 60;
int hours = millisecs / MSecHour;
int minutes = (millisecs % MSecHour) / MSecMinute;
int seconds = ((millisecs % MSecHour) % MSecMinute) / 1000;
std::stringstream s;
if (hours)
s << hours << ':';
s << std::setfill('0') << std::setw(2) << minutes << ':' << std::setw(2) << seconds;
return s.str();
}
const string score_string(Value v) {
std::stringstream s;
if (v >= VALUE_MATE - 200)
s << "#" << (VALUE_MATE - v + 1) / 2;
else if (v <= -VALUE_MATE + 200)
s << "-#" << (VALUE_MATE + v) / 2;
else
s << std::setprecision(2) << std::fixed << std::showpos << float(v) / PawnValueMidgame;
return s.str();
}
}

View File

@@ -1,195 +0,0 @@
/*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Stockfish is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if !defined(MOVE_H_INCLUDED)
#define MOVE_H_INCLUDED
#include <string>
#include "misc.h"
#include "types.h"
// Maximum number of allowed moves per position
const int MAX_MOVES = 256;
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in
/// because in any normal move destination square is always different
/// from origin square while MOVE_NONE and MOVE_NULL have the same
/// origin and destination square, 0 and 1 respectively.
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) { return f.score < s.score; }
// An helper insertion sort implementation, works with pointers and iterators
template<typename T, typename K>
inline void insertion_sort(K firstMove, K lastMove)
{
T value;
K cur, p, d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (*p < value)
{
do *d = *p;
while (--d != firstMove && *--p < value);
*d = value;
}
}
}
// Our dedicated sort in range [firstMove, lastMove), first splits
// positive scores from ramining then order seaprately the two sets.
template<typename T>
inline void sort_moves(T* firstMove, T* lastMove, T** lastPositive)
{
T tmp;
T *p, *d;
d = lastMove;
p = firstMove - 1;
d->score = -1; // right guard
// Split positives vs non-positives
do {
while ((++p)->score > 0) {}
if (p != d)
{
while (--d != p && d->score <= 0) {}
tmp = *p;
*p = *d;
*d = tmp;
}
} while (p != d);
// Sort just positive scored moves, remaining only when we get there
insertion_sort<T, T*>(firstMove, p);
*lastPositive = p;
}
// Picks up the best move in range [curMove, lastMove), one per cycle.
// It is faster then sorting all the moves in advance when moves are few,
// as normally are the possible captures. Note that is not a stable alghoritm.
template<typename T>
inline T pick_best(T* curMove, T* lastMove)
{
T bestMove, tmp;
bestMove = *curMove;
while (++curMove != lastMove)
{
if (bestMove < *curMove)
{
tmp = *curMove;
*curMove = bestMove;
bestMove = tmp;
}
}
return bestMove;
}
inline Square move_from(Move m) {
return Square((int(m) >> 6) & 0x3F);
}
inline Square move_to(Move m) {
return Square(m & 0x3F);
}
inline bool move_is_special(Move m) {
return m & (3 << 14);
}
inline bool move_is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int move_is_ep(Move m) {
return (m & (3 << 14)) == (2 << 14);
}
inline int move_is_castle(Move m) {
return (m & (3 << 14)) == (3 << 14);
}
inline bool move_is_short_castle(Move m) {
return move_is_castle(m) && (move_to(m) > move_from(m));
}
inline bool move_is_long_castle(Move m) {
return move_is_castle(m) && (move_to(m) < move_from(m));
}
inline PieceType move_promotion_piece(Move m) {
return move_is_promotion(m) ? PieceType(((int(m) >> 12) & 3) + 2) : PIECE_TYPE_NONE;
}
inline Move make_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(int(to) | (int(from) << 6) | ((int(promotion) - 2) << 12) | (1 << 14));
}
inline Move make_ep_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (2 << 14));
}
inline Move make_castle_move(Square from, Square to) {
return Move(int(to) | (int(from) << 6) | (3 << 14));
}
inline bool move_is_ok(Move m) {
return move_from(m) != move_to(m); // Catches also MOVE_NONE
}
class Position;
extern const std::string move_to_uci(Move m, bool chess960);
extern Move move_from_uci(const Position& pos, const std::string& str);
extern const std::string move_to_san(Position& pos, Move m);
extern const std::string pretty_pv(Position& pos, int depth, Value score, int time, Move pv[]);
#endif // !defined(MOVE_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,9 +18,11 @@
*/ */
#include <cassert> #include <cassert>
#include <algorithm>
#include "bitcount.h" #include "bitcount.h"
#include "movegen.h" #include "movegen.h"
#include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility, // Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'. // hardcoded list name 'mlist' and from square 'from'.
@@ -45,14 +47,13 @@ namespace {
template<PieceType Pt> template<PieceType Pt>
inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) { inline MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
assert(Pt != QUEEN); assert(Pt != QUEEN && Pt != PAWN);
Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares(); Bitboard b = pos.attacks_from<Pt>(from) & pos.empty_squares();
if (Pt == KING) if (Pt == KING)
{ b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))];
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
SERIALIZE_MOVES(b); SERIALIZE_MOVES(b);
return mlist; return mlist;
} }
@@ -60,13 +61,13 @@ namespace {
template<PieceType Pt> template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us, inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) { Bitboard dc, Square ksq) {
assert(Pt != KING); assert(Pt != KING && Pt != PAWN);
Bitboard checkSqs, b; Bitboard checkSqs, b;
Square from; Square from;
const Square* ptr = pos.piece_list_begin(us, Pt); const Square* pl = pos.piece_list(us, Pt);
if ((from = *ptr++) == SQ_NONE) if ((from = *pl++) == SQ_NONE)
return mlist; return mlist;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares(); checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
@@ -84,7 +85,7 @@ namespace {
b = pos.attacks_from<Pt>(from) & checkSqs; b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b); SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE); } while ((from = *pl++) != SQ_NONE);
return mlist; return mlist;
} }
@@ -111,15 +112,15 @@ namespace {
Bitboard b; Bitboard b;
Square from; Square from;
const Square* ptr = pos.piece_list_begin(us, Pt); const Square* pl = pos.piece_list(us, Pt);
if (*ptr != SQ_NONE) if (*pl != SQ_NONE)
{ {
do { do {
from = *ptr; from = *pl;
b = pos.attacks_from<Pt>(from) & target; b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b); SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE); } while (*++pl != SQ_NONE);
} }
return mlist; return mlist;
} }
@@ -150,14 +151,13 @@ namespace {
template<MoveType Type> template<MoveType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) { MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check()); assert(!pos.in_check());
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Bitboard target; Bitboard target;
if (Type == MV_CAPTURE || Type == MV_NON_EVASION) if (Type == MV_CAPTURE || Type == MV_NON_EVASION)
target = pos.pieces_of_color(opposite_color(us)); target = pos.pieces(flip(us));
else if (Type == MV_NON_CAPTURE) else if (Type == MV_NON_CAPTURE)
target = pos.empty_squares(); target = pos.empty_squares();
else else
@@ -178,12 +178,12 @@ MoveStack* generate(const Position& pos, MoveStack* mlist) {
mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target); mlist = generate_piece_moves<QUEEN>(pos, mlist, us, target);
mlist = generate_piece_moves<KING>(pos, mlist, us, target); mlist = generate_piece_moves<KING>(pos, mlist, us, target);
if (Type != MV_CAPTURE) if (Type != MV_CAPTURE && pos.can_castle(us))
{ {
if (pos.can_castle_kingside(us)) if (pos.can_castle(us == WHITE ? WHITE_OO : BLACK_OO))
mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us); mlist = generate_castle_moves<KING_SIDE>(pos, mlist, us);
if (pos.can_castle_queenside(us)) if (pos.can_castle(us == WHITE ? WHITE_OOO : BLACK_OOO))
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us); mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist, us);
} }
@@ -196,28 +196,27 @@ template MoveStack* generate<MV_NON_CAPTURE>(const Position& pos, MoveStack* mli
template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist); template MoveStack* generate<MV_NON_EVASION>(const Position& pos, MoveStack* mlist);
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight /// generate<MV_NON_CAPTURE_CHECK> generates all pseudo-legal non-captures and knight
/// underpromotions that give check. Returns a pointer to the end of the move list. /// underpromotions that give check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) { MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check()); assert(!pos.in_check());
Bitboard b, dc; Bitboard b, dc;
Square from; Square from;
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(opposite_color(us)); Square ksq = pos.king_square(flip(us));
assert(pos.piece_on(ksq) == make_piece(opposite_color(us), KING)); assert(pos.piece_on(ksq) == make_piece(flip(us), KING));
// Discovered non-capture checks // Discovered non-capture checks
b = dc = pos.discovered_check_candidates(us); b = dc = pos.discovered_check_candidates();
while (b) while (b)
{ {
from = pop_1st_bit(&b); from = pop_1st_bit(&b);
switch (pos.type_of_piece_on(from)) switch (type_of(pos.piece_on(from)))
{ {
case PAWN: /* Will be generated togheter with pawns direct checks */ break; case PAWN: /* Will be generated togheter with pawns direct checks */ break;
case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break; case KNIGHT: mlist = generate_discovered_checks<KNIGHT>(pos, mlist, from); break;
@@ -237,12 +236,11 @@ MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist)
} }
/// generate_evasions() generates all pseudo-legal check evasions when /// generate<MV_EVASION> generates all pseudo-legal check evasions when the side
/// the side to move is in check. Returns a pointer to the end of the move list. /// to move is in check. Returns a pointer to the end of the move list.
template<> template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) { MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.in_check()); assert(pos.in_check());
Bitboard b, target; Bitboard b, target;
@@ -251,7 +249,7 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
Color us = pos.side_to_move(); Color us = pos.side_to_move();
Square ksq = pos.king_square(us); Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers(); Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = EmptyBoardBB; Bitboard sliderAttacks = 0;
assert(pos.piece_on(ksq) == make_piece(us, KING)); assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers); assert(checkers);
@@ -265,26 +263,31 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
checkersCnt++; checkersCnt++;
checksq = pop_1st_bit(&b); checksq = pop_1st_bit(&b);
assert(pos.color_of_piece_on(checksq) == opposite_color(us)); assert(color_of(pos.piece_on(checksq)) == flip(us));
switch (pos.type_of_piece_on(checksq)) switch (type_of(pos.piece_on(checksq)))
{ {
case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break; case BISHOP: sliderAttacks |= BishopPseudoAttacks[checksq]; break;
case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break; case ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case QUEEN: case QUEEN:
// In case of a queen remove also squares attacked in the other direction to // If queen and king are far we can safely remove all the squares attacked
// avoid possible illegal moves when queen and king are on adjacent squares. // in the other direction becuase are not reachable by the king anyway.
if (RookPseudoAttacks[checksq] & (1ULL << ksq)) if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq)))
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq); sliderAttacks |= QueenPseudoAttacks[checksq];
// Otherwise, if king and queen are adjacent and on a diagonal line, we need to
// use real rook attacks to check if king is safe to move in the other direction.
// For example: king in B2, queen in A1 a knight in B1, and we can safely move to C1.
else else
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq); sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
default: default:
break; break;
} }
} while (b); } while (b);
// Generate evasions for king, capture and non capture moves // Generate evasions for king, capture and non capture moves
b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks; b = pos.attacks_from<KING>(ksq) & ~pos.pieces(us) & ~sliderAttacks;
from = ksq; from = ksq;
SERIALIZE_MOVES(b); SERIALIZE_MOVES(b);
@@ -304,26 +307,16 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
} }
/// generate<MV_LEGAL / MV_PSEUDO_LEGAL> computes a complete list of legal /// generate<MV_LEGAL> computes a complete list of legal moves in the current position
/// or pseudo-legal moves in the current position.
template<>
MoveStack* generate<MV_PSEUDO_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
return pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist);
}
template<> template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) { MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
MoveStack *last, *cur = mlist; MoveStack *last, *cur = mlist;
Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Bitboard pinned = pos.pinned_pieces();
last = generate<MV_PSEUDO_LEGAL>(pos, mlist); last = pos.in_check() ? generate<MV_EVASION>(pos, mlist)
: generate<MV_NON_EVASION>(pos, mlist);
// Remove illegal moves from the list // Remove illegal moves from the list
while (cur != last) while (cur != last)
@@ -360,7 +353,7 @@ namespace {
return mlist; return mlist;
} }
template<Color Us, MoveType Type, Square Delta> template<MoveType Type, Square Delta>
inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) { inline MoveStack* generate_promotions(const Position& pos, MoveStack* mlist, Bitboard pawnsOn7, Bitboard target) {
const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB); const Bitboard TFileABB = (Delta == DELTA_NE || Delta == DELTA_SE ? FileABB : FileHBB);
@@ -391,7 +384,7 @@ namespace {
// This is the only possible under promotion that can give a check // This is the only possible under promotion that can give a check
// not already included in the queen-promotion. // not already included in the queen-promotion.
if ( Type == MV_CHECK if ( Type == MV_CHECK
&& bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(opposite_color(Us)))) && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Delta > 0 ? BLACK : WHITE)))
(*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT); (*mlist++).move = make_promotion_move(to - Delta, to, KNIGHT);
else (void)pos; // Silence a warning under MSVC else (void)pos; // Silence a warning under MSVC
} }
@@ -406,21 +399,21 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB); const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB); const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S); const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE); const Square RIGHT_UP = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW); const Square LEFT_UP = (Us == WHITE ? DELTA_NW : DELTA_SE);
Square to; Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares; Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us); Bitboard pawns = pos.pieces(PAWN, Us);
Bitboard pawnsOn7 = pawns & TRank7BB; Bitboard pawnsOn7 = pawns & TRank7BB;
Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces_of_color(Them)); Bitboard enemyPieces = (Type == MV_CAPTURE ? target : pos.pieces(Them));
// Pre-calculate pawn pushes before changing emptySquares definition // Pre-calculate pawn pushes before changing emptySquares definition
if (Type != MV_CAPTURE) if (Type != MV_CAPTURE)
{ {
emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares()); emptySquares = (Type == MV_NON_CAPTURE ? target : pos.empty_squares());
pawnPushes = move_pawns<TDELTA_N>(pawns & ~TRank7BB) & emptySquares; pawnPushes = move_pawns<UP>(pawns & ~TRank7BB) & emptySquares;
} }
if (Type == MV_EVASION) if (Type == MV_EVASION)
@@ -436,23 +429,23 @@ namespace {
emptySquares = pos.empty_squares(); emptySquares = pos.empty_squares();
pawns &= ~TRank7BB; pawns &= ~TRank7BB;
mlist = generate_promotions<Us, Type, TDELTA_NE>(pos, mlist, pawnsOn7, enemyPieces); mlist = generate_promotions<Type, RIGHT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Us, Type, TDELTA_NW>(pos, mlist, pawnsOn7, enemyPieces); mlist = generate_promotions<Type, LEFT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Us, Type, TDELTA_N >(pos, mlist, pawnsOn7, emptySquares); mlist = generate_promotions<Type, UP>(pos, mlist, pawnsOn7, emptySquares);
} }
// Standard captures // Standard captures
if (Type == MV_CAPTURE || Type == MV_EVASION) if (Type == MV_CAPTURE || Type == MV_EVASION)
{ {
mlist = generate_pawn_captures<Type, TDELTA_NE>(mlist, pawns, enemyPieces); mlist = generate_pawn_captures<Type, RIGHT_UP>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, TDELTA_NW>(mlist, pawns, enemyPieces); mlist = generate_pawn_captures<Type, LEFT_UP>(mlist, pawns, enemyPieces);
} }
// Single and double pawn pushes // Single and double pawn pushes
if (Type != MV_CAPTURE) if (Type != MV_CAPTURE)
{ {
b1 = pawnPushes & emptySquares; b1 = (Type != MV_EVASION ? pawnPushes : pawnPushes & emptySquares);
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares; b2 = move_pawns<UP>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK) if (Type == MV_CHECK)
{ {
@@ -465,37 +458,37 @@ namespace {
// don't generate captures. // don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard if (pawns & target) // For CHECK type target is dc bitboard
{ {
dc1 = move_pawns<TDELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares; dc1 = move_pawns<UP>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<TDELTA_N>(dc1 & TRank3BB) & emptySquares; dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1; b1 |= dc1;
b2 |= dc2; b2 |= dc2;
} }
} }
SERIALIZE_MOVES_D(b1, -TDELTA_N); SERIALIZE_MOVES_D(b1, -UP);
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N); SERIALIZE_MOVES_D(b2, -UP -UP);
} }
// En passant captures // En passant captures
if ((Type == MV_CAPTURE || Type == MV_EVASION) && pos.ep_square() != SQ_NONE) if ((Type == MV_CAPTURE || Type == MV_EVASION) && pos.ep_square() != SQ_NONE)
{ {
assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6); assert(Us != WHITE || rank_of(pos.ep_square()) == RANK_6);
assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3); assert(Us != BLACK || rank_of(pos.ep_square()) == RANK_3);
// An en passant capture can be an evasion only if the checking piece // An en passant capture can be an evasion only if the checking piece
// is the double pushed pawn and so is in the target. Otherwise this // is the double pushed pawn and so is in the target. Otherwise this
// is a discovery check and we are forced to do otherwise. // is a discovery check and we are forced to do otherwise.
if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N)) if (Type == MV_EVASION && !bit_is_set(target, pos.ep_square() - UP))
return mlist; return mlist;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them); b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1 != EmptyBoardBB); assert(b1);
while (b1) while (b1)
{ {
to = pop_1st_bit(&b1); to = pop_1st_bit(&b1);
(*mlist++).move = make_ep_move(to, pos.ep_square()); (*mlist++).move = make_enpassant_move(to, pos.ep_square());
} }
} }
return mlist; return mlist;
@@ -504,37 +497,45 @@ namespace {
template<CastlingSide Side> template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) { MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
Color them = opposite_color(us); CastleRight f = CastleRight((Side == KING_SIDE ? WHITE_OO : WHITE_OOO) << us);
Square ksq = pos.king_square(us); Color them = flip(us);
assert(pos.piece_on(ksq) == make_piece(us, KING)); // After castling, the rook and king's final positions are exactly the same
// in Chess960 as they would be in standard chess.
Square kfrom = pos.king_square(us);
Square rfrom = pos.castle_rook_square(f);
Square kto = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square rto = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square rsq = (Side == KING_SIDE ? pos.initial_kr_square(us) : pos.initial_qr_square(us)); assert(!pos.in_check());
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1); assert(pos.piece_on(kfrom) == make_piece(us, KING));
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1); assert(pos.piece_on(rfrom) == make_piece(us, ROOK));
Square s;
bool illegal = false;
assert(pos.piece_on(rsq) == make_piece(us, ROOK)); // Unimpeded rule: All the squares between the king's initial and final squares
// (including the final square), and all the squares between the rook's initial
// and final squares (including the final square), must be vacant except for
// the king and castling rook.
for (Square s = std::min(kfrom, kto); s <= std::max(kfrom, kto); s++)
if ( (s != kfrom && s != rfrom && !pos.square_is_empty(s))
||(pos.attackers_to(s) & pos.pieces(them)))
return mlist;
// It is a bit complicated to correctly handle Chess960 for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++)
for (s = Min(ksq, s1); s <= Max(ksq, s1); s++) if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
if ( (s != ksq && s != rsq && pos.square_is_occupied(s)) return mlist;
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
illegal = true;
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++) // Because we generate only legal castling moves we need to verify that
if (s != ksq && s != rsq && pos.square_is_occupied(s)) // when moving the castling rook we do not discover some hidden checker.
illegal = true; // For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
if (pos.is_chess960())
{
Bitboard occ = pos.occupied_squares();
clear_bit(&occ, rfrom);
if (pos.attackers_to(kto, occ) & pos.pieces(them))
return mlist;
}
if ( Side == QUEEN_SIDE (*mlist++).move = make_castle_move(kfrom, rfrom);
&& square_file(rsq) == FILE_B
&& ( pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, ROOK)
|| pos.piece_on(relative_square(us, SQ_A1)) == make_piece(them, QUEEN)))
illegal = true;
if (!illegal)
(*mlist++).move = make_castle_move(ksq, rsq);
return mlist; return mlist;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,8 +20,7 @@
#if !defined(MOVEGEN_H_INCLUDED) #if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED #define MOVEGEN_H_INCLUDED
#include "move.h" #include "types.h"
#include "position.h"
enum MoveType { enum MoveType {
MV_CAPTURE, MV_CAPTURE,
@@ -30,11 +29,28 @@ enum MoveType {
MV_NON_CAPTURE_CHECK, MV_NON_CAPTURE_CHECK,
MV_EVASION, MV_EVASION,
MV_NON_EVASION, MV_NON_EVASION,
MV_LEGAL, MV_LEGAL
MV_PSEUDO_LEGAL
}; };
class Position;
template<MoveType> template<MoveType>
MoveStack* generate(const Position& pos, MoveStack* mlist); MoveStack* generate(const Position& pos, MoveStack* mlist);
/// The MoveList struct is a simple wrapper around generate(), sometimes comes
/// handy to use this class instead of the low level generate() function.
template<MoveType T>
struct MoveList {
explicit MoveList(const Position& pos) : cur(mlist), last(generate<T>(pos, mlist)) {}
void operator++() { cur++; }
bool end() const { return cur == last; }
Move move() const { return cur->move; }
int size() const { return int(last - mlist); }
private:
MoveStack mlist[MAX_MOVES];
MoveStack *cur, *last;
};
#endif // !defined(MOVEGEN_H_INCLUDED) #endif // !defined(MOVEGEN_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <algorithm>
#include <cassert> #include <cassert>
#include "movegen.h" #include "movegen.h"
@@ -28,93 +29,127 @@
namespace { namespace {
enum MovegenPhase { enum MovegenPhase {
PH_TT_MOVES, // Transposition table move and mate killer PH_TT_MOVE, // Transposition table move
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0 PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= captureThreshold (captureThreshold <= 0)
PH_GOOD_PROBCUT, // Queen promotions and captures with SEE values > captureThreshold (captureThreshold >= 0)
PH_KILLERS, // Killer moves from the current ply PH_KILLERS, // Killer moves from the current ply
PH_NONCAPTURES, // Non-captures and underpromotions PH_NONCAPTURES_1, // Non-captures and underpromotions with positive score
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0 PH_NONCAPTURES_2, // Non-captures and underpromotions with non-positive score
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < captureThreshold (captureThreshold <= 0)
PH_EVASIONS, // Check evasions PH_EVASIONS, // Check evasions
PH_QCAPTURES, // Captures in quiescence search PH_QCAPTURES, // Captures in quiescence search
PH_QRECAPTURES, // Recaptures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP PH_STOP
}; };
CACHE_LINE_ALIGNMENT CACHE_LINE_ALIGNMENT
const uint8_t MainSearchTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP }; const uint8_t MainSearchTable[] = { PH_TT_MOVE, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES_1, PH_NONCAPTURES_2, PH_BAD_CAPTURES, PH_STOP };
const uint8_t EvasionTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP }; const uint8_t EvasionTable[] = { PH_TT_MOVE, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP }; const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP }; const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_STOP };
const uint8_t QsearchRecapturesTable[] = { PH_TT_MOVE, PH_QRECAPTURES, PH_STOP };
const uint8_t ProbCutTable[] = { PH_TT_MOVE, PH_GOOD_PROBCUT, PH_STOP };
// Unary predicate used by std::partition to split positive scores from remaining
// ones so to sort separately the two sets, and with the second sort delayed.
inline bool has_positive_score(const MoveStack& move) { return move.score > 0; }
// Picks and pushes to the front the best move in range [firstMove, lastMove),
// it is faster than sorting all the moves in advance when moves are few, as
// normally are the possible captures.
inline MoveStack* pick_best(MoveStack* firstMove, MoveStack* lastMove)
{
std::swap(*firstMove, *std::max_element(firstMove, lastMove));
return firstMove;
}
} }
/// Constructors for the MovePicker class. As arguments we pass information
/// Constructor for the MovePicker class. Apart from the position for which
/// it is asked to pick legal moves, MovePicker also wants some information
/// to help it to return the presumably good moves first, to decide which /// to help it to return the presumably good moves first, to decide which
/// moves to return (in the quiescence search, for instance, we only want to /// moves to return (in the quiescence search, for instance, we only want to
/// search captures, promotions and some checks) and about how important good /// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node. /// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
SearchStack* ss, Value beta) : pos(p), H(h) { Search::Stack* ss, Value beta) : pos(p), H(h), depth(d) {
int searchTT = ttm; captureThreshold = 0;
ttMoves[0].move = ttm;
badCaptureThreshold = 0;
badCaptures = moves + MAX_MOVES; badCaptures = moves + MAX_MOVES;
assert(d > DEPTH_ZERO); assert(d > DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check()) if (p.in_check())
{ {
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE; killers[0].move = killers[1].move = MOVE_NONE;
phasePtr = EvasionTable; phasePtr = EvasionTable;
} }
else else
{ {
ttMoves[1].move = (ss->mateKiller == ttm) ? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0]; killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1]; killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low // Consider sligtly negative captures as good if at low depth and far from beta
// depth and far from beta.
if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY) if (ss && ss->eval < beta - PawnValueMidgame && d < 3 * ONE_PLY)
badCaptureThreshold = -PawnValueMidgame; captureThreshold = -PawnValueMidgame;
// Consider negative captures as good if still enough to reach beta
else if (ss && ss->eval > beta)
captureThreshold = beta - ss->eval;
phasePtr = MainSearchTable; phasePtr = MainSearchTable;
} }
phasePtr += int(!searchTT) - 1; ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase(); go_next_phase();
} }
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h) MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, Square recaptureSq)
: pos(p), H(h) { : pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
ttMoves[1].move = MOVE_NONE;
assert(d <= DEPTH_ZERO); assert(d <= DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check()) if (p.in_check())
phasePtr = EvasionTable; phasePtr = EvasionTable;
else if (d >= DEPTH_QS_CHECKS) else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksTable; phasePtr = QsearchWithChecksTable;
else else if (d >= DEPTH_QS_RECAPTURES)
{ {
phasePtr = QsearchWithoutChecksTable; phasePtr = QsearchWithoutChecksTable;
// Skip TT move if is not a capture or a promotion, this avoids // Skip TT move if is not a capture or a promotion, this avoids
// qsearch tree explosion due to a possible perpetual check or // qsearch tree explosion due to a possible perpetual check or
// similar rare cases when TT table is full. // similar rare cases when TT table is full.
if (ttm != MOVE_NONE && !pos.move_is_capture_or_promotion(ttm)) if (ttm != MOVE_NONE && !pos.is_capture_or_promotion(ttm))
searchTT = ttMoves[0].move = MOVE_NONE; ttm = MOVE_NONE;
}
else
{
phasePtr = QsearchRecapturesTable;
recaptureSquare = recaptureSq;
ttm = MOVE_NONE;
} }
phasePtr += int(!searchTT) - 1; ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase();
}
MovePicker::MovePicker(const Position& p, Move ttm, const History& h, PieceType parentCapture)
: pos(p), H(h) {
assert (!pos.in_check());
// In ProbCut we consider only captures better than parent's move
captureThreshold = PieceValueMidgame[Piece(parentCapture)];
phasePtr = ProbCutTable;
if ( ttm != MOVE_NONE
&& (!pos.is_capture(ttm) || pos.see(ttm) <= captureThreshold))
ttm = MOVE_NONE;
ttMove = (ttm && pos.is_pseudo_legal(ttm) ? ttm : MOVE_NONE);
phasePtr += int(ttMove == MOVE_NONE) - 1;
go_next_phase(); go_next_phase();
} }
@@ -128,12 +163,12 @@ void MovePicker::go_next_phase() {
phase = *(++phasePtr); phase = *(++phasePtr);
switch (phase) { switch (phase) {
case PH_TT_MOVES: case PH_TT_MOVE:
curMove = ttMoves; lastMove = curMove + 1;
lastMove = curMove + 2;
return; return;
case PH_GOOD_CAPTURES: case PH_GOOD_CAPTURES:
case PH_GOOD_PROBCUT:
lastMove = generate<MV_CAPTURE>(pos, moves); lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures(); score_captures();
return; return;
@@ -143,10 +178,18 @@ void MovePicker::go_next_phase() {
lastMove = curMove + 2; lastMove = curMove + 2;
return; return;
case PH_NONCAPTURES: case PH_NONCAPTURES_1:
lastMove = generate<MV_NON_CAPTURE>(pos, moves); lastNonCapture = lastMove = generate<MV_NON_CAPTURE>(pos, moves);
score_noncaptures(); score_noncaptures();
sort_moves(moves, lastMove, &lastGoodNonCapture); lastMove = std::partition(curMove, lastMove, has_positive_score);
sort<MoveStack>(curMove, lastMove);
return;
case PH_NONCAPTURES_2:
curMove = lastMove;
lastMove = lastNonCapture;
if (depth >= 3 * ONE_PLY)
sort<MoveStack>(curMove, lastMove);
return; return;
case PH_BAD_CAPTURES: case PH_BAD_CAPTURES:
@@ -167,6 +210,10 @@ void MovePicker::go_next_phase() {
score_captures(); score_captures();
return; return;
case PH_QRECAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
return;
case PH_QCHECKS: case PH_QCHECKS:
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves); lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves);
return; return;
@@ -185,7 +232,7 @@ void MovePicker::go_next_phase() {
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and /// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// MovePicker::score_evasions() assign a numerical move ordering score /// MovePicker::score_evasions() assign a numerical move ordering score
/// to each move in a move list. The moves with highest scores will be /// to each move in a move list. The moves with highest scores will be
/// picked first by get_next_move(). /// picked first by next_move().
void MovePicker::score_captures() { void MovePicker::score_captures() {
// Winning and equal captures in the main search are ordered by MVV/LVA. // Winning and equal captures in the main search are ordered by MVV/LVA.
@@ -207,11 +254,11 @@ void MovePicker::score_captures() {
for (MoveStack* cur = moves; cur != lastMove; cur++) for (MoveStack* cur = moves; cur != lastMove; cur++)
{ {
m = cur->move; m = cur->move;
if (move_is_promotion(m)) cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
cur->score = QueenValueMidgame; - type_of(pos.piece_on(move_from(m)));
else
cur->score = pos.midgame_value_of_piece_on(move_to(m)) if (is_promotion(m))
- pos.type_of_piece_on(move_from(m)); cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))];
} }
} }
@@ -245,22 +292,22 @@ void MovePicker::score_evasions() {
m = cur->move; m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0) if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
else if (pos.move_is_capture(m)) else if (pos.is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m)) cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
- pos.type_of_piece_on(move_from(m)) + History::MaxValue; - type_of(pos.piece_on(move_from(m))) + History::MaxValue;
else else
cur->score = H.value(pos.piece_on(move_from(m)), move_to(m)); cur->score = H.value(pos.piece_on(move_from(m)), move_to(m));
} }
} }
/// MovePicker::get_next_move() is the most important method of the MovePicker /// MovePicker::next_move() is the most important method of the MovePicker class.
/// class. It returns a new legal move every time it is called, until there /// It returns a new pseudo legal move every time it is called, until there
/// are no more moves left. It picks the move with the biggest score from a list /// are no more moves left. It picks the move with the biggest score from a list
/// of generated moves taking care not to return the tt move if has already been /// of generated moves taking care not to return the tt move if has already been
/// searched previously. Note that this function is not thread safe so should be /// searched previously. Note that this function is not thread safe so should be
/// lock protected by caller when accessed through a shared MovePicker object. /// lock protected by caller when accessed through a shared MovePicker object.
Move MovePicker::get_next_move() { Move MovePicker::next_move() {
Move move; Move move;
@@ -271,71 +318,73 @@ Move MovePicker::get_next_move() {
switch (phase) { switch (phase) {
case PH_TT_MOVES: case PH_TT_MOVE:
move = (curMove++)->move; curMove++;
if ( move != MOVE_NONE return ttMove;
&& pos.move_is_legal(move, pinned))
return move;
break; break;
case PH_GOOD_CAPTURES: case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove).move; move = pick_best(curMove++, lastMove)->move;
if ( move != ttMoves[0].move if (move != ttMove)
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
{ {
assert(captureThreshold <= 0); // Otherwise we must use see instead of see_sign
// Check for a non negative SEE now // Check for a non negative SEE now
int seeValue = pos.see_sign(move); int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold) if (seeValue >= captureThreshold)
return move; return move;
// Losing capture, move it to the tail of the array, note // Losing capture, move it to the tail of the array
// that move has now been already checked for legality.
(--badCaptures)->move = move; (--badCaptures)->move = move;
badCaptures->score = seeValue; badCaptures->score = seeValue;
} }
break; break;
case PH_KILLERS: case PH_GOOD_PROBCUT:
move = (curMove++)->move; move = pick_best(curMove++, lastMove)->move;
if ( move != MOVE_NONE if ( move != ttMove
&& pos.move_is_legal(move, pinned) && pos.see(move) > captureThreshold)
&& move != ttMoves[0].move
&& move != ttMoves[1].move
&& !pos.move_is_capture(move))
return move; return move;
break; break;
case PH_NONCAPTURES: case PH_KILLERS:
// Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture)
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
move = (curMove++)->move; move = (curMove++)->move;
if ( move != ttMoves[0].move if ( move != MOVE_NONE
&& move != ttMoves[1].move && pos.is_pseudo_legal(move)
&& move != ttMove
&& !pos.is_capture(move))
return move;
break;
case PH_NONCAPTURES_1:
case PH_NONCAPTURES_2:
move = (curMove++)->move;
if ( move != ttMove
&& move != killers[0].move && move != killers[0].move
&& move != killers[1].move && move != killers[1].move)
&& pos.pl_move_is_legal(move, pinned))
return move; return move;
break; break;
case PH_BAD_CAPTURES: case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move; move = pick_best(curMove++, lastMove)->move;
return move; return move;
case PH_EVASIONS: case PH_EVASIONS:
case PH_QCAPTURES: case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move; move = pick_best(curMove++, lastMove)->move;
if ( move != ttMoves[0].move if (move != ttMove)
&& pos.pl_move_is_legal(move, pinned)) return move;
break;
case PH_QRECAPTURES:
move = (curMove++)->move;
if (move_to(move) == recaptureSquare)
return move; return move;
break; break;
case PH_QCHECKS: case PH_QCHECKS:
move = (curMove++)->move; move = (curMove++)->move;
if ( move != ttMoves[0].move if (move != ttMove)
&& pos.pl_move_is_legal(move, pinned))
return move; return move;
break; break;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,17 +21,15 @@
#define MOVEPICK_H_INCLUDED #define MOVEPICK_H_INCLUDED
#include "history.h" #include "history.h"
#include "move.h"
#include "position.h" #include "position.h"
#include "search.h"
#include "types.h" #include "types.h"
struct SearchStack; /// MovePicker is a class which is used to pick one pseudo legal move at a time
/// from the current position. It is initialized with a Position object and a few
/// 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 /// moves we have reason to believe are good. The most important method is
/// MovePicker::get_next_move(), which returns a new legal move each time it /// MovePicker::next_move(), which returns a new pseudo legal move each time
/// is called, until there are no legal moves left, when MOVE_NONE is returned. /// it is called, until there are no moves left, when MOVE_NONE is returned.
/// In order to improve the efficiency of the alpha beta algorithm, MovePicker /// In order to improve the efficiency of the alpha beta algorithm, MovePicker
/// attempts to return the moves which are most likely to get a cut-off first. /// attempts to return the moves which are most likely to get a cut-off first.
@@ -40,9 +38,10 @@ class MovePicker {
MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC MovePicker& operator=(const MovePicker&); // Silence a warning under MSVC
public: public:
MovePicker(const Position&, Move, Depth, const History&, SearchStack*, Value); MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value);
MovePicker(const Position&, Move, Depth, const History&); MovePicker(const Position&, Move, Depth, const History&, Square recaptureSq);
Move get_next_move(); MovePicker(const Position&, Move, const History&, PieceType parentCapture);
Move next_move();
private: private:
void score_captures(); void score_captures();
@@ -52,11 +51,13 @@ private:
const Position& pos; const Position& pos;
const History& H; const History& H;
Bitboard pinned; Depth depth;
MoveStack ttMoves[2], killers[2]; Move ttMove;
int badCaptureThreshold, phase; MoveStack killers[2];
Square recaptureSquare;
int captureThreshold, phase;
const uint8_t* phasePtr; const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures; MoveStack *curMove, *lastMove, *lastNonCapture, *badCaptures;
MoveStack moves[MAX_MOVES]; MoveStack moves[MAX_MOVES];
}; };

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -72,16 +72,14 @@ namespace {
} }
/// PawnInfoTable::get_pawn_info() takes a position object as input, computes /// PawnInfoTable::pawn_info() takes a position object as input, computes
/// a PawnInfo object, and returns a pointer to it. The result is also stored /// a PawnInfo object, and returns a pointer to it. The result is also stored
/// in an hash table, so we don't have to recompute everything when the same /// in an hash table, so we don't have to recompute everything when the same
/// pawn structure occurs again. /// pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const { PawnInfo* PawnInfoTable::pawn_info(const Position& pos) const {
assert(pos.is_ok()); Key key = pos.pawn_key();
Key key = pos.get_pawn_key();
PawnInfo* pi = probe(key); PawnInfo* pi = probe(key);
// If pi->key matches the position's pawn hash key, it means that we // If pi->key matches the position's pawn hash key, it means that we
@@ -118,7 +116,6 @@ template<Color Us>
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns, Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Bitboard theirPawns, PawnInfo* pi) { Bitboard theirPawns, PawnInfo* pi) {
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE); const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b; Bitboard b;
@@ -127,15 +124,15 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Rank r; Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate; bool passed, isolated, doubled, opposed, chain, backward, candidate;
Score value = SCORE_ZERO; Score value = SCORE_ZERO;
const Square* ptr = pos.piece_list_begin(Us, PAWN); const Square* pl = pos.piece_list(Us, PAWN);
// Loop through all pawns of the current color and score each pawn // Loop through all pawns of the current color and score each pawn
while ((s = *ptr++) != SQ_NONE) while ((s = *pl++) != SQ_NONE)
{ {
assert(pos.piece_on(s) == make_piece(Us, PAWN)); assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = square_file(s); f = file_of(s);
r = square_rank(s); r = rank_of(s);
// This file cannot be half open // This file cannot be half open
pi->halfOpenFiles[Us] &= ~(1 << f); pi->halfOpenFiles[Us] &= ~(1 << f);
@@ -184,8 +181,8 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
// pawn on neighboring files is higher or equal than the number of // pawn on neighboring files is higher or equal than the number of
// enemy pawns in the forward direction on the neighboring files. // enemy pawns in the forward direction on the neighboring files.
candidate = !(opposed | passed | backward | isolated) candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != EmptyBoardBB && (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns); && popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need // Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed // full attack info to evaluate passed pawns. Only the frontmost passed
@@ -225,12 +222,12 @@ Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
if (relative_rank(Us, ksq) <= RANK_4) if (relative_rank(Us, ksq) <= RANK_4)
{ {
pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(ksq); pawns = pos.pieces(PAWN, Us) & this_and_neighboring_files_bb(file_of(ksq));
r = ksq & (7 << 3); r = ksq & (7 << 3);
for (int i = 0; i < 3; i++) for (int i = 0; i < 3; i++)
{ {
r += Shift; r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (64 >> i); shelter += BitCount8Bit[(pawns >> r) & 0xFF] << (6 - i);
} }
} }
kingSquares[Us] = ksq; kingSquares[Us] = ksq;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -29,9 +29,9 @@ const int PawnTableSize = 16384;
/// PawnInfo is a class which contains various information about a pawn /// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game /// structure. Currently, it only includes a middle game and an end game
/// pawn structure evaluation, and a bitboard of passed pawns. We may want /// pawn structure evaluation, and a bitboard of passed pawns. We may want
/// to add further information in the future. A lookup to the pawn hash table /// to add further information in the future. A lookup to the pawn hash
/// (performed by calling the get_pawn_info method in a PawnInfoTable object) /// table (performed by calling the pawn_info method in a PawnInfoTable
/// returns a pointer to a PawnInfo object. /// object) returns a pointer to a PawnInfo object.
class PawnInfo { class PawnInfo {
@@ -63,11 +63,11 @@ private:
/// The PawnInfoTable class represents a pawn hash table. The most important /// The PawnInfoTable class represents a pawn hash table. The most important
/// method is get_pawn_info, which returns a pointer to a PawnInfo object. /// method is pawn_info, which returns a pointer to a PawnInfo object.
class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> { class PawnInfoTable : public SimpleHash<PawnInfo, PawnTableSize> {
public: public:
PawnInfo* get_pawn_info(const Position& pos) const; PawnInfo* pawn_info(const Position& pos) const;
private: private:
template<Color Us> template<Color Us>

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,44 +20,23 @@
#if !defined(POSITION_H_INCLUDED) #if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED #define POSITION_H_INCLUDED
#include <cassert>
#include "bitboard.h" #include "bitboard.h"
#include "move.h"
#include "types.h" #include "types.h"
/// Maximum number of plies per game (220 should be enough, because the
/// maximum search depth is 100, and during position setup we reset the
/// move counter for every non-reversible move).
const int MaxGameLength = 220;
/// The checkInfo struct is initialized at c'tor time and keeps info used
/// to detect if a move gives check.
class Position; class Position;
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo { struct CheckInfo {
explicit CheckInfo(const Position&); explicit CheckInfo(const Position&);
Bitboard dcCandidates; Bitboard dcCandidates;
Bitboard checkSq[8]; Bitboard pinned;
Square ksq; Bitboard checkSq[8];
};
/// Castle rights, encoded as bit fields
enum CastleRights {
CASTLES_NONE = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
}; };
@@ -68,14 +47,14 @@ enum Phase {
struct StateInfo { struct StateInfo {
Key pawnKey, materialKey; Key pawnKey, materialKey;
int castleRights, rule50, gamePly, pliesFromNull;
Square epSquare;
Score value;
Value npMaterial[2]; Value npMaterial[2];
int castleRights, rule50, pliesFromNull;
Score value;
Square epSquare;
PieceType capturedType;
Key key; Key key;
Bitboard checkersBB; Bitboard checkersBB;
PieceType capturedType;
StateInfo* previous; StateInfo* previous;
}; };
@@ -104,35 +83,24 @@ struct StateInfo {
class Position { class Position {
Position(); // No default or copy c'tor allowed // No copy c'tor or assignment operator allowed
Position(const Position& pos); Position(const Position&);
Position& operator=(const Position&);
public: public:
enum GamePhase { Position() {}
MidGame, Position(const Position& pos, int th) { copy(pos, th); }
EndGame Position(const std::string& fen, bool isChess960, int th);
};
// Constructors
Position(const Position& pos, int threadID);
Position(const std::string& fen, bool isChess960, int threadID);
// Text input/output // Text input/output
void copy(const Position& pos, int th);
void from_fen(const std::string& fen, bool isChess960); void from_fen(const std::string& fen, bool isChess960);
const std::string to_fen() const; const std::string to_fen() const;
void print(Move m = MOVE_NONE) const; void print(Move m = MOVE_NONE) const;
// Copying
void flip();
// The piece on a given square // The piece on a given square
Piece piece_on(Square s) const; Piece piece_on(Square s) const;
PieceType type_of_piece_on(Square s) const;
Color color_of_piece_on(Square s) const;
bool square_is_empty(Square s) const; bool square_is_empty(Square s) const;
bool square_is_occupied(Square s) const;
Value midgame_value_of_piece_on(Square s) const;
Value endgame_value_of_piece_on(Square s) const;
// Side to move // Side to move
Color side_to_move() const; Color side_to_move() const;
@@ -140,7 +108,7 @@ public:
// Bitboard representation of the position // Bitboard representation of the position
Bitboard empty_squares() const; Bitboard empty_squares() const;
Bitboard occupied_squares() const; Bitboard occupied_squares() const;
Bitboard pieces_of_color(Color c) const; Bitboard pieces(Color c) const;
Bitboard pieces(PieceType pt) const; Bitboard pieces(PieceType pt) const;
Bitboard pieces(PieceType pt, Color c) const; Bitboard pieces(PieceType pt, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const; Bitboard pieces(PieceType pt1, PieceType pt2) const;
@@ -156,42 +124,37 @@ public:
Square king_square(Color c) const; Square king_square(Color c) const;
// Castling rights // Castling rights
bool can_castle_kingside(Color c) const; bool can_castle(CastleRight f) const;
bool can_castle_queenside(Color c) const;
bool can_castle(Color c) const; bool can_castle(Color c) const;
Square initial_kr_square(Color c) const; Square castle_rook_square(CastleRight f) const;
Square initial_qr_square(Color c) const;
// Bitboards for pinned pieces and discovered check candidates // Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates(Color c) const; Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces(Color c) const; Bitboard pinned_pieces() const;
// Checking pieces and under check information // Checking pieces and under check information
Bitboard checkers() const; Bitboard checkers() const;
bool in_check() const; bool in_check() const;
// Piece lists // Piece lists
Square piece_list(Color c, PieceType pt, int index) const; const Square* piece_list(Color c, PieceType pt) const;
const Square* piece_list_begin(Color c, PieceType pt) const;
// Information about attacks to or from a given square // Information about attacks to or from a given square
Bitboard attackers_to(Square s) const; Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occ) const;
Bitboard attacks_from(Piece p, Square s) const; Bitboard attacks_from(Piece p, Square s) const;
static Bitboard attacks_from(Piece p, Square s, Bitboard occ); static Bitboard attacks_from(Piece p, Square s, Bitboard occ);
template<PieceType> Bitboard attacks_from(Square s) const; template<PieceType> Bitboard attacks_from(Square s) const;
template<PieceType> Bitboard attacks_from(Square s, Color c) const; template<PieceType> Bitboard attacks_from(Square s, Color c) const;
// Properties of moves // Properties of moves
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool pl_move_is_evasion(Move m, Bitboard pinned) const;
bool move_is_legal(const Move m) const;
bool move_is_legal(const Move m, Bitboard pinned) const;
bool move_gives_check(Move m) const;
bool move_gives_check(Move m, const CheckInfo& ci) const; bool move_gives_check(Move m, const CheckInfo& ci) const;
bool move_is_capture(Move m) const;
bool move_is_capture_or_promotion(Move m) const;
bool move_is_passed_pawn_push(Move m) const;
bool move_attacks_square(Move m, Square s) const; bool move_attacks_square(Move m, Square s) const;
bool pl_move_is_legal(Move m, Bitboard pinned) const;
bool is_pseudo_legal(const Move m) const;
bool is_capture(Move m) const;
bool is_capture_or_promotion(Move m) const;
bool is_passed_pawn_push(Move m) const;
// Piece captured with previous moves // Piece captured with previous moves
PieceType captured_piece_type() const; PieceType captured_piece_type() const;
@@ -199,38 +162,32 @@ public:
// Information about pawns // Information about pawns
bool pawn_is_passed(Color c, Square s) const; bool pawn_is_passed(Color c, Square s) const;
// Weak squares
bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves // Doing and undoing moves
void do_setup_move(Move m);
void do_move(Move m, StateInfo& st); void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck); void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m); void undo_move(Move m);
void do_null_move(StateInfo& st); template<bool Do> void do_null_move(StateInfo& st);
void undo_null_move();
// Static exchange evaluation // Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const; int see(Move m) const;
int see_sign(Move m) const; int see_sign(Move m) const;
// Accessing hash keys // Accessing hash keys
Key get_key() const; Key key() const;
Key get_exclusion_key() const; Key exclusion_key() const;
Key get_pawn_key() const; Key pawn_key() const;
Key get_material_key() const; Key material_key() const;
// Incremental evaluation // Incremental evaluation
Score value() const; Score value() const;
Value non_pawn_material(Color c) const; Value non_pawn_material(Color c) const;
static Score pst_delta(Piece piece, Square from, Square to); Score pst_delta(Piece piece, Square from, Square to) const;
// Game termination checks // Game termination checks
bool is_mate() const; bool is_mate() const;
bool is_draw() const; template<bool SkipRepetition> bool is_draw() const;
// Number of plies from starting position // Plies from start position to the beginning of search
int startpos_ply_counter() const; int startpos_ply_counter() const;
// Other properties of the position // Other properties of the position
@@ -245,30 +202,23 @@ public:
void set_nodes_searched(int64_t n); void set_nodes_searched(int64_t n);
// Position consistency check, for debugging // Position consistency check, for debugging
bool is_ok(int* failedStep = NULL) const; bool pos_is_ok(int* failedStep = NULL) const;
void flip_me();
// Static member functions // Global initialization
static void init_zobrist(); static void init();
static void init_piece_square_tables();
private: private:
// Initialization helper functions (used while setting up a position) // Initialization helper functions (used while setting up a position)
void clear(); void clear();
void detach();
void put_piece(Piece p, Square s); void put_piece(Piece p, Square s);
void do_allow_oo(Color c); void set_castle_right(Square ksq, Square rsq);
void do_allow_ooo(Color c); bool move_is_legal(const Move m) const;
bool set_castling_rights(char token);
// Helper functions for doing and undoing moves // Helper template functions
void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep); template<bool Do> void do_castle_move(Move m);
void do_castle_move(Move m); template<bool FindPinned> Bitboard hidden_checkers() const;
void undo_castle_move(Move m);
void find_checkers();
template<bool FindPinned>
Bitboard hidden_checkers(Color c) const;
// Computing hash keys from scratch (for initialization and debugging) // Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const; Key compute_key() const;
@@ -276,45 +226,43 @@ private:
Key compute_material_key() const; Key compute_material_key() const;
// Computing incremental evaluation scores and material counts // Computing incremental evaluation scores and material counts
static Score pst(Color c, PieceType pt, Square s); Score pst(Piece p, Square s) const;
Score compute_value() const; Score compute_value() const;
Value compute_non_pawn_material(Color c) const; Value compute_non_pawn_material(Color c) const;
// Board // Board
Piece board[64]; Piece board[64]; // [square]
// Bitboards // Bitboards
Bitboard byTypeBB[8], byColorBB[2]; Bitboard byTypeBB[8]; // [pieceType]
Bitboard byColorBB[2]; // [color]
Bitboard occupied;
// Piece counts // Piece counts
int pieceCount[2][8]; // [color][pieceType] int pieceCount[2][8]; // [color][pieceType]
// Piece lists // Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index] Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square] int index[64]; // [square]
// Other info // Other info
Color sideToMove; int castleRightsMask[64]; // [square]
Key history[MaxGameLength]; Square castleRookSquare[16]; // [castleRight]
int castleRightsMask[64];
StateInfo startState; StateInfo startState;
File initialKFile, initialKRFile, initialQRFile;
bool chess960;
int startPosPlyCounter;
int threadID;
int64_t nodes; int64_t nodes;
int startPosPly;
Color sideToMove;
int threadID;
StateInfo* st; StateInfo* st;
int chess960;
// Static variables // Static variables
static Key zobrist[2][8][64]; static Score pieceSquareTable[16][64]; // [piece][square]
static Key zobEp[64]; static Key zobrist[2][8][64]; // [color][pieceType][square]/[piece count]
static Key zobCastle[16]; static Key zobEp[64]; // [square]
static Key zobCastle[16]; // [castleRight]
static Key zobSideToMove; static Key zobSideToMove;
static Score PieceSquareTable[16][64];
static Key zobExclusion; static Key zobExclusion;
static const Value seeValues[8];
static const Value PieceValueMidgame[17];
static const Value PieceValueEndgame[17];
}; };
inline int64_t Position::nodes_searched() const { inline int64_t Position::nodes_searched() const {
@@ -329,28 +277,8 @@ inline Piece Position::piece_on(Square s) const {
return board[s]; return board[s];
} }
inline Color Position::color_of_piece_on(Square s) const {
return color_of_piece(piece_on(s));
}
inline PieceType Position::type_of_piece_on(Square s) const {
return type_of_piece(piece_on(s));
}
inline bool Position::square_is_empty(Square s) const { inline bool Position::square_is_empty(Square s) const {
return piece_on(s) == PIECE_NONE; return board[s] == NO_PIECE;
}
inline bool Position::square_is_occupied(Square s) const {
return !square_is_empty(s);
}
inline Value Position::midgame_value_of_piece_on(Square s) const {
return PieceValueMidgame[piece_on(s)];
}
inline Value Position::endgame_value_of_piece_on(Square s) const {
return PieceValueEndgame[piece_on(s)];
} }
inline Color Position::side_to_move() const { inline Color Position::side_to_move() const {
@@ -358,14 +286,14 @@ inline Color Position::side_to_move() const {
} }
inline Bitboard Position::occupied_squares() const { inline Bitboard Position::occupied_squares() const {
return byTypeBB[0]; return occupied;
} }
inline Bitboard Position::empty_squares() const { inline Bitboard Position::empty_squares() const {
return ~occupied_squares(); return ~occupied;
} }
inline Bitboard Position::pieces_of_color(Color c) const { inline Bitboard Position::pieces(Color c) const {
return byColorBB[c]; return byColorBB[c];
} }
@@ -389,11 +317,7 @@ inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt]; return pieceCount[c][pt];
} }
inline Square Position::piece_list(Color c, PieceType pt, int idx) const { inline const Square* Position::piece_list(Color c, PieceType pt) const {
return pieceList[c][pt][idx];
}
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
return pieceList[c][pt]; return pieceList[c][pt];
} }
@@ -405,24 +329,16 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0]; return pieceList[c][KING][0];
} }
inline bool Position::can_castle_kingside(Color side) const { inline bool Position::can_castle(CastleRight f) const {
return st->castleRights & (1+int(side)); return st->castleRights & f;
} }
inline bool Position::can_castle_queenside(Color side) const { inline bool Position::can_castle(Color c) const {
return st->castleRights & (4+4*int(side)); return st->castleRights & ((WHITE_OO | WHITE_OOO) << c);
} }
inline bool Position::can_castle(Color side) const { inline Square Position::castle_rook_square(CastleRight f) const {
return can_castle_kingside(side) || can_castle_queenside(side); return castleRookSquare[f];
}
inline Square Position::initial_kr_square(Color c) const {
return relative_square(c, make_square(initialKRFile, RANK_1));
}
inline Square Position::initial_qr_square(Color c) const {
return relative_square(c, make_square(initialQRFile, RANK_1));
} }
template<> template<>
@@ -450,44 +366,56 @@ inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s); return attacks_from<ROOK>(s) | attacks_from<BISHOP>(s);
} }
inline Bitboard Position::attacks_from(Piece p, Square s) const {
return attacks_from(p, s, occupied_squares());
}
inline Bitboard Position::attackers_to(Square s) const {
return attackers_to(s, occupied_squares());
}
inline Bitboard Position::checkers() const { inline Bitboard Position::checkers() const {
return st->checkersBB; return st->checkersBB;
} }
inline bool Position::in_check() const { inline bool Position::in_check() const {
return st->checkersBB != EmptyBoardBB; return st->checkersBB != 0;
}
inline Bitboard Position::discovered_check_candidates() const {
return hidden_checkers<false>();
}
inline Bitboard Position::pinned_pieces() const {
return hidden_checkers<true>();
} }
inline bool Position::pawn_is_passed(Color c, Square s) const { inline bool Position::pawn_is_passed(Color c, Square s) const {
return !(pieces(PAWN, opposite_color(c)) & passed_pawn_mask(c, s)); return !(pieces(PAWN, flip(c)) & passed_pawn_mask(c, s));
} }
inline bool Position::square_is_weak(Square s, Color c) const { inline Key Position::key() const {
return !(pieces(PAWN, opposite_color(c)) & attack_span_mask(c, s));
}
inline Key Position::get_key() const {
return st->key; return st->key;
} }
inline Key Position::get_exclusion_key() const { inline Key Position::exclusion_key() const {
return st->key ^ zobExclusion; return st->key ^ zobExclusion;
} }
inline Key Position::get_pawn_key() const { inline Key Position::pawn_key() const {
return st->pawnKey; return st->pawnKey;
} }
inline Key Position::get_material_key() const { inline Key Position::material_key() const {
return st->materialKey; return st->materialKey;
} }
inline Score Position::pst(Color c, PieceType pt, Square s) { inline Score Position::pst(Piece p, Square s) const {
return PieceSquareTable[make_piece(c, pt)][s]; return pieceSquareTable[p][s];
} }
inline Score Position::pst_delta(Piece piece, Square from, Square to) { inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from]; return pieceSquareTable[piece][to] - pieceSquareTable[piece][from];
} }
inline Score Position::value() const { inline Score Position::value() const {
@@ -498,21 +426,21 @@ inline Value Position::non_pawn_material(Color c) const {
return st->npMaterial[c]; return st->npMaterial[c];
} }
inline bool Position::move_is_passed_pawn_push(Move m) const { inline bool Position::is_passed_pawn_push(Move m) const {
Color c = side_to_move(); return board[move_from(m)] == make_piece(sideToMove, PAWN)
return piece_on(move_from(m)) == make_piece(c, PAWN) && pawn_is_passed(sideToMove, move_to(m));
&& pawn_is_passed(c, move_to(m));
} }
inline int Position::startpos_ply_counter() const { inline int Position::startpos_ply_counter() const {
return startPosPlyCounter; return startPosPly + st->pliesFromNull; // HACK
} }
inline bool Position::opposite_colored_bishops() const { inline bool Position::opposite_colored_bishops() const {
return piece_count(WHITE, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1 return pieceCount[WHITE][BISHOP] == 1
&& opposite_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0)); && pieceCount[BLACK][BISHOP] == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
} }
inline bool Position::has_pawn_on_7th(Color c) const { inline bool Position::has_pawn_on_7th(Color c) const {
@@ -523,16 +451,17 @@ inline bool Position::is_chess960() const {
return chess960; return chess960;
} }
inline bool Position::move_is_capture(Move m) const { inline bool Position::is_capture_or_promotion(Move m) const {
// Move must not be MOVE_NONE ! assert(is_ok(m));
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m)); return is_special(m) ? !is_castle(m) : !square_is_empty(move_to(m));
} }
inline bool Position::move_is_capture_or_promotion(Move m) const { inline bool Position::is_capture(Move m) const {
// Move must not be MOVE_NONE ! // Note that castle is coded as "king captures the rook"
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m)); assert(is_ok(m));
return (!square_is_empty(move_to(m)) && !is_castle(m)) || is_enpassant(m);
} }
inline PieceType Position::captured_piece_type() const { inline PieceType Position::captured_piece_type() const {
@@ -543,12 +472,4 @@ inline int Position::thread() const {
return threadID; return threadID;
} }
inline void Position::do_allow_oo(Color c) {
st->castleRights |= (1 + int(c));
}
inline void Position::do_allow_ooo(Color c) {
st->castleRights |= (4 + 4*int(c));
}
#endif // !defined(POSITION_H_INCLUDED) #endif // !defined(POSITION_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,164 +22,77 @@
#include "types.h" #include "types.h"
namespace { #define S(mg, eg) make_score(mg, eg)
////
//// Constants modified by Joona Kiiski
////
const Value MP = PawnValueMidgame; /// PSQT[PieceType][Square] contains Piece-Square scores. For each piece type on
const Value MK = KnightValueMidgame; /// a given square a (midgame, endgame) score pair is assigned. PSQT is defined
const Value MB = BishopValueMidgame; /// for white side, for black side the tables are symmetric.
const Value MR = RookValueMidgame;
const Value MQ = QueenValueMidgame;
const Value EP = PawnValueEndgame; static const Score PSQT[][64] = {
const Value EK = KnightValueEndgame;
const Value EB = BishopValueEndgame;
const Value ER = RookValueEndgame;
const Value EQ = QueenValueEndgame;
const int MgPST[][64] = {
{ }, { },
{// Pawn { // Pawn
// A B C D E F G H S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0),
0, 0, 0, 0, 0, 0, 0, 0, S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28, S(-28,-8), S(-6,-8), S( 9,-8), S(36,-8), S(36,-8), S( 9,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+ 9, MP+36, MP+36, MP+ 9, MP-6, MP-28, S(-28,-8), S(-6,-8), S(17,-8), S(58,-8), S(58,-8), S(17,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+17, MP+58, MP+58, MP+17, MP-6, MP-28, S(-28,-8), S(-6,-8), S(17,-8), S(36,-8), S(36,-8), S(17,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+17, MP+36, MP+36, MP+17, MP-6, MP-28, S(-28,-8), S(-6,-8), S( 9,-8), S(14,-8), S(14,-8), S( 9,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+ 9, MP+14, MP+14, MP+ 9, MP-6, MP-28, S(-28,-8), S(-6,-8), S( 4,-8), S(14,-8), S(14,-8), S( 4,-8), S(-6,-8), S(-28,-8),
MP-28, MP-6, MP+ 4, MP+14, MP+14, MP+ 4, MP-6, MP-28, S( 0, 0), S( 0, 0), S( 0, 0), S( 0, 0), S(0, 0), S( 0, 0), S( 0, 0), S( 0, 0)
0, 0, 0, 0, 0, 0, 0, 0
}, },
{// Knight { // Knight
// A B C D E F G H S(-135,-104), S(-107,-79), S(-80,-55), S(-67,-42), S(-67,-42), S(-80,-55), S(-107,-79), S(-135,-104),
MK-135, MK-107, MK-80, MK-67, MK-67, MK-80, MK-107, MK-135, S( -93, -79), S( -67,-55), S(-39,-30), S(-25,-17), S(-25,-17), S(-39,-30), S( -67,-55), S( -93, -79),
MK- 93, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK- 93, S( -53, -55), S( -25,-30), S( 1, -6), S( 13, 5), S( 13, 5), S( 1, -6), S( -25,-30), S( -53, -55),
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53, S( -25, -42), S( 1,-17), S( 27, 5), S( 41, 18), S( 41, 18), S( 27, 5), S( 1,-17), S( -25, -42),
MK- 25, MK+ 1, MK+27, MK+41, MK+41, MK+27, MK+ 1, MK- 25, S( -11, -42), S( 13,-17), S( 41, 5), S( 55, 18), S( 55, 18), S( 41, 5), S( 13,-17), S( -11, -42),
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11, S( -11, -55), S( 13,-30), S( 41, -6), S( 55, 5), S( 55, 5), S( 41, -6), S( 13,-30), S( -11, -55),
MK- 11, MK+ 13, MK+41, MK+55, MK+55, MK+41, MK+ 13, MK- 11, S( -53, -79), S( -25,-55), S( 1,-30), S( 13,-17), S( 13,-17), S( 1,-30), S( -25,-55), S( -53, -79),
MK- 53, MK- 25, MK+ 1, MK+13, MK+13, MK+ 1, MK- 25, MK- 53, S(-193,-104), S( -67,-79), S(-39,-55), S(-25,-42), S(-25,-42), S(-39,-55), S( -67,-79), S(-193,-104)
MK-193, MK- 67, MK-39, MK-25, MK-25, MK-39, MK- 67, MK-193
}, },
{// Bishop { // Bishop
// A B C D E F G H S(-40,-59), S(-40,-42), S(-35,-35), S(-30,-26), S(-30,-26), S(-35,-35), S(-40,-42), S(-40,-59),
MB-40, MB-40, MB-35, MB-30, MB-30, MB-35, MB-40, MB-40, S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42),
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17, S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35),
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13, S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26),
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8, S( -8,-26), S( 0,-11), S( 4, -4), S( 17, 4), S( 17, 4), S( 4, -4), S( 0,-11), S( -8,-26),
MB- 8, MB+ 0, MB+ 4, MB+17, MB+17, MB+ 4, MB+ 0, MB- 8, S(-13,-35), S( -4,-18), S( 8,-11), S( 4, -4), S( 4, -4), S( 8,-11), S( -4,-18), S(-13,-35),
MB-13, MB- 4, MB+ 8, MB+ 4, MB+ 4, MB+ 8, MB- 4, MB-13, S(-17,-42), S( 0,-26), S( -4,-18), S( 0,-11), S( 0,-11), S( -4,-18), S( 0,-26), S(-17,-42),
MB-17, MB+ 0, MB- 4, MB+ 0, MB+ 0, MB- 4, MB+ 0, MB-17, S(-17,-59), S(-17,-42), S(-13,-35), S( -8,-26), S( -8,-26), S(-13,-35), S(-17,-42), S(-17,-59)
MB-17, MB-17, MB-13, MB- 8, MB- 8, MB-13, MB-17, MB-17
}, },
{// Rook { // Rook
// A B C D E F G H S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3),
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12, S(-12, 3), S(-7, 3), S(-2, 3), S(2, 3), S(2, 3), S(-2, 3), S(-7, 3), S(-12, 3)
MR-12, MR-7, MR-2, MR+2, MR+2, MR-2, MR-7, MR-12
}, },
{// Queen { // Queen
// A B C D E F G H S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-30), S(8, -6), S(8, 6), S(8, 18), S(8, 18), S(8, 6), S(8, -6), S(8,-30),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-42), S(8,-18), S(8, -6), S(8, 6), S(8, 6), S(8, -6), S(8,-18), S(8,-42),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-54), S(8,-30), S(8,-18), S(8, -6), S(8, -6), S(8,-18), S(8,-30), S(8,-54),
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, S(8,-80), S(8,-54), S(8,-42), S(8,-30), S(8,-30), S(8,-42), S(8,-54), S(8,-80)
MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8, MQ+8
}, },
{// King { // King
//A B C D E F G H S(287, 18), S(311, 77), S(262,105), S(214,135), S(214,135), S(262,105), S(311, 77), S(287, 18),
287, 311, 262, 214, 214, 262, 311, 287, S(262, 77), S(287,135), S(238,165), S(190,193), S(190,193), S(238,165), S(287,135), S(262, 77),
262, 287, 238, 190, 190, 238, 287, 262, S(214,105), S(238,165), S(190,193), S(142,222), S(142,222), S(190,193), S(238,165), S(214,105),
214, 238, 190, 142, 142, 190, 238, 214, S(190,135), S(214,193), S(167,222), S(119,251), S(119,251), S(167,222), S(214,193), S(190,135),
190, 214, 167, 119, 119, 167, 214, 190, S(167,135), S(190,193), S(142,222), S( 94,251), S( 94,251), S(142,222), S(190,193), S(167,135),
167, 190, 142, 94, 94, 142, 190, 167, S(142,105), S(167,165), S(119,193), S( 69,222), S( 69,222), S(119,193), S(167,165), S(142,105),
142, 167, 119, 69, 69, 119, 167, 142, S(119, 77), S(142,135), S( 94,165), S( 46,193), S( 46,193), S( 94,165), S(142,135), S(119, 77),
119, 142, 94, 46, 46, 94, 142, 119, S(94, 18), S(119, 77), S( 69,105), S( 21,135), S( 21,135), S( 69,105), S(119, 77), S( 94, 18)
94, 119, 69, 21, 21, 69, 119, 94
} }
}; };
const int EgPST[][64] = { #undef S
{ },
{// Pawn
// A B C D E F G H
0, 0, 0, 0, 0, 0, 0, 0,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8, EP-8,
0, 0, 0, 0, 0, 0, 0, 0
},
{// Knight
// A B C D E F G H
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104,
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
EK- 42, EK-17, EK+ 5, EK+18, EK+18, EK+ 5, EK-17, EK- 42,
EK- 55, EK-30, EK- 6, EK+ 5, EK+ 5, EK- 6, EK-30, EK- 55,
EK- 79, EK-55, EK-30, EK-17, EK-17, EK-30, EK-55, EK- 79,
EK-104, EK-79, EK-55, EK-42, EK-42, EK-55, EK-79, EK-104
},
{// Bishop
// A B C D E F G H
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59,
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
EB-26, EB-11, EB- 4, EB+ 4, EB+ 4, EB- 4, EB-11, EB-26,
EB-35, EB-18, EB-11, EB- 4, EB- 4, EB-11, EB-18, EB-35,
EB-42, EB-26, EB-18, EB-11, EB-11, EB-18, EB-26, EB-42,
EB-59, EB-42, EB-35, EB-26, EB-26, EB-35, EB-42, EB-59
},
{// Rook
// A B C D E F G H
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3,
ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3, ER+3
},
{// Queen
// A B C D E F G H
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80,
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
EQ-30, EQ- 6, EQ+ 6, EQ+18, EQ+18, EQ+ 6, EQ- 6, EQ-30,
EQ-42, EQ-18, EQ- 6, EQ+ 6, EQ+ 6, EQ- 6, EQ-18, EQ-42,
EQ-54, EQ-30, EQ-18, EQ- 6, EQ- 6, EQ-18, EQ-30, EQ-54,
EQ-80, EQ-54, EQ-42, EQ-30, EQ-30, EQ-42, EQ-54, EQ-80
},
{// King
//A B C D E F G H
18, 77, 105, 135, 135, 105, 77, 18,
77, 135, 165, 193, 193, 165, 135, 77,
105, 165, 193, 222, 222, 193, 165, 105,
135, 193, 222, 251, 251, 222, 193, 135,
135, 193, 222, 251, 251, 222, 193, 135,
105, 165, 193, 222, 222, 193, 165, 105,
77, 135, 165, 193, 193, 165, 135, 77,
18, 77, 105, 135, 135, 105, 77, 18
}
};
} // namespace
#endif // !defined(PSQTAB_H_INCLUDED) #endif // !defined(PSQTAB_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,19 +20,6 @@
available under the GNU General Public License as published by available under the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or the Free Software Foundation, either version 3 of the License, or
(at your option) any later version. (at your option) any later version.
** George Marsaglia invented the RNG-Kiss-family in the early 90's.
** This is a specific version that Heinz van Saanen derived and
** tested from some public domain code by Bob Jenkins:
**
** Quite platform independent
** Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
** ~12 times faster than my *nix sys-rand()
** ~4 times faster than SSE2-version of Mersenne twister
** Average cycle length: ~2^126
** 64 bit seed
** Return doubles with a full 53 bit mantissa
** Thread safe
*/ */
#if !defined(RKISS_H_INCLUDED) #if !defined(RKISS_H_INCLUDED)
@@ -40,6 +27,20 @@
#include "types.h" #include "types.h"
/// RKISS is our pseudo random number generator (PRNG) used to compute hash keys.
/// George Marsaglia invented the RNG-Kiss-family in the early 90's. This is a
/// specific version that Heinz van Saanen derived from some public domain code
/// by Bob Jenkins. Following the feature list, as tested by Heinz.
///
/// - Quite platform independent
/// - Passes ALL dieharder tests! Here *nix sys-rand() e.g. fails miserably:-)
/// - ~12 times faster than my *nix sys-rand()
/// - ~4 times faster than SSE2-version of Mersenne twister
/// - Average cycle length: ~2^126
/// - 64 bit seed
/// - Return doubles with a full 53 bit mantissa
/// - Thread safe
class RKISS { class RKISS {
// Keep variables always together // Keep variables always together

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -21,53 +21,62 @@
#define SEARCH_H_INCLUDED #define SEARCH_H_INCLUDED
#include <cstring> #include <cstring>
#include <vector>
#include "move.h"
#include "types.h" #include "types.h"
class Position; class Position;
struct SplitPoint; struct SplitPoint;
/// The SearchStack struct keeps track of the information we need to remember namespace Search {
/// from nodes shallower and deeper in the tree during the search. Each
/// search thread has its own array of SearchStack objects, indexed by the
/// current ply.
struct SearchStack { /// The Stack struct keeps track of the information we need to remember from
/// nodes shallower and deeper in the tree during the search. Each search thread
/// has its own array of Stack objects, indexed by the current ply.
struct Stack {
SplitPoint* sp;
int ply; int ply;
Move currentMove; Move currentMove;
Move mateKiller;
Move excludedMove; Move excludedMove;
Move bestMove; Move bestMove;
Move killers[2]; Move killers[2];
Depth reduction; Depth reduction;
Value eval; Value eval;
Value evalMargin; Value evalMargin;
bool skipNullMove; int skipNullMove;
SplitPoint* sp;
}; };
/// The SearchLimits struct stores information sent by GUI about available time /// The LimitsType struct stores information sent by GUI about available time
/// to search the current move, maximum depth/time, if we are in analysis mode /// to search the current move, maximum depth/time, if we are in analysis mode
/// or if we have to ponder while is our opponent's side to move. /// or if we have to ponder while is our opponent's side to move.
struct SearchLimits { struct LimitsType {
SearchLimits() { memset(this, 0, sizeof(SearchLimits)); } LimitsType() { memset(this, 0, sizeof(LimitsType)); }
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | infinite); }
SearchLimits(int t, int i, int mtg, int mt, int md, int mn, bool inf, bool pon) int time, increment, movesToGo, maxTime, maxDepth, maxNodes, infinite, ponder;
: time(t), increment(i), movesToGo(mtg), maxTime(mt), maxDepth(md),
maxNodes(mn), infinite(inf), ponder(pon) {}
bool useTimeManagement() const { return !(maxTime | maxDepth | maxNodes | int(infinite)); }
int time, increment, movesToGo, maxTime, maxDepth, maxNodes;
bool infinite, ponder;
}; };
extern void init_search();
/// The SignalsType struct stores volatile flags updated during the search
/// typically in an async fashion, for instance to stop the search by the GUI.
struct SignalsType {
bool stopOnPonderhit, firstRootMove, stop, failedLowAtRoot;
};
extern volatile SignalsType Signals;
extern LimitsType Limits;
extern std::vector<Move> SearchMoves;
extern Position RootPosition;
extern void init();
extern int64_t perft(Position& pos, Depth depth); extern int64_t perft(Position& pos, Depth depth);
extern bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]); extern void think();
} // namespace
#endif // !defined(SEARCH_H_INCLUDED) #endif // !defined(SEARCH_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -19,36 +19,41 @@
#include <iostream> #include <iostream>
#include "search.h"
#include "thread.h" #include "thread.h"
#include "ucioption.h" #include "ucioption.h"
ThreadsManager Threads; // Global object definition using namespace Search;
ThreadsManager Threads; // Global object
namespace { extern "C" { namespace { extern "C" {
// start_routine() is the C function which is called when a new thread // start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() with the supplied threadID. // is launched. It simply calls idle_loop() of the supplied thread. The first
// There are two versions of this function; one for POSIX threads and // and last thread are special. First one is the main search thread while the
// one for Windows threads. // last one mimics a timer, they run in main_loop() and timer_loop().
#if defined(_MSC_VER) #if defined(_MSC_VER)
DWORD WINAPI start_routine(LPVOID thread) {
#else
void* start_routine(void* thread) {
#endif
DWORD WINAPI start_routine(LPVOID threadID) { Thread* th = (Thread*)thread;
if (th->threadID == 0)
th->main_loop();
else if (th->threadID == MAX_THREADS)
th->timer_loop();
else
th->idle_loop(NULL);
Threads.idle_loop(*(int*)threadID, NULL);
return 0; return 0;
} }
#else
void* start_routine(void* threadID) {
Threads.idle_loop(*(int*)threadID, NULL);
return NULL;
}
#endif
} } } }
@@ -63,15 +68,15 @@ void Thread::wake_up() {
} }
// cutoff_occurred() checks whether a beta cutoff has occurred in // cutoff_occurred() checks whether a beta cutoff has occurred in the current
// the thread's currently active split point, or in some ancestor of // active split point, or in some ancestor of the split point.
// the current split point.
bool Thread::cutoff_occurred() const { bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent) for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff) if (sp->is_betaCutoff)
return true; return true;
return false; return false;
} }
@@ -85,7 +90,7 @@ bool Thread::cutoff_occurred() const {
bool Thread::is_available_to(int master) const { bool Thread::is_available_to(int master) const {
if (state != AVAILABLE) if (is_searching)
return false; return false;
// Make a local copy to be sure doesn't become zero under our feet while // Make a local copy to be sure doesn't become zero under our feet while
@@ -102,16 +107,41 @@ bool Thread::is_available_to(int master) const {
} }
// read_uci_options() updates number of active threads and other internal // read_uci_options() updates number of active threads and other parameters
// parameters according to the UCI options values. It is called before // according to the UCI options values. It is called before to start a new search.
// to start a new search.
void ThreadsManager::read_uci_options() { void ThreadsManager::read_uci_options() {
maxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value<int>(); maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Minimum Split Depth"].value<int>() * ONE_PLY; minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"].value<bool>(); useSleepingThreads = Options["Use Sleeping Threads"];
activeThreads = Options["Threads"].value<int>();
set_size(Options["Threads"]);
}
// set_size() changes the number of active threads and raises do_sleep flag for
// all the unused threads that will go immediately to sleep.
void ThreadsManager::set_size(int cnt) {
assert(cnt > 0 && cnt <= MAX_THREADS);
activeThreads = cnt;
for (int i = 1; i < MAX_THREADS; i++) // Ignore main thread
if (i < activeThreads)
{
// Dynamically allocate pawn and material hash tables according to the
// number of active threads. This avoids preallocating memory for all
// possible threads if only few are used.
threads[i].pawnTable.init();
threads[i].materialTable.init();
threads[i].do_sleep = false;
}
else
threads[i].do_sleep = true;
} }
@@ -120,22 +150,12 @@ void ThreadsManager::read_uci_options() {
void ThreadsManager::init() { void ThreadsManager::init() {
int threadID[MAX_THREADS]; // Initialize sleep condition and lock used by thread manager
cond_init(&sleepCond);
lock_init(&threadsLock);
// This flag is needed to properly end the threads when program exits // Initialize thread's sleep conditions and split point locks
allThreadsShouldExit = false; for (int i = 0; i <= MAX_THREADS; i++)
// Threads will sent to sleep as soon as created, only main thread is kept alive
activeThreads = 1;
threads[0].state = Thread::SEARCHING;
// Allocate pawn and material hash tables for main thread
init_hash_tables();
lock_init(&mpLock);
// Initialize thread and split point locks
for (int i = 0; i < MAX_THREADS; i++)
{ {
lock_init(&threads[i].sleepLock); lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond); cond_init(&threads[i].sleepCond);
@@ -144,48 +164,51 @@ void ThreadsManager::init() {
lock_init(&(threads[i].splitPoints[j].lock)); lock_init(&(threads[i].splitPoints[j].lock));
} }
// Create and startup all the threads but the main that is already running // Allocate main thread tables to call evaluate() also when not searching
for (int i = 1; i < MAX_THREADS; i++) threads[0].pawnTable.init();
threads[0].materialTable.init();
// Create and launch all the threads, threads will go immediately to sleep
for (int i = 0; i <= MAX_THREADS; i++)
{ {
threads[i].state = Thread::INITIALIZING; threads[i].is_searching = false;
threadID[i] = i; threads[i].do_sleep = true;
threads[i].threadID = i;
#if defined(_MSC_VER) #if defined(_MSC_VER)
bool ok = (CreateThread(NULL, 0, start_routine, (LPVOID)&threadID[i], 0, NULL) != NULL); threads[i].handle = CreateThread(NULL, 0, start_routine, &threads[i], 0, NULL);
bool ok = (threads[i].handle != NULL);
#else #else
pthread_t pthreadID; bool ok = !pthread_create(&threads[i].handle, NULL, start_routine, &threads[i]);
bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threadID[i]) == 0);
pthread_detach(pthreadID);
#endif #endif
if (!ok) if (!ok)
{ {
std::cout << "Failed to create thread number " << i << std::endl; std::cerr << "Failed to create thread number " << i << std::endl;
::exit(EXIT_FAILURE); ::exit(EXIT_FAILURE);
} }
// Wait until the thread has finished launching and is gone to sleep
while (threads[i].state == Thread::INITIALIZING) {}
} }
} }
// exit() is called to cleanly exit the threads when the program finishes // exit() is called to cleanly terminate the threads when the program finishes
void ThreadsManager::exit() { void ThreadsManager::exit() {
// Force the woken up threads to exit idle_loop() and hence terminate for (int i = 0; i <= MAX_THREADS; i++)
allThreadsShouldExit = true;
for (int i = 0; i < MAX_THREADS; i++)
{ {
// Wake up all the threads and waits for termination threads[i].do_terminate = true; // Search must be already finished
if (i != 0) threads[i].wake_up();
{
threads[i].wake_up();
while (threads[i].state != Thread::TERMINATED) {}
}
// Now we can safely destroy the locks and wait conditions // Wait for thread termination
#if defined(_MSC_VER)
WaitForSingleObject(threads[i].handle, 0);
CloseHandle(threads[i].handle);
#else
pthread_join(threads[i].handle, NULL);
#endif
// Now we can safely destroy associated locks and wait conditions
lock_destroy(&threads[i].sleepLock); lock_destroy(&threads[i].sleepLock);
cond_destroy(&threads[i].sleepCond); cond_destroy(&threads[i].sleepCond);
@@ -193,58 +216,56 @@ void ThreadsManager::exit() {
lock_destroy(&(threads[i].splitPoints[j].lock)); lock_destroy(&(threads[i].splitPoints[j].lock));
} }
lock_destroy(&mpLock); lock_destroy(&threadsLock);
} cond_destroy(&sleepCond);
// init_hash_tables() dynamically allocates pawn and material hash tables
// according to the number of active threads. This avoids preallocating
// memory for all possible threads if only few are used as, for instance,
// on mobile devices where memory is scarce and allocating for MAX_THREADS
// threads could even result in a crash.
void ThreadsManager::init_hash_tables() {
for (int i = 0; i < activeThreads; i++)
{
threads[i].pawnTable.init();
threads[i].materialTable.init();
}
} }
// available_slave_exists() tries to find an idle thread which is available as // available_slave_exists() tries to find an idle thread which is available as
// a slave for the thread with threadID "master". // a slave for the thread with threadID 'master'.
bool ThreadsManager::available_slave_exists(int master) const { bool ThreadsManager::available_slave_exists(int master) const {
assert(master >= 0 && master < activeThreads); assert(master >= 0 && master < activeThreads);
for (int i = 0; i < activeThreads; i++) for (int i = 0; i < activeThreads; i++)
if (i != master && threads[i].is_available_to(master)) if (threads[i].is_available_to(master))
return true; return true;
return false; return false;
} }
// split_point_finished() checks if all the slave threads of a given split
// point have finished searching.
bool ThreadsManager::split_point_finished(SplitPoint* sp) const {
for (int i = 0; i < activeThreads; i++)
if (sp->is_slave[i])
return false;
return true;
}
// split() does the actual work of distributing the work at a node between // split() does the actual work of distributing the work at a node between
// several available threads. If it does not succeed in splitting the // several available threads. If it does not succeed in splitting the node
// node (because no idle threads are available, or because we have no unused // (because no idle threads are available, or because we have no unused split
// split point objects), the function immediately returns. If splitting is // point objects), the function immediately returns. If splitting is possible, a
// possible, a SplitPoint object is initialized with all the data that must be // SplitPoint object is initialized with all the data that must be copied to the
// copied to the helper threads and we tell our helper threads that they have // helper threads and then helper threads are told that they have been assigned
// been assigned work. This will cause them to instantly leave their idle loops and // work. This will cause them to instantly leave their idle loops and call
// call search().When all threads have returned from search() then split() returns. // search(). When all threads have returned from search() then split() returns.
template <bool Fake> template <bool Fake>
void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value* bestValue, Depth depth, Move threatMove, Value bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, bool pvNode) { int moveCount, MovePicker* mp, int nodeType) {
assert(pos.is_ok()); assert(pos.pos_is_ok());
assert(*bestValue >= -VALUE_INFINITE); assert(bestValue > -VALUE_INFINITE);
assert(*bestValue <= *alpha); assert(bestValue <= alpha);
assert(*alpha < beta); assert(alpha < beta);
assert(beta <= VALUE_INFINITE); assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO); assert(depth > DEPTH_ZERO);
assert(pos.thread() >= 0 && pos.thread() < activeThreads); assert(pos.thread() >= 0 && pos.thread() < activeThreads);
@@ -253,93 +274,228 @@ void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const V
int i, master = pos.thread(); int i, master = pos.thread();
Thread& masterThread = threads[master]; Thread& masterThread = threads[master];
lock_grab(&mpLock); // If we already have too many active split points, don't split
if (masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
return bestValue;
// If no other thread is available to help us, or if we have too many // Pick the next available split point from the split point stack
// active split points, don't split. SplitPoint* sp = &masterThread.splitPoints[masterThread.activeSplitPoints];
if ( !available_slave_exists(master)
|| masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
{
lock_release(&mpLock);
return;
}
// Pick the next available split point object from the split point stack // Initialize the split point
SplitPoint& splitPoint = masterThread.splitPoints[masterThread.activeSplitPoints++]; sp->parent = masterThread.splitPoint;
sp->master = master;
sp->is_betaCutoff = false;
sp->depth = depth;
sp->threatMove = threatMove;
sp->alpha = alpha;
sp->beta = beta;
sp->nodeType = nodeType;
sp->bestValue = bestValue;
sp->mp = mp;
sp->moveCount = moveCount;
sp->pos = &pos;
sp->nodes = 0;
sp->ss = ss;
// Initialize the split point object
splitPoint.parent = masterThread.splitPoint;
splitPoint.master = master;
splitPoint.is_betaCutoff = false;
splitPoint.depth = depth;
splitPoint.threatMove = threatMove;
splitPoint.alpha = *alpha;
splitPoint.beta = beta;
splitPoint.pvNode = pvNode;
splitPoint.bestValue = *bestValue;
splitPoint.mp = mp;
splitPoint.moveCount = moveCount;
splitPoint.pos = &pos;
splitPoint.nodes = 0;
splitPoint.ss = ss;
for (i = 0; i < activeThreads; i++) for (i = 0; i < activeThreads; i++)
splitPoint.is_slave[i] = false; sp->is_slave[i] = false;
masterThread.splitPoint = &splitPoint;
// If we are here it means we are not available // If we are here it means we are not available
assert(masterThread.state != Thread::AVAILABLE); assert(masterThread.is_searching);
int workersCnt = 1; // At least the master is included int workersCnt = 1; // At least the master is included
// Allocate available threads setting state to THREAD_BOOKED // Try to allocate available threads and ask them to start searching setting
// is_searching flag. This must be done under lock protection to avoid concurrent
// allocation of the same slave by another master.
lock_grab(&threadsLock);
for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++) for (i = 0; !Fake && i < activeThreads && workersCnt < maxThreadsPerSplitPoint; i++)
if (i != master && threads[i].is_available_to(master)) if (threads[i].is_available_to(master))
{ {
threads[i].state = Thread::BOOKED;
threads[i].splitPoint = &splitPoint;
splitPoint.is_slave[i] = true;
workersCnt++; workersCnt++;
} sp->is_slave[i] = true;
threads[i].splitPoint = sp;
assert(Fake || workersCnt > 1); // This makes the slave to exit from idle_loop()
threads[i].is_searching = true;
// We can release the lock because slave threads are already booked and master is not available if (useSleepingThreads)
lock_release(&mpLock);
// Tell the threads that they have work to do. This will make them leave
// their idle loop.
for (i = 0; i < activeThreads; i++)
if (i == master || splitPoint.is_slave[i])
{
assert(i == master || threads[i].state == Thread::BOOKED);
threads[i].state = Thread::WORKISWAITING; // This makes the slave to exit from idle_loop()
if (useSleepingThreads && i != master)
threads[i].wake_up(); threads[i].wake_up();
} }
// Everything is set up. The master thread enters the idle loop, from lock_release(&threadsLock);
// which it will instantly launch a search, because its state is
// THREAD_WORKISWAITING. We send the split point as a second parameter to the // We failed to allocate even one slave, return
// idle loop, which means that the main thread will return from the idle if (!Fake && workersCnt == 1)
// loop when all threads have finished their work at this split point. return bestValue;
idle_loop(master, &splitPoint);
masterThread.splitPoint = sp;
masterThread.activeSplitPoints++;
// Everything is set up. The master thread enters the idle loop, from which
// it will instantly launch a search, because its is_searching flag is set.
// We pass the split point as a parameter to the idle loop, which means that
// the thread will return from the idle loop when all slaves have finished
// their work at this split point.
masterThread.idle_loop(sp);
// In helpful master concept a master can help only a sub-tree of its split
// point, and because here is all finished is not possible master is booked.
assert(!masterThread.is_searching);
// We have returned from the idle loop, which means that all threads are // We have returned from the idle loop, which means that all threads are
// finished. Update alpha and bestValue, and return. // finished. Note that changing state and decreasing activeSplitPoints is done
lock_grab(&mpLock); // under lock protection to avoid a race with Thread::is_available_to().
lock_grab(&threadsLock);
*alpha = splitPoint.alpha; masterThread.is_searching = true;
*bestValue = splitPoint.bestValue;
masterThread.activeSplitPoints--; masterThread.activeSplitPoints--;
masterThread.splitPoint = splitPoint.parent;
pos.set_nodes_searched(pos.nodes_searched() + splitPoint.nodes);
lock_release(&mpLock); lock_release(&threadsLock);
masterThread.splitPoint = sp->parent;
pos.set_nodes_searched(pos.nodes_searched() + sp->nodes);
return sp->bestValue;
} }
// Explicit template instantiations // Explicit template instantiations
template void ThreadsManager::split<false>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool); template Value ThreadsManager::split<false>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
template void ThreadsManager::split<true>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool); template Value ThreadsManager::split<true>(Position&, Stack*, Value, Value, Value, Depth, Move, int, MovePicker*, int);
// Thread::timer_loop() is where the timer thread waits maxPly milliseconds and
// then calls do_timer_event(). If maxPly is 0 thread sleeps until is woken up.
extern void do_timer_event();
void Thread::timer_loop() {
while (!do_terminate)
{
lock_grab(&sleepLock);
timed_wait(&sleepCond, &sleepLock, maxPly ? maxPly : INT_MAX);
lock_release(&sleepLock);
do_timer_event();
}
}
// ThreadsManager::set_timer() is used to set the timer to trigger after msec
// milliseconds. If msec is 0 then timer is stopped.
void ThreadsManager::set_timer(int msec) {
Thread& timer = threads[MAX_THREADS];
lock_grab(&timer.sleepLock);
timer.maxPly = msec;
cond_signal(&timer.sleepCond); // Wake up and restart the timer
lock_release(&timer.sleepLock);
}
// Thread::main_loop() is where the main thread is parked waiting to be started
// when there is a new search. Main thread will launch all the slave threads.
void Thread::main_loop() {
while (true)
{
lock_grab(&sleepLock);
do_sleep = true; // Always return to sleep after a search
is_searching = false;
while (do_sleep && !do_terminate)
{
cond_signal(&Threads.sleepCond); // Wake up UI thread if needed
cond_wait(&sleepCond, &sleepLock);
}
is_searching = true;
lock_release(&sleepLock);
if (do_terminate)
return;
think(); // This is the search entry point
}
}
// ThreadsManager::start_thinking() is used by UI thread to wake up the main
// thread parked in main_loop() and starting a new search. If asyncMode is true
// then function returns immediately, otherwise caller is blocked waiting for
// the search to finish.
void ThreadsManager::start_thinking(const Position& pos, const LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode) {
Thread& main = threads[0];
lock_grab(&main.sleepLock);
// Wait main thread has finished before to launch a new search
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
// Copy input arguments to initialize the search
RootPosition.copy(pos, 0);
Limits = limits;
SearchMoves = searchMoves;
// Reset signals before to start the new search
memset((void*)&Signals, 0, sizeof(Signals));
main.do_sleep = false;
cond_signal(&main.sleepCond); // Wake up main thread and start searching
if (!asyncMode)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::stop_thinking() is used by UI thread to raise a stop request
// and to wait for the main thread finishing the search. Needed to wait exiting
// and terminate the threads after a 'quit' command.
void ThreadsManager::stop_thinking() {
Thread& main = threads[0];
Search::Signals.stop = true;
lock_grab(&main.sleepLock);
cond_signal(&main.sleepCond); // In case is waiting for stop or ponderhit
while (!main.do_sleep)
cond_wait(&sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}
// ThreadsManager::wait_for_stop_or_ponderhit() is called when the maximum depth
// is reached while the program is pondering. The point is to work around a wrinkle
// in the UCI protocol: When pondering, the engine is not allowed to give a
// "bestmove" before the GUI sends it a "stop" or "ponderhit" command. We simply
// wait here until one of these commands (that raise StopRequest) is sent and
// then return, after which the bestmove and pondermove will be printed.
void ThreadsManager::wait_for_stop_or_ponderhit() {
Signals.stopOnPonderhit = true;
Thread& main = threads[0];
lock_grab(&main.sleepLock);
while (!Signals.stop)
cond_wait(&main.sleepCond, &main.sleepLock);
lock_release(&main.sleepLock);
}

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@
#include "movepick.h" #include "movepick.h"
#include "pawns.h" #include "pawns.h"
#include "position.h" #include "position.h"
#include "search.h"
const int MAX_THREADS = 32; const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8; const int MAX_ACTIVE_SPLIT_POINTS = 8;
@@ -37,15 +38,15 @@ struct SplitPoint {
SplitPoint* parent; SplitPoint* parent;
const Position* pos; const Position* pos;
Depth depth; Depth depth;
bool pvNode;
Value beta; Value beta;
int nodeType;
int ply; int ply;
int master; int master;
Move threatMove; Move threatMove;
// Const pointers to shared data // Const pointers to shared data
MovePicker* mp; MovePicker* mp;
SearchStack* ss; Search::Stack* ss;
// Shared data // Shared data
Lock lock; Lock lock;
@@ -58,42 +59,44 @@ struct SplitPoint {
}; };
/// Thread struct is used to keep together all the thread related stuff like locks, /// Thread struct keeps together all the thread related stuff like locks, state
/// state and especially split points. We also use per-thread pawn and material hash /// and especially split points. We also use per-thread pawn and material hash
/// tables so that once we get a pointer to an entry its life time is unlimited and /// tables so that once we get a pointer to an entry its life time is unlimited
/// we don't have to care about someone changing the entry under our feet. /// and we don't have to care about someone changing the entry under our feet.
struct Thread { struct Thread {
enum ThreadState
{
INITIALIZING, // Thread is initializing itself
SEARCHING, // Thread is performing work
AVAILABLE, // Thread is waiting for work
BOOKED, // Other thread (master) has booked us as a slave
WORKISWAITING, // Master has ordered us to start
TERMINATED // We are quitting and thread is terminated
};
void wake_up(); void wake_up();
bool cutoff_occurred() const; bool cutoff_occurred() const;
bool is_available_to(int master) const; bool is_available_to(int master) const;
void idle_loop(SplitPoint* sp);
void main_loop();
void timer_loop();
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS];
MaterialInfoTable materialTable; MaterialInfoTable materialTable;
PawnInfoTable pawnTable; PawnInfoTable pawnTable;
int threadID;
int maxPly; int maxPly;
Lock sleepLock; Lock sleepLock;
WaitCondition sleepCond; WaitCondition sleepCond;
volatile ThreadState state;
SplitPoint* volatile splitPoint; SplitPoint* volatile splitPoint;
volatile int activeSplitPoints; volatile int activeSplitPoints;
SplitPoint splitPoints[MAX_ACTIVE_SPLIT_POINTS]; volatile bool is_searching;
volatile bool do_sleep;
volatile bool do_terminate;
#if defined(_MSC_VER)
HANDLE handle;
#else
pthread_t handle;
#endif
}; };
/// ThreadsManager class is used to handle all the threads related stuff like init, /// ThreadsManager class handles all the threads related stuff like init, starting,
/// starting, parking and, the most important, launching a slave thread at a split /// parking and, the most important, launching a slave thread at a split point.
/// point. All the access to shared thread data is done through this class. /// All the access to shared thread data is done through this class.
class ThreadsManager { class ThreadsManager {
/* As long as the single ThreadsManager object is defined as a global we don't /* As long as the single ThreadsManager object is defined as a global we don't
@@ -104,27 +107,34 @@ public:
Thread& operator[](int threadID) { return threads[threadID]; } Thread& operator[](int threadID) { return threads[threadID]; }
void init(); void init();
void exit(); void exit();
void init_hash_tables();
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; } int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; } int size() const { return activeThreads; }
void set_size(int cnt) { activeThreads = cnt; }
void set_size(int cnt);
void read_uci_options(); void read_uci_options();
bool available_slave_exists(int master) const; bool available_slave_exists(int master) const;
void idle_loop(int threadID, SplitPoint* sp); bool split_point_finished(SplitPoint* sp) const;
void set_timer(int msec);
void wait_for_stop_or_ponderhit();
void stop_thinking();
void start_thinking(const Position& pos, const Search::LimitsType& limits,
const std::vector<Move>& searchMoves, bool asyncMode);
template <bool Fake> template <bool Fake>
void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue, Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode); Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private: private:
Lock mpLock; friend struct Thread;
Thread threads[MAX_THREADS + 1]; // Last one is used as a timer
Lock threadsLock;
Depth minimumSplitDepth; Depth minimumSplitDepth;
int maxThreadsPerSplitPoint; int maxThreadsPerSplitPoint;
bool useSleepingThreads;
int activeThreads; int activeThreads;
volatile bool allThreadsShouldExit; bool useSleepingThreads;
Thread threads[MAX_THREADS]; WaitCondition sleepCond;
}; };
extern ThreadsManager Threads; extern ThreadsManager Threads;

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -18,6 +18,7 @@
*/ */
#include <cmath> #include <cmath>
#include <algorithm>
#include "misc.h" #include "misc.h"
#include "search.h" #include "search.h"
@@ -64,7 +65,7 @@ namespace {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 2, 2, 2, 2,
2, 1, 1, 1, 1, 1, 1, 1 }; 2, 1, 1, 1, 1, 1, 1, 1 };
int move_importance(int ply) { return MoveImportance[Min(ply, 511)]; } int move_importance(int ply) { return MoveImportance[std::min(ply, 511)]; }
/// Function Prototypes /// Function Prototypes
@@ -72,18 +73,18 @@ namespace {
enum TimeType { OptimumTime, MaxTime }; enum TimeType { OptimumTime, MaxTime };
template<TimeType> template<TimeType>
int remaining(int myTime, int movesToGo, int currentPly); int remaining(int myTime, int movesToGo, int fullMoveNumber);
} }
void TimeManager::pv_instability(int curChanges, int prevChanges) { void TimeManager::pv_instability(int curChanges, int prevChanges) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2) unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3); + prevChanges * (optimumSearchTime / 3);
} }
void TimeManager::init(const SearchLimits& limits, int currentPly) void TimeManager::init(const Search::LimitsType& limits, int currentPly)
{ {
/* We support four different kind of time controls: /* We support four different kind of time controls:
@@ -103,10 +104,10 @@ void TimeManager::init(const SearchLimits& limits, int currentPly)
int hypMTG, hypMyTime, t1, t2; int hypMTG, hypMyTime, t1, t2;
// Read uci parameters // Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"].value<int>(); int emergencyMoveHorizon = Options["Emergency Move Horizon"];
int emergencyBaseTime = Options["Emergency Base Time"].value<int>(); int emergencyBaseTime = Options["Emergency Base Time"];
int emergencyMoveTime = Options["Emergency Move Time"].value<int>(); int emergencyMoveTime = Options["Emergency Move Time"];
int minThinkingTime = Options["Minimum Thinking Time"].value<int>(); int minThinkingTime = Options["Minimum Thinking Time"];
// Initialize to maximum values but unstablePVExtraTime that is reset // Initialize to maximum values but unstablePVExtraTime that is reset
unstablePVExtraTime = 0; unstablePVExtraTime = 0;
@@ -114,28 +115,28 @@ void TimeManager::init(const SearchLimits& limits, int currentPly)
// We calculate optimum time usage for different hypothetic "moves to go"-values and choose the // We calculate optimum time usage for different hypothetic "moves to go"-values and choose the
// minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values. // minimum of calculated search time values. Usually the greatest hypMTG gives the minimum values.
for (hypMTG = 1; hypMTG <= (limits.movesToGo ? Min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++) for (hypMTG = 1; hypMTG <= (limits.movesToGo ? std::min(limits.movesToGo, MoveHorizon) : MoveHorizon); hypMTG++)
{ {
// Calculate thinking time for hypothetic "moves to go"-value // Calculate thinking time for hypothetic "moves to go"-value
hypMyTime = limits.time hypMyTime = limits.time
+ limits.increment * (hypMTG - 1) + limits.increment * (hypMTG - 1)
- emergencyBaseTime - emergencyBaseTime
- emergencyMoveTime * Min(hypMTG, emergencyMoveHorizon); - emergencyMoveTime * std::min(hypMTG, emergencyMoveHorizon);
hypMyTime = Max(hypMyTime, 0); hypMyTime = std::max(hypMyTime, 0);
t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly); t1 = minThinkingTime + remaining<OptimumTime>(hypMyTime, hypMTG, currentPly);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly); t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
optimumSearchTime = Min(optimumSearchTime, t1); optimumSearchTime = std::min(optimumSearchTime, t1);
maximumSearchTime = Min(maximumSearchTime, t2); maximumSearchTime = std::min(maximumSearchTime, t2);
} }
if (Options["Ponder"].value<bool>()) if (Options["Ponder"])
optimumSearchTime += optimumSearchTime / 4; optimumSearchTime += optimumSearchTime / 4;
// Make sure that maxSearchTime is not over absoluteMaxSearchTime // Make sure that maxSearchTime is not over absoluteMaxSearchTime
optimumSearchTime = Min(optimumSearchTime, maximumSearchTime); optimumSearchTime = std::min(optimumSearchTime, maximumSearchTime);
} }
@@ -156,6 +157,6 @@ namespace {
float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance); float ratio1 = (TMaxRatio * thisMoveImportance) / float(TMaxRatio * thisMoveImportance + otherMovesImportance);
float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance); float ratio2 = (thisMoveImportance + TStealRatio * otherMovesImportance) / float(thisMoveImportance + otherMovesImportance);
return int(floor(myTime * Min(ratio1, ratio2))); return int(floor(myTime * std::min(ratio1, ratio2)));
} }
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,12 +20,12 @@
#if !defined(TIMEMAN_H_INCLUDED) #if !defined(TIMEMAN_H_INCLUDED)
#define TIMEMAN_H_INCLUDED #define TIMEMAN_H_INCLUDED
struct SearchLimits; /// The TimeManager class computes the optimal time to think depending on the
/// maximum available time, the move game number and other parameters.
class TimeManager { class TimeManager {
public: public:
void init(const Search::LimitsType& limits, int currentPly);
void init(const SearchLimits& limits, int currentPly);
void pv_instability(int curChanges, int prevChanges); void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; } int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; } int maximum_time() const { return maximumSearchTime; }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,7 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
@@ -60,7 +59,7 @@ void TranspositionTable::set_size(size_t mbSize) {
if (!entries) if (!entries)
{ {
std::cerr << "Failed to allocate " << mbSize std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl; << "MB for transposition table." << std::endl;
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
clear(); clear();

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -22,7 +22,7 @@
#include <iostream> #include <iostream>
#include "move.h" #include "misc.h"
#include "types.h" #include "types.h"

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -20,6 +20,26 @@
#if !defined(TYPES_H_INCLUDED) #if !defined(TYPES_H_INCLUDED)
#define TYPES_H_INCLUDED #define TYPES_H_INCLUDED
/// For Linux and OSX configuration is done automatically using Makefile. To get
/// started type 'make help'.
///
/// For Windows, part of the configuration is detected automatically, but some
/// switches need to be set manually:
///
/// -DNDEBUG | Disable debugging mode. Use always.
///
/// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want
/// | the executable to run on some very old machines.
///
/// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction. Works
/// | only in 64-bit mode. For compiling requires hardware with
/// | popcnt support.
///
/// -DOLD_LOCKS | Under Windows are used the fast Slim Reader/Writer (SRW)
/// | Locks and Condition Variables: these are not supported by
/// | Windows XP and older, to compile for those platforms you
/// | should enable OLD_LOCKS.
#include <climits> #include <climits>
#include <cstdlib> #include <cstdlib>
#include <ctype.h> #include <ctype.h>
@@ -27,9 +47,9 @@
#if defined(_MSC_VER) #if defined(_MSC_VER)
// Disable some silly and noisy warning from MSVC compiler // Disable some silly and noisy warning from MSVC compiler
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
#pragma warning(disable: 4127) // Conditional expression is constant #pragma warning(disable: 4127) // Conditional expression is constant
#pragma warning(disable: 4146) // Unary minus operator applied to unsigned type #pragma warning(disable: 4146) // Unary minus operator applied to unsigned type
#pragma warning(disable: 4800) // Forcing value to bool 'true' or 'false'
// MSVC does not support <inttypes.h> // MSVC does not support <inttypes.h>
typedef signed __int8 int8_t; typedef signed __int8 int8_t;
@@ -42,125 +62,111 @@ typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t; typedef unsigned __int64 uint64_t;
#else #else
# include <inttypes.h>
#include <inttypes.h>
#endif #endif
#define Min(x, y) (((x) < (y)) ? (x) : (y))
#define Max(x, y) (((x) < (y)) ? (y) : (x))
////
//// Configuration
////
//// For Linux and OSX configuration is done automatically using Makefile.
//// To get started type "make help".
////
//// For windows part of the configuration is detected automatically, but
//// some switches need to be set manually:
////
//// -DNDEBUG | Disable debugging mode. Use always.
////
//// -DNO_PREFETCH | Disable use of prefetch asm-instruction. A must if you want the
//// | executable to run on some very old machines.
////
//// -DUSE_POPCNT | Add runtime support for use of popcnt asm-instruction.
//// | Works only in 64-bit mode. For compiling requires hardware
//// | with popcnt support. Around 4% speed-up.
////
//// -DOLD_LOCKS | By default under Windows are used the fast Slim Reader/Writer (SRW)
//// | Locks and Condition Variables: these are not supported by Windows XP
//// | and older, to compile for those platforms you should enable OLD_LOCKS.
// Automatic detection for 64-bit under Windows
#if defined(_WIN64) #if defined(_WIN64)
#define IS_64BIT # include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
#endif #endif
// Automatic detection for use of bsfq asm-instruction under Windows
#if defined(_WIN64)
#define USE_BSFQ
#endif
// Intel header for _mm_popcnt_u64() intrinsic
#if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER) #if defined(USE_POPCNT) && defined(_MSC_VER) && defined(__INTEL_COMPILER)
#include <nmmintrin.h> # include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif #endif
// Cache line alignment specification
#if defined(_MSC_VER) || defined(__INTEL_COMPILER) #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define CACHE_LINE_ALIGNMENT __declspec(align(64)) # define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else #else
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64))) # define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif #endif
// Define a __cpuid() function for gcc compilers, for Intel and MSVC
// is already available as an intrinsic.
#if defined(_MSC_VER) #if defined(_MSC_VER)
#include <intrin.h> # define FORCE_INLINE __forceinline
#elif defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
inline void __cpuid(int CPUInfo[4], int InfoType)
{
int* eax = CPUInfo + 0;
int* ebx = CPUInfo + 1;
int* ecx = CPUInfo + 2;
int* edx = CPUInfo + 3;
*eax = InfoType;
*ecx = 0;
__asm__("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
: "0" (*eax), "2" (*ecx));
}
#else
inline void __cpuid(int CPUInfo[4], int)
{
CPUInfo[0] = CPUInfo[1] = CPUInfo[2] = CPUInfo[3] = 0;
}
#endif
// Define FORCE_INLINE macro to force inlining overriding compiler choice
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#elif defined(__GNUC__) #elif defined(__GNUC__)
#define FORCE_INLINE inline __attribute__((always_inline)) # define FORCE_INLINE inline __attribute__((always_inline))
#else #else
#define FORCE_INLINE inline # define FORCE_INLINE inline
#endif #endif
/// cpu_has_popcnt() detects support for popcnt instruction at runtime
inline bool cpu_has_popcnt() {
int CPUInfo[4] = {-1};
__cpuid(CPUInfo, 0x00000001);
return (CPUInfo[2] >> 23) & 1;
}
/// CpuHasPOPCNT is a global constant initialized at startup that
/// is set to true if CPU on which application runs supports popcnt
/// hardware instruction. Unless USE_POPCNT is not defined.
#if defined(USE_POPCNT) #if defined(USE_POPCNT)
const bool CpuHasPOPCNT = cpu_has_popcnt(); const bool HasPopCnt = true;
#else #else
const bool CpuHasPOPCNT = false; const bool HasPopCnt = false;
#endif #endif
/// CpuIs64Bit is a global constant initialized at compile time that
/// is set to true if CPU on which application runs is a 64 bits.
#if defined(IS_64BIT) #if defined(IS_64BIT)
const bool CpuIs64Bit = true; const bool Is64Bit = true;
#else #else
const bool CpuIs64Bit = false; const bool Is64Bit = false;
#endif #endif
#include <string>
typedef uint64_t Key; typedef uint64_t Key;
typedef uint64_t Bitboard; typedef uint64_t Bitboard;
const int PLY_MAX = 100; const int MAX_MOVES = 256;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2; const int MAX_PLY = 100;
const int MAX_PLY_PLUS_2 = MAX_PLY + 2;
const Bitboard FileABB = 0x0101010101010101ULL;
const Bitboard FileBBB = FileABB << 1;
const Bitboard FileCBB = FileABB << 2;
const Bitboard FileDBB = FileABB << 3;
const Bitboard FileEBB = FileABB << 4;
const Bitboard FileFBB = FileABB << 5;
const Bitboard FileGBB = FileABB << 6;
const Bitboard FileHBB = FileABB << 7;
const Bitboard Rank1BB = 0xFF;
const Bitboard Rank2BB = Rank1BB << (8 * 1);
const Bitboard Rank3BB = Rank1BB << (8 * 2);
const Bitboard Rank4BB = Rank1BB << (8 * 3);
const Bitboard Rank5BB = Rank1BB << (8 * 4);
const Bitboard Rank6BB = Rank1BB << (8 * 5);
const Bitboard Rank7BB = Rank1BB << (8 * 6);
const Bitboard Rank8BB = Rank1BB << (8 * 7);
/// A move needs 16 bits to be stored
///
/// bit 0- 5: destination square (from 0 to 63)
/// bit 6-11: origin square (from 0 to 63)
/// bit 12-13: promotion piece type - 2 (from KNIGHT-2 to QUEEN-2)
/// bit 14-15: special move flag: promotion (1), en passant (2), castle (3)
///
/// Special cases are MOVE_NONE and MOVE_NULL. We can sneak these in because in
/// any normal move destination square is always different from origin square
/// while MOVE_NONE and MOVE_NULL have the same origin and destination square.
enum Move {
MOVE_NONE = 0,
MOVE_NULL = 65
};
struct MoveStack {
Move move;
int score;
};
inline bool operator<(const MoveStack& f, const MoveStack& s) {
return f.score < s.score;
}
enum CastleRight {
CASTLES_NONE = 0,
WHITE_OO = 1,
BLACK_OO = 2,
WHITE_OOO = 4,
BLACK_OOO = 8,
ALL_CASTLES = 15
};
enum ScaleFactor {
SCALE_FACTOR_DRAW = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
SCALE_FACTOR_NONE = 255
};
enum ValueType { enum ValueType {
VALUE_TYPE_NONE = 0, VALUE_TYPE_NONE = 0,
@@ -177,34 +183,36 @@ enum Value {
VALUE_INFINITE = 30001, VALUE_INFINITE = 30001,
VALUE_NONE = 30002, VALUE_NONE = 30002,
VALUE_MATE_IN_PLY_MAX = VALUE_MATE - PLY_MAX, VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_PLY_MAX = -VALUE_MATE + PLY_MAX, VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX, VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
}; };
enum PieceType { enum PieceType {
PIECE_TYPE_NONE = 0, NO_PIECE_TYPE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6 PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
}; };
enum Piece { enum Piece {
PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6, NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16 W_PAWN = 1, W_KNIGHT = 2, W_BISHOP = 3, W_ROOK = 4, W_QUEEN = 5, W_KING = 6,
B_PAWN = 9, B_KNIGHT = 10, B_BISHOP = 11, B_ROOK = 12, B_QUEEN = 13, B_KING = 14
}; };
enum Color { enum Color {
WHITE, BLACK, COLOR_NONE WHITE, BLACK, NO_COLOR
}; };
enum Depth { enum Depth {
ONE_PLY = 2, ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY, DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY, DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY, DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -4 * ONE_PLY,
DEPTH_NONE = -127 * ONE_PLY DEPTH_NONE = -127 * ONE_PLY
}; };
@@ -241,42 +249,54 @@ enum Rank {
RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8 RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8
}; };
enum SquareColor {
DARK, LIGHT
};
enum ScaleFactor { /// Score enum keeps a midgame and an endgame value in a single integer (enum),
SCALE_FACTOR_ZERO = 0, /// first LSB 16 bits are used to store endgame value, while upper bits are used
SCALE_FACTOR_NORMAL = 64, /// for midgame value. Compiler is free to choose the enum type as long as can
SCALE_FACTOR_MAX = 128, /// keep its data, so ensure Score to be an integer type.
SCALE_FACTOR_NONE = 255
};
/// Score enum keeps a midgame and an endgame value in a single
/// integer (enum), first LSB 16 bits are used to store endgame
/// value, while upper bits are used for midgame value. Compiler
/// is free to choose the enum type as long as can keep its data,
/// so ensure Score to be an integer type.
enum Score { enum Score {
SCORE_ZERO = 0, SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX, SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
}; };
#define ENABLE_OPERATORS_ON(T) \ inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
inline T operator+ (const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator- (const T d1, const T d2) { return T(int(d1) - int(d2)); } \ /// Extracting the signed lower and upper 16 bits it not so trivial because
inline T operator* (int i, const T d) { return T(i * int(d)); } \ /// according to the standard a simple cast to short is implementation defined
inline T operator* (const T d, int i) { return T(int(d) * i); } \ /// and so is a right shift of a signed integer.
inline T operator/ (const T d, int i) { return T(int(d) / i); } \ inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); }
inline T operator- (const T d) { return T(-int(d)); } \
inline T operator++ (T& d, int) {d = T(int(d) + 1); return d; } \ /// On Intel 64 bit we have a small speed regression with the standard conforming
inline T operator-- (T& d, int) { d = T(int(d) - 1); return d; } \ /// version, so use a faster code in this case that, although not 100% standard
inline void operator+= (T& d1, const T d2) { d1 = d1 + d2; } \ /// compliant it seems to work for Intel and MSVC.
inline void operator-= (T& d1, const T d2) { d1 = d1 - d2; } \ #if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline void operator*= (T& d, int i) { d = T(int(d) * i); } \
inline void operator/= (T& d, int i) { d = T(int(d) / i); } inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) {
return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u));
}
#endif
#define ENABLE_SAFE_OPERATORS_ON(T) \
inline T operator+(const T d1, const T d2) { return T(int(d1) + int(d2)); } \
inline T operator-(const T d1, const T d2) { return T(int(d1) - int(d2)); } \
inline T operator*(int i, const T d) { return T(i * int(d)); } \
inline T operator*(const T d, int i) { return T(int(d) * i); } \
inline T operator-(const T d) { return T(-int(d)); } \
inline T& operator+=(T& d1, const T d2) { d1 = d1 + d2; return d1; } \
inline T& operator-=(T& d1, const T d2) { d1 = d1 - d2; return d1; } \
inline T& operator*=(T& d, int i) { d = T(int(d) * i); return d; }
#define ENABLE_OPERATORS_ON(T) ENABLE_SAFE_OPERATORS_ON(T) \
inline T operator++(T& d, int) { d = T(int(d) + 1); return d; } \
inline T operator--(T& d, int) { d = T(int(d) - 1); return d; } \
inline T operator/(const T d, int i) { return T(int(d) / i); } \
inline T& operator/=(T& d, int i) { d = T(int(d) / i); return d; }
ENABLE_OPERATORS_ON(Value) ENABLE_OPERATORS_ON(Value)
ENABLE_OPERATORS_ON(PieceType) ENABLE_OPERATORS_ON(PieceType)
@@ -287,44 +307,23 @@ ENABLE_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File) ENABLE_OPERATORS_ON(File)
ENABLE_OPERATORS_ON(Rank) ENABLE_OPERATORS_ON(Rank)
#undef ENABLE_OPERATORS_ON /// Added operators for adding integers to a Value
inline Value operator+(Value v, int i) { return Value(int(v) + i); }
inline Value operator-(Value v, int i) { return Value(int(v) - i); }
// Extra operators for adding integers to a Value ENABLE_SAFE_OPERATORS_ON(Score)
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
// Extracting the _signed_ lower and upper 16 bits it not so trivial /// Only declared but not defined. We don't want to multiply two scores due to
// because according to the standard a simple cast to short is /// a very high risk of overflow. So user should explicitly convert to integer.
// implementation defined and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((int(s) + 32768) & ~0xffff) / 0x10000); }
// Unfortunatly on Intel 64 bit we have a small speed regression, so use a faster code in
// this case, although not 100% standard compliant it seems to work for Intel and MSVC.
#if defined(IS_64BIT) && (!defined(__GNUC__) || defined(__INTEL_COMPILER))
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
#else
inline Value eg_value(Score s) { return Value((int)(unsigned(s) & 0x7fffu) - (int)(unsigned(s) & 0x8000u)); }
#endif
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
// Division must be handled separately for each term
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
// Only declared but not defined. We don't want to multiply two scores due to
// a very high risk of overflow. So user should explicitly convert to integer.
inline Score operator*(Score s1, Score s2); inline Score operator*(Score s1, Score s2);
// Remaining Score operators are standard /// Division of a Score must be handled separately for each term
inline Score operator+ (const Score d1, const Score d2) { return Score(int(d1) + int(d2)); } inline Score operator/(Score s, int i) {
inline Score operator- (const Score d1, const Score d2) { return Score(int(d1) - int(d2)); } return make_score(mg_value(s) / i, eg_value(s) / i);
inline Score operator* (int i, const Score d) { return Score(i * int(d)); } }
inline Score operator* (const Score d, int i) { return Score(int(d) * i); }
inline Score operator- (const Score d) { return Score(-int(d)); } #undef ENABLE_OPERATORS_ON
inline void operator+= (Score& d1, const Score d2) { d1 = d1 + d2; } #undef ENABLE_SAFE_OPERATORS_ON
inline void operator-= (Score& d1, const Score d2) { d1 = d1 - d2; }
inline void operator*= (Score& d, int i) { d = Score(int(d) * i); }
inline void operator/= (Score& d, int i) { d = Score(int(d) / i); }
const Value PawnValueMidgame = Value(0x0C6); const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102); const Value PawnValueEndgame = Value(0x102);
@@ -337,135 +336,178 @@ const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9); const Value QueenValueMidgame = Value(0x9D9);
const Value QueenValueEndgame = Value(0x9FE); const Value QueenValueEndgame = Value(0x9FE);
inline Value value_mate_in(int ply) { extern const Value PieceValueMidgame[17];
extern const Value PieceValueEndgame[17];
extern int SquareDistance[64][64];
inline Value mate_in(int ply) {
return VALUE_MATE - ply; return VALUE_MATE - ply;
} }
inline Value value_mated_in(int ply) { inline Value mated_in(int ply) {
return -VALUE_MATE + ply; return -VALUE_MATE + ply;
} }
inline Piece make_piece(Color c, PieceType pt) { inline Piece make_piece(Color c, PieceType pt) {
return Piece((int(c) << 3) | int(pt)); return Piece((c << 3) | pt);
} }
inline PieceType type_of_piece(Piece p) { inline PieceType type_of(Piece p) {
return PieceType(int(p) & 7); return PieceType(p & 7);
} }
inline Color color_of_piece(Piece p) { inline Color color_of(Piece p) {
return Color(int(p) >> 3); return Color(p >> 3);
} }
inline Color opposite_color(Color c) { inline Color flip(Color c) {
return Color(int(c) ^ 1); return Color(c ^ 1);
}
inline bool color_is_ok(Color c) {
return c == WHITE || c == BLACK;
}
inline bool piece_type_is_ok(PieceType pt) {
return pt >= PAWN && pt <= KING;
}
inline bool piece_is_ok(Piece p) {
return piece_type_is_ok(type_of_piece(p)) && color_is_ok(color_of_piece(p));
}
inline char piece_type_to_char(PieceType pt) {
static const char ch[] = " PNBRQK";
return ch[pt];
} }
inline Square make_square(File f, Rank r) { inline Square make_square(File f, Rank r) {
return Square((int(r) << 3) | int(f)); return Square((r << 3) | f);
}
inline File square_file(Square s) {
return File(int(s) & 7);
}
inline Rank square_rank(Square s) {
return Rank(int(s) >> 3);
}
inline Square flip_square(Square s) {
return Square(int(s) ^ 56);
}
inline Square flop_square(Square s) {
return Square(int(s) ^ 7);
}
inline Square relative_square(Color c, Square s) {
return Square(int(s) ^ (int(c) * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(int(r) ^ (int(c) * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, square_rank(s));
}
inline SquareColor square_color(Square s) {
return SquareColor(int(square_rank(s) + s) & 1);
}
inline bool opposite_color_squares(Square s1, Square s2) {
int s = int(s1) ^ int(s2);
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(square_file(s1) - square_file(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(square_rank(s1) - square_rank(s2));
}
inline int square_distance(Square s1, Square s2) {
return Max(file_distance(s1, s2), rank_distance(s1, s2));
}
inline File file_from_char(char c) {
return File(c - 'a') + FILE_A;
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline Rank rank_from_char(char c) {
return Rank(c - '1') + RANK_1;
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
}
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(square_file(s)), rank_to_char(square_rank(s)), 0 };
return std::string(ch);
}
inline bool file_is_ok(File f) {
return f >= FILE_A && f <= FILE_H;
}
inline bool rank_is_ok(Rank r) {
return r >= RANK_1 && r <= RANK_8;
} }
inline bool square_is_ok(Square s) { inline bool square_is_ok(Square s) {
return s >= SQ_A1 && s <= SQ_H8; return s >= SQ_A1 && s <= SQ_H8;
} }
inline File file_of(Square s) {
return File(s & 7);
}
inline Rank rank_of(Square s) {
return Rank(s >> 3);
}
inline Square flip(Square s) {
return Square(s ^ 56);
}
inline Square mirror(Square s) {
return Square(s ^ 7);
}
inline Square relative_square(Color c, Square s) {
return Square(s ^ (c * 56));
}
inline Rank relative_rank(Color c, Rank r) {
return Rank(r ^ (c * 7));
}
inline Rank relative_rank(Color c, Square s) {
return relative_rank(c, rank_of(s));
}
inline bool opposite_colors(Square s1, Square s2) {
int s = s1 ^ s2;
return ((s >> 3) ^ s) & 1;
}
inline int file_distance(Square s1, Square s2) {
return abs(file_of(s1) - file_of(s2));
}
inline int rank_distance(Square s1, Square s2) {
return abs(rank_of(s1) - rank_of(s2));
}
inline int square_distance(Square s1, Square s2) {
return SquareDistance[s1][s2];
}
inline char piece_type_to_char(PieceType pt) {
return " PNBRQK"[pt];
}
inline char file_to_char(File f) {
return char(f - FILE_A + int('a'));
}
inline char rank_to_char(Rank r) {
return char(r - RANK_1 + int('1'));
}
inline Square pawn_push(Color c) { inline Square pawn_push(Color c) {
return c == WHITE ? DELTA_N : DELTA_S; return c == WHITE ? DELTA_N : DELTA_S;
} }
inline Square move_from(Move m) {
return Square((m >> 6) & 0x3F);
}
inline Square move_to(Move m) {
return Square(m & 0x3F);
}
inline bool is_special(Move m) {
return m & (3 << 14);
}
inline bool is_promotion(Move m) {
return (m & (3 << 14)) == (1 << 14);
}
inline int is_enpassant(Move m) {
return (m & (3 << 14)) == (2 << 14);
}
inline int is_castle(Move m) {
return (m & (3 << 14)) == (3 << 14);
}
inline PieceType promotion_piece_type(Move m) {
return PieceType(((m >> 12) & 3) + 2);
}
inline Move make_move(Square from, Square to) {
return Move(to | (from << 6));
}
inline Move make_promotion_move(Square from, Square to, PieceType promotion) {
return Move(to | (from << 6) | (1 << 14) | ((promotion - 2) << 12)) ;
}
inline Move make_enpassant_move(Square from, Square to) {
return Move(to | (from << 6) | (2 << 14));
}
inline Move make_castle_move(Square from, Square to) {
return Move(to | (from << 6) | (3 << 14));
}
inline bool is_ok(Move m) {
return move_from(m) != move_to(m); // Catches also MOVE_NULL and MOVE_NONE
}
#include <string>
inline const std::string square_to_string(Square s) {
char ch[] = { file_to_char(file_of(s)), rank_to_char(rank_of(s)), 0 };
return ch;
}
/// Our insertion sort implementation, works with pointers and iterators and is
/// guaranteed to be stable, as is needed.
template<typename T, typename K>
void sort(K firstMove, K lastMove)
{
T value;
K cur, p, d;
if (firstMove != lastMove)
for (cur = firstMove + 1; cur != lastMove; cur++)
{
p = d = cur;
value = *p--;
if (*p < value)
{
do *d = *p;
while (--d != firstMove && *--p < value);
*d = value;
}
}
}
#endif // !defined(TYPES_H_INCLUDED) #endif // !defined(TYPES_H_INCLUDED)

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,97 +17,112 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <vector>
#include "evaluate.h" #include "evaluate.h"
#include "misc.h" #include "misc.h"
#include "move.h"
#include "position.h" #include "position.h"
#include "search.h" #include "search.h"
#include "thread.h"
#include "ucioption.h" #include "ucioption.h"
using namespace std; using namespace std;
namespace { namespace {
// FEN string for the initial position // FEN string of the initial position, normal chess
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; const char* StartFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// UCIParser is a class for parsing UCI input. The class // Keep track of position keys along the setup moves (from start position to the
// is actually a string stream built on a given input string. // position just before to start searching). This is needed by draw detection
typedef istringstream UCIParser; // where, due to 50 moves rule, we need to check at most 100 plies back.
StateInfo StateRingBuf[102], *SetupState = StateRingBuf;
void set_option(UCIParser& up); void set_option(istringstream& up);
void set_position(Position& pos, UCIParser& up); void set_position(Position& pos, istringstream& up);
bool go(Position& pos, UCIParser& up); void go(Position& pos, istringstream& up);
void perft(Position& pos, UCIParser& up); void perft(Position& pos, istringstream& up);
} }
/// execute_uci_command() takes a string as input, uses a UCIParser /// Wait for a command from the user, parse this text string as an UCI command,
/// object to parse this text string as a UCI command, and calls /// and call the appropriate functions. Also intercepts EOF from stdin to ensure
/// the appropriate functions. In addition to the UCI commands, /// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// the function also supports a few debug commands. /// commands, the function also supports a few debug commands.
bool execute_uci_command(const string& cmd) { void uci_loop() {
static Position pos(StartPositionFEN, false, 0); // The root position Position pos(StartFEN, false, 0); // The root position
string cmd, token;
UCIParser up(cmd); while (token != "quit")
string token;
up >> token; // operator>>() skips any whitespace
if (token == "quit")
return false;
if (token == "go")
return go(pos, up);
if (token == "ucinewgame")
pos.from_fen(StartPositionFEN, false);
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(pos, up);
else if (token == "setoption")
set_option(up);
else if (token == "perft")
perft(pos, up);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip();
else if (token == "eval")
{ {
read_evaluation_uci_options(pos.side_to_move()); if (!getline(cin, cmd)) // Block here waiting for input
cout << trace_evaluate(pos) << endl; cmd = "quit";
istringstream is(cmd);
is >> skipws >> token;
if (token == "quit" || token == "stop")
Threads.stop_thinking();
else if (token == "ponderhit")
{
// The opponent has played the expected move. GUI sends "ponderhit" if
// we were told to ponder on the same move the opponent has played. We
// should continue searching but switching from pondering to normal search.
Search::Limits.ponder = false;
if (Search::Signals.stopOnPonderhit)
Threads.stop_thinking();
}
else if (token == "go")
go(pos, is);
else if (token == "ucinewgame")
pos.from_fen(StartFEN, false);
else if (token == "isready")
cout << "readyok" << endl;
else if (token == "position")
set_position(pos, is);
else if (token == "setoption")
set_option(is);
else if (token == "perft")
perft(pos, is);
else if (token == "d")
pos.print();
else if (token == "flip")
pos.flip_me();
else if (token == "eval")
{
read_evaluation_uci_options(pos.side_to_move());
cout << trace_evaluate(pos) << endl;
}
else if (token == "key")
cout << "key: " << hex << pos.key()
<< "\nmaterial key: " << pos.material_key()
<< "\npawn key: " << pos.pawn_key() << endl;
else if (token == "uci")
cout << "id name " << engine_info(true)
<< "\n" << Options
<< "\nuciok" << endl;
else
cout << "Unknown command: " << cmd << endl;
} }
else if (token == "key")
cout << "key: " << hex << pos.get_key()
<< "\nmaterial key: " << pos.get_material_key()
<< "\npawn key: " << pos.get_pawn_key() << endl;
else if (token == "uci")
cout << "id name " << engine_name()
<< "\nid author " << engine_authors()
<< "\n" << Options.print_all()
<< "\nuciok" << endl;
else
cout << "Unknown command: " << cmd << endl;
return true;
} }
@@ -118,127 +133,127 @@ namespace {
// fen string ("fen") or the starting position ("startpos") and then // fen string ("fen") or the starting position ("startpos") and then
// makes the moves given in the following move list ("moves"). // makes the moves given in the following move list ("moves").
void set_position(Position& pos, UCIParser& up) { void set_position(Position& pos, istringstream& is) {
Move m;
string token, fen; string token, fen;
up >> token; // operator>>() skips any whitespace is >> token;
if (token == "startpos") if (token == "startpos")
{ {
pos.from_fen(StartPositionFEN, false); fen = StartFEN;
up >> token; // Consume "moves" token if any is >> token; // Consume "moves" token if any
} }
else if (token == "fen") else if (token == "fen")
{ while (is >> token && token != "moves")
while (up >> token && token != "moves")
fen += token + " "; fen += token + " ";
else
return;
pos.from_fen(fen, Options["UCI_Chess960"].value<bool>()); pos.from_fen(fen, Options["UCI_Chess960"]);
}
else return;
// Parse move list (if any) // Parse move list (if any)
while (up >> token) while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
pos.do_setup_move(move_from_uci(pos, token)); {
pos.do_move(m, *SetupState);
// Increment pointer to StateRingBuf circular buffer
if (++SetupState - StateRingBuf >= 102)
SetupState = StateRingBuf;
}
} }
// set_option() is called when engine receives the "setoption" UCI // set_option() is called when engine receives the "setoption" UCI command. The
// command. The function updates the corresponding UCI option ("name") // function updates the UCI option ("name") to the given value ("value").
// to the given value ("value").
void set_option(UCIParser& up) { void set_option(istringstream& is) {
string token, name; string token, name, value;
string value = "true"; // UCI buttons don't have a "value" field
up >> token; // Consume "name" token is >> token; // Consume "name" token
up >> name; // Read option name
// Handle names with included spaces // Read option name (can contain spaces)
while (up >> token && token != "value") while (is >> token && token != "value")
name += " " + token; name += string(" ", !name.empty()) + token;
up >> value; // Read option value // Read option value (can contain spaces)
while (is >> token)
value += string(" ", !value.empty()) + token;
// Handle values with included spaces if (!Options.count(name))
while (up >> token)
value += " " + token;
if (Options.find(name) != Options.end())
Options[name].set_value(value);
else
cout << "No such option: " << name << endl; cout << "No such option: " << name << endl;
else if (value.empty()) // UCI buttons don't have a value
Options[name] = true;
else
Options[name] = value;
} }
// go() is called when engine receives the "go" UCI command. The // go() is called when engine receives the "go" UCI command. The function sets
// function sets the thinking time and other parameters from the input // the thinking time and other parameters from the input string, and then starts
// string, and then calls think(). Returns false if a quit command // the main searching thread.
// is received while thinking, true otherwise.
bool go(Position& pos, UCIParser& up) { void go(Position& pos, istringstream& is) {
string token; string token;
SearchLimits limits; Search::LimitsType limits;
Move searchMoves[MAX_MOVES], *cur = searchMoves; std::vector<Move> searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 }; int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (up >> token) while (is >> token)
{ {
if (token == "infinite") if (token == "infinite")
limits.infinite = true; limits.infinite = true;
else if (token == "ponder") else if (token == "ponder")
limits.ponder = true; limits.ponder = true;
else if (token == "wtime") else if (token == "wtime")
up >> time[WHITE]; is >> time[WHITE];
else if (token == "btime") else if (token == "btime")
up >> time[BLACK]; is >> time[BLACK];
else if (token == "winc") else if (token == "winc")
up >> inc[WHITE]; is >> inc[WHITE];
else if (token == "binc") else if (token == "binc")
up >> inc[BLACK]; is >> inc[BLACK];
else if (token == "movestogo") else if (token == "movestogo")
up >> limits.movesToGo; is >> limits.movesToGo;
else if (token == "depth") else if (token == "depth")
up >> limits.maxDepth; is >> limits.maxDepth;
else if (token == "nodes") else if (token == "nodes")
up >> limits.maxNodes; is >> limits.maxNodes;
else if (token == "movetime") else if (token == "movetime")
up >> limits.maxTime; is >> limits.maxTime;
else if (token == "searchmoves") else if (token == "searchmoves")
while (up >> token) while (is >> token)
*cur++ = move_from_uci(pos, token); searchMoves.push_back(move_from_uci(pos, token));
} }
*cur = MOVE_NONE;
limits.time = time[pos.side_to_move()]; limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()]; limits.increment = inc[pos.side_to_move()];
assert(pos.is_ok()); Threads.start_thinking(pos, limits, searchMoves, true);
return think(pos, limits, searchMoves);
} }
// perft() is called when engine receives the "perft" command. // perft() is called when engine receives the "perft" command. The function
// The function calls perft() passing the required search depth // calls perft() with the required search depth then prints counted leaf nodes
// then prints counted leaf nodes and elapsed time. // and elapsed time.
void perft(Position& pos, UCIParser& up) { void perft(Position& pos, istringstream& is) {
int depth, time; int depth, time;
int64_t n;
if (!(up >> depth)) if (!(is >> depth))
return; return;
time = get_system_time(); time = system_time();
n = perft(pos, depth * ONE_PLY); int64_t n = Search::perft(pos, depth * ONE_PLY);
time = get_system_time() - time; time = system_time() - time;
std::cout << "\nNodes " << n std::cout << "\nNodes " << n
<< "\nTime (ms) " << time << "\nTime (ms) " << time

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -17,8 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cctype> #include <algorithm>
#include <iostream>
#include <sstream> #include <sstream>
#include "misc.h" #include "misc.h"
@@ -26,145 +25,103 @@
#include "ucioption.h" #include "ucioption.h"
using std::string; using std::string;
using std::cout;
using std::endl;
OptionsMap Options; // Global object OptionsMap Options; // Global object
// Our case insensitive less() function as required by UCI protocol /// Our case insensitive less() function as required by UCI protocol
static bool ci_less(char c1, char c2) { return tolower(c1) < tolower(c2); }
bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const { bool CaseInsensitiveLess::operator() (const string& s1, const string& s2) const {
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
int c1, c2;
size_t i = 0;
while (i < s1.size() && i < s2.size())
{
c1 = tolower(s1[i]);
c2 = tolower(s2[i++]);
if (c1 != c2)
return c1 < c2;
}
return s1.size() < s2.size();
}
// stringify() converts a numeric value of type T to a std::string
template<typename T>
static string stringify(const T& v) {
std::ostringstream ss;
ss << v;
return ss.str();
} }
/// OptionsMap c'tor initializes the UCI options to their hard coded default /// OptionsMap c'tor initializes the UCI options to their hard coded default
/// values and initializes the default value of "Threads" and "Minimum Split Depth" /// values and initializes the default value of "Threads" and "Min Split Depth"
/// parameters according to the number of CPU cores. /// parameters according to the number of CPU cores detected.
OptionsMap::OptionsMap() { OptionsMap::OptionsMap() {
int cpus = std::min(cpu_count(), MAX_THREADS);
int msd = cpus < 8 ? 4 : 7;
OptionsMap& o = *this; OptionsMap& o = *this;
o["Use Search Log"] = UCIOption(false); o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt"); o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin"); o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false); o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200); o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200); o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200); o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200); o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200); o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200); o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200); o["Cowardice"] = UCIOption(100, 0, 200);
o["Minimum Split Depth"] = UCIOption(4, 4, 7); o["Min Split Depth"] = UCIOption(msd, 4, 7);
o["Maximum Number of Threads per Split Point"] = UCIOption(5, 4, 8); o["Max Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(1, 1, MAX_THREADS); o["Threads"] = UCIOption(cpus, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false); o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192); o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button"); o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true); o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true); o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500); o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20); o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50); o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000); o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000); o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000); o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = UCIOption(false); o["UCI_Chess960"] = UCIOption(false);
o["UCI_AnalyseMode"] = UCIOption(false); o["UCI_AnalyseMode"] = UCIOption(false);
// Set some SMP parameters accordingly to the detected CPU count
UCIOption& thr = o["Threads"];
UCIOption& msd = o["Minimum Split Depth"];
thr.defaultValue = thr.currentValue = stringify(cpu_count());
if (cpu_count() >= 8)
msd.defaultValue = msd.currentValue = stringify(7);
} }
/// OptionsMap::print_all() returns a string with all the UCI options in chronological /// operator<<() is used to output all the UCI options in chronological insertion
/// insertion order (the idx field) and in the format defined by the UCI protocol. /// order (the idx field) and in the format defined by the UCI protocol.
std::ostream& operator<<(std::ostream& os, const OptionsMap& om) {
string OptionsMap::print_all() const { for (size_t idx = 0; idx < om.size(); idx++)
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
std::stringstream s; if (it->second.idx == idx)
for (size_t i = 0; i <= size(); i++)
for (OptionsMap::const_iterator it = begin(); it != end(); ++it)
if (it->second.idx == i)
{ {
const UCIOption& o = it->second; const UCIOption& o = it->second;
s << "\noption name " << it->first << " type " << o.type; os << "\noption name " << it->first << " type " << o.type;
if (o.type != "button") if (o.type != "button")
s << " default " << o.defaultValue; os << " default " << o.defaultValue;
if (o.type == "spin") if (o.type == "spin")
s << " min " << o.minValue << " max " << o.maxValue; os << " min " << o.min << " max " << o.max;
break; break;
} }
return s.str(); return os;
} }
/// Option class c'tors /// UCIOption class c'tors
UCIOption::UCIOption(const char* def) : type("string"), minValue(0), maxValue(0), idx(Options.size()) UCIOption::UCIOption(const char* v) : type("string"), min(0), max(0), idx(Options.size())
{ defaultValue = currentValue = def; } { defaultValue = currentValue = v; }
UCIOption::UCIOption(bool def, string t) : type(t), minValue(0), maxValue(0), idx(Options.size()) UCIOption::UCIOption(bool v, string t) : type(t), min(0), max(0), idx(Options.size())
{ defaultValue = currentValue = (def ? "true" : "false"); } { defaultValue = currentValue = (v ? "true" : "false"); }
UCIOption::UCIOption(int def, int minv, int maxv) : type("spin"), minValue(minv), maxValue(maxv), idx(Options.size()) UCIOption::UCIOption(int v, int minv, int maxv) : type("spin"), min(minv), max(maxv), idx(Options.size())
{ defaultValue = currentValue = stringify(def); } { std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
/// set_value() updates currentValue of the Option object. Normally it's up to /// UCIOption::operator=() updates currentValue. Normally it's up to the GUI to
/// the GUI to check for option's limits, but we could receive the new value /// check for option's limits, but we could receive the new value directly from
/// directly from the user by teminal window. So let's check the bounds anyway. /// the user by teminal window, so let's check the bounds anyway.
void UCIOption::set_value(const string& v) { void UCIOption::operator=(const string& v) {
assert(!type.empty()); assert(!type.empty());
if (v.empty()) if ( !v.empty()
return; && (type == "check" || type == "button") == (v == "true" || v == "false")
&& (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max)))
if ((type == "check" || type == "button") != (v == "true" || v == "false")) currentValue = v;
return;
if (type == "spin")
{
int val = atoi(v.c_str());
if (val < minValue || val > maxValue)
return;
}
currentValue = v;
} }

View File

@@ -1,7 +1,7 @@
/* /*
Stockfish, a UCI chess playing engine derived from Glaurung 2.1 Stockfish, a UCI chess playing engine derived from Glaurung 2.1
Copyright (C) 2004-2008 Tord Romstad (Glaurung author) Copyright (C) 2004-2008 Tord Romstad (Glaurung author)
Copyright (C) 2008-2010 Marco Costalba, Joona Kiiski, Tord Romstad Copyright (C) 2008-2012 Marco Costalba, Joona Kiiski, Tord Romstad
Stockfish is free software: you can redistribute it and/or modify Stockfish is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by it under the terms of the GNU General Public License as published by
@@ -25,61 +25,50 @@
#include <map> #include <map>
#include <string> #include <string>
struct OptionsMap;
/// UCIOption class implements an option as defined by UCI protocol
class UCIOption { class UCIOption {
public: public:
UCIOption() {} // To be used in a std::map UCIOption() {} // Required by std::map::operator[]
UCIOption(const char* defaultValue); UCIOption(const char* v);
UCIOption(bool defaultValue, std::string type = "check"); UCIOption(bool v, std::string type = "check");
UCIOption(int defaultValue, int minValue, int maxValue); UCIOption(int v, int min, int max);
void set_value(const std::string& v); void operator=(const std::string& v);
template<typename T> T value() const; void operator=(bool v) { *this = std::string(v ? "true" : "false"); }
operator int() const {
assert(type == "check" || type == "button" || type == "spin");
return (type == "spin" ? atoi(currentValue.c_str()) : currentValue == "true");
}
operator std::string() const {
assert(type == "string");
return currentValue;
}
private: private:
friend class OptionsMap; friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
std::string defaultValue, currentValue, type; std::string defaultValue, currentValue, type;
int minValue, maxValue; int min, max;
size_t idx; size_t idx;
}; };
/// Custom comparator because UCI options should not be case sensitive /// Custom comparator because UCI options should be case insensitive
struct CaseInsensitiveLess { struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const; bool operator() (const std::string&, const std::string&) const;
}; };
/// Our options container is actually a map with a customized c'tor /// Our options container is actually a map with a customized c'tor
class OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> { struct OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
public:
OptionsMap(); OptionsMap();
std::string print_all() const;
}; };
extern std::ostream& operator<<(std::ostream&, const OptionsMap&);
extern OptionsMap Options; extern OptionsMap Options;
/// Option::value() definition and specializations
template<typename T>
T UCIOption::value() const {
assert(type == "spin");
return T(atoi(currentValue.c_str()));
}
template<>
inline std::string UCIOption::value<std::string>() const {
assert(type == "string");
return currentValue;
}
template<>
inline bool UCIOption::value<bool>() const {
assert(type == "check" || type == "button");
return currentValue == "true";
}
#endif // !defined(UCIOPTION_H_INCLUDED) #endif // !defined(UCIOPTION_H_INCLUDED)

View File

@@ -165,7 +165,7 @@
</string-array> </string-array>
<string name="about_info">\ <string name="about_info">\
<b>Information</b>\n\ <b>Information</b>\n\
<i>DroidFish</i> ist eine Android-Version von <i>Stockfish 2.1.1</i>, \ <i>DroidFish</i> ist eine Android-Version von <i>Stockfish 2.2</i>, \
das zu den spielstärksten Schachprogrammen der Welt zählt. \ das zu den spielstärksten Schachprogrammen der Welt zählt. \
Die Android-Version erreicht auf einem 1GHz-Snapdragon-Prozessor oft eine Rechentiefe von 15 oder mehr Halbzügen.\n\ Die Android-Version erreicht auf einem 1GHz-Snapdragon-Prozessor oft eine Rechentiefe von 15 oder mehr Halbzügen.\n\
\n\ \n\

View File

@@ -183,7 +183,7 @@
<string name="about_info">\ <string name="about_info">\
<b>About</b>\n\ <b>About</b>\n\
<i>DroidFish</i> is an Android port of the famous <i>stockfish 2.1.1</i> chess engine. \ <i>DroidFish</i> is an Android port of the famous <i>stockfish 2.2</i> chess engine. \
<i>Stockfish</i> is one of the strongest chess engines in the world. \ <i>Stockfish</i> is one of the strongest chess engines in the world. \
The Android version can often search 15 ply or deeper on a 1GHz Snapdragon CPU.\n\ The Android version can often search 15 ply or deeper on a 1GHz Snapdragon CPU.\n\
\n\ \n\