mirror of
https://github.com/official-stockfish/Stockfish.git
synced 2026-07-23 05:07:14 +00:00
Merge commit '738ac2a10025ca58198e3d2d7f0bc70d83c2cb7f' into cluster
This is the last commit before there are more conflicts.
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"config": [
|
||||
{
|
||||
"name": "Ubuntu 20.04 GCC",
|
||||
"os": "ubuntu-20.04",
|
||||
"name": "Ubuntu 22.04 GCC",
|
||||
"os": "ubuntu-22.04",
|
||||
"simple_name": "ubuntu",
|
||||
"compiler": "g++",
|
||||
"comp": "gcc",
|
||||
@@ -111,7 +111,7 @@
|
||||
{
|
||||
"binaries": "x86-64-avxvnni",
|
||||
"config": {
|
||||
"ubuntu-20.04": null
|
||||
"ubuntu-22.04": null
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -153,7 +153,7 @@
|
||||
{
|
||||
"binaries": "apple-silicon",
|
||||
"config": {
|
||||
"os": "ubuntu-20.04"
|
||||
"os": "ubuntu-22.04"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -18,7 +18,7 @@ permissions:
|
||||
jobs:
|
||||
Clang-Format:
|
||||
name: Clang-Format
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
|
||||
@@ -13,15 +13,15 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- name: Ubuntu 20.04 GCC
|
||||
os: ubuntu-20.04
|
||||
- name: Ubuntu 22.04 GCC
|
||||
os: ubuntu-22.04
|
||||
compiler: g++
|
||||
comp: gcc
|
||||
run_32bit_tests: true
|
||||
run_64bit_tests: true
|
||||
shell: bash
|
||||
- name: Ubuntu 20.04 Clang
|
||||
os: ubuntu-20.04
|
||||
- name: Ubuntu 22.04 Clang
|
||||
os: ubuntu-22.04
|
||||
compiler: clang++
|
||||
comp: clang
|
||||
run_32bit_tests: true
|
||||
|
||||
+1
-2
@@ -49,7 +49,7 @@ further discussion._
|
||||
- Provide a clear and concise description of the changes in the pull request
|
||||
description.
|
||||
|
||||
_First time contributors should add their name to [AUTHORS](../AUTHORS)._
|
||||
_First time contributors should add their name to [AUTHORS](./AUTHORS)._
|
||||
|
||||
_Stockfish's development is not focused on adding new features. Thus any pull
|
||||
request introducing new features will potentially be closed without further
|
||||
@@ -86,7 +86,6 @@ more details.
|
||||
|
||||
Thank you for contributing to Stockfish and helping us make it even better!
|
||||
|
||||
|
||||
[copying-link]: https://github.com/official-stockfish/Stockfish/blob/master/Copying.txt
|
||||
[discord-link]: https://discord.gg/GWDRS3kU6R
|
||||
[discussions-link]: https://github.com/official-stockfish/Stockfish/discussions/new
|
||||
|
||||
@@ -76,7 +76,7 @@ case $uname_s in
|
||||
case $uname_m in
|
||||
'arm64')
|
||||
true_arch='apple-silicon'
|
||||
file_arch='x86-64-sse41-popcnt' # Supported by Rosetta 2
|
||||
file_arch='m1-apple-silicon'
|
||||
;;
|
||||
'x86_64')
|
||||
flags=$(sysctl -n machdep.cpu.features machdep.cpu.leaf7_features | tr '\n' ' ' | tr '[:upper:]' '[:lower:]' | tr -d '_.')
|
||||
|
||||
+2
-2
@@ -162,6 +162,7 @@ void MovePicker::score() {
|
||||
m.value += (*continuationHistory[1])[pc][to];
|
||||
m.value += (*continuationHistory[2])[pc][to];
|
||||
m.value += (*continuationHistory[3])[pc][to];
|
||||
m.value += (*continuationHistory[4])[pc][to] / 3;
|
||||
m.value += (*continuationHistory[5])[pc][to];
|
||||
|
||||
// bonus for checks
|
||||
@@ -186,8 +187,7 @@ void MovePicker::score() {
|
||||
else // Type == EVASIONS
|
||||
{
|
||||
if (pos.capture_stage(m))
|
||||
m.value =
|
||||
PieceValue[pos.piece_on(m.to_sq())] - type_of(pos.moved_piece(m)) + (1 << 28);
|
||||
m.value = PieceValue[pos.piece_on(m.to_sq())] + (1 << 28);
|
||||
else
|
||||
m.value = (*mainHistory)[pos.side_to_move()][m.from_to()]
|
||||
+ (*continuationHistory[0])[pos.moved_piece(m)][m.to_sq()]
|
||||
|
||||
@@ -38,17 +38,99 @@
|
||||
namespace Stockfish::Eval::NNUE::Layers {
|
||||
|
||||
#if (USE_SSSE3 | (USE_NEON >= 8))
|
||||
alignas(CacheLineSize) static inline const
|
||||
std::array<std::array<std::uint16_t, 8>, 256> lookup_indices = []() {
|
||||
std::array<std::array<std::uint16_t, 8>, 256> v{};
|
||||
for (unsigned i = 0; i < 256; ++i)
|
||||
{
|
||||
std::uint64_t j = i, k = 0;
|
||||
while (j)
|
||||
v[i][k++] = pop_lsb(j);
|
||||
}
|
||||
return v;
|
||||
}();
|
||||
|
||||
#if (USE_SSE41)
|
||||
alignas(CacheLineSize) static constexpr std::uint8_t
|
||||
#else
|
||||
alignas(CacheLineSize) static constexpr std::uint16_t
|
||||
#endif
|
||||
lookup_indices[256][8] = {
|
||||
{0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 1, 0, 0, 0, 0, 0, 0}, {2, 0, 0, 0, 0, 0, 0, 0}, {0, 2, 0, 0, 0, 0, 0, 0},
|
||||
{1, 2, 0, 0, 0, 0, 0, 0}, {0, 1, 2, 0, 0, 0, 0, 0}, {3, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 3, 0, 0, 0, 0, 0, 0}, {1, 3, 0, 0, 0, 0, 0, 0}, {0, 1, 3, 0, 0, 0, 0, 0},
|
||||
{2, 3, 0, 0, 0, 0, 0, 0}, {0, 2, 3, 0, 0, 0, 0, 0}, {1, 2, 3, 0, 0, 0, 0, 0},
|
||||
{0, 1, 2, 3, 0, 0, 0, 0}, {4, 0, 0, 0, 0, 0, 0, 0}, {0, 4, 0, 0, 0, 0, 0, 0},
|
||||
{1, 4, 0, 0, 0, 0, 0, 0}, {0, 1, 4, 0, 0, 0, 0, 0}, {2, 4, 0, 0, 0, 0, 0, 0},
|
||||
{0, 2, 4, 0, 0, 0, 0, 0}, {1, 2, 4, 0, 0, 0, 0, 0}, {0, 1, 2, 4, 0, 0, 0, 0},
|
||||
{3, 4, 0, 0, 0, 0, 0, 0}, {0, 3, 4, 0, 0, 0, 0, 0}, {1, 3, 4, 0, 0, 0, 0, 0},
|
||||
{0, 1, 3, 4, 0, 0, 0, 0}, {2, 3, 4, 0, 0, 0, 0, 0}, {0, 2, 3, 4, 0, 0, 0, 0},
|
||||
{1, 2, 3, 4, 0, 0, 0, 0}, {0, 1, 2, 3, 4, 0, 0, 0}, {5, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 5, 0, 0, 0, 0, 0, 0}, {1, 5, 0, 0, 0, 0, 0, 0}, {0, 1, 5, 0, 0, 0, 0, 0},
|
||||
{2, 5, 0, 0, 0, 0, 0, 0}, {0, 2, 5, 0, 0, 0, 0, 0}, {1, 2, 5, 0, 0, 0, 0, 0},
|
||||
{0, 1, 2, 5, 0, 0, 0, 0}, {3, 5, 0, 0, 0, 0, 0, 0}, {0, 3, 5, 0, 0, 0, 0, 0},
|
||||
{1, 3, 5, 0, 0, 0, 0, 0}, {0, 1, 3, 5, 0, 0, 0, 0}, {2, 3, 5, 0, 0, 0, 0, 0},
|
||||
{0, 2, 3, 5, 0, 0, 0, 0}, {1, 2, 3, 5, 0, 0, 0, 0}, {0, 1, 2, 3, 5, 0, 0, 0},
|
||||
{4, 5, 0, 0, 0, 0, 0, 0}, {0, 4, 5, 0, 0, 0, 0, 0}, {1, 4, 5, 0, 0, 0, 0, 0},
|
||||
{0, 1, 4, 5, 0, 0, 0, 0}, {2, 4, 5, 0, 0, 0, 0, 0}, {0, 2, 4, 5, 0, 0, 0, 0},
|
||||
{1, 2, 4, 5, 0, 0, 0, 0}, {0, 1, 2, 4, 5, 0, 0, 0}, {3, 4, 5, 0, 0, 0, 0, 0},
|
||||
{0, 3, 4, 5, 0, 0, 0, 0}, {1, 3, 4, 5, 0, 0, 0, 0}, {0, 1, 3, 4, 5, 0, 0, 0},
|
||||
{2, 3, 4, 5, 0, 0, 0, 0}, {0, 2, 3, 4, 5, 0, 0, 0}, {1, 2, 3, 4, 5, 0, 0, 0},
|
||||
{0, 1, 2, 3, 4, 5, 0, 0}, {6, 0, 0, 0, 0, 0, 0, 0}, {0, 6, 0, 0, 0, 0, 0, 0},
|
||||
{1, 6, 0, 0, 0, 0, 0, 0}, {0, 1, 6, 0, 0, 0, 0, 0}, {2, 6, 0, 0, 0, 0, 0, 0},
|
||||
{0, 2, 6, 0, 0, 0, 0, 0}, {1, 2, 6, 0, 0, 0, 0, 0}, {0, 1, 2, 6, 0, 0, 0, 0},
|
||||
{3, 6, 0, 0, 0, 0, 0, 0}, {0, 3, 6, 0, 0, 0, 0, 0}, {1, 3, 6, 0, 0, 0, 0, 0},
|
||||
{0, 1, 3, 6, 0, 0, 0, 0}, {2, 3, 6, 0, 0, 0, 0, 0}, {0, 2, 3, 6, 0, 0, 0, 0},
|
||||
{1, 2, 3, 6, 0, 0, 0, 0}, {0, 1, 2, 3, 6, 0, 0, 0}, {4, 6, 0, 0, 0, 0, 0, 0},
|
||||
{0, 4, 6, 0, 0, 0, 0, 0}, {1, 4, 6, 0, 0, 0, 0, 0}, {0, 1, 4, 6, 0, 0, 0, 0},
|
||||
{2, 4, 6, 0, 0, 0, 0, 0}, {0, 2, 4, 6, 0, 0, 0, 0}, {1, 2, 4, 6, 0, 0, 0, 0},
|
||||
{0, 1, 2, 4, 6, 0, 0, 0}, {3, 4, 6, 0, 0, 0, 0, 0}, {0, 3, 4, 6, 0, 0, 0, 0},
|
||||
{1, 3, 4, 6, 0, 0, 0, 0}, {0, 1, 3, 4, 6, 0, 0, 0}, {2, 3, 4, 6, 0, 0, 0, 0},
|
||||
{0, 2, 3, 4, 6, 0, 0, 0}, {1, 2, 3, 4, 6, 0, 0, 0}, {0, 1, 2, 3, 4, 6, 0, 0},
|
||||
{5, 6, 0, 0, 0, 0, 0, 0}, {0, 5, 6, 0, 0, 0, 0, 0}, {1, 5, 6, 0, 0, 0, 0, 0},
|
||||
{0, 1, 5, 6, 0, 0, 0, 0}, {2, 5, 6, 0, 0, 0, 0, 0}, {0, 2, 5, 6, 0, 0, 0, 0},
|
||||
{1, 2, 5, 6, 0, 0, 0, 0}, {0, 1, 2, 5, 6, 0, 0, 0}, {3, 5, 6, 0, 0, 0, 0, 0},
|
||||
{0, 3, 5, 6, 0, 0, 0, 0}, {1, 3, 5, 6, 0, 0, 0, 0}, {0, 1, 3, 5, 6, 0, 0, 0},
|
||||
{2, 3, 5, 6, 0, 0, 0, 0}, {0, 2, 3, 5, 6, 0, 0, 0}, {1, 2, 3, 5, 6, 0, 0, 0},
|
||||
{0, 1, 2, 3, 5, 6, 0, 0}, {4, 5, 6, 0, 0, 0, 0, 0}, {0, 4, 5, 6, 0, 0, 0, 0},
|
||||
{1, 4, 5, 6, 0, 0, 0, 0}, {0, 1, 4, 5, 6, 0, 0, 0}, {2, 4, 5, 6, 0, 0, 0, 0},
|
||||
{0, 2, 4, 5, 6, 0, 0, 0}, {1, 2, 4, 5, 6, 0, 0, 0}, {0, 1, 2, 4, 5, 6, 0, 0},
|
||||
{3, 4, 5, 6, 0, 0, 0, 0}, {0, 3, 4, 5, 6, 0, 0, 0}, {1, 3, 4, 5, 6, 0, 0, 0},
|
||||
{0, 1, 3, 4, 5, 6, 0, 0}, {2, 3, 4, 5, 6, 0, 0, 0}, {0, 2, 3, 4, 5, 6, 0, 0},
|
||||
{1, 2, 3, 4, 5, 6, 0, 0}, {0, 1, 2, 3, 4, 5, 6, 0}, {7, 0, 0, 0, 0, 0, 0, 0},
|
||||
{0, 7, 0, 0, 0, 0, 0, 0}, {1, 7, 0, 0, 0, 0, 0, 0}, {0, 1, 7, 0, 0, 0, 0, 0},
|
||||
{2, 7, 0, 0, 0, 0, 0, 0}, {0, 2, 7, 0, 0, 0, 0, 0}, {1, 2, 7, 0, 0, 0, 0, 0},
|
||||
{0, 1, 2, 7, 0, 0, 0, 0}, {3, 7, 0, 0, 0, 0, 0, 0}, {0, 3, 7, 0, 0, 0, 0, 0},
|
||||
{1, 3, 7, 0, 0, 0, 0, 0}, {0, 1, 3, 7, 0, 0, 0, 0}, {2, 3, 7, 0, 0, 0, 0, 0},
|
||||
{0, 2, 3, 7, 0, 0, 0, 0}, {1, 2, 3, 7, 0, 0, 0, 0}, {0, 1, 2, 3, 7, 0, 0, 0},
|
||||
{4, 7, 0, 0, 0, 0, 0, 0}, {0, 4, 7, 0, 0, 0, 0, 0}, {1, 4, 7, 0, 0, 0, 0, 0},
|
||||
{0, 1, 4, 7, 0, 0, 0, 0}, {2, 4, 7, 0, 0, 0, 0, 0}, {0, 2, 4, 7, 0, 0, 0, 0},
|
||||
{1, 2, 4, 7, 0, 0, 0, 0}, {0, 1, 2, 4, 7, 0, 0, 0}, {3, 4, 7, 0, 0, 0, 0, 0},
|
||||
{0, 3, 4, 7, 0, 0, 0, 0}, {1, 3, 4, 7, 0, 0, 0, 0}, {0, 1, 3, 4, 7, 0, 0, 0},
|
||||
{2, 3, 4, 7, 0, 0, 0, 0}, {0, 2, 3, 4, 7, 0, 0, 0}, {1, 2, 3, 4, 7, 0, 0, 0},
|
||||
{0, 1, 2, 3, 4, 7, 0, 0}, {5, 7, 0, 0, 0, 0, 0, 0}, {0, 5, 7, 0, 0, 0, 0, 0},
|
||||
{1, 5, 7, 0, 0, 0, 0, 0}, {0, 1, 5, 7, 0, 0, 0, 0}, {2, 5, 7, 0, 0, 0, 0, 0},
|
||||
{0, 2, 5, 7, 0, 0, 0, 0}, {1, 2, 5, 7, 0, 0, 0, 0}, {0, 1, 2, 5, 7, 0, 0, 0},
|
||||
{3, 5, 7, 0, 0, 0, 0, 0}, {0, 3, 5, 7, 0, 0, 0, 0}, {1, 3, 5, 7, 0, 0, 0, 0},
|
||||
{0, 1, 3, 5, 7, 0, 0, 0}, {2, 3, 5, 7, 0, 0, 0, 0}, {0, 2, 3, 5, 7, 0, 0, 0},
|
||||
{1, 2, 3, 5, 7, 0, 0, 0}, {0, 1, 2, 3, 5, 7, 0, 0}, {4, 5, 7, 0, 0, 0, 0, 0},
|
||||
{0, 4, 5, 7, 0, 0, 0, 0}, {1, 4, 5, 7, 0, 0, 0, 0}, {0, 1, 4, 5, 7, 0, 0, 0},
|
||||
{2, 4, 5, 7, 0, 0, 0, 0}, {0, 2, 4, 5, 7, 0, 0, 0}, {1, 2, 4, 5, 7, 0, 0, 0},
|
||||
{0, 1, 2, 4, 5, 7, 0, 0}, {3, 4, 5, 7, 0, 0, 0, 0}, {0, 3, 4, 5, 7, 0, 0, 0},
|
||||
{1, 3, 4, 5, 7, 0, 0, 0}, {0, 1, 3, 4, 5, 7, 0, 0}, {2, 3, 4, 5, 7, 0, 0, 0},
|
||||
{0, 2, 3, 4, 5, 7, 0, 0}, {1, 2, 3, 4, 5, 7, 0, 0}, {0, 1, 2, 3, 4, 5, 7, 0},
|
||||
{6, 7, 0, 0, 0, 0, 0, 0}, {0, 6, 7, 0, 0, 0, 0, 0}, {1, 6, 7, 0, 0, 0, 0, 0},
|
||||
{0, 1, 6, 7, 0, 0, 0, 0}, {2, 6, 7, 0, 0, 0, 0, 0}, {0, 2, 6, 7, 0, 0, 0, 0},
|
||||
{1, 2, 6, 7, 0, 0, 0, 0}, {0, 1, 2, 6, 7, 0, 0, 0}, {3, 6, 7, 0, 0, 0, 0, 0},
|
||||
{0, 3, 6, 7, 0, 0, 0, 0}, {1, 3, 6, 7, 0, 0, 0, 0}, {0, 1, 3, 6, 7, 0, 0, 0},
|
||||
{2, 3, 6, 7, 0, 0, 0, 0}, {0, 2, 3, 6, 7, 0, 0, 0}, {1, 2, 3, 6, 7, 0, 0, 0},
|
||||
{0, 1, 2, 3, 6, 7, 0, 0}, {4, 6, 7, 0, 0, 0, 0, 0}, {0, 4, 6, 7, 0, 0, 0, 0},
|
||||
{1, 4, 6, 7, 0, 0, 0, 0}, {0, 1, 4, 6, 7, 0, 0, 0}, {2, 4, 6, 7, 0, 0, 0, 0},
|
||||
{0, 2, 4, 6, 7, 0, 0, 0}, {1, 2, 4, 6, 7, 0, 0, 0}, {0, 1, 2, 4, 6, 7, 0, 0},
|
||||
{3, 4, 6, 7, 0, 0, 0, 0}, {0, 3, 4, 6, 7, 0, 0, 0}, {1, 3, 4, 6, 7, 0, 0, 0},
|
||||
{0, 1, 3, 4, 6, 7, 0, 0}, {2, 3, 4, 6, 7, 0, 0, 0}, {0, 2, 3, 4, 6, 7, 0, 0},
|
||||
{1, 2, 3, 4, 6, 7, 0, 0}, {0, 1, 2, 3, 4, 6, 7, 0}, {5, 6, 7, 0, 0, 0, 0, 0},
|
||||
{0, 5, 6, 7, 0, 0, 0, 0}, {1, 5, 6, 7, 0, 0, 0, 0}, {0, 1, 5, 6, 7, 0, 0, 0},
|
||||
{2, 5, 6, 7, 0, 0, 0, 0}, {0, 2, 5, 6, 7, 0, 0, 0}, {1, 2, 5, 6, 7, 0, 0, 0},
|
||||
{0, 1, 2, 5, 6, 7, 0, 0}, {3, 5, 6, 7, 0, 0, 0, 0}, {0, 3, 5, 6, 7, 0, 0, 0},
|
||||
{1, 3, 5, 6, 7, 0, 0, 0}, {0, 1, 3, 5, 6, 7, 0, 0}, {2, 3, 5, 6, 7, 0, 0, 0},
|
||||
{0, 2, 3, 5, 6, 7, 0, 0}, {1, 2, 3, 5, 6, 7, 0, 0}, {0, 1, 2, 3, 5, 6, 7, 0},
|
||||
{4, 5, 6, 7, 0, 0, 0, 0}, {0, 4, 5, 6, 7, 0, 0, 0}, {1, 4, 5, 6, 7, 0, 0, 0},
|
||||
{0, 1, 4, 5, 6, 7, 0, 0}, {2, 4, 5, 6, 7, 0, 0, 0}, {0, 2, 4, 5, 6, 7, 0, 0},
|
||||
{1, 2, 4, 5, 6, 7, 0, 0}, {0, 1, 2, 4, 5, 6, 7, 0}, {3, 4, 5, 6, 7, 0, 0, 0},
|
||||
{0, 3, 4, 5, 6, 7, 0, 0}, {1, 3, 4, 5, 6, 7, 0, 0}, {0, 1, 3, 4, 5, 6, 7, 0},
|
||||
{2, 3, 4, 5, 6, 7, 0, 0}, {0, 2, 3, 4, 5, 6, 7, 0}, {1, 2, 3, 4, 5, 6, 7, 0},
|
||||
{0, 1, 2, 3, 4, 5, 6, 7}};
|
||||
|
||||
// Find indices of nonzero numbers in an int32_t array
|
||||
template<const IndexType InputDimensions>
|
||||
@@ -74,7 +156,11 @@ void find_nnz(const std::int32_t* input, std::uint16_t* out, IndexType& count_ou
|
||||
using vec128_t = __m128i;
|
||||
#define vec128_zero _mm_setzero_si128()
|
||||
#define vec128_set_16(a) _mm_set1_epi16(a)
|
||||
#define vec128_load(a) _mm_load_si128(a)
|
||||
#if (USE_SSE41)
|
||||
#define vec128_load(a) _mm_cvtepu8_epi16(_mm_loadl_epi64(a))
|
||||
#else
|
||||
#define vec128_load(a) _mm_load_si128(a)
|
||||
#endif
|
||||
#define vec128_storeu(a, b) _mm_storeu_si128(a, b)
|
||||
#define vec128_add(a, b) _mm_add_epi16(a, b)
|
||||
#elif defined(USE_NEON)
|
||||
|
||||
@@ -447,8 +447,8 @@ class FeatureTransformer {
|
||||
|
||||
void hint_common_access(const Position& pos,
|
||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||
hint_common_access_for_perspective<WHITE>(pos, cache);
|
||||
hint_common_access_for_perspective<BLACK>(pos, cache);
|
||||
update_accumulator<WHITE>(pos, cache);
|
||||
update_accumulator<BLACK>(pos, cache);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -470,9 +470,8 @@ class FeatureTransformer {
|
||||
return st;
|
||||
}
|
||||
|
||||
// It computes the accumulator of the next position, or updates the
|
||||
// current position's accumulator if CurrentOnly is true.
|
||||
template<Color Perspective, bool CurrentOnly>
|
||||
// Computes the accumulator of the next position.
|
||||
template<Color Perspective>
|
||||
void update_accumulator_incremental(const Position& pos, StateInfo* computed) const {
|
||||
assert((computed->*accPtr).computed[Perspective]);
|
||||
assert(computed->next != nullptr);
|
||||
@@ -491,16 +490,10 @@ class FeatureTransformer {
|
||||
// feature set's update cost calculation to be correct and never allow
|
||||
// updates with more added/removed features than MaxActiveDimensions.
|
||||
FeatureSet::IndexList removed, added;
|
||||
FeatureSet::append_changed_indices<Perspective>(ksq, computed->next->dirtyPiece, removed,
|
||||
added);
|
||||
|
||||
if constexpr (CurrentOnly)
|
||||
for (StateInfo* st = pos.state(); st != computed; st = st->previous)
|
||||
FeatureSet::append_changed_indices<Perspective>(ksq, st->dirtyPiece, removed,
|
||||
added);
|
||||
else
|
||||
FeatureSet::append_changed_indices<Perspective>(ksq, computed->next->dirtyPiece,
|
||||
removed, added);
|
||||
|
||||
StateInfo* next = CurrentOnly ? pos.state() : computed->next;
|
||||
StateInfo* next = computed->next;
|
||||
assert(!(next->*accPtr).computed[Perspective]);
|
||||
|
||||
#ifdef VECTOR
|
||||
@@ -663,8 +656,8 @@ class FeatureTransformer {
|
||||
|
||||
(next->*accPtr).computed[Perspective] = true;
|
||||
|
||||
if (!CurrentOnly && next != pos.state())
|
||||
update_accumulator_incremental<Perspective, false>(pos, next);
|
||||
if (next != pos.state())
|
||||
update_accumulator_incremental<Perspective>(pos, next);
|
||||
}
|
||||
|
||||
template<Color Perspective>
|
||||
@@ -826,37 +819,18 @@ class FeatureTransformer {
|
||||
entry.byTypeBB[pt] = pos.pieces(pt);
|
||||
}
|
||||
|
||||
template<Color Perspective>
|
||||
void hint_common_access_for_perspective(const Position& pos,
|
||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||
|
||||
// Works like update_accumulator, but performs less work.
|
||||
// Updates ONLY the accumulator for pos.
|
||||
|
||||
// Look for a usable accumulator of an earlier position. We keep track
|
||||
// of the estimated gain in terms of features to be added/subtracted.
|
||||
// Fast early exit.
|
||||
if ((pos.state()->*accPtr).computed[Perspective])
|
||||
return;
|
||||
|
||||
StateInfo* oldest = try_find_computed_accumulator<Perspective>(pos);
|
||||
|
||||
if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state())
|
||||
update_accumulator_incremental<Perspective, true>(pos, oldest);
|
||||
else
|
||||
update_accumulator_refresh_cache<Perspective>(pos, cache);
|
||||
}
|
||||
|
||||
template<Color Perspective>
|
||||
void update_accumulator(const Position& pos,
|
||||
AccumulatorCaches::Cache<HalfDimensions>* cache) const {
|
||||
|
||||
if ((pos.state()->*accPtr).computed[Perspective])
|
||||
return;
|
||||
StateInfo* oldest = try_find_computed_accumulator<Perspective>(pos);
|
||||
|
||||
if ((oldest->*accPtr).computed[Perspective] && oldest != pos.state())
|
||||
// Start from the oldest computed accumulator, update all the
|
||||
// accumulators up to the current position.
|
||||
update_accumulator_incremental<Perspective, false>(pos, oldest);
|
||||
update_accumulator_incremental<Perspective>(pos, oldest);
|
||||
else
|
||||
update_accumulator_refresh_cache<Perspective>(pos, cache);
|
||||
}
|
||||
|
||||
+29
-30
@@ -493,14 +493,23 @@ void Position::update_slider_blockers(Color c) const {
|
||||
// Slider attacks use the occupied bitboard to indicate occupancy.
|
||||
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
||||
|
||||
return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
|
||||
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
|
||||
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|
||||
| (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
|
||||
return (attacks_bb<ROOK>(s, occupied) & pieces(ROOK, QUEEN))
|
||||
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
|
||||
| (attacks_bb<KING>(s) & pieces(KING));
|
||||
| (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
|
||||
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
|
||||
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT)) | (attacks_bb<KING>(s) & pieces(KING));
|
||||
}
|
||||
|
||||
bool Position::attackers_to_exist(Square s, Bitboard occupied, Color c) const {
|
||||
|
||||
return ((attacks_bb<ROOK>(s) & pieces(c, ROOK, QUEEN))
|
||||
&& (attacks_bb<ROOK>(s, occupied) & pieces(c, ROOK, QUEEN)))
|
||||
|| ((attacks_bb<BISHOP>(s) & pieces(c, BISHOP, QUEEN))
|
||||
&& (attacks_bb<BISHOP>(s, occupied) & pieces(c, BISHOP, QUEEN)))
|
||||
|| (((pawn_attacks_bb(~c, s) & pieces(PAWN)) | (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|
||||
| (attacks_bb<KING>(s) & pieces(KING)))
|
||||
& pieces(c));
|
||||
}
|
||||
|
||||
// Tests whether a pseudo-legal move is legal
|
||||
bool Position::legal(Move m) const {
|
||||
@@ -542,7 +551,7 @@ bool Position::legal(Move m) const {
|
||||
Direction step = to > from ? WEST : EAST;
|
||||
|
||||
for (Square s = to; s != from; s += step)
|
||||
if (attackers_to(s) & pieces(~us))
|
||||
if (attackers_to_exist(s, pieces(), ~us))
|
||||
return false;
|
||||
|
||||
// In case of Chess960, verify if the Rook blocks some checks.
|
||||
@@ -553,7 +562,7 @@ bool Position::legal(Move m) const {
|
||||
// If the moving piece is a king, check whether the destination square is
|
||||
// attacked by the opponent.
|
||||
if (type_of(piece_on(from)) == KING)
|
||||
return !(attackers_to(to, pieces() ^ from) & pieces(~us));
|
||||
return !(attackers_to_exist(to, pieces() ^ from, ~us));
|
||||
|
||||
// A non-king move is legal if and only if it is not pinned or it
|
||||
// is moving along the ray towards or away from the king.
|
||||
@@ -622,7 +631,7 @@ bool Position::pseudo_legal(const Move m) const {
|
||||
}
|
||||
// In case of king moves under check we have to remove the king so as to catch
|
||||
// invalid moves like b1a1 when opposite queen is on c1.
|
||||
else if (attackers_to(to, pieces() ^ from) & pieces(~us))
|
||||
else if (attackers_to_exist(to, pieces() ^ from, ~us))
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -680,7 +689,12 @@ bool Position::gives_check(Move m) const {
|
||||
// Makes a move, and saves all information necessary
|
||||
// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
|
||||
// moves should be filtered out before this function is called.
|
||||
void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
// If a pointer to the TT table is passed, the entry for the new position
|
||||
// will be prefetched
|
||||
void Position::do_move(Move m,
|
||||
StateInfo& newSt,
|
||||
bool givesCheck,
|
||||
const TranspositionTable* tt = nullptr) {
|
||||
|
||||
assert(m.is_ok());
|
||||
assert(&newSt != st);
|
||||
@@ -878,11 +892,13 @@ void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
||||
st->minorPieceKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
||||
}
|
||||
|
||||
// Set capture piece
|
||||
st->capturedPiece = captured;
|
||||
|
||||
// Update the key with the final value
|
||||
st->key = k;
|
||||
if (tt)
|
||||
prefetch(tt->first_entry(key()));
|
||||
|
||||
// Set capture piece
|
||||
st->capturedPiece = captured;
|
||||
|
||||
// Calculate checkers bitboard (if move gives check)
|
||||
st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
|
||||
@@ -1060,23 +1076,6 @@ void Position::undo_null_move() {
|
||||
}
|
||||
|
||||
|
||||
// Computes the new hash key after the given move. Needed
|
||||
// for speculative prefetch. It doesn't recognize special moves like castling,
|
||||
// en passant and promotions.
|
||||
Key Position::key_after(Move m) const {
|
||||
|
||||
Square from = m.from_sq();
|
||||
Square to = m.to_sq();
|
||||
Piece pc = piece_on(from);
|
||||
Piece captured = piece_on(to);
|
||||
Key k = st->key ^ Zobrist::side;
|
||||
|
||||
k ^= Zobrist::psq[captured][to] ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
|
||||
|
||||
return (captured || type_of(pc) == PAWN) ? k : adjust_key50<true>(k);
|
||||
}
|
||||
|
||||
|
||||
// Tests if the SEE (Static Exchange Evaluation)
|
||||
// value of move is greater or equal to the given threshold. We'll use an
|
||||
// algorithm similar to alpha-beta pruning with a null window.
|
||||
@@ -1308,7 +1307,7 @@ bool Position::pos_is_ok() const {
|
||||
return true;
|
||||
|
||||
if (pieceCount[W_KING] != 1 || pieceCount[B_KING] != 1
|
||||
|| attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
|
||||
|| attackers_to_exist(square<KING>(~sideToMove), pieces(), sideToMove))
|
||||
assert(0 && "pos_is_ok: Kings");
|
||||
|
||||
if ((pieces(PAWN) & (Rank1BB | Rank8BB)) || pieceCount[W_PAWN] > 8 || pieceCount[B_PAWN] > 8)
|
||||
|
||||
+6
-4
@@ -126,6 +126,7 @@ class Position {
|
||||
// Attacks to/from a given square
|
||||
Bitboard attackers_to(Square s) const;
|
||||
Bitboard attackers_to(Square s, Bitboard occupied) const;
|
||||
bool attackers_to_exist(Square s, Bitboard occupied, Color c) const;
|
||||
void update_slider_blockers(Color c) const;
|
||||
template<PieceType Pt>
|
||||
Bitboard attacks_by(Color c) const;
|
||||
@@ -140,8 +141,8 @@ class Position {
|
||||
Piece captured_piece() const;
|
||||
|
||||
// Doing and undoing moves
|
||||
void do_move(Move m, StateInfo& newSt);
|
||||
void do_move(Move m, StateInfo& newSt, bool givesCheck);
|
||||
void do_move(Move m, StateInfo& newSt, const TranspositionTable* tt);
|
||||
void do_move(Move m, StateInfo& newSt, bool givesCheck, const TranspositionTable* tt);
|
||||
void undo_move(Move m);
|
||||
void do_null_move(StateInfo& newSt, const TranspositionTable& tt);
|
||||
void undo_null_move();
|
||||
@@ -151,7 +152,6 @@ class Position {
|
||||
|
||||
// Accessing hash keys
|
||||
Key key() const;
|
||||
Key key_after(Move m) const;
|
||||
Key material_key() const;
|
||||
Key pawn_key() const;
|
||||
Key major_piece_key() const;
|
||||
@@ -368,7 +368,9 @@ inline void Position::move_piece(Square from, Square to) {
|
||||
board[to] = pc;
|
||||
}
|
||||
|
||||
inline void Position::do_move(Move m, StateInfo& newSt) { do_move(m, newSt, gives_check(m)); }
|
||||
inline void Position::do_move(Move m, StateInfo& newSt, const TranspositionTable* tt = nullptr) {
|
||||
do_move(m, newSt, gives_check(m), tt);
|
||||
}
|
||||
|
||||
inline StateInfo* Position::state() const { return st; }
|
||||
|
||||
|
||||
+154
-146
@@ -67,7 +67,7 @@ namespace {
|
||||
|
||||
// Futility margin
|
||||
Value futility_margin(Depth d, bool noTtCutNode, bool improving, bool oppWorsening) {
|
||||
Value futilityMult = 109 - 27 * noTtCutNode;
|
||||
Value futilityMult = 112 - 26 * noTtCutNode;
|
||||
Value improvingDeduction = improving * futilityMult * 2;
|
||||
Value worseningDeduction = oppWorsening * futilityMult / 3;
|
||||
|
||||
@@ -78,7 +78,7 @@ constexpr int futility_move_count(bool improving, Depth depth) {
|
||||
return (3 + depth * depth) / (2 - improving);
|
||||
}
|
||||
|
||||
int correction_value(const Worker& w, const Position& pos, Stack* ss) {
|
||||
int correction_value(const Worker& w, const Position& pos, const Stack* ss) {
|
||||
const Color us = pos.side_to_move();
|
||||
const auto m = (ss - 1)->currentMove;
|
||||
const auto pcv = w.pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)];
|
||||
@@ -90,7 +90,7 @@ int correction_value(const Worker& w, const Position& pos, Stack* ss) {
|
||||
m.is_ok() ? (*(ss - 2)->continuationCorrectionHistory)[pos.piece_on(m.to_sq())][m.to_sq()]
|
||||
: 0;
|
||||
|
||||
return (6384 * pcv + 3583 * macv + 6492 * micv + 6725 * (wnpcv + bnpcv) + 5880 * cntcv);
|
||||
return (6922 * pcv + 3837 * macv + 6238 * micv + 7490 * (wnpcv + bnpcv) + 6270 * cntcv);
|
||||
}
|
||||
|
||||
// Add correctionHistory value to raw staticEval and guarantee evaluation
|
||||
@@ -100,10 +100,10 @@ Value to_corrected_static_eval(Value v, const int cv) {
|
||||
}
|
||||
|
||||
// History and stats update bonus, based on depth
|
||||
int stat_bonus(Depth d) { return std::min(168 * d - 100, 1718); }
|
||||
int stat_bonus(Depth d) { return std::min(154 * d - 102, 1661); }
|
||||
|
||||
// History and stats update malus, based on depth
|
||||
int stat_malus(Depth d) { return std::min(768 * d - 257, 2351); }
|
||||
int stat_malus(Depth d) { return std::min(831 * d - 269, 2666); }
|
||||
|
||||
// Add a small random component to draw evaluations to avoid 3-fold blindness
|
||||
Value value_draw(size_t nodes) { return VALUE_DRAW - 1 + Value(nodes & 0x2); }
|
||||
@@ -120,7 +120,8 @@ void update_all_stats(const Position& pos,
|
||||
Square prevSq,
|
||||
ValueList<Move, 32>& quietsSearched,
|
||||
ValueList<Move, 32>& capturesSearched,
|
||||
Depth depth);
|
||||
Depth depth,
|
||||
bool isTTMove);
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -289,10 +290,14 @@ void Search::Worker::iterative_deepening() {
|
||||
&this->continuationHistory[0][0][NO_PIECE][0]; // Use as a sentinel
|
||||
(ss - i)->continuationCorrectionHistory = &this->continuationCorrectionHistory[NO_PIECE][0];
|
||||
(ss - i)->staticEval = VALUE_NONE;
|
||||
(ss - i)->reduction = 0;
|
||||
}
|
||||
|
||||
for (int i = 0; i <= MAX_PLY + 2; ++i)
|
||||
(ss + i)->ply = i;
|
||||
{
|
||||
(ss + i)->ply = i;
|
||||
(ss + i)->reduction = 0;
|
||||
}
|
||||
|
||||
ss->pv = pv;
|
||||
|
||||
@@ -316,7 +321,7 @@ void Search::Worker::iterative_deepening() {
|
||||
|
||||
int searchAgainCounter = 0;
|
||||
|
||||
lowPlyHistory.fill(106);
|
||||
lowPlyHistory.fill(97);
|
||||
|
||||
// Iterative deepening loop until requested to stop or the target depth is reached
|
||||
while (++rootDepth < MAX_PLY && !threads.stop
|
||||
@@ -352,13 +357,13 @@ void Search::Worker::iterative_deepening() {
|
||||
selDepth = 0;
|
||||
|
||||
// Reset aspiration window starting size
|
||||
delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 13461;
|
||||
delta = 5 + std::abs(rootMoves[pvIdx].meanSquaredScore) / 12991;
|
||||
Value avg = rootMoves[pvIdx].averageScore;
|
||||
alpha = std::max(avg - delta, -VALUE_INFINITE);
|
||||
beta = std::min(avg + delta, VALUE_INFINITE);
|
||||
|
||||
// Adjust optimism based on root move's averageScore (~4 Elo)
|
||||
optimism[us] = 150 * avg / (std::abs(avg) + 85);
|
||||
optimism[us] = 141 * avg / (std::abs(avg) + 83);
|
||||
optimism[~us] = -optimism[us];
|
||||
|
||||
// Start with a small aspiration window and, in the case of a fail
|
||||
@@ -489,17 +494,19 @@ void Search::Worker::iterative_deepening() {
|
||||
// Do we have time for the next iteration? Can we stop searching now?
|
||||
if (limits.use_time_management() && !threads.stop && !mainThread->stopOnPonderhit)
|
||||
{
|
||||
int nodesEffort = rootMoves[0].effort * 100 / std::max(size_t(1), size_t(nodes));
|
||||
int nodesEffort = rootMoves[0].effort * 100000 / std::max(size_t(1), size_t(nodes));
|
||||
|
||||
double fallingEval = (11 + 2 * (mainThread->bestPreviousAverageScore - bestValue)
|
||||
+ (mainThread->iterValue[iterIdx] - bestValue))
|
||||
/ 100.0;
|
||||
fallingEval = std::clamp(fallingEval, 0.580, 1.667);
|
||||
double fallingEval =
|
||||
(11.396 + 2.035 * (mainThread->bestPreviousAverageScore - bestValue)
|
||||
+ 0.968 * (mainThread->iterValue[iterIdx] - bestValue))
|
||||
/ 100.0;
|
||||
fallingEval = std::clamp(fallingEval, 0.5786, 1.6752);
|
||||
|
||||
// If the bestMove is stable over several iterations, reduce time accordingly
|
||||
timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.495 : 0.687;
|
||||
double reduction = (1.48 + mainThread->previousTimeReduction) / (2.17 * timeReduction);
|
||||
double bestMoveInstability = 1 + 1.88 * totBestMoveChanges / threads.size();
|
||||
timeReduction = lastBestMoveDepth + 8 < completedDepth ? 1.4857 : 0.7046;
|
||||
double reduction =
|
||||
(1.4540 + mainThread->previousTimeReduction) / (2.1593 * timeReduction);
|
||||
double bestMoveInstability = 0.9929 + 1.8519 * totBestMoveChanges / threads.size();
|
||||
|
||||
double totalTime =
|
||||
mainThread->tm.optimum() * fallingEval * reduction * bestMoveInstability;
|
||||
@@ -510,7 +517,7 @@ void Search::Worker::iterative_deepening() {
|
||||
|
||||
auto elapsedTime = elapsed();
|
||||
|
||||
if (completedDepth >= 10 && nodesEffort >= 97 && elapsedTime > totalTime * 0.739
|
||||
if (completedDepth >= 10 && nodesEffort >= 97056 && elapsedTime > totalTime * 0.6540
|
||||
&& !mainThread->ponder)
|
||||
threads.stop = true;
|
||||
|
||||
@@ -525,7 +532,7 @@ void Search::Worker::iterative_deepening() {
|
||||
threads.stop = true;
|
||||
}
|
||||
else
|
||||
threads.increaseDepth = mainThread->ponder || elapsedTime <= totalTime * 0.506;
|
||||
threads.increaseDepth = mainThread->ponder || elapsedTime <= totalTime * 0.5138;
|
||||
}
|
||||
|
||||
mainThread->iterValue[iterIdx] = bestValue;
|
||||
@@ -546,10 +553,10 @@ void Search::Worker::iterative_deepening() {
|
||||
|
||||
// Reset histories, usually before a new game
|
||||
void Search::Worker::clear() {
|
||||
mainHistory.fill(61);
|
||||
lowPlyHistory.fill(106);
|
||||
captureHistory.fill(-598);
|
||||
pawnHistory.fill(-1181);
|
||||
mainHistory.fill(63);
|
||||
lowPlyHistory.fill(108);
|
||||
captureHistory.fill(-631);
|
||||
pawnHistory.fill(-1210);
|
||||
pawnCorrectionHistory.fill(0);
|
||||
majorPieceCorrectionHistory.fill(0);
|
||||
minorPieceCorrectionHistory.fill(0);
|
||||
@@ -564,10 +571,10 @@ void Search::Worker::clear() {
|
||||
for (StatsType c : {NoCaptures, Captures})
|
||||
for (auto& to : continuationHistory[inCheck][c])
|
||||
for (auto& h : to)
|
||||
h.fill(-427);
|
||||
h.fill(-479);
|
||||
|
||||
for (size_t i = 1; i < reductions.size(); ++i)
|
||||
reductions[i] = int(19.43 * std::log(i));
|
||||
reductions[i] = int(2143 / 100.0 * std::log(i));
|
||||
|
||||
refreshTable.clear(networks[numaAccessToken]);
|
||||
}
|
||||
@@ -615,6 +622,8 @@ Value Search::Worker::search(
|
||||
Value bestValue, value, eval, maxValue, probCutBeta;
|
||||
bool givesCheck, improving, priorCapture, opponentWorsening;
|
||||
bool capture, ttCapture;
|
||||
int priorReduction = ss->reduction;
|
||||
ss->reduction = 0;
|
||||
Piece movedPiece;
|
||||
|
||||
ValueList<Move, 32> capturesSearched;
|
||||
@@ -684,20 +693,20 @@ Value Search::Worker::search(
|
||||
if (!PvNode && !excludedMove && ttData.depth > depth - (ttData.value <= beta)
|
||||
&& is_valid(ttData.value) // Can happen when !ttHit or when access race in probe()
|
||||
&& (ttData.bound & (ttData.value >= beta ? BOUND_LOWER : BOUND_UPPER))
|
||||
&& (cutNode == (ttData.value >= beta) || depth > 8))
|
||||
&& (cutNode == (ttData.value >= beta) || depth > 9))
|
||||
{
|
||||
// If ttMove is quiet, update move sorting heuristics on TT hit (~2 Elo)
|
||||
if (ttData.move && ttData.value >= beta)
|
||||
{
|
||||
// Bonus for a quiet ttMove that fails high (~2 Elo)
|
||||
if (!ttCapture)
|
||||
update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth) * 747 / 1024);
|
||||
update_quiet_histories(pos, ss, *this, ttData.move, stat_bonus(depth) * 746 / 1024);
|
||||
|
||||
// Extra penalty for early quiet moves of
|
||||
// the previous ply (~1 Elo on STC, ~2 Elo on LTC)
|
||||
if (prevSq != SQ_NONE && (ss - 1)->moveCount <= 2 && !priorCapture)
|
||||
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
|
||||
-stat_malus(depth + 1) * 1091 / 1024);
|
||||
-stat_malus(depth + 1) * 1042 / 1024);
|
||||
}
|
||||
|
||||
// Partial workaround for the graph history interaction problem
|
||||
@@ -807,11 +816,11 @@ Value Search::Worker::search(
|
||||
// Use static evaluation difference to improve quiet move ordering (~9 Elo)
|
||||
if (((ss - 1)->currentMove).is_ok() && !(ss - 1)->inCheck && !priorCapture)
|
||||
{
|
||||
int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1831, 1428) + 623;
|
||||
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1340 / 1024;
|
||||
int bonus = std::clamp(-10 * int((ss - 1)->staticEval + ss->staticEval), -1881, 1413) + 616;
|
||||
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << bonus * 1151 / 1024;
|
||||
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
|
||||
thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq]
|
||||
<< bonus * 1159 / 1024;
|
||||
<< bonus * 1107 / 1024;
|
||||
}
|
||||
|
||||
// Set up the improving flag, which is true if current static evaluation is
|
||||
@@ -822,34 +831,37 @@ Value Search::Worker::search(
|
||||
|
||||
opponentWorsening = ss->staticEval + (ss - 1)->staticEval > 2;
|
||||
|
||||
if (priorReduction >= 3 && ss->staticEval + (ss - 1)->staticEval < 0)
|
||||
{
|
||||
depth++;
|
||||
}
|
||||
|
||||
// Step 7. Razoring (~1 Elo)
|
||||
// If eval is really low, check with qsearch if we can exceed alpha. If the
|
||||
// search suggests we cannot exceed alpha, return a speculative fail low.
|
||||
// If eval is really low, skip search entirely and return the qsearch value.
|
||||
// For PvNodes, we must have a guard against mates being returned.
|
||||
if (!PvNode && eval < alpha - 469 - 307 * depth * depth)
|
||||
return qsearch<NonPV>(pos, ss, alpha - 1, alpha);
|
||||
if (!PvNode && eval < alpha - 462 - 297 * depth * depth)
|
||||
return qsearch<NonPV>(pos, ss, alpha, beta);
|
||||
|
||||
// Step 8. Futility pruning: child node (~40 Elo)
|
||||
// The depth condition is important for mate finding.
|
||||
if (!ss->ttPv && depth < 14
|
||||
&& eval - futility_margin(depth, cutNode && !ss->ttHit, improving, opponentWorsening)
|
||||
- (ss - 1)->statScore / 290
|
||||
+ (ss->staticEval == eval) * (40 - std::abs(correctionValue) / 131072)
|
||||
- (ss - 1)->statScore / 310 + 40 - std::abs(correctionValue) / 131072
|
||||
>= beta
|
||||
&& eval >= beta && (!ttData.move || ttCapture) && !is_loss(beta) && !is_win(eval))
|
||||
return beta + (eval - beta) / 3;
|
||||
|
||||
improving |= ss->staticEval >= beta + 100;
|
||||
improving |= ss->staticEval >= beta + 97;
|
||||
|
||||
// Step 9. Null move search with verification search (~35 Elo)
|
||||
if (cutNode && (ss - 1)->currentMove != Move::null() && eval >= beta
|
||||
&& ss->staticEval >= beta - 21 * depth + 421 && !excludedMove && pos.non_pawn_material(us)
|
||||
&& ss->staticEval >= beta - 20 * depth + 440 && !excludedMove && pos.non_pawn_material(us)
|
||||
&& ss->ply >= thisThread->nmpMinPly && !is_loss(beta))
|
||||
{
|
||||
assert(eval - beta >= 0);
|
||||
|
||||
// Null move dynamic reduction based on depth and eval
|
||||
Depth R = std::min(int(eval - beta) / 235, 7) + depth / 3 + 5;
|
||||
Depth R = std::min(int(eval - beta) / 215, 7) + depth / 3 + 5;
|
||||
|
||||
ss->currentMove = Move::null();
|
||||
ss->continuationHistory = &thisThread->continuationHistory[0][0][NO_PIECE][0];
|
||||
@@ -883,23 +895,21 @@ Value Search::Worker::search(
|
||||
}
|
||||
|
||||
// Step 10. Internal iterative reductions (~9 Elo)
|
||||
// For PV nodes without a ttMove, we decrease depth.
|
||||
if (PvNode && !ttData.move)
|
||||
depth -= 3;
|
||||
// For PV nodes without a ttMove as well as for deep enough cutNodes, we decrease depth.
|
||||
// This heuristic is known to scale non-linearly, current version was tested at VVLTC.
|
||||
// Further improvements need to be tested at similar time control if they make IIR
|
||||
// more aggressive.
|
||||
if ((PvNode || (cutNode && depth >= 7)) && !ttData.move)
|
||||
depth -= 2;
|
||||
|
||||
// Use qsearch if depth <= 0
|
||||
if (depth <= 0)
|
||||
return qsearch<PV>(pos, ss, alpha, beta);
|
||||
|
||||
// For cutNodes, if depth is high enough, decrease depth by 2 if there is no ttMove,
|
||||
// or by 1 if there is a ttMove with an upper bound.
|
||||
if (cutNode && depth >= 7 && (!ttData.move || ttData.bound == BOUND_UPPER))
|
||||
depth -= 1 + !ttData.move;
|
||||
|
||||
// Step 11. ProbCut (~10 Elo)
|
||||
// If we have a good enough capture (or queen promotion) and a reduced search
|
||||
// returns a value much above beta, we can (almost) safely prune the previous move.
|
||||
probCutBeta = beta + 187 - 56 * improving;
|
||||
probCutBeta = beta + 174 - 56 * improving;
|
||||
if (depth > 3
|
||||
&& !is_decisive(beta)
|
||||
// If value from transposition table is lower than probCutBeta, don't attempt
|
||||
@@ -911,7 +921,6 @@ Value Search::Worker::search(
|
||||
assert(probCutBeta < VALUE_INFINITE && probCutBeta > beta);
|
||||
|
||||
MovePicker mp(pos, ttData.move, probCutBeta - ss->staticEval, &thisThread->captureHistory);
|
||||
Piece captured;
|
||||
|
||||
while ((move = mp.next_move()) != Move::none())
|
||||
{
|
||||
@@ -926,20 +935,15 @@ Value Search::Worker::search(
|
||||
assert(pos.capture_stage(move));
|
||||
|
||||
movedPiece = pos.moved_piece(move);
|
||||
captured = pos.piece_on(move.to_sq());
|
||||
|
||||
|
||||
// Prefetch the TT entry for the resulting position
|
||||
prefetch(tt.first_entry(pos.key_after(move)));
|
||||
pos.do_move(move, st, &tt);
|
||||
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
ss->currentMove = move;
|
||||
ss->continuationHistory =
|
||||
&this->continuationHistory[ss->inCheck][true][pos.moved_piece(move)][move.to_sq()];
|
||||
&this->continuationHistory[ss->inCheck][true][movedPiece][move.to_sq()];
|
||||
ss->continuationCorrectionHistory =
|
||||
&this->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()];
|
||||
|
||||
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
|
||||
pos.do_move(move, st);
|
||||
&this->continuationCorrectionHistory[movedPiece][move.to_sq()];
|
||||
|
||||
// Perform a preliminary qsearch to verify that the move holds
|
||||
value = -qsearch<NonPV>(pos, ss + 1, -probCutBeta, -probCutBeta + 1);
|
||||
@@ -953,8 +957,6 @@ Value Search::Worker::search(
|
||||
|
||||
if (value >= probCutBeta)
|
||||
{
|
||||
thisThread->captureHistory[movedPiece][move.to_sq()][type_of(captured)] << 1226;
|
||||
|
||||
// Save ProbCut data into transposition table
|
||||
Distributed::save(tt, threads, thisThread, ttWriter, posKey,
|
||||
value_to_tt(value, ss->ply), ss->ttPv, BOUND_LOWER, depth - 3,
|
||||
@@ -969,17 +971,14 @@ Value Search::Worker::search(
|
||||
moves_loop: // When in check, search starts here
|
||||
|
||||
// Step 12. A small Probcut idea (~4 Elo)
|
||||
probCutBeta = beta + 417;
|
||||
probCutBeta = beta + 412;
|
||||
if ((ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 4 && ttData.value >= probCutBeta
|
||||
&& !is_decisive(beta) && is_valid(ttData.value) && !is_decisive(ttData.value))
|
||||
return probCutBeta;
|
||||
|
||||
const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory,
|
||||
(ss - 2)->continuationHistory,
|
||||
(ss - 3)->continuationHistory,
|
||||
(ss - 4)->continuationHistory,
|
||||
nullptr,
|
||||
(ss - 6)->continuationHistory};
|
||||
const PieceToHistory* contHist[] = {
|
||||
(ss - 1)->continuationHistory, (ss - 2)->continuationHistory, (ss - 3)->continuationHistory,
|
||||
(ss - 4)->continuationHistory, (ss - 5)->continuationHistory, (ss - 6)->continuationHistory};
|
||||
|
||||
|
||||
MovePicker mp(pos, ttData.move, depth, &thisThread->mainHistory, &thisThread->lowPlyHistory,
|
||||
@@ -1032,6 +1031,10 @@ moves_loop: // When in check, search starts here
|
||||
|
||||
Depth r = reduction(improving, depth, moveCount, delta);
|
||||
|
||||
// Decrease reduction if position is or has been on the PV (~7 Elo)
|
||||
if (ss->ttPv)
|
||||
r -= 1037 + (ttData.value > alpha) * 965 + (ttData.depth >= depth) * 960;
|
||||
|
||||
// Step 14. Pruning at shallow depth (~120 Elo).
|
||||
// Depth conditions are important for mate finding.
|
||||
if (!rootNode && pos.non_pawn_material(us) && !is_loss(bestValue))
|
||||
@@ -1052,15 +1055,15 @@ moves_loop: // When in check, search starts here
|
||||
// Futility pruning for captures (~2 Elo)
|
||||
if (!givesCheck && lmrDepth < 7 && !ss->inCheck)
|
||||
{
|
||||
Value futilityValue = ss->staticEval + 287 + 253 * lmrDepth
|
||||
Value futilityValue = ss->staticEval + 271 + 243 * lmrDepth
|
||||
+ PieceValue[capturedPiece] + captHist / 7;
|
||||
if (futilityValue <= alpha)
|
||||
continue;
|
||||
}
|
||||
|
||||
// SEE based pruning for captures and checks (~11 Elo)
|
||||
int seeHist = std::clamp(captHist / 33, -161 * depth, 156 * depth);
|
||||
if (!pos.see_ge(move, -162 * depth - seeHist))
|
||||
int seeHist = std::clamp(captHist / 37, -152 * depth, 141 * depth);
|
||||
if (!pos.see_ge(move, -156 * depth - seeHist))
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -1071,15 +1074,15 @@ moves_loop: // When in check, search starts here
|
||||
+ thisThread->pawnHistory[pawn_structure_index(pos)][movedPiece][move.to_sq()];
|
||||
|
||||
// Continuation history based pruning (~2 Elo)
|
||||
if (history < -3884 * depth)
|
||||
if (history < -3901 * depth)
|
||||
continue;
|
||||
|
||||
history += 2 * thisThread->mainHistory[us][move.from_to()];
|
||||
|
||||
lmrDepth += history / 3609;
|
||||
lmrDepth += history / 3459;
|
||||
|
||||
Value futilityValue =
|
||||
ss->staticEval + (bestValue < ss->staticEval - 45 ? 140 : 43) + 141 * lmrDepth;
|
||||
ss->staticEval + (bestValue < ss->staticEval - 47 ? 137 : 47) + 142 * lmrDepth;
|
||||
|
||||
// Futility pruning: parent node (~13 Elo)
|
||||
if (!ss->inCheck && lmrDepth < 12 && futilityValue <= alpha)
|
||||
@@ -1116,11 +1119,11 @@ moves_loop: // When in check, search starts here
|
||||
// and lower extension margins scale well.
|
||||
|
||||
if (!rootNode && move == ttData.move && !excludedMove
|
||||
&& depth >= 4 - (thisThread->completedDepth > 33) + ss->ttPv
|
||||
&& depth >= 5 - (thisThread->completedDepth > 33) + ss->ttPv
|
||||
&& is_valid(ttData.value) && !is_decisive(ttData.value)
|
||||
&& (ttData.bound & BOUND_LOWER) && ttData.depth >= depth - 3)
|
||||
{
|
||||
Value singularBeta = ttData.value - (56 + 79 * (ss->ttPv && !PvNode)) * depth / 64;
|
||||
Value singularBeta = ttData.value - (52 + 74 * (ss->ttPv && !PvNode)) * depth / 64;
|
||||
Depth singularDepth = newDepth / 2;
|
||||
|
||||
ss->excludedMove = move;
|
||||
@@ -1130,13 +1133,18 @@ moves_loop: // When in check, search starts here
|
||||
|
||||
if (value < singularBeta)
|
||||
{
|
||||
int doubleMargin = 249 * PvNode - 194 * !ttCapture;
|
||||
int tripleMargin = 94 + 287 * PvNode - 249 * !ttCapture + 99 * ss->ttPv;
|
||||
int corrValAdj = std::abs(correctionValue) / 262144;
|
||||
int doubleMargin = 249 * PvNode - 194 * !ttCapture - corrValAdj;
|
||||
int tripleMargin =
|
||||
94 + 287 * PvNode - 249 * !ttCapture + 99 * ss->ttPv - corrValAdj;
|
||||
int quadMargin =
|
||||
394 + 287 * PvNode - 249 * !ttCapture + 99 * ss->ttPv - corrValAdj;
|
||||
|
||||
extension = 1 + (value < singularBeta - doubleMargin)
|
||||
+ (value < singularBeta - tripleMargin);
|
||||
+ (value < singularBeta - tripleMargin)
|
||||
+ (value < singularBeta - quadMargin);
|
||||
|
||||
depth += ((!PvNode) && (depth < 14));
|
||||
depth += ((!PvNode) && (depth < 15));
|
||||
}
|
||||
|
||||
// Multi-cut pruning
|
||||
@@ -1169,16 +1177,17 @@ moves_loop: // When in check, search starts here
|
||||
else if (PvNode && move.to_sq() == prevSq
|
||||
&& thisThread->captureHistory[movedPiece][move.to_sq()]
|
||||
[type_of(pos.piece_on(move.to_sq()))]
|
||||
> 4321)
|
||||
> 4126)
|
||||
extension = 1;
|
||||
}
|
||||
|
||||
// Step 16. Make the move
|
||||
pos.do_move(move, st, givesCheck, &tt);
|
||||
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Add extension to new depth
|
||||
newDepth += extension;
|
||||
|
||||
// Speculative prefetch as early as possible
|
||||
prefetch(tt.first_entry(pos.key_after(move)));
|
||||
|
||||
// Update the current move (this must be done after singular extension search)
|
||||
ss->currentMove = move;
|
||||
ss->continuationHistory =
|
||||
@@ -1187,57 +1196,49 @@ moves_loop: // When in check, search starts here
|
||||
&thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()];
|
||||
uint64_t nodeCount = rootNode ? uint64_t(nodes) : 0;
|
||||
|
||||
// Step 16. Make the move
|
||||
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
|
||||
pos.do_move(move, st, givesCheck);
|
||||
|
||||
// These reduction adjustments have proven non-linear scaling.
|
||||
// They are optimized to time controls of 180 + 1.8 and longer,
|
||||
// so changing them or adding conditions that are similar requires
|
||||
// tests at these types of time controls.
|
||||
|
||||
// Decrease reduction if position is or has been on the PV (~7 Elo)
|
||||
if (ss->ttPv)
|
||||
r -= 1024 + (ttData.value > alpha) * 1024 + (ttData.depth >= depth) * 1024;
|
||||
|
||||
// Decrease reduction for PvNodes (~0 Elo on STC, ~2 Elo on LTC)
|
||||
if (PvNode)
|
||||
r -= 1024;
|
||||
r -= 1018;
|
||||
|
||||
// These reduction adjustments have no proven non-linear scaling
|
||||
|
||||
r += 330;
|
||||
r += 307;
|
||||
|
||||
r -= std::abs(correctionValue) / 32768;
|
||||
r -= std::abs(correctionValue) / 34112;
|
||||
|
||||
// Increase reduction for cut nodes (~4 Elo)
|
||||
if (cutNode)
|
||||
r += 2518 - (ttData.depth >= depth && ss->ttPv) * 991;
|
||||
r += 2355 - (ttData.depth >= depth && ss->ttPv) * 1141;
|
||||
|
||||
// Increase reduction if ttMove is a capture but the current move is not a capture (~3 Elo)
|
||||
if (ttCapture && !capture)
|
||||
r += 1043 + (depth < 8) * 999;
|
||||
r += 1087 + (depth < 8) * 990;
|
||||
|
||||
// Increase reduction if next ply has a lot of fail high (~5 Elo)
|
||||
if ((ss + 1)->cutoffCnt > 3)
|
||||
r += 938 + allNode * 960;
|
||||
r += 940 + allNode * 887;
|
||||
|
||||
// For first picked move (ttMove) reduce reduction (~3 Elo)
|
||||
else if (move == ttData.move)
|
||||
r -= 1879;
|
||||
r -= 1960;
|
||||
|
||||
if (capture)
|
||||
ss->statScore =
|
||||
7 * int(PieceValue[pos.captured_piece()])
|
||||
+ thisThread->captureHistory[movedPiece][move.to_sq()][type_of(pos.captured_piece())]
|
||||
- 5000;
|
||||
- 4666;
|
||||
else
|
||||
ss->statScore = 2 * thisThread->mainHistory[us][move.from_to()]
|
||||
+ (*contHist[0])[movedPiece][move.to_sq()]
|
||||
+ (*contHist[1])[movedPiece][move.to_sq()] - 3996;
|
||||
+ (*contHist[1])[movedPiece][move.to_sq()] - 3874;
|
||||
|
||||
// Decrease/increase reduction for moves with a good/bad history (~8 Elo)
|
||||
r -= ss->statScore * 1287 / 16384;
|
||||
r -= ss->statScore * 1451 / 16384;
|
||||
|
||||
// Step 17. Late moves reduction / extension (LMR, ~117 Elo)
|
||||
if (depth >= 2 && moveCount > 1)
|
||||
@@ -1247,17 +1248,23 @@ moves_loop: // When in check, search starts here
|
||||
// beyond the first move depth.
|
||||
// To prevent problems when the max value is less than the min value,
|
||||
// std::clamp has been replaced by a more robust implementation.
|
||||
|
||||
|
||||
Depth d = std::max(
|
||||
1, std::min(newDepth - r / 1024, newDepth + !allNode + (PvNode && !bestMove)));
|
||||
|
||||
value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, d, true);
|
||||
(ss + 1)->reduction = newDepth - d;
|
||||
|
||||
value = -search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, d, true);
|
||||
(ss + 1)->reduction = 0;
|
||||
|
||||
|
||||
// Do a full-depth search when reduced LMR search fails high
|
||||
if (value > alpha && d < newDepth)
|
||||
{
|
||||
// Adjust full-depth search based on LMR results - if the result was
|
||||
// good enough search deeper, if it was bad enough search shallower.
|
||||
const bool doDeeperSearch = value > (bestValue + 42 + 2 * newDepth); // (~1 Elo)
|
||||
const bool doDeeperSearch = value > (bestValue + 40 + 2 * newDepth); // (~1 Elo)
|
||||
const bool doShallowerSearch = value < bestValue + 10; // (~2 Elo)
|
||||
|
||||
newDepth += doDeeperSearch - doShallowerSearch;
|
||||
@@ -1276,11 +1283,11 @@ moves_loop: // When in check, search starts here
|
||||
{
|
||||
// Increase reduction if ttMove is not present (~6 Elo)
|
||||
if (!ttData.move)
|
||||
r += 2037;
|
||||
r += 2111;
|
||||
|
||||
// Note that if expected reduction is high, we reduce search depth by 1 here (~9 Elo)
|
||||
value =
|
||||
-search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 2983), !cutNode);
|
||||
-search<NonPV>(pos, ss + 1, -(alpha + 1), -alpha, newDepth - (r > 3444), !cutNode);
|
||||
}
|
||||
|
||||
// For PV nodes only, do a full PV search on the first move or after a fail high,
|
||||
@@ -1424,30 +1431,31 @@ moves_loop: // When in check, search starts here
|
||||
// If there is a move that produces search value greater than alpha,
|
||||
// we update the stats of searched moves.
|
||||
else if (bestMove)
|
||||
update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth);
|
||||
update_all_stats(pos, ss, *this, bestMove, prevSq, quietsSearched, capturesSearched, depth,
|
||||
bestMove == ttData.move);
|
||||
|
||||
// Bonus for prior countermove that caused the fail low
|
||||
else if (!priorCapture && prevSq != SQ_NONE)
|
||||
{
|
||||
int bonusScale = (117 * (depth > 5) + 39 * !allNode + 168 * ((ss - 1)->moveCount > 8)
|
||||
+ 115 * (!ss->inCheck && bestValue <= ss->staticEval - 108)
|
||||
+ 119 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 83));
|
||||
int bonusScale = (118 * (depth > 5) + 37 * !allNode + 169 * ((ss - 1)->moveCount > 8)
|
||||
+ 128 * (!ss->inCheck && bestValue <= ss->staticEval - 102)
|
||||
+ 115 * (!(ss - 1)->inCheck && bestValue <= -(ss - 1)->staticEval - 82));
|
||||
|
||||
// Proportional to "how much damage we have to undo"
|
||||
bonusScale += std::min(-(ss - 1)->statScore / 113, 300);
|
||||
bonusScale += std::min(-(ss - 1)->statScore / 106, 318);
|
||||
|
||||
bonusScale = std::max(bonusScale, 0);
|
||||
|
||||
const int scaledBonus = stat_bonus(depth) * bonusScale / 32;
|
||||
|
||||
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq,
|
||||
scaledBonus * 416 / 1024);
|
||||
scaledBonus * 436 / 1024);
|
||||
|
||||
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << scaledBonus * 212 / 1024;
|
||||
thisThread->mainHistory[~us][((ss - 1)->currentMove).from_to()] << scaledBonus * 207 / 1024;
|
||||
|
||||
if (type_of(pos.piece_on(prevSq)) != PAWN && ((ss - 1)->currentMove).type_of() != PROMOTION)
|
||||
thisThread->pawnHistory[pawn_structure_index(pos)][pos.piece_on(prevSq)][prevSq]
|
||||
<< scaledBonus * 1073 / 1024;
|
||||
<< scaledBonus * 1195 / 1024;
|
||||
}
|
||||
|
||||
else if (priorCapture && prevSq != SQ_NONE)
|
||||
@@ -1482,15 +1490,15 @@ moves_loop: // When in check, search starts here
|
||||
&& ((bestValue < ss->staticEval && bestValue < beta) // negative correction & no fail high
|
||||
|| (bestValue > ss->staticEval && bestMove))) // positive correction & no fail low
|
||||
{
|
||||
const auto m = (ss - 1)->currentMove;
|
||||
static const int nonPawnWeight = 154;
|
||||
const auto m = (ss - 1)->currentMove;
|
||||
constexpr int nonPawnWeight = 165;
|
||||
|
||||
auto bonus = std::clamp(int(bestValue - ss->staticEval) * depth / 8,
|
||||
-CORRECTION_HISTORY_LIMIT / 4, CORRECTION_HISTORY_LIMIT / 4);
|
||||
thisThread->pawnCorrectionHistory[us][pawn_structure_index<Correction>(pos)]
|
||||
<< bonus * 107 / 128;
|
||||
thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 162 / 128;
|
||||
thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 148 / 128;
|
||||
<< bonus * 114 / 128;
|
||||
thisThread->majorPieceCorrectionHistory[us][major_piece_index(pos)] << bonus * 163 / 128;
|
||||
thisThread->minorPieceCorrectionHistory[us][minor_piece_index(pos)] << bonus * 146 / 128;
|
||||
thisThread->nonPawnCorrectionHistory[WHITE][us][non_pawn_index<WHITE>(pos)]
|
||||
<< bonus * nonPawnWeight / 128;
|
||||
thisThread->nonPawnCorrectionHistory[BLACK][us][non_pawn_index<BLACK>(pos)]
|
||||
@@ -1624,7 +1632,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
|
||||
if (bestValue > alpha)
|
||||
alpha = bestValue;
|
||||
|
||||
futilityBase = ss->staticEval + 306;
|
||||
futilityBase = ss->staticEval + 301;
|
||||
}
|
||||
|
||||
const PieceToHistory* contHist[] = {(ss - 1)->continuationHistory,
|
||||
@@ -1687,28 +1695,27 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
|
||||
+ (*contHist[1])[pos.moved_piece(move)][move.to_sq()]
|
||||
+ thisThread->pawnHistory[pawn_structure_index(pos)][pos.moved_piece(move)]
|
||||
[move.to_sq()]
|
||||
<= 5095)
|
||||
<= 5228)
|
||||
continue;
|
||||
|
||||
// Do not search moves with bad enough SEE values (~5 Elo)
|
||||
if (!pos.see_ge(move, -83))
|
||||
if (!pos.see_ge(move, -80))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Speculative prefetch as early as possible
|
||||
prefetch(tt.first_entry(pos.key_after(move)));
|
||||
// Step 7. Make and search the move
|
||||
Piece movedPiece = pos.moved_piece(move);
|
||||
|
||||
pos.do_move(move, st, givesCheck, &tt);
|
||||
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Update the current move
|
||||
ss->currentMove = move;
|
||||
ss->continuationHistory =
|
||||
&thisThread
|
||||
->continuationHistory[ss->inCheck][capture][pos.moved_piece(move)][move.to_sq()];
|
||||
&thisThread->continuationHistory[ss->inCheck][capture][movedPiece][move.to_sq()];
|
||||
ss->continuationCorrectionHistory =
|
||||
&thisThread->continuationCorrectionHistory[pos.moved_piece(move)][move.to_sq()];
|
||||
&thisThread->continuationCorrectionHistory[movedPiece][move.to_sq()];
|
||||
|
||||
// Step 7. Make and search the move
|
||||
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
|
||||
pos.do_move(move, st, givesCheck);
|
||||
value = -qsearch<nodeType>(pos, ss + 1, -beta, -alpha);
|
||||
pos.undo_move(move);
|
||||
|
||||
@@ -1759,7 +1766,7 @@ Value Search::Worker::qsearch(Position& pos, Stack* ss, Value alpha, Value beta)
|
||||
|
||||
Depth Search::Worker::reduction(bool i, Depth d, int mn, int delta) const {
|
||||
int reductionScale = reductions[d] * reductions[mn];
|
||||
return reductionScale - delta * 814 / rootDelta + !i * reductionScale / 3 + 1304;
|
||||
return reductionScale - delta * 768 / rootDelta + !i * reductionScale * 108 / 300 + 1168;
|
||||
}
|
||||
|
||||
// elapsed() returns the time elapsed since the search started. If the
|
||||
@@ -1847,41 +1854,42 @@ void update_all_stats(const Position& pos,
|
||||
Square prevSq,
|
||||
ValueList<Move, 32>& quietsSearched,
|
||||
ValueList<Move, 32>& capturesSearched,
|
||||
Depth depth) {
|
||||
Depth depth,
|
||||
bool isTTMove) {
|
||||
|
||||
CapturePieceToHistory& captureHistory = workerThread.captureHistory;
|
||||
Piece moved_piece = pos.moved_piece(bestMove);
|
||||
PieceType captured;
|
||||
|
||||
int bonus = stat_bonus(depth);
|
||||
int bonus = stat_bonus(depth) + 300 * isTTMove;
|
||||
int malus = stat_malus(depth);
|
||||
|
||||
if (!pos.capture_stage(bestMove))
|
||||
{
|
||||
update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1131 / 1024);
|
||||
update_quiet_histories(pos, ss, workerThread, bestMove, bonus * 1216 / 1024);
|
||||
|
||||
// Decrease stats for all non-best quiet moves
|
||||
for (Move move : quietsSearched)
|
||||
update_quiet_histories(pos, ss, workerThread, move, -malus * 1028 / 1024);
|
||||
update_quiet_histories(pos, ss, workerThread, move, -malus * 1062 / 1024);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Increase stats for the best move in case it was a capture move
|
||||
captured = type_of(pos.piece_on(bestMove.to_sq()));
|
||||
captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus * 1291 / 1024;
|
||||
captureHistory[moved_piece][bestMove.to_sq()][captured] << bonus * 1272 / 1024;
|
||||
}
|
||||
|
||||
// Extra penalty for a quiet early move that was not a TT move in
|
||||
// previous ply when it gets refuted.
|
||||
if (prevSq != SQ_NONE && ((ss - 1)->moveCount == 1 + (ss - 1)->ttHit) && !pos.captured_piece())
|
||||
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 919 / 1024);
|
||||
update_continuation_histories(ss - 1, pos.piece_on(prevSq), prevSq, -malus * 966 / 1024);
|
||||
|
||||
// Decrease stats for all non-best capture moves
|
||||
for (Move move : capturesSearched)
|
||||
{
|
||||
moved_piece = pos.moved_piece(move);
|
||||
captured = type_of(pos.piece_on(move.to_sq()));
|
||||
captureHistory[moved_piece][move.to_sq()][captured] << -malus * 1090 / 1024;
|
||||
captureHistory[moved_piece][move.to_sq()][captured] << -malus * 1205 / 1024;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1889,8 +1897,8 @@ void update_all_stats(const Position& pos,
|
||||
// Updates histories of the move pairs formed by moves
|
||||
// at ply -1, -2, -3, -4, and -6 with current move.
|
||||
void update_continuation_histories(Stack* ss, Piece pc, Square to, int bonus) {
|
||||
static constexpr std::array<ConthistBonus, 5> conthist_bonuses = {
|
||||
{{1, 1024}, {2, 571}, {3, 339}, {4, 500}, {6, 592}}};
|
||||
static constexpr std::array<ConthistBonus, 6> conthist_bonuses = {
|
||||
{{1, 1025}, {2, 621}, {3, 325}, {4, 512}, {5, 122}, {6, 534}}};
|
||||
|
||||
for (const auto [i, weight] : conthist_bonuses)
|
||||
{
|
||||
@@ -1911,12 +1919,12 @@ void update_quiet_histories(
|
||||
workerThread.mainHistory[us][move.from_to()] << bonus; // Untuned to prevent duplicate effort
|
||||
|
||||
if (ss->ply < LOW_PLY_HISTORY_SIZE)
|
||||
workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 874 / 1024;
|
||||
workerThread.lowPlyHistory[ss->ply][move.from_to()] << bonus * 879 / 1024;
|
||||
|
||||
update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 853 / 1024);
|
||||
update_continuation_histories(ss, pos.moved_piece(move), move.to_sq(), bonus * 888 / 1024);
|
||||
|
||||
int pIndex = pawn_structure_index(pos);
|
||||
workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * 628 / 1024;
|
||||
workerThread.pawnHistory[pIndex][pos.moved_piece(move)][move.to_sq()] << bonus * 634 / 1024;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2187,7 +2195,7 @@ void SearchManager::pv(Search::Worker& worker,
|
||||
if (!isExact)
|
||||
info.bound = bound;
|
||||
|
||||
TimePoint time = tm.elapsed_time() + 1;
|
||||
TimePoint time = std::max(TimePoint(1), tm.elapsed_time());
|
||||
info.timeMs = time;
|
||||
info.nodes = nodes;
|
||||
info.nps = nodes * 1000 / time;
|
||||
@@ -2212,7 +2220,7 @@ bool RootMove::extract_ponder_from_tt(const TranspositionTable& tt, Position& po
|
||||
if (pv[0] == Move::none())
|
||||
return false;
|
||||
|
||||
pos.do_move(pv[0], st);
|
||||
pos.do_move(pv[0], st, &tt);
|
||||
|
||||
auto [ttHit, ttData, ttWriter] = tt.probe(pos.key());
|
||||
if (ttHit)
|
||||
|
||||
@@ -76,6 +76,7 @@ struct Stack {
|
||||
bool ttPv;
|
||||
bool ttHit;
|
||||
int cutoffCnt;
|
||||
int reduction;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+17
-14
@@ -88,17 +88,19 @@ void TimeManagement::init(Search::LimitsType& limits,
|
||||
const TimePoint scaledInc = limits.inc[us] / scaleFactor;
|
||||
|
||||
// Maximum move horizon of 50 moves
|
||||
int mtg = limits.movestogo ? std::min(limits.movestogo, 50) : 50;
|
||||
int centiMTG = limits.movestogo ? std::min(limits.movestogo * 100, 5000) : 5051;
|
||||
|
||||
// If less than one second, gradually reduce mtg
|
||||
if (scaledTime < 1000 && double(mtg) / scaledInc > 0.05)
|
||||
if (scaledTime < 1000 && double(centiMTG) / scaledInc > 5.051)
|
||||
{
|
||||
mtg = scaledTime * 0.05;
|
||||
centiMTG = scaledTime * 5.051;
|
||||
}
|
||||
|
||||
// Make sure timeLeft is > 0 since we may use it as a divisor
|
||||
TimePoint timeLeft = std::max(TimePoint(1), limits.time[us] + limits.inc[us] * (mtg - 1)
|
||||
- moveOverhead * (2 + mtg));
|
||||
TimePoint timeLeft =
|
||||
std::max(TimePoint(1),
|
||||
limits.time[us]
|
||||
+ (limits.inc[us] * (centiMTG - 100) - moveOverhead * (200 + centiMTG)) / 100);
|
||||
|
||||
// x basetime (+ z increment)
|
||||
// If there is a healthy increment, timeLeft can exceed the actual available
|
||||
@@ -107,31 +109,32 @@ void TimeManagement::init(Search::LimitsType& limits,
|
||||
{
|
||||
// Extra time according to timeLeft
|
||||
if (originalTimeAdjust < 0)
|
||||
originalTimeAdjust = 0.3285 * std::log10(timeLeft) - 0.4830;
|
||||
originalTimeAdjust = 0.3128 * std::log10(timeLeft) - 0.4354;
|
||||
|
||||
// Calculate time constants based on current time left.
|
||||
double logTimeInSec = std::log10(scaledTime / 1000.0);
|
||||
double optConstant = std::min(0.00308 + 0.000319 * logTimeInSec, 0.00506);
|
||||
double maxConstant = std::max(3.39 + 3.01 * logTimeInSec, 2.93);
|
||||
double optConstant = std::min(0.0032116 + 0.000321123 * logTimeInSec, 0.00508017);
|
||||
double maxConstant = std::max(3.3977 + 3.03950 * logTimeInSec, 2.94761);
|
||||
|
||||
optScale = std::min(0.0122 + std::pow(ply + 2.95, 0.462) * optConstant,
|
||||
0.213 * limits.time[us] / timeLeft)
|
||||
optScale = std::min(0.0121431 + std::pow(ply + 2.94693, 0.461073) * optConstant,
|
||||
0.213035 * limits.time[us] / timeLeft)
|
||||
* originalTimeAdjust;
|
||||
|
||||
maxScale = std::min(6.64, maxConstant + ply / 12.0);
|
||||
maxScale = std::min(6.67704, maxConstant + ply / 11.9847);
|
||||
}
|
||||
|
||||
// x moves in y seconds (+ z increment)
|
||||
else
|
||||
{
|
||||
optScale = std::min((0.88 + ply / 116.4) / mtg, 0.88 * limits.time[us] / timeLeft);
|
||||
maxScale = std::min(6.3, 1.5 + 0.11 * mtg);
|
||||
optScale =
|
||||
std::min((0.88 + ply / 116.4) / (centiMTG / 100.0), 0.88 * limits.time[us] / timeLeft);
|
||||
maxScale = 1.3 + 0.11 * (centiMTG / 100.0);
|
||||
}
|
||||
|
||||
// Limit the maximum possible time for this move
|
||||
optimumTime = TimePoint(optScale * timeLeft);
|
||||
maximumTime =
|
||||
TimePoint(std::min(0.825 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10;
|
||||
TimePoint(std::min(0.825179 * limits.time[us] - moveOverhead, maxScale * optimumTime)) - 10;
|
||||
|
||||
if (options["Ponder"])
|
||||
optimumTime += optimumTime / 4;
|
||||
|
||||
+3
-1
@@ -238,7 +238,9 @@ std::tuple<bool, TTData, TTWriter> TranspositionTable::probe(const Key key) cons
|
||||
> tte[i].depth8 - tte[i].relative_age(generation8) * 2)
|
||||
replace = &tte[i];
|
||||
|
||||
return {false, TTData(), TTWriter(replace)};
|
||||
return {false,
|
||||
TTData{Move::none(), VALUE_NONE, VALUE_NONE, DEPTH_ENTRY_OFFSET, BOUND_NONE, false},
|
||||
TTWriter(replace)};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user