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
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
it under the terms of the GNU General Public License as published by
@ -21,16 +21,19 @@
#include <iostream>
#include <vector>
#include "misc.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
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",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq -",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -",
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 10",
"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - - 0 11",
"4rrk1/pp1n3p/3q2pQ/2p1pb2/2PP4/2P3N1/P2B2PP/4RRK1 b - - 7 19",
"rq3rk1/ppp2ppp/1bnpb3/3N2B1/3NP3/7P/PPPQ1PP1/2KR3R w - - 7 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",
"r1q2rk1/2p1bppp/2Pp4/p6b/Q1PNp3/4B3/PP1R1PPP/2K4R w - - 2 18",
"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
/// 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
/// be used, the limit value spent for each position (optional, default
/// is ply 12), an optional file name where to look for positions in fen
/// format (default are the BenchmarkPositions defined above) and the type
/// of the limit value: depth (default), time in secs or number of nodes.
/// The analysis is written to a file named bench.txt.
/// be used, the limit value spent for each position (optional, default is
/// depth 12), an optional file name where to look for positions in fen
/// format (defaults are the positions defined above) and the type of the
/// limit value: depth (default), time in secs or number of nodes.
void benchmark(int argc, char* argv[]) {
vector<string> fenList;
SearchLimits limits;
int64_t totalNodes;
vector<string> fens;
LimitsType limits;
int time;
// Load default positions
for (int i = 0; !Defaults[i].empty(); i++)
fenList.push_back(Defaults[i]);
int64_t nodes = 0;
// Assign default values to missing arguments
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 valType = argc > 6 ? argv[6] : "depth";
Options["Hash"].set_value(ttSize);
Options["Threads"].set_value(threads);
Options["OwnBook"].set_value("false");
Options["Hash"] = ttSize;
Options["Threads"] = threads;
Options["OwnBook"] = false;
// Search should be limited by nodes, time or depth ?
if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else if (valType == "time")
if (valType == "time")
limits.maxTime = 1000 * atoi(valStr.c_str()); // maxTime is in ms
else if (valType == "nodes")
limits.maxNodes = atoi(valStr.c_str());
else
limits.maxDepth = atoi(valStr.c_str());
// Do we need to load positions from a given FEN file ?
if (fenFile != "default")
{
string fen;
ifstream f(fenFile.c_str());
ifstream file(fenFile.c_str());
if (f.is_open())
if (!file.is_open())
{
fenList.clear();
while (getline(f, fen))
if (!fen.empty())
fenList.push_back(fen);
f.close();
}
else
{
cerr << "Unable to open FEN file " << fenFile << endl;
cerr << "Unable to open file " << fenFile << endl;
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 !
totalNodes = 0;
time = get_system_time();
time = 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(fenList[i], false, 0);
Position pos(fens[i], false, 0);
cerr << "\nBench position: " << i + 1 << '/' << fenList.size() << endl;
cerr << "\nPosition: " << i + 1 << '/' << fens.size() << endl;
if (valType == "perft")
{
int64_t cnt = perft(pos, limits.maxDepth * ONE_PLY);
totalNodes += cnt;
cerr << "\nPerft " << limits.maxDepth << " nodes counted: " << cnt << endl;
cerr << "\nPerft " << limits.maxDepth << " leaf nodes: " << cnt << endl;
nodes += cnt;
}
else
{
if (!think(pos, limits, moves))
break;
totalNodes += pos.nodes_searched();
Threads.start_thinking(pos, limits, vector<Move>(), false);
nodes += RootPosition.nodes_searched();
}
}
time = get_system_time() - time;
time = system_time() - time;
cerr << "\n==============================="
cerr << "\n==========================="
<< "\nTotal time (ms) : " << time
<< "\nNodes searched : " << totalNodes
<< "\nNodes/second : " << (int)(totalNodes / (time / 1000.0)) << endl << 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
<< "\nNodes searched : " << nodes
<< "\nNodes/second : " << int(nodes / (time / 1000.0)) << endl;
}

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -28,31 +28,31 @@ namespace {
RESULT_UNKNOWN,
RESULT_INVALID,
RESULT_WIN,
RESULT_LOSS,
RESULT_DRAW
};
struct KPKPosition {
Result classify_knowns(int index);
Result classify(int index, Result db[]);
private:
void from_index(int index);
bool is_legal() const;
bool is_immediate_draw() const;
bool is_immediate_win() const;
Bitboard wk_attacks() const { return StepAttacksBB[WK][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[BK][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[WP][pawnSquare]; }
Result classify_white(const Result db[]);
Result classify_black(const Result db[]);
Bitboard wk_attacks() const { return StepAttacksBB[W_KING][whiteKingSquare]; }
Bitboard bk_attacks() const { return StepAttacksBB[B_KING][blackKingSquare]; }
Bitboard pawn_attacks() const { return StepAttacksBB[W_PAWN][pawnSquare]; }
Square whiteKingSquare, blackKingSquare, pawnSquare;
Color sideToMove;
};
// 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
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);
}
@ -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;
bool repeat;
int index, bit, repeat = 1;
// Initialize table
for (int i = 0; i < IndexMax; i++)
{
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;
}
for (index = 0; index < IndexMax; index++)
db[index] = pos.classify_knowns(index);
// Iterate until all positions are classified (30 cycles needed)
do {
repeat = false;
for (int i = 0; i < IndexMax; i++)
if (bb[i] == RESULT_UNKNOWN)
{
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);
while (repeat)
for (repeat = index = 0; index < IndexMax; index++)
if ( db[index] == RESULT_UNKNOWN
&& pos.classify(index, db) != RESULT_UNKNOWN)
repeat = 1;
// Map 32 position results into one KPKBitbase[] entry
for (int i = 0; i < IndexMax / 32; i++)
for (int j = 0; j < 32; j++)
if (bb[32 * i + j] == RESULT_WIN || bb[32 * i + j] == RESULT_LOSS)
KPKBitbase[i] |= (1 << j);
for (index = 0; index < IndexMax / 32; index++)
for (bit = 0; bit < 32; bit++)
if (db[32 * index + bit] == RESULT_WIN)
KPKBitbase[index] |= (1 << bit);
}
namespace {
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// 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 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)
// A KPK bitbase index is an integer in [0, IndexMax] range
//
// Information is mapped in this way
//
// bit 0: side to move (WHITE or BLACK)
// 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 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)
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 r = int(stm) + 2 * int(bksq) + 128 * int(wksq) + 8192 * p;
int p = file_of(wpsq) + 4 * (rank_of(wpsq) - 1);
int r = stm + 2 * bksq + 128 * wksq + 8192 * p;
assert(r >= 0 && r < IndexMax);
@ -131,73 +116,79 @@ namespace {
void KPKPosition::from_index(int index) {
int s = (index / 8192) % 24;
sideToMove = Color(index % 2);
blackKingSquare = Square((index / 2) % 64);
whiteKingSquare = Square((index / 128) % 64);
pawnSquare = make_square(File(s % 4), Rank(s / 4 + 1));
int s = index >> 13;
sideToMove = Color(index & 1);
blackKingSquare = Square((index >> 1) & 63);
whiteKingSquare = Square((index >> 7) & 63);
pawnSquare = make_square(File(s & 3), Rank((s >> 2) + 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
|| whiteKingSquare == blackKingSquare
|| blackKingSquare == pawnSquare)
return false;
return RESULT_INVALID;
if (sideToMove == WHITE)
{
if ( bit_is_set(wk_attacks(), blackKingSquare)
|| bit_is_set(pawn_attacks(), blackKingSquare))
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 {
// Check if a king can be captured
if ( bit_is_set(wk_attacks(), blackKingSquare)
|| (bit_is_set(pawn_attacks(), blackKingSquare) && sideToMove == WHITE))
return RESULT_INVALID;
// The position is an immediate win if it is white to move and the
// white pawn can be promoted without getting captured.
return sideToMove == WHITE
&& square_rank(pawnSquare) == RANK_7
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| bit_is_set(wk_attacks(), pawnSquare + DELTA_N));
if ( rank_of(pawnSquare) == RANK_7
&& sideToMove == WHITE
&& whiteKingSquare != pawnSquare + DELTA_N
&& ( square_distance(blackKingSquare, pawnSquare + DELTA_N) > 1
|| 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
// classified as RESULT_DRAW, the current position is classified RESULT_DRAW
// otherwise the current position is classified as RESULT_UNKNOWN.
@ -208,13 +199,13 @@ namespace {
Result r;
// King moves
b = pos.wk_attacks();
b = wk_attacks();
while (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;
if (r == RESULT_UNKNOWN)
@ -222,26 +213,24 @@ namespace {
}
// Pawn moves
if (square_rank(pos.pawnSquare) < RANK_7)
if (rank_of(pawnSquare) < RANK_7)
{
s = pos.pawnSquare + DELTA_N;
r = bb[compute_index(pos.whiteKingSquare, pos.blackKingSquare, s, BLACK)];
s = pawnSquare + DELTA_N;
r = db[compute_index(whiteKingSquare, blackKingSquare, s, BLACK)];
if (r == RESULT_LOSS)
if (r == RESULT_WIN)
return RESULT_WIN;
if (r == RESULT_UNKNOWN)
unknownFound = true;
// Double pawn push
if ( square_rank(s) == RANK_3
&& s != pos.whiteKingSquare
&& s != pos.blackKingSquare)
if (rank_of(s) == RANK_3 && r != RESULT_INVALID)
{
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;
if (r == RESULT_UNKNOWN)
@ -251,14 +240,12 @@ namespace {
return unknownFound ? RESULT_UNKNOWN : RESULT_DRAW;
}
Result classify_btm(const KPKPosition& pos, const Result bb[]) {
Result KPKPosition::classify_black(const Result db[]) {
// 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
// classified as RESULT_WIN, the current position is classified as
// RESULT_LOSS. Otherwise, the current position is classified as
// RESULT_UNKNOWN.
// classified as RESULT_WIN, the position is classified as RESULT_WIN.
// Otherwise, the current position is classified as RESULT_UNKNOWN.
bool unknownFound = false;
Bitboard b;
@ -266,11 +253,11 @@ namespace {
Result r;
// King moves
b = pos.bk_attacks();
b = bk_attacks();
while (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)
return RESULT_DRAW;
@ -278,7 +265,7 @@ namespace {
if (r == RESULT_UNKNOWN)
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
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
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/>.
*/
#include <algorithm>
#include <cstring>
#include <iostream>
#include "bitboard.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] = {
0x0440049104032280ULL, 0x1021023C82008040ULL, 0x0404040082000048ULL,
0x48C4440084048090ULL, 0x2801104026490000ULL, 0x4100880442040800ULL,
0x0181011002E06040ULL, 0x9101004104200E00ULL, 0x1240848848310401ULL,
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 BMasks[64];
Bitboard BMagics[64];
Bitboard* BAttacks[64];
int BShifts[64];
Bitboard SetMaskBB[65];
Bitboard ClearMaskBB[65];
Bitboard SquaresByColorBB[2];
Bitboard FileBB[8];
Bitboard RankBB[8];
Bitboard NeighboringFilesBB[8];
@ -186,19 +54,18 @@ Bitboard RookPseudoAttacks[64];
Bitboard QueenPseudoAttacks[64];
uint8_t BitCount8Bit[256];
int SquareDistance[64][64];
namespace {
void init_masks();
void init_step_attacks();
void init_pseudo_attacks();
void init_between_bitboards();
Bitboard index_to_bitboard(int index, Bitboard mask);
Bitboard sliding_attacks(int sq, Bitboard occupied, int deltas[][2],
int fmin, int fmax, int rmin, int rmax);
void init_sliding_attacks(Bitboard attacks[], int attackIndex[], Bitboard mask[],
const int shift[], const Bitboard mult[], int deltas[][2]);
CACHE_LINE_ALIGNMENT
int BSFTable[64];
Bitboard RookTable[0x19000]; // Storage space for rook attacks
Bitboard BishopTable[0x1480]; // Storage space for bishop attacks
void init_magic_bitboards(PieceType pt, Bitboard* attacks[], Bitboard magics[],
Bitboard masks[], int shifts[]);
}
@ -211,7 +78,7 @@ void print_bitboard(Bitboard b) {
{
std::cout << "+---+---+---+---+---+---+---+---+" << '\n';
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";
}
@ -225,39 +92,22 @@ void print_bitboard(Bitboard b) {
#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) {
return Square(BitTable[((b & -b) * 0x218a392cd3d5dbfULL) >> 58]);
return Square(BSFTable[((b & -b) * 0x218A392CD3D5DBFULL) >> 58]);
}
Square pop_1st_bit(Bitboard* b) {
Bitboard bb = *b;
*b &= (*b - 1);
return Square(BitTable[((bb & -bb) * 0x218a392cd3d5dbfULL) >> 58]);
return Square(BSFTable[((bb & -bb) * 0x218A392CD3D5DBFULL) >> 58]);
}
#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) {
b ^= (b - 1);
uint32_t fold = int(b) ^ int(b >> 32);
return Square(BitTable[(fold * 0x783a9b23) >> 26]);
uint32_t fold = unsigned(b) ^ unsigned(b >> 32);
return Square(BSFTable[(fold * 0x783A9B23) >> 26]);
}
// Use type-punning
@ -284,12 +134,12 @@ Square pop_1st_bit(Bitboard* bb) {
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);
*bb = u.b;
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);
*bb = u.b;
return ret;
@ -298,189 +148,219 @@ Square pop_1st_bit(Bitboard* bb) {
#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.
void init_bitboards() {
void bitboards_init() {
int rookDeltas[4][2] = {{0,1},{0,-1},{1,0},{-1,0}};
int bishopDeltas[4][2] = {{1,1},{-1,1},{1,-1},{-1,-1}};
for (Bitboard b = 0; b < 256; b++)
BitCount8Bit[b] = (uint8_t)popcount<Max15>(b);
init_masks();
init_step_attacks();
init_sliding_attacks(RAttacks, RAttackIndex, RMask, RShift, RMult, rookDeltas);
init_sliding_attacks(BAttacks, BAttackIndex, BMask, BShift, BMult, bishopDeltas);
init_pseudo_attacks();
init_between_bitboards();
for (Square s = SQ_A1; s <= SQ_H8; s++)
{
SetMaskBB[s] = 1ULL << s;
ClearMaskBB[s] = ~SetMaskBB[s];
}
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 {
// All functions below are used to precompute various bitboards during
// 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.
Bitboard sliding_attacks(PieceType pt, Square sq, Bitboard occupied) {
void init_masks() {
SquaresByColorBB[DARK] = 0xAA55AA55AA55AA55ULL;
SquaresByColorBB[LIGHT] = ~SquaresByColorBB[DARK];
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;
Square deltas[][4] = { { DELTA_N, DELTA_E, DELTA_S, DELTA_W },
{ DELTA_NE, DELTA_SE, DELTA_SW, DELTA_NW } };
Bitboard attacks = 0;
Square* delta = (pt == ROOK ? deltas[0] : deltas[1]);
for (int i = 0; i < 4; i++)
{
dx = deltas[i][0];
dy = deltas[i][1];
f = fl + dx;
r = rk + dy;
Square s = sq + delta[i];
while ( (dx == 0 || (f >= fmin && f <= fmax))
&& (dy == 0 || (r >= rmin && r <= rmax)))
while (square_is_ok(s) && square_distance(s, s - delta[i]) == 1)
{
attacks |= (1ULL << (f + r * 8));
set_bit(&attacks, s);
if (occupied & (1ULL << (f + r * 8)))
if (bit_is_set(occupied, s))
break;
f += dx;
r += dy;
s += delta[i];
}
}
return attacks;
}
Bitboard index_to_bitboard(int index, Bitboard mask) {
Bitboard result = EmptyBoardBB;
int sq, cnt = 0;
Bitboard pick_random(Bitboard mask, RKISS& rk, int booster) {
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++))
result |= (1ULL << sq);
}
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;
if (BitCount8Bit[(mask * magic) >> 56] >= 6)
return magic;
}
}
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++)
{
BishopPseudoAttacks[s] = bishop_attacks_bb(s, EmptyBoardBB);
RookPseudoAttacks[s] = rook_attacks_bb(s, EmptyBoardBB);
QueenPseudoAttacks[s] = queen_attacks_bb(s, EmptyBoardBB);
// Board edges are not considered in the relevant occupancies
edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
// 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
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
it under the terms of the GNU General Public License as published by
@ -23,27 +23,6 @@
#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 NeighboringFilesBB[8];
extern Bitboard ThisAndNeighboringFilesBB[8];
@ -60,17 +39,15 @@ extern Bitboard SquaresInFrontMask[2][64];
extern Bitboard PassedPawnMask[2][64];
extern Bitboard AttackSpanMask[2][64];
extern const uint64_t RMult[64];
extern const int RShift[64];
extern Bitboard RMask[64];
extern int RAttackIndex[64];
extern Bitboard RAttacks[0x19000];
extern uint64_t RMagics[64];
extern int RShifts[64];
extern Bitboard RMasks[64];
extern Bitboard* RAttacks[64];
extern const uint64_t BMult[64];
extern const int BShift[64];
extern Bitboard BMask[64];
extern int BAttackIndex[64];
extern Bitboard BAttacks[0x1480];
extern uint64_t BMagics[64];
extern int BShifts[64];
extern Bitboard BMasks[64];
extern Bitboard* BAttacks[64];
extern Bitboard BishopPseudoAttacks[64];
extern Bitboard RookPseudoAttacks[64];
@ -86,11 +63,11 @@ inline Bitboard bit_is_set(Bitboard b, Square s) {
return b & SetMaskBB[s];
}
inline void set_bit(Bitboard *b, Square s) {
inline void set_bit(Bitboard* b, Square s) {
*b |= SetMaskBB[s];
}
inline void clear_bit(Bitboard *b, Square s) {
inline void clear_bit(Bitboard* b, Square s) {
*b &= ClearMaskBB[s];
}
@ -102,7 +79,7 @@ inline Bitboard make_move_bb(Square from, Square 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;
}
@ -115,7 +92,7 @@ inline Bitboard rank_bb(Rank r) {
}
inline Bitboard rank_bb(Square s) {
return RankBB[square_rank(s)];
return RankBB[rank_of(s)];
}
inline Bitboard file_bb(File f) {
@ -123,33 +100,25 @@ inline Bitboard file_bb(File f) {
}
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
/// bitboard representing all squares on the neighboring files.
/// neighboring_files_bb takes a file as input and returns a bitboard representing
/// all squares on the neighboring files.
inline Bitboard neighboring_files_bb(File f) {
return NeighboringFilesBB[f];
}
inline Bitboard neighboring_files_bb(Square s) {
return NeighboringFilesBB[square_file(s)];
}
/// this_and_neighboring_files_bb takes a file or a square as input and returns
/// a bitboard representing all squares on the given and neighboring files.
/// this_and_neighboring_files_bb takes a file as input and returns a bitboard
/// representing all squares on the given and neighboring files.
inline Bitboard this_and_neighboring_files_bb(File 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
/// 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) {
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)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] + ((b * RMult[s]) >> RShift[s])];
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
return unsigned(((occ & RMasks[s]) * RMagics[s]) >> RShifts[s]);
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] + ((b * BMult[s]) >> BShift[s])];
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
return unsigned(((occ & BMasks[s]) * BMagics[s]) >> BShifts[s]);
}
#else // if !defined(IS_64BIT)
inline Bitboard rook_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & RMask[s];
return RAttacks[RAttackIndex[s] +
(unsigned(int(b) * int(RMult[s]) ^ int(b >> 32) * int(RMult[s] >> 32)) >> RShift[s])];
FORCE_INLINE unsigned rook_index(Square s, Bitboard occ) {
Bitboard b = occ & RMasks[s];
return unsigned(int(b) * int(RMagics[s]) ^ int(b >> 32) * int(RMagics[s] >> 32)) >> RShifts[s];
}
inline Bitboard bishop_attacks_bb(Square s, Bitboard blockers) {
Bitboard b = blockers & BMask[s];
return BAttacks[BAttackIndex[s] +
(unsigned(int(b) * int(BMult[s]) ^ int(b >> 32) * int(BMult[s] >> 32)) >> BShift[s])];
FORCE_INLINE unsigned bishop_index(Square s, Bitboard occ) {
Bitboard b = occ & BMasks[s];
return unsigned(int(b) * int(BMagics[s]) ^ int(b >> 32) * int(BMagics[s] >> 32)) >> BShifts[s];
}
#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) {
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) {
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 init_bitboards();
extern void bitboards_init();
#endif // !defined(BITBOARD_H_INCLUDED)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -21,25 +21,29 @@
#if !defined(BITCOUNT_H_INCLUDED)
#define BITCOUNT_H_INCLUDED
#include <cassert>
#include "types.h"
enum BitCountType {
CNT64,
CNT64_MAX15,
CNT32,
CNT32_MAX15,
CNT_POPCNT
CNT_64,
CNT_64_MAX15,
CNT_32,
CNT_32_MAX15,
CNT_HW_POPCNT
};
/// count_1s() counts the number of nonzero bits in a bitboard.
/// We have different optimized versions according if platform
/// is 32 or 64 bits, and to the maximum number of nonzero bits.
/// We also support hardware popcnt instruction. See Readme.txt
/// on how to pgo compile with popcnt support.
template<BitCountType> inline int count_1s(Bitboard);
/// Determine at compile time the best popcount<> specialization according if
/// platform is 32 or 64 bits, to the maximum number of nonzero bits to count or
/// use hardware popcnt instruction when available.
const BitCountType Full = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64 : CNT_32;
const BitCountType Max15 = HasPopCnt ? CNT_HW_POPCNT : Is64Bit ? CNT_64_MAX15 : CNT_32_MAX15;
/// popcount() counts the number of nonzero bits in a bitboard
template<BitCountType> inline int popcount(Bitboard);
template<>
inline int count_1s<CNT64>(Bitboard b) {
inline int popcount<CNT_64>(Bitboard b) {
b -= ((b>>1) & 0x5555555555555555ULL);
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b = ((b>>4) + b) & 0x0F0F0F0F0F0F0F0FULL;
@ -48,7 +52,7 @@ inline int count_1s<CNT64>(Bitboard b) {
}
template<>
inline int count_1s<CNT64_MAX15>(Bitboard b) {
inline int popcount<CNT_64_MAX15>(Bitboard b) {
b -= (b>>1) & 0x5555555555555555ULL;
b = ((b>>2) & 0x3333333333333333ULL) + (b & 0x3333333333333333ULL);
b *= 0x1111111111111111ULL;
@ -56,7 +60,7 @@ inline int count_1s<CNT64_MAX15>(Bitboard b) {
}
template<>
inline int count_1s<CNT32>(Bitboard b) {
inline int popcount<CNT_32>(Bitboard b) {
unsigned w = unsigned(b >> 32), v = unsigned(b);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
@ -69,7 +73,7 @@ inline int count_1s<CNT32>(Bitboard b) {
}
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);
v -= (v >> 1) & 0x55555555; // 0-2 in 2 bits
w -= (w >> 1) & 0x55555555;
@ -81,17 +85,27 @@ inline int count_1s<CNT32_MAX15>(Bitboard b) {
}
template<>
inline int count_1s<CNT_POPCNT>(Bitboard b) {
inline int popcount<CNT_HW_POPCNT>(Bitboard b) {
#if !defined(USE_POPCNT)
assert(false);
return int(b != 0); // Avoid 'b not used' warning
#elif defined(_MSC_VER) && defined(__INTEL_COMPILER)
return _mm_popcnt_u64(b);
#elif defined(_MSC_VER)
return (int)__popcnt64(b);
#elif defined(__GNUC__)
#else
unsigned long ret;
__asm__("popcnt %1, %0" : "=r" (ret) : "r" (b));
return ret;
#endif
}

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -28,6 +28,7 @@
#include <iostream>
#include "book.h"
#include "misc.h"
#include "movegen.h"
using namespace std;
@ -35,7 +36,7 @@ using namespace std;
namespace {
// Random numbers from PolyGlot, used to compute book hash keys
const uint64_t Random64[781] = {
const Key PolyGlotRandoms[781] = {
0x9D39247E33776D41ULL, 0x2AF7398005AAA5C7ULL, 0x44DB015024623547ULL,
0x9C15F73E62A76AE2ULL, 0x75834465489C0C89ULL, 0x3290AC3A203001BFULL,
0x0FBBAD1F61042279ULL, 0xE83A908FF2FB60CAULL, 0x0D7E765D58755C10ULL,
@ -299,57 +300,56 @@ namespace {
0xF8D626AAAF278509ULL
};
// Indices to the Random64[] array
const int PieceIdx = 0;
const int CastleIdx = 768;
const int EnPassantIdx = 772;
const int TurnIdx = 780;
// Offsets to the PolyGlotRandoms[] array of zobrist keys
const Key* ZobPiece = PolyGlotRandoms + 0;
const Key* ZobCastle = PolyGlotRandoms + 768;
const Key* ZobEnPassant = PolyGlotRandoms + 772;
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
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;
Bitboard b = pos.occupied_squares();
while (b)
{
Square s = pop_1st_bit(&b);
int p = PieceToPoly[int(pos.piece_on(s))];
result ^= Random64[PieceIdx + 64 * p + int(s)];
result ^= ZobPiece[PieceOfs[pos.piece_on(s)] + s];
}
if (pos.can_castle_kingside(WHITE))
result ^= Random64[CastleIdx + 0];
if (pos.can_castle(WHITE_OO))
result ^= ZobCastle[0];
if (pos.can_castle_queenside(WHITE))
result ^= Random64[CastleIdx + 1];
if (pos.can_castle(WHITE_OOO))
result ^= ZobCastle[1];
if (pos.can_castle_kingside(BLACK))
result ^= Random64[CastleIdx + 2];
if (pos.can_castle(BLACK_OO))
result ^= ZobCastle[2];
if (pos.can_castle_queenside(BLACK))
result ^= Random64[CastleIdx + 3];
if (pos.can_castle(BLACK_OOO))
result ^= ZobCastle[3];
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)
result ^= Random64[TurnIdx];
result ^= ZobTurn[0];
return result;
}
}
/// 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>();
}
@ -362,8 +362,8 @@ Book::~Book() {
}
/// Book::close() closes the file only if it is open, otherwise
/// we can end up in a little mess due to how std::ifstream works.
/// Book::close() closes the file only if it is open, otherwise the call fails
/// and the failbit internal state flag is set.
void Book::close() {
@ -371,24 +371,24 @@ void Book::close() {
bookFile.close();
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) {
// Close old file before opening the new
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
if (!bookFile.is_open())
return;
// Get the book size in number of entries
bookFile.seekg(0, ios::end);
// Get the book size in number of entries, we are already at the file end
bookSize = long(bookFile.tellg()) / sizeof(BookEntry);
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
/// MOVE_NONE if no book move is found. If findBestMove is true then
/// return always the highest rated book move.
/// Book::probe() gets a book move for a given position. Returns MOVE_NONE
/// if no book move is found. If findBest is true then returns always the
/// 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;
BookEntry entry;
int bookMove = MOVE_NONE;
unsigned score, scoresSum = 0, bestScore = 0;
unsigned scoresSum = 0, bestScore = 0, bookMove = 0;
uint64_t key = book_key(pos);
int idx = first_entry(key) - 1;
// 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)
break;
score = entry.count;
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;
// Choose book move according to its score. If a move has a very
// high score it has higher probability to be choosen than a move
// with lower score. Note that first entry is always chosen.
if ( RKiss.rand<unsigned>() % scoresSum < entry.count
|| (findBest && entry.count > bestScore))
bookMove = entry.move;
}
if (entry.count > bestScore)
bestScore = entry.count;
}
if (!bookMove)
return MOVE_NONE;
// A PolyGlot book move is encoded as follows:
//
// 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
// all other cases we can directly compare with a Move after having
// masked out special Move's flags that are not supported by PolyGlot.
int p = (bookMove >> 12) & 7;
int promotion = (bookMove >> 12) & 7;
if (p)
bookMove = int(make_promotion_move(move_from(Move(bookMove)),
move_to(Move(bookMove)), PieceType(p + 1)));
// Verify the book move (if any) is legal
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if ((int(cur->move) & ~(3 << 14)) == bookMove) // Mask out special flags
return cur->move;
if (promotion)
bookMove = make_promotion_move(move_from(Move(bookMove)),
move_to(Move(bookMove)),
PieceType(promotion + 1));
// Verify the book move is legal
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (unsigned(ml.move() & ~(3 << 14)) == bookMove) // Mask out special flags
return ml.move();
return MOVE_NONE;
}
/// Book::find_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
/// entry with the same key as the input is returned. When the key is not
/// 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 (leftmost)
/// book entry with the same key as the input is returned. When the key is not
/// 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;
// Binary search (finds the leftmost entry)
// Binary search (finds the leftmost entry with given key)
left = 0;
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.
template<typename T>
void Book::get_number(T& n) {
Book& Book::operator>>(T& n) {
n = 0;
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);
get_number(e.key);
get_number(e.move);
get_number(e.count);
get_number(e.learn);
*this >> e.key >> e.move >> e.count >> e.learn;
if (!bookFile.good())
{

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -23,14 +23,13 @@
#include <fstream>
#include <string>
#include "move.h"
#include "position.h"
#include "rkiss.h"
// 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
// according to key. Lowest key first.
/// 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
/// according to key. Lowest key first.
struct BookEntry {
uint64_t key;
uint16_t move;
@ -44,19 +43,19 @@ public:
~Book();
void open(const std::string& fileName);
void close();
Move get_move(const Position& pos, bool findBestMove);
Move probe(const Position& pos, bool findBestMove);
const std::string name() const { return bookName; }
private:
template<typename T> void get_number(T& n);
template<typename T> Book& operator>>(T& n);
BookEntry read_entry(int idx);
int find_entry(uint64_t key);
int first_entry(uint64_t key);
RKISS RKiss;
std::ifstream bookFile;
std::string bookName;
int bookSize;
RKISS RKiss;
};
#endif // !defined(BOOK_H_INCLUDED)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -17,6 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "bitcount.h"
@ -59,107 +60,75 @@ namespace {
// the two kings in basic endgames.
const int DistanceBonus[8] = { 0, 0, 100, 80, 60, 40, 20, 10 };
// Penalty for big distance between king and knight for the defending king
// and knight in KR vs KN endgames.
const int KRKNKingKnightDistancePenalty[8] = { 0, 0, 4, 10, 20, 32, 48, 70 };
// Get the material key of a Position out of the given endgame key code
// like "KBPKN". The trick here is to first forge an ad-hoc fen string
// 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"
const string swap_colors(const string& keyCode) {
assert(code.length() > 0 && code.length() < 8);
assert(code[0] == 'K');
size_t idx = keyCode.find('K', 1);
return keyCode.substr(idx) + keyCode.substr(0, idx);
string sides[] = { code.substr(code.find('K', 1)), // Weaker
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
// like "KBPKN". The trick here is to first build up a FEN string and then
// 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;
template<typename M>
void delete_endgame(const typename M::value_type& p) { delete p.second; }
} // namespace
/// Endgames member definitions
template<> const Endgames::EFMap& Endgames::get<EF>() const { return maps.first; }
template<> const Endgames::SFMap& Endgames::get<SF>() const { return maps.second; }
/// Endgames members definitions
Endgames::Endgames() {
add<Endgame<Value, KNNK> >("KNNK");
add<Endgame<Value, KPK> >("KPK");
add<Endgame<Value, KBNK> >("KBNK");
add<Endgame<Value, KRKP> >("KRKP");
add<Endgame<Value, KRKB> >("KRKB");
add<Endgame<Value, KRKN> >("KRKN");
add<Endgame<Value, KQKR> >("KQKR");
add<Endgame<Value, KBBKN> >("KBBKN");
add<KPK>("KPK");
add<KNNK>("KNNK");
add<KBNK>("KBNK");
add<KRKP>("KRKP");
add<KRKB>("KRKB");
add<KRKN>("KRKN");
add<KQKR>("KQKR");
add<KBBKN>("KBBKN");
add<Endgame<ScaleFactor, KNPK> >("KNPK");
add<Endgame<ScaleFactor, KRPKR> >("KRPKR");
add<Endgame<ScaleFactor, KBPKB> >("KBPKB");
add<Endgame<ScaleFactor, KBPPKB> >("KBPPKB");
add<Endgame<ScaleFactor, KBPKN> >("KBPKN");
add<Endgame<ScaleFactor, KRPPKRP> >("KRPPKRP");
add<KNPK>("KNPK");
add<KRPKR>("KRPKR");
add<KBPKB>("KBPKB");
add<KBPKN>("KBPKN");
add<KBPPKB>("KBPPKB");
add<KRPPKRP>("KRPPKRP");
}
Endgames::~Endgames() {
for (EFMap::const_iterator it = get<EF>().begin(); it != get<EF>().end(); ++it)
delete it->second;
for (SFMap::const_iterator it = get<SF>().begin(); it != get<SF>().end(); ++it)
delete it->second;
for_each(m1.begin(), m1.end(), delete_endgame<M1>);
for_each(m2.begin(), m2.end(), delete_endgame<M2>);
}
template<class T>
void Endgames::add(const string& keyCode) {
template<EndgameType E>
void Endgames::add(const string& code) {
typedef typename T::Base F;
typedef std::map<Key, F*> M;
typedef typename eg_family<E>::type T;
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(keyCode), new T(WHITE)));
const_cast<M&>(get<F>()).insert(std::pair<Key, F*>(mat_key(swap_colors(keyCode)), new T(BLACK)));
map((T*)0)[key(code, WHITE)] = new Endgame<E>(WHITE);
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
/// 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
/// of the board, and for keeping the distance between the two kings small.
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.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
/// defending king towards a corner square of the right color.
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.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 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,
// if we have a bishop that cannot reach the above squares we
// 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);
loserKSq = flop_square(loserKSq);
winnerKSq = mirror(winnerKSq);
loserKSq = mirror(loserKSq);
}
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.
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(weakerSide) == VALUE_ZERO);
@ -231,22 +200,22 @@ Value Endgame<Value, KPK>::apply(const Position& pos) const {
{
wksq = pos.king_square(WHITE);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN, 0);
wpsq = pos.piece_list(WHITE, PAWN)[0];
stm = pos.side_to_move();
}
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());
wksq = flip(pos.king_square(BLACK));
bksq = flip(pos.king_square(WHITE));
wpsq = flip(pos.piece_list(BLACK, PAWN)[0]);
stm = flip(pos.side_to_move());
}
if (square_file(wpsq) >= FILE_E)
if (file_of(wpsq) >= FILE_E)
{
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
wksq = mirror(wksq);
bksq = mirror(bksq);
wpsq = mirror(wpsq);
}
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
+ PawnValueEndgame
+ Value(square_rank(wpsq));
+ Value(rank_of(wpsq));
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
/// away.
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.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);
wksq = pos.king_square(strongerSide);
wrsq = pos.piece_list(strongerSide, ROOK, 0);
wrsq = pos.piece_list(strongerSide, ROOK)[0];
bksq = pos.king_square(weakerSide);
bpsq = pos.piece_list(weakerSide, PAWN, 0);
bpsq = pos.piece_list(weakerSide, PAWN)[0];
if (strongerSide == BLACK)
{
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
bksq = flip_square(bksq);
bpsq = flip_square(bpsq);
wksq = flip(wksq);
wrsq = flip(wrsq);
bksq = flip(bksq);
bpsq = flip(bpsq);
}
Square queeningSq = make_square(square_file(bpsq), RANK_1);
Square queeningSq = make_square(file_of(bpsq), RANK_1);
Value result;
// 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));
// 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,
// the position is drawish
else if ( square_rank(bksq) <= RANK_3
else if ( rank_of(bksq) <= RANK_3
&& square_distance(bksq, bpsq) == 1
&& square_rank(wksq) >= RANK_4
&& rank_of(wksq) >= RANK_4
&& square_distance(wksq, bpsq) - tempo > 2)
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
/// score is slightly bigger when the defending king is close to the edge.
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.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
/// in KR vs KB, particularly if the king and the knight are far apart.
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.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, KNIGHT) == 1);
Square defendingKSq = pos.king_square(weakerSide);
Square nSq = pos.piece_list(weakerSide, KNIGHT, 0);
int d = square_distance(defendingKSq, nSq);
Value result = Value(10)
+ MateTable[defendingKSq]
+ KRKNKingKnightDistancePenalty[d];
const int penalty[8] = { 0, 10, 14, 20, 30, 42, 58, 80 };
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;
}
@ -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
/// able to win KQ vs KR.
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.piece_count(strongerSide, PAWN) == 0);
@ -383,18 +349,18 @@ Value Endgame<Value, KQKR>::apply(const Position& pos) const {
}
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.non_pawn_material(strongerSide) == 2*BishopValueMidgame);
assert(pos.piece_count(weakerSide, KNIGHT) == 1);
assert(pos.non_pawn_material(weakerSide) == KnightValueMidgame);
assert(pos.pieces(PAWN) == EmptyBoardBB);
assert(!pos.pieces(PAWN));
Value result = BishopValueEndgame;
Square wksq = pos.king_square(strongerSide);
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
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);
// 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;
}
@ -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
/// king alone are always draw.
template<>
Value Endgame<Value, KmmKm>::apply(const Position&) const {
Value Endgame<KmmKm>::operator()(const Position&) const {
return VALUE_DRAW;
}
template<>
Value Endgame<Value, KNNK>::apply(const Position&) const {
Value Endgame<KNNK>::operator()(const Position&) const {
return VALUE_DRAW;
}
/// KBPKScalingFunction scales endgames where the stronger side has king,
/// bishop and one or more pawns. It checks for draws with rook pawns and a
/// bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_ZERO is
/// returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// K, bishop and one or more pawns vs K. It checks for draws with rook pawns and
/// a bishop of the wrong color. If such a draw is detected, SCALE_FACTOR_DRAW
/// is returned. If not, the return value is SCALE_FACTOR_NONE, i.e. no scaling
/// will be used.
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.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.
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 ?
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 kingSq = pos.king_square(weakerSide);
if ( opposite_color_squares(queeningSq, bishopSq)
&& abs(square_file(kingSq) - pawnFile) <= 1)
if ( opposite_colors(queeningSq, bishopSq)
&& abs(file_of(kingSq) - pawnFile) <= 1)
{
// 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
@ -456,12 +421,12 @@ ScaleFactor Endgame<ScaleFactor, KBPsK>::apply(const Position& pos) const {
Rank rank;
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);
}
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
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.
if ( square_distance(kingSq, queeningSq) <= 1
|| relative_rank(strongerSide, kingSq) >= rank)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
}
}
return SCALE_FACTOR_NONE;
}
/// KQKRPScalingFunction scales endgames where the stronger side has only
/// king and queen, while the weaker side has at least a rook and a pawn.
/// It tests for fortress draws with a rook on the third rank defended by
/// a pawn.
/// K and queen vs K, rook and one or more pawns. It tests for fortress draws with
/// a rook on the third rank defended by a pawn.
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.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.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))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
}
/// KRPKRScalingFunction scales KRP vs KR endgames. This function knows a
/// handful of the most important classes of drawn positions, but is far
/// from perfect. It would probably be a good idea to add more knowledge
/// in the future.
/// K, rook and one pawn vs K and a rook. This function knows a handful of the
/// most important classes of drawn positions, but is far from perfect. It would
/// probably be a good idea to add more knowledge in the future.
///
/// 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.
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.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);
Square wksq = pos.king_square(strongerSide);
Square wrsq = pos.piece_list(strongerSide, ROOK, 0);
Square wpsq = pos.piece_list(strongerSide, PAWN, 0);
Square wrsq = pos.piece_list(strongerSide, ROOK)[0];
Square wpsq = pos.piece_list(strongerSide, PAWN)[0];
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
// pawn is on the left half of the board.
if (strongerSide == BLACK)
{
wksq = flip_square(wksq);
wrsq = flip_square(wrsq);
wpsq = flip_square(wpsq);
bksq = flip_square(bksq);
brsq = flip_square(brsq);
wksq = flip(wksq);
wrsq = flip(wrsq);
wpsq = flip(wpsq);
bksq = flip(bksq);
brsq = flip(brsq);
}
if (square_file(wpsq) > FILE_D)
if (file_of(wpsq) > FILE_D)
{
wksq = flop_square(wksq);
wrsq = flop_square(wrsq);
wpsq = flop_square(wpsq);
bksq = flop_square(bksq);
brsq = flop_square(brsq);
wksq = mirror(wksq);
wrsq = mirror(wrsq);
wpsq = mirror(wpsq);
bksq = mirror(bksq);
brsq = mirror(brsq);
}
File f = square_file(wpsq);
Rank r = square_rank(wpsq);
File f = file_of(wpsq);
Rank r = rank_of(wpsq);
Square queeningSq = make_square(f, RANK_8);
int tempo = (pos.side_to_move() == strongerSide);
@ -554,31 +516,31 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
if ( r <= RANK_5
&& square_distance(bksq, queeningSq) <= 1
&& wksq <= SQ_H5
&& (square_rank(brsq) == RANK_6 || (r <= RANK_3 && square_rank(wrsq) != RANK_6)))
return SCALE_FACTOR_ZERO;
&& (rank_of(brsq) == RANK_6 || (r <= RANK_3 && rank_of(wrsq) != RANK_6)))
return SCALE_FACTOR_DRAW;
// The defending side saves a draw by checking from behind in case the pawn
// has advanced to the 6th rank with the king behind.
if ( r == RANK_6
&& square_distance(bksq, queeningSq) <= 1
&& square_rank(wksq) + tempo <= RANK_6
&& (square_rank(brsq) == RANK_1 || (!tempo && abs(square_file(brsq) - f) >= 3)))
return SCALE_FACTOR_ZERO;
&& rank_of(wksq) + tempo <= RANK_6
&& (rank_of(brsq) == RANK_1 || (!tempo && abs(file_of(brsq) - f) >= 3)))
return SCALE_FACTOR_DRAW;
if ( r >= RANK_6
&& bksq == queeningSq
&& square_rank(brsq) == RANK_1
&& rank_of(brsq) == RANK_1
&& (!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
// and the black rook is behind the pawn.
if ( wpsq == SQ_A7
&& wrsq == SQ_A8
&& (bksq == SQ_H7 || bksq == SQ_G7)
&& square_file(brsq) == FILE_A
&& (square_rank(brsq) <= RANK_3 || square_file(wksq) >= FILE_D || square_rank(wksq) <= RANK_5))
return SCALE_FACTOR_ZERO;
&& file_of(brsq) == FILE_A
&& (rank_of(brsq) <= RANK_3 || file_of(wksq) >= FILE_D || rank_of(wksq) <= RANK_5))
return SCALE_FACTOR_DRAW;
// If the defending king blocks the pawn and the attacking king is too far
// away, it's a draw.
@ -586,14 +548,14 @@ ScaleFactor Endgame<ScaleFactor, KRPKR>::apply(const Position& pos) const {
&& bksq == wpsq + DELTA_N
&& square_distance(wksq, wpsq) - 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
// attacking king is closer to the queening square than the defending king,
// and the defending king cannot gain tempi by threatening the attacking rook.
if ( r == RANK_7
&& f != FILE_A
&& square_file(wrsq) == f
&& file_of(wrsq) == f
&& wrsq != queeningSq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 2 + 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
if ( f != FILE_A
&& square_file(wrsq) == f
&& file_of(wrsq) == f
&& wrsq < wpsq
&& (square_distance(wksq, queeningSq) < square_distance(bksq, queeningSq) - 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.
if (r <= RANK_4 && bksq > wpsq)
{
if (square_file(bksq) == square_file(wpsq))
if (file_of(bksq) == file_of(wpsq))
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)
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
/// single pattern: If the stronger side has no pawns and the defending king
/// K, rook and two pawns vs K, rook and one pawn. There is only a single
/// pattern: If the stronger side has no passed pawns and the defending king
/// is actively placed, the position is drawish.
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.piece_count(strongerSide, PAWN) == 2);
assert(pos.non_pawn_material(weakerSide) == RookValueMidgame);
assert(pos.piece_count(weakerSide, PAWN) == 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN, 0);
Square wpsq2 = pos.piece_list(strongerSide, PAWN, 1);
Square wpsq1 = pos.piece_list(strongerSide, PAWN)[0];
Square wpsq2 = pos.piece_list(strongerSide, PAWN)[1];
Square bksq = pos.king_square(weakerSide);
// 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))
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
&& 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
/// against king. There is just a single rule here: If all pawns are on
/// the same rook file and are blocked by the defending king, it's a draw.
/// K and two or more pawns vs K. There is just a single rule here: If all pawns
/// are on the same rook file and are blocked by the defending king, it's a draw.
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.piece_count(strongerSide, PAWN) >= 2);
@ -680,34 +641,33 @@ ScaleFactor Endgame<ScaleFactor, KPsK>::apply(const Position& pos) const {
Bitboard pawns = pos.pieces(PAWN, strongerSide);
// Are all pawns on the 'a' file?
if ((pawns & ~FileABB) == EmptyBoardBB)
if (!(pawns & ~FileABB))
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_A8)) <= 1
|| ( square_file(ksq) == FILE_A
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
return SCALE_FACTOR_ZERO;
|| ( file_of(ksq) == FILE_A
&& !in_front_bb(strongerSide, ksq) & pawns))
return SCALE_FACTOR_DRAW;
}
// Are all pawns on the 'h' file?
else if ((pawns & ~FileHBB) == EmptyBoardBB)
else if (!(pawns & ~FileHBB))
{
// Does the defending king block the pawns?
if ( square_distance(ksq, relative_square(strongerSide, SQ_H8)) <= 1
|| ( square_file(ksq) == FILE_H
&& (in_front_bb(strongerSide, ksq) & pawns) == EmptyBoardBB))
return SCALE_FACTOR_ZERO;
|| ( file_of(ksq) == FILE_H
&& !in_front_bb(strongerSide, ksq) & pawns))
return SCALE_FACTOR_DRAW;
}
return SCALE_FACTOR_NONE;
}
/// KBPKBScalingFunction scales KBP vs KB endgames. There are two rules:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw. If the two bishops have opposite color, it's almost always
/// a draw.
/// K, bishop and a pawn vs K and a bishop. There are two rules: If the defending
/// king is somewhere along the path of the pawn, and the square of the king is
/// not of the same color as the stronger side's bishop, it's a draw. If the two
/// bishops have opposite color, it's almost always a draw.
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.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, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP, 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
Square weakerBishopSq = pos.piece_list(weakerSide, BISHOP)[0];
Square weakerKingSq = pos.king_square(weakerSide);
// 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)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
&& ( opposite_colors(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
// 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:
//
@ -742,27 +702,27 @@ ScaleFactor Endgame<ScaleFactor, KBPKB>::apply(const Position& pos) const {
// reasonably well.
if (relative_rank(strongerSide, pawnSq) <= RANK_5)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
else
{
Bitboard path = squares_in_front_of(strongerSide, pawnSq);
if (path & pos.pieces(KING, weakerSide))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
if ( (pos.attacks_from<BISHOP>(weakerBishopSq) & path)
&& square_distance(weakerBishopSq, pawnSq) >= 3)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
}
}
return SCALE_FACTOR_NONE;
}
/// KBPPKBScalingFunction scales KBPP vs KB endgames. It detects a few basic
/// draws with opposite-colored bishops.
/// K, bishop and two pawns vs K and bishop. It detects a few basic draws with
/// opposite-colored bishops.
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.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, PAWN) == 0);
Square wbsq = pos.piece_list(strongerSide, BISHOP, 0);
Square bbsq = pos.piece_list(weakerSide, BISHOP, 0);
Square wbsq = pos.piece_list(strongerSide, 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;
Square ksq = pos.king_square(weakerSide);
Square psq1 = pos.piece_list(strongerSide, PAWN, 0);
Square psq2 = pos.piece_list(strongerSide, PAWN, 1);
Rank r1 = square_rank(psq1);
Rank r2 = square_rank(psq2);
Square psq1 = pos.piece_list(strongerSide, PAWN)[0];
Square psq2 = pos.piece_list(strongerSide, PAWN)[1];
Rank r1 = rank_of(psq1);
Rank r2 = rank_of(psq2);
Square blockSq1, blockSq2;
if (relative_rank(strongerSide, psq1) > relative_rank(strongerSide, psq2))
{
blockSq1 = psq1 + pawn_push(strongerSide);
blockSq2 = make_square(square_file(psq2), square_rank(psq1));
blockSq2 = make_square(file_of(psq2), rank_of(psq1));
}
else
{
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))
@ -800,10 +760,10 @@ ScaleFactor Endgame<ScaleFactor, KBPPKB>::apply(const Position& pos) const {
case 0:
// Both pawns are on the same file. Easy draw if defender firmly controls
// 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)
&& opposite_color_squares(ksq, wbsq))
return SCALE_FACTOR_ZERO;
&& opposite_colors(ksq, wbsq))
return SCALE_FACTOR_DRAW;
else
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
// this square on the file of the other pawn.
if ( ksq == blockSq1
&& opposite_color_squares(ksq, wbsq)
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq2
|| (pos.attacks_from<BISHOP>(blockSq2) & pos.pieces(BISHOP, weakerSide))
|| abs(r1 - r2) >= 2))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
else if ( ksq == blockSq2
&& opposite_color_squares(ksq, wbsq)
&& opposite_colors(ksq, wbsq)
&& ( bbsq == blockSq1
|| (pos.attacks_from<BISHOP>(blockSq1) & pos.pieces(BISHOP, weakerSide))))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
else
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:
/// If the defending king is somewhere along the path of the pawn, and the
/// square of the king is not of the same color as the stronger side's bishop,
/// it's a draw.
/// K, bisop and a pawn vs K and knight. There is a single rule: If the defending
/// king is somewhere along the path of the pawn, and the square of the king is
/// not of the same color as the stronger side's bishop, it's a draw.
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.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, PAWN) == 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN, 0);
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP, 0);
Square pawnSq = pos.piece_list(strongerSide, PAWN)[0];
Square strongerBishopSq = pos.piece_list(strongerSide, BISHOP)[0];
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)
&& ( opposite_color_squares(weakerKingSq, strongerBishopSq)
&& ( opposite_colors(weakerKingSq, strongerBishopSq)
|| relative_rank(strongerSide, weakerKingSq) <= RANK_6))
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KNPKScalingFunction scales KNP vs K endgames. There is a single rule:
/// If the pawn is a rook pawn on the 7th rank and the defending king prevents
/// the pawn from advancing, the position is drawn.
/// K, knight and a pawn vs K. There is a single rule: If the pawn is a rook pawn
/// on the 7th rank and the defending king prevents the pawn from advancing, the
/// position is drawn.
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.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.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);
if ( pawnSq == relative_square(strongerSide, SQ_A7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_A8)) <= 1)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
if ( pawnSq == relative_square(strongerSide, SQ_H7)
&& square_distance(weakerKingSq, relative_square(strongerSide, SQ_H8)) <= 1)
return SCALE_FACTOR_ZERO;
return SCALE_FACTOR_DRAW;
return SCALE_FACTOR_NONE;
}
/// KPKPScalingFunction scales KP vs KP endgames. This is done by removing
/// the weakest side's pawn and probing the KP vs K bitbase: If the weakest
/// side has a draw without the pawn, she probably has at least a draw with
/// the pawn as well. The exception is when the stronger side's pawn is far
/// advanced and not on a rook file; in this case it is often possible to win
/// (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
/// K and a pawn vs K and a pawn. This is done by removing the weakest side's
/// pawn and probing the KP vs K bitbase: If the weakest side has a draw without
/// the pawn, she probably has at least a draw with the pawn as well. The exception
/// is when the stronger side's pawn is far advanced and not on a rook file; in
/// this case it is often possible to win (e.g. 8/4k3/3p4/3P4/6K1/8/8/8 w - - 0 1).
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(weakerSide) == VALUE_ZERO);
assert(pos.piece_count(WHITE, PAWN) == 1);
assert(pos.piece_count(BLACK, PAWN) == 1);
Square wksq, bksq, wpsq;
Color stm;
Square wksq = pos.king_square(strongerSide);
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);
bksq = pos.king_square(BLACK);
wpsq = pos.piece_list(WHITE, PAWN, 0);
stm = pos.side_to_move();
}
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());
wksq = flip(wksq);
bksq = flip(bksq);
wpsq = flip(wpsq);
stm = flip(stm);
}
if (square_file(wpsq) >= FILE_E)
if (file_of(wpsq) >= FILE_E)
{
wksq = flop_square(wksq);
bksq = flop_square(bksq);
wpsq = flop_square(wpsq);
wksq = mirror(wksq);
bksq = mirror(bksq);
wpsq = mirror(wpsq);
}
// 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.
if ( square_rank(wpsq) >= RANK_5
&& square_file(wpsq) != FILE_A)
if ( rank_of(wpsq) >= RANK_5
&& file_of(wpsq) != FILE_A)
return SCALE_FACTOR_NONE;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a
// draw, 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;
// Probe the KPK bitbase with the weakest side's pawn removed. If it's a draw,
// it's probably at least a draw even with the pawn.
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
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
it under the terms of the GNU General Public License as published by
@ -20,8 +20,8 @@
#if !defined(ENDGAME_H_INCLUDED)
#define ENDGAME_H_INCLUDED
#include <string>
#include <map>
#include <string>
#include "position.h"
#include "types.h"
@ -46,6 +46,7 @@ enum EndgameType {
// Scaling functions
SCALE_FUNS,
KBPsK, // KB+pawns vs K
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
template<typename T>
struct EndgameBase {
typedef EndgameBase<T> Base;
virtual ~EndgameBase() {}
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> {
explicit Endgame(Color c) : strongerSide(c), weakerSide(opposite_color(c)) {}
explicit Endgame(Color c) : strongerSide(c), weakerSide(flip(c)) {}
Color color() const { return strongerSide; }
T apply(const Position&) const;
T operator()(const Position&) const;
private:
Color strongerSide, weakerSide;
@ -87,26 +93,28 @@ private:
/// Endgames class stores in two std::map the pointers to endgame evaluation
/// 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 {
typedef std::map<Key, EndgameBase<Value>* > EFMap;
typedef std::map<Key, EndgameBase<ScaleFactor>* > SFMap;
typedef std::map<Key, EndgameBase<Value>*> M1;
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:
Endgames();
~Endgames();
template<class T> T* get(Key key) const;
private:
template<class T> void add(const std::string& keyCode);
// 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;
template<typename T> EndgameBase<T>* get(Key key) {
return map((T*)0).count(key) ? map((T*)0)[key] : NULL;
}
};
#endif // !defined(ENDGAME_H_INCLUDED)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -21,6 +21,7 @@
#include <iostream>
#include <iomanip>
#include <sstream>
#include <algorithm>
#include "bitcount.h"
#include "evaluate.h"
@ -44,20 +45,20 @@ namespace {
// all squares attacked by the given color.
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
// 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
// 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.
Bitboard kingZone[2];
Bitboard kingRing[2];
// 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];
// 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
// QueenAttackWeight, RookAttackWeight, BishopAttackWeight and
// KnightAttackWeight in evaluate.cpp
@ -88,7 +89,7 @@ namespace {
//
// Values modified by Joona Kiiski
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
@ -224,28 +225,27 @@ namespace {
};
// Function prototypes
template<bool HasPopCnt, bool Trace>
template<bool Trace>
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);
template<Color Us, bool HasPopCnt, bool Trace>
template<Color Us, bool Trace>
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[]);
template<Color Us>
Score evaluate_threats(const Position& pos, EvalInfo& ei);
template<Color Us, bool HasPopCnt>
template<Color Us>
int evaluate_space(const Position& pos, EvalInfo& ei);
template<Color Us>
Score evaluate_passed_pawns(const Position& pos, EvalInfo& ei);
template<bool HasPopCnt>
Score evaluate_unstoppable_pawns(const Position& pos, EvalInfo& ei);
inline Score apply_weight(Score v, Score weight);
@ -260,22 +260,17 @@ namespace {
/// evaluate() is the main evaluation function. It always computes two
/// values, an endgame score and a middle game score, and interpolates
/// between them based on the remaining material.
Value evaluate(const Position& pos, Value& margin) {
return CpuHasPOPCNT ? do_evaluate<true, false>(pos, margin)
: do_evaluate<false, false>(pos, margin);
}
Value evaluate(const Position& pos, Value& margin) { return do_evaluate<false>(pos, margin); }
namespace {
template<bool HasPopCnt, bool Trace>
template<bool Trace>
Value do_evaluate(const Position& pos, Value& margin) {
EvalInfo ei;
Value margins[2];
Score score, mobilityWhite, mobilityBlack;
assert(pos.is_ok());
assert(pos.thread() >= 0 && pos.thread() < MAX_THREADS);
assert(!pos.in_check());
@ -288,7 +283,7 @@ Value do_evaluate(const Position& pos, Value& margin) {
margins[WHITE] = margins[BLACK] = VALUE_ZERO;
// 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();
// 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
ei.pi = Threads[pos.thread()].pawnTable.get_pawn_info(pos);
ei.pi = Threads[pos.thread()].pawnTable.pawn_info(pos);
score += ei.pi->pawns_value();
// Initialize attack and king safety bitboards
init_eval_info<WHITE, HasPopCnt>(pos, ei);
init_eval_info<BLACK, HasPopCnt>(pos, ei);
init_eval_info<WHITE>(pos, ei);
init_eval_info<BLACK>(pos, ei);
// Evaluate pieces and mobility
score += evaluate_pieces_of_color<WHITE, HasPopCnt, Trace>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, HasPopCnt, Trace>(pos, ei, mobilityBlack);
score += evaluate_pieces_of_color<WHITE, Trace>(pos, ei, mobilityWhite)
- evaluate_pieces_of_color<BLACK, Trace>(pos, ei, mobilityBlack);
score += apply_weight(mobilityWhite - mobilityBlack, Weights[Mobility]);
// Evaluate kings after all other pieces because we need complete attack
// information when computing the king safety evaluation.
score += evaluate_king<WHITE, HasPopCnt, Trace>(pos, ei, margins)
- evaluate_king<BLACK, HasPopCnt, Trace>(pos, ei, margins);
score += evaluate_king<WHITE, Trace>(pos, ei, margins)
- evaluate_king<BLACK, Trace>(pos, ei, margins);
// Evaluate tactical threats, we need full attack information including king
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 (!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.
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]);
}
@ -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(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(UNSTOPPABLE, evaluate_unstoppable_pawns<false>(pos, ei));
Score w = make_score(ei.mi->space_weight() * evaluate_space<WHITE, false>(pos, ei), 0);
Score b = make_score(ei.mi->space_weight() * evaluate_space<BLACK, false>(pos, ei), 0);
trace_add(UNSTOPPABLE, evaluate_unstoppable_pawns(pos, ei));
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>(pos, ei), 0);
trace_add(SPACE, apply_weight(w, Weights[Space]), apply_weight(b, Weights[Space]));
trace_add(TOTAL, score);
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
// 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;
init_safety();
@ -424,10 +419,9 @@ namespace {
// init_eval_info() initializes king bitboards for given color adding
// 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) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
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)
&& 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];
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;
} 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.
if (bonus && bit_is_set(ei.attackedBy[Us][PAWN], s))
{
if ( pos.pieces(KNIGHT, Them) == EmptyBoardBB
&& (SquaresByColorBB[square_color(s)] & pos.pieces(BISHOP, Them)) == EmptyBoardBB)
if ( !pos.pieces(KNIGHT, Them)
&& !(same_color_squares(s) & pos.pieces(BISHOP, Them)))
bonus += bonus + bonus / 2;
else
bonus += bonus / 2;
@ -474,7 +468,7 @@ namespace {
// 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) {
Bitboard b;
@ -483,14 +477,12 @@ namespace {
File f;
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 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
if (Piece == KNIGHT || Piece == QUEEN)
@ -506,18 +498,18 @@ namespace {
ei.attackedBy[Us][Piece] |= b;
// King attacks
if (b & ei.kingZone[Us])
if (b & ei.kingRing[Them])
{
ei.kingAttackersCount[Us]++;
ei.kingAttackersWeight[Us] += KingAttackWeights[Piece];
Bitboard bb = (b & ei.attackedBy[Them][KING]);
if (bb)
ei.kingAdjacentZoneAttacksCount[Us] += count_1s<Max15>(bb);
ei.kingAdjacentZoneAttacksCount[Us] += popcount<Max15>(bb);
}
// Mobility
mob = (Piece != QUEEN ? count_1s<Max15>(b & mobilityArea)
: count_1s<Full >(b & mobilityArea));
mob = (Piece != QUEEN ? popcount<Max15>(b & mobilityArea)
: popcount<Full >(b & mobilityArea));
mobility += MobilityBonus[Piece][mob];
@ -527,7 +519,8 @@ namespace {
score -= ThreatenedByPawnPenalty[Piece];
// 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);
// Queen or rook on 7th rank
@ -546,7 +539,7 @@ namespace {
// problem, especially when that pawn is also blocked.
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.square_is_empty(s + d + pawn_push(Us)))
@ -563,7 +556,7 @@ namespace {
if (Piece == ROOK)
{
// 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(Them, f))
@ -579,21 +572,21 @@ namespace {
ksq = pos.king_square(Us);
if ( square_file(ksq) >= FILE_E
&& square_file(s) > square_file(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s)))
if ( file_of(ksq) >= FILE_E
&& file_of(s) > file_of(ksq)
&& (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?
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
: (TrappedRookPenalty - mob * 16), 0);
}
else if ( square_file(ksq) <= FILE_D
&& square_file(s) < square_file(ksq)
&& (relative_rank(Us, ksq) == RANK_1 || square_rank(ksq) == square_rank(s)))
else if ( file_of(ksq) <= FILE_D
&& file_of(s) < file_of(ksq)
&& (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?
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
: (TrappedRookPenalty - mob * 16), 0);
}
@ -619,7 +612,7 @@ namespace {
Score score = SCORE_ZERO;
// 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[Us][0];
if (!weakEnemies)
@ -643,7 +636,7 @@ namespace {
// evaluate_pieces_of_color<>() assigns bonuses and penalties to all the
// 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) {
const Color Them = (Us == WHITE ? BLACK : WHITE);
@ -651,12 +644,12 @@ namespace {
Score score = mobility = SCORE_ZERO;
// 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<BISHOP, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<ROOK, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<QUEEN, Us, HasPopCnt, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<KNIGHT, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<BISHOP, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<ROOK, Us, Trace>(pos, ei, mobility, mobilityArea);
score += evaluate_pieces<QUEEN, Us, Trace>(pos, ei, mobility, mobilityArea);
// Sum up all attacked squares
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
template<Color Us, bool HasPopCnt, bool Trace>
template<Color Us, bool Trace>
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);
Bitboard undefended, b, b1, b2, safe;
@ -698,14 +690,14 @@ namespace {
// the number and types of the enemy's attacking pieces, the number of
// attacked and undefended squares around our king, the square of the
// king, and the quality of the pawn shelter.
attackUnits = Min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + count_1s<Max15>(undefended))
attackUnits = std::min(25, (ei.kingAttackersCount[Them] * ei.kingAttackersWeight[Them]) / 2)
+ 3 * (ei.kingAdjacentZoneAttacksCount[Them] + popcount<Max15>(undefended))
+ InitKingDanger[relative_square(Us, ksq)]
- mg_value(ei.pi->king_shelter<Us>(pos, ksq)) / 32;
// Analyse enemy's safe queen contact checks. First find undefended
// 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)
{
// ...then remove squares not supported by another enemy piece
@ -713,13 +705,13 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][ROOK]);
if (b)
attackUnits += QueenContactCheckBonus
* count_1s<Max15>(b)
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
}
// Analyse enemy's safe rook contact checks. First find undefended
// 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
b &= RookPseudoAttacks[ksq];
@ -731,12 +723,12 @@ namespace {
| ei.attackedBy[Them][BISHOP] | ei.attackedBy[Them][QUEEN]);
if (b)
attackUnits += RookContactCheckBonus
* count_1s<Max15>(b)
* popcount<Max15>(b)
* (Them == pos.side_to_move() ? 2 : 1);
}
// 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;
b2 = pos.attacks_from<BISHOP>(ksq) & safe;
@ -744,25 +736,25 @@ namespace {
// Enemy queen safe checks
b = (b1 | b2) & ei.attackedBy[Them][QUEEN];
if (b)
attackUnits += QueenCheckBonus * count_1s<Max15>(b);
attackUnits += QueenCheckBonus * popcount<Max15>(b);
// Enemy rooks safe checks
b = b1 & ei.attackedBy[Them][ROOK];
if (b)
attackUnits += RookCheckBonus * count_1s<Max15>(b);
attackUnits += RookCheckBonus * popcount<Max15>(b);
// Enemy bishops safe checks
b = b2 & ei.attackedBy[Them][BISHOP];
if (b)
attackUnits += BishopCheckBonus * count_1s<Max15>(b);
attackUnits += BishopCheckBonus * popcount<Max15>(b);
// Enemy knights safe checks
b = pos.attacks_from<KNIGHT>(ksq) & ei.attackedBy[Them][KNIGHT] & safe;
if (b)
attackUnits += KnightCheckBonus * count_1s<Max15>(b);
attackUnits += KnightCheckBonus * popcount<Max15>(b);
// 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[]
// array and subtract the score from evaluation. Set also margins[]
@ -812,11 +804,11 @@ namespace {
Square blockSq = s + pawn_push(Us);
// Adjust bonus based on kings proximity
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 6 * rr);
ebonus -= Value(square_distance(pos.king_square(Us), blockSq) * 3 * rr);
ebonus += Value(square_distance(pos.king_square(Them), blockSq) * 5 * 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 (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);
// 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)))
unsafeSquares = squaresToQueen;
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
// 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 no, somewhat smaller bonus.
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
// 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.
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))
ebonus += Value(r * 20);
else if (supportingPawns & rank_bb(s - pawn_push(Us)))
ebonus += Value(r * 12);
@ -866,7 +854,7 @@ namespace {
// we try the following: Increase the value for rook pawns if the
// other side has no pieces apart from a knight, and decrease the
// 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)
ebonus += ebonus / 4;
@ -885,11 +873,8 @@ namespace {
// 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.
template<bool HasPopCnt>
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;
Square s, blockSq, queeningSquare;
Color c, winnerSide, loserSide;
@ -902,7 +887,7 @@ namespace {
for (c = WHITE; c <= BLACK; c++)
{
// Skip if other side has non-pawn pieces
if (pos.non_pawn_material(opposite_color(c)))
if (pos.non_pawn_material(flip(c)))
continue;
b = ei.pi->passed_pawns(c);
@ -910,12 +895,12 @@ namespace {
while (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);
// Compute plies to queening and check direct advancement
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);
if (movesToGo >= oppMovesToGo && !pathDefended)
@ -924,16 +909,16 @@ namespace {
// Opponent king cannot block because path is defended and position
// is not in check. So only friendly pieces can be blockers.
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
movesToGo += count_1s<Max15>(queeningPath & pos.pieces_of_color(c));
movesToGo += popcount<Max15>(queeningPath & pos.pieces(c));
if (movesToGo >= oppMovesToGo && !pathDefended)
continue;
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;
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?
b = candidates = pos.pieces(PAWN, loserSide);
@ -953,7 +938,7 @@ namespace {
s = pop_1st_bit(&b);
// 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);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
@ -977,12 +962,12 @@ namespace {
minKingDist = kingptg = 256;
// 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);
pliesToGo = 2 * movesToGo - int(loserSide == pos.side_to_move());
// 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);
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)
{
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)
{
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,
@ -1027,7 +1012,7 @@ namespace {
// Plies needed for the king to capture all the blocking pawns
d = square_distance(pos.king_square(loserSide), blockSq);
minKingDist = Min(minKingDist, d);
minKingDist = std::min(minKingDist, d);
kingptg = (minKingDist + blockersCount) * 2;
}
@ -1052,10 +1037,9 @@ namespace {
// squares one, two or three squares behind a friendly pawn are counted
// twice. Finally, the space bonus is scaled by a weight taken from the
// material hash table. 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) {
const BitCountType Max15 = HasPopCnt ? CNT_POPCNT : CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
// 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 >> 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) {
// Scale option value from 100 to 256
int mg = Options[mgOpt].value<int>() * 256 / 100;
int eg = Options[egOpt].value<int>() * 256 / 100;
int mg = Options[mgOpt] * 256 / 100;
int eg = Options[egOpt] * 256 / 100;
return apply_weight(make_score(mg, eg), internalWeight);
}
@ -1126,9 +1110,9 @@ namespace {
t[i] = Value(int(0.4 * i * i));
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
@ -1190,7 +1174,7 @@ std::string trace_evaluate(const Position& pos) {
TraceStream << std::showpoint << std::showpos << std::fixed << std::setprecision(2);
memset(TracedScores, 0, 2 * 16 * sizeof(Score));
do_evaluate<false, true>(pos, margin);
do_evaluate<true>(pos, margin);
totals = TraceStream.str();
TraceStream.str("");

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -20,8 +20,9 @@
#if !defined(HISTORY_H_INCLUDED)
#define HISTORY_H_INCLUDED
#include <cstring>
#include "types.h"
#include <cstring>
#include <algorithm>
/// The History class stores statistics about how often different moves
/// have been successful or unsuccessful during the current search. These
@ -35,7 +36,7 @@ class History {
public:
void clear();
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;
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];
}
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;
}
@ -64,7 +65,7 @@ inline Value History::gain(Piece p, Square to) const {
}
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)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -35,12 +35,15 @@ typedef pthread_cond_t WaitCondition;
# define cond_init(x) pthread_cond_init(x, NULL)
# define cond_signal(x) pthread_cond_signal(x)
# define cond_wait(x,y) pthread_cond_wait(x,y)
# define cond_timedwait(x,y,z) pthread_cond_timedwait(x,y,z)
#else
#define NOMINMAX // disable macros min() and max()
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef WIN32_LEAN_AND_MEAN
#undef NOMINMAX
// Default fast and race free locks and condition variables
#if !defined(OLD_LOCKS)
@ -55,7 +58,8 @@ typedef CONDITION_VARIABLE WaitCondition;
# define cond_destroy(x) (x)
# define cond_init(x) InitializeConditionVariable(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
// 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_destroy(x) CloseHandle(*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

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -17,71 +17,41 @@
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 <string>
#include "bitboard.h"
#include "evaluate.h"
#include "misc.h"
#include "position.h"
#include "thread.h"
#include "search.h"
#include "ucioption.h"
#ifdef USE_CALLGRIND
#include <valgrind/callgrind.h>
#endif
#include "thread.h"
using namespace std;
extern bool execute_uci_command(const string& cmd);
extern void uci_loop();
extern void benchmark(int argc, char* argv[]);
extern void init_kpk_bitbase();
extern void kpk_bitbase_init();
int main(int argc, char* argv[]) {
// Disable IO buffering for C and C++ standard libraries
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
cout.rdbuf()->pubsetbuf(NULL, 0);
cin.rdbuf()->pubsetbuf(NULL, 0);
// Startup initializations
init_bitboards();
Position::init_zobrist();
Position::init_piece_square_tables();
init_kpk_bitbase();
init_search();
bitboards_init();
Position::init();
kpk_bitbase_init();
Search::init();
Threads.init();
#ifdef USE_CALLGRIND
CALLGRIND_START_INSTRUMENTATION;
#endif
cout << engine_info() << endl;
if (argc < 2)
{
// Print copyright notice
cout << engine_name() << " by " << engine_authors() << endl;
if (argc == 1)
uci_loop();
if (CpuHasPOPCNT)
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)
else if (string(argv[1]) == "bench")
benchmark(argc, argv);
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] "
<< "[limited by depth, time, nodes or perft = depth]" << endl;
Threads.exit();
return 0;
}

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -19,6 +19,7 @@
#include <cassert>
#include <cstring>
#include <algorithm>
#include "material.h"
@ -49,13 +50,13 @@ namespace {
// Endgame evaluation and scaling functions accessed direcly and not through
// 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<Value, KXK> EvaluateKXK[] = { Endgame<Value, KXK>(WHITE), Endgame<Value, KXK>(BLACK) };
Endgame<KmmKm> EvaluateKmmKm[] = { Endgame<KmmKm>(WHITE), Endgame<KmmKm>(BLACK) };
Endgame<KXK> EvaluateKXK[] = { Endgame<KXK>(WHITE), Endgame<KXK>(BLACK) };
Endgame<ScaleFactor, KBPsK> ScaleKBPsK[] = { Endgame<ScaleFactor, KBPsK>(WHITE), Endgame<ScaleFactor, KBPsK>(BLACK) };
Endgame<ScaleFactor, KQKRPs> ScaleKQKRPs[] = { Endgame<ScaleFactor, KQKRPs>(WHITE), Endgame<ScaleFactor, KQKRPs>(BLACK) };
Endgame<ScaleFactor, KPsK> ScaleKPsK[] = { Endgame<ScaleFactor, KPsK>(WHITE), Endgame<ScaleFactor, KPsK>(BLACK) };
Endgame<ScaleFactor, KPKP> ScaleKPKP[] = { Endgame<ScaleFactor, KPKP>(WHITE), Endgame<ScaleFactor, KPKP>(BLACK) };
Endgame<KBPsK> ScaleKBPsK[] = { Endgame<KBPsK>(WHITE), Endgame<KBPsK>(BLACK) };
Endgame<KQKRPs> ScaleKQKRPs[] = { Endgame<KQKRPs>(WHITE), Endgame<KQKRPs>(BLACK) };
Endgame<KPsK> ScaleKPsK[] = { Endgame<KPsK>(WHITE), Endgame<KPsK>(BLACK) };
Endgame<KPKP> ScaleKPKP[] = { Endgame<KPKP>(WHITE), Endgame<KPKP>(BLACK) };
// Helper templates used to detect a given material distribution
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::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.
/// 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
/// 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);
// 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
// particular material configuration. First we look for a fixed
// 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;
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
&& pos.piece_count(BLACK, BISHOP) + pos.piece_count(BLACK, KNIGHT) <= 2)
{
mi->evaluationFunction = &EvaluateKmmKm[WHITE];
mi->evaluationFunction = &EvaluateKmmKm[pos.side_to_move()];
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.
EndgameBase<ScaleFactor>* sf;
if ((sf = funcs->get<EndgameBase<ScaleFactor> >(key)) != NULL)
if ((sf = funcs->get<ScaleFactor>(key)) != NULL)
{
mi->scalingFunction[sf->color()] = sf;
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)
{
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)
{
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
@ -253,7 +254,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
+ RedundantQueenPenalty * pieceCount[Us][QUEEN];
// 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];
if (!pc)
@ -261,7 +262,7 @@ int MaterialInfoTable::imbalance(const int pieceCount[][8]) {
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]
+ QuadraticCoefficientsOppositeColor[pt1][pt2] * pieceCount[Them][pt2];

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -27,6 +27,13 @@
const int MaterialTableSize = 8192;
/// Game phase
enum Phase {
PHASE_ENDGAME = 0,
PHASE_MIDGAME = 128
};
/// MaterialInfo is a class which contains various information about a
/// material configuration. It contains a material balance evaluation,
/// 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
/// 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> {
public:
~MaterialInfoTable();
void init();
MaterialInfo* get_material_info(const Position& pos) const;
MaterialInfo* material_info(const Position& pos) const;
static Phase game_phase(const Position& pos);
private:
@ -90,12 +97,12 @@ inline ScaleFactor MaterialInfo::scale_factor(const Position& pos, Color c) cons
if (!scalingFunction[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;
}
inline Value MaterialInfo::evaluate(const Position& pos) const {
return evaluationFunction->apply(pos);
return (*evaluationFunction)(pos);
}
inline Score MaterialInfo::material_value() const {

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -17,7 +17,14 @@
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/types.h>
@ -26,18 +33,13 @@
# include <sys/pstat.h>
# endif
#else
#define _CRT_SECURE_NO_DEPRECATE
#include <windows.h>
#include <sys/timeb.h>
#endif
#if !defined(NO_PREFETCH)
# include <xmmintrin.h>
#endif
#include <algorithm>
#include <cassert>
#include <cstdio>
#include <iomanip>
@ -50,91 +52,69 @@
using namespace std;
/// Version number. If EngineVersion is left empty, then AppTag plus
/// current date (in the format YYMMDD) is used as a version number.
/// Version number. If Version is left empty, then Tag plus current
/// date (in the format YYMMDD) is used as a version number.
static const string AppName = "Stockfish";
static const string EngineVersion = "2.1.1";
static const string AppTag = "";
static const string Version = "2.2";
static const string Tag = "";
/// 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
/// 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 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;
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 + " "
<< year.substr(2, 2) << setw(2)
<< (1 + months.find(month) / 4) << setw(2)
<< day << cpu64;
s << "Stockfish " << Tag
<< setfill('0') << " " << year.substr(2)
<< setw(2) << (1 + months.find(month) / 4)
<< 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();
}
/// Our brave developers! Required by UCI
/// Debug functions used mainly to collect run-time statistics
const string engine_authors() {
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++;
}
static uint64_t hits[2], means[2];
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_before() { dbg_hit_on(false); }
void dbg_after() { dbg_hit_on(true); dbg_hit_cnt0--; }
void dbg_mean_of(int v) { means[0]++; means[1] += v; }
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)
struct _timeb t;
@ -155,16 +135,16 @@ int cpu_count() {
#if defined(_MSC_VER)
SYSTEM_INFO s;
GetSystemInfo(&s);
return Min(s.dwNumberOfProcessors, MAX_THREADS);
return std::min(int(s.dwNumberOfProcessors), MAX_THREADS);
#else
# 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)
struct pst_dynamic psd;
if (pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0) == -1)
return 1;
return Min(psd.psd_proc_cnt, MAX_THREADS);
return std::min((int)psd.psd_proc_cnt, MAX_THREADS);
# else
return 1;
# 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
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));
}
void timed_wait(WaitCondition* sleepCond, Lock* sleepLock, int msec) {
#if defined(_MSC_VER)
int tm = msec;
#else
struct timeval t;
struct timespec abstime, *tm = &abstime;
int input_available() {
gettimeofday(&t, NULL);
static HANDLE inh = NULL;
static bool usePipe = false;
INPUT_RECORD rec[256];
DWORD nchars, recCnt;
abstime.tv_sec = t.tv_sec + (msec / 1000);
abstime.tv_nsec = (t.tv_usec + (msec % 1000) * 1000) * 1000;
if (!inh)
if (abstime.tv_nsec > 1000000000LL)
{
inh = GetStdHandle(STD_INPUT_HANDLE);
if (GetConsoleMode(inh, &nchars))
{
SetConsoleMode(inh, nchars & ~(ENABLE_MOUSE_INPUT | ENABLE_WINDOW_INPUT));
FlushConsoleInputBuffer(inh);
} else
usePipe = true;
abstime.tv_sec += 1;
abstime.tv_nsec -= 1000000000LL;
}
// 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
cond_timedwait(sleepCond, sleepLock, tm);
}
/// 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
@ -257,11 +191,11 @@ void prefetch(char*) {}
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
// Intel compiler. Both MSVC and gcc seems not affected.
__asm__ ("");
#endif
# endif
_mm_prefetch(addr, _MM_HINT_T2);
_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
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
it under the terms of the GNU General Public License as published by
@ -20,22 +20,31 @@
#if !defined(MISC_H_INCLUDED)
#define MISC_H_INCLUDED
#include <fstream>
#include <string>
#include "lock.h"
#include "types.h"
extern const std::string engine_name();
extern const std::string engine_authors();
extern int get_system_time();
extern const std::string engine_info(bool to_uci = false);
extern int system_time();
extern int cpu_count();
extern int input_available();
extern void timed_wait(WaitCondition*, Lock*, int);
extern void prefetch(char* addr);
extern void dbg_hit_on(bool b);
extern void dbg_hit_on_c(bool c, bool b);
extern void dbg_before();
extern void dbg_after();
extern void dbg_mean_of(int v);
extern void dbg_print_hit_rate();
extern void dbg_print_mean();
extern void dbg_print();
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)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -19,26 +19,17 @@
#include <cassert>
#include <cstring>
#include <iomanip>
#include <string>
#include <sstream>
#include "move.h"
#include "movegen.h"
#include "search.h"
#include "position.h"
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
/// (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
/// Chess960 mode.
/// Chess960 mode. Instead internally Move is coded as "king captures rook".
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)
return "0000";
if (move_is_short_castle(m) && !chess960)
return from == SQ_E1 ? "e1g1" : "e8g8";
if (is_castle(m) && !chess960)
to = from + (file_of(to) == FILE_H ? Square(2) : -Square(2));
if (move_is_long_castle(m) && !chess960)
return from == SQ_E1 ? "e1c1" : "e8c8";
if (move_is_promotion(m))
promotion = char(tolower(piece_type_to_char(move_promotion_piece(m))));
if (is_promotion(m))
promotion = char(tolower(piece_type_to_char(promotion_piece_type(m))));
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) {
MoveStack mlist[MAX_MOVES];
MoveStack* last = generate<MV_LEGAL>(pos, mlist);
for (MoveStack* cur = mlist; cur != last; cur++)
if (str == move_to_uci(cur->move, pos.is_chess960()))
return cur->move;
for (MoveList<MV_LEGAL> ml(pos); !ml.end(); ++ml)
if (str == move_to_uci(ml.move(), pos.is_chess960()))
return ml.move();
return MOVE_NONE;
}
/// 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.
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)
return "(none)";
if (m == MOVE_NULL)
return "(null)";
if (move_is_long_castle(m))
san = "O-O-O";
else if (move_is_short_castle(m))
san = "O-O";
assert(is_ok(m));
Bitboard attackers;
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
{
if (pt != PAWN)
{
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'
if (f == 1 && r > 1)
san += file_to_char(square_file(from));
else if (f > 1 && r == 1)
san += rank_to_char(square_rank(from));
else if (f > 1 && r > 1)
san += square_to_string(from);
// note that for pawns is not needed because starting file is explicit.
attackers = pos.attackers_to(to) & pos.pieces(pt, pos.side_to_move());
clear_bit(&attackers, from);
ambiguousMove = ambiguousFile = ambiguousRank = false;
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)
san += file_to_char(square_file(from));
san += file_to_char(file_of(from));
san += 'x';
}
san += square_to_string(to);
if (move_is_promotion(m))
if (is_promotion(m))
{
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;
}
/// 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
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
it under the terms of the GNU General Public License as published by
@ -18,9 +18,11 @@
*/
#include <cassert>
#include <algorithm>
#include "bitcount.h"
#include "movegen.h"
#include "position.h"
// Simple macro to wrap a very common while loop, no facny, no flexibility,
// hardcoded list name 'mlist' and from square 'from'.
@ -45,14 +47,13 @@ namespace {
template<PieceType Pt>
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();
if (Pt == KING)
{
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
b &= ~QueenPseudoAttacks[ksq];
}
b &= ~QueenPseudoAttacks[pos.king_square(flip(pos.side_to_move()))];
SERIALIZE_MOVES(b);
return mlist;
}
@ -60,13 +61,13 @@ namespace {
template<PieceType Pt>
inline MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
Bitboard dc, Square ksq) {
assert(Pt != KING);
assert(Pt != KING && Pt != PAWN);
Bitboard checkSqs, b;
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;
checkSqs = pos.attacks_from<Pt>(ksq) & pos.empty_squares();
@ -84,7 +85,7 @@ namespace {
b = pos.attacks_from<Pt>(from) & checkSqs;
SERIALIZE_MOVES(b);
} while ((from = *ptr++) != SQ_NONE);
} while ((from = *pl++) != SQ_NONE);
return mlist;
}
@ -111,15 +112,15 @@ namespace {
Bitboard b;
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 {
from = *ptr;
from = *pl;
b = pos.attacks_from<Pt>(from) & target;
SERIALIZE_MOVES(b);
} while (*++ptr != SQ_NONE);
} while (*++pl != SQ_NONE);
}
return mlist;
}
@ -150,14 +151,13 @@ namespace {
template<MoveType Type>
MoveStack* generate(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check());
Color us = pos.side_to_move();
Bitboard target;
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)
target = pos.empty_squares();
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<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);
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);
}
@ -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);
/// 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.
template<>
MoveStack* generate<MV_NON_CAPTURE_CHECK>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(!pos.in_check());
Bitboard b, dc;
Square from;
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
b = dc = pos.discovered_check_candidates(us);
b = dc = pos.discovered_check_candidates();
while (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 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
/// the side to move is in check. Returns a pointer to the end of the move list.
/// generate<MV_EVASION> generates all pseudo-legal check evasions when the side
/// to move is in check. Returns a pointer to the end of the move list.
template<>
MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
assert(pos.in_check());
Bitboard b, target;
@ -251,7 +249,7 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
Color us = pos.side_to_move();
Square ksq = pos.king_square(us);
Bitboard checkers = pos.checkers();
Bitboard sliderAttacks = EmptyBoardBB;
Bitboard sliderAttacks = 0;
assert(pos.piece_on(ksq) == make_piece(us, KING));
assert(checkers);
@ -265,26 +263,31 @@ MoveStack* generate<MV_EVASION>(const Position& pos, MoveStack* mlist) {
checkersCnt++;
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 ROOK: sliderAttacks |= RookPseudoAttacks[checksq]; break;
case QUEEN:
// In case of a queen remove also squares attacked in the other direction to
// avoid possible illegal moves when queen and king are on adjacent squares.
if (RookPseudoAttacks[checksq] & (1ULL << ksq))
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
// If queen and king are far we can safely remove all the squares attacked
// in the other direction becuase are not reachable by the king anyway.
if (squares_between(ksq, checksq) || (RookPseudoAttacks[checksq] & (1ULL << ksq)))
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
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
default:
break;
}
} while (b);
// 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;
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
/// 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);
}
/// generate<MV_LEGAL> computes a complete list of legal moves in the current position
template<>
MoveStack* generate<MV_LEGAL>(const Position& pos, MoveStack* mlist) {
assert(pos.is_ok());
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
while (cur != last)
@ -360,7 +353,7 @@ namespace {
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) {
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
// not already included in the queen-promotion.
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);
else (void)pos; // Silence a warning under MSVC
}
@ -406,21 +399,21 @@ namespace {
const Color Them = (Us == WHITE ? BLACK : WHITE);
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
const Square TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
const Square TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
const Square TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
const Square UP = (Us == WHITE ? DELTA_N : DELTA_S);
const Square RIGHT_UP = (Us == WHITE ? DELTA_NE : DELTA_SW);
const Square LEFT_UP = (Us == WHITE ? DELTA_NW : DELTA_SE);
Square to;
Bitboard b1, b2, dc1, dc2, pawnPushes, emptySquares;
Bitboard pawns = pos.pieces(PAWN, Us);
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
if (Type != MV_CAPTURE)
{
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)
@ -436,23 +429,23 @@ namespace {
emptySquares = pos.empty_squares();
pawns &= ~TRank7BB;
mlist = generate_promotions<Us, Type, TDELTA_NE>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Us, Type, TDELTA_NW>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Us, Type, TDELTA_N >(pos, mlist, pawnsOn7, emptySquares);
mlist = generate_promotions<Type, RIGHT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, LEFT_UP>(pos, mlist, pawnsOn7, enemyPieces);
mlist = generate_promotions<Type, UP>(pos, mlist, pawnsOn7, emptySquares);
}
// Standard captures
if (Type == MV_CAPTURE || Type == MV_EVASION)
{
mlist = generate_pawn_captures<Type, TDELTA_NE>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, TDELTA_NW>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, RIGHT_UP>(mlist, pawns, enemyPieces);
mlist = generate_pawn_captures<Type, LEFT_UP>(mlist, pawns, enemyPieces);
}
// Single and double pawn pushes
if (Type != MV_CAPTURE)
{
b1 = pawnPushes & emptySquares;
b2 = move_pawns<TDELTA_N>(pawnPushes & TRank3BB) & emptySquares;
b1 = (Type != MV_EVASION ? pawnPushes : pawnPushes & emptySquares);
b2 = move_pawns<UP>(pawnPushes & TRank3BB) & emptySquares;
if (Type == MV_CHECK)
{
@ -465,37 +458,37 @@ namespace {
// don't generate captures.
if (pawns & target) // For CHECK type target is dc bitboard
{
dc1 = move_pawns<TDELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<TDELTA_N>(dc1 & TRank3BB) & emptySquares;
dc1 = move_pawns<UP>(pawns & target & ~file_bb(ksq)) & emptySquares;
dc2 = move_pawns<UP>(dc1 & TRank3BB) & emptySquares;
b1 |= dc1;
b2 |= dc2;
}
}
SERIALIZE_MOVES_D(b1, -TDELTA_N);
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
SERIALIZE_MOVES_D(b1, -UP);
SERIALIZE_MOVES_D(b2, -UP -UP);
}
// En passant captures
if ((Type == MV_CAPTURE || Type == MV_EVASION) && pos.ep_square() != SQ_NONE)
{
assert(Us != WHITE || square_rank(pos.ep_square()) == RANK_6);
assert(Us != BLACK || square_rank(pos.ep_square()) == RANK_3);
assert(Us != WHITE || rank_of(pos.ep_square()) == RANK_6);
assert(Us != BLACK || rank_of(pos.ep_square()) == RANK_3);
// 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 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;
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
assert(b1 != EmptyBoardBB);
assert(b1);
while (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;
@ -504,37 +497,45 @@ namespace {
template<CastlingSide Side>
MoveStack* generate_castle_moves(const Position& pos, MoveStack* mlist, Color us) {
Color them = opposite_color(us);
Square ksq = pos.king_square(us);
CastleRight f = CastleRight((Side == KING_SIDE ? WHITE_OO : WHITE_OOO) << 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));
Square s1 = relative_square(us, Side == KING_SIDE ? SQ_G1 : SQ_C1);
Square s2 = relative_square(us, Side == KING_SIDE ? SQ_F1 : SQ_D1);
Square s;
bool illegal = false;
assert(!pos.in_check());
assert(pos.piece_on(kfrom) == make_piece(us, KING));
assert(pos.piece_on(rfrom) == make_piece(us, ROOK));
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 (s = Min(ksq, s1); s <= Max(ksq, s1); s++)
if ( (s != ksq && s != rsq && pos.square_is_occupied(s))
||(pos.attackers_to(s) & pos.pieces_of_color(them)))
illegal = true;
for (Square s = std::min(rfrom, rto); s <= std::max(rfrom, rto); s++)
if (s != kfrom && s != rfrom && !pos.square_is_empty(s))
return mlist;
for (s = Min(rsq, s2); s <= Max(rsq, s2); s++)
if (s != ksq && s != rsq && pos.square_is_occupied(s))
illegal = true;
// Because we generate only legal castling moves we need to verify that
// when moving the castling rook we do not discover some hidden checker.
// 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
&& 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);
(*mlist++).move = make_castle_move(kfrom, rfrom);
return mlist;
}

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -20,8 +20,7 @@
#if !defined(MOVEGEN_H_INCLUDED)
#define MOVEGEN_H_INCLUDED
#include "move.h"
#include "position.h"
#include "types.h"
enum MoveType {
MV_CAPTURE,
@ -30,11 +29,28 @@ enum MoveType {
MV_NON_CAPTURE_CHECK,
MV_EVASION,
MV_NON_EVASION,
MV_LEGAL,
MV_PSEUDO_LEGAL
MV_LEGAL
};
class Position;
template<MoveType>
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)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -18,6 +18,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <cassert>
#include "movegen.h"
@ -28,93 +29,127 @@
namespace {
enum MovegenPhase {
PH_TT_MOVES, // Transposition table move and mate killer
PH_GOOD_CAPTURES, // Queen promotions and captures with SEE values >= 0
PH_TT_MOVE, // Transposition table move
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_NONCAPTURES, // Non-captures and underpromotions
PH_BAD_CAPTURES, // Queen promotions and captures with SEE values < 0
PH_NONCAPTURES_1, // Non-captures and underpromotions with positive score
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_QCAPTURES, // Captures in quiescence search
PH_QRECAPTURES, // Recaptures in quiescence search
PH_QCHECKS, // Non-capture checks in quiescence search
PH_STOP
};
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 EvasionTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP };
const uint8_t QsearchWithoutChecksTable[] = { PH_TT_MOVES, PH_QCAPTURES, 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_MOVE, PH_EVASIONS, PH_STOP };
const uint8_t QsearchWithChecksTable[] = { PH_TT_MOVE, PH_QCAPTURES, PH_QCHECKS, 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;
}
}
/// Constructor for the MovePicker class. Apart from the position for which
/// it is asked to pick legal moves, MovePicker also wants some information
/// Constructors for the MovePicker class. As arguments we pass information
/// 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
/// search captures, promotions and some checks) and about how important good
/// move ordering is at the current node.
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h,
SearchStack* ss, Value beta) : pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
badCaptureThreshold = 0;
Search::Stack* ss, Value beta) : pos(p), H(h), depth(d) {
captureThreshold = 0;
badCaptures = moves + MAX_MOVES;
assert(d > DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
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;
}
else
{
ttMoves[1].move = (ss->mateKiller == ttm) ? MOVE_NONE : ss->mateKiller;
searchTT |= ttMoves[1].move;
killers[0].move = ss->killers[0];
killers[1].move = ss->killers[1];
// Consider sligtly negative captures as good if at low
// depth and far from beta.
// Consider sligtly negative captures as good if at low depth and far from beta
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 += 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, Depth d, const History& h)
MovePicker::MovePicker(const Position& p, Move ttm, Depth d, const History& h, Square recaptureSq)
: pos(p), H(h) {
int searchTT = ttm;
ttMoves[0].move = ttm;
ttMoves[1].move = MOVE_NONE;
assert(d <= DEPTH_ZERO);
pinned = p.pinned_pieces(pos.side_to_move());
if (p.in_check())
phasePtr = EvasionTable;
else if (d >= DEPTH_QS_CHECKS)
phasePtr = QsearchWithChecksTable;
else
else if (d >= DEPTH_QS_RECAPTURES)
{
phasePtr = QsearchWithoutChecksTable;
// Skip TT move if is not a capture or a promotion, this avoids
// qsearch tree explosion due to a possible perpetual check or
// similar rare cases when TT table is full.
if (ttm != MOVE_NONE && !pos.move_is_capture_or_promotion(ttm))
searchTT = ttMoves[0].move = MOVE_NONE;
if (ttm != MOVE_NONE && !pos.is_capture_or_promotion(ttm))
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();
}
@ -128,12 +163,12 @@ void MovePicker::go_next_phase() {
phase = *(++phasePtr);
switch (phase) {
case PH_TT_MOVES:
curMove = ttMoves;
lastMove = curMove + 2;
case PH_TT_MOVE:
lastMove = curMove + 1;
return;
case PH_GOOD_CAPTURES:
case PH_GOOD_PROBCUT:
lastMove = generate<MV_CAPTURE>(pos, moves);
score_captures();
return;
@ -143,10 +178,18 @@ void MovePicker::go_next_phase() {
lastMove = curMove + 2;
return;
case PH_NONCAPTURES:
lastMove = generate<MV_NON_CAPTURE>(pos, moves);
case PH_NONCAPTURES_1:
lastNonCapture = lastMove = generate<MV_NON_CAPTURE>(pos, moves);
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;
case PH_BAD_CAPTURES:
@ -167,6 +210,10 @@ void MovePicker::go_next_phase() {
score_captures();
return;
case PH_QRECAPTURES:
lastMove = generate<MV_CAPTURE>(pos, moves);
return;
case PH_QCHECKS:
lastMove = generate<MV_NON_CAPTURE_CHECK>(pos, moves);
return;
@ -185,7 +232,7 @@ void MovePicker::go_next_phase() {
/// MovePicker::score_captures(), MovePicker::score_noncaptures() and
/// MovePicker::score_evasions() assign a numerical move ordering score
/// 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() {
// 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++)
{
m = cur->move;
if (move_is_promotion(m))
cur->score = QueenValueMidgame;
else
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m));
cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
- type_of(pos.piece_on(move_from(m)));
if (is_promotion(m))
cur->score += PieceValueMidgame[Piece(promotion_piece_type(m))];
}
}
@ -245,22 +292,22 @@ void MovePicker::score_evasions() {
m = cur->move;
if ((seeScore = pos.see_sign(m)) < 0)
cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom
else if (pos.move_is_capture(m))
cur->score = pos.midgame_value_of_piece_on(move_to(m))
- pos.type_of_piece_on(move_from(m)) + History::MaxValue;
else if (pos.is_capture(m))
cur->score = PieceValueMidgame[pos.piece_on(move_to(m))]
- type_of(pos.piece_on(move_from(m))) + History::MaxValue;
else
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
/// class. It returns a new legal move every time it is called, until there
/// MovePicker::next_move() is the most important method of the MovePicker class.
/// 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
/// 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
/// lock protected by caller when accessed through a shared MovePicker object.
Move MovePicker::get_next_move() {
Move MovePicker::next_move() {
Move move;
@ -271,71 +318,73 @@ Move MovePicker::get_next_move() {
switch (phase) {
case PH_TT_MOVES:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned))
return move;
case PH_TT_MOVE:
curMove++;
return ttMove;
break;
case PH_GOOD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
&& pos.pl_move_is_legal(move, pinned))
move = pick_best(curMove++, lastMove)->move;
if (move != ttMove)
{
assert(captureThreshold <= 0); // Otherwise we must use see instead of see_sign
// Check for a non negative SEE now
int seeValue = pos.see_sign(move);
if (seeValue >= badCaptureThreshold)
if (seeValue >= captureThreshold)
return move;
// Losing capture, move it to the tail of the array, note
// that move has now been already checked for legality.
// Losing capture, move it to the tail of the array
(--badCaptures)->move = move;
badCaptures->score = seeValue;
}
break;
case PH_KILLERS:
move = (curMove++)->move;
if ( move != MOVE_NONE
&& pos.move_is_legal(move, pinned)
&& move != ttMoves[0].move
&& move != ttMoves[1].move
&& !pos.move_is_capture(move))
case PH_GOOD_PROBCUT:
move = pick_best(curMove++, lastMove)->move;
if ( move != ttMove
&& pos.see(move) > captureThreshold)
return move;
break;
case PH_NONCAPTURES:
// Sort negative scored moves only when we get there
if (curMove == lastGoodNonCapture)
insertion_sort<MoveStack>(lastGoodNonCapture, lastMove);
case PH_KILLERS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& move != ttMoves[1].move
if ( move != MOVE_NONE
&& 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[1].move
&& pos.pl_move_is_legal(move, pinned))
&& move != killers[1].move)
return move;
break;
case PH_BAD_CAPTURES:
move = pick_best(curMove++, lastMove).move;
move = pick_best(curMove++, lastMove)->move;
return move;
case PH_EVASIONS:
case PH_QCAPTURES:
move = pick_best(curMove++, lastMove).move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
move = pick_best(curMove++, lastMove)->move;
if (move != ttMove)
return move;
break;
case PH_QRECAPTURES:
move = (curMove++)->move;
if (move_to(move) == recaptureSquare)
return move;
break;
case PH_QCHECKS:
move = (curMove++)->move;
if ( move != ttMoves[0].move
&& pos.pl_move_is_legal(move, pinned))
if (move != ttMove)
return move;
break;

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -21,17 +21,15 @@
#define MOVEPICK_H_INCLUDED
#include "history.h"
#include "move.h"
#include "position.h"
#include "search.h"
#include "types.h"
struct SearchStack;
/// MovePicker is a class which is used to pick one legal move at a time from
/// the current position. It is initialized with a Position object and a few
/// 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
/// 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
/// is called, until there are no legal moves left, when MOVE_NONE is returned.
/// MovePicker::next_move(), which returns a new pseudo legal move each time
/// 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
/// 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
public:
MovePicker(const Position&, Move, Depth, const History&, SearchStack*, Value);
MovePicker(const Position&, Move, Depth, const History&);
Move get_next_move();
MovePicker(const Position&, Move, Depth, const History&, Search::Stack*, Value);
MovePicker(const Position&, Move, Depth, const History&, Square recaptureSq);
MovePicker(const Position&, Move, const History&, PieceType parentCapture);
Move next_move();
private:
void score_captures();
@ -52,11 +51,13 @@ private:
const Position& pos;
const History& H;
Bitboard pinned;
MoveStack ttMoves[2], killers[2];
int badCaptureThreshold, phase;
Depth depth;
Move ttMove;
MoveStack killers[2];
Square recaptureSquare;
int captureThreshold, phase;
const uint8_t* phasePtr;
MoveStack *curMove, *lastMove, *lastGoodNonCapture, *badCaptures;
MoveStack *curMove, *lastMove, *lastNonCapture, *badCaptures;
MoveStack moves[MAX_MOVES];
};

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -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
/// in an hash table, so we don't have to recompute everything when the same
/// pawn structure occurs again.
PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) const {
PawnInfo* PawnInfoTable::pawn_info(const Position& pos) const {
assert(pos.is_ok());
Key key = pos.get_pawn_key();
Key key = pos.pawn_key();
PawnInfo* pi = probe(key);
// 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,
Bitboard theirPawns, PawnInfo* pi) {
const BitCountType Max15 = CpuIs64Bit ? CNT64_MAX15 : CNT32_MAX15;
const Color Them = (Us == WHITE ? BLACK : WHITE);
Bitboard b;
@ -127,15 +124,15 @@ Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
Rank r;
bool passed, isolated, doubled, opposed, chain, backward, candidate;
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
while ((s = *ptr++) != SQ_NONE)
while ((s = *pl++) != SQ_NONE)
{
assert(pos.piece_on(s) == make_piece(Us, PAWN));
f = square_file(s);
r = square_rank(s);
f = file_of(s);
r = rank_of(s);
// This file cannot be half open
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
// enemy pawns in the forward direction on the neighboring files.
candidate = !(opposed | passed | backward | isolated)
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != EmptyBoardBB
&& count_1s<Max15>(b) >= count_1s<Max15>(attack_span_mask(Us, s) & theirPawns);
&& (b = attack_span_mask(Them, s + pawn_push(Us)) & ourPawns) != 0
&& popcount<Max15>(b) >= popcount<Max15>(attack_span_mask(Us, s) & theirPawns);
// Passed pawns will be properly scored in evaluation because we need
// full attack info to evaluate passed pawns. Only the frontmost passed
@ -225,12 +222,12 @@ Score PawnInfo::updateShelter(const Position& pos, Square ksq) {
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);
for (int i = 0; i < 3; i++)
{
r += Shift;
shelter += BitCount8Bit[(pawns >> r) & 0xFF] * (64 >> i);
shelter += BitCount8Bit[(pawns >> r) & 0xFF] << (6 - i);
}
}
kingSquares[Us] = ksq;

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -29,9 +29,9 @@ const int PawnTableSize = 16384;
/// PawnInfo is a class which contains various information about a pawn
/// structure. Currently, it only includes a middle game and an end game
/// 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
/// (performed by calling the get_pawn_info method in a PawnInfoTable object)
/// returns a pointer to a PawnInfo object.
/// to add further information in the future. A lookup to the pawn hash
/// table (performed by calling the pawn_info method in a PawnInfoTable
/// object) returns a pointer to a PawnInfo object.
class PawnInfo {
@ -63,11 +63,11 @@ private:
/// 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> {
public:
PawnInfo* get_pawn_info(const Position& pos) const;
PawnInfo* pawn_info(const Position& pos) const;
private:
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
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
it under the terms of the GNU General Public License as published by
@ -20,44 +20,23 @@
#if !defined(POSITION_H_INCLUDED)
#define POSITION_H_INCLUDED
#include <cassert>
#include "bitboard.h"
#include "move.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;
/// struct checkInfo is initialized at c'tor time and keeps
/// info used to detect if a move gives check.
struct CheckInfo {
explicit CheckInfo(const Position&);
explicit CheckInfo(const Position&);
Bitboard dcCandidates;
Bitboard checkSq[8];
Square ksq;
};
/// 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
Bitboard dcCandidates;
Bitboard pinned;
Bitboard checkSq[8];
};
@ -68,14 +47,14 @@ enum Phase {
struct StateInfo {
Key pawnKey, materialKey;
int castleRights, rule50, gamePly, pliesFromNull;
Square epSquare;
Score value;
Value npMaterial[2];
int castleRights, rule50, pliesFromNull;
Score value;
Square epSquare;
PieceType capturedType;
Key key;
Bitboard checkersBB;
PieceType capturedType;
StateInfo* previous;
};
@ -104,35 +83,24 @@ struct StateInfo {
class Position {
Position(); // No default or copy c'tor allowed
Position(const Position& pos);
// No copy c'tor or assignment operator allowed
Position(const Position&);
Position& operator=(const Position&);
public:
enum GamePhase {
MidGame,
EndGame
};
// Constructors
Position(const Position& pos, int threadID);
Position(const std::string& fen, bool isChess960, int threadID);
Position() {}
Position(const Position& pos, int th) { copy(pos, th); }
Position(const std::string& fen, bool isChess960, int th);
// Text input/output
void copy(const Position& pos, int th);
void from_fen(const std::string& fen, bool isChess960);
const std::string to_fen() const;
void print(Move m = MOVE_NONE) const;
// Copying
void flip();
// The piece on a given square
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_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
Color side_to_move() const;
@ -140,7 +108,7 @@ public:
// Bitboard representation of the position
Bitboard empty_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, Color c) const;
Bitboard pieces(PieceType pt1, PieceType pt2) const;
@ -156,42 +124,37 @@ public:
Square king_square(Color c) const;
// Castling rights
bool can_castle_kingside(Color c) const;
bool can_castle_queenside(Color c) const;
bool can_castle(CastleRight f) const;
bool can_castle(Color c) const;
Square initial_kr_square(Color c) const;
Square initial_qr_square(Color c) const;
Square castle_rook_square(CastleRight f) const;
// Bitboards for pinned pieces and discovered check candidates
Bitboard discovered_check_candidates(Color c) const;
Bitboard pinned_pieces(Color c) const;
Bitboard discovered_check_candidates() const;
Bitboard pinned_pieces() const;
// Checking pieces and under check information
Bitboard checkers() const;
bool in_check() const;
// Piece lists
Square piece_list(Color c, PieceType pt, int index) const;
const Square* piece_list_begin(Color c, PieceType pt) const;
const Square* piece_list(Color c, PieceType pt) const;
// Information about attacks to or from a given square
Bitboard attackers_to(Square s) const;
Bitboard attackers_to(Square s, Bitboard occ) const;
Bitboard attacks_from(Piece p, Square s) const;
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, Color c) const;
// 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_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 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
PieceType captured_piece_type() const;
@ -199,38 +162,32 @@ public:
// Information about pawns
bool pawn_is_passed(Color c, Square s) const;
// Weak squares
bool square_is_weak(Square s, Color c) const;
// Doing and undoing moves
void do_setup_move(Move m);
void do_move(Move m, StateInfo& st);
void do_move(Move m, StateInfo& st, const CheckInfo& ci, bool moveIsCheck);
void undo_move(Move m);
void do_null_move(StateInfo& st);
void undo_null_move();
template<bool Do> void do_null_move(StateInfo& st);
// Static exchange evaluation
int see(Square from, Square to) const;
int see(Move m) const;
int see_sign(Move m) const;
// Accessing hash keys
Key get_key() const;
Key get_exclusion_key() const;
Key get_pawn_key() const;
Key get_material_key() const;
Key key() const;
Key exclusion_key() const;
Key pawn_key() const;
Key material_key() const;
// Incremental evaluation
Score value() 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
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;
// Other properties of the position
@ -245,30 +202,23 @@ public:
void set_nodes_searched(int64_t n);
// 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
static void init_zobrist();
static void init_piece_square_tables();
// Global initialization
static void init();
private:
// Initialization helper functions (used while setting up a position)
void clear();
void detach();
void put_piece(Piece p, Square s);
void do_allow_oo(Color c);
void do_allow_ooo(Color c);
bool set_castling_rights(char token);
void set_castle_right(Square ksq, Square rsq);
bool move_is_legal(const Move m) const;
// Helper functions for doing and undoing moves
void do_capture_move(Key& key, PieceType capture, Color them, Square to, bool ep);
void do_castle_move(Move m);
void undo_castle_move(Move m);
void find_checkers();
template<bool FindPinned>
Bitboard hidden_checkers(Color c) const;
// Helper template functions
template<bool Do> void do_castle_move(Move m);
template<bool FindPinned> Bitboard hidden_checkers() const;
// Computing hash keys from scratch (for initialization and debugging)
Key compute_key() const;
@ -276,45 +226,43 @@ private:
Key compute_material_key() const;
// 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;
Value compute_non_pawn_material(Color c) const;
// Board
Piece board[64];
Piece board[64]; // [square]
// Bitboards
Bitboard byTypeBB[8], byColorBB[2];
Bitboard byTypeBB[8]; // [pieceType]
Bitboard byColorBB[2]; // [color]
Bitboard occupied;
// Piece counts
int pieceCount[2][8]; // [color][pieceType]
int pieceCount[2][8]; // [color][pieceType]
// Piece lists
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
Square pieceList[2][8][16]; // [color][pieceType][index]
int index[64]; // [square]
// Other info
Color sideToMove;
Key history[MaxGameLength];
int castleRightsMask[64];
int castleRightsMask[64]; // [square]
Square castleRookSquare[16]; // [castleRight]
StateInfo startState;
File initialKFile, initialKRFile, initialQRFile;
bool chess960;
int startPosPlyCounter;
int threadID;
int64_t nodes;
int startPosPly;
Color sideToMove;
int threadID;
StateInfo* st;
int chess960;
// Static variables
static Key zobrist[2][8][64];
static Key zobEp[64];
static Key zobCastle[16];
static Score pieceSquareTable[16][64]; // [piece][square]
static Key zobrist[2][8][64]; // [color][pieceType][square]/[piece count]
static Key zobEp[64]; // [square]
static Key zobCastle[16]; // [castleRight]
static Key zobSideToMove;
static Score PieceSquareTable[16][64];
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 {
@ -329,28 +277,8 @@ inline Piece Position::piece_on(Square s) const {
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 {
return piece_on(s) == PIECE_NONE;
}
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)];
return board[s] == NO_PIECE;
}
inline Color Position::side_to_move() const {
@ -358,14 +286,14 @@ inline Color Position::side_to_move() const {
}
inline Bitboard Position::occupied_squares() const {
return byTypeBB[0];
return occupied;
}
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];
}
@ -389,11 +317,7 @@ inline int Position::piece_count(Color c, PieceType pt) const {
return pieceCount[c][pt];
}
inline Square Position::piece_list(Color c, PieceType pt, int idx) const {
return pieceList[c][pt][idx];
}
inline const Square* Position::piece_list_begin(Color c, PieceType pt) const {
inline const Square* Position::piece_list(Color c, PieceType pt) const {
return pieceList[c][pt];
}
@ -405,24 +329,16 @@ inline Square Position::king_square(Color c) const {
return pieceList[c][KING][0];
}
inline bool Position::can_castle_kingside(Color side) const {
return st->castleRights & (1+int(side));
inline bool Position::can_castle(CastleRight f) const {
return st->castleRights & f;
}
inline bool Position::can_castle_queenside(Color side) const {
return st->castleRights & (4+4*int(side));
inline bool Position::can_castle(Color c) const {
return st->castleRights & ((WHITE_OO | WHITE_OOO) << c);
}
inline bool Position::can_castle(Color side) const {
return can_castle_kingside(side) || can_castle_queenside(side);
}
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));
inline Square Position::castle_rook_square(CastleRight f) const {
return castleRookSquare[f];
}
template<>
@ -450,44 +366,56 @@ inline Bitboard Position::attacks_from<QUEEN>(Square s) const {
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 {
return st->checkersBB;
}
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 {
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 {
return !(pieces(PAWN, opposite_color(c)) & attack_span_mask(c, s));
}
inline Key Position::get_key() const {
inline Key Position::key() const {
return st->key;
}
inline Key Position::get_exclusion_key() const {
inline Key Position::exclusion_key() const {
return st->key ^ zobExclusion;
}
inline Key Position::get_pawn_key() const {
inline Key Position::pawn_key() const {
return st->pawnKey;
}
inline Key Position::get_material_key() const {
inline Key Position::material_key() const {
return st->materialKey;
}
inline Score Position::pst(Color c, PieceType pt, Square s) {
return PieceSquareTable[make_piece(c, pt)][s];
inline Score Position::pst(Piece p, Square s) const {
return pieceSquareTable[p][s];
}
inline Score Position::pst_delta(Piece piece, Square from, Square to) {
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
return pieceSquareTable[piece][to] - pieceSquareTable[piece][from];
}
inline Score Position::value() const {
@ -498,21 +426,21 @@ inline Value Position::non_pawn_material(Color c) const {
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 piece_on(move_from(m)) == make_piece(c, PAWN)
&& pawn_is_passed(c, move_to(m));
return board[move_from(m)] == make_piece(sideToMove, PAWN)
&& pawn_is_passed(sideToMove, move_to(m));
}
inline int Position::startpos_ply_counter() const {
return startPosPlyCounter;
return startPosPly + st->pliesFromNull; // HACK
}
inline bool Position::opposite_colored_bishops() const {
return piece_count(WHITE, BISHOP) == 1 && piece_count(BLACK, BISHOP) == 1
&& opposite_color_squares(piece_list(WHITE, BISHOP, 0), piece_list(BLACK, BISHOP, 0));
return pieceCount[WHITE][BISHOP] == 1
&& pieceCount[BLACK][BISHOP] == 1
&& opposite_colors(pieceList[WHITE][BISHOP][0], pieceList[BLACK][BISHOP][0]);
}
inline bool Position::has_pawn_on_7th(Color c) const {
@ -523,16 +451,17 @@ inline bool Position::is_chess960() const {
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 !
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
assert(is_ok(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 !
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
// Note that castle is coded as "king captures the rook"
assert(is_ok(m));
return (!square_is_empty(move_to(m)) && !is_castle(m)) || is_enpassant(m);
}
inline PieceType Position::captured_piece_type() const {
@ -543,12 +472,4 @@ inline int Position::thread() const {
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)

View File

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

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -20,19 +20,6 @@
available under 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.
** 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)
@ -40,6 +27,20 @@
#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 {
// 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
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
it under the terms of the GNU General Public License as published by
@ -21,53 +21,62 @@
#define SEARCH_H_INCLUDED
#include <cstring>
#include <vector>
#include "move.h"
#include "types.h"
class Position;
struct SplitPoint;
/// The SearchStack 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 SearchStack objects, indexed by the
/// current ply.
namespace Search {
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;
Move currentMove;
Move mateKiller;
Move excludedMove;
Move bestMove;
Move killers[2];
Depth reduction;
Value eval;
Value evalMargin;
bool skipNullMove;
SplitPoint* sp;
int skipNullMove;
};
/// 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
/// 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)
: 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;
int time, increment, movesToGo, maxTime, maxDepth, maxNodes, 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 bool think(Position& pos, const SearchLimits& limits, Move searchMoves[]);
extern void think();
} // namespace
#endif // !defined(SEARCH_H_INCLUDED)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -19,36 +19,41 @@
#include <iostream>
#include "search.h"
#include "thread.h"
#include "ucioption.h"
ThreadsManager Threads; // Global object definition
using namespace Search;
ThreadsManager Threads; // Global object
namespace { extern "C" {
// start_routine() is the C function which is called when a new thread
// is launched. It simply calls idle_loop() with the supplied threadID.
// There are two versions of this function; one for POSIX threads and
// one for Windows threads.
// is launched. It simply calls idle_loop() of the supplied thread. The first
// and last thread are special. First one is the main search thread while the
// last one mimics a timer, they run in main_loop() and timer_loop().
#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;
}
#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
// the thread's currently active split point, or in some ancestor of
// the current split point.
// cutoff_occurred() checks whether a beta cutoff has occurred in the current
// active split point, or in some ancestor of the split point.
bool Thread::cutoff_occurred() const {
for (SplitPoint* sp = splitPoint; sp; sp = sp->parent)
if (sp->is_betaCutoff)
return true;
return false;
}
@ -85,7 +90,7 @@ bool Thread::cutoff_occurred() const {
bool Thread::is_available_to(int master) const {
if (state != AVAILABLE)
if (is_searching)
return false;
// 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
// parameters according to the UCI options values. It is called before
// to start a new search.
// read_uci_options() updates number of active threads and other parameters
// according to the UCI options values. It is called before to start a new search.
void ThreadsManager::read_uci_options() {
maxThreadsPerSplitPoint = Options["Maximum Number of Threads per Split Point"].value<int>();
minimumSplitDepth = Options["Minimum Split Depth"].value<int>() * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"].value<bool>();
activeThreads = Options["Threads"].value<int>();
maxThreadsPerSplitPoint = Options["Max Threads per Split Point"];
minimumSplitDepth = Options["Min Split Depth"] * ONE_PLY;
useSleepingThreads = Options["Use Sleeping Threads"];
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() {
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
allThreadsShouldExit = false;
// 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++)
// Initialize thread's sleep conditions and split point locks
for (int i = 0; i <= MAX_THREADS; i++)
{
lock_init(&threads[i].sleepLock);
cond_init(&threads[i].sleepCond);
@ -144,48 +164,51 @@ void ThreadsManager::init() {
lock_init(&(threads[i].splitPoints[j].lock));
}
// Create and startup all the threads but the main that is already running
for (int i = 1; i < MAX_THREADS; i++)
// Allocate main thread tables to call evaluate() also when not searching
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;
threadID[i] = i;
threads[i].is_searching = false;
threads[i].do_sleep = true;
threads[i].threadID = i;
#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
pthread_t pthreadID;
bool ok = (pthread_create(&pthreadID, NULL, start_routine, (void*)&threadID[i]) == 0);
pthread_detach(pthreadID);
bool ok = !pthread_create(&threads[i].handle, NULL, start_routine, &threads[i]);
#endif
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);
}
// 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() {
// Force the woken up threads to exit idle_loop() and hence terminate
allThreadsShouldExit = true;
for (int i = 0; i < MAX_THREADS; i++)
for (int i = 0; i <= MAX_THREADS; i++)
{
// Wake up all the threads and waits for termination
if (i != 0)
{
threads[i].wake_up();
while (threads[i].state != Thread::TERMINATED) {}
}
threads[i].do_terminate = true; // Search must be already finished
threads[i].wake_up();
// 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);
cond_destroy(&threads[i].sleepCond);
@ -193,58 +216,56 @@ void ThreadsManager::exit() {
lock_destroy(&(threads[i].splitPoints[j].lock));
}
lock_destroy(&mpLock);
}
// 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();
}
lock_destroy(&threadsLock);
cond_destroy(&sleepCond);
}
// 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 {
assert(master >= 0 && master < activeThreads);
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 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
// several available threads. If it does not succeed in splitting the
// node (because no idle threads are available, or because we have no unused
// split point objects), the function immediately returns. If splitting is
// possible, a SplitPoint object is initialized with all the data that must be
// copied to the helper threads and we tell our helper threads that they have
// been assigned work. This will cause them to instantly leave their idle loops and
// call search().When all threads have returned from search() then split() returns.
// several available threads. If it does not succeed in splitting the node
// (because no idle threads are available, or because we have no unused split
// point objects), the function immediately returns. If splitting is possible, a
// SplitPoint object is initialized with all the data that must be copied to the
// helper threads and then helper threads are told that they have been assigned
// work. This will cause them to instantly leave their idle loops and call
// search(). When all threads have returned from search() then split() returns.
template <bool Fake>
void ThreadsManager::split(Position& pos, SearchStack* ss, Value* alpha, const Value beta,
Value* bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, bool pvNode) {
assert(pos.is_ok());
assert(*bestValue >= -VALUE_INFINITE);
assert(*bestValue <= *alpha);
assert(*alpha < beta);
Value ThreadsManager::split(Position& pos, Stack* ss, Value alpha, Value beta,
Value bestValue, Depth depth, Move threatMove,
int moveCount, MovePicker* mp, int nodeType) {
assert(pos.pos_is_ok());
assert(bestValue > -VALUE_INFINITE);
assert(bestValue <= alpha);
assert(alpha < beta);
assert(beta <= VALUE_INFINITE);
assert(depth > DEPTH_ZERO);
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();
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
// active split points, don't split.
if ( !available_slave_exists(master)
|| masterThread.activeSplitPoints >= MAX_ACTIVE_SPLIT_POINTS)
{
lock_release(&mpLock);
return;
}
// Pick the next available split point from the split point stack
SplitPoint* sp = &masterThread.splitPoints[masterThread.activeSplitPoints];
// Pick the next available split point object from the split point stack
SplitPoint& splitPoint = masterThread.splitPoints[masterThread.activeSplitPoints++];
// Initialize the split point
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++)
splitPoint.is_slave[i] = false;
masterThread.splitPoint = &splitPoint;
sp->is_slave[i] = false;
// 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
// 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++)
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++;
}
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
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)
if (useSleepingThreads)
threads[i].wake_up();
}
// Everything is set up. The master thread enters the idle loop, from
// which it will instantly launch a search, because its state is
// THREAD_WORKISWAITING. We send the split point as a second parameter to the
// idle loop, which means that the main thread will return from the idle
// loop when all threads have finished their work at this split point.
idle_loop(master, &splitPoint);
lock_release(&threadsLock);
// We failed to allocate even one slave, return
if (!Fake && workersCnt == 1)
return bestValue;
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
// finished. Update alpha and bestValue, and return.
lock_grab(&mpLock);
// finished. Note that changing state and decreasing activeSplitPoints is done
// under lock protection to avoid a race with Thread::is_available_to().
lock_grab(&threadsLock);
*alpha = splitPoint.alpha;
*bestValue = splitPoint.bestValue;
masterThread.is_searching = true;
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
template void ThreadsManager::split<false>(Position&, SearchStack*, Value*, const Value, Value*, Depth, Move, int, MovePicker*, bool);
template void ThreadsManager::split<true>(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 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
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
it under the terms of the GNU General Public License as published by
@ -27,6 +27,7 @@
#include "movepick.h"
#include "pawns.h"
#include "position.h"
#include "search.h"
const int MAX_THREADS = 32;
const int MAX_ACTIVE_SPLIT_POINTS = 8;
@ -37,15 +38,15 @@ struct SplitPoint {
SplitPoint* parent;
const Position* pos;
Depth depth;
bool pvNode;
Value beta;
int nodeType;
int ply;
int master;
Move threatMove;
// Const pointers to shared data
MovePicker* mp;
SearchStack* ss;
Search::Stack* ss;
// Shared data
Lock lock;
@ -58,42 +59,44 @@ struct SplitPoint {
};
/// Thread struct is used to keep together all the thread related stuff like locks,
/// state 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
/// we don't have to care about someone changing the entry under our feet.
/// Thread struct keeps together all the thread related stuff like locks, state
/// 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 we don't have to care about someone changing the entry under our feet.
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();
bool cutoff_occurred() 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;
PawnInfoTable pawnTable;
int threadID;
int maxPly;
Lock sleepLock;
WaitCondition sleepCond;
volatile ThreadState state;
SplitPoint* volatile splitPoint;
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,
/// starting, parking and, the most important, launching a slave thread at a split
/// point. All the access to shared thread data is done through this class.
/// ThreadsManager class handles all the threads related stuff like init, starting,
/// parking and, the most important, launching a slave thread at a split point.
/// All the access to shared thread data is done through this class.
class ThreadsManager {
/* 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]; }
void init();
void exit();
void init_hash_tables();
bool use_sleeping_threads() const { return useSleepingThreads; }
int min_split_depth() const { return minimumSplitDepth; }
int size() const { return activeThreads; }
void set_size(int cnt) { activeThreads = cnt; }
void set_size(int cnt);
void read_uci_options();
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>
void split(Position& pos, SearchStack* ss, Value* alpha, const Value beta, Value* bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, bool pvNode);
Value split(Position& pos, Search::Stack* ss, Value alpha, Value beta, Value bestValue,
Depth depth, Move threatMove, int moveCount, MovePicker* mp, int nodeType);
private:
Lock mpLock;
friend struct Thread;
Thread threads[MAX_THREADS + 1]; // Last one is used as a timer
Lock threadsLock;
Depth minimumSplitDepth;
int maxThreadsPerSplitPoint;
bool useSleepingThreads;
int activeThreads;
volatile bool allThreadsShouldExit;
Thread threads[MAX_THREADS];
bool useSleepingThreads;
WaitCondition sleepCond;
};
extern ThreadsManager Threads;

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -18,6 +18,7 @@
*/
#include <cmath>
#include <algorithm>
#include "misc.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,
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
@ -72,18 +73,18 @@ namespace {
enum TimeType { OptimumTime, MaxTime };
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) {
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ prevChanges * (optimumSearchTime / 3);
unstablePVExtraTime = curChanges * (optimumSearchTime / 2)
+ 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:
@ -103,10 +104,10 @@ void TimeManager::init(const SearchLimits& limits, int currentPly)
int hypMTG, hypMyTime, t1, t2;
// Read uci parameters
int emergencyMoveHorizon = Options["Emergency Move Horizon"].value<int>();
int emergencyBaseTime = Options["Emergency Base Time"].value<int>();
int emergencyMoveTime = Options["Emergency Move Time"].value<int>();
int minThinkingTime = Options["Minimum Thinking Time"].value<int>();
int emergencyMoveHorizon = Options["Emergency Move Horizon"];
int emergencyBaseTime = Options["Emergency Base Time"];
int emergencyMoveTime = Options["Emergency Move Time"];
int minThinkingTime = Options["Minimum Thinking Time"];
// Initialize to maximum values but unstablePVExtraTime that is reset
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
// 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
hypMyTime = limits.time
+ limits.increment * (hypMTG - 1)
- 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);
t2 = minThinkingTime + remaining<MaxTime>(hypMyTime, hypMTG, currentPly);
optimumSearchTime = Min(optimumSearchTime, t1);
maximumSearchTime = Min(maximumSearchTime, t2);
optimumSearchTime = std::min(optimumSearchTime, t1);
maximumSearchTime = std::min(maximumSearchTime, t2);
}
if (Options["Ponder"].value<bool>())
if (Options["Ponder"])
optimumSearchTime += optimumSearchTime / 4;
// 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 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
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
it under the terms of the GNU General Public License as published by
@ -20,12 +20,12 @@
#if !defined(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 {
public:
void init(const SearchLimits& limits, int currentPly);
void init(const Search::LimitsType& limits, int currentPly);
void pv_instability(int curChanges, int prevChanges);
int available_time() const { return optimumSearchTime + unstablePVExtraTime; }
int maximum_time() const { return maximumSearchTime; }

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -17,7 +17,6 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <cstring>
#include <iostream>
@ -60,7 +59,7 @@ void TranspositionTable::set_size(size_t mbSize) {
if (!entries)
{
std::cerr << "Failed to allocate " << mbSize
<< " MB for transposition table." << std::endl;
<< "MB for transposition table." << std::endl;
exit(EXIT_FAILURE);
}
clear();

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -22,7 +22,7 @@
#include <iostream>
#include "move.h"
#include "misc.h"
#include "types.h"

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -20,6 +20,26 @@
#if !defined(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 <cstdlib>
#include <ctype.h>
@ -27,9 +47,9 @@
#if defined(_MSC_VER)
// 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: 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>
typedef signed __int8 int8_t;
@ -42,125 +62,111 @@ typedef signed __int64 int64_t;
typedef unsigned __int64 uint64_t;
#else
#include <inttypes.h>
# include <inttypes.h>
#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)
#define IS_64BIT
# include <intrin.h> // MSVC popcnt and bsfq instrinsics
# define IS_64BIT
# define USE_BSFQ
#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)
#include <nmmintrin.h>
# include <nmmintrin.h> // Intel header for _mm_popcnt_u64() intrinsic
#endif
// Cache line alignment specification
#if defined(_MSC_VER) || defined(__INTEL_COMPILER)
#define CACHE_LINE_ALIGNMENT __declspec(align(64))
# define CACHE_LINE_ALIGNMENT __declspec(align(64))
#else
#define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
# define CACHE_LINE_ALIGNMENT __attribute__ ((aligned(64)))
#endif
// Define a __cpuid() function for gcc compilers, for Intel and MSVC
// is already available as an intrinsic.
#if defined(_MSC_VER)
#include <intrin.h>
#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
# define FORCE_INLINE __forceinline
#elif defined(__GNUC__)
#define FORCE_INLINE inline __attribute__((always_inline))
# define FORCE_INLINE inline __attribute__((always_inline))
#else
#define FORCE_INLINE inline
# define FORCE_INLINE inline
#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)
const bool CpuHasPOPCNT = cpu_has_popcnt();
const bool HasPopCnt = true;
#else
const bool CpuHasPOPCNT = false;
const bool HasPopCnt = false;
#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)
const bool CpuIs64Bit = true;
const bool Is64Bit = true;
#else
const bool CpuIs64Bit = false;
const bool Is64Bit = false;
#endif
#include <string>
typedef uint64_t Key;
typedef uint64_t Bitboard;
const int PLY_MAX = 100;
const int PLY_MAX_PLUS_2 = PLY_MAX + 2;
const int MAX_MOVES = 256;
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 {
VALUE_TYPE_NONE = 0,
@ -177,34 +183,36 @@ enum Value {
VALUE_INFINITE = 30001,
VALUE_NONE = 30002,
VALUE_MATE_IN_PLY_MAX = VALUE_MATE - PLY_MAX,
VALUE_MATED_IN_PLY_MAX = -VALUE_MATE + PLY_MAX,
VALUE_MATE_IN_MAX_PLY = VALUE_MATE - MAX_PLY,
VALUE_MATED_IN_MAX_PLY = -VALUE_MATE + MAX_PLY,
VALUE_ENSURE_INTEGER_SIZE_P = INT_MAX,
VALUE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
enum PieceType {
PIECE_TYPE_NONE = 0,
NO_PIECE_TYPE = 0,
PAWN = 1, KNIGHT = 2, BISHOP = 3, ROOK = 4, QUEEN = 5, KING = 6
};
enum Piece {
PIECE_NONE_DARK_SQ = 0, WP = 1, WN = 2, WB = 3, WR = 4, WQ = 5, WK = 6,
BP = 9, BN = 10, BB = 11, BR = 12, BQ = 13, BK = 14, PIECE_NONE = 16
NO_PIECE = 16, // color_of(NO_PIECE) == NO_COLOR
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 {
WHITE, BLACK, COLOR_NONE
WHITE, BLACK, NO_COLOR
};
enum Depth {
ONE_PLY = 2,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_ZERO = 0 * ONE_PLY,
DEPTH_QS_CHECKS = -1 * ONE_PLY,
DEPTH_QS_NO_CHECKS = -2 * ONE_PLY,
DEPTH_QS_RECAPTURES = -4 * 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
};
enum SquareColor {
DARK, LIGHT
};
enum ScaleFactor {
SCALE_FACTOR_ZERO = 0,
SCALE_FACTOR_NORMAL = 64,
SCALE_FACTOR_MAX = 128,
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.
/// 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 {
SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
SCORE_ZERO = 0,
SCORE_ENSURE_INTEGER_SIZE_P = INT_MAX,
SCORE_ENSURE_INTEGER_SIZE_N = INT_MIN
};
#define ENABLE_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, int i) { return T(int(d) / i); } \
inline T operator- (const T d) { return T(-int(d)); } \
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 void operator+= (T& d1, const T d2) { d1 = d1 + d2; } \
inline void operator-= (T& d1, const T d2) { d1 = d1 - d2; } \
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 Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
/// Extracting the signed lower and upper 16 bits it not so trivial because
/// according to the standard a simple cast to short is implementation defined
/// and so is a right shift of a signed integer.
inline Value mg_value(Score s) { return Value(((s + 32768) & ~0xffff) / 0x10000); }
/// On Intel 64 bit we have a small speed regression with the standard conforming
/// version, so use a faster code in this case that, 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
#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(PieceType)
@ -287,44 +307,23 @@ ENABLE_OPERATORS_ON(Square)
ENABLE_OPERATORS_ON(File)
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
inline Value operator+ (Value v, int i) { return Value(int(v) + i); }
inline Value operator- (Value v, int i) { return Value(int(v) - i); }
ENABLE_SAFE_OPERATORS_ON(Score)
// Extracting the _signed_ lower and upper 16 bits it not so trivial
// because according to the standard a simple cast to short is
// 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.
/// 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);
// Remaining Score operators are standard
inline Score operator+ (const Score d1, const Score d2) { return Score(int(d1) + int(d2)); }
inline Score operator- (const Score d1, const Score d2) { return Score(int(d1) - int(d2)); }
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)); }
inline void operator+= (Score& d1, const Score d2) { d1 = d1 + d2; }
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); }
/// Division of a Score 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);
}
#undef ENABLE_OPERATORS_ON
#undef ENABLE_SAFE_OPERATORS_ON
const Value PawnValueMidgame = Value(0x0C6);
const Value PawnValueEndgame = Value(0x102);
@ -337,135 +336,178 @@ const Value RookValueEndgame = Value(0x4FE);
const Value QueenValueMidgame = Value(0x9D9);
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;
}
inline Value value_mated_in(int ply) {
inline Value mated_in(int ply) {
return -VALUE_MATE + ply;
}
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) {
return PieceType(int(p) & 7);
inline PieceType type_of(Piece p) {
return PieceType(p & 7);
}
inline Color color_of_piece(Piece p) {
return Color(int(p) >> 3);
inline Color color_of(Piece p) {
return Color(p >> 3);
}
inline Color opposite_color(Color c) {
return Color(int(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 Color flip(Color c) {
return Color(c ^ 1);
}
inline Square make_square(File f, Rank r) {
return Square((int(r) << 3) | int(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;
return Square((r << 3) | f);
}
inline bool square_is_ok(Square s) {
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) {
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)

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -17,97 +17,112 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cassert>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "evaluate.h"
#include "misc.h"
#include "move.h"
#include "position.h"
#include "search.h"
#include "thread.h"
#include "ucioption.h"
using namespace std;
namespace {
// FEN string for the initial position
const string StartPositionFEN = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
// FEN string of the initial position, normal chess
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
// is actually a string stream built on a given input string.
typedef istringstream UCIParser;
// Keep track of position keys along the setup moves (from start position to the
// position just before to start searching). This is needed by draw detection
// 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_position(Position& pos, UCIParser& up);
bool go(Position& pos, UCIParser& up);
void perft(Position& pos, UCIParser& up);
void set_option(istringstream& up);
void set_position(Position& pos, istringstream& up);
void go(Position& pos, istringstream& up);
void perft(Position& pos, istringstream& up);
}
/// execute_uci_command() takes a string as input, uses a UCIParser
/// object to parse this text string as a UCI command, and calls
/// the appropriate functions. In addition to the UCI commands,
/// the function also supports a few debug commands.
/// Wait for a command from the user, parse this text string as an UCI command,
/// and call the appropriate functions. Also intercepts EOF from stdin to ensure
/// that we exit gracefully if the GUI dies unexpectedly. In addition to the UCI
/// 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);
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")
while (token != "quit")
{
read_evaluation_uci_options(pos.side_to_move());
cout << trace_evaluate(pos) << endl;
if (!getline(cin, cmd)) // Block here waiting for input
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
// 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;
up >> token; // operator>>() skips any whitespace
is >> token;
if (token == "startpos")
{
pos.from_fen(StartPositionFEN, false);
up >> token; // Consume "moves" token if any
fen = StartFEN;
is >> token; // Consume "moves" token if any
}
else if (token == "fen")
{
while (up >> token && token != "moves")
while (is >> token && token != "moves")
fen += token + " ";
else
return;
pos.from_fen(fen, Options["UCI_Chess960"].value<bool>());
}
else return;
pos.from_fen(fen, Options["UCI_Chess960"]);
// Parse move list (if any)
while (up >> token)
pos.do_setup_move(move_from_uci(pos, token));
while (is >> token && (m = move_from_uci(pos, token)) != MOVE_NONE)
{
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
// command. The function updates the corresponding UCI option ("name")
// to the given value ("value").
// set_option() is called when engine receives the "setoption" UCI command. The
// function updates the UCI option ("name") to the given value ("value").
void set_option(UCIParser& up) {
void set_option(istringstream& is) {
string token, name;
string value = "true"; // UCI buttons don't have a "value" field
string token, name, value;
up >> token; // Consume "name" token
up >> name; // Read option name
is >> token; // Consume "name" token
// Handle names with included spaces
while (up >> token && token != "value")
name += " " + token;
// Read option name (can contain spaces)
while (is >> token && token != "value")
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
while (up >> token)
value += " " + token;
if (Options.find(name) != Options.end())
Options[name].set_value(value);
else
if (!Options.count(name))
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
// function sets the thinking time and other parameters from the input
// string, and then calls think(). Returns false if a quit command
// is received while thinking, true otherwise.
// go() is called when engine receives the "go" UCI command. The function sets
// the thinking time and other parameters from the input string, and then starts
// the main searching thread.
bool go(Position& pos, UCIParser& up) {
void go(Position& pos, istringstream& is) {
string token;
SearchLimits limits;
Move searchMoves[MAX_MOVES], *cur = searchMoves;
Search::LimitsType limits;
std::vector<Move> searchMoves;
int time[] = { 0, 0 }, inc[] = { 0, 0 };
while (up >> token)
while (is >> token)
{
if (token == "infinite")
limits.infinite = true;
else if (token == "ponder")
limits.ponder = true;
else if (token == "wtime")
up >> time[WHITE];
is >> time[WHITE];
else if (token == "btime")
up >> time[BLACK];
is >> time[BLACK];
else if (token == "winc")
up >> inc[WHITE];
is >> inc[WHITE];
else if (token == "binc")
up >> inc[BLACK];
is >> inc[BLACK];
else if (token == "movestogo")
up >> limits.movesToGo;
is >> limits.movesToGo;
else if (token == "depth")
up >> limits.maxDepth;
is >> limits.maxDepth;
else if (token == "nodes")
up >> limits.maxNodes;
is >> limits.maxNodes;
else if (token == "movetime")
up >> limits.maxTime;
is >> limits.maxTime;
else if (token == "searchmoves")
while (up >> token)
*cur++ = move_from_uci(pos, token);
while (is >> token)
searchMoves.push_back(move_from_uci(pos, token));
}
*cur = MOVE_NONE;
limits.time = time[pos.side_to_move()];
limits.increment = inc[pos.side_to_move()];
assert(pos.is_ok());
return think(pos, limits, searchMoves);
Threads.start_thinking(pos, limits, searchMoves, true);
}
// perft() is called when engine receives the "perft" command.
// The function calls perft() passing the required search depth
// then prints counted leaf nodes and elapsed time.
// perft() is called when engine receives the "perft" command. The function
// calls perft() with the required search depth then prints counted leaf nodes
// and elapsed time.
void perft(Position& pos, UCIParser& up) {
void perft(Position& pos, istringstream& is) {
int depth, time;
int64_t n;
if (!(up >> depth))
if (!(is >> depth))
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
<< "\nTime (ms) " << time

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -17,8 +17,7 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cctype>
#include <iostream>
#include <algorithm>
#include <sstream>
#include "misc.h"
@ -26,145 +25,103 @@
#include "ucioption.h"
using std::string;
using std::cout;
using std::endl;
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 {
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();
return std::lexicographical_compare(s1.begin(), s1.end(), s2.begin(), s2.end(), ci_less);
}
/// OptionsMap c'tor initializes the UCI options to their hard coded default
/// values and initializes the default value of "Threads" and "Minimum Split Depth"
/// parameters according to the number of CPU cores.
/// values and initializes the default value of "Threads" and "Min Split Depth"
/// parameters according to the number of CPU cores detected.
OptionsMap::OptionsMap() {
int cpus = std::min(cpu_count(), MAX_THREADS);
int msd = cpus < 8 ? 4 : 7;
OptionsMap& o = *this;
o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200);
o["Minimum Split Depth"] = UCIOption(4, 4, 7);
o["Maximum Number of Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(1, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = 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);
o["Use Search Log"] = UCIOption(false);
o["Search Log Filename"] = UCIOption("SearchLog.txt");
o["Book File"] = UCIOption("book.bin");
o["Best Book Move"] = UCIOption(false);
o["Mobility (Middle Game)"] = UCIOption(100, 0, 200);
o["Mobility (Endgame)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Middle Game)"] = UCIOption(100, 0, 200);
o["Passed Pawns (Endgame)"] = UCIOption(100, 0, 200);
o["Space"] = UCIOption(100, 0, 200);
o["Aggressiveness"] = UCIOption(100, 0, 200);
o["Cowardice"] = UCIOption(100, 0, 200);
o["Min Split Depth"] = UCIOption(msd, 4, 7);
o["Max Threads per Split Point"] = UCIOption(5, 4, 8);
o["Threads"] = UCIOption(cpus, 1, MAX_THREADS);
o["Use Sleeping Threads"] = UCIOption(false);
o["Hash"] = UCIOption(32, 4, 8192);
o["Clear Hash"] = UCIOption(false, "button");
o["Ponder"] = UCIOption(true);
o["OwnBook"] = UCIOption(true);
o["MultiPV"] = UCIOption(1, 1, 500);
o["Skill Level"] = UCIOption(20, 0, 20);
o["Emergency Move Horizon"] = UCIOption(40, 0, 50);
o["Emergency Base Time"] = UCIOption(200, 0, 30000);
o["Emergency Move Time"] = UCIOption(70, 0, 5000);
o["Minimum Thinking Time"] = UCIOption(20, 0, 5000);
o["UCI_Chess960"] = UCIOption(false);
o["UCI_AnalyseMode"] = UCIOption(false);
}
/// OptionsMap::print_all() returns a string with all the UCI options in chronological
/// insertion order (the idx field) and in the format defined by the UCI protocol.
/// operator<<() is used to output all the UCI options in chronological insertion
/// 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 {
std::stringstream s;
for (size_t i = 0; i <= size(); i++)
for (OptionsMap::const_iterator it = begin(); it != end(); ++it)
if (it->second.idx == i)
for (size_t idx = 0; idx < om.size(); idx++)
for (OptionsMap::const_iterator it = om.begin(); it != om.end(); ++it)
if (it->second.idx == idx)
{
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")
s << " default " << o.defaultValue;
os << " default " << o.defaultValue;
if (o.type == "spin")
s << " min " << o.minValue << " max " << o.maxValue;
os << " min " << o.min << " max " << o.max;
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())
{ defaultValue = currentValue = def; }
UCIOption::UCIOption(const char* v) : type("string"), min(0), max(0), idx(Options.size())
{ defaultValue = currentValue = v; }
UCIOption::UCIOption(bool def, string t) : type(t), minValue(0), maxValue(0), idx(Options.size())
{ defaultValue = currentValue = (def ? "true" : "false"); }
UCIOption::UCIOption(bool v, string t) : type(t), min(0), max(0), idx(Options.size())
{ defaultValue = currentValue = (v ? "true" : "false"); }
UCIOption::UCIOption(int def, int minv, int maxv) : type("spin"), minValue(minv), maxValue(maxv), idx(Options.size())
{ defaultValue = currentValue = stringify(def); }
UCIOption::UCIOption(int v, int minv, int maxv) : type("spin"), min(minv), max(maxv), idx(Options.size())
{ std::ostringstream ss; ss << v; defaultValue = currentValue = ss.str(); }
/// set_value() updates currentValue of the Option object. Normally it's up to
/// the GUI to check for option's limits, but we could receive the new value
/// directly from the user by teminal window. So let's check the bounds anyway.
/// UCIOption::operator=() updates currentValue. Normally it's up to the GUI to
/// check for option's limits, but we could receive the new value directly from
/// 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());
if (v.empty())
return;
if ((type == "check" || type == "button") != (v == "true" || v == "false"))
return;
if (type == "spin")
{
int val = atoi(v.c_str());
if (val < minValue || val > maxValue)
return;
}
currentValue = v;
if ( !v.empty()
&& (type == "check" || type == "button") == (v == "true" || v == "false")
&& (type != "spin" || (atoi(v.c_str()) >= min && atoi(v.c_str()) <= max)))
currentValue = v;
}

View File

@ -1,7 +1,7 @@
/*
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
Copyright (C) 2008-2012 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
@ -25,61 +25,50 @@
#include <map>
#include <string>
struct OptionsMap;
/// UCIOption class implements an option as defined by UCI protocol
class UCIOption {
public:
UCIOption() {} // To be used in a std::map
UCIOption(const char* defaultValue);
UCIOption(bool defaultValue, std::string type = "check");
UCIOption(int defaultValue, int minValue, int maxValue);
UCIOption() {} // Required by std::map::operator[]
UCIOption(const char* v);
UCIOption(bool v, std::string type = "check");
UCIOption(int v, int min, int max);
void set_value(const std::string& v);
template<typename T> T value() const;
void operator=(const std::string& v);
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:
friend class OptionsMap;
friend std::ostream& operator<<(std::ostream&, const OptionsMap&);
std::string defaultValue, currentValue, type;
int minValue, maxValue;
int min, max;
size_t idx;
};
/// Custom comparator because UCI options should not be case sensitive
/// Custom comparator because UCI options should be case insensitive
struct CaseInsensitiveLess {
bool operator() (const std::string&, const std::string&) const;
};
/// Our options container is actually a map with a customized c'tor
class OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
public:
struct OptionsMap : public std::map<std::string, UCIOption, CaseInsensitiveLess> {
OptionsMap();
std::string print_all() const;
};
extern std::ostream& operator<<(std::ostream&, const OptionsMap&);
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)

View File

@ -165,7 +165,7 @@
</string-array>
<string name="about_info">\
<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. \
Die Android-Version erreicht auf einem 1GHz-Snapdragon-Prozessor oft eine Rechentiefe von 15 oder mehr Halbzügen.\n\
\n\

View File

@ -183,7 +183,7 @@
<string name="about_info">\
<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. \
The Android version can often search 15 ply or deeper on a 1GHz Snapdragon CPU.\n\
\n\