mirror of
https://github.com/HChaZZY/Stockfish.git
synced 2025-12-16 23:26:23 +08:00
Compare commits
139 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc0871acbc | ||
|
|
2643f1552f | ||
|
|
ba07b95ee0 | ||
|
|
ef58551a2d | ||
|
|
7d34e7bf84 | ||
|
|
12aeac5e14 | ||
|
|
734fb9a13b | ||
|
|
c12364bb67 | ||
|
|
ad5b5cef4a | ||
|
|
0d88b832e3 | ||
|
|
14c3da5cad | ||
|
|
0b9b34655f | ||
|
|
a66f31f129 | ||
|
|
2161d8b0b3 | ||
|
|
7bc72d092f | ||
|
|
b056e5d40a | ||
|
|
d0b8bc5fdf | ||
|
|
9a46ac6b2c | ||
|
|
0fc9d9ef61 | ||
|
|
bd618941ce | ||
|
|
403db5a6e9 | ||
|
|
3f3365221b | ||
|
|
ae0b965711 | ||
|
|
455993b289 | ||
|
|
c7a77dd3c0 | ||
|
|
af3dd21e90 | ||
|
|
1a7047f544 | ||
|
|
25c22ffe7a | ||
|
|
ae6157fcf3 | ||
|
|
5bec768d42 | ||
|
|
6ae30e7cb1 | ||
|
|
5ea8167921 | ||
|
|
55b5b03273 | ||
|
|
850c021f86 | ||
|
|
cd0c7373cd | ||
|
|
9a59535962 | ||
|
|
48246468f2 | ||
|
|
2b6bc70f7b | ||
|
|
8b3fdec7ec | ||
|
|
58452de86d | ||
|
|
93c9f342ca | ||
|
|
16acf57773 | ||
|
|
77eec9f9cb | ||
|
|
bc35f4c42d | ||
|
|
889c8538a8 | ||
|
|
c52da3b806 | ||
|
|
b599da01fa | ||
|
|
52bca81dcb | ||
|
|
0eedf47661 | ||
|
|
989833205f | ||
|
|
87507121d5 | ||
|
|
89fe8bc0a6 | ||
|
|
4c58db0dab | ||
|
|
71e852ea81 | ||
|
|
314faa905a | ||
|
|
a530fc2b60 | ||
|
|
7d0e0ff95e | ||
|
|
764229a2e2 | ||
|
|
5e340346db | ||
|
|
bf395c6be1 | ||
|
|
ad44ff2bca | ||
|
|
8e96149c8c | ||
|
|
3c085775d7 | ||
|
|
dd5a3ae4a6 | ||
|
|
f7f09b91ea | ||
|
|
8a116ce691 | ||
|
|
16626dd655 | ||
|
|
2f01d67a92 | ||
|
|
975d5e9c64 | ||
|
|
30075e4abc | ||
|
|
37398d9456 | ||
|
|
e05039156c | ||
|
|
f35ddb04af | ||
|
|
ef6fca98a0 | ||
|
|
fea46a8212 | ||
|
|
776c7df30c | ||
|
|
5e112f16da | ||
|
|
15ec3e911e | ||
|
|
1ecd8e13ee | ||
|
|
444c7c5183 | ||
|
|
e9757f7610 | ||
|
|
1ab01f1c14 | ||
|
|
4626ec2890 | ||
|
|
dda7e4639a | ||
|
|
1ae8c59c0b | ||
|
|
06f06a9be8 | ||
|
|
2f5ee9e4e8 | ||
|
|
dd884b65b7 | ||
|
|
0fdc75c0bd | ||
|
|
dae7cacd3b | ||
|
|
7c0cb8e73d | ||
|
|
53ce6ce49c | ||
|
|
7a68916ff9 | ||
|
|
82a1e2d5fc | ||
|
|
dc286d2673 | ||
|
|
a9e536a7eb | ||
|
|
e59d053984 | ||
|
|
1c73c1c150 | ||
|
|
423b8b9ded | ||
|
|
94dcac1fee | ||
|
|
0855d93de8 | ||
|
|
deecb3757c | ||
|
|
53c2bf0697 | ||
|
|
483a257618 | ||
|
|
12461996a5 | ||
|
|
70b7404a63 | ||
|
|
dddaeff7d8 | ||
|
|
941d923bf8 | ||
|
|
23de3e16f1 | ||
|
|
8ebe5075eb | ||
|
|
fa49311b36 | ||
|
|
d9b920acfb | ||
|
|
12b0517d1b | ||
|
|
b38685625a | ||
|
|
73da3a431c | ||
|
|
b50921fd5c | ||
|
|
a72c55283d | ||
|
|
cf4df0327a | ||
|
|
51c3af9dd0 | ||
|
|
f0b0a3b135 | ||
|
|
4dd7fccfd1 | ||
|
|
c1ea5ed6f7 | ||
|
|
53f882ff1a | ||
|
|
d8e7ce1863 | ||
|
|
181d34e5a0 | ||
|
|
2655f93c32 | ||
|
|
ab4d26f9bd | ||
|
|
d6b04c2798 | ||
|
|
e2e249eabd | ||
|
|
ed19a9f909 | ||
|
|
eddfd46a10 | ||
|
|
a806d7c3d6 | ||
|
|
ccdb634b77 | ||
|
|
564ed5b38c | ||
|
|
f05d059b17 | ||
|
|
06a5b602dc | ||
|
|
d892063cd3 | ||
|
|
64d6ba2e98 | ||
|
|
5a5dc6fa10 |
@@ -52,14 +52,13 @@ UCI parameter "Book File".
|
||||
|
||||
On Unix-like systems, it should usually be possible to compile
|
||||
Stockfish directly from the source code with the included Makefile.
|
||||
The exception is computer with big-endian CPUs, like PowerPC
|
||||
Macintoshes. Some of the bitboard routines in the current version of
|
||||
Stockfish are endianness-sensitive, and won't work on a big-endian CPU.
|
||||
|
||||
For big-endian machines like Power PC you need to enable the proper
|
||||
flag changing from -DNBIGENDIAN to -DBIGENDIAN in the Makefile.
|
||||
|
||||
Stockfish has POPCNT instruction runtime detection and support. This can
|
||||
give an extra speed on Core i7 or similar systems. To enable this feature
|
||||
(disabled by default) simply uncomment #define USE_POPCNT in bitcount.h
|
||||
before to compile.
|
||||
compile with 'make icc-profile-popcnt'
|
||||
|
||||
On 64 bit Unix-like systems the 'bsfq' assembly instruction will be used
|
||||
for bit counting. Detection is automatic at compile time, but in case you
|
||||
|
||||
54
src/Makefile
54
src/Makefile
@@ -25,8 +25,11 @@ EXE = stockfish
|
||||
### ==========================================================================
|
||||
### Compiler speed switches for both GCC and ICC. These settings are generally
|
||||
### fast on a broad range of systems, but may be changed experimentally
|
||||
###
|
||||
### NOTE: Some versions of gcc miscompile value.h with -O2 or -O3, this is the
|
||||
### safe setup, try changing to -O3 or -O2 and verify it works for you.
|
||||
### ==========================================================================
|
||||
GCCFLAGS = -O3 -msse
|
||||
GCCFLAGS = -O1 -msse
|
||||
ICCFLAGS = -fast -msse
|
||||
ICCFLAGS-OSX = -fast -mdynamic-no-pic
|
||||
|
||||
@@ -39,6 +42,14 @@ ICCFLAGS += -DNDEBUG
|
||||
ICCFLAGS-OSX += -DNDEBUG
|
||||
|
||||
|
||||
### ==========================================================================
|
||||
### Remove below comments to compile for a big-endian machine
|
||||
### ==========================================================================
|
||||
#GCCFLAGS += -DBIGENDIAN
|
||||
#ICCFLAGS += -DBIGENDIAN
|
||||
#ICCFLAGS-OSX += -DBIGENDIAN
|
||||
|
||||
|
||||
### ==========================================================================
|
||||
### Run built-in benchmark for pgo-builds with: 32MB hash 1 thread 10 depth
|
||||
### These settings are generally fast, but may be changed experimentally
|
||||
@@ -47,9 +58,9 @@ PGOBENCH = ./$(EXE) bench 32 1 10 default depth
|
||||
|
||||
|
||||
### General compiler settings. Do not change
|
||||
GCCFLAGS += -g -Wall -fno-exceptions -fno-rtti -fno-strict-aliasing
|
||||
ICCFLAGS += -g -Wall -fno-exceptions -fno-rtti -fno-strict-aliasing -wd383,869,981,10187,10188,11505,11503
|
||||
ICCFLAGS-OSX += -g -Wall -fno-exceptions -fno-rtti -fno-strict-aliasing -wd383,869,981,10187,10188,11505,11503
|
||||
GCCFLAGS += -g -Wall -fno-exceptions -fno-rtti
|
||||
ICCFLAGS += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
|
||||
ICCFLAGS-OSX += -g -Wall -fno-exceptions -fno-rtti -wd383,869,981,10187,10188,11505,11503
|
||||
|
||||
|
||||
### General linker settings. Do not change
|
||||
@@ -74,6 +85,7 @@ help:
|
||||
@echo "make > Default: Compiler = g++"
|
||||
@echo "make icc > Compiler = icpc"
|
||||
@echo "make icc-profile > Compiler = icpc + automatic pgo-build"
|
||||
@echo "make icc-profile-popcnt > Compiler = icpc + automatic pgo-build + popcnt-support"
|
||||
@echo "make osx-ppc32 > PPC-Mac OS X 32 bit. Compiler = g++"
|
||||
@echo "make osx-ppc64 > PPC-Mac OS X 64 bit. Compiler = g++"
|
||||
@echo "make osx-x86 > x86-Mac OS X 32 bit. Compiler = g++"
|
||||
@@ -133,6 +145,40 @@ icc-profile:
|
||||
$(MAKE) icc-profile-use
|
||||
@rm -rf profdir bench.txt
|
||||
|
||||
icc-profile-make-with-popcnt:
|
||||
$(MAKE) \
|
||||
CXX='icpc' \
|
||||
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
|
||||
CXXFLAGS+='-prof-gen=srcpos -prof_dir ./profdir' \
|
||||
all
|
||||
|
||||
icc-profile-use-with-popcnt:
|
||||
$(MAKE) \
|
||||
CXX='icpc' \
|
||||
CXXFLAGS="$(ICCFLAGS) -DUSE_POPCNT" \
|
||||
CXXFLAGS+='-prof_use -prof_dir ./profdir' \
|
||||
all
|
||||
|
||||
icc-profile-popcnt:
|
||||
@rm -rf profdir
|
||||
@mkdir profdir
|
||||
@touch *.cpp *.h
|
||||
$(MAKE) icc-profile-make
|
||||
@echo ""
|
||||
@echo "Running benchmark for pgo-build (popcnt disabled)..."
|
||||
@$(PGOBENCH) > /dev/null
|
||||
@touch *.cpp *.h
|
||||
$(MAKE) icc-profile-make-with-popcnt
|
||||
@echo ""
|
||||
@echo "Running benchmark for pgo-build (popcnt enabled)..."
|
||||
@$(PGOBENCH) > /dev/null
|
||||
@echo "Benchmarks finished. Build final executable now ..."
|
||||
@echo ""
|
||||
@touch *.cpp *.h
|
||||
$(MAKE) icc-profile-use-with-popcnt
|
||||
@rm -rf profdir bench.txt
|
||||
|
||||
|
||||
osx-ppc32:
|
||||
$(MAKE) \
|
||||
CXX='g++' \
|
||||
|
||||
@@ -104,7 +104,7 @@ void benchmark(const string& commandLine) {
|
||||
|
||||
if (limitType == "time")
|
||||
secsPerPos = val * 1000;
|
||||
else if (limitType == "depth")
|
||||
else if (limitType == "depth" || limitType == "perft")
|
||||
maxDepth = val;
|
||||
else
|
||||
maxNodes = val;
|
||||
@@ -153,7 +153,9 @@ void benchmark(const string& commandLine) {
|
||||
int dummy[2] = {0, 0};
|
||||
Position pos(*it);
|
||||
cerr << "\nBench position: " << cnt << '/' << positions.size() << endl << endl;
|
||||
if (!think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
|
||||
if (limitType == "perft")
|
||||
totalNodes += perft(pos, maxDepth * OnePly);
|
||||
else if (!think(pos, true, false, 0, dummy, dummy, 0, maxDepth, maxNodes, secsPerPos, moves))
|
||||
break;
|
||||
totalNodes += nodes_searched();
|
||||
}
|
||||
|
||||
@@ -317,8 +317,8 @@ Square pop_1st_bit(Bitboard* b) {
|
||||
|
||||
#elif !defined(USE_BSFQ)
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
static const int BitTable[64] = {
|
||||
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,
|
||||
@@ -336,30 +336,59 @@ union b_union {
|
||||
|
||||
Bitboard b;
|
||||
struct {
|
||||
#if defined (BIGENDIAN)
|
||||
uint32_t h;
|
||||
uint32_t l;
|
||||
#else
|
||||
uint32_t l;
|
||||
uint32_t h;
|
||||
#endif
|
||||
} dw;
|
||||
};
|
||||
|
||||
// WARNING: Needs -fno-strict-aliasing compiler option
|
||||
Square pop_1st_bit(Bitboard* bb) {
|
||||
|
||||
b_union u;
|
||||
b_union* u;
|
||||
Square ret;
|
||||
|
||||
u.b = *bb;
|
||||
u = (b_union*)bb;
|
||||
|
||||
if (u.dw.l)
|
||||
if (u->dw.l)
|
||||
{
|
||||
*((uint32_t*)bb) = u.dw.l & (u.dw.l - 1);
|
||||
return Square(BitTable[((u.dw.l ^ (u.dw.l - 1)) * 0x783a9b23) >> 26]);
|
||||
ret = Square(BitTable[((u->dw.l ^ (u->dw.l - 1)) * 0x783a9b23) >> 26]);
|
||||
u->dw.l &= (u->dw.l - 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*((uint32_t*)bb+1) = u.dw.h & (u.dw.h - 1); // Little endian only?
|
||||
return Square(BitTable[((~(u.dw.h ^ (u.dw.h - 1))) * 0x783a9b23) >> 26]);
|
||||
ret = Square(BitTable[((~(u->dw.h ^ (u->dw.h - 1))) * 0x783a9b23) >> 26]);
|
||||
u->dw.h &= (u->dw.h - 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int bitScanReverse32(uint32_t b)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (b > 0xFFFF) {
|
||||
b >>= 16;
|
||||
result += 16;
|
||||
}
|
||||
if (b > 0xFF) {
|
||||
b >>= 8;
|
||||
result += 8;
|
||||
}
|
||||
if (b > 0xF) {
|
||||
b >>= 4;
|
||||
result += 4;
|
||||
}
|
||||
if (b > 0x3) {
|
||||
b >>= 2;
|
||||
result += 2;
|
||||
}
|
||||
return result + (b > 0) + (b > 1);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// All functions below are used to precompute various bitboards during
|
||||
|
||||
@@ -349,6 +349,7 @@ extern Square pop_1st_bit(Bitboard* b);
|
||||
|
||||
extern void print_bitboard(Bitboard b);
|
||||
extern void init_bitboards();
|
||||
extern int bitScanReverse32(uint32_t b);
|
||||
|
||||
|
||||
#endif // !defined(BITBOARD_H_INCLUDED)
|
||||
|
||||
@@ -22,19 +22,12 @@
|
||||
#if !defined(BITCOUNT_H_INCLUDED)
|
||||
#define BITCOUNT_H_INCLUDED
|
||||
|
||||
// To enable POPCNT support uncomment USE_POPCNT define. For PGO compile on a Core i7
|
||||
// you may want to collect profile data first with USE_POPCNT disabled and then, in a
|
||||
// second profiling session, with USE_POPCNT enabled so to exercise both paths. Don't
|
||||
// forget to leave USE_POPCNT enabled for the final optimized compile though ;-)
|
||||
|
||||
//#define USE_POPCNT
|
||||
|
||||
|
||||
#include "types.h"
|
||||
|
||||
// Select type of intrinsic bit count instruction to use
|
||||
// Select type of intrinsic bit count instruction to use, see
|
||||
// README.txt on how to pgo compile with POPCNT support.
|
||||
|
||||
#if defined(__INTEL_COMPILER) && defined(IS_64BIT) && defined(USE_POPCNT) // Intel compiler
|
||||
#if defined(__INTEL_COMPILER) && defined(USE_POPCNT) // Intel compiler
|
||||
|
||||
#include <nmmintrin.h>
|
||||
|
||||
@@ -45,17 +38,9 @@ inline bool cpu_has_popcnt() {
|
||||
return (CPUInfo[2] >> 23) & 1;
|
||||
}
|
||||
|
||||
// Define a dummy template to workaround a compile error if _mm_popcnt_u64() is not defined.
|
||||
//
|
||||
// If _mm_popcnt_u64() is defined in <nmmintrin.h> it will be choosen first due to
|
||||
// C++ overload rules that always prefer a function to a template with the same name.
|
||||
// If not, we avoid a compile error and because cpu_has_popcnt() should return false,
|
||||
// our templetized _mm_popcnt_u64() is never called anyway.
|
||||
template<typename T> inline unsigned _mm_popcnt_u64(T) { return 0; } // Is never called
|
||||
|
||||
#define POPCNT_INTRINSIC(x) _mm_popcnt_u64(x)
|
||||
|
||||
#elif defined(_MSC_VER) && defined(IS_64BIT) && defined(USE_POPCNT) // Microsoft compiler
|
||||
#elif defined(_MSC_VER) && defined(USE_POPCNT) // Microsoft compiler
|
||||
|
||||
#include <intrin.h>
|
||||
|
||||
@@ -66,9 +51,6 @@ inline bool cpu_has_popcnt() {
|
||||
return (CPUInfo[2] >> 23) & 1;
|
||||
}
|
||||
|
||||
// See comment of _mm_popcnt_u64<>() few lines above for an explanation.
|
||||
template<typename T> inline unsigned __popcnt64(T) { return 0; } // Is never called
|
||||
|
||||
#define POPCNT_INTRINSIC(x) __popcnt64(x)
|
||||
|
||||
#else // Safe fallback for unsupported compilers or when USE_POPCNT is disabled
|
||||
|
||||
701
src/evaluate.cpp
701
src/evaluate.cpp
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,8 @@
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "material.h"
|
||||
#include "pawns.h"
|
||||
|
||||
@@ -46,7 +48,7 @@ class Position;
|
||||
struct EvalInfo {
|
||||
|
||||
// Middle game and endgame evaluations
|
||||
Value mgValue, egValue;
|
||||
Score value;
|
||||
|
||||
// Pointers to material and pawn hash table entries
|
||||
MaterialInfo* mi;
|
||||
@@ -89,7 +91,7 @@ struct EvalInfo {
|
||||
Move mateThreat[2];
|
||||
|
||||
// Middle game and endgame mobility scores.
|
||||
Value mgMobility, egMobility;
|
||||
Score mobility;
|
||||
|
||||
// Extra futility margin. This is added to the standard futility margin
|
||||
// in the quiescence search.
|
||||
|
||||
@@ -42,8 +42,6 @@ History::History() { clear(); }
|
||||
|
||||
void History::clear() {
|
||||
memset(history, 0, 2 * 8 * 64 * sizeof(int));
|
||||
memset(successCount, 0, 2 * 8 * 64 * sizeof(int));
|
||||
memset(failureCount, 0, 2 * 8 * 64 * sizeof(int));
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +56,6 @@ void History::success(Piece p, Square to, Depth d) {
|
||||
assert(square_is_ok(to));
|
||||
|
||||
history[p][to] += int(d) * int(d);
|
||||
successCount[p][to]++;
|
||||
|
||||
// Prevent history overflow
|
||||
if (history[p][to] >= HistoryMax)
|
||||
@@ -72,12 +69,14 @@ void History::success(Piece p, Square to, Depth d) {
|
||||
/// called for each non-capturing move which failed to produce a beta cutoff
|
||||
/// at a node where a beta cutoff was finally found.
|
||||
|
||||
void History::failure(Piece p, Square to) {
|
||||
void History::failure(Piece p, Square to, Depth d) {
|
||||
|
||||
assert(piece_is_ok(p));
|
||||
assert(square_is_ok(to));
|
||||
|
||||
failureCount[p][to]++;
|
||||
history[p][to] -= int(d) * int(d);
|
||||
if (history[p][to] < 0)
|
||||
history[p][to] = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -91,15 +90,3 @@ int History::move_ordering_score(Piece p, Square to) const {
|
||||
|
||||
return history[p][to];
|
||||
}
|
||||
|
||||
|
||||
/// History::ok_to_prune() decides whether a move has been sufficiently
|
||||
/// unsuccessful that it makes sense to prune it entirely.
|
||||
|
||||
bool History::ok_to_prune(Piece p, Square to, Depth d) const {
|
||||
|
||||
assert(piece_is_ok(p));
|
||||
assert(square_is_ok(to));
|
||||
|
||||
return (int(d) * successCount[p][to] < failureCount[p][to]);
|
||||
}
|
||||
|
||||
@@ -47,14 +47,11 @@ public:
|
||||
History();
|
||||
void clear();
|
||||
void success(Piece p, Square to, Depth d);
|
||||
void failure(Piece p, Square to);
|
||||
void failure(Piece p, Square to, Depth d);
|
||||
int move_ordering_score(Piece p, Square to) const;
|
||||
bool ok_to_prune(Piece p, Square to, Depth d) const;
|
||||
|
||||
private:
|
||||
int history[16][64]; // [piece][square]
|
||||
int successCount[16][64];
|
||||
int failureCount[16][64];
|
||||
};
|
||||
|
||||
|
||||
@@ -64,7 +61,7 @@ private:
|
||||
|
||||
/// HistoryMax controls how often the history counters will be scaled down:
|
||||
/// When the history score for a move gets bigger than HistoryMax, all
|
||||
/// entries in the table are divided by 2. It is difficult to guess what
|
||||
/// entries in the table are divided by 4. It is difficult to guess what
|
||||
/// the ideal value of this constant is. Scaling down the scores often has
|
||||
/// the effect that parts of the search tree which have been searched
|
||||
/// recently have a bigger importance for move ordering than the moves which
|
||||
|
||||
@@ -63,7 +63,7 @@ int main(int argc, char *argv[]) {
|
||||
if (string(argv[1]) != "bench" || argc < 4 || argc > 8)
|
||||
cout << "Usage: stockfish bench <hash size> <threads> "
|
||||
<< "[time = 60s] [fen positions file = default] "
|
||||
<< "[time, depth or node limited = time] "
|
||||
<< "[time, depth, perft or node limited = time] "
|
||||
<< "[timing file name = none]" << endl;
|
||||
else
|
||||
{
|
||||
|
||||
@@ -37,9 +37,14 @@ using namespace std;
|
||||
|
||||
namespace {
|
||||
|
||||
// Values modified by Joona Kiiski
|
||||
const Value MidgameLimit = Value(15581);
|
||||
const Value EndgameLimit = Value(3998);
|
||||
|
||||
// Polynomial material balance parameters
|
||||
const Value RedundantQueenPenalty = Value(320);
|
||||
const Value RedundantRookPenalty = Value(554);
|
||||
|
||||
const int LinearCoefficients[6] = { 1617, -162, -1172, -190, 105, 26 };
|
||||
|
||||
const int QuadraticCoefficientsSameColor[][6] = {
|
||||
@@ -129,6 +134,22 @@ MaterialInfoTable::~MaterialInfoTable() {
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfoTable::game_phase() calculates the phase given the current
|
||||
/// position. Because the phase is strictly a function of the material, it
|
||||
/// is stored in MaterialInfo.
|
||||
|
||||
Phase MaterialInfoTable::game_phase(const Position& pos) {
|
||||
|
||||
Value npm = pos.non_pawn_material(WHITE) + pos.non_pawn_material(BLACK);
|
||||
|
||||
if (npm >= MidgameLimit)
|
||||
return PHASE_MIDGAME;
|
||||
else if (npm <= EndgameLimit)
|
||||
return PHASE_ENDGAME;
|
||||
|
||||
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
|
||||
}
|
||||
|
||||
/// MaterialInfoTable::get_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
|
||||
@@ -151,6 +172,9 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
|
||||
mi->clear();
|
||||
mi->key = key;
|
||||
|
||||
// Store game phase
|
||||
mi->gamePhase = MaterialInfoTable::game_phase(pos);
|
||||
|
||||
// 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.
|
||||
@@ -269,8 +293,8 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
|
||||
{ pos.piece_count(BLACK, BISHOP) > 1, pos.piece_count(BLACK, PAWN), pos.piece_count(BLACK, KNIGHT),
|
||||
pos.piece_count(BLACK, BISHOP), pos.piece_count(BLACK, ROOK), pos.piece_count(BLACK, QUEEN) } };
|
||||
Color c, them;
|
||||
int sign;
|
||||
int matValue = 0;
|
||||
int sign, pt1, pt2, pc;
|
||||
int v, vv, matValue = 0;
|
||||
|
||||
for (c = WHITE, sign = 1; c <= BLACK; c++, sign = -sign)
|
||||
{
|
||||
@@ -304,25 +328,27 @@ MaterialInfo* MaterialInfoTable::get_material_info(const Position& pos) {
|
||||
matValue -= sign * ((pieceCount[c][ROOK] - 1) * RedundantRookPenalty + pieceCount[c][QUEEN] * RedundantQueenPenalty);
|
||||
|
||||
them = opposite_color(c);
|
||||
v = 0;
|
||||
|
||||
// Second-degree polynomial material imbalance by Tord Romstad
|
||||
//
|
||||
// We use NO_PIECE_TYPE as a place holder for the bishop pair "extended piece",
|
||||
// this allow us to be more flexible in defining bishop pair bonuses.
|
||||
for (int pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
|
||||
for (pt1 = NO_PIECE_TYPE; pt1 <= QUEEN; pt1++)
|
||||
{
|
||||
int c1 = sign * pieceCount[c][pt1];
|
||||
if (!c1)
|
||||
pc = pieceCount[c][pt1];
|
||||
if (!pc)
|
||||
continue;
|
||||
|
||||
matValue += c1 * LinearCoefficients[pt1];
|
||||
vv = LinearCoefficients[pt1];
|
||||
|
||||
for (int pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
|
||||
{
|
||||
matValue += c1 * pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2];
|
||||
matValue += c1 * pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
|
||||
}
|
||||
for (pt2 = NO_PIECE_TYPE; pt2 <= pt1; pt2++)
|
||||
vv += pieceCount[c][pt2] * QuadraticCoefficientsSameColor[pt1][pt2]
|
||||
+ pieceCount[them][pt2] * QuadraticCoefficientsOppositeColor[pt1][pt2];
|
||||
|
||||
v += pc * vv;
|
||||
}
|
||||
matValue += sign * v;
|
||||
}
|
||||
mi->value = int16_t(matValue / 16);
|
||||
return mi;
|
||||
|
||||
@@ -51,9 +51,10 @@ class MaterialInfo {
|
||||
public:
|
||||
MaterialInfo() : key(0) { clear(); }
|
||||
|
||||
Value material_value() const;
|
||||
Score material_value() const;
|
||||
ScaleFactor scale_factor(const Position& pos, Color c) const;
|
||||
int space_weight() const;
|
||||
Phase game_phase() const;
|
||||
bool specialized_eval_exists() const;
|
||||
Value evaluate(const Position& pos) const;
|
||||
|
||||
@@ -66,6 +67,7 @@ private:
|
||||
EndgameEvaluationFunctionBase* evaluationFunction;
|
||||
EndgameScalingFunctionBase* scalingFunction[2];
|
||||
int spaceWeight;
|
||||
Phase gamePhase;
|
||||
};
|
||||
|
||||
/// The MaterialInfoTable class represents a pawn hash table. It is basically
|
||||
@@ -81,6 +83,8 @@ public:
|
||||
~MaterialInfoTable();
|
||||
MaterialInfo* get_material_info(const Position& pos);
|
||||
|
||||
static Phase game_phase(const Position& pos);
|
||||
|
||||
private:
|
||||
unsigned size;
|
||||
MaterialInfo* entries;
|
||||
@@ -92,12 +96,13 @@ private:
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
|
||||
/// MaterialInfo::material_value simply returns the material balance
|
||||
/// evaluation that is independent from game phase.
|
||||
|
||||
inline Value MaterialInfo::material_value() const {
|
||||
inline Score MaterialInfo::material_value() const {
|
||||
|
||||
return Value(value);
|
||||
return make_score(value, value);
|
||||
}
|
||||
|
||||
|
||||
@@ -141,6 +146,14 @@ inline int MaterialInfo::space_weight() const {
|
||||
return spaceWeight;
|
||||
}
|
||||
|
||||
/// MaterialInfo::game_phase() returns the game phase according
|
||||
/// to this material configuration.
|
||||
|
||||
inline Phase MaterialInfo::game_phase() const {
|
||||
|
||||
return gamePhase;
|
||||
}
|
||||
|
||||
|
||||
/// MaterialInfo::specialized_eval_exists decides whether there is a
|
||||
/// specialized evaluation function for the current material configuration,
|
||||
|
||||
@@ -50,7 +50,7 @@ using namespace std;
|
||||
/// Version number. If this is left empty, the current date (in the format
|
||||
/// YYMMDD) is used as a version number.
|
||||
|
||||
static const string EngineVersion = "1.5.1";
|
||||
static const string EngineVersion = "1.6.1";
|
||||
static const string AppName = "Stockfish";
|
||||
static const string AppTag = "";
|
||||
|
||||
|
||||
100
src/move.h
100
src/move.h
@@ -62,9 +62,103 @@ struct MoveStack {
|
||||
int score;
|
||||
};
|
||||
|
||||
// Note that operator< is set up such that std::sort() will sort in descending order
|
||||
// Note that operator< is set up such that sorting will be in descending order
|
||||
inline bool operator<(const MoveStack& f, const MoveStack& s) { return s.score < f.score; }
|
||||
|
||||
// An helper insertion sort implementation
|
||||
template<typename T>
|
||||
inline void insertion_sort(T* firstMove, T* lastMove)
|
||||
{
|
||||
T value;
|
||||
T *cur, *p, *d;
|
||||
|
||||
if (firstMove != lastMove)
|
||||
for (cur = firstMove + 1; cur != lastMove; cur++)
|
||||
{
|
||||
p = d = cur;
|
||||
value = *p--;
|
||||
if (value < *p)
|
||||
{
|
||||
do *d = *p;
|
||||
while (--d != firstMove && value < *--p);
|
||||
*d = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Our dedicated sort in range [firstMove, lastMove), it is well
|
||||
// tuned for non-captures where we have a lot of zero scored moves.
|
||||
template<typename T>
|
||||
inline void sort_moves(T* firstMove, T* lastMove)
|
||||
{
|
||||
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 positives
|
||||
insertion_sort<T>(firstMove, p);
|
||||
|
||||
d = lastMove;
|
||||
p--;
|
||||
|
||||
// Split zero vs negatives
|
||||
do {
|
||||
while ((++p)->score == 0);
|
||||
|
||||
if (p != d)
|
||||
{
|
||||
while (--d != p && d->score < 0);
|
||||
|
||||
tmp = *p;
|
||||
*p = *d;
|
||||
*d = tmp;
|
||||
}
|
||||
|
||||
} while (p != d);
|
||||
|
||||
// Sort negatives
|
||||
insertion_sort<T>(p, lastMove);
|
||||
}
|
||||
|
||||
// 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 (*curMove < bestMove)
|
||||
{
|
||||
tmp = *curMove;
|
||||
*curMove = bestMove;
|
||||
bestMove = tmp;
|
||||
}
|
||||
}
|
||||
return bestMove;
|
||||
}
|
||||
|
||||
////
|
||||
//// Inline functions
|
||||
@@ -82,6 +176,10 @@ inline PieceType move_promotion_piece(Move m) {
|
||||
return PieceType((int(m) >> 12) & 7);
|
||||
}
|
||||
|
||||
inline int move_is_special(Move m) {
|
||||
return m & (0x1F << 12);
|
||||
}
|
||||
|
||||
inline int move_is_promotion(Move m) {
|
||||
return m & (7 << 12);
|
||||
}
|
||||
|
||||
541
src/movegen.cpp
541
src/movegen.cpp
@@ -52,16 +52,12 @@ namespace {
|
||||
EVASION
|
||||
};
|
||||
|
||||
// Functions
|
||||
bool castling_is_check(const Position&, CastlingSide);
|
||||
|
||||
// Helper templates
|
||||
template<CastlingSide Side>
|
||||
MoveStack* generate_castle_moves(const Position&, MoveStack*);
|
||||
|
||||
template<Color Us, MoveType Type>
|
||||
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard = EmptyBoardBB,
|
||||
Square = SQ_NONE, Bitboard = EmptyBoardBB);
|
||||
MoveStack* generate_pawn_moves(const Position&, MoveStack*, Bitboard, Square);
|
||||
|
||||
// Template generate_piece_moves (captures and non-captures) with specializations and overloads
|
||||
template<PieceType>
|
||||
@@ -71,37 +67,29 @@ namespace {
|
||||
MoveStack* generate_piece_moves<KING>(const Position&, MoveStack*, Color, Bitboard);
|
||||
|
||||
template<PieceType Piece, MoveType Type>
|
||||
inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us) {
|
||||
inline MoveStack* generate_piece_moves(const Position& p, MoveStack* m, Color us, Bitboard t) {
|
||||
|
||||
assert(Piece == PAWN);
|
||||
assert(Type == CAPTURE || Type == NON_CAPTURE);
|
||||
assert(Type == CAPTURE || Type == NON_CAPTURE || Type == EVASION);
|
||||
|
||||
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m)
|
||||
: generate_pawn_moves<BLACK, Type>(p, m));
|
||||
return (us == WHITE ? generate_pawn_moves<WHITE, Type>(p, m, t, SQ_NONE)
|
||||
: generate_pawn_moves<BLACK, Type>(p, m, t, SQ_NONE));
|
||||
}
|
||||
|
||||
// Template generate_piece_checks with specializations
|
||||
// Templates for non-capture checks generation
|
||||
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_discovered_checks(const Position&, MoveStack*, Square);
|
||||
|
||||
template<PieceType>
|
||||
MoveStack* generate_piece_checks(const Position&, MoveStack*, Color, Bitboard, Square);
|
||||
MoveStack* generate_direct_checks(const Position&, MoveStack*, Color, Bitboard, Square);
|
||||
|
||||
template<>
|
||||
inline MoveStack* generate_piece_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
|
||||
inline MoveStack* generate_direct_checks<PAWN>(const Position& p, MoveStack* m, Color us, Bitboard dc, Square ksq) {
|
||||
|
||||
return (us == WHITE ? generate_pawn_moves<WHITE, CHECK>(p, m, dc, ksq)
|
||||
: generate_pawn_moves<BLACK, CHECK>(p, m, dc, ksq));
|
||||
}
|
||||
|
||||
// Template generate_piece_evasions with specializations
|
||||
template<PieceType>
|
||||
MoveStack* generate_piece_evasions(const Position&, MoveStack*, Color, Bitboard, Bitboard);
|
||||
|
||||
template<>
|
||||
inline MoveStack* generate_piece_evasions<PAWN>(const Position& p, MoveStack* m,
|
||||
Color us, Bitboard t, Bitboard pnd) {
|
||||
|
||||
return (us == WHITE ? generate_pawn_moves<WHITE, EVASION>(p, m, pnd, SQ_NONE, t)
|
||||
: generate_pawn_moves<BLACK, EVASION>(p, m, pnd, SQ_NONE, t));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +113,7 @@ MoveStack* generate_captures(const Position& pos, MoveStack* mlist) {
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us);
|
||||
mlist = generate_piece_moves<PAWN, CAPTURE>(pos, mlist, us, target);
|
||||
return generate_piece_moves<KING>(pos, mlist, us, target);
|
||||
}
|
||||
|
||||
@@ -141,7 +129,7 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
|
||||
Color us = pos.side_to_move();
|
||||
Bitboard target = pos.empty_squares();
|
||||
|
||||
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us);
|
||||
mlist = generate_piece_moves<PAWN, NON_CAPTURE>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
@@ -152,175 +140,111 @@ MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist) {
|
||||
}
|
||||
|
||||
|
||||
/// generate_non_capture_checks() generates all pseudo-legal non-captures and
|
||||
/// generate_non_capture_checks() generates all pseudo-legal non-captures and knight
|
||||
/// underpromotions that give check. Returns a pointer to the end of the move list.
|
||||
|
||||
MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc) {
|
||||
MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(!pos.is_check());
|
||||
|
||||
Bitboard b, dc;
|
||||
Square from;
|
||||
Color us = pos.side_to_move();
|
||||
Square ksq = pos.king_square(opposite_color(us));
|
||||
|
||||
assert(pos.piece_on(ksq) == piece_of_color_and_type(opposite_color(us), KING));
|
||||
|
||||
// Pieces moves
|
||||
mlist = generate_piece_checks<PAWN>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_piece_checks<KNIGHT>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_piece_checks<BISHOP>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_piece_checks<ROOK>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_piece_checks<QUEEN>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_piece_checks<KING>(pos, mlist, us, dc, ksq);
|
||||
// Discovered non-capture checks
|
||||
b = dc = pos.discovered_check_candidates(us);
|
||||
|
||||
// Castling moves that give check. Very rare but nice to have!
|
||||
if ( pos.can_castle_queenside(us)
|
||||
&& (square_rank(ksq) == square_rank(pos.king_square(us)) || square_file(ksq) == FILE_D)
|
||||
&& castling_is_check(pos, QUEEN_SIDE))
|
||||
mlist = generate_castle_moves<QUEEN_SIDE>(pos, mlist);
|
||||
while (b)
|
||||
{
|
||||
from = pop_1st_bit(&b);
|
||||
switch (pos.type_of_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;
|
||||
case BISHOP: mlist = generate_discovered_checks<BISHOP>(pos, mlist, from); break;
|
||||
case ROOK: mlist = generate_discovered_checks<ROOK>(pos, mlist, from); break;
|
||||
case KING: mlist = generate_discovered_checks<KING>(pos, mlist, from); break;
|
||||
default: assert(false); break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( pos.can_castle_kingside(us)
|
||||
&& (square_rank(ksq) == square_rank(pos.king_square(us)) || square_file(ksq) == FILE_F)
|
||||
&& castling_is_check(pos, KING_SIDE))
|
||||
mlist = generate_castle_moves<KING_SIDE>(pos, mlist);
|
||||
|
||||
return mlist;
|
||||
// Direct non-capture checks
|
||||
mlist = generate_direct_checks<PAWN>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_direct_checks<KNIGHT>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_direct_checks<BISHOP>(pos, mlist, us, dc, ksq);
|
||||
mlist = generate_direct_checks<ROOK>(pos, mlist, us, dc, ksq);
|
||||
return generate_direct_checks<QUEEN>(pos, mlist, us, dc, ksq);
|
||||
}
|
||||
|
||||
|
||||
/// generate_evasions() generates all check evasions when the side to move is
|
||||
/// in check. Unlike the other move generation functions, this one generates
|
||||
/// only legal moves. Returns a pointer to the end of the move list.
|
||||
/// 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.
|
||||
|
||||
MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned) {
|
||||
MoveStack* generate_evasions(const Position& pos, MoveStack* mlist) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(pos.is_check());
|
||||
|
||||
Square from, to;
|
||||
Bitboard b, target;
|
||||
Square from, checksq;
|
||||
int checkersCnt = 0;
|
||||
Color us = pos.side_to_move();
|
||||
Color them = opposite_color(us);
|
||||
Square ksq = pos.king_square(us);
|
||||
Bitboard sliderAttacks = EmptyBoardBB;
|
||||
Bitboard checkers = pos.checkers();
|
||||
Bitboard sliderAttacks = EmptyBoardBB;
|
||||
|
||||
assert(pos.piece_on(ksq) == piece_of_color_and_type(us, KING));
|
||||
|
||||
// The bitboard of occupied pieces without our king
|
||||
Bitboard b_noKing = pos.occupied_squares();
|
||||
clear_bit(&b_noKing, ksq);
|
||||
assert(checkers);
|
||||
|
||||
// Find squares attacked by slider checkers, we will remove
|
||||
// them from the king evasions set so to avoid a couple
|
||||
// of cycles in the slow king evasions legality check loop
|
||||
// and to be able to use attackers_to().
|
||||
Bitboard b = checkers & pos.pieces(BISHOP, QUEEN);
|
||||
while (b)
|
||||
// them from the king evasions set so to early skip known
|
||||
// illegal moves and avoid an useless legality check later.
|
||||
b = checkers;
|
||||
do
|
||||
{
|
||||
from = pop_1st_bit(&b);
|
||||
sliderAttacks |= bishop_attacks_bb(from, b_noKing);
|
||||
}
|
||||
checkersCnt++;
|
||||
checksq = pop_1st_bit(&b);
|
||||
|
||||
b = checkers & pos.pieces(ROOK, QUEEN);
|
||||
while (b)
|
||||
assert(pos.color_of_piece_on(checksq) == opposite_color(us));
|
||||
|
||||
switch (pos.type_of_piece_on(checksq))
|
||||
{
|
||||
from = pop_1st_bit(&b);
|
||||
sliderAttacks |= rook_attacks_bb(from, b_noKing);
|
||||
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 (direction_is_straight(checksq, ksq))
|
||||
sliderAttacks |= RookPseudoAttacks[checksq] | pos.attacks_from<BISHOP>(checksq);
|
||||
else
|
||||
sliderAttacks |= BishopPseudoAttacks[checksq] | pos.attacks_from<ROOK>(checksq);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} while (b);
|
||||
|
||||
// Generate evasions for king, capture and non capture moves
|
||||
Bitboard enemy = pos.pieces_of_color(them);
|
||||
Bitboard b1 = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
|
||||
while (b1)
|
||||
{
|
||||
// Note that we can use attackers_to() only because we have already
|
||||
// removed from b1 the squares attacked by slider checkers.
|
||||
to = pop_1st_bit(&b1);
|
||||
if (!(pos.attackers_to(to) & enemy))
|
||||
(*mlist++).move = make_move(ksq, to);
|
||||
}
|
||||
b = pos.attacks_from<KING>(ksq) & ~pos.pieces_of_color(us) & ~sliderAttacks;
|
||||
from = ksq;
|
||||
SERIALIZE_MOVES(b);
|
||||
|
||||
// Generate evasions for other pieces only if not double check. We use a
|
||||
// simple bit twiddling hack here rather than calling count_1s in order to
|
||||
// save some time (we know that pos.checkers() has at most two nonzero bits).
|
||||
if (!(checkers & (checkers - 1))) // Only one bit set?
|
||||
{
|
||||
Square checksq = first_1(checkers);
|
||||
|
||||
assert(pos.color_of_piece_on(checksq) == them);
|
||||
|
||||
// Generate captures of the checking piece
|
||||
|
||||
// Pawn captures
|
||||
b1 = pos.attacks_from<PAWN>(checksq, them) & pos.pieces(PAWN, us) & ~pinned;
|
||||
while (b1)
|
||||
{
|
||||
from = pop_1st_bit(&b1);
|
||||
if (relative_rank(us, checksq) == RANK_8)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(from, checksq, QUEEN);
|
||||
(*mlist++).move = make_promotion_move(from, checksq, ROOK);
|
||||
(*mlist++).move = make_promotion_move(from, checksq, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(from, checksq, KNIGHT);
|
||||
} else
|
||||
(*mlist++).move = make_move(from, checksq);
|
||||
}
|
||||
|
||||
// Pieces captures
|
||||
b1 = ( (pos.attacks_from<KNIGHT>(checksq) & pos.pieces(KNIGHT, us))
|
||||
| (pos.attacks_from<BISHOP>(checksq) & pos.pieces(BISHOP, QUEEN, us))
|
||||
| (pos.attacks_from<ROOK>(checksq) & pos.pieces(ROOK, QUEEN, us)) ) & ~pinned;
|
||||
|
||||
while (b1)
|
||||
{
|
||||
from = pop_1st_bit(&b1);
|
||||
(*mlist++).move = make_move(from, checksq);
|
||||
}
|
||||
|
||||
// Blocking check evasions are possible only if the checking piece is a slider
|
||||
if (sliderAttacks)
|
||||
{
|
||||
Bitboard blockSquares = squares_between(checksq, ksq);
|
||||
|
||||
assert((pos.occupied_squares() & blockSquares) == EmptyBoardBB);
|
||||
|
||||
if (blockSquares)
|
||||
{
|
||||
mlist = generate_piece_evasions<PAWN>(pos, mlist, us, blockSquares, pinned);
|
||||
mlist = generate_piece_evasions<KNIGHT>(pos, mlist, us, blockSquares, pinned);
|
||||
mlist = generate_piece_evasions<BISHOP>(pos, mlist, us, blockSquares, pinned);
|
||||
mlist = generate_piece_evasions<ROOK>(pos, mlist, us, blockSquares, pinned);
|
||||
mlist = generate_piece_evasions<QUEEN>(pos, mlist, us, blockSquares, pinned);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, the special case of en passant captures. An en passant
|
||||
// capture can only be a check evasion if the check is not a discovered
|
||||
// check. If pos.ep_square() is set, the last move made must have been
|
||||
// a double pawn push. If, furthermore, the checking piece is a pawn,
|
||||
// an en passant check evasion may be possible.
|
||||
if (pos.ep_square() != SQ_NONE && (checkers & pos.pieces(PAWN, them)))
|
||||
{
|
||||
to = pos.ep_square();
|
||||
b1 = pos.attacks_from<PAWN>(to, them) & pos.pieces(PAWN, us);
|
||||
|
||||
// The checking pawn cannot be a discovered (bishop) check candidate
|
||||
// otherwise we were in check also before last double push move.
|
||||
assert(!bit_is_set(pos.discovered_check_candidates(them), checksq));
|
||||
assert(count_1s(b1) == 1 || count_1s(b1) == 2);
|
||||
|
||||
b1 &= ~pinned;
|
||||
while (b1)
|
||||
{
|
||||
from = pop_1st_bit(&b1);
|
||||
// Move is always legal because checking pawn is not a discovered
|
||||
// check candidate and our capturing pawn has been already tested
|
||||
// against pinned pieces.
|
||||
(*mlist++).move = make_ep_move(from, to);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Generate evasions for other pieces only if not double check
|
||||
if (checkersCnt > 1)
|
||||
return mlist;
|
||||
|
||||
// Find squares where a blocking evasion or a capture of the
|
||||
// checker piece is possible.
|
||||
target = squares_between(checksq, ksq) | checkers;
|
||||
|
||||
mlist = generate_piece_moves<PAWN, EVASION>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<KNIGHT>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<BISHOP>(pos, mlist, us, target);
|
||||
mlist = generate_piece_moves<ROOK>(pos, mlist, us, target);
|
||||
return generate_piece_moves<QUEEN>(pos, mlist, us, target);
|
||||
}
|
||||
|
||||
|
||||
@@ -332,24 +256,25 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
|
||||
|
||||
assert(pos.is_ok());
|
||||
|
||||
MoveStack *last, *cur = mlist;
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
|
||||
if (pos.is_check())
|
||||
return generate_evasions(pos, mlist, pinned);
|
||||
|
||||
// Generate pseudo-legal moves
|
||||
MoveStack* last = generate_captures(pos, mlist);
|
||||
last = generate_noncaptures(pos, last);
|
||||
if (pos.is_check())
|
||||
last = generate_evasions(pos, mlist);
|
||||
else
|
||||
last = generate_noncaptures(pos, generate_captures(pos, mlist));
|
||||
|
||||
if (pseudoLegal)
|
||||
return last;
|
||||
|
||||
// Remove illegal moves from the list
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
if (!pos.pl_move_is_legal(cur->move, pinned))
|
||||
{
|
||||
while (cur != last)
|
||||
if (pos.pl_move_is_legal(cur->move, pinned))
|
||||
cur++;
|
||||
else
|
||||
cur->move = (--last)->move;
|
||||
cur--;
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
@@ -361,10 +286,11 @@ MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLega
|
||||
bool move_is_legal(const Position& pos, const Move m) {
|
||||
|
||||
MoveStack mlist[256];
|
||||
MoveStack* last = generate_moves(pos, mlist, true);
|
||||
for (MoveStack* cur = mlist; cur != last; cur++)
|
||||
MoveStack *cur, *last = generate_moves(pos, mlist, true);
|
||||
|
||||
for (cur = mlist; cur != last; cur++)
|
||||
if (cur->move == m)
|
||||
return pos.pl_move_is_legal(m);
|
||||
return pos.pl_move_is_legal(m, pos.pinned_pieces(pos.side_to_move()));
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -372,25 +298,23 @@ bool move_is_legal(const Position& pos, const Move m) {
|
||||
|
||||
/// Fast version of move_is_legal() that takes a position a move and a
|
||||
/// bitboard of pinned pieces as input, and tests whether the move is legal.
|
||||
/// This version must only be used when the side to move is not in check.
|
||||
|
||||
bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
|
||||
|
||||
assert(pos.is_ok());
|
||||
assert(!pos.is_check());
|
||||
assert(move_is_ok(m));
|
||||
assert(pinned == pos.pinned_pieces(pos.side_to_move()));
|
||||
|
||||
// Use a slower but simpler function for uncommon cases
|
||||
if (move_is_ep(m) || move_is_castle(m))
|
||||
return move_is_legal(pos, m);
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Color them = opposite_color(us);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Piece pc = pos.piece_on(from);
|
||||
|
||||
// Use a slower but simpler function for uncommon cases
|
||||
if (move_is_ep(m) || move_is_castle(m))
|
||||
return move_is_legal(pos, m);
|
||||
|
||||
// If the from square is not occupied by a piece belonging to the side to
|
||||
// move, the move is obviously not legal.
|
||||
if (color_of_piece(pc) != us)
|
||||
@@ -459,13 +383,13 @@ bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) {
|
||||
return false;
|
||||
}
|
||||
// The move is pseudo-legal, check if it is also legal
|
||||
return pos.pl_move_is_legal(m, pinned);
|
||||
return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned);
|
||||
}
|
||||
|
||||
// Luckly we can handle all the other pieces in one go
|
||||
return ( bit_is_set(pos.attacks_from(pc, from), to)
|
||||
&& pos.pl_move_is_legal(m, pinned)
|
||||
&& !move_is_promotion(m));
|
||||
return bit_is_set(pos.attacks_from(pc, from), to)
|
||||
&& (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned))
|
||||
&& !move_is_promotion(m);
|
||||
}
|
||||
|
||||
|
||||
@@ -474,8 +398,8 @@ namespace {
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_piece_moves(const Position& pos, MoveStack* mlist, Color us, Bitboard target) {
|
||||
|
||||
Square from;
|
||||
Bitboard b;
|
||||
Square from;
|
||||
const Square* ptr = pos.piece_list_begin(us, Piece);
|
||||
|
||||
while ((from = *ptr++) != SQ_NONE)
|
||||
@@ -497,24 +421,6 @@ namespace {
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_piece_evasions(const Position& pos, MoveStack* mlist,
|
||||
Color us, Bitboard target, Bitboard pinned) {
|
||||
Square from;
|
||||
Bitboard b;
|
||||
const Square* ptr = pos.piece_list_begin(us, Piece);
|
||||
|
||||
while ((from = *ptr++) != SQ_NONE)
|
||||
{
|
||||
if (pinned && bit_is_set(pinned, from))
|
||||
continue;
|
||||
|
||||
b = pos.attacks_from<Piece>(from) & target;
|
||||
SERIALIZE_MOVES(b);
|
||||
}
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<Color Us, SquareDelta Direction>
|
||||
inline Bitboard move_pawns(Bitboard p) {
|
||||
|
||||
@@ -528,8 +434,8 @@ namespace {
|
||||
return p;
|
||||
}
|
||||
|
||||
template<Color Us, SquareDelta Diagonal>
|
||||
MoveStack* generate_pawn_diagonal_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces, bool promotion) {
|
||||
template<Color Us, MoveType Type, SquareDelta Diagonal>
|
||||
inline MoveStack* generate_pawn_captures(MoveStack* mlist, Bitboard pawns, Bitboard enemyPieces) {
|
||||
|
||||
// Calculate our parametrized parameters at compile time
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
@@ -538,136 +444,152 @@ namespace {
|
||||
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
|
||||
const SquareDelta TTDELTA_NE = (Diagonal == DELTA_NE ? TDELTA_NE : TDELTA_NW);
|
||||
|
||||
Bitboard b1, b2;
|
||||
Square to;
|
||||
|
||||
// Captures in the a1-h8 (a8-h1 for black) diagonal or in the h1-a8 (h8-a1 for black)
|
||||
Bitboard b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
|
||||
b1 = move_pawns<Us, Diagonal>(pawns) & ~TFileABB & enemyPieces;
|
||||
|
||||
// Capturing promotions
|
||||
if (promotion)
|
||||
// Capturing promotions and under-promotions
|
||||
if (b1 & TRank8BB)
|
||||
{
|
||||
Bitboard b2 = b1 & TRank8BB;
|
||||
b2 = b1 & TRank8BB;
|
||||
b1 &= ~TRank8BB;
|
||||
while (b2)
|
||||
{
|
||||
to = pop_1st_bit(&b2);
|
||||
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, QUEEN);
|
||||
|
||||
if (Type == NON_CAPTURE || Type == EVASION)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
|
||||
}
|
||||
|
||||
// This is the only possible under promotion that can give a check
|
||||
// not already included in the queen-promotion. It is not sure that
|
||||
// the promoted knight will give check, but it doesn't worth to verify.
|
||||
if (Type == CHECK)
|
||||
(*mlist++).move = make_promotion_move(to - TTDELTA_NE, to, KNIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Capturing non-promotions
|
||||
// Serialize standard captures
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
SERIALIZE_MOVES_D(b1, -TTDELTA_NE);
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
template<Color Us, MoveType Type>
|
||||
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard dcp,
|
||||
Square ksq, Bitboard blockSquares) {
|
||||
MoveStack* generate_pawn_moves(const Position& pos, MoveStack* mlist, Bitboard target, Square ksq) {
|
||||
|
||||
// Calculate our parametrized parameters at compile time
|
||||
const Color Them = (Us == WHITE ? BLACK : WHITE);
|
||||
const Bitboard TRank8BB = (Us == WHITE ? Rank8BB : Rank1BB);
|
||||
const Bitboard TRank7BB = (Us == WHITE ? Rank7BB : Rank2BB);
|
||||
const Bitboard TRank3BB = (Us == WHITE ? Rank3BB : Rank6BB);
|
||||
const SquareDelta TDELTA_NE = (Us == WHITE ? DELTA_NE : DELTA_SE);
|
||||
const SquareDelta TDELTA_NW = (Us == WHITE ? DELTA_NW : DELTA_SW);
|
||||
const SquareDelta TDELTA_N = (Us == WHITE ? DELTA_N : DELTA_S);
|
||||
|
||||
Bitboard b1, b2, dcPawns1, dcPawns2;
|
||||
Square to;
|
||||
Bitboard pawns = (Type == EVASION ? pos.pieces(PAWN, Us) & ~dcp : pos.pieces(PAWN, Us));
|
||||
bool possiblePromotion = pawns & TRank7BB;
|
||||
Bitboard b1, b2, enemyPieces, emptySquares;
|
||||
Bitboard pawns = pos.pieces(PAWN, Us);
|
||||
|
||||
if (Type == CAPTURE)
|
||||
// Standard captures and capturing promotions and underpromotions
|
||||
if (Type == CAPTURE || Type == EVASION || (pawns & TRank7BB))
|
||||
{
|
||||
// Standard captures and capturing promotions in both directions
|
||||
Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us));
|
||||
mlist = generate_pawn_diagonal_captures<Us, DELTA_NE>(mlist, pawns, enemyPieces, possiblePromotion);
|
||||
mlist = generate_pawn_diagonal_captures<Us, DELTA_NW>(mlist, pawns, enemyPieces, possiblePromotion);
|
||||
enemyPieces = (Type == CAPTURE ? target : pos.pieces_of_color(opposite_color(Us)));
|
||||
|
||||
if (Type == EVASION)
|
||||
enemyPieces &= target; // Capture only the checker piece
|
||||
|
||||
mlist = generate_pawn_captures<Us, Type, DELTA_NE>(mlist, pawns, enemyPieces);
|
||||
mlist = generate_pawn_captures<Us, Type, DELTA_NW>(mlist, pawns, enemyPieces);
|
||||
}
|
||||
|
||||
if (possiblePromotion)
|
||||
// Non-capturing promotions and underpromotions
|
||||
if (pawns & TRank7BB)
|
||||
{
|
||||
// When generating checks consider under-promotion moves (both captures
|
||||
// and non captures) only if can give a discovery check. Note that dcp
|
||||
// is dc bitboard or pinned bitboard when Type == EVASION.
|
||||
Bitboard pp = (Type == CHECK ? pawns & dcp : pawns);
|
||||
b1 = move_pawns<Us, DELTA_N>(pawns) & TRank8BB & pos.empty_squares();
|
||||
|
||||
if (Type != EVASION && Type != CAPTURE)
|
||||
{
|
||||
Bitboard enemyPieces = pos.pieces_of_color(opposite_color(Us));
|
||||
|
||||
// Underpromotion captures in the a1-h8 (a8-h1 for black) direction
|
||||
b1 = move_pawns<Us, DELTA_NE>(pp) & ~FileABB & enemyPieces & TRank8BB;
|
||||
while (b1)
|
||||
{
|
||||
to = pop_1st_bit(&b1);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_NE, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_NE, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_NE, to, KNIGHT);
|
||||
}
|
||||
|
||||
// Underpromotion captures in the h1-a8 (h8-a1 for black) direction
|
||||
b1 = move_pawns<Us, DELTA_NW>(pp) & ~FileHBB & enemyPieces & TRank8BB;
|
||||
while (b1)
|
||||
{
|
||||
to = pop_1st_bit(&b1);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_NW, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_NW, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_NW, to, KNIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Underpromotion pawn pushes. Also queen promotions for evasions and captures.
|
||||
b1 = move_pawns<Us, DELTA_N>(pp) & TRank8BB;
|
||||
b1 &= (Type == EVASION ? blockSquares : pos.empty_squares());
|
||||
if (Type == EVASION)
|
||||
b1 &= target; // Only blocking promotion pushes
|
||||
|
||||
while (b1)
|
||||
{
|
||||
to = pop_1st_bit(&b1);
|
||||
if (Type == EVASION || Type == CAPTURE)
|
||||
|
||||
if (Type == CAPTURE || Type == EVASION)
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, QUEEN);
|
||||
|
||||
if (Type != CAPTURE)
|
||||
if (Type == NON_CAPTURE || Type == EVASION)
|
||||
{
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, ROOK);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, BISHOP);
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
|
||||
}
|
||||
|
||||
// This is the only possible under promotion that can give a check
|
||||
// not already included in the queen-promotion.
|
||||
if (Type == CHECK && bit_is_set(pos.attacks_from<KNIGHT>(to), pos.king_square(Them)))
|
||||
(*mlist++).move = make_promotion_move(to - TDELTA_N, to, KNIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
// Standard pawn pushes and double pushes
|
||||
if (Type != CAPTURE)
|
||||
{
|
||||
Bitboard emptySquares = pos.empty_squares();
|
||||
dcPawns1 = dcPawns2 = EmptyBoardBB;
|
||||
if (Type == CHECK && (pawns & dcp))
|
||||
{
|
||||
// Pawn moves which gives discovered check. This is possible only if the
|
||||
// pawn is not on the same file as the enemy king, because we don't
|
||||
// generate captures.
|
||||
dcPawns1 = move_pawns<Us, DELTA_N>(pawns & dcp & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
|
||||
dcPawns2 = move_pawns<Us, DELTA_N>(dcPawns1 & TRank3BB) & emptySquares;
|
||||
}
|
||||
emptySquares = (Type == NON_CAPTURE ? target : pos.empty_squares());
|
||||
|
||||
// Single pawn pushes
|
||||
// Single and double pawn pushes
|
||||
b1 = move_pawns<Us, DELTA_N>(pawns) & emptySquares & ~TRank8BB;
|
||||
b2 = (Type == CHECK ? (b1 & pos.attacks_from<PAWN>(ksq, Them)) | dcPawns1 :
|
||||
(Type == EVASION ? b1 & blockSquares : b1));
|
||||
SERIALIZE_MOVES_D(b2, -TDELTA_N);
|
||||
b2 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
|
||||
|
||||
// Double pawn pushes
|
||||
b1 = move_pawns<Us, DELTA_N>(b1 & TRank3BB) & emptySquares;
|
||||
b2 = (Type == CHECK ? (b1 & pos.attacks_from<PAWN>(ksq, Them)) | dcPawns2 :
|
||||
(Type == EVASION ? b1 & blockSquares : b1));
|
||||
// Filter out unwanted pushes according to the move type
|
||||
if (Type == EVASION)
|
||||
{
|
||||
b1 &= target;
|
||||
b2 &= target;
|
||||
}
|
||||
else if (Type == CHECK)
|
||||
{
|
||||
// Pawn moves which give direct cheks
|
||||
b1 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
b2 &= pos.attacks_from<PAWN>(ksq, Them);
|
||||
|
||||
// Pawn moves which gives discovered check. This is possible only if
|
||||
// the pawn is not on the same file as the enemy king, because we
|
||||
// don't generate captures.
|
||||
if (pawns & target) // For CHECK type target is dc bitboard
|
||||
{
|
||||
Bitboard dc1 = move_pawns<Us, DELTA_N>(pawns & target & ~file_bb(ksq)) & emptySquares & ~TRank8BB;
|
||||
Bitboard dc2 = move_pawns<Us, DELTA_N>(dc1 & TRank3BB) & emptySquares;
|
||||
|
||||
b1 |= dc1;
|
||||
b2 |= dc2;
|
||||
}
|
||||
}
|
||||
SERIALIZE_MOVES_D(b1, -TDELTA_N);
|
||||
SERIALIZE_MOVES_D(b2, -TDELTA_N -TDELTA_N);
|
||||
}
|
||||
else if (pos.ep_square() != SQ_NONE) // En passant captures
|
||||
|
||||
// En passant captures
|
||||
if ((Type == CAPTURE || Type == 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);
|
||||
|
||||
// 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 == EVASION && !bit_is_set(target, pos.ep_square() - TDELTA_N))
|
||||
return mlist;
|
||||
|
||||
b1 = pawns & pos.attacks_from<PAWN>(pos.ep_square(), Them);
|
||||
|
||||
assert(b1 != EmptyBoardBB);
|
||||
|
||||
while (b1)
|
||||
@@ -680,43 +602,49 @@ namespace {
|
||||
}
|
||||
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_piece_checks(const Position& pos, MoveStack* mlist, Color us,
|
||||
Bitboard dc, Square ksq) {
|
||||
MoveStack* generate_discovered_checks(const Position& pos, MoveStack* mlist, Square from) {
|
||||
|
||||
Bitboard target = pos.pieces(Piece, us);
|
||||
assert(Piece != QUEEN);
|
||||
|
||||
// Discovered non-capture checks
|
||||
Bitboard b = target & dc;
|
||||
|
||||
assert(Piece != QUEEN || !b);
|
||||
|
||||
while (b)
|
||||
{
|
||||
Square from = pop_1st_bit(&b);
|
||||
Bitboard bb = pos.attacks_from<Piece>(from) & pos.empty_squares();
|
||||
Bitboard b = pos.attacks_from<Piece>(from) & pos.empty_squares();
|
||||
if (Piece == KING)
|
||||
bb &= ~QueenPseudoAttacks[ksq];
|
||||
|
||||
SERIALIZE_MOVES(bb);
|
||||
{
|
||||
Square ksq = pos.king_square(opposite_color(pos.side_to_move()));
|
||||
b &= ~QueenPseudoAttacks[ksq];
|
||||
}
|
||||
SERIALIZE_MOVES(b);
|
||||
return mlist;
|
||||
}
|
||||
|
||||
// Direct non-capture checks
|
||||
b = target & ~dc;
|
||||
Bitboard checkSqs = pos.attacks_from<Piece>(ksq) & pos.empty_squares();
|
||||
if (Piece != KING && checkSqs)
|
||||
template<PieceType Piece>
|
||||
MoveStack* generate_direct_checks(const Position& pos, MoveStack* mlist, Color us,
|
||||
Bitboard dc, Square ksq) {
|
||||
assert(Piece != KING);
|
||||
|
||||
Bitboard checkSqs, b;
|
||||
Square from;
|
||||
const Square* ptr = pos.piece_list_begin(us, Piece);
|
||||
|
||||
if ((from = *ptr++) == SQ_NONE)
|
||||
return mlist;
|
||||
|
||||
checkSqs = pos.attacks_from<Piece>(ksq) & pos.empty_squares();
|
||||
|
||||
do
|
||||
{
|
||||
while (b)
|
||||
{
|
||||
Square from = pop_1st_bit(&b);
|
||||
if ( (Piece == QUEEN && !(QueenPseudoAttacks[from] & checkSqs))
|
||||
|| (Piece == ROOK && !(RookPseudoAttacks[from] & checkSqs))
|
||||
|| (Piece == BISHOP && !(BishopPseudoAttacks[from] & checkSqs)))
|
||||
continue;
|
||||
|
||||
Bitboard bb = pos.attacks_from<Piece>(from) & checkSqs;
|
||||
SERIALIZE_MOVES(bb);
|
||||
}
|
||||
}
|
||||
if (dc && bit_is_set(dc, from))
|
||||
continue;
|
||||
|
||||
b = pos.attacks_from<Piece>(from) & checkSqs;
|
||||
SERIALIZE_MOVES(b);
|
||||
|
||||
} while ((from = *ptr++) != SQ_NONE);
|
||||
|
||||
return mlist;
|
||||
}
|
||||
|
||||
@@ -762,17 +690,4 @@ namespace {
|
||||
}
|
||||
return mlist;
|
||||
}
|
||||
|
||||
bool castling_is_check(const Position& pos, CastlingSide side) {
|
||||
|
||||
// After castling opponent king is attacked by the castled rook?
|
||||
File rookFile = (side == QUEEN_SIDE ? FILE_D : FILE_F);
|
||||
Color us = pos.side_to_move();
|
||||
Square ksq = pos.king_square(us);
|
||||
Bitboard occ = pos.occupied_squares();
|
||||
|
||||
clear_bit(&occ, ksq); // Remove our king from the board
|
||||
Square rsq = make_square(rookFile, square_rank(ksq));
|
||||
return bit_is_set(rook_attacks_bb(rsq, occ), pos.king_square(opposite_color(us)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
|
||||
extern MoveStack* generate_captures(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_noncaptures(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist, Bitboard dc);
|
||||
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist, Bitboard pinned);
|
||||
extern MoveStack* generate_non_capture_checks(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_evasions(const Position& pos, MoveStack* mlist);
|
||||
extern MoveStack* generate_moves(const Position& pos, MoveStack* mlist, bool pseudoLegal = false);
|
||||
extern bool move_is_legal(const Position& pos, const Move m, Bitboard pinned);
|
||||
extern bool move_is_legal(const Position& pos, const Move m);
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
//// Includes
|
||||
////
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
#include "history.h"
|
||||
@@ -53,7 +52,7 @@ namespace {
|
||||
|
||||
CACHE_LINE_ALIGNMENT
|
||||
const uint8_t MainSearchPhaseTable[] = { PH_TT_MOVES, PH_GOOD_CAPTURES, PH_KILLERS, PH_NONCAPTURES, PH_BAD_CAPTURES, PH_STOP};
|
||||
const uint8_t EvasionsPhaseTable[] = { PH_EVASIONS, PH_STOP};
|
||||
const uint8_t EvasionsPhaseTable[] = { PH_TT_MOVES, PH_EVASIONS, PH_STOP};
|
||||
const uint8_t QsearchWithChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_QCHECKS, PH_STOP};
|
||||
const uint8_t QsearchWithoutChecksPhaseTable[] = { PH_TT_MOVES, PH_QCAPTURES, PH_STOP};
|
||||
}
|
||||
@@ -78,7 +77,9 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
||||
finished = false;
|
||||
lastBadCapture = badCaptures;
|
||||
|
||||
if (ss)
|
||||
pinned = p.pinned_pieces(pos.side_to_move());
|
||||
|
||||
if (ss && !p.is_check())
|
||||
{
|
||||
ttMoves[1].move = (ss->mateKiller == ttm)? MOVE_NONE : ss->mateKiller;
|
||||
searchTT |= ttMoves[1].move;
|
||||
@@ -87,21 +88,16 @@ MovePicker::MovePicker(const Position& p, Move ttm, Depth d,
|
||||
} else
|
||||
ttMoves[1].move = killers[0].move = killers[1].move = MOVE_NONE;
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
|
||||
dc = p.discovered_check_candidates(us);
|
||||
pinned = p.pinned_pieces(us);
|
||||
|
||||
if (p.is_check())
|
||||
phasePtr = EvasionsPhaseTable;
|
||||
else if (d > Depth(0))
|
||||
phasePtr = MainSearchPhaseTable + !searchTT;
|
||||
phasePtr = MainSearchPhaseTable;
|
||||
else if (d == Depth(0))
|
||||
phasePtr = QsearchWithChecksPhaseTable + !searchTT;
|
||||
phasePtr = QsearchWithChecksPhaseTable;
|
||||
else
|
||||
phasePtr = QsearchWithoutChecksPhaseTable + !searchTT;
|
||||
phasePtr = QsearchWithoutChecksPhaseTable;
|
||||
|
||||
phasePtr--;
|
||||
phasePtr += !searchTT - 1;
|
||||
go_next_phase();
|
||||
}
|
||||
|
||||
@@ -123,7 +119,6 @@ void MovePicker::go_next_phase() {
|
||||
case PH_GOOD_CAPTURES:
|
||||
lastMove = generate_captures(pos, moves);
|
||||
score_captures();
|
||||
std::sort(moves, lastMove);
|
||||
return;
|
||||
|
||||
case PH_KILLERS:
|
||||
@@ -134,7 +129,7 @@ void MovePicker::go_next_phase() {
|
||||
case PH_NONCAPTURES:
|
||||
lastMove = generate_noncaptures(pos, moves);
|
||||
score_noncaptures();
|
||||
std::sort(moves, lastMove);
|
||||
sort_moves(moves, lastMove);
|
||||
return;
|
||||
|
||||
case PH_BAD_CAPTURES:
|
||||
@@ -142,25 +137,22 @@ void MovePicker::go_next_phase() {
|
||||
// to get SEE move ordering.
|
||||
curMove = badCaptures;
|
||||
lastMove = lastBadCapture;
|
||||
std::sort(badCaptures, lastMove);
|
||||
return;
|
||||
|
||||
case PH_EVASIONS:
|
||||
assert(pos.is_check());
|
||||
lastMove = generate_evasions(pos, moves, pinned);
|
||||
lastMove = generate_evasions(pos, moves);
|
||||
score_evasions();
|
||||
std::sort(moves, lastMove);
|
||||
return;
|
||||
|
||||
case PH_QCAPTURES:
|
||||
lastMove = generate_captures(pos, moves);
|
||||
score_captures();
|
||||
std::sort(moves, lastMove);
|
||||
return;
|
||||
|
||||
case PH_QCHECKS:
|
||||
// Perhaps we should order moves move here? FIXME
|
||||
lastMove = generate_non_capture_checks(pos, moves, dc);
|
||||
lastMove = generate_non_capture_checks(pos, moves);
|
||||
return;
|
||||
|
||||
case PH_STOP:
|
||||
@@ -202,8 +194,8 @@ void MovePicker::score_captures() {
|
||||
if (move_is_promotion(m))
|
||||
cur->score = QueenValueMidgame;
|
||||
else
|
||||
cur->score = int(pos.midgame_value_of_piece_on(move_to(m)))
|
||||
-int(pos.type_of_piece_on(move_from(m)));
|
||||
cur->score = pos.midgame_value_of_piece_on(move_to(m))
|
||||
- pos.type_of_piece_on(move_from(m));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,24 +221,27 @@ void MovePicker::score_noncaptures() {
|
||||
hs += 1000;
|
||||
|
||||
// pst based scoring
|
||||
cur->score = hs + pos.pst_delta<Position::MidGame>(piece, from, to);
|
||||
cur->score = hs + mg_value(pos.pst_delta(piece, from, to));
|
||||
}
|
||||
}
|
||||
|
||||
void MovePicker::score_evasions() {
|
||||
|
||||
// Try good captures ordered by MVV/LVA, then non-captures if
|
||||
// destination square is not under attack, ordered by history
|
||||
// value, and at the end bad-captures and non-captures with a
|
||||
// negative SEE. This last group is ordered by the SEE score.
|
||||
Move m;
|
||||
int seeScore;
|
||||
|
||||
for (MoveStack* cur = moves; cur != lastMove; cur++)
|
||||
{
|
||||
m = cur->move;
|
||||
if (m == ttMoves[0].move)
|
||||
cur->score = 2 * HistoryMax;
|
||||
else if (!pos.square_is_empty(move_to(m)))
|
||||
{
|
||||
int seeScore = pos.see(m);
|
||||
cur->score = seeScore + (seeScore >= 0 ? HistoryMax : 0);
|
||||
} else
|
||||
if ((seeScore = pos.see_sign(m)) < 0)
|
||||
cur->score = seeScore;
|
||||
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)) + HistoryMax;
|
||||
else
|
||||
cur->score = H.move_ordering_score(pos.piece_on(move_from(m)), move_to(m));
|
||||
}
|
||||
}
|
||||
@@ -259,26 +254,23 @@ void MovePicker::score_evasions() {
|
||||
|
||||
Move MovePicker::get_next_move() {
|
||||
|
||||
assert(!pos.is_check() || *phasePtr == PH_EVASIONS || *phasePtr == PH_STOP);
|
||||
assert( pos.is_check() || *phasePtr != PH_EVASIONS);
|
||||
|
||||
Move move;
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (curMove != lastMove)
|
||||
{
|
||||
move = (curMove++)->move;
|
||||
|
||||
switch (phase) {
|
||||
|
||||
case PH_TT_MOVES:
|
||||
move = (curMove++)->move;
|
||||
if ( move != MOVE_NONE
|
||||
&& move_is_legal(pos, move, pinned))
|
||||
return move;
|
||||
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))
|
||||
@@ -298,6 +290,7 @@ Move MovePicker::get_next_move() {
|
||||
break;
|
||||
|
||||
case PH_KILLERS:
|
||||
move = (curMove++)->move;
|
||||
if ( move != MOVE_NONE
|
||||
&& move != ttMoves[0].move
|
||||
&& move != ttMoves[1].move
|
||||
@@ -307,6 +300,7 @@ Move MovePicker::get_next_move() {
|
||||
break;
|
||||
|
||||
case PH_NONCAPTURES:
|
||||
move = (curMove++)->move;
|
||||
if ( move != ttMoves[0].move
|
||||
&& move != ttMoves[1].move
|
||||
&& move != killers[0].move
|
||||
@@ -315,13 +309,20 @@ Move MovePicker::get_next_move() {
|
||||
return move;
|
||||
break;
|
||||
|
||||
case PH_EVASIONS:
|
||||
case PH_BAD_CAPTURES:
|
||||
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))
|
||||
return move;
|
||||
break;
|
||||
|
||||
case PH_QCHECKS:
|
||||
// Maybe postpone the legality check until after futility pruning?
|
||||
move = (curMove++)->move;
|
||||
if ( move != ttMoves[0].move
|
||||
&& pos.pl_move_is_legal(move, pinned))
|
||||
return move;
|
||||
|
||||
@@ -54,7 +54,6 @@ public:
|
||||
Move get_next_move();
|
||||
Move get_next_move(Lock& lock);
|
||||
int number_of_evasions() const;
|
||||
Bitboard discovered_check_candidates() const;
|
||||
|
||||
private:
|
||||
void score_captures();
|
||||
@@ -69,7 +68,7 @@ private:
|
||||
int phase;
|
||||
const uint8_t* phasePtr;
|
||||
MoveStack *curMove, *lastMove, *lastBadCapture;
|
||||
Bitboard dc, pinned;
|
||||
Bitboard pinned;
|
||||
MoveStack moves[256], badCaptures[64];
|
||||
};
|
||||
|
||||
@@ -88,12 +87,4 @@ inline int MovePicker::number_of_evasions() const {
|
||||
return int(lastMove - moves);
|
||||
}
|
||||
|
||||
/// MovePicker::discovered_check_candidates() returns a bitboard containing
|
||||
/// all pieces which can possibly give discovered check. This bitboard is
|
||||
/// computed by the constructor function.
|
||||
|
||||
inline Bitboard MovePicker::discovered_check_candidates() const {
|
||||
return dc;
|
||||
}
|
||||
|
||||
#endif // !defined(MOVEPICK_H_INCLUDED)
|
||||
|
||||
227
src/pawns.cpp
227
src/pawns.cpp
@@ -38,64 +38,36 @@ namespace {
|
||||
|
||||
/// Constants and variables
|
||||
|
||||
// Doubled pawn penalty by file, middle game
|
||||
const Value DoubledPawnMidgamePenalty[8] = {
|
||||
Value(13), Value(20), Value(23), Value(23),
|
||||
Value(23), Value(23), Value(20), Value(13)
|
||||
#define S(mg, eg) make_score(mg, eg)
|
||||
|
||||
// Doubled pawn penalty by file
|
||||
const Score DoubledPawnPenalty[8] = {
|
||||
S(13, 43), S(20, 48), S(23, 48), S(23, 48),
|
||||
S(23, 48), S(23, 48), S(20, 48), S(13, 43)
|
||||
};
|
||||
|
||||
// Doubled pawn penalty by file, endgame
|
||||
const Value DoubledPawnEndgamePenalty[8] = {
|
||||
Value(43), Value(48), Value(48), Value(48),
|
||||
Value(48), Value(48), Value(48), Value(43)
|
||||
// Isolated pawn penalty by file
|
||||
const Score IsolatedPawnPenalty[8] = {
|
||||
S(25, 30), S(36, 35), S(40, 35), S(40, 35),
|
||||
S(40, 35), S(40, 35), S(36, 35), S(25, 30)
|
||||
};
|
||||
|
||||
// Isolated pawn penalty by file, middle game
|
||||
const Value IsolatedPawnMidgamePenalty[8] = {
|
||||
Value(25), Value(36), Value(40), Value(40),
|
||||
Value(40), Value(40), Value(36), Value(25)
|
||||
// Backward pawn penalty by file
|
||||
const Score BackwardPawnPenalty[8] = {
|
||||
S(20, 28), S(29, 31), S(33, 31), S(33, 31),
|
||||
S(33, 31), S(33, 31), S(29, 31), S(20, 28)
|
||||
};
|
||||
|
||||
// Isolated pawn penalty by file, endgame
|
||||
const Value IsolatedPawnEndgamePenalty[8] = {
|
||||
Value(30), Value(35), Value(35), Value(35),
|
||||
Value(35), Value(35), Value(35), Value(30)
|
||||
// Pawn chain membership bonus by file
|
||||
const Score ChainBonus[8] = {
|
||||
S(11,-1), S(13,-1), S(13,-1), S(14,-1),
|
||||
S(14,-1), S(13,-1), S(13,-1), S(11,-1)
|
||||
};
|
||||
|
||||
// Backward pawn penalty by file, middle game
|
||||
const Value BackwardPawnMidgamePenalty[8] = {
|
||||
Value(20), Value(29), Value(33), Value(33),
|
||||
Value(33), Value(33), Value(29), Value(20)
|
||||
};
|
||||
|
||||
// Backward pawn penalty by file, endgame
|
||||
const Value BackwardPawnEndgamePenalty[8] = {
|
||||
Value(28), Value(31), Value(31), Value(31),
|
||||
Value(31), Value(31), Value(31), Value(28)
|
||||
};
|
||||
|
||||
// Pawn chain membership bonus by file, middle game
|
||||
const Value ChainMidgameBonus[8] = {
|
||||
Value(11), Value(13), Value(13), Value(14),
|
||||
Value(14), Value(13), Value(13), Value(11)
|
||||
};
|
||||
|
||||
// Pawn chain membership bonus by file, endgame
|
||||
const Value ChainEndgameBonus[8] = {
|
||||
Value(-1), Value(-1), Value(-1), Value(-1),
|
||||
Value(-1), Value(-1), Value(-1), Value(-1)
|
||||
};
|
||||
|
||||
// Candidate passed pawn bonus by rank, middle game
|
||||
const Value CandidateMidgameBonus[8] = {
|
||||
Value( 0), Value( 6), Value(6), Value(14),
|
||||
Value(34), Value(83), Value(0), Value( 0)
|
||||
};
|
||||
|
||||
// Candidate passed pawn bonus by rank, endgame
|
||||
const Value CandidateEndgameBonus[8] = {
|
||||
Value( 0), Value( 13), Value(13), Value(29),
|
||||
Value(68), Value(166), Value( 0), Value( 0)
|
||||
// Candidate passed pawn bonus by rank
|
||||
const Score CandidateBonus[8] = {
|
||||
S( 0, 0), S( 6, 13), S(6,13), S(14,29),
|
||||
S(34,68), S(83,166), S(0, 0), S( 0, 0)
|
||||
};
|
||||
|
||||
// Pawn storm tables for positions with opposite castling
|
||||
@@ -190,43 +162,53 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
pi->clear();
|
||||
pi->key = key;
|
||||
|
||||
Value mgValue[2] = {Value(0), Value(0)};
|
||||
Value egValue[2] = {Value(0), Value(0)};
|
||||
|
||||
// Calculate pawn attacks
|
||||
pi->pawnAttacks[WHITE] = ((pos.pieces(PAWN, WHITE) << 9) & ~FileABB) | ((pos.pieces(PAWN, WHITE) << 7) & ~FileHBB);
|
||||
pi->pawnAttacks[BLACK] = ((pos.pieces(PAWN, BLACK) >> 7) & ~FileABB) | ((pos.pieces(PAWN, BLACK) >> 9) & ~FileHBB);
|
||||
Bitboard whitePawns = pos.pieces(PAWN, WHITE);
|
||||
Bitboard blackPawns = pos.pieces(PAWN, BLACK);
|
||||
pi->pawnAttacks[WHITE] = ((whitePawns << 9) & ~FileABB) | ((whitePawns << 7) & ~FileHBB);
|
||||
pi->pawnAttacks[BLACK] = ((blackPawns >> 7) & ~FileABB) | ((blackPawns >> 9) & ~FileHBB);
|
||||
|
||||
// Loop through the pawns for both colors
|
||||
for (Color us = WHITE; us <= BLACK; us++)
|
||||
{
|
||||
Color them = opposite_color(us);
|
||||
Bitboard ourPawns = pos.pieces(PAWN, us);
|
||||
Bitboard theirPawns = pos.pieces(PAWN, them);
|
||||
Bitboard pawns = ourPawns;
|
||||
// Evaluate pawns for both colors
|
||||
pi->value = evaluate_pawns<WHITE>(pos, whitePawns, blackPawns, pi)
|
||||
- evaluate_pawns<BLACK>(pos, blackPawns, whitePawns, pi);
|
||||
return pi;
|
||||
}
|
||||
|
||||
|
||||
/// PawnInfoTable::evaluate_pawns() evaluates each pawn of the given color
|
||||
|
||||
template<Color Us>
|
||||
Score PawnInfoTable::evaluate_pawns(const Position& pos, Bitboard ourPawns,
|
||||
Bitboard theirPawns, PawnInfo* pi) {
|
||||
Square s;
|
||||
File f;
|
||||
Rank r;
|
||||
bool passed, isolated, doubled, chain, backward, candidate;
|
||||
int bonus;
|
||||
Score value = make_score(0, 0);
|
||||
const Square* ptr = pos.piece_list_begin(Us, PAWN);
|
||||
|
||||
// Initialize pawn storm scores by giving bonuses for open files
|
||||
for (File f = FILE_A; f <= FILE_H; f++)
|
||||
if (!(pawns & file_bb(f)))
|
||||
for (f = FILE_A; f <= FILE_H; f++)
|
||||
if (!(ourPawns & file_bb(f)))
|
||||
{
|
||||
pi->ksStormValue[us] += KStormOpenFileBonus[f];
|
||||
pi->qsStormValue[us] += QStormOpenFileBonus[f];
|
||||
pi->halfOpenFiles[us] |= (1 << f);
|
||||
pi->ksStormValue[Us] += KStormOpenFileBonus[f];
|
||||
pi->qsStormValue[Us] += QStormOpenFileBonus[f];
|
||||
pi->halfOpenFiles[Us] |= (1 << f);
|
||||
}
|
||||
|
||||
// Loop through all pawns of the current color and score each pawn
|
||||
while (pawns)
|
||||
while ((s = *ptr++) != SQ_NONE)
|
||||
{
|
||||
Square s = pop_1st_bit(&pawns);
|
||||
File f = square_file(s);
|
||||
Rank r = square_rank(s);
|
||||
f = square_file(s);
|
||||
r = square_rank(s);
|
||||
|
||||
assert(pos.piece_on(s) == piece_of_color_and_type(us, PAWN));
|
||||
assert(pos.piece_on(s) == piece_of_color_and_type(Us, PAWN));
|
||||
|
||||
// Passed, isolated or doubled pawn?
|
||||
bool passed = Position::pawn_is_passed(theirPawns, us, s);
|
||||
bool isolated = Position::pawn_is_isolated(ourPawns, s);
|
||||
bool doubled = Position::pawn_is_doubled(ourPawns, us, s);
|
||||
passed = Position::pawn_is_passed(theirPawns, Us, s);
|
||||
isolated = Position::pawn_is_isolated(ourPawns, s);
|
||||
doubled = Position::pawn_is_doubled(ourPawns, Us, s);
|
||||
|
||||
// We calculate kingside and queenside pawn storm
|
||||
// scores for both colors. These are used when evaluating
|
||||
@@ -238,10 +220,10 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
// enemy pawn on an adjacent file gets an additional bonus.
|
||||
|
||||
// Kingside pawn storms
|
||||
int bonus = KStormTable[relative_square(us, s)];
|
||||
bonus = KStormTable[relative_square(Us, s)];
|
||||
if (f >= FILE_F)
|
||||
{
|
||||
Bitboard b = outpost_mask(us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
|
||||
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileFBB | FileGBB | FileHBB);
|
||||
while (b)
|
||||
{
|
||||
Square s2 = pop_1st_bit(&b);
|
||||
@@ -261,13 +243,13 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
bonus += StormLeverBonus[f] - 2*square_distance(s, s2);
|
||||
}
|
||||
}
|
||||
pi->ksStormValue[us] += bonus;
|
||||
pi->ksStormValue[Us] += bonus;
|
||||
|
||||
// Queenside pawn storms
|
||||
bonus = QStormTable[relative_square(us, s)];
|
||||
bonus = QStormTable[relative_square(Us, s)];
|
||||
if (f <= FILE_C)
|
||||
{
|
||||
Bitboard b = outpost_mask(us, s) & theirPawns & (FileABB | FileBBB | FileCBB);
|
||||
Bitboard b = outpost_mask(Us, s) & theirPawns & (FileABB | FileBBB | FileCBB);
|
||||
while (b)
|
||||
{
|
||||
Square s2 = pop_1st_bit(&b);
|
||||
@@ -287,15 +269,15 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
bonus += StormLeverBonus[f] - 4*square_distance(s, s2);
|
||||
}
|
||||
}
|
||||
pi->qsStormValue[us] += bonus;
|
||||
pi->qsStormValue[Us] += bonus;
|
||||
|
||||
// Member of a pawn chain (but not the backward one)? We could speed up
|
||||
// the test a little by introducing an array of masks indexed by color
|
||||
// and square for doing the test, but because everything is hashed,
|
||||
// it probably won't make any noticable difference.
|
||||
bool chain = ourPawns
|
||||
chain = ourPawns
|
||||
& neighboring_files_bb(f)
|
||||
& (rank_bb(r) | rank_bb(r - (us == WHITE ? 1 : -1)));
|
||||
& (rank_bb(r) | rank_bb(r - (Us == WHITE ? 1 : -1)));
|
||||
|
||||
// Test for backward pawn
|
||||
//
|
||||
@@ -303,12 +285,9 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
// it cannot be backward. If can capture an enemy pawn or if
|
||||
// there are friendly pawns behind on neighboring files it cannot
|
||||
// be backward either.
|
||||
bool backward;
|
||||
if ( passed
|
||||
|| isolated
|
||||
|| chain
|
||||
|| (pos.attacks_from<PAWN>(s, us) & theirPawns)
|
||||
|| (ourPawns & behind_bb(us, r) & neighboring_files_bb(f)))
|
||||
if ( (passed | isolated | chain)
|
||||
|| (ourPawns & behind_bb(Us, r) & neighboring_files_bb(f))
|
||||
|| (pos.attacks_from<PAWN>(s, Us) & theirPawns))
|
||||
backward = false;
|
||||
else
|
||||
{
|
||||
@@ -316,31 +295,29 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
// pawn on neighboring files. We now check whether the pawn is
|
||||
// backward by looking in the forward direction on the neighboring
|
||||
// files, and seeing whether we meet a friendly or an enemy pawn first.
|
||||
Bitboard b = pos.attacks_from<PAWN>(s, us);
|
||||
if (us == WHITE)
|
||||
{
|
||||
for ( ; !(b & (ourPawns | theirPawns)); b <<= 8);
|
||||
backward = (b | (b << 8)) & theirPawns;
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( ; !(b & (ourPawns | theirPawns)); b >>= 8);
|
||||
backward = (b | (b >> 8)) & theirPawns;
|
||||
}
|
||||
Bitboard b = pos.attacks_from<PAWN>(s, Us);
|
||||
|
||||
// Note that we are sure to find something because pawn is not passed
|
||||
// nor isolated, so loop is potentially infinite, but it isn't.
|
||||
while (!(b & (ourPawns | theirPawns)))
|
||||
Us == WHITE ? b <<= 8 : b >>= 8;
|
||||
|
||||
// The friendly pawn needs to be at least two ranks closer than the enemy
|
||||
// pawn in order to help the potentially backward pawn advance.
|
||||
backward = (b | (Us == WHITE ? b << 8 : b >> 8)) & theirPawns;
|
||||
}
|
||||
|
||||
// Test for candidate passed pawn
|
||||
bool candidate;
|
||||
candidate = !passed
|
||||
&& !(theirPawns & file_bb(f))
|
||||
&& ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(us, r) | rank_bb(r)) & ourPawns)
|
||||
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(us, r) & theirPawns)
|
||||
&& ( count_1s_max_15(neighboring_files_bb(f) & (behind_bb(Us, r) | rank_bb(r)) & ourPawns)
|
||||
- count_1s_max_15(neighboring_files_bb(f) & in_front_bb(Us, r) & theirPawns)
|
||||
>= 0);
|
||||
|
||||
// In order to prevent doubled passed pawns from receiving a too big
|
||||
// bonus, only the frontmost passed pawn on each file is considered as
|
||||
// a true passed pawn.
|
||||
if (passed && (ourPawns & squares_in_front_of(us, s)))
|
||||
if (passed && (ourPawns & squares_in_front_of(Us, s)))
|
||||
passed = false;
|
||||
|
||||
// Score this pawn
|
||||
@@ -349,45 +326,27 @@ PawnInfo* PawnInfoTable::get_pawn_info(const Position& pos) {
|
||||
|
||||
if (isolated)
|
||||
{
|
||||
mgValue[us] -= IsolatedPawnMidgamePenalty[f];
|
||||
egValue[us] -= IsolatedPawnEndgamePenalty[f];
|
||||
value -= IsolatedPawnPenalty[f];
|
||||
if (!(theirPawns & file_bb(f)))
|
||||
{
|
||||
mgValue[us] -= IsolatedPawnMidgamePenalty[f] / 2;
|
||||
egValue[us] -= IsolatedPawnEndgamePenalty[f] / 2;
|
||||
}
|
||||
value -= IsolatedPawnPenalty[f] / 2;
|
||||
}
|
||||
if (doubled)
|
||||
{
|
||||
mgValue[us] -= DoubledPawnMidgamePenalty[f];
|
||||
egValue[us] -= DoubledPawnEndgamePenalty[f];
|
||||
}
|
||||
value -= DoubledPawnPenalty[f];
|
||||
|
||||
if (backward)
|
||||
{
|
||||
mgValue[us] -= BackwardPawnMidgamePenalty[f];
|
||||
egValue[us] -= BackwardPawnEndgamePenalty[f];
|
||||
value -= BackwardPawnPenalty[f];
|
||||
if (!(theirPawns & file_bb(f)))
|
||||
{
|
||||
mgValue[us] -= BackwardPawnMidgamePenalty[f] / 2;
|
||||
egValue[us] -= BackwardPawnEndgamePenalty[f] / 2;
|
||||
}
|
||||
value -= BackwardPawnPenalty[f] / 2;
|
||||
}
|
||||
if (chain)
|
||||
{
|
||||
mgValue[us] += ChainMidgameBonus[f];
|
||||
egValue[us] += ChainEndgameBonus[f];
|
||||
}
|
||||
if (candidate)
|
||||
{
|
||||
mgValue[us] += CandidateMidgameBonus[relative_rank(us, s)];
|
||||
egValue[us] += CandidateEndgameBonus[relative_rank(us, s)];
|
||||
}
|
||||
} // while(pawns)
|
||||
} // for(colors)
|
||||
value += ChainBonus[f];
|
||||
|
||||
pi->mgValue = int16_t(mgValue[WHITE] - mgValue[BLACK]);
|
||||
pi->egValue = int16_t(egValue[WHITE] - egValue[BLACK]);
|
||||
return pi;
|
||||
if (candidate)
|
||||
value += CandidateBonus[relative_rank(Us, s)];
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
16
src/pawns.h
16
src/pawns.h
@@ -47,8 +47,7 @@ class PawnInfo {
|
||||
public:
|
||||
PawnInfo() { clear(); }
|
||||
|
||||
Value mg_value() const;
|
||||
Value eg_value() const;
|
||||
Score pawns_value() const;
|
||||
Value kingside_storm_value(Color c) const;
|
||||
Value queenside_storm_value(Color c) const;
|
||||
Bitboard pawn_attacks(Color c) const;
|
||||
@@ -65,7 +64,7 @@ private:
|
||||
Key key;
|
||||
Bitboard passedPawns;
|
||||
Bitboard pawnAttacks[2];
|
||||
int16_t mgValue, egValue;
|
||||
Score value;
|
||||
int16_t ksStormValue[2], qsStormValue[2];
|
||||
uint8_t halfOpenFiles[2];
|
||||
Square kingSquares[2];
|
||||
@@ -85,6 +84,9 @@ public:
|
||||
PawnInfo* get_pawn_info(const Position& pos);
|
||||
|
||||
private:
|
||||
template<Color Us>
|
||||
Score evaluate_pawns(const Position& pos, Bitboard ourPawns, Bitboard theirPawns, PawnInfo* pi);
|
||||
|
||||
unsigned size;
|
||||
PawnInfo* entries;
|
||||
};
|
||||
@@ -94,12 +96,8 @@ private:
|
||||
//// Inline functions
|
||||
////
|
||||
|
||||
inline Value PawnInfo::mg_value() const {
|
||||
return Value(mgValue);
|
||||
}
|
||||
|
||||
inline Value PawnInfo::eg_value() const {
|
||||
return Value(egValue);
|
||||
inline Score PawnInfo::pawns_value() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
inline Bitboard PawnInfo::passed_pawns() const {
|
||||
|
||||
341
src/position.cpp
341
src/position.cpp
@@ -51,18 +51,31 @@ Key Position::zobEp[64];
|
||||
Key Position::zobCastle[16];
|
||||
Key Position::zobMaterial[2][8][16];
|
||||
Key Position::zobSideToMove;
|
||||
Key Position::zobExclusion;
|
||||
|
||||
Value Position::MgPieceSquareTable[16][64];
|
||||
Value Position::EgPieceSquareTable[16][64];
|
||||
Score Position::PieceSquareTable[16][64];
|
||||
|
||||
static bool RequestPending = false;
|
||||
|
||||
////
|
||||
//// Functions
|
||||
////
|
||||
|
||||
/// Constructors
|
||||
|
||||
CheckInfo::CheckInfo(const Position& pos) {
|
||||
|
||||
Color us = pos.side_to_move();
|
||||
Color them = opposite_color(us);
|
||||
|
||||
ksq = pos.king_square(them);
|
||||
dcCandidates = pos.discovered_check_candidates(us);
|
||||
|
||||
checkSq[PAWN] = pos.attacks_from<PAWN>(ksq, them);
|
||||
checkSq[KNIGHT] = pos.attacks_from<KNIGHT>(ksq);
|
||||
checkSq[BISHOP] = pos.attacks_from<BISHOP>(ksq);
|
||||
checkSq[ROOK] = pos.attacks_from<ROOK>(ksq);
|
||||
checkSq[QUEEN] = checkSq[BISHOP] | checkSq[ROOK];
|
||||
checkSq[KING] = EmptyBoardBB;
|
||||
}
|
||||
|
||||
Position::Position(const Position& pos) {
|
||||
copy(pos);
|
||||
}
|
||||
@@ -213,8 +226,7 @@ void Position::from_fen(const string& fen) {
|
||||
st->key = compute_key();
|
||||
st->pawnKey = compute_pawn_key();
|
||||
st->materialKey = compute_material_key();
|
||||
st->mgValue = compute_value<MidGame>();
|
||||
st->egValue = compute_value<EndGame>();
|
||||
st->value = compute_value();
|
||||
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
|
||||
st->npMaterial[BLACK] = compute_non_pawn_material(BLACK);
|
||||
}
|
||||
@@ -332,16 +344,15 @@ void Position::copy(const Position& pos) {
|
||||
template<bool FindPinned>
|
||||
Bitboard Position::hidden_checkers(Color c) const {
|
||||
|
||||
Bitboard pinners, result = EmptyBoardBB;
|
||||
Bitboard result = EmptyBoardBB;
|
||||
Bitboard pinners = pieces_of_color(FindPinned ? opposite_color(c) : c);
|
||||
|
||||
// Pinned pieces protect our king, dicovery checks attack
|
||||
// the enemy king.
|
||||
Square ksq = king_square(FindPinned ? c : opposite_color(c));
|
||||
|
||||
// Pinners are sliders, not checkers, that give check when
|
||||
// candidate pinned is removed.
|
||||
pinners = (pieces(ROOK, QUEEN, FindPinned ? opposite_color(c) : c) & RookPseudoAttacks[ksq])
|
||||
| (pieces(BISHOP, QUEEN, FindPinned ? opposite_color(c) : c) & BishopPseudoAttacks[ksq]);
|
||||
// Pinners are sliders, not checkers, that give check when candidate pinned is removed
|
||||
pinners &= (pieces(ROOK, QUEEN) & RookPseudoAttacks[ksq]) | (pieces(BISHOP, QUEEN) & BishopPseudoAttacks[ksq]);
|
||||
|
||||
if (FindPinned && pinners)
|
||||
pinners &= ~st->checkersBB;
|
||||
@@ -458,19 +469,11 @@ void Position::find_checkers() {
|
||||
|
||||
/// Position::pl_move_is_legal() tests whether a pseudo-legal move is legal
|
||||
|
||||
bool Position::pl_move_is_legal(Move m) const {
|
||||
|
||||
// If we're in check, all pseudo-legal moves are legal, because our
|
||||
// check evasion generator only generates true legal moves.
|
||||
return is_check() || pl_move_is_legal(m, pinned_pieces(side_to_move()));
|
||||
}
|
||||
|
||||
bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
|
||||
assert(is_ok());
|
||||
assert(move_is_ok(m));
|
||||
assert(pinned == pinned_pieces(side_to_move()));
|
||||
assert(!is_check());
|
||||
|
||||
// Castling moves are checked for legality during move generation.
|
||||
if (move_is_castle(m))
|
||||
@@ -519,107 +522,109 @@ bool Position::pl_move_is_legal(Move m, Bitboard pinned) const {
|
||||
}
|
||||
|
||||
|
||||
/// Position::pl_move_is_evasion() tests whether a pseudo-legal move is a legal evasion
|
||||
|
||||
bool Position::pl_move_is_evasion(Move m, Bitboard pinned) const
|
||||
{
|
||||
assert(is_check());
|
||||
|
||||
Color us = side_to_move();
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
|
||||
// King moves and en-passant captures are verified in pl_move_is_legal()
|
||||
if (type_of_piece_on(from) == KING || move_is_ep(m))
|
||||
return pl_move_is_legal(m, pinned);
|
||||
|
||||
Bitboard target = checkers();
|
||||
Square checksq = pop_1st_bit(&target);
|
||||
|
||||
if (target) // double check ?
|
||||
return false;
|
||||
|
||||
// Our move must be a blocking evasion or a capture of the checking piece
|
||||
target = squares_between(checksq, king_square(us)) | checkers();
|
||||
return bit_is_set(target, to) && pl_move_is_legal(m, pinned);
|
||||
}
|
||||
|
||||
|
||||
/// Position::move_is_check() tests whether a pseudo-legal move is a check
|
||||
|
||||
bool Position::move_is_check(Move m) const {
|
||||
|
||||
Bitboard dc = discovered_check_candidates(side_to_move());
|
||||
return move_is_check(m, dc);
|
||||
return move_is_check(m, CheckInfo(*this));
|
||||
}
|
||||
|
||||
bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
|
||||
bool Position::move_is_check(Move m, const CheckInfo& ci) const {
|
||||
|
||||
assert(is_ok());
|
||||
assert(move_is_ok(m));
|
||||
assert(dcCandidates == discovered_check_candidates(side_to_move()));
|
||||
assert(ci.dcCandidates == discovered_check_candidates(side_to_move()));
|
||||
assert(color_of_piece_on(move_from(m)) == side_to_move());
|
||||
assert(piece_on(ci.ksq) == piece_of_color_and_type(opposite_color(side_to_move()), KING));
|
||||
|
||||
Color us = side_to_move();
|
||||
Color them = opposite_color(us);
|
||||
Square from = move_from(m);
|
||||
Square to = move_to(m);
|
||||
Square ksq = king_square(them);
|
||||
PieceType pt = type_of_piece_on(from);
|
||||
|
||||
assert(color_of_piece_on(from) == us);
|
||||
assert(piece_on(ksq) == piece_of_color_and_type(them, KING));
|
||||
|
||||
// Proceed according to the type of the moving piece
|
||||
switch (type_of_piece_on(from))
|
||||
{
|
||||
case PAWN:
|
||||
|
||||
if (bit_is_set(attacks_from<PAWN>(ksq, them), to)) // Normal check?
|
||||
// Direct check ?
|
||||
if (bit_is_set(ci.checkSq[pt], to))
|
||||
return true;
|
||||
|
||||
if ( dcCandidates // Discovered check?
|
||||
&& bit_is_set(dcCandidates, from)
|
||||
&& (direction_between_squares(from, ksq) != direction_between_squares(to, ksq)))
|
||||
return true;
|
||||
|
||||
if (move_is_promotion(m)) // Promotion with check?
|
||||
// Discovery check ?
|
||||
if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from))
|
||||
{
|
||||
// For pawn and king moves we need to verify also direction
|
||||
if ( (pt != PAWN && pt != KING)
|
||||
||(direction_between_squares(from, ci.ksq) != direction_between_squares(to, ci.ksq)))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Can we skip the ugly special cases ?
|
||||
if (!move_is_special(m))
|
||||
return false;
|
||||
|
||||
Color us = side_to_move();
|
||||
Bitboard b = occupied_squares();
|
||||
|
||||
// Promotion with check ?
|
||||
if (move_is_promotion(m))
|
||||
{
|
||||
clear_bit(&b, from);
|
||||
|
||||
switch (move_promotion_piece(m))
|
||||
{
|
||||
case KNIGHT:
|
||||
return bit_is_set(attacks_from<KNIGHT>(to), ksq);
|
||||
return bit_is_set(attacks_from<KNIGHT>(to), ci.ksq);
|
||||
case BISHOP:
|
||||
return bit_is_set(bishop_attacks_bb(to, b), ksq);
|
||||
return bit_is_set(bishop_attacks_bb(to, b), ci.ksq);
|
||||
case ROOK:
|
||||
return bit_is_set(rook_attacks_bb(to, b), ksq);
|
||||
return bit_is_set(rook_attacks_bb(to, b), ci.ksq);
|
||||
case QUEEN:
|
||||
return bit_is_set(queen_attacks_bb(to, b), ksq);
|
||||
return bit_is_set(queen_attacks_bb(to, b), ci.ksq);
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
// En passant capture with check? We have already handled the case
|
||||
// of direct checks and ordinary discovered check, the only case we
|
||||
// need to handle is the unusual case of a discovered check through the
|
||||
// captured pawn.
|
||||
else if (move_is_ep(m))
|
||||
if (move_is_ep(m))
|
||||
{
|
||||
Square capsq = make_square(square_file(to), square_rank(from));
|
||||
Bitboard b = occupied_squares();
|
||||
clear_bit(&b, from);
|
||||
clear_bit(&b, capsq);
|
||||
set_bit(&b, to);
|
||||
return (rook_attacks_bb(ksq, b) & pieces(ROOK, QUEEN, us))
|
||||
||(bishop_attacks_bb(ksq, b) & pieces(BISHOP, QUEEN, us));
|
||||
return (rook_attacks_bb(ci.ksq, b) & pieces(ROOK, QUEEN, us))
|
||||
||(bishop_attacks_bb(ci.ksq, b) & pieces(BISHOP, QUEEN, us));
|
||||
}
|
||||
return false;
|
||||
|
||||
// Test discovered check and normal check according to piece type
|
||||
case KNIGHT:
|
||||
return (dcCandidates && bit_is_set(dcCandidates, from))
|
||||
|| bit_is_set(attacks_from<KNIGHT>(ksq), to);
|
||||
|
||||
case BISHOP:
|
||||
return (dcCandidates && bit_is_set(dcCandidates, from))
|
||||
|| (direction_is_diagonal(ksq, to) && bit_is_set(attacks_from<BISHOP>(ksq), to));
|
||||
|
||||
case ROOK:
|
||||
return (dcCandidates && bit_is_set(dcCandidates, from))
|
||||
|| (direction_is_straight(ksq, to) && bit_is_set(attacks_from<ROOK>(ksq), to));
|
||||
|
||||
case QUEEN:
|
||||
// Discovered checks are impossible!
|
||||
assert(!bit_is_set(dcCandidates, from));
|
||||
return ( (direction_is_straight(ksq, to) && bit_is_set(attacks_from<ROOK>(ksq), to))
|
||||
|| (direction_is_diagonal(ksq, to) && bit_is_set(attacks_from<BISHOP>(ksq), to)));
|
||||
|
||||
case KING:
|
||||
// Discovered check?
|
||||
if ( bit_is_set(dcCandidates, from)
|
||||
&& (direction_between_squares(from, ksq) != direction_between_squares(to, ksq)))
|
||||
return true;
|
||||
|
||||
// Castling with check?
|
||||
// Castling with check ?
|
||||
if (move_is_castle(m))
|
||||
{
|
||||
Square kfrom, kto, rfrom, rto;
|
||||
Bitboard b = occupied_squares();
|
||||
kfrom = from;
|
||||
rfrom = to;
|
||||
|
||||
@@ -635,50 +640,10 @@ bool Position::move_is_check(Move m, Bitboard dcCandidates) const {
|
||||
clear_bit(&b, rfrom);
|
||||
set_bit(&b, rto);
|
||||
set_bit(&b, kto);
|
||||
return bit_is_set(rook_attacks_bb(rto, b), ksq);
|
||||
return bit_is_set(rook_attacks_bb(rto, b), ci.ksq);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
default: // NO_PIECE_TYPE
|
||||
break;
|
||||
}
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// Position::update_checkers() udpates chekers info given the move. It is called
|
||||
/// in do_move() and is faster then find_checkers().
|
||||
|
||||
template<PieceType Piece>
|
||||
inline void Position::update_checkers(Bitboard* pCheckersBB, Square ksq, Square from,
|
||||
Square to, Bitboard dcCandidates) {
|
||||
|
||||
const bool Bishop = (Piece == QUEEN || Piece == BISHOP);
|
||||
const bool Rook = (Piece == QUEEN || Piece == ROOK);
|
||||
const bool Slider = Bishop || Rook;
|
||||
|
||||
// Direct checks
|
||||
if ( ( (Bishop && bit_is_set(BishopPseudoAttacks[ksq], to))
|
||||
|| (Rook && bit_is_set(RookPseudoAttacks[ksq], to)))
|
||||
&& bit_is_set(attacks_from<Piece>(ksq), to)) // slow, try to early skip
|
||||
set_bit(pCheckersBB, to);
|
||||
|
||||
else if ( Piece != KING
|
||||
&& !Slider
|
||||
&& bit_is_set(Piece == PAWN ? attacks_from<PAWN>(ksq, opposite_color(sideToMove))
|
||||
: attacks_from<Piece>(ksq), to))
|
||||
set_bit(pCheckersBB, to);
|
||||
|
||||
// Discovery checks
|
||||
if (Piece != QUEEN && bit_is_set(dcCandidates, from))
|
||||
{
|
||||
if (Piece != ROOK)
|
||||
(*pCheckersBB) |= (attacks_from<ROOK>(ksq) & pieces(ROOK, QUEEN, side_to_move()));
|
||||
|
||||
if (Piece != BISHOP)
|
||||
(*pCheckersBB) |= (attacks_from<BISHOP>(ksq) & pieces(BISHOP, QUEEN, side_to_move()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -688,10 +653,11 @@ inline void Position::update_checkers(Bitboard* pCheckersBB, Square ksq, Square
|
||||
|
||||
void Position::do_move(Move m, StateInfo& newSt) {
|
||||
|
||||
do_move(m, newSt, discovered_check_candidates(side_to_move()));
|
||||
CheckInfo ci(*this);
|
||||
do_move(m, newSt, ci, move_is_check(m, ci));
|
||||
}
|
||||
|
||||
void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
void Position::do_move(Move m, StateInfo& newSt, const CheckInfo& ci, bool moveIsCheck) {
|
||||
|
||||
assert(is_ok());
|
||||
assert(move_is_ok(m));
|
||||
@@ -702,10 +668,10 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
// ones which are recalculated from scratch anyway, then switch our state
|
||||
// pointer to point to the new, ready to be updated, state.
|
||||
struct ReducedStateInfo {
|
||||
Key key, pawnKey, materialKey;
|
||||
int castleRights, rule50;
|
||||
Key pawnKey, materialKey;
|
||||
int castleRights, rule50, pliesFromNull;
|
||||
Square epSquare;
|
||||
Value mgValue, egValue;
|
||||
Score value;
|
||||
Value npMaterial[2];
|
||||
};
|
||||
|
||||
@@ -724,6 +690,7 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
// Increment the 50 moves rule draw counter. Resetting it to zero in the
|
||||
// case of non-reversible moves is taken care of later.
|
||||
st->rule50++;
|
||||
st->pliesFromNull++;
|
||||
|
||||
if (move_is_castle(m))
|
||||
{
|
||||
@@ -741,16 +708,15 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
|
||||
Piece piece = piece_on(from);
|
||||
PieceType pt = type_of_piece(piece);
|
||||
PieceType capture = ep ? PAWN : type_of_piece_on(to);
|
||||
|
||||
assert(color_of_piece_on(from) == us);
|
||||
assert(color_of_piece_on(to) == them || square_is_empty(to));
|
||||
assert(!(ep || pm) || piece == piece_of_color_and_type(us, PAWN));
|
||||
assert(!pm || relative_rank(us, to) == RANK_8);
|
||||
|
||||
st->capture = ep ? PAWN : type_of_piece_on(to);
|
||||
|
||||
if (st->capture)
|
||||
do_capture_move(key, st->capture, them, to, ep);
|
||||
if (capture)
|
||||
do_capture_move(key, capture, them, to, ep);
|
||||
|
||||
// Update hash key
|
||||
key ^= zobrist[us][pt][from] ^ zobrist[us][pt][to];
|
||||
@@ -800,7 +766,7 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
st->pawnKey ^= zobrist[us][PAWN][from] ^ zobrist[us][PAWN][to];
|
||||
|
||||
// Set en passant square, only if moved pawn can be captured
|
||||
if (abs(int(to) - int(from)) == 16)
|
||||
if ((to ^ from) == 16)
|
||||
{
|
||||
if (attacks_from<PAWN>(from + (us == WHITE ? DELTA_N : DELTA_S), us) & pieces(PAWN, them))
|
||||
{
|
||||
@@ -811,8 +777,10 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
}
|
||||
|
||||
// Update incremental scores
|
||||
st->mgValue += pst_delta<MidGame>(piece, from, to);
|
||||
st->egValue += pst_delta<EndGame>(piece, from, to);
|
||||
st->value += pst_delta(piece, from, to);
|
||||
|
||||
// Set capture piece
|
||||
st->capture = capture;
|
||||
|
||||
if (pm) // promotion ?
|
||||
{
|
||||
@@ -847,10 +815,8 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
st->pawnKey ^= zobrist[us][PAWN][to];
|
||||
|
||||
// Partially revert and update incremental scores
|
||||
st->mgValue -= pst<MidGame>(us, PAWN, to);
|
||||
st->mgValue += pst<MidGame>(us, promotion, to);
|
||||
st->egValue -= pst<EndGame>(us, PAWN, to);
|
||||
st->egValue += pst<EndGame>(us, promotion, to);
|
||||
st->value -= pst(us, PAWN, to);
|
||||
st->value += pst(us, promotion, to);
|
||||
|
||||
// Update material
|
||||
st->npMaterial[us] += piece_value_midgame(promotion);
|
||||
@@ -860,29 +826,33 @@ void Position::do_move(Move m, StateInfo& newSt, Bitboard dcCandidates) {
|
||||
st->key = key;
|
||||
|
||||
// Update checkers bitboard, piece must be already moved
|
||||
st->checkersBB = EmptyBoardBB;
|
||||
|
||||
if (moveIsCheck)
|
||||
{
|
||||
if (ep | pm)
|
||||
st->checkersBB = attackers_to(king_square(them)) & pieces_of_color(us);
|
||||
else
|
||||
{
|
||||
st->checkersBB = EmptyBoardBB;
|
||||
Square ksq = king_square(them);
|
||||
switch (pt)
|
||||
// Direct checks
|
||||
if (bit_is_set(ci.checkSq[pt], to))
|
||||
st->checkersBB = SetMaskBB[to];
|
||||
|
||||
// Discovery checks
|
||||
if (ci.dcCandidates && bit_is_set(ci.dcCandidates, from))
|
||||
{
|
||||
case PAWN: update_checkers<PAWN>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
|
||||
case KNIGHT: update_checkers<KNIGHT>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
|
||||
case BISHOP: update_checkers<BISHOP>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
|
||||
case ROOK: update_checkers<ROOK>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
|
||||
case QUEEN: update_checkers<QUEEN>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
|
||||
case KING: update_checkers<KING>(&(st->checkersBB), ksq, from, to, dcCandidates); break;
|
||||
default: assert(false); break;
|
||||
if (pt != ROOK)
|
||||
st->checkersBB |= (attacks_from<ROOK>(ci.ksq) & pieces(ROOK, QUEEN, us));
|
||||
|
||||
if (pt != BISHOP)
|
||||
st->checkersBB |= (attacks_from<BISHOP>(ci.ksq) & pieces(BISHOP, QUEEN, us));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finish
|
||||
sideToMove = opposite_color(sideToMove);
|
||||
|
||||
st->mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
|
||||
st->egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
|
||||
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
|
||||
|
||||
assert(is_ok());
|
||||
}
|
||||
@@ -918,8 +888,7 @@ void Position::do_capture_move(Bitboard& key, PieceType capture, Color them, Squ
|
||||
key ^= zobrist[them][capture][capsq];
|
||||
|
||||
// Update incremental scores
|
||||
st->mgValue -= pst<MidGame>(them, capture, capsq);
|
||||
st->egValue -= pst<EndGame>(them, capture, capsq);
|
||||
st->value -= pst(them, capture, capsq);
|
||||
|
||||
// If the captured piece was a pawn, update pawn hash key,
|
||||
// otherwise update non-pawn material.
|
||||
@@ -1016,10 +985,8 @@ void Position::do_castle_move(Move m) {
|
||||
index[rto] = tmp;
|
||||
|
||||
// Update incremental scores
|
||||
st->mgValue += pst_delta<MidGame>(king, kfrom, kto);
|
||||
st->egValue += pst_delta<EndGame>(king, kfrom, kto);
|
||||
st->mgValue += pst_delta<MidGame>(rook, rfrom, rto);
|
||||
st->egValue += pst_delta<EndGame>(rook, rfrom, rto);
|
||||
st->value += pst_delta(king, kfrom, kto);
|
||||
st->value += pst_delta(rook, rfrom, rto);
|
||||
|
||||
// Update hash key
|
||||
st->key ^= zobrist[us][KING][kfrom] ^ zobrist[us][KING][kto];
|
||||
@@ -1045,9 +1012,7 @@ void Position::do_castle_move(Move m) {
|
||||
|
||||
// Finish
|
||||
sideToMove = opposite_color(sideToMove);
|
||||
|
||||
st->mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
|
||||
st->egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
|
||||
st->value += (sideToMove == WHITE ? TempoValue : -TempoValue);
|
||||
|
||||
assert(is_ok());
|
||||
}
|
||||
@@ -1239,9 +1204,9 @@ void Position::do_null_move(StateInfo& backupSt) {
|
||||
// a backup storage not as a new state to be used.
|
||||
backupSt.key = st->key;
|
||||
backupSt.epSquare = st->epSquare;
|
||||
backupSt.mgValue = st->mgValue;
|
||||
backupSt.egValue = st->egValue;
|
||||
backupSt.value = st->value;
|
||||
backupSt.previous = st->previous;
|
||||
backupSt.pliesFromNull = st->pliesFromNull;
|
||||
st->previous = &backupSt;
|
||||
|
||||
// Save the current key to the history[] array, in order to be able to
|
||||
@@ -1258,10 +1223,9 @@ void Position::do_null_move(StateInfo& backupSt) {
|
||||
sideToMove = opposite_color(sideToMove);
|
||||
st->epSquare = SQ_NONE;
|
||||
st->rule50++;
|
||||
st->pliesFromNull = 0;
|
||||
st->value += (sideToMove == WHITE) ? TempoValue : -TempoValue;
|
||||
gamePly++;
|
||||
|
||||
st->mgValue += (sideToMove == WHITE)? TempoValueMidgame : -TempoValueMidgame;
|
||||
st->egValue += (sideToMove == WHITE)? TempoValueEndgame : -TempoValueEndgame;
|
||||
}
|
||||
|
||||
|
||||
@@ -1276,9 +1240,9 @@ void Position::undo_null_move() {
|
||||
StateInfo* backupSt = st->previous;
|
||||
st->key = backupSt->key;
|
||||
st->epSquare = backupSt->epSquare;
|
||||
st->mgValue = backupSt->mgValue;
|
||||
st->egValue = backupSt->egValue;
|
||||
st->value = backupSt->value;
|
||||
st->previous = backupSt->previous;
|
||||
st->pliesFromNull = backupSt->pliesFromNull;
|
||||
|
||||
// Update the necessary information
|
||||
sideToMove = opposite_color(sideToMove);
|
||||
@@ -1346,6 +1310,10 @@ int Position::see(Square from, Square to) const {
|
||||
Piece capture = piece_on(to);
|
||||
Bitboard occ = occupied_squares();
|
||||
|
||||
// King cannot be recaptured
|
||||
if (type_of_piece(piece) == KING)
|
||||
return seeValues[capture];
|
||||
|
||||
// Handle en passant moves
|
||||
if (st->epSquare == to && type_of_piece_on(from) == PAWN)
|
||||
{
|
||||
@@ -1484,8 +1452,8 @@ void Position::clear() {
|
||||
for (int i = 0; i < 64; i++)
|
||||
board[i] = EMPTY;
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
for (int j = 0; j < 8; j++)
|
||||
for (int i = 0; i < 8; i++)
|
||||
for (int j = 0; j < 16; j++)
|
||||
pieceList[0][i][j] = pieceList[1][i][j] = SQ_NONE;
|
||||
|
||||
sideToMove = WHITE;
|
||||
@@ -1619,10 +1587,9 @@ Key Position::compute_material_key() const {
|
||||
/// game and the endgame. These functions are used to initialize the incremental
|
||||
/// scores when a new position is set up, and to verify that the scores are correctly
|
||||
/// updated by do_move and undo_move when the program is running in debug mode.
|
||||
template<Position::GamePhase Phase>
|
||||
Value Position::compute_value() const {
|
||||
Score Position::compute_value() const {
|
||||
|
||||
Value result = Value(0);
|
||||
Score result = make_score(0, 0);
|
||||
Bitboard b;
|
||||
Square s;
|
||||
|
||||
@@ -1634,12 +1601,11 @@ Value Position::compute_value() const {
|
||||
{
|
||||
s = pop_1st_bit(&b);
|
||||
assert(piece_on(s) == piece_of_color_and_type(c, pt));
|
||||
result += pst<Phase>(c, pt, s);
|
||||
result += pst(c, pt, s);
|
||||
}
|
||||
}
|
||||
|
||||
const Value TempoValue = (Phase == MidGame ? TempoValueMidgame : TempoValueEndgame);
|
||||
result += (side_to_move() == WHITE)? TempoValue / 2 : -TempoValue / 2;
|
||||
result += (side_to_move() == WHITE ? TempoValue / 2 : -TempoValue / 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1683,7 +1649,7 @@ bool Position::is_draw() const {
|
||||
return true;
|
||||
|
||||
// Draw by repetition?
|
||||
for (int i = 2; i < Min(gamePly, st->rule50); i += 2)
|
||||
for (int i = 2; i < Min(Min(gamePly, st->rule50), st->pliesFromNull); i += 2)
|
||||
if (history[gamePly - i] == st->key)
|
||||
return true;
|
||||
|
||||
@@ -1697,8 +1663,7 @@ bool Position::is_draw() const {
|
||||
bool Position::is_mate() const {
|
||||
|
||||
MoveStack moves[256];
|
||||
|
||||
return is_check() && (generate_evasions(*this, moves, pinned_pieces(sideToMove)) == moves);
|
||||
return is_check() && (generate_moves(*this, moves, false) == moves);
|
||||
}
|
||||
|
||||
|
||||
@@ -1719,11 +1684,10 @@ bool Position::has_mate_threat(Color c) {
|
||||
|
||||
MoveStack mlist[120];
|
||||
bool result = false;
|
||||
Bitboard dc = discovered_check_candidates(sideToMove);
|
||||
Bitboard pinned = pinned_pieces(sideToMove);
|
||||
|
||||
// Generate pseudo-legal non-capture and capture check moves
|
||||
MoveStack* last = generate_non_capture_checks(*this, mlist, dc);
|
||||
MoveStack* last = generate_non_capture_checks(*this, mlist);
|
||||
last = generate_captures(*this, last);
|
||||
|
||||
// Loop through the moves, and see if one of them is mate
|
||||
@@ -1773,6 +1737,8 @@ void Position::init_zobrist() {
|
||||
|
||||
for (int i = 0; i < 16; i++)
|
||||
zobMaterial[0][KING][i] = zobMaterial[1][KING][i] = Key(0ULL);
|
||||
|
||||
zobExclusion = genrand_int64();
|
||||
}
|
||||
|
||||
|
||||
@@ -1790,16 +1756,12 @@ void Position::init_piece_square_tables() {
|
||||
for (Piece p = WP; p <= WK; p++)
|
||||
{
|
||||
i = (r == 0)? 0 : (genrand_int32() % (r*2) - r);
|
||||
MgPieceSquareTable[p][s] = Value(MgPST[p][s] + i);
|
||||
EgPieceSquareTable[p][s] = Value(EgPST[p][s] + i);
|
||||
PieceSquareTable[p][s] = make_score(MgPST[p][s] + i, EgPST[p][s] + i);
|
||||
}
|
||||
|
||||
for (Square s = SQ_A1; s <= SQ_H8; s++)
|
||||
for (Piece p = BP; p <= BK; p++)
|
||||
{
|
||||
MgPieceSquareTable[p][s] = -MgPieceSquareTable[p-8][flip_square(s)];
|
||||
EgPieceSquareTable[p][s] = -EgPieceSquareTable[p-8][flip_square(s)];
|
||||
}
|
||||
PieceSquareTable[p][s] = -PieceSquareTable[p-8][flip_square(s)];
|
||||
}
|
||||
|
||||
|
||||
@@ -1854,8 +1816,7 @@ void Position::flipped_copy(const Position& pos) {
|
||||
st->materialKey = compute_material_key();
|
||||
|
||||
// Incremental scores
|
||||
st->mgValue = compute_value<MidGame>();
|
||||
st->egValue = compute_value<EndGame>();
|
||||
st->value = compute_value();
|
||||
|
||||
// Material
|
||||
st->npMaterial[WHITE] = compute_non_pawn_material(WHITE);
|
||||
@@ -1982,15 +1943,9 @@ bool Position::is_ok(int* failedStep) const {
|
||||
|
||||
// Incremental eval OK?
|
||||
if (failedStep) (*failedStep)++;
|
||||
if (debugIncrementalEval)
|
||||
{
|
||||
if (st->mgValue != compute_value<MidGame>())
|
||||
if (debugIncrementalEval && st->value != compute_value())
|
||||
return false;
|
||||
|
||||
if (st->egValue != compute_value<EndGame>())
|
||||
return false;
|
||||
}
|
||||
|
||||
// Non-pawn material OK?
|
||||
if (failedStep) (*failedStep)++;
|
||||
if (debugNonPawnMaterial)
|
||||
|
||||
@@ -63,6 +63,18 @@ const int MaxGameLength = 220;
|
||||
//// Types
|
||||
////
|
||||
|
||||
/// struct checkInfo is initialized at c'tor time and keeps
|
||||
/// info used to detect if a move gives check.
|
||||
|
||||
struct CheckInfo {
|
||||
|
||||
CheckInfo(const Position&);
|
||||
|
||||
Square ksq;
|
||||
Bitboard dcCandidates;
|
||||
Bitboard checkSq[8];
|
||||
};
|
||||
|
||||
/// Castle rights, encoded as bit fields
|
||||
|
||||
enum CastleRights {
|
||||
@@ -87,12 +99,13 @@ enum Phase {
|
||||
/// must be passed as a parameter.
|
||||
|
||||
struct StateInfo {
|
||||
Key key, pawnKey, materialKey;
|
||||
int castleRights, rule50;
|
||||
Key pawnKey, materialKey;
|
||||
int castleRights, rule50, pliesFromNull;
|
||||
Square epSquare;
|
||||
Value mgValue, egValue;
|
||||
Score value;
|
||||
Value npMaterial[2];
|
||||
|
||||
Key key;
|
||||
PieceType capture;
|
||||
Bitboard checkersBB;
|
||||
StateInfo* previous;
|
||||
@@ -202,11 +215,12 @@ public:
|
||||
template<PieceType> Bitboard attacks_from(Square s, Color c) const;
|
||||
|
||||
// Properties of moves
|
||||
bool pl_move_is_legal(Move m) const;
|
||||
bool pl_move_is_legal(Move m, Bitboard pinned) const;
|
||||
bool pl_move_is_evasion(Move m, Bitboard pinned) const;
|
||||
bool move_is_check(Move m) const;
|
||||
bool move_is_check(Move m, Bitboard dcCandidates) const;
|
||||
bool move_is_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;
|
||||
|
||||
@@ -222,7 +236,7 @@ public:
|
||||
// Doing and undoing moves
|
||||
void saveState();
|
||||
void do_move(Move m, StateInfo& st);
|
||||
void do_move(Move m, StateInfo& st, Bitboard dcCandidates);
|
||||
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();
|
||||
@@ -235,15 +249,14 @@ public:
|
||||
|
||||
// Accessing hash keys
|
||||
Key get_key() const;
|
||||
Key get_exclusion_key() const;
|
||||
Key get_pawn_key() const;
|
||||
Key get_material_key() const;
|
||||
|
||||
// Incremental evaluation
|
||||
Value mg_value() const;
|
||||
Value eg_value() const;
|
||||
Score value() const;
|
||||
Value non_pawn_material(Color c) const;
|
||||
Phase game_phase() const;
|
||||
template<GamePhase> Value pst_delta(Piece piece, Square from, Square to) const;
|
||||
Score pst_delta(Piece piece, Square from, Square to) const;
|
||||
|
||||
// Game termination checks
|
||||
bool is_mate() const;
|
||||
@@ -283,9 +296,6 @@ private:
|
||||
void undo_castle_move(Move m);
|
||||
void find_checkers();
|
||||
|
||||
template<PieceType Piece>
|
||||
void update_checkers(Bitboard* pCheckersBB, Square ksq, Square from, Square to, Bitboard dcCandidates);
|
||||
|
||||
template<bool FindPinned>
|
||||
Bitboard hidden_checkers(Color c) const;
|
||||
|
||||
@@ -295,8 +305,8 @@ private:
|
||||
Key compute_material_key() const;
|
||||
|
||||
// Computing incremental evaluation scores and material counts
|
||||
template<GamePhase> Value pst(Color c, PieceType pt, Square s) const;
|
||||
template<GamePhase> Value compute_value() const;
|
||||
Score pst(Color c, PieceType pt, Square s) const;
|
||||
Score compute_value() const;
|
||||
Value compute_non_pawn_material(Color c) const;
|
||||
|
||||
// Board
|
||||
@@ -327,8 +337,8 @@ private:
|
||||
static Key zobCastle[16];
|
||||
static Key zobMaterial[2][8][16];
|
||||
static Key zobSideToMove;
|
||||
static Value MgPieceSquareTable[16][64];
|
||||
static Value EgPieceSquareTable[16][64];
|
||||
static Score PieceSquareTable[16][64];
|
||||
static Key zobExclusion;
|
||||
};
|
||||
|
||||
|
||||
@@ -493,6 +503,10 @@ inline Key Position::get_key() const {
|
||||
return st->key;
|
||||
}
|
||||
|
||||
inline Key Position::get_exclusion_key() const {
|
||||
return st->key ^ zobExclusion;
|
||||
}
|
||||
|
||||
inline Key Position::get_pawn_key() const {
|
||||
return st->pawnKey;
|
||||
}
|
||||
@@ -501,46 +515,22 @@ inline Key Position::get_material_key() const {
|
||||
return st->materialKey;
|
||||
}
|
||||
|
||||
template<Position::GamePhase Ph>
|
||||
inline Value Position::pst(Color c, PieceType pt, Square s) const {
|
||||
return (Ph == MidGame ? MgPieceSquareTable[piece_of_color_and_type(c, pt)][s]
|
||||
: EgPieceSquareTable[piece_of_color_and_type(c, pt)][s]);
|
||||
inline Score Position::pst(Color c, PieceType pt, Square s) const {
|
||||
return PieceSquareTable[piece_of_color_and_type(c, pt)][s];
|
||||
}
|
||||
|
||||
template<Position::GamePhase Ph>
|
||||
inline Value Position::pst_delta(Piece piece, Square from, Square to) const {
|
||||
return (Ph == MidGame ? MgPieceSquareTable[piece][to] - MgPieceSquareTable[piece][from]
|
||||
: EgPieceSquareTable[piece][to] - EgPieceSquareTable[piece][from]);
|
||||
inline Score Position::pst_delta(Piece piece, Square from, Square to) const {
|
||||
return PieceSquareTable[piece][to] - PieceSquareTable[piece][from];
|
||||
}
|
||||
|
||||
inline Value Position::mg_value() const {
|
||||
return st->mgValue;
|
||||
}
|
||||
|
||||
inline Value Position::eg_value() const {
|
||||
return st->egValue;
|
||||
inline Score Position::value() const {
|
||||
return st->value;
|
||||
}
|
||||
|
||||
inline Value Position::non_pawn_material(Color c) const {
|
||||
return st->npMaterial[c];
|
||||
}
|
||||
|
||||
inline Phase Position::game_phase() const {
|
||||
|
||||
// Values modified by Joona Kiiski
|
||||
static const Value MidgameLimit = Value(15581);
|
||||
static const Value EndgameLimit = Value(3998);
|
||||
|
||||
Value npm = non_pawn_material(WHITE) + non_pawn_material(BLACK);
|
||||
|
||||
if (npm >= MidgameLimit)
|
||||
return PHASE_MIDGAME;
|
||||
else if(npm <= EndgameLimit)
|
||||
return PHASE_ENDGAME;
|
||||
else
|
||||
return Phase(((npm - EndgameLimit) * 128) / (MidgameLimit - EndgameLimit));
|
||||
}
|
||||
|
||||
inline bool Position::move_is_passed_pawn_push(Move m) const {
|
||||
|
||||
Color c = side_to_move();
|
||||
@@ -568,8 +558,13 @@ inline bool Position::has_pawn_on_7th(Color c) const {
|
||||
inline bool Position::move_is_capture(Move m) const {
|
||||
|
||||
// Move must not be MOVE_NONE !
|
||||
return (m & (3 << 15)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
|
||||
}
|
||||
|
||||
return (!square_is_empty(move_to(m)) && !move_is_castle(m)) || move_is_ep(m);
|
||||
inline bool Position::move_is_capture_or_promotion(Move m) const {
|
||||
|
||||
// Move must not be MOVE_NONE !
|
||||
return (m & (0x1F << 12)) ? !move_is_castle(m) : !square_is_empty(move_to(m));
|
||||
}
|
||||
|
||||
#endif // !defined(POSITION_H_INCLUDED)
|
||||
|
||||
@@ -142,13 +142,14 @@ Move move_from_san(const Position& pos, const string& movestr) {
|
||||
assert(pos.is_ok());
|
||||
|
||||
MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
|
||||
// Castling moves
|
||||
if (movestr == "O-O-O" || movestr == "O-O-O+")
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_long_castle(m) && pos.pl_move_is_legal(m))
|
||||
if (move_is_long_castle(m) && pos.pl_move_is_legal(m, pinned))
|
||||
return m;
|
||||
|
||||
return MOVE_NONE;
|
||||
@@ -157,7 +158,7 @@ Move move_from_san(const Position& pos, const string& movestr) {
|
||||
{
|
||||
Move m;
|
||||
while ((m = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_is_short_castle(m) && pos.pl_move_is_legal(m))
|
||||
if (move_is_short_castle(m) && pos.pl_move_is_legal(m, pinned))
|
||||
return m;
|
||||
|
||||
return MOVE_NONE;
|
||||
@@ -367,11 +368,12 @@ namespace {
|
||||
return AMBIGUITY_NONE;
|
||||
|
||||
MovePicker mp = MovePicker(pos, MOVE_NONE, OnePly, H);
|
||||
Bitboard pinned = pos.pinned_pieces(pos.side_to_move());
|
||||
Move mv, moveList[8];
|
||||
|
||||
int n = 0;
|
||||
while ((mv = mp.get_next_move()) != MOVE_NONE)
|
||||
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv))
|
||||
if (move_to(mv) == to && pos.piece_on(move_from(mv)) == pc && pos.pl_move_is_legal(mv, pinned))
|
||||
moveList[n++] = mv;
|
||||
|
||||
if (n == 1)
|
||||
|
||||
590
src/search.cpp
590
src/search.cpp
File diff suppressed because it is too large
Load Diff
@@ -48,7 +48,7 @@ const int KILLER_MAX = 2;
|
||||
/// current ply.
|
||||
|
||||
struct SearchStack {
|
||||
Move pv[PLY_MAX];
|
||||
Move pv[PLY_MAX_PLUS_2];
|
||||
Move currentMove;
|
||||
Move mateKiller;
|
||||
Move threatMove;
|
||||
@@ -69,6 +69,7 @@ extern void stop_threads();
|
||||
extern bool think(const Position &pos, bool infinite, bool ponder, int side_to_move,
|
||||
int time[], int increment[], int movesToGo, int maxDepth,
|
||||
int maxNodes, int maxTime, Move searchMoves[]);
|
||||
extern int perft(Position &pos, Depth depth);
|
||||
extern int64_t nodes_searched();
|
||||
|
||||
|
||||
|
||||
@@ -46,13 +46,13 @@ const int THREAD_MAX = 8;
|
||||
struct SplitPoint {
|
||||
SplitPoint *parent;
|
||||
Position pos;
|
||||
SearchStack sstack[THREAD_MAX][PLY_MAX];
|
||||
SearchStack sstack[THREAD_MAX][PLY_MAX_PLUS_2];
|
||||
SearchStack *parentSstack;
|
||||
int ply;
|
||||
Depth depth;
|
||||
volatile Value alpha, beta, bestValue;
|
||||
volatile Value alpha, beta, bestValue, futilityValue;
|
||||
Value approximateEval;
|
||||
bool pvNode;
|
||||
Bitboard dcCandidates;
|
||||
int master, slaves[THREAD_MAX];
|
||||
Lock lock;
|
||||
MovePicker *mp;
|
||||
|
||||
@@ -208,6 +208,8 @@ void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
|
||||
|
||||
for (int i = 0; pv[i] != MOVE_NONE; i++)
|
||||
{
|
||||
TTEntry *tte = retrieve(p.get_key());
|
||||
if (!tte || tte->move() != pv[i])
|
||||
store(p.get_key(), VALUE_NONE, VALUE_TYPE_NONE, Depth(-127*OnePly), pv[i]);
|
||||
p.do_move(pv[i], st);
|
||||
}
|
||||
@@ -220,7 +222,7 @@ void TranspositionTable::insert_pv(const Position& pos, Move pv[]) {
|
||||
/// will often get single-move PVs when the search stops while failing high,
|
||||
/// and a single-move PV means that we don't have a ponder move.
|
||||
|
||||
void TranspositionTable::extract_pv(const Position& pos, Move pv[], int pvSize) {
|
||||
void TranspositionTable::extract_pv(const Position& pos, Move pv[], const int PLY_MAX) {
|
||||
|
||||
const TTEntry* tte;
|
||||
StateInfo st;
|
||||
@@ -236,7 +238,7 @@ void TranspositionTable::extract_pv(const Position& pos, Move pv[], int pvSize)
|
||||
&& tte->move() != MOVE_NONE
|
||||
&& move_is_legal(p, tte->move())
|
||||
&& (!p.is_draw() || ply < 2)
|
||||
&& ply < pvSize)
|
||||
&& ply < PLY_MAX)
|
||||
{
|
||||
pv[ply] = tte->move();
|
||||
p.do_move(pv[ply++], st);
|
||||
|
||||
2
src/tt.h
2
src/tt.h
@@ -102,7 +102,7 @@ public:
|
||||
void prefetch(const Key posKey) const;
|
||||
void new_search();
|
||||
void insert_pv(const Position& pos, Move pv[]);
|
||||
void extract_pv(const Position& pos, Move pv[], int pvSize);
|
||||
void extract_pv(const Position& pos, Move pv[], const int PLY_MAX);
|
||||
int full() const;
|
||||
|
||||
private:
|
||||
|
||||
@@ -33,7 +33,7 @@ typedef __int16 int16;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef __int64 int64;
|
||||
typedef __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
|
||||
typedef __int16 int16_t;
|
||||
|
||||
28
src/uci.cpp
28
src/uci.cpp
@@ -61,6 +61,7 @@ namespace {
|
||||
void set_option(UCIInputParser& uip);
|
||||
void set_position(UCIInputParser& uip);
|
||||
bool go(UCIInputParser& uip);
|
||||
void perft(UCIInputParser& uip);
|
||||
}
|
||||
|
||||
|
||||
@@ -123,7 +124,7 @@ namespace {
|
||||
}
|
||||
else if (token == "ucinewgame")
|
||||
{
|
||||
push_button("Clear Hash");
|
||||
push_button("New Game");
|
||||
Position::init_piece_square_tables();
|
||||
RootPosition.from_fen(StartPosition);
|
||||
}
|
||||
@@ -147,14 +148,16 @@ namespace {
|
||||
else if (token == "eval")
|
||||
{
|
||||
EvalInfo ei;
|
||||
cout << "Incremental mg: " << RootPosition.mg_value()
|
||||
<< "\nIncremental eg: " << RootPosition.eg_value()
|
||||
cout << "Incremental mg: " << mg_value(RootPosition.value())
|
||||
<< "\nIncremental eg: " << eg_value(RootPosition.value())
|
||||
<< "\nFull eval: " << evaluate(RootPosition, ei, 0) << endl;
|
||||
}
|
||||
else if (token == "key")
|
||||
cout << "key: " << hex << RootPosition.get_key()
|
||||
<< "\nmaterial key: " << RootPosition.get_material_key()
|
||||
<< "\npawn key: " << RootPosition.get_pawn_key() << endl;
|
||||
else if (token == "perft")
|
||||
perft(uip);
|
||||
else
|
||||
{
|
||||
cout << "Unknown command: " << command << endl;
|
||||
@@ -318,4 +321,23 @@ namespace {
|
||||
time, inc, movesToGo, depth, nodes, moveTime, searchMoves);
|
||||
}
|
||||
|
||||
void perft(UCIInputParser& uip) {
|
||||
|
||||
string token;
|
||||
int depth, tm, n;
|
||||
Position pos = RootPosition;
|
||||
|
||||
if (uip.eof())
|
||||
return;
|
||||
|
||||
uip >> depth;
|
||||
tm = get_system_time();
|
||||
|
||||
n = perft(pos, depth * OnePly);
|
||||
|
||||
tm = get_system_time() - tm;
|
||||
std::cout << "\nNodes " << n
|
||||
<< "\nTime (ms) " << tm
|
||||
<< "\nNodes/second " << (int)(n/(tm/1000.0)) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,6 +125,7 @@ namespace {
|
||||
o["Threads"] = Option(1, 1, 8);
|
||||
o["Hash"] = Option(32, 4, 4096);
|
||||
o["Clear Hash"] = Option(false, BUTTON);
|
||||
o["New Game"] = Option(false, BUTTON);
|
||||
o["Ponder"] = Option(true);
|
||||
o["OwnBook"] = Option(true);
|
||||
o["MultiPV"] = Option(1, 1, 500);
|
||||
@@ -169,6 +170,18 @@ namespace {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Specialization for std::string where instruction 'ss >> ret;'
|
||||
// would erroneusly tokenize a string with spaces.
|
||||
|
||||
template<>
|
||||
string get_option_value<string>(const string& optionName) {
|
||||
|
||||
if (options.find(optionName) == options.end())
|
||||
return string();
|
||||
|
||||
return options[optionName].currentValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
29
src/value.h
29
src/value.h
@@ -52,6 +52,32 @@ enum Value {
|
||||
};
|
||||
|
||||
|
||||
/// 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.
|
||||
|
||||
enum Score {};
|
||||
|
||||
inline Value eg_value(Score s) { return Value(int16_t(s & 0xffff)); }
|
||||
inline Value mg_value(Score s) { return Value((int(s) + 32768) >> 16); }
|
||||
|
||||
inline Score make_score(int mg, int eg) { return Score((mg << 16) + eg); }
|
||||
|
||||
inline Score operator-(Score s) { return Score(-int(s)); }
|
||||
inline Score operator+(Score s1, Score s2) { return Score(int(s1) + int(s2)); }
|
||||
inline Score operator-(Score s1, Score s2) { return Score(int(s1) - int(s2)); }
|
||||
inline void operator+=(Score& s1, Score s2) { s1 = Score(int(s1) + int(s2)); }
|
||||
inline void operator-=(Score& s1, Score s2) { s1 = Score(int(s1) - int(s2)); }
|
||||
inline Score operator*(int i, Score s) { return Score(i * int(s)); }
|
||||
|
||||
// Division must be handled separately for each term
|
||||
inline Score operator/(Score s, int i) { return make_score(mg_value(s) / i, eg_value(s) / i); }
|
||||
|
||||
// Only declared but not defined. We don't want to multiply two scores due to
|
||||
// a very high risk of overflow. So user should explicitly convert to integer.
|
||||
inline Score operator*(Score s1, Score s2);
|
||||
|
||||
|
||||
////
|
||||
//// Constants and variables
|
||||
////
|
||||
@@ -97,8 +123,7 @@ const Value PieceValueEndgame[17] = {
|
||||
|
||||
/// Bonus for having the side to move (modified by Joona Kiiski)
|
||||
|
||||
const Value TempoValueMidgame = Value(48);
|
||||
const Value TempoValueEndgame = Value(22);
|
||||
const Score TempoValue = make_score(48, 22);
|
||||
|
||||
|
||||
////
|
||||
|
||||
Reference in New Issue
Block a user