2008-08-31 23:59:13 -06:00
|
|
|
/*
|
2008-10-19 10:56:28 -06:00
|
|
|
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
|
2021-01-08 09:04:23 -07:00
|
|
|
Copyright (C) 2004-2021 The Stockfish developers (see AUTHORS file)
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2008-10-19 10:56:28 -06:00
|
|
|
Stockfish is free software: you can redistribute it and/or modify
|
2008-08-31 23:59:13 -06:00
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2008-10-19 10:56:28 -06:00
|
|
|
Stockfish is distributed in the hope that it will be useful,
|
2008-08-31 23:59:13 -06:00
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2008-08-31 23:59:13 -06:00
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2019-03-31 04:02:19 -06:00
|
|
|
#include <algorithm>
|
2008-08-31 23:59:13 -06:00
|
|
|
#include <cassert>
|
2016-09-17 00:19:06 -06:00
|
|
|
#include <cstddef> // For offsetof()
|
|
|
|
#include <cstring> // For std::memset, std::memcmp
|
2013-02-19 00:54:16 -07:00
|
|
|
#include <iomanip>
|
2010-07-23 02:38:19 -06:00
|
|
|
#include <sstream>
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2016-04-08 11:52:15 -06:00
|
|
|
#include "bitboard.h"
|
2014-12-08 00:23:09 -07:00
|
|
|
#include "misc.h"
|
2008-08-31 23:59:13 -06:00
|
|
|
#include "movegen.h"
|
|
|
|
#include "position.h"
|
2011-04-24 02:20:03 -06:00
|
|
|
#include "thread.h"
|
2009-08-09 08:53:51 -06:00
|
|
|
#include "tt.h"
|
2014-10-26 00:16:16 -06:00
|
|
|
#include "uci.h"
|
2016-07-16 00:10:45 -06:00
|
|
|
#include "syzygy/tbprobe.h"
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2009-05-10 10:58:53 -06:00
|
|
|
using std::string;
|
|
|
|
|
2021-02-26 02:02:13 -07:00
|
|
|
namespace Stockfish {
|
|
|
|
|
2013-06-09 15:24:36 -06:00
|
|
|
namespace Zobrist {
|
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
Key psq[PIECE_NB][SQUARE_NB];
|
2013-06-09 15:24:36 -06:00
|
|
|
Key enpassant[FILE_NB];
|
2014-03-08 07:08:55 -07:00
|
|
|
Key castling[CASTLING_RIGHT_NB];
|
2016-11-21 08:17:47 -07:00
|
|
|
Key side, noPawns;
|
2013-06-09 15:24:36 -06:00
|
|
|
}
|
|
|
|
|
2012-09-15 02:50:23 -06:00
|
|
|
namespace {
|
|
|
|
|
2014-11-01 14:35:10 -06:00
|
|
|
const string PieceToChar(" PNBRQK pnbrqk");
|
|
|
|
|
2018-03-18 16:38:58 -06:00
|
|
|
constexpr Piece Pieces[] = { W_PAWN, W_KNIGHT, W_BISHOP, W_ROOK, W_QUEEN, W_KING,
|
|
|
|
B_PAWN, B_KNIGHT, B_BISHOP, B_ROOK, B_QUEEN, B_KING };
|
2012-09-15 02:50:23 -06:00
|
|
|
} // namespace
|
|
|
|
|
2012-09-01 03:45:14 -06:00
|
|
|
|
2014-11-01 11:50:27 -06:00
|
|
|
/// operator<<(Position) returns an ASCII representation of the position
|
|
|
|
|
2016-11-27 01:11:56 -07:00
|
|
|
std::ostream& operator<<(std::ostream& os, const Position& pos) {
|
2014-11-01 11:50:27 -06:00
|
|
|
|
|
|
|
os << "\n +---+---+---+---+---+---+---+---+\n";
|
|
|
|
|
|
|
|
for (Rank r = RANK_8; r >= RANK_1; --r)
|
|
|
|
{
|
|
|
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
|
|
|
os << " | " << PieceToChar[pos.piece_on(make_square(f, r))];
|
|
|
|
|
2020-06-07 15:48:38 -06:00
|
|
|
os << " | " << (1 + r) << "\n +---+---+---+---+---+---+---+---+\n";
|
2014-11-01 11:50:27 -06:00
|
|
|
}
|
|
|
|
|
2020-06-07 15:48:38 -06:00
|
|
|
os << " a b c d e f g h\n"
|
|
|
|
<< "\nFen: " << pos.fen() << "\nKey: " << std::hex << std::uppercase
|
2016-07-16 00:10:45 -06:00
|
|
|
<< std::setfill('0') << std::setw(16) << pos.key()
|
|
|
|
<< std::setfill(' ') << std::dec << "\nCheckers: ";
|
2014-11-01 11:50:27 -06:00
|
|
|
|
|
|
|
for (Bitboard b = pos.checkers(); b; )
|
2021-03-13 09:40:07 -07:00
|
|
|
os << UCI::square(pop_lsb(b)) << " ";
|
2014-11-01 11:50:27 -06:00
|
|
|
|
2016-07-16 00:10:45 -06:00
|
|
|
if ( int(Tablebases::MaxCardinality) >= popcount(pos.pieces())
|
|
|
|
&& !pos.can_castle(ANY_CASTLING))
|
|
|
|
{
|
2016-11-27 01:11:56 -07:00
|
|
|
StateInfo st;
|
2021-04-19 11:50:19 -06:00
|
|
|
ASSERT_ALIGNED(&st, Eval::NNUE::CacheLineSize);
|
2020-11-03 03:23:35 -07:00
|
|
|
|
2016-11-27 01:11:56 -07:00
|
|
|
Position p;
|
|
|
|
p.set(pos.fen(), pos.is_chess960(), &st, pos.this_thread());
|
2016-07-16 00:10:45 -06:00
|
|
|
Tablebases::ProbeState s1, s2;
|
2016-11-27 01:11:56 -07:00
|
|
|
Tablebases::WDLScore wdl = Tablebases::probe_wdl(p, &s1);
|
|
|
|
int dtz = Tablebases::probe_dtz(p, &s2);
|
2016-07-16 00:10:45 -06:00
|
|
|
os << "\nTablebases WDL: " << std::setw(4) << wdl << " (" << s1 << ")"
|
|
|
|
<< "\nTablebases DTZ: " << std::setw(4) << dtz << " (" << s2 << ")";
|
|
|
|
}
|
|
|
|
|
2014-11-01 11:50:27 -06:00
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-05-21 01:37:16 -06:00
|
|
|
// Marcel van Kervinck's cuckoo algorithm for fast detection of "upcoming repetition"
|
|
|
|
// situations. Description of the algorithm in the following paper:
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
// https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
|
|
|
|
|
|
|
|
// First and second hash functions for indexing the cuckoo tables
|
2018-05-21 01:37:16 -06:00
|
|
|
inline int H1(Key h) { return h & 0x1fff; }
|
|
|
|
inline int H2(Key h) { return (h >> 16) & 0x1fff; }
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
|
|
|
|
// Cuckoo tables with Zobrist hashes of valid reversible moves, and the moves themselves
|
|
|
|
Key cuckoo[8192];
|
|
|
|
Move cuckooMove[8192];
|
|
|
|
|
|
|
|
|
2020-06-24 14:19:58 -06:00
|
|
|
/// Position::init() initializes at startup the various arrays used to compute hash keys
|
2013-06-09 05:43:30 -06:00
|
|
|
|
|
|
|
void Position::init() {
|
|
|
|
|
Simpler PRNG and faster magics search
This patch replaces RKISS by a simpler and faster PRNG, xorshift64* proposed
by S. Vigna (2014). It is extremely simple, has a large enough period for
Stockfish's needs (2^64), requires no warming-up (allowing such code to be
removed), and offers slightly better randomness than MT19937.
Paper: http://xorshift.di.unimi.it/
Reference source code (public domain):
http://xorshift.di.unimi.it/xorshift64star.c
The patch also simplifies how init_magics() searches for magics:
- Old logic: seed the PRNG always with the same seed,
then use optimized bit rotations to tailor the RNG sequence per rank.
- New logic: seed the PRNG with an optimized seed per rank.
This has two advantages:
1. Less code and less computation to perform during magics search (not ROTL).
2. More choices for random sequence tuning. The old logic only let us choose
from 4096 bit rotation pairs. With the new one, we can look for the best seeds
among 2^64 values. Indeed, the set of seeds[][] provided in the patch reduces
the effort needed to find the magics:
64-bit SF:
Old logic -> 5,783,789 rand64() calls needed to find the magics
New logic -> 4,420,086 calls
32-bit SF:
Old logic -> 2,175,518 calls
New logic -> 1,895,955 calls
In the 64-bit case, init_magics() take 25 ms less to complete (Intel Core i5).
Finally, when playing with strength handicap, non-determinism is achieved
by setting the seed of the static RNG only once. Afterwards, there is no need
to skip output values.
The bench only changes because the Zobrist keys are now different (since they
are random numbers straight out of the PRNG).
The RNG seed has been carefully chosen so that the
resulting Zobrist keys are particularly well-behaved:
1. All triplets of XORed keys are unique, implying that it
would take at least 7 keys to find a 64-bit collision
(test suggested by ceebo)
2. All pairs of XORed keys are unique modulo 2^32
3. The cardinality of { (key1 ^ key2) >> 48 } is as close
as possible to the maximum (65536)
Point 2 aims at ensuring a good distribution among the bits
that determine an TT entry's cluster, likewise point 3
among the bits that form the TT entry's key16 inside a
cluster.
Details:
Bitset card(key1^key2)
------ ---------------
RKISS
key16 64894 = 99.020% of theoretical maximum
low18 180117 = 99.293%
low32 305362 = 99.997%
Xorshift64*, old seed
key16 64918 = 99.057%
low18 179994 = 99.225%
low32 305350 = 99.993%
Xorshift64*, new seed
key16 65027 = 99.223%
low18 181118 = 99.845%
low32 305371 = 100.000%
Bench: 9324905
Resolves #148
2014-12-07 17:10:57 -07:00
|
|
|
PRNG rng(1070372);
|
2013-06-09 05:43:30 -06:00
|
|
|
|
2016-09-04 07:29:11 -06:00
|
|
|
for (Piece pc : Pieces)
|
|
|
|
for (Square s = SQ_A1; s <= SQ_H8; ++s)
|
|
|
|
Zobrist::psq[pc][s] = rng.rand<Key>();
|
2013-06-09 05:43:30 -06:00
|
|
|
|
2013-09-15 01:02:09 -06:00
|
|
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
Simpler PRNG and faster magics search
This patch replaces RKISS by a simpler and faster PRNG, xorshift64* proposed
by S. Vigna (2014). It is extremely simple, has a large enough period for
Stockfish's needs (2^64), requires no warming-up (allowing such code to be
removed), and offers slightly better randomness than MT19937.
Paper: http://xorshift.di.unimi.it/
Reference source code (public domain):
http://xorshift.di.unimi.it/xorshift64star.c
The patch also simplifies how init_magics() searches for magics:
- Old logic: seed the PRNG always with the same seed,
then use optimized bit rotations to tailor the RNG sequence per rank.
- New logic: seed the PRNG with an optimized seed per rank.
This has two advantages:
1. Less code and less computation to perform during magics search (not ROTL).
2. More choices for random sequence tuning. The old logic only let us choose
from 4096 bit rotation pairs. With the new one, we can look for the best seeds
among 2^64 values. Indeed, the set of seeds[][] provided in the patch reduces
the effort needed to find the magics:
64-bit SF:
Old logic -> 5,783,789 rand64() calls needed to find the magics
New logic -> 4,420,086 calls
32-bit SF:
Old logic -> 2,175,518 calls
New logic -> 1,895,955 calls
In the 64-bit case, init_magics() take 25 ms less to complete (Intel Core i5).
Finally, when playing with strength handicap, non-determinism is achieved
by setting the seed of the static RNG only once. Afterwards, there is no need
to skip output values.
The bench only changes because the Zobrist keys are now different (since they
are random numbers straight out of the PRNG).
The RNG seed has been carefully chosen so that the
resulting Zobrist keys are particularly well-behaved:
1. All triplets of XORed keys are unique, implying that it
would take at least 7 keys to find a 64-bit collision
(test suggested by ceebo)
2. All pairs of XORed keys are unique modulo 2^32
3. The cardinality of { (key1 ^ key2) >> 48 } is as close
as possible to the maximum (65536)
Point 2 aims at ensuring a good distribution among the bits
that determine an TT entry's cluster, likewise point 3
among the bits that form the TT entry's key16 inside a
cluster.
Details:
Bitset card(key1^key2)
------ ---------------
RKISS
key16 64894 = 99.020% of theoretical maximum
low18 180117 = 99.293%
low32 305362 = 99.997%
Xorshift64*, old seed
key16 64918 = 99.057%
low18 179994 = 99.225%
low32 305350 = 99.993%
Xorshift64*, new seed
key16 65027 = 99.223%
low18 181118 = 99.845%
low32 305371 = 100.000%
Bench: 9324905
Resolves #148
2014-12-07 17:10:57 -07:00
|
|
|
Zobrist::enpassant[f] = rng.rand<Key>();
|
2013-06-09 05:43:30 -06:00
|
|
|
|
2014-12-07 16:53:33 -07:00
|
|
|
for (int cr = NO_CASTLING; cr <= ANY_CASTLING; ++cr)
|
Remove old zobrist trick for castling rights
Removes an 8 year old micro optimization aimed at 32-bit architectures
because back then doing an xor of a Key could not be done in one instruction.
See original commit here 821e1c7
STC https://tests.stockfishchess.org/tests/view/5ef5833dde213bf647527d0c
LLR: 2.94 (-2.94,2.94) {-1.50,0.50}
Total: 162648 W: 31053 L: 31097 D: 100498
Ptnml(0-2): 2841, 18966, 37715, 19000, 2802
LTC https://tests.stockfishchess.org/tests/view/5ef7b1bbf993893290cc1489
LLR: 2.93 (-2.94,2.94) {-1.50,0.50}
Total: 62360 W: 7617 L: 7586 D: 47157
Ptnml(0-2): 423, 5662, 18994, 5663, 438
closes https://github.com/official-stockfish/Stockfish/pull/2775
bench: 4591425
2020-06-25 23:08:17 -06:00
|
|
|
Zobrist::castling[cr] = rng.rand<Key>();
|
2013-06-09 05:43:30 -06:00
|
|
|
|
Simpler PRNG and faster magics search
This patch replaces RKISS by a simpler and faster PRNG, xorshift64* proposed
by S. Vigna (2014). It is extremely simple, has a large enough period for
Stockfish's needs (2^64), requires no warming-up (allowing such code to be
removed), and offers slightly better randomness than MT19937.
Paper: http://xorshift.di.unimi.it/
Reference source code (public domain):
http://xorshift.di.unimi.it/xorshift64star.c
The patch also simplifies how init_magics() searches for magics:
- Old logic: seed the PRNG always with the same seed,
then use optimized bit rotations to tailor the RNG sequence per rank.
- New logic: seed the PRNG with an optimized seed per rank.
This has two advantages:
1. Less code and less computation to perform during magics search (not ROTL).
2. More choices for random sequence tuning. The old logic only let us choose
from 4096 bit rotation pairs. With the new one, we can look for the best seeds
among 2^64 values. Indeed, the set of seeds[][] provided in the patch reduces
the effort needed to find the magics:
64-bit SF:
Old logic -> 5,783,789 rand64() calls needed to find the magics
New logic -> 4,420,086 calls
32-bit SF:
Old logic -> 2,175,518 calls
New logic -> 1,895,955 calls
In the 64-bit case, init_magics() take 25 ms less to complete (Intel Core i5).
Finally, when playing with strength handicap, non-determinism is achieved
by setting the seed of the static RNG only once. Afterwards, there is no need
to skip output values.
The bench only changes because the Zobrist keys are now different (since they
are random numbers straight out of the PRNG).
The RNG seed has been carefully chosen so that the
resulting Zobrist keys are particularly well-behaved:
1. All triplets of XORed keys are unique, implying that it
would take at least 7 keys to find a 64-bit collision
(test suggested by ceebo)
2. All pairs of XORed keys are unique modulo 2^32
3. The cardinality of { (key1 ^ key2) >> 48 } is as close
as possible to the maximum (65536)
Point 2 aims at ensuring a good distribution among the bits
that determine an TT entry's cluster, likewise point 3
among the bits that form the TT entry's key16 inside a
cluster.
Details:
Bitset card(key1^key2)
------ ---------------
RKISS
key16 64894 = 99.020% of theoretical maximum
low18 180117 = 99.293%
low32 305362 = 99.997%
Xorshift64*, old seed
key16 64918 = 99.057%
low18 179994 = 99.225%
low32 305350 = 99.993%
Xorshift64*, new seed
key16 65027 = 99.223%
low18 181118 = 99.845%
low32 305371 = 100.000%
Bench: 9324905
Resolves #148
2014-12-07 17:10:57 -07:00
|
|
|
Zobrist::side = rng.rand<Key>();
|
2016-11-21 08:17:47 -07:00
|
|
|
Zobrist::noPawns = rng.rand<Key>();
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
|
|
|
|
// Prepare the cuckoo tables
|
2018-07-17 15:13:12 -06:00
|
|
|
std::memset(cuckoo, 0, sizeof(cuckoo));
|
|
|
|
std::memset(cuckooMove, 0, sizeof(cuckooMove));
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
int count = 0;
|
|
|
|
for (Piece pc : Pieces)
|
|
|
|
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
|
|
|
|
for (Square s2 = Square(s1 + 1); s2 <= SQ_H8; ++s2)
|
2020-05-28 09:48:31 -06:00
|
|
|
if ((type_of(pc) != PAWN) && (attacks_bb(type_of(pc), s1, 0) & s2))
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
{
|
|
|
|
Move move = make_move(s1, s2);
|
|
|
|
Key key = Zobrist::psq[pc][s1] ^ Zobrist::psq[pc][s2] ^ Zobrist::side;
|
2018-05-21 01:37:16 -06:00
|
|
|
int i = H1(key);
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
std::swap(cuckoo[i], key);
|
|
|
|
std::swap(cuckooMove[i], move);
|
2019-01-01 06:10:26 -07:00
|
|
|
if (move == MOVE_NONE) // Arrived at empty slot?
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
break;
|
|
|
|
i = (i == H1(key)) ? H2(key) : H1(key); // Push victim to alternative slot
|
|
|
|
}
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
assert(count == 3668);
|
2013-06-09 05:43:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-11-11 05:18:10 -07:00
|
|
|
/// Position::set() initializes the position object with the given FEN string.
|
|
|
|
/// This function is not very robust - make sure that input FENs are correct,
|
|
|
|
/// this is assumed to be the responsibility of the GUI.
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2016-04-11 08:45:36 -06:00
|
|
|
Position& Position::set(const string& fenStr, bool isChess960, StateInfo* si, Thread* th) {
|
2010-07-23 02:38:19 -06:00
|
|
|
/*
|
|
|
|
A FEN string defines a particular position using only the ASCII character set.
|
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
A FEN string contains six fields separated by a space. The fields are:
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
1) Piece placement (from white's perspective). Each rank is described, starting
|
2013-12-02 11:04:09 -07:00
|
|
|
with rank 8 and ending with rank 1. Within each rank, the contents of each
|
2011-10-30 08:46:00 -06:00
|
|
|
square are described from file A through file H. Following the Standard
|
|
|
|
Algebraic Notation (SAN), each piece is identified by a single letter taken
|
|
|
|
from the standard English names. White pieces are designated using upper-case
|
2013-12-02 11:04:09 -07:00
|
|
|
letters ("PNBRQK") whilst Black uses lowercase ("pnbrqk"). Blank squares are
|
2011-10-30 08:46:00 -06:00
|
|
|
noted using digits 1 through 8 (the number of blank squares), and "/"
|
|
|
|
separates ranks.
|
2010-07-23 02:38:19 -06:00
|
|
|
|
|
|
|
2) Active color. "w" means white moves next, "b" means black.
|
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
3) Castling availability. If neither side can castle, this is "-". Otherwise,
|
|
|
|
this has one or more letters: "K" (White can castle kingside), "Q" (White
|
|
|
|
can castle queenside), "k" (Black can castle kingside), and/or "q" (Black
|
|
|
|
can castle queenside).
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
4) En passant target square (in algebraic notation). If there's no en passant
|
|
|
|
target square, this is "-". If a pawn has just made a 2-square move, this
|
2020-07-06 20:43:54 -06:00
|
|
|
is the position "behind" the pawn. Following X-FEN standard, this is recorded only
|
|
|
|
if there is a pawn in position to make an en passant capture, and if there really
|
|
|
|
is a pawn that might have advanced two squares.
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
5) Halfmove clock. This is the number of halfmoves since the last pawn advance
|
|
|
|
or capture. This is used to determine if a draw can be claimed under the
|
|
|
|
fifty-move rule.
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
6) Fullmove number. The number of the full move. It starts at 1, and is
|
|
|
|
incremented after Black's move.
|
2010-07-23 02:38:19 -06:00
|
|
|
*/
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-06-06 17:16:31 -06:00
|
|
|
unsigned char col, row, token;
|
2013-12-03 02:09:17 -07:00
|
|
|
size_t idx;
|
2011-01-14 01:20:23 -07:00
|
|
|
Square sq = SQ_A8;
|
2012-11-11 05:18:10 -07:00
|
|
|
std::istringstream ss(fenStr);
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2016-04-11 08:45:36 -06:00
|
|
|
std::memset(this, 0, sizeof(Position));
|
|
|
|
std::memset(si, 0, sizeof(StateInfo));
|
|
|
|
st = si;
|
|
|
|
|
2012-11-11 05:18:10 -07:00
|
|
|
ss >> std::noskipws;
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2011-06-27 03:07:57 -06:00
|
|
|
// 1. Piece placement
|
2012-11-11 05:18:10 -07:00
|
|
|
while ((ss >> token) && !isspace(token))
|
2008-09-27 05:28:58 -06:00
|
|
|
{
|
2012-01-12 10:12:25 -07:00
|
|
|
if (isdigit(token))
|
2017-12-04 09:52:31 -07:00
|
|
|
sq += (token - '0') * EAST; // Advance the given number of files
|
2011-06-27 03:07:57 -06:00
|
|
|
|
2012-01-12 10:12:25 -07:00
|
|
|
else if (token == '/')
|
2017-12-04 09:52:31 -07:00
|
|
|
sq += 2 * SOUTH;
|
2011-06-27 03:07:57 -06:00
|
|
|
|
Remove EvalList
This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code.
The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective.
If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object.
If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE).
An STC test has confirmed a small speedup:
https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 87704 W: 9763 L: 9500 D: 68441
Ptnml(0-2): 426, 6950, 28845, 7197, 434
closes https://github.com/official-stockfish/Stockfish/pull/3068
No functional change
2020-08-23 18:29:38 -06:00
|
|
|
else if ((idx = PieceToChar.find(token)) != string::npos) {
|
|
|
|
put_piece(Piece(idx), sq);
|
2013-09-15 01:02:09 -06:00
|
|
|
++sq;
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-23 02:38:19 -06:00
|
|
|
// 2. Active color
|
2012-11-11 05:18:10 -07:00
|
|
|
ss >> token;
|
2010-07-23 02:38:19 -06:00
|
|
|
sideToMove = (token == 'w' ? WHITE : BLACK);
|
2012-11-11 05:18:10 -07:00
|
|
|
ss >> token;
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2011-10-29 05:23:54 -06:00
|
|
|
// 3. Castling availability. Compatible with 3 standards: Normal FEN standard,
|
|
|
|
// Shredder-FEN that uses the letters of the columns on which the rooks began
|
|
|
|
// the game instead of KQkq and also X-FEN standard that, in case of Chess960,
|
|
|
|
// if an inner rook is associated with the castling right, the castling tag is
|
|
|
|
// replaced by the file letter of the involved rook, as for the Shredder-FEN.
|
2012-11-11 05:18:10 -07:00
|
|
|
while ((ss >> token) && !isspace(token))
|
2011-10-29 05:23:54 -06:00
|
|
|
{
|
|
|
|
Square rsq;
|
|
|
|
Color c = islower(token) ? BLACK : WHITE;
|
2015-05-28 21:38:40 -06:00
|
|
|
Piece rook = make_piece(c, ROOK);
|
2011-10-29 05:23:54 -06:00
|
|
|
|
|
|
|
token = char(toupper(token));
|
|
|
|
|
|
|
|
if (token == 'K')
|
2015-05-28 21:38:40 -06:00
|
|
|
for (rsq = relative_square(c, SQ_H1); piece_on(rsq) != rook; --rsq) {}
|
2011-10-29 05:23:54 -06:00
|
|
|
|
|
|
|
else if (token == 'Q')
|
2015-05-28 21:38:40 -06:00
|
|
|
for (rsq = relative_square(c, SQ_A1); piece_on(rsq) != rook; ++rsq) {}
|
2011-10-29 05:23:54 -06:00
|
|
|
|
|
|
|
else if (token >= 'A' && token <= 'H')
|
2014-03-22 16:35:30 -06:00
|
|
|
rsq = make_square(File(token - 'A'), relative_rank(c, RANK_1));
|
2011-10-29 05:23:54 -06:00
|
|
|
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
|
2014-03-08 07:08:55 -07:00
|
|
|
set_castling_right(c, rsq);
|
2011-10-29 05:23:54 -06:00
|
|
|
}
|
2011-06-27 03:07:57 -06:00
|
|
|
|
2020-07-06 20:43:54 -06:00
|
|
|
// 4. En passant square.
|
|
|
|
// Ignore if square is invalid or not on side to move relative rank 6.
|
|
|
|
bool enpassant = false;
|
|
|
|
|
2012-11-11 05:18:10 -07:00
|
|
|
if ( ((ss >> col) && (col >= 'a' && col <= 'h'))
|
2020-07-06 20:43:54 -06:00
|
|
|
&& ((ss >> row) && (row == (sideToMove == WHITE ? '6' : '3'))))
|
2010-01-06 01:58:41 -07:00
|
|
|
{
|
2014-03-22 16:35:30 -06:00
|
|
|
st->epSquare = make_square(File(col - 'a'), Rank(row - '1'));
|
2010-07-23 02:38:19 -06:00
|
|
|
|
2020-07-06 20:43:54 -06:00
|
|
|
// En passant square will be considered only if
|
|
|
|
// a) side to move have a pawn threatening epSquare
|
|
|
|
// b) there is an enemy pawn in front of epSquare
|
|
|
|
// c) there is no piece on epSquare or behind epSquare
|
|
|
|
enpassant = pawn_attacks_bb(~sideToMove, st->epSquare) & pieces(sideToMove, PAWN)
|
|
|
|
&& (pieces(~sideToMove, PAWN) & (st->epSquare + pawn_push(~sideToMove)))
|
2021-06-04 04:20:27 -06:00
|
|
|
&& !(pieces() & (st->epSquare | (st->epSquare + pawn_push(sideToMove))));
|
2010-01-06 01:58:41 -07:00
|
|
|
}
|
2020-07-06 20:43:54 -06:00
|
|
|
|
2021-06-04 04:20:27 -06:00
|
|
|
if (!enpassant)
|
2016-04-11 08:45:36 -06:00
|
|
|
st->epSquare = SQ_NONE;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2011-06-27 03:07:57 -06:00
|
|
|
// 5-6. Halfmove clock and fullmove number
|
2013-02-16 04:42:22 -07:00
|
|
|
ss >> std::skipws >> st->rule50 >> gamePly;
|
2011-07-20 02:01:12 -06:00
|
|
|
|
Let ss->ply denote the number of plies from the root to the current node
This patch lets ss->ply be equal to 0 at the root of the search.
Currently, the root has ss->ply == 1, which is less intuitive:
- Setting the rootNode bool has to check (ss-1)->ply == 0.
- All mate values are off by one: the code seems to assume that mated-in-0
is -VALUE_MATE, mate-1-in-ply is VALUE_MATE-1, mated-in-2-ply is VALUE_MATE+2, etc.
But the mate_in() and mated_in() functions are called with ss->ply, which is 1 in
at the root.
- The is_draw() function currently needs to explain why it has "ply - 1 > i" instead
of simply "ply > i".
- The ss->ply >= MAX_PLY tests in search() and qsearch() already assume that
ss->ply == 0 at the root. If we start at ss->ply == 1, it would make more sense to
go up to and including ss->ply == MAX_PLY, so stop at ss->ply > MAX_PLY. See also
the asserts testing for 0 <= ss->ply && ss->ply < MAX_PLY.
The reason for ss->ply == 1 at the root is the line "ss->ply = (ss-1)->ply + 1" at
the start for search() and qsearch(). By replacing this with "(ss+1)->ply = ss->ply + 1"
we keep ss->ply == 0 at the root. Note that search() already clears killers in (ss+2),
so there is no danger in accessing ss+1.
I have NOT changed pv[MAX_PLY + 1] to pv[MAX_PLY + 2] in search() and qsearch().
It seems to me that MAX_PLY + 1 is exactly right:
- MAX_PLY entries for ss->ply running from 0 to MAX_PLY-1, and 1 entry for the
final MOVE_NONE.
I have verified that mate scores are reported correctly. (They were already reported
correctly due to the extra ply being rounded down when converting to moves.)
The value of seldepth output to the user should probably not change, so I add 1 to it.
(Humans count from 1, computers from 0.)
A small optimisation I did not include: instead of setting ss->ply in every invocation
of search() and qsearch(), it could be set once for all plies at the start of
Thread::search(). This saves a couple of instructions per node.
No functional change (unless the search searches a branch MAX_PLY deep), so bench
does not change.
2017-09-16 13:49:29 -06:00
|
|
|
// Convert from fullmove starting from 1 to gamePly starting from 0,
|
2011-07-20 02:01:12 -06:00
|
|
|
// handle also common incorrect FEN with fullmove = 0.
|
2014-04-27 03:40:44 -06:00
|
|
|
gamePly = std::max(2 * (gamePly - 1), 0) + (sideToMove == BLACK);
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2011-10-29 05:23:54 -06:00
|
|
|
chess960 = isChess960;
|
2012-04-06 11:36:46 -06:00
|
|
|
thisThread = th;
|
2021-06-04 04:20:27 -06:00
|
|
|
set_state(st);
|
2011-07-16 03:42:27 -06:00
|
|
|
|
2011-10-16 16:56:25 -06:00
|
|
|
assert(pos_is_ok());
|
2016-04-11 08:45:36 -06:00
|
|
|
|
|
|
|
return *this;
|
2010-07-23 02:38:19 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-08 07:08:55 -07:00
|
|
|
/// Position::set_castling_right() is a helper function used to set castling
|
|
|
|
/// rights given the corresponding color and the rook starting square.
|
2011-06-27 09:06:15 -06:00
|
|
|
|
2014-03-08 07:08:55 -07:00
|
|
|
void Position::set_castling_right(Color c, Square rfrom) {
|
2011-10-29 05:23:54 -06:00
|
|
|
|
2015-08-04 01:00:52 -06:00
|
|
|
Square kfrom = square<KING>(c);
|
2019-08-12 08:42:28 -06:00
|
|
|
CastlingRights cr = c & (kfrom < rfrom ? KING_SIDE: QUEEN_SIDE);
|
2011-06-27 09:06:15 -06:00
|
|
|
|
2014-03-08 07:08:55 -07:00
|
|
|
st->castlingRights |= cr;
|
|
|
|
castlingRightsMask[kfrom] |= cr;
|
|
|
|
castlingRightsMask[rfrom] |= cr;
|
|
|
|
castlingRookSquare[cr] = rfrom;
|
2012-02-27 04:11:18 -07:00
|
|
|
|
2019-08-12 08:42:28 -06:00
|
|
|
Square kto = relative_square(c, cr & KING_SIDE ? SQ_G1 : SQ_C1);
|
|
|
|
Square rto = relative_square(c, cr & KING_SIDE ? SQ_F1 : SQ_D1);
|
2012-02-27 04:11:18 -07:00
|
|
|
|
Change definition of between_bb()
We remark that in current master, most of our use cases for between_bb() can be
optimized if the second parameter of the function is added to the segment. So we
change the definition of between_bb(s1, s2) such that it excludes s1 but includes s2.
We also use a precomputed array for between_bb() for another small speed gain
(see https://tests.stockfishchess.org/tests/view/604d09f72433018de7a389fb).
Passed STC:
LLR: 2.96 (-2.94,2.94) {-0.25,1.25}
Total: 18736 W: 1746 L: 1607 D: 15383
Ptnml(0-2): 61, 1226, 6644, 1387, 50
https://tests.stockfishchess.org/tests/view/60428c84ddcba5f0627bb6e4
Yellow LTC:
LTC:
LLR: -3.00 (-2.94,2.94) {0.25,1.25}
Total: 39144 W: 1431 L: 1413 D: 36300
Ptnml(0-2): 13, 1176, 17184, 1178, 21
https://tests.stockfishchess.org/tests/view/605128702433018de7a38ca1
Closes https://github.com/official-stockfish/Stockfish/pull/3397
---------
Verified for correctness by running perft on the following position:
./stockfish
position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21
go perft 6
Nodes searched: 6136386434
--------
No functional change
2021-03-15 13:06:42 -06:00
|
|
|
castlingPath[cr] = (between_bb(rfrom, rto) | between_bb(kfrom, kto))
|
2020-04-29 09:39:25 -06:00
|
|
|
& ~(kfrom | rfrom);
|
2011-06-27 09:06:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-03 02:35:44 -06:00
|
|
|
/// Position::set_check_info() sets king attacks to detect if a move gives check
|
|
|
|
|
2016-08-27 02:05:42 -06:00
|
|
|
void Position::set_check_info(StateInfo* si) const {
|
2016-07-03 02:35:44 -06:00
|
|
|
|
Renaming some variables in code
Implements renaming suggestions by Marco Costalba, Günther Demetz,
Gontran Lemaire, Ronald de Man, Stéphane Nicolet, Alain Savard,
Joost VandeVondele, Jerry Donald Watson, Mike Whiteley, xoto10,
and I hope that I haven't forgotten anybody.
Perpetual renaming thread for suggestions:
https://github.com/official-stockfish/Stockfish/issues/1426
No functional change.
2018-03-15 03:44:26 -06:00
|
|
|
si->blockersForKing[WHITE] = slider_blockers(pieces(BLACK), square<KING>(WHITE), si->pinners[BLACK]);
|
|
|
|
si->blockersForKing[BLACK] = slider_blockers(pieces(WHITE), square<KING>(BLACK), si->pinners[WHITE]);
|
2016-07-03 02:35:44 -06:00
|
|
|
|
2016-08-27 02:05:42 -06:00
|
|
|
Square ksq = square<KING>(~sideToMove);
|
2016-07-03 02:35:44 -06:00
|
|
|
|
2020-05-28 09:48:31 -06:00
|
|
|
si->checkSquares[PAWN] = pawn_attacks_bb(~sideToMove, ksq);
|
|
|
|
si->checkSquares[KNIGHT] = attacks_bb<KNIGHT>(ksq);
|
|
|
|
si->checkSquares[BISHOP] = attacks_bb<BISHOP>(ksq, pieces());
|
|
|
|
si->checkSquares[ROOK] = attacks_bb<ROOK>(ksq, pieces());
|
2016-08-27 02:05:42 -06:00
|
|
|
si->checkSquares[QUEEN] = si->checkSquares[BISHOP] | si->checkSquares[ROOK];
|
|
|
|
si->checkSquares[KING] = 0;
|
2016-07-03 02:35:44 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-12 15:46:17 -06:00
|
|
|
/// Position::set_state() computes the hash keys of the position, and other
|
|
|
|
/// data that once computed is updated incrementally as moves are made.
|
|
|
|
/// The function is only used when a new position is set up, and to verify
|
|
|
|
/// the correctness of the StateInfo data when running in debug mode.
|
|
|
|
|
|
|
|
void Position::set_state(StateInfo* si) const {
|
|
|
|
|
2016-11-21 08:17:47 -07:00
|
|
|
si->key = si->materialKey = 0;
|
|
|
|
si->pawnKey = Zobrist::noPawns;
|
2014-12-07 16:53:33 -07:00
|
|
|
si->nonPawnMaterial[WHITE] = si->nonPawnMaterial[BLACK] = VALUE_ZERO;
|
2015-08-04 01:00:52 -06:00
|
|
|
si->checkersBB = attackers_to(square<KING>(sideToMove)) & pieces(~sideToMove);
|
2014-03-12 15:46:17 -06:00
|
|
|
|
2016-08-27 02:05:42 -06:00
|
|
|
set_check_info(si);
|
2016-07-03 02:35:44 -06:00
|
|
|
|
2014-03-12 15:46:17 -06:00
|
|
|
for (Bitboard b = pieces(); b; )
|
|
|
|
{
|
2021-03-13 09:40:07 -07:00
|
|
|
Square s = pop_lsb(b);
|
2014-03-12 15:46:17 -06:00
|
|
|
Piece pc = piece_on(s);
|
2016-09-03 10:14:01 -06:00
|
|
|
si->key ^= Zobrist::psq[pc][s];
|
2019-05-16 06:11:00 -06:00
|
|
|
|
|
|
|
if (type_of(pc) == PAWN)
|
|
|
|
si->pawnKey ^= Zobrist::psq[pc][s];
|
|
|
|
|
2019-05-02 11:36:25 -06:00
|
|
|
else if (type_of(pc) != KING)
|
2019-05-16 06:11:00 -06:00
|
|
|
si->nonPawnMaterial[color_of(pc)] += PieceValue[MG][pc];
|
2014-03-12 15:46:17 -06:00
|
|
|
}
|
|
|
|
|
2015-02-08 05:21:50 -07:00
|
|
|
if (si->epSquare != SQ_NONE)
|
|
|
|
si->key ^= Zobrist::enpassant[file_of(si->epSquare)];
|
2014-03-12 15:46:17 -06:00
|
|
|
|
|
|
|
if (sideToMove == BLACK)
|
|
|
|
si->key ^= Zobrist::side;
|
|
|
|
|
2015-02-07 11:34:24 -07:00
|
|
|
si->key ^= Zobrist::castling[si->castlingRights];
|
2014-03-12 15:46:17 -06:00
|
|
|
|
2016-09-04 07:29:11 -06:00
|
|
|
for (Piece pc : Pieces)
|
|
|
|
for (int cnt = 0; cnt < pieceCount[pc]; ++cnt)
|
|
|
|
si->materialKey ^= Zobrist::psq[pc][cnt];
|
2014-03-12 15:46:17 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-16 00:10:45 -06:00
|
|
|
/// Position::set() is an overload to initialize the position object with
|
2017-03-08 19:35:23 -07:00
|
|
|
/// the given endgame code string like "KBPKN". It is mainly a helper to
|
2017-06-24 04:36:07 -06:00
|
|
|
/// get the material key out of an endgame code.
|
2016-07-16 00:10:45 -06:00
|
|
|
|
|
|
|
Position& Position::set(const string& code, Color c, StateInfo* si) {
|
|
|
|
|
|
|
|
assert(code[0] == 'K');
|
|
|
|
|
|
|
|
string sides[] = { code.substr(code.find('K', 1)), // Weak
|
2020-02-07 02:42:10 -07:00
|
|
|
code.substr(0, std::min(code.find('v'), code.find('K', 1))) }; // Strong
|
|
|
|
|
|
|
|
assert(sides[0].length() > 0 && sides[0].length() < 8);
|
|
|
|
assert(sides[1].length() > 0 && sides[1].length() < 8);
|
2016-07-16 00:10:45 -06:00
|
|
|
|
|
|
|
std::transform(sides[c].begin(), sides[c].end(), sides[c].begin(), tolower);
|
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
string fenStr = "8/" + sides[0] + char(8 - sides[0].length() + '0') + "/8/8/8/8/"
|
|
|
|
+ sides[1] + char(8 - sides[1].length() + '0') + "/8 w - - 0 10";
|
2016-07-16 00:10:45 -06:00
|
|
|
|
|
|
|
return set(fenStr, false, si, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-02 11:04:09 -07:00
|
|
|
/// Position::fen() returns a FEN representation of the position. In case of
|
|
|
|
/// Chess960 the Shredder-FEN notation is used. This is mainly a debugging function.
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2021-01-30 01:50:04 -07:00
|
|
|
string Position::fen() const {
|
2008-09-29 11:02:48 -06:00
|
|
|
|
2013-12-03 02:09:17 -07:00
|
|
|
int emptyCnt;
|
2012-11-11 05:18:10 -07:00
|
|
|
std::ostringstream ss;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-04-06 02:50:27 -06:00
|
|
|
for (Rank r = RANK_8; r >= RANK_1; --r)
|
2008-09-29 11:02:48 -06:00
|
|
|
{
|
2014-04-06 02:50:27 -06:00
|
|
|
for (File f = FILE_A; f <= FILE_H; ++f)
|
2008-09-29 11:02:48 -06:00
|
|
|
{
|
2014-04-06 02:50:27 -06:00
|
|
|
for (emptyCnt = 0; f <= FILE_H && empty(make_square(f, r)); ++f)
|
2013-12-03 02:09:17 -07:00
|
|
|
++emptyCnt;
|
2013-01-04 04:32:13 -07:00
|
|
|
|
2013-12-03 02:09:17 -07:00
|
|
|
if (emptyCnt)
|
2013-01-04 04:32:13 -07:00
|
|
|
ss << emptyCnt;
|
2013-12-03 02:09:17 -07:00
|
|
|
|
2014-04-06 02:50:27 -06:00
|
|
|
if (f <= FILE_H)
|
|
|
|
ss << PieceToChar[piece_on(make_square(f, r))];
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
2011-01-29 05:14:01 -07:00
|
|
|
|
2014-04-06 02:50:27 -06:00
|
|
|
if (r > RANK_1)
|
2012-11-11 05:18:10 -07:00
|
|
|
ss << '/';
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
2010-07-24 10:59:18 -06:00
|
|
|
|
2012-11-11 05:18:10 -07:00
|
|
|
ss << (sideToMove == WHITE ? " w " : " b ");
|
2010-07-24 10:59:18 -06:00
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
if (can_castle(WHITE_OO))
|
2019-01-01 06:10:26 -07:00
|
|
|
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OO ))) : 'K');
|
2011-10-30 08:46:00 -06:00
|
|
|
|
|
|
|
if (can_castle(WHITE_OOO))
|
2019-01-01 06:10:26 -07:00
|
|
|
ss << (chess960 ? char('A' + file_of(castling_rook_square(WHITE_OOO))) : 'Q');
|
2010-07-24 10:59:18 -06:00
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
if (can_castle(BLACK_OO))
|
2019-01-01 06:10:26 -07:00
|
|
|
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OO ))) : 'k');
|
2010-07-24 10:59:18 -06:00
|
|
|
|
2011-10-30 08:46:00 -06:00
|
|
|
if (can_castle(BLACK_OOO))
|
2019-01-01 06:10:26 -07:00
|
|
|
ss << (chess960 ? char('a' + file_of(castling_rook_square(BLACK_OOO))) : 'q');
|
2010-07-24 10:59:18 -06:00
|
|
|
|
2018-12-11 05:47:56 -07:00
|
|
|
if (!can_castle(ANY_CASTLING))
|
2012-11-11 05:18:10 -07:00
|
|
|
ss << '-';
|
2011-07-03 04:32:34 -06:00
|
|
|
|
2014-12-14 01:31:13 -07:00
|
|
|
ss << (ep_square() == SQ_NONE ? " - " : " " + UCI::square(ep_square()) + " ")
|
2014-04-27 03:40:44 -06:00
|
|
|
<< st->rule50 << " " << 1 + (gamePly - (sideToMove == BLACK)) / 2;
|
2008-09-29 11:02:48 -06:00
|
|
|
|
2012-11-11 05:18:10 -07:00
|
|
|
return ss.str();
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-09-12 00:47:19 -06:00
|
|
|
/// Position::slider_blockers() returns a bitboard of all the pieces (both colors)
|
|
|
|
/// that are blocking attacks on the square 's' from 'sliders'. A piece blocks a
|
|
|
|
/// slider if removing that piece from the board would result in a position where
|
|
|
|
/// square 's' is attacked. For example, a king-attack blocking piece can be either
|
|
|
|
/// a pinned or a discovered check piece, according if its color is the opposite
|
2016-09-19 00:21:41 -06:00
|
|
|
/// or the same of the color of the slider.
|
2014-01-11 02:58:09 -07:00
|
|
|
|
2016-09-12 00:47:19 -06:00
|
|
|
Bitboard Position::slider_blockers(Bitboard sliders, Square s, Bitboard& pinners) const {
|
2013-06-23 00:08:16 -06:00
|
|
|
|
2018-02-26 17:18:33 -07:00
|
|
|
Bitboard blockers = 0;
|
2016-09-19 00:21:41 -06:00
|
|
|
pinners = 0;
|
2013-06-23 00:08:16 -06:00
|
|
|
|
Change pinning logic in Static Exchange Evaluation (SEE)
This changes 2 parts with regards to static exchange evaluation.
Currently, we do not allow pinned pieces to recapture if *all* opponent
pinners are still in their starting squares. This changes that to having
a less strict requirement, checking if *any* pinners are still in their
starting square. This makes our SEE give more respect to the pinning
side with regards to exchanges, which makes sense because it helps our
search explore more tactical options.
Furthermore, we change the logic for saving pinners into our state
variable when computing slider_blockers. We will include double pinners,
where two sliders may be looking at the same blocker, a similar concept
to our mobility calculation for sliders in our evaluation section.
Interestingly, I think SEE is the only place where the pinners bitboard
is actually used, so as far as I know there are no other side effects
to this change.
An example and some insights:
White Bf2, Kg1
Black Qe3, Bc5
The move Qg3 will be given the correct value of 0. (Previously < 0)
The move Qd4 will be incorrectly given a value of 0. (Previously < 0)
It seems the tradeoff in search is worth it. Qd4 will likely be pruned
soon by something like probcut anyway, while Qg3 could help us spot
tactics at an earlier depth.
STC:
LLR: 2.96 (-2.94,2.94) [0.50,4.50]
Total: 62162 W: 13879 L: 13408 D: 34875
http://tests.stockfishchess.org/tests/view/5c4ba1a70ebc593af5d49c55
LTC: (Thanks to @alayant)
LLR: 3.40 (-2.94,2.94) [0.00,3.50]
Total: 140285 W: 23416 L: 22825 D: 94044
http://tests.stockfishchess.org/tests/view/5c4bcfba0ebc593af5d49ea8
Bench: 3937213
2019-01-24 23:37:03 -07:00
|
|
|
// Snipers are sliders that attack 's' when a piece and other snipers are removed
|
2020-05-28 09:48:31 -06:00
|
|
|
Bitboard snipers = ( (attacks_bb< ROOK>(s) & pieces(QUEEN, ROOK))
|
|
|
|
| (attacks_bb<BISHOP>(s) & pieces(QUEEN, BISHOP))) & sliders;
|
2019-05-02 11:36:25 -06:00
|
|
|
Bitboard occupancy = pieces() ^ snipers;
|
2008-10-23 04:59:20 -06:00
|
|
|
|
2016-09-19 00:21:41 -06:00
|
|
|
while (snipers)
|
2009-03-04 04:04:28 -07:00
|
|
|
{
|
2021-03-13 09:40:07 -07:00
|
|
|
Square sniperSq = pop_lsb(snipers);
|
Change pinning logic in Static Exchange Evaluation (SEE)
This changes 2 parts with regards to static exchange evaluation.
Currently, we do not allow pinned pieces to recapture if *all* opponent
pinners are still in their starting squares. This changes that to having
a less strict requirement, checking if *any* pinners are still in their
starting square. This makes our SEE give more respect to the pinning
side with regards to exchanges, which makes sense because it helps our
search explore more tactical options.
Furthermore, we change the logic for saving pinners into our state
variable when computing slider_blockers. We will include double pinners,
where two sliders may be looking at the same blocker, a similar concept
to our mobility calculation for sliders in our evaluation section.
Interestingly, I think SEE is the only place where the pinners bitboard
is actually used, so as far as I know there are no other side effects
to this change.
An example and some insights:
White Bf2, Kg1
Black Qe3, Bc5
The move Qg3 will be given the correct value of 0. (Previously < 0)
The move Qd4 will be incorrectly given a value of 0. (Previously < 0)
It seems the tradeoff in search is worth it. Qd4 will likely be pruned
soon by something like probcut anyway, while Qg3 could help us spot
tactics at an earlier depth.
STC:
LLR: 2.96 (-2.94,2.94) [0.50,4.50]
Total: 62162 W: 13879 L: 13408 D: 34875
http://tests.stockfishchess.org/tests/view/5c4ba1a70ebc593af5d49c55
LTC: (Thanks to @alayant)
LLR: 3.40 (-2.94,2.94) [0.00,3.50]
Total: 140285 W: 23416 L: 22825 D: 94044
http://tests.stockfishchess.org/tests/view/5c4bcfba0ebc593af5d49ea8
Bench: 3937213
2019-01-24 23:37:03 -07:00
|
|
|
Bitboard b = between_bb(s, sniperSq) & occupancy;
|
2016-09-19 00:21:41 -06:00
|
|
|
|
2018-02-26 17:18:33 -07:00
|
|
|
if (b && !more_than_one(b))
|
2016-09-19 00:21:41 -06:00
|
|
|
{
|
2018-02-26 17:18:33 -07:00
|
|
|
blockers |= b;
|
2016-09-19 00:21:41 -06:00
|
|
|
if (b & pieces(color_of(piece_on(s))))
|
|
|
|
pinners |= sniperSq;
|
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
2018-02-26 17:18:33 -07:00
|
|
|
return blockers;
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2009-03-04 15:11:23 -07:00
|
|
|
|
2011-10-30 11:59:12 -06:00
|
|
|
/// Position::attackers_to() computes a bitboard of all pieces which attack a
|
2014-12-07 16:53:33 -07:00
|
|
|
/// given square. Slider attacks use the occupied bitboard to indicate occupancy.
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-12-07 16:53:33 -07:00
|
|
|
Bitboard Position::attackers_to(Square s, Bitboard occupied) const {
|
2011-05-22 03:57:06 -06:00
|
|
|
|
2020-05-28 09:48:31 -06:00
|
|
|
return (pawn_attacks_bb(BLACK, s) & pieces(WHITE, PAWN))
|
|
|
|
| (pawn_attacks_bb(WHITE, s) & pieces(BLACK, PAWN))
|
|
|
|
| (attacks_bb<KNIGHT>(s) & pieces(KNIGHT))
|
2017-06-11 15:31:15 -06:00
|
|
|
| (attacks_bb< ROOK>(s, occupied) & pieces( ROOK, QUEEN))
|
2014-12-07 16:53:33 -07:00
|
|
|
| (attacks_bb<BISHOP>(s, occupied) & pieces(BISHOP, QUEEN))
|
2020-05-28 09:48:31 -06:00
|
|
|
| (attacks_bb<KING>(s) & pieces(KING));
|
2011-05-22 03:57:06 -06:00
|
|
|
}
|
|
|
|
|
2011-10-30 11:16:28 -06:00
|
|
|
|
2013-09-28 06:43:50 -06:00
|
|
|
/// Position::legal() tests whether a pseudo-legal move is legal
|
2009-02-28 01:18:29 -07:00
|
|
|
|
2016-07-03 02:35:44 -06:00
|
|
|
bool Position::legal(Move m) const {
|
2008-10-23 14:51:26 -06:00
|
|
|
|
2011-10-03 02:56:49 -06:00
|
|
|
assert(is_ok(m));
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2012-01-12 11:31:18 -07:00
|
|
|
Color us = sideToMove;
|
2012-01-01 08:00:00 -07:00
|
|
|
Square from = from_sq(m);
|
2019-01-02 00:31:03 -07:00
|
|
|
Square to = to_sq(m);
|
2011-05-22 03:57:06 -06:00
|
|
|
|
2013-09-28 06:43:50 -06:00
|
|
|
assert(color_of(moved_piece(m)) == us);
|
2015-08-04 01:00:52 -06:00
|
|
|
assert(piece_on(square<KING>(us)) == make_piece(us, KING));
|
2011-05-22 03:57:06 -06:00
|
|
|
|
2021-06-04 04:20:27 -06:00
|
|
|
// En passant captures are a tricky special case. Because they are rather
|
|
|
|
// uncommon, we do it simply by testing whether the king is attacked after
|
|
|
|
// the move is made.
|
2021-01-09 08:46:06 -07:00
|
|
|
if (type_of(m) == EN_PASSANT)
|
2021-06-04 04:20:27 -06:00
|
|
|
{
|
|
|
|
Square ksq = square<KING>(us);
|
|
|
|
Square capsq = to - pawn_push(us);
|
|
|
|
Bitboard occupied = (pieces() ^ from ^ capsq) | to;
|
|
|
|
|
|
|
|
assert(to == ep_square());
|
|
|
|
assert(moved_piece(m) == make_piece(us, PAWN));
|
|
|
|
assert(piece_on(capsq) == make_piece(~us, PAWN));
|
|
|
|
assert(piece_on(to) == NO_PIECE);
|
|
|
|
|
|
|
|
return !(attacks_bb< ROOK>(ksq, occupied) & pieces(~us, QUEEN, ROOK))
|
|
|
|
&& !(attacks_bb<BISHOP>(ksq, occupied) & pieces(~us, QUEEN, BISHOP));
|
|
|
|
}
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2019-01-02 00:31:03 -07:00
|
|
|
// Castling moves generation does not check if the castling path is clear of
|
|
|
|
// enemy attacks, it is delayed at a later time: now!
|
|
|
|
if (type_of(m) == CASTLING)
|
|
|
|
{
|
|
|
|
// After castling, the rook and king final positions are the same in
|
|
|
|
// Chess960 as they would be in standard chess.
|
|
|
|
to = relative_square(us, to > from ? SQ_G1 : SQ_C1);
|
|
|
|
Direction step = to > from ? WEST : EAST;
|
|
|
|
|
|
|
|
for (Square s = to; s != from; s += step)
|
|
|
|
if (attackers_to(s) & pieces(~us))
|
|
|
|
return false;
|
|
|
|
|
2021-01-17 11:21:45 -07:00
|
|
|
// In case of Chess960, verify if the Rook blocks some checks
|
2019-01-02 00:31:03 -07:00
|
|
|
// For instance an enemy queen in SQ_A1 when castling rook is in SQ_B1.
|
2021-01-17 11:21:45 -07:00
|
|
|
return !chess960 || !(blockers_for_king(us) & to_sq(m));
|
2019-01-02 00:31:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the moving piece is a king, check whether the destination square is
|
|
|
|
// attacked by the opponent.
|
2011-10-02 01:33:40 -06:00
|
|
|
if (type_of(piece_on(from)) == KING)
|
Simplify King Evasion
Simplify away the removal of some illegal `KING`-evasion moves during move
generation. Verified for correctness by running perft on the following positions:
```
./stockfish
bench 16 1 6 default perft
Nodes searched: 71608931810
./stockfish
position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21
go perft 6
Nodes searched: 6136386434
```
Passed STC:
LLR: 2.94 (-2.94,2.94) {-1.00,0.20}
Total: 16072 W: 1473 L: 1349 D: 13250
Ptnml(0-2): 57, 1047, 5710, 1159, 63
https://tests.stockfishchess.org/tests/view/60629e7ef183b42957b423b1
Passed LTC:
LLR: 2.94 (-2.94,2.94) {-0.70,0.20}
Total: 59064 W: 2214 L: 2177 D: 54673
Ptnml(0-2): 26, 1944, 25556, 1979, 27
https://tests.stockfishchess.org/tests/view/6062dce4f183b42957b423de
closes https://github.com/official-stockfish/Stockfish/pull/3415
No functional change
2021-03-29 21:42:45 -06:00
|
|
|
return !(attackers_to(to, pieces() ^ from) & pieces(~us));
|
2008-08-31 23:59:13 -06:00
|
|
|
|
|
|
|
// 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.
|
2021-01-31 05:46:05 -07:00
|
|
|
return !(blockers_for_king(us) & from)
|
|
|
|
|| aligned(from, to, square<KING>(us));
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-09-28 06:43:50 -06:00
|
|
|
/// Position::pseudo_legal() takes a random move and tests whether the move is
|
|
|
|
/// pseudo legal. It is used to validate moves from TT that can be corrupted
|
2011-10-03 02:56:49 -06:00
|
|
|
/// due to SMP concurrent access or hash position key aliasing.
|
2011-04-13 03:54:41 -06:00
|
|
|
|
2013-09-28 06:43:50 -06:00
|
|
|
bool Position::pseudo_legal(const Move m) const {
|
2011-04-13 03:54:41 -06:00
|
|
|
|
|
|
|
Color us = sideToMove;
|
2012-01-01 08:00:00 -07:00
|
|
|
Square from = from_sq(m);
|
|
|
|
Square to = to_sq(m);
|
2013-09-28 06:43:50 -06:00
|
|
|
Piece pc = moved_piece(m);
|
2011-04-13 03:54:41 -06:00
|
|
|
|
|
|
|
// Use a slower but simpler function for uncommon cases
|
Avoid more expensive legality check
speedup of the code, enough to pass STC, failed LTC.
Passed STC:
LLR: 2.93 (-2.94,2.94) {-0.25,1.25}
Total: 68928 W: 6334 L: 6122 D: 56472
Ptnml(0-2): 233, 4701, 24369, 4943, 218
https://tests.stockfishchess.org/tests/view/6002747f6019e097de3ef8dc
Failed LTC:
LLR: -2.96 (-2.94,2.94) {0.25,1.25}
Total: 44560 W: 1702 L: 1675 D: 41183
Ptnml(0-2): 25, 1383, 19438, 1408, 26
https://tests.stockfishchess.org/tests/view/6002a4836019e097de3ef8e3
About 1% speedup:
Result of 50 runs
==================
base (...kfish.master) = 2237500 +/- 7428
test (...ckfish.patch) = 2267003 +/- 7017
diff = +29503 +/- 4774
speedup = +0.0132
P(speedup > 0) = 1.0000
closes https://github.com/official-stockfish/Stockfish/pull/3304
No functional change.
2021-01-15 21:44:11 -07:00
|
|
|
// yet we skip the legality check of MoveList<LEGAL>().
|
2012-06-24 03:08:16 -06:00
|
|
|
if (type_of(m) != NORMAL)
|
Avoid more expensive legality check
speedup of the code, enough to pass STC, failed LTC.
Passed STC:
LLR: 2.93 (-2.94,2.94) {-0.25,1.25}
Total: 68928 W: 6334 L: 6122 D: 56472
Ptnml(0-2): 233, 4701, 24369, 4943, 218
https://tests.stockfishchess.org/tests/view/6002747f6019e097de3ef8dc
Failed LTC:
LLR: -2.96 (-2.94,2.94) {0.25,1.25}
Total: 44560 W: 1702 L: 1675 D: 41183
Ptnml(0-2): 25, 1383, 19438, 1408, 26
https://tests.stockfishchess.org/tests/view/6002a4836019e097de3ef8e3
About 1% speedup:
Result of 50 runs
==================
base (...kfish.master) = 2237500 +/- 7428
test (...ckfish.patch) = 2267003 +/- 7017
diff = +29503 +/- 4774
speedup = +0.0132
P(speedup > 0) = 1.0000
closes https://github.com/official-stockfish/Stockfish/pull/3304
No functional change.
2021-01-15 21:44:11 -07:00
|
|
|
return checkers() ? MoveList< EVASIONS>(*this).contains(m)
|
|
|
|
: MoveList<NON_EVASIONS>(*this).contains(m);
|
2011-04-13 03:54:41 -06:00
|
|
|
|
2011-05-22 01:35:14 -06:00
|
|
|
// Is not a promotion, so promotion piece must be empty
|
2015-02-08 05:21:50 -07:00
|
|
|
if (promotion_type(m) - KNIGHT != NO_PIECE_TYPE)
|
2011-05-22 01:35:14 -06:00
|
|
|
return false;
|
|
|
|
|
2013-12-04 23:18:12 -07:00
|
|
|
// If the 'from' square is not occupied by a piece belonging to the side to
|
2011-04-13 03:54:41 -06:00
|
|
|
// move, the move is obviously not legal.
|
2011-12-27 11:26:27 -07:00
|
|
|
if (pc == NO_PIECE || color_of(pc) != us)
|
2011-04-13 03:54:41 -06:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// The destination square cannot be occupied by a friendly piece
|
2013-07-22 04:47:59 -06:00
|
|
|
if (pieces(us) & to)
|
2011-04-13 03:54:41 -06:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// Handle the special case of a pawn move
|
2011-10-02 01:33:40 -06:00
|
|
|
if (type_of(pc) == PAWN)
|
2011-04-13 03:54:41 -06:00
|
|
|
{
|
|
|
|
// We have already handled promotion moves, so destination
|
2013-12-02 15:47:38 -07:00
|
|
|
// cannot be on the 8th/1st rank.
|
2019-03-31 03:47:36 -06:00
|
|
|
if ((Rank8BB | Rank1BB) & to)
|
2011-04-13 03:54:41 -06:00
|
|
|
return false;
|
|
|
|
|
2020-05-28 09:48:31 -06:00
|
|
|
if ( !(pawn_attacks_bb(us, from) & pieces(~us) & to) // Not a capture
|
2014-03-10 01:38:23 -06:00
|
|
|
&& !((from + pawn_push(us) == to) && empty(to)) // Not a single push
|
|
|
|
&& !( (from + 2 * pawn_push(us) == to) // Not a double push
|
2020-05-23 05:26:13 -06:00
|
|
|
&& (relative_rank(us, from) == RANK_2)
|
2014-03-10 01:38:23 -06:00
|
|
|
&& empty(to)
|
|
|
|
&& empty(to - pawn_push(us))))
|
2011-05-22 02:35:34 -06:00
|
|
|
return false;
|
2011-04-13 03:54:41 -06:00
|
|
|
}
|
2020-05-28 09:48:31 -06:00
|
|
|
else if (!(attacks_bb(type_of(pc), from, pieces()) & to))
|
2011-04-13 03:54:41 -06:00
|
|
|
return false;
|
|
|
|
|
2011-10-22 07:53:35 -06:00
|
|
|
// Evasions generator already takes care to avoid some kind of illegal moves
|
2014-03-10 01:38:23 -06:00
|
|
|
// and legal() relies on this. We therefore have to take care that the same
|
|
|
|
// kind of moves are filtered out here.
|
2012-12-25 09:59:35 -07:00
|
|
|
if (checkers())
|
2011-05-22 03:57:06 -06:00
|
|
|
{
|
2012-02-26 09:34:24 -07:00
|
|
|
if (type_of(pc) != KING)
|
2011-05-22 03:57:06 -06:00
|
|
|
{
|
2012-12-25 03:22:55 -07:00
|
|
|
// Double check? In this case a king move is required
|
|
|
|
if (more_than_one(checkers()))
|
2011-05-22 03:57:06 -06:00
|
|
|
return false;
|
|
|
|
|
Change definition of between_bb()
We remark that in current master, most of our use cases for between_bb() can be
optimized if the second parameter of the function is added to the segment. So we
change the definition of between_bb(s1, s2) such that it excludes s1 but includes s2.
We also use a precomputed array for between_bb() for another small speed gain
(see https://tests.stockfishchess.org/tests/view/604d09f72433018de7a389fb).
Passed STC:
LLR: 2.96 (-2.94,2.94) {-0.25,1.25}
Total: 18736 W: 1746 L: 1607 D: 15383
Ptnml(0-2): 61, 1226, 6644, 1387, 50
https://tests.stockfishchess.org/tests/view/60428c84ddcba5f0627bb6e4
Yellow LTC:
LTC:
LLR: -3.00 (-2.94,2.94) {0.25,1.25}
Total: 39144 W: 1431 L: 1413 D: 36300
Ptnml(0-2): 13, 1176, 17184, 1178, 21
https://tests.stockfishchess.org/tests/view/605128702433018de7a38ca1
Closes https://github.com/official-stockfish/Stockfish/pull/3397
---------
Verified for correctness by running perft on the following position:
./stockfish
position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21
go perft 6
Nodes searched: 6136386434
--------
No functional change
2021-03-15 13:06:42 -06:00
|
|
|
// Our move must be a blocking interposition or a capture of the checking piece
|
|
|
|
if (!(between_bb(square<KING>(us), lsb(checkers())) & to))
|
2011-05-22 03:57:06 -06:00
|
|
|
return false;
|
|
|
|
}
|
2013-12-04 23:18:12 -07:00
|
|
|
// In case of king moves under check we have to remove king so as to catch
|
|
|
|
// invalid moves like b1a1 when opposite queen is on c1.
|
2012-03-18 04:10:12 -06:00
|
|
|
else if (attackers_to(to, pieces() ^ from) & pieces(~us))
|
2012-02-26 09:34:24 -07:00
|
|
|
return false;
|
2011-05-22 03:57:06 -06:00
|
|
|
}
|
|
|
|
|
2011-05-23 04:04:59 -06:00
|
|
|
return true;
|
2011-04-13 03:54:41 -06:00
|
|
|
}
|
|
|
|
|
2009-11-05 04:52:40 -07:00
|
|
|
|
2014-01-06 22:25:35 -07:00
|
|
|
/// Position::gives_check() tests whether a pseudo-legal move gives a check
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2016-07-03 02:35:44 -06:00
|
|
|
bool Position::gives_check(Move m) const {
|
2009-11-09 13:02:07 -07:00
|
|
|
|
2011-10-03 02:56:49 -06:00
|
|
|
assert(is_ok(m));
|
2013-09-28 06:43:50 -06:00
|
|
|
assert(color_of(moved_piece(m)) == sideToMove);
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2012-01-01 08:00:00 -07:00
|
|
|
Square from = from_sq(m);
|
|
|
|
Square to = to_sq(m);
|
2008-10-23 14:51:26 -06:00
|
|
|
|
2013-12-04 23:18:12 -07:00
|
|
|
// Is there a direct check?
|
2020-02-05 07:18:24 -07:00
|
|
|
if (check_squares(type_of(piece_on(from))) & to)
|
2012-11-11 03:04:58 -07:00
|
|
|
return true;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2013-12-04 23:18:12 -07:00
|
|
|
// Is there a discovered check?
|
2020-02-05 07:18:24 -07:00
|
|
|
if ( (blockers_for_king(~sideToMove) & from)
|
2016-08-27 02:05:42 -06:00
|
|
|
&& !aligned(from, to, square<KING>(~sideToMove)))
|
2013-12-01 02:07:16 -07:00
|
|
|
return true;
|
2008-10-26 06:23:27 -06:00
|
|
|
|
2012-11-17 04:57:58 -07:00
|
|
|
switch (type_of(m))
|
|
|
|
{
|
2014-03-11 15:58:08 -06:00
|
|
|
case NORMAL:
|
|
|
|
return false;
|
|
|
|
|
2012-11-17 04:57:58 -07:00
|
|
|
case PROMOTION:
|
2017-04-28 21:33:30 -06:00
|
|
|
return attacks_bb(promotion_type(m), to, pieces() ^ from) & square<KING>(~sideToMove);
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2021-06-04 04:20:27 -06:00
|
|
|
// En passant capture with check? We have already handled the case
|
|
|
|
// of direct checks and ordinary discovered check, so the only case we
|
|
|
|
// need to handle is the unusual case of a discovered check through
|
|
|
|
// the captured pawn.
|
2021-01-09 08:46:06 -07:00
|
|
|
case EN_PASSANT:
|
2021-06-04 04:20:27 -06:00
|
|
|
{
|
|
|
|
Square capsq = make_square(file_of(to), rank_of(from));
|
|
|
|
Bitboard b = (pieces() ^ from ^ capsq) | to;
|
2012-02-26 09:34:24 -07:00
|
|
|
|
2021-06-04 04:20:27 -06:00
|
|
|
return (attacks_bb< ROOK>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, ROOK))
|
|
|
|
| (attacks_bb<BISHOP>(square<KING>(~sideToMove), b) & pieces(sideToMove, QUEEN, BISHOP));
|
|
|
|
}
|
Clean Up Castling in gives_check
There is no need to add rto or kto on the Bitboard which represents the pieces.
STC:
LLR: 2.93 (-2.94,2.94) {-1.25,0.25}
Total: 57064 W: 5096 L: 5067 D: 46901
Ptnml(0-2): 202, 3862, 20355, 3931, 182
https://tests.stockfishchess.org/tests/view/6005ea2c6019e097de3efa55
LTC:
LLR: 2.92 (-2.94,2.94) {-0.75,0.25}
Total: 30088 W: 1094 L: 1052 D: 27942
Ptnml(0-2): 10, 882, 13217, 926, 9
https://tests.stockfishchess.org/tests/view/6006115a6019e097de3efa6e
closes https://github.com/official-stockfish/Stockfish/pull/3311
No functional change.
2021-01-18 13:03:43 -07:00
|
|
|
default: //CASTLING
|
2009-11-09 13:29:22 -07:00
|
|
|
{
|
Clean Up Castling in gives_check
There is no need to add rto or kto on the Bitboard which represents the pieces.
STC:
LLR: 2.93 (-2.94,2.94) {-1.25,0.25}
Total: 57064 W: 5096 L: 5067 D: 46901
Ptnml(0-2): 202, 3862, 20355, 3931, 182
https://tests.stockfishchess.org/tests/view/6005ea2c6019e097de3efa55
LTC:
LLR: 2.92 (-2.94,2.94) {-0.75,0.25}
Total: 30088 W: 1094 L: 1052 D: 27942
Ptnml(0-2): 10, 882, 13217, 926, 9
https://tests.stockfishchess.org/tests/view/6006115a6019e097de3efa6e
closes https://github.com/official-stockfish/Stockfish/pull/3311
No functional change.
2021-01-18 13:03:43 -07:00
|
|
|
// Castling is encoded as 'king captures the rook'
|
|
|
|
Square ksq = square<KING>(~sideToMove);
|
|
|
|
Square rto = relative_square(sideToMove, to > from ? SQ_F1 : SQ_D1);
|
2008-09-23 16:32:53 -06:00
|
|
|
|
Clean Up Castling in gives_check
There is no need to add rto or kto on the Bitboard which represents the pieces.
STC:
LLR: 2.93 (-2.94,2.94) {-1.25,0.25}
Total: 57064 W: 5096 L: 5067 D: 46901
Ptnml(0-2): 202, 3862, 20355, 3931, 182
https://tests.stockfishchess.org/tests/view/6005ea2c6019e097de3efa55
LTC:
LLR: 2.92 (-2.94,2.94) {-0.75,0.25}
Total: 30088 W: 1094 L: 1052 D: 27942
Ptnml(0-2): 10, 882, 13217, 926, 9
https://tests.stockfishchess.org/tests/view/6006115a6019e097de3efa6e
closes https://github.com/official-stockfish/Stockfish/pull/3311
No functional change.
2021-01-18 13:03:43 -07:00
|
|
|
return (attacks_bb<ROOK>(rto) & ksq)
|
|
|
|
&& (attacks_bb<ROOK>(rto, pieces() ^ from ^ to) & ksq);
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
2012-11-17 04:57:58 -07:00
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-22 12:16:21 -07:00
|
|
|
/// Position::do_move() makes a move, and saves all information necessary
|
2011-04-26 03:19:57 -06:00
|
|
|
/// to a StateInfo object. The move is assumed to be legal. Pseudo-legal
|
|
|
|
/// moves should be filtered out before this function is called.
|
2008-08-31 23:59:13 -06:00
|
|
|
|
Compute checkers from scratch
This micro-optimization only complicates the code and provides no benefit.
Removing it is even a speedup on my machine (i7-3770k, linux, gcc 4.9.1):
stat test master diff
mean 2,403,118 2,390,904 12,214
stdev 12,043 10,620 3,677
speedup 0.51%
P(speedup>0) 100.0%
No functional change.
2015-02-15 00:49:20 -07:00
|
|
|
void Position::do_move(Move m, StateInfo& newSt, bool givesCheck) {
|
2009-03-02 08:20:00 -07:00
|
|
|
|
2011-10-03 02:56:49 -06:00
|
|
|
assert(is_ok(m));
|
2011-01-09 03:22:59 -07:00
|
|
|
assert(&newSt != st);
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2017-06-23 23:15:46 -06:00
|
|
|
thisThread->nodes.fetch_add(1, std::memory_order_relaxed);
|
2015-02-07 11:34:24 -07:00
|
|
|
Key k = st->key ^ Zobrist::side;
|
2009-08-17 01:57:09 -06:00
|
|
|
|
2013-12-04 23:18:12 -07:00
|
|
|
// Copy some fields of the old state to our new StateInfo object except the
|
|
|
|
// ones which are going to be recalculated from scratch anyway and then switch
|
|
|
|
// our state pointer to point to the new (ready to be updated) state.
|
2015-02-07 11:34:24 -07:00
|
|
|
std::memcpy(&newSt, st, offsetof(StateInfo, key));
|
2009-02-28 01:18:29 -07:00
|
|
|
newSt.previous = st;
|
2009-02-22 12:16:21 -07:00
|
|
|
st = &newSt;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2013-12-04 23:18:12 -07:00
|
|
|
// Increment ply counters. In particular, rule50 will be reset to zero later on
|
2013-02-16 04:42:22 -07:00
|
|
|
// in case of a capture or a pawn move.
|
2013-10-02 22:01:38 -06:00
|
|
|
++gamePly;
|
|
|
|
++st->rule50;
|
|
|
|
++st->pliesFromNull;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
// Used by NNUE
|
Reduce the number of accumulator states
Reduce from 3 to 2. Make the intent of the states clearer.
STC: https://tests.stockfishchess.org/tests/view/60c50111457376eb8bcaad03
LLR: 2.95 (-2.94,2.94) <-2.50,0.50>
Total: 61888 W: 5007 L: 4944 D: 51937
Ptnml(0-2): 164, 3947, 22649, 4030, 154
LTC: https://tests.stockfishchess.org/tests/view/60c52b1c457376eb8bcaad2c
LLR: 2.94 (-2.94,2.94) <-2.50,0.50>
Total: 20248 W: 688 L: 618 D: 18942
Ptnml(0-2): 7, 551, 8946, 605, 15
closes https://github.com/official-stockfish/Stockfish/pull/3548
No functional change.
2021-06-12 12:45:14 -06:00
|
|
|
st->accumulator.computed[WHITE] = false;
|
|
|
|
st->accumulator.computed[BLACK] = false;
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
auto& dp = st->dirtyPiece;
|
|
|
|
dp.dirty_num = 1;
|
|
|
|
|
2012-01-12 11:31:18 -07:00
|
|
|
Color us = sideToMove;
|
|
|
|
Color them = ~us;
|
2012-01-01 08:00:00 -07:00
|
|
|
Square from = from_sq(m);
|
|
|
|
Square to = to_sq(m);
|
2016-09-03 10:14:01 -06:00
|
|
|
Piece pc = piece_on(from);
|
2021-01-09 08:46:06 -07:00
|
|
|
Piece captured = type_of(m) == EN_PASSANT ? make_piece(them, PAWN) : piece_on(to);
|
2009-08-16 07:49:15 -06:00
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(color_of(pc) == us);
|
|
|
|
assert(captured == NO_PIECE || color_of(captured) == (type_of(m) != CASTLING ? them : us));
|
|
|
|
assert(type_of(captured) != KING);
|
2009-02-22 10:31:24 -07:00
|
|
|
|
2013-12-01 02:25:10 -07:00
|
|
|
if (type_of(m) == CASTLING)
|
2013-01-27 10:48:27 -07:00
|
|
|
{
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(pc == make_piece(us, KING));
|
|
|
|
assert(captured == make_piece(us, ROOK));
|
2013-01-27 10:48:27 -07:00
|
|
|
|
2014-03-13 05:53:03 -06:00
|
|
|
Square rfrom, rto;
|
2015-02-08 05:21:50 -07:00
|
|
|
do_castling<true>(us, from, to, rfrom, rto);
|
2013-01-27 10:48:27 -07:00
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
k ^= Zobrist::psq[captured][rfrom] ^ Zobrist::psq[captured][rto];
|
|
|
|
captured = NO_PIECE;
|
2013-01-27 10:48:27 -07:00
|
|
|
}
|
|
|
|
|
2013-09-28 06:43:50 -06:00
|
|
|
if (captured)
|
2011-10-30 03:57:00 -06:00
|
|
|
{
|
|
|
|
Square capsq = to;
|
|
|
|
|
2011-10-30 04:26:06 -06:00
|
|
|
// If the captured piece is a pawn, update pawn hash key, otherwise
|
2011-10-30 03:57:00 -06:00
|
|
|
// update non-pawn material.
|
2016-09-03 10:14:01 -06:00
|
|
|
if (type_of(captured) == PAWN)
|
2011-10-30 03:57:00 -06:00
|
|
|
{
|
2021-01-09 08:46:06 -07:00
|
|
|
if (type_of(m) == EN_PASSANT)
|
2011-10-30 03:57:00 -06:00
|
|
|
{
|
2015-02-07 11:34:24 -07:00
|
|
|
capsq -= pawn_push(us);
|
2011-10-30 03:57:00 -06:00
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(pc == make_piece(us, PAWN));
|
2011-10-30 03:57:00 -06:00
|
|
|
assert(to == st->epSquare);
|
|
|
|
assert(relative_rank(us, to) == RANK_6);
|
2011-12-27 11:26:27 -07:00
|
|
|
assert(piece_on(to) == NO_PIECE);
|
2011-10-30 03:57:00 -06:00
|
|
|
assert(piece_on(capsq) == make_piece(them, PAWN));
|
|
|
|
}
|
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
st->pawnKey ^= Zobrist::psq[captured][capsq];
|
2011-10-30 03:57:00 -06:00
|
|
|
}
|
|
|
|
else
|
2014-12-07 16:53:33 -07:00
|
|
|
st->nonPawnMaterial[them] -= PieceValue[MG][captured];
|
2011-10-30 03:57:00 -06:00
|
|
|
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
if (Eval::useNNUE)
|
|
|
|
{
|
Remove EvalList
This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code.
The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective.
If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object.
If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE).
An STC test has confirmed a small speedup:
https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 87704 W: 9763 L: 9500 D: 68441
Ptnml(0-2): 426, 6950, 28845, 7197, 434
closes https://github.com/official-stockfish/Stockfish/pull/3068
No functional change
2020-08-23 18:29:38 -06:00
|
|
|
dp.dirty_num = 2; // 1 piece moved, 1 piece captured
|
|
|
|
dp.piece[1] = captured;
|
|
|
|
dp.from[1] = capsq;
|
|
|
|
dp.to[1] = SQ_NONE;
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
}
|
|
|
|
|
2013-08-01 07:58:38 -06:00
|
|
|
// Update board and piece lists
|
2019-12-21 15:36:29 -07:00
|
|
|
remove_piece(capsq);
|
|
|
|
|
2021-01-09 08:46:06 -07:00
|
|
|
if (type_of(m) == EN_PASSANT)
|
2019-12-21 15:36:29 -07:00
|
|
|
board[capsq] = NO_PIECE;
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2013-01-27 02:15:59 -07:00
|
|
|
// Update material hash key and prefetch access to materialTable
|
2016-09-03 10:14:01 -06:00
|
|
|
k ^= Zobrist::psq[captured][capsq];
|
|
|
|
st->materialKey ^= Zobrist::psq[captured][pieceCount[captured]];
|
2015-02-07 11:13:41 -07:00
|
|
|
prefetch(thisThread->materialTable[st->materialKey]);
|
2011-10-30 03:57:00 -06:00
|
|
|
|
|
|
|
// Reset rule 50 counter
|
|
|
|
st->rule50 = 0;
|
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2009-08-17 01:57:09 -06:00
|
|
|
// Update hash key
|
2016-09-03 10:14:01 -06:00
|
|
|
k ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
2009-02-12 04:12:46 -07:00
|
|
|
|
2009-08-17 01:57:09 -06:00
|
|
|
// Reset en passant square
|
|
|
|
if (st->epSquare != SQ_NONE)
|
|
|
|
{
|
2012-08-20 11:09:57 -06:00
|
|
|
k ^= Zobrist::enpassant[file_of(st->epSquare)];
|
2009-08-17 01:57:09 -06:00
|
|
|
st->epSquare = SQ_NONE;
|
|
|
|
}
|
2009-08-09 08:53:51 -06:00
|
|
|
|
2014-03-08 07:08:55 -07:00
|
|
|
// Update castling rights if needed
|
|
|
|
if (st->castlingRights && (castlingRightsMask[from] | castlingRightsMask[to]))
|
2009-08-17 01:57:09 -06:00
|
|
|
{
|
Remove old zobrist trick for castling rights
Removes an 8 year old micro optimization aimed at 32-bit architectures
because back then doing an xor of a Key could not be done in one instruction.
See original commit here 821e1c7
STC https://tests.stockfishchess.org/tests/view/5ef5833dde213bf647527d0c
LLR: 2.94 (-2.94,2.94) {-1.50,0.50}
Total: 162648 W: 31053 L: 31097 D: 100498
Ptnml(0-2): 2841, 18966, 37715, 19000, 2802
LTC https://tests.stockfishchess.org/tests/view/5ef7b1bbf993893290cc1489
LLR: 2.93 (-2.94,2.94) {-1.50,0.50}
Total: 62360 W: 7617 L: 7586 D: 47157
Ptnml(0-2): 423, 5662, 18994, 5663, 438
closes https://github.com/official-stockfish/Stockfish/pull/2775
bench: 4591425
2020-06-25 23:08:17 -06:00
|
|
|
k ^= Zobrist::castling[st->castlingRights];
|
|
|
|
st->castlingRights &= ~(castlingRightsMask[from] | castlingRightsMask[to]);
|
|
|
|
k ^= Zobrist::castling[st->castlingRights];
|
2009-08-17 01:57:09 -06:00
|
|
|
}
|
2009-08-09 08:53:51 -06:00
|
|
|
|
2013-12-01 02:25:10 -07:00
|
|
|
// Move the piece. The tricky Chess960 castling is handled earlier
|
|
|
|
if (type_of(m) != CASTLING)
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
{
|
|
|
|
if (Eval::useNNUE)
|
|
|
|
{
|
Remove EvalList
This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code.
The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective.
If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object.
If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE).
An STC test has confirmed a small speedup:
https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 87704 W: 9763 L: 9500 D: 68441
Ptnml(0-2): 426, 6950, 28845, 7197, 434
closes https://github.com/official-stockfish/Stockfish/pull/3068
No functional change
2020-08-23 18:29:38 -06:00
|
|
|
dp.piece[0] = pc;
|
|
|
|
dp.from[0] = from;
|
|
|
|
dp.to[0] = to;
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
}
|
|
|
|
|
2019-12-21 15:36:29 -07:00
|
|
|
move_piece(from, to);
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
}
|
2009-08-09 08:53:51 -06:00
|
|
|
|
2011-10-30 04:26:06 -06:00
|
|
|
// If the moving piece is a pawn do some special extra work
|
2016-09-03 10:14:01 -06:00
|
|
|
if (type_of(pc) == PAWN)
|
2009-08-17 01:57:09 -06:00
|
|
|
{
|
2021-01-09 08:46:06 -07:00
|
|
|
// Set en passant square if the moved pawn can be captured
|
2012-02-26 09:34:24 -07:00
|
|
|
if ( (int(to) ^ int(from)) == 16
|
2020-05-28 09:48:31 -06:00
|
|
|
&& (pawn_attacks_bb(us, to - pawn_push(us)) & pieces(them, PAWN)))
|
2009-08-17 01:57:09 -06:00
|
|
|
{
|
2017-12-04 09:52:31 -07:00
|
|
|
st->epSquare = to - pawn_push(us);
|
2012-08-20 11:09:57 -06:00
|
|
|
k ^= Zobrist::enpassant[file_of(st->epSquare)];
|
2009-08-17 01:57:09 -06:00
|
|
|
}
|
2010-05-31 05:01:33 -06:00
|
|
|
|
2014-04-26 16:25:18 -06:00
|
|
|
else if (type_of(m) == PROMOTION)
|
2010-05-31 05:01:33 -06:00
|
|
|
{
|
2016-09-03 10:14:01 -06:00
|
|
|
Piece promotion = make_piece(us, promotion_type(m));
|
2010-05-31 05:01:33 -06:00
|
|
|
|
2011-10-30 04:26:06 -06:00
|
|
|
assert(relative_rank(us, to) == RANK_8);
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(type_of(promotion) >= KNIGHT && type_of(promotion) <= QUEEN);
|
2010-05-31 05:01:33 -06:00
|
|
|
|
2019-12-21 15:36:29 -07:00
|
|
|
remove_piece(to);
|
2016-09-03 10:14:01 -06:00
|
|
|
put_piece(promotion, to);
|
2010-05-31 05:01:33 -06:00
|
|
|
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
if (Eval::useNNUE)
|
|
|
|
{
|
Remove EvalList
This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code.
The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective.
If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object.
If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE).
An STC test has confirmed a small speedup:
https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 87704 W: 9763 L: 9500 D: 68441
Ptnml(0-2): 426, 6950, 28845, 7197, 434
closes https://github.com/official-stockfish/Stockfish/pull/3068
No functional change
2020-08-23 18:29:38 -06:00
|
|
|
// Promoting pawn to SQ_NONE, promoted piece from SQ_NONE
|
|
|
|
dp.to[0] = SQ_NONE;
|
|
|
|
dp.piece[dp.dirty_num] = promotion;
|
|
|
|
dp.from[dp.dirty_num] = SQ_NONE;
|
|
|
|
dp.to[dp.dirty_num] = to;
|
|
|
|
dp.dirty_num++;
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
}
|
|
|
|
|
2011-10-30 04:26:06 -06:00
|
|
|
// Update hash keys
|
2016-09-03 10:14:01 -06:00
|
|
|
k ^= Zobrist::psq[pc][to] ^ Zobrist::psq[promotion][to];
|
|
|
|
st->pawnKey ^= Zobrist::psq[pc][to];
|
|
|
|
st->materialKey ^= Zobrist::psq[promotion][pieceCount[promotion]-1]
|
|
|
|
^ Zobrist::psq[pc][pieceCount[pc]];
|
2010-05-31 05:01:33 -06:00
|
|
|
|
|
|
|
// Update material
|
2014-12-07 16:53:33 -07:00
|
|
|
st->nonPawnMaterial[us] += PieceValue[MG][promotion];
|
2010-05-31 05:01:33 -06:00
|
|
|
}
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2020-01-09 12:49:13 -07:00
|
|
|
// Update pawn hash key
|
2016-09-03 10:14:01 -06:00
|
|
|
st->pawnKey ^= Zobrist::psq[pc][from] ^ Zobrist::psq[pc][to];
|
2011-10-30 04:26:06 -06:00
|
|
|
|
|
|
|
// Reset rule 50 draw counter
|
|
|
|
st->rule50 = 0;
|
2009-08-17 01:57:09 -06:00
|
|
|
}
|
2009-08-16 07:49:15 -06:00
|
|
|
|
2009-11-08 09:56:41 -07:00
|
|
|
// Set capture piece
|
2016-09-03 10:14:01 -06:00
|
|
|
st->capturedPiece = captured;
|
2009-11-08 09:56:41 -07:00
|
|
|
|
2009-08-17 01:57:09 -06:00
|
|
|
// Update the key with the final value
|
2011-12-25 03:50:59 -07:00
|
|
|
st->key = k;
|
2009-08-17 01:57:09 -06:00
|
|
|
|
2015-02-15 04:20:47 -07:00
|
|
|
// Calculate checkers bitboard (if move gives check)
|
2015-08-04 01:00:52 -06:00
|
|
|
st->checkersBB = givesCheck ? attackers_to(square<KING>(them)) & pieces(us) : 0;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2012-01-12 11:31:18 -07:00
|
|
|
sideToMove = ~sideToMove;
|
2009-08-20 09:30:34 -06:00
|
|
|
|
2016-08-27 02:05:42 -06:00
|
|
|
// Update king attacks used for fast check detection
|
|
|
|
set_check_info(st);
|
2016-07-03 02:35:44 -06:00
|
|
|
|
2019-05-15 02:22:21 -06:00
|
|
|
// Calculate the repetition info. It is the ply distance from the previous
|
|
|
|
// occurrence of the same position, negative in the 3-fold case, or zero
|
|
|
|
// if the position was not repeated.
|
|
|
|
st->repetition = 0;
|
|
|
|
int end = std::min(st->rule50, st->pliesFromNull);
|
|
|
|
if (end >= 4)
|
|
|
|
{
|
|
|
|
StateInfo* stp = st->previous->previous;
|
2019-08-14 14:15:41 -06:00
|
|
|
for (int i = 4; i <= end; i += 2)
|
2019-05-15 02:22:21 -06:00
|
|
|
{
|
|
|
|
stp = stp->previous->previous;
|
|
|
|
if (stp->key == st->key)
|
|
|
|
{
|
|
|
|
st->repetition = stp->repetition ? -i : i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-16 16:56:25 -06:00
|
|
|
assert(pos_is_ok());
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
|
2009-02-12 04:43:14 -07:00
|
|
|
|
2011-10-30 04:26:06 -06:00
|
|
|
/// Position::undo_move() unmakes a move. When it returns, the position should
|
|
|
|
/// be restored to exactly the same state as before the move was made.
|
|
|
|
|
|
|
|
void Position::undo_move(Move m) {
|
|
|
|
|
|
|
|
assert(is_ok(m));
|
|
|
|
|
2012-01-12 11:31:18 -07:00
|
|
|
sideToMove = ~sideToMove;
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2012-01-12 11:31:18 -07:00
|
|
|
Color us = sideToMove;
|
2012-01-01 08:00:00 -07:00
|
|
|
Square from = from_sq(m);
|
|
|
|
Square to = to_sq(m);
|
2016-09-03 10:14:01 -06:00
|
|
|
Piece pc = piece_on(to);
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2013-12-01 02:25:10 -07:00
|
|
|
assert(empty(from) || type_of(m) == CASTLING);
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(type_of(st->capturedPiece) != KING);
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2012-06-24 03:08:16 -06:00
|
|
|
if (type_of(m) == PROMOTION)
|
2011-10-30 04:26:06 -06:00
|
|
|
{
|
|
|
|
assert(relative_rank(us, to) == RANK_8);
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(type_of(pc) == promotion_type(m));
|
|
|
|
assert(type_of(pc) >= KNIGHT && type_of(pc) <= QUEEN);
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2019-12-21 15:36:29 -07:00
|
|
|
remove_piece(to);
|
2016-09-03 10:14:01 -06:00
|
|
|
pc = make_piece(us, PAWN);
|
|
|
|
put_piece(pc, to);
|
2011-10-30 04:26:06 -06:00
|
|
|
}
|
|
|
|
|
2013-12-01 02:25:10 -07:00
|
|
|
if (type_of(m) == CASTLING)
|
2013-01-27 10:48:27 -07:00
|
|
|
{
|
2014-03-13 05:53:03 -06:00
|
|
|
Square rfrom, rto;
|
2015-02-08 05:21:50 -07:00
|
|
|
do_castling<false>(us, from, to, rfrom, rto);
|
2013-01-27 10:48:27 -07:00
|
|
|
}
|
|
|
|
else
|
2011-10-30 04:26:06 -06:00
|
|
|
{
|
2019-12-21 15:36:29 -07:00
|
|
|
move_piece(to, from); // Put the piece back at the source square
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
if (st->capturedPiece)
|
2011-10-30 04:26:06 -06:00
|
|
|
{
|
2014-03-15 04:21:47 -06:00
|
|
|
Square capsq = to;
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2021-01-09 08:46:06 -07:00
|
|
|
if (type_of(m) == EN_PASSANT)
|
2014-03-15 04:21:47 -06:00
|
|
|
{
|
|
|
|
capsq -= pawn_push(us);
|
2011-10-30 04:26:06 -06:00
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(type_of(pc) == PAWN);
|
2014-03-15 04:21:47 -06:00
|
|
|
assert(to == st->previous->epSquare);
|
|
|
|
assert(relative_rank(us, to) == RANK_6);
|
|
|
|
assert(piece_on(capsq) == NO_PIECE);
|
2016-09-03 10:14:01 -06:00
|
|
|
assert(st->capturedPiece == make_piece(~us, PAWN));
|
2014-03-15 04:21:47 -06:00
|
|
|
}
|
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
put_piece(st->capturedPiece, capsq); // Restore the captured piece
|
2014-03-15 04:21:47 -06:00
|
|
|
}
|
2011-10-30 04:26:06 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Finally point our state pointer back to the previous state
|
|
|
|
st = st->previous;
|
2013-10-02 22:01:38 -06:00
|
|
|
--gamePly;
|
2011-10-30 04:26:06 -06:00
|
|
|
|
|
|
|
assert(pos_is_ok());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-12-01 02:25:10 -07:00
|
|
|
/// Position::do_castling() is a helper used to do/undo a castling move. This
|
2016-09-03 10:14:01 -06:00
|
|
|
/// is a bit tricky in Chess960 where from/to squares can overlap.
|
2014-03-13 05:53:03 -06:00
|
|
|
template<bool Do>
|
2015-02-08 05:21:50 -07:00
|
|
|
void Position::do_castling(Color us, Square from, Square& to, Square& rfrom, Square& rto) {
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-03-13 05:53:03 -06:00
|
|
|
bool kingSide = to > from;
|
|
|
|
rfrom = to; // Castling is encoded as "king captures friendly rook"
|
2015-02-08 05:21:50 -07:00
|
|
|
rto = relative_square(us, kingSide ? SQ_F1 : SQ_D1);
|
|
|
|
to = relative_square(us, kingSide ? SQ_G1 : SQ_C1);
|
2008-08-31 23:59:13 -06:00
|
|
|
|
Remove EvalList
This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code.
The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective.
If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object.
If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE).
An STC test has confirmed a small speedup:
https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 87704 W: 9763 L: 9500 D: 68441
Ptnml(0-2): 426, 6950, 28845, 7197, 434
closes https://github.com/official-stockfish/Stockfish/pull/3068
No functional change
2020-08-23 18:29:38 -06:00
|
|
|
if (Do && Eval::useNNUE)
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
{
|
|
|
|
auto& dp = st->dirtyPiece;
|
Remove EvalList
This patch removes the EvalList structure from the Position object and generally simplifies the interface between do_move() and the NNUE code.
The NNUE evaluation function first calculates the "accumulator". The accumulator consists of two halves: one for white's perspective, one for black's perspective.
If the "friendly king" has moved or the accumulator for the parent position is not available, the accumulator for this half has to be calculated from scratch. To do this, the NNUE node needs to know the positions and types of all non-king pieces and the position of the friendly king. This information can easily be obtained from the Position object.
If the "friendly king" has not moved, its half of the accumulator can be calculated by incrementally updating the accumulator for the previous position. For this, the NNUE code needs to know which pieces have been added to which squares and which pieces have been removed from which squares. In principle this information can be derived from the Position object and StateInfo struct (in the same way as undo_move() does this). However, it is probably a bit faster to prepare this information in do_move(), so I have kept the DirtyPiece struct. Since the DirtyPiece struct now stores the squares rather than "PieceSquare" indices, there are now at most three "dirty pieces" (previously two). A promotion move that captures a piece removes the capturing pawn and the captured piece from the board (to SQ_NONE) and moves the promoted piece to the promotion square (from SQ_NONE).
An STC test has confirmed a small speedup:
https://tests.stockfishchess.org/tests/view/5f43f06b5089a564a10d850a
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 87704 W: 9763 L: 9500 D: 68441
Ptnml(0-2): 426, 6950, 28845, 7197, 434
closes https://github.com/official-stockfish/Stockfish/pull/3068
No functional change
2020-08-23 18:29:38 -06:00
|
|
|
dp.piece[0] = make_piece(us, KING);
|
|
|
|
dp.from[0] = from;
|
|
|
|
dp.to[0] = to;
|
|
|
|
dp.piece[1] = make_piece(us, ROOK);
|
|
|
|
dp.from[1] = rfrom;
|
|
|
|
dp.to[1] = rto;
|
|
|
|
dp.dirty_num = 2;
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
}
|
|
|
|
|
2013-08-03 06:42:58 -06:00
|
|
|
// Remove both pieces first since squares could overlap in Chess960
|
2019-12-21 15:36:29 -07:00
|
|
|
remove_piece(Do ? from : to);
|
|
|
|
remove_piece(Do ? rfrom : rto);
|
|
|
|
board[Do ? from : to] = board[Do ? rfrom : rto] = NO_PIECE; // Since remove_piece doesn't do this for us
|
2016-09-03 10:14:01 -06:00
|
|
|
put_piece(make_piece(us, KING), Do ? to : from);
|
|
|
|
put_piece(make_piece(us, ROOK), Do ? rto : rfrom);
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-17 01:13:34 -06:00
|
|
|
/// Position::do_null_move() is used to do a "null move": it flips
|
2013-01-27 03:45:01 -07:00
|
|
|
/// the side to move without executing any move on the board.
|
2008-10-25 02:06:52 -06:00
|
|
|
|
2013-01-27 03:45:01 -07:00
|
|
|
void Position::do_null_move(StateInfo& newSt) {
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2013-01-27 03:45:01 -07:00
|
|
|
assert(!checkers());
|
2015-02-07 11:34:24 -07:00
|
|
|
assert(&newSt != st);
|
2011-10-29 10:49:20 -06:00
|
|
|
|
More incremental accumulator updates
This patch was inspired by c065abd which updates the accumulator,
if possible, based on the accumulator of two plies back if
the accumulator of the preceding ply is not available.
With this patch we look back even further in the position history
in an attempt to reduce the number of complete recomputations.
When we find a usable accumulator for the position N plies back,
we also update the accumulator of the position N-1 plies back
because that accumulator is most likely to be helpful later
when evaluating positions in sibling branches.
By not updating all intermediate accumulators immediately,
we avoid doing too much work that is not certain to be useful.
Overall, roughly 2-3% speedup.
This patch makes the code more specific to the net architecture,
changing input features of the net will require additional changes
to the incremental update code as discussed in the PR #3193 and #3191.
Passed STC:
https://tests.stockfishchess.org/tests/view/5f9056712c92c7fe3a8c60d0
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 10040 W: 1116 L: 968 D: 7956
Ptnml(0-2): 42, 722, 3365, 828, 63
closes https://github.com/official-stockfish/Stockfish/pull/3193
No functional change.
2020-10-20 13:06:06 -06:00
|
|
|
std::memcpy(&newSt, st, offsetof(StateInfo, accumulator));
|
Add NNUE evaluation
This patch ports the efficiently updatable neural network (NNUE) evaluation to Stockfish.
Both the NNUE and the classical evaluations are available, and can be used to
assign a value to a position that is later used in alpha-beta (PVS) search to find the
best move. The classical evaluation computes this value as a function of various chess
concepts, handcrafted by experts, tested and tuned using fishtest. The NNUE evaluation
computes this value with a neural network based on basic inputs. The network is optimized
and trained on the evalutions of millions of positions at moderate search depth.
The NNUE evaluation was first introduced in shogi, and ported to Stockfish afterward.
It can be evaluated efficiently on CPUs, and exploits the fact that only parts
of the neural network need to be updated after a typical chess move.
[The nodchip repository](https://github.com/nodchip/Stockfish) provides additional
tools to train and develop the NNUE networks.
This patch is the result of contributions of various authors, from various communities,
including: nodchip, ynasu87, yaneurao (initial port and NNUE authors), domschl, FireFather,
rqs, xXH4CKST3RXx, tttak, zz4032, joergoster, mstembera, nguyenpham, erbsenzaehler,
dorzechowski, and vondele.
This new evaluation needed various changes to fishtest and the corresponding infrastructure,
for which tomtor, ppigazzini, noobpwnftw, daylen, and vondele are gratefully acknowledged.
The first networks have been provided by gekkehenker and sergiovieri, with the latter
net (nn-97f742aaefcd.nnue) being the current default.
The evaluation function can be selected at run time with the `Use NNUE` (true/false) UCI option,
provided the `EvalFile` option points the the network file (depending on the GUI, with full path).
The performance of the NNUE evaluation relative to the classical evaluation depends somewhat on
the hardware, and is expected to improve quickly, but is currently on > 80 Elo on fishtest:
60000 @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f28fe6ea5abc164f05e4c4c
ELO: 92.77 +-2.1 (95%) LOS: 100.0%
Total: 60000 W: 24193 L: 8543 D: 27264
Ptnml(0-2): 609, 3850, 9708, 10948, 4885
40000 @ 20+0.2 th 8
https://tests.stockfishchess.org/tests/view/5f290229a5abc164f05e4c58
ELO: 89.47 +-2.0 (95%) LOS: 100.0%
Total: 40000 W: 12756 L: 2677 D: 24567
Ptnml(0-2): 74, 1583, 8550, 7776, 2017
At the same time, the impact on the classical evaluation remains minimal, causing no significant
regression:
sprt @ 10+0.1 th 1
https://tests.stockfishchess.org/tests/view/5f2906a2a5abc164f05e4c5b
LLR: 2.94 (-2.94,2.94) {-6.00,-4.00}
Total: 34936 W: 6502 L: 6825 D: 21609
Ptnml(0-2): 571, 4082, 8434, 3861, 520
sprt @ 60+0.6 th 1
https://tests.stockfishchess.org/tests/view/5f2906cfa5abc164f05e4c5d
LLR: 2.93 (-2.94,2.94) {-6.00,-4.00}
Total: 10088 W: 1232 L: 1265 D: 7591
Ptnml(0-2): 49, 914, 3170, 843, 68
The needed networks can be found at https://tests.stockfishchess.org/nns
It is recommended to use the default one as indicated by the `EvalFile` UCI option.
Guidelines for testing new nets can be found at
https://github.com/glinscott/fishtest/wiki/Creating-my-first-test#nnue-net-tests
Integration has been discussed in various issues:
https://github.com/official-stockfish/Stockfish/issues/2823
https://github.com/official-stockfish/Stockfish/issues/2728
The integration branch will be closed after the merge:
https://github.com/official-stockfish/Stockfish/pull/2825
https://github.com/official-stockfish/Stockfish/tree/nnue-player-wip
closes https://github.com/official-stockfish/Stockfish/pull/2912
This will be an exciting time for computer chess, looking forward to seeing the evolution of
this approach.
Bench: 4746616
2020-08-05 09:11:15 -06:00
|
|
|
|
2013-01-27 03:45:01 -07:00
|
|
|
newSt.previous = st;
|
|
|
|
st = &newSt;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
More incremental accumulator updates
This patch was inspired by c065abd which updates the accumulator,
if possible, based on the accumulator of two plies back if
the accumulator of the preceding ply is not available.
With this patch we look back even further in the position history
in an attempt to reduce the number of complete recomputations.
When we find a usable accumulator for the position N plies back,
we also update the accumulator of the position N-1 plies back
because that accumulator is most likely to be helpful later
when evaluating positions in sibling branches.
By not updating all intermediate accumulators immediately,
we avoid doing too much work that is not certain to be useful.
Overall, roughly 2-3% speedup.
This patch makes the code more specific to the net architecture,
changing input features of the net will require additional changes
to the incremental update code as discussed in the PR #3193 and #3191.
Passed STC:
https://tests.stockfishchess.org/tests/view/5f9056712c92c7fe3a8c60d0
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 10040 W: 1116 L: 968 D: 7956
Ptnml(0-2): 42, 722, 3365, 828, 63
closes https://github.com/official-stockfish/Stockfish/pull/3193
No functional change.
2020-10-20 13:06:06 -06:00
|
|
|
st->dirtyPiece.dirty_num = 0;
|
|
|
|
st->dirtyPiece.piece[0] = NO_PIECE; // Avoid checks in UpdateAccumulator()
|
Reduce the number of accumulator states
Reduce from 3 to 2. Make the intent of the states clearer.
STC: https://tests.stockfishchess.org/tests/view/60c50111457376eb8bcaad03
LLR: 2.95 (-2.94,2.94) <-2.50,0.50>
Total: 61888 W: 5007 L: 4944 D: 51937
Ptnml(0-2): 164, 3947, 22649, 4030, 154
LTC: https://tests.stockfishchess.org/tests/view/60c52b1c457376eb8bcaad2c
LLR: 2.94 (-2.94,2.94) <-2.50,0.50>
Total: 20248 W: 688 L: 618 D: 18942
Ptnml(0-2): 7, 551, 8946, 605, 15
closes https://github.com/official-stockfish/Stockfish/pull/3548
No functional change.
2021-06-12 12:45:14 -06:00
|
|
|
st->accumulator.computed[WHITE] = false;
|
|
|
|
st->accumulator.computed[BLACK] = false;
|
More incremental accumulator updates
This patch was inspired by c065abd which updates the accumulator,
if possible, based on the accumulator of two plies back if
the accumulator of the preceding ply is not available.
With this patch we look back even further in the position history
in an attempt to reduce the number of complete recomputations.
When we find a usable accumulator for the position N plies back,
we also update the accumulator of the position N-1 plies back
because that accumulator is most likely to be helpful later
when evaluating positions in sibling branches.
By not updating all intermediate accumulators immediately,
we avoid doing too much work that is not certain to be useful.
Overall, roughly 2-3% speedup.
This patch makes the code more specific to the net architecture,
changing input features of the net will require additional changes
to the incremental update code as discussed in the PR #3193 and #3191.
Passed STC:
https://tests.stockfishchess.org/tests/view/5f9056712c92c7fe3a8c60d0
LLR: 2.94 (-2.94,2.94) {-0.25,1.25}
Total: 10040 W: 1116 L: 968 D: 7956
Ptnml(0-2): 42, 722, 3365, 828, 63
closes https://github.com/official-stockfish/Stockfish/pull/3193
No functional change.
2020-10-20 13:06:06 -06:00
|
|
|
|
2013-01-27 03:45:01 -07:00
|
|
|
if (st->epSquare != SQ_NONE)
|
2011-10-29 10:49:20 -06:00
|
|
|
{
|
2013-01-27 03:45:01 -07:00
|
|
|
st->key ^= Zobrist::enpassant[file_of(st->epSquare)];
|
2011-10-29 10:49:20 -06:00
|
|
|
st->epSquare = SQ_NONE;
|
|
|
|
}
|
2011-07-16 03:42:27 -06:00
|
|
|
|
2013-01-27 03:45:01 -07:00
|
|
|
st->key ^= Zobrist::side;
|
2021-10-22 21:01:05 -06:00
|
|
|
++st->rule50;
|
Improve play for closed positions
This patch give a small bonus to incite the attacking side to keep more
pawns on the board.
A consequence of this bonus is that Stockfish will tend to play positions
slightly more closed on average than master, especially when it believes
that it has an advantage.
To lower the risk of blockades where Stockfish start shuffling without
progress, we also implement a progressive decrease of the evaluation
value with the 50 moves counter (along with the necessary aging of the
transposition table to reduce the impact of the Graph History Interaction
problem): since the evaluation decreases during shuffling phases, the
engine will tend to examine the consequences of pawn breaks faster during
the search.
Passed STC:
LLR: 2.96 (-2.94,2.94) {-0.25,1.25}
Total: 26184 W: 2406 L: 2252 D: 21526
Ptnml(0-2): 85, 1784, 9223, 1892, 108
https://tests.stockfishchess.org/tests/view/600cc08b735dd7f0f0352c06
Passed LCT:
LLR: 2.95 (-2.94,2.94) {0.25,1.25}
Total: 199768 W: 7695 L: 7191 D: 184882
Ptnml(0-2): 85, 6478, 86269, 6952, 100
https://tests.stockfishchess.org/tests/view/600ccd28735dd7f0f0352c10
Closes https://github.com/official-stockfish/Stockfish/pull/3321
Bench: 3988915
2021-01-21 10:53:29 -07:00
|
|
|
prefetch(TT.first_entry(key()));
|
2013-01-27 03:45:01 -07:00
|
|
|
|
|
|
|
st->pliesFromNull = 0;
|
|
|
|
|
|
|
|
sideToMove = ~sideToMove;
|
|
|
|
|
2016-08-27 02:05:42 -06:00
|
|
|
set_check_info(st);
|
2016-07-03 02:35:44 -06:00
|
|
|
|
2019-05-15 02:22:21 -06:00
|
|
|
st->repetition = 0;
|
|
|
|
|
2011-10-16 16:56:25 -06:00
|
|
|
assert(pos_is_ok());
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
|
2021-05-17 01:13:34 -06:00
|
|
|
|
|
|
|
/// Position::undo_null_move() must be used to undo a "null move"
|
|
|
|
|
2013-01-27 03:45:01 -07:00
|
|
|
void Position::undo_null_move() {
|
|
|
|
|
|
|
|
assert(!checkers());
|
|
|
|
|
|
|
|
st = st->previous;
|
|
|
|
sideToMove = ~sideToMove;
|
|
|
|
}
|
2011-10-29 10:49:20 -06:00
|
|
|
|
2014-10-03 22:07:55 -06:00
|
|
|
|
2014-11-18 15:39:17 -07:00
|
|
|
/// Position::key_after() computes the new hash key after the given move. Needed
|
2014-10-03 22:07:55 -06:00
|
|
|
/// for speculative prefetch. It doesn't recognize special moves like castling,
|
2021-01-09 08:46:06 -07:00
|
|
|
/// en passant and promotions.
|
2014-10-03 22:07:55 -06:00
|
|
|
|
|
|
|
Key Position::key_after(Move m) const {
|
|
|
|
|
|
|
|
Square from = from_sq(m);
|
|
|
|
Square to = to_sq(m);
|
2016-09-03 10:14:01 -06:00
|
|
|
Piece pc = piece_on(from);
|
|
|
|
Piece captured = piece_on(to);
|
2014-10-03 22:07:55 -06:00
|
|
|
Key k = st->key ^ Zobrist::side;
|
|
|
|
|
|
|
|
if (captured)
|
2016-09-03 10:14:01 -06:00
|
|
|
k ^= Zobrist::psq[captured][to];
|
2014-10-03 22:07:55 -06:00
|
|
|
|
2016-09-03 10:14:01 -06:00
|
|
|
return k ^ Zobrist::psq[pc][to] ^ Zobrist::psq[pc][from];
|
2014-10-02 15:19:14 -06:00
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-10-03 22:07:55 -06:00
|
|
|
|
2016-10-06 11:55:10 -06:00
|
|
|
/// Position::see_ge (Static Exchange Evaluation Greater or Equal) tests if the
|
2017-06-24 04:36:07 -06:00
|
|
|
/// SEE value of move is greater or equal to the given threshold. We'll use an
|
2016-10-06 11:55:10 -06:00
|
|
|
/// algorithm similar to alpha-beta pruning with a null window.
|
2008-12-09 03:20:47 -07:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
bool Position::see_ge(Move m, Value threshold) const {
|
2009-07-11 02:54:30 -06:00
|
|
|
|
2011-10-03 02:56:49 -06:00
|
|
|
assert(is_ok(m));
|
2009-07-11 02:54:30 -06:00
|
|
|
|
2021-01-12 06:59:51 -07:00
|
|
|
// Only deal with normal moves, assume others pass a simple SEE
|
2017-08-25 07:15:26 -06:00
|
|
|
if (type_of(m) != NORMAL)
|
2017-06-24 04:36:07 -06:00
|
|
|
return VALUE_ZERO >= threshold;
|
2016-10-06 11:55:10 -06:00
|
|
|
|
|
|
|
Square from = from_sq(m), to = to_sq(m);
|
2008-12-21 00:29:46 -07:00
|
|
|
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
int swap = PieceValue[MG][piece_on(to)] - threshold;
|
|
|
|
if (swap < 0)
|
2016-10-06 11:55:10 -06:00
|
|
|
return false;
|
2008-12-09 03:20:47 -07:00
|
|
|
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
swap = PieceValue[MG][piece_on(from)] - swap;
|
|
|
|
if (swap <= 0)
|
2016-10-06 11:55:10 -06:00
|
|
|
return true;
|
2008-10-25 13:44:10 -06:00
|
|
|
|
2021-07-07 06:32:54 -06:00
|
|
|
assert(color_of(piece_on(from)) == sideToMove);
|
2020-01-09 12:49:13 -07:00
|
|
|
Bitboard occupied = pieces() ^ from ^ to;
|
2021-07-07 06:32:54 -06:00
|
|
|
Color stm = sideToMove;
|
2020-01-09 12:49:13 -07:00
|
|
|
Bitboard attackers = attackers_to(to, occupied);
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
Bitboard stmAttackers, bb;
|
|
|
|
int res = 1;
|
2016-09-12 00:47:19 -06:00
|
|
|
|
2016-10-06 11:55:10 -06:00
|
|
|
while (true)
|
|
|
|
{
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
stm = ~stm;
|
2020-01-09 12:49:13 -07:00
|
|
|
attackers &= occupied;
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
|
|
|
|
// If stm has no more attackers then give up: stm loses
|
|
|
|
if (!(stmAttackers = attackers & pieces(stm)))
|
|
|
|
break;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2021-05-17 01:13:34 -06:00
|
|
|
// Don't allow pinned pieces to attack as long as there are
|
|
|
|
// pinners on their original square.
|
2020-07-11 08:59:33 -06:00
|
|
|
if (pinners(~stm) & occupied)
|
|
|
|
stmAttackers &= ~blockers_for_king(stm);
|
2012-09-01 03:45:14 -06:00
|
|
|
|
2016-10-06 11:55:10 -06:00
|
|
|
if (!stmAttackers)
|
2017-11-08 10:21:46 -07:00
|
|
|
break;
|
2012-09-01 03:45:14 -06:00
|
|
|
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
res ^= 1;
|
|
|
|
|
2018-02-23 14:02:10 -07:00
|
|
|
// Locate and remove the next least valuable attacker, and add to
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
// the bitboard 'attackers' any X-ray attackers behind it.
|
|
|
|
if ((bb = stmAttackers & pieces(PAWN)))
|
|
|
|
{
|
|
|
|
if ((swap = PawnValueMg - swap) < res)
|
|
|
|
break;
|
2016-09-21 01:32:00 -06:00
|
|
|
|
2021-03-16 13:51:31 -06:00
|
|
|
occupied ^= least_significant_square_bb(bb);
|
2020-01-09 12:49:13 -07:00
|
|
|
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
}
|
2013-07-20 07:26:52 -06:00
|
|
|
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
else if ((bb = stmAttackers & pieces(KNIGHT)))
|
|
|
|
{
|
|
|
|
if ((swap = KnightValueMg - swap) < res)
|
|
|
|
break;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2021-03-16 13:51:31 -06:00
|
|
|
occupied ^= least_significant_square_bb(bb);
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
else if ((bb = stmAttackers & pieces(BISHOP)))
|
2018-02-23 14:02:10 -07:00
|
|
|
{
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
if ((swap = BishopValueMg - swap) < res)
|
|
|
|
break;
|
|
|
|
|
2021-03-16 13:51:31 -06:00
|
|
|
occupied ^= least_significant_square_bb(bb);
|
2020-01-09 12:49:13 -07:00
|
|
|
attackers |= attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN);
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
else if ((bb = stmAttackers & pieces(ROOK)))
|
|
|
|
{
|
|
|
|
if ((swap = RookValueMg - swap) < res)
|
|
|
|
break;
|
|
|
|
|
2021-03-16 13:51:31 -06:00
|
|
|
occupied ^= least_significant_square_bb(bb);
|
2020-01-09 12:49:13 -07:00
|
|
|
attackers |= attacks_bb<ROOK>(to, occupied) & pieces(ROOK, QUEEN);
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
else if ((bb = stmAttackers & pieces(QUEEN)))
|
|
|
|
{
|
|
|
|
if ((swap = QueenValueMg - swap) < res)
|
|
|
|
break;
|
|
|
|
|
2021-03-16 13:51:31 -06:00
|
|
|
occupied ^= least_significant_square_bb(bb);
|
2020-01-09 12:49:13 -07:00
|
|
|
attackers |= (attacks_bb<BISHOP>(to, occupied) & pieces(BISHOP, QUEEN))
|
|
|
|
| (attacks_bb<ROOK >(to, occupied) & pieces(ROOK , QUEEN));
|
2018-02-23 14:02:10 -07:00
|
|
|
}
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
|
|
|
|
else // KING
|
|
|
|
// If we "capture" with the king but opponent still has attackers,
|
|
|
|
// reverse the result.
|
|
|
|
return (attackers & ~pieces(stm)) ? res ^ 1 : res;
|
2016-10-06 11:55:10 -06:00
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2020-01-09 12:49:13 -07:00
|
|
|
return bool(res);
|
Use a faster implementation of Static Exchange Evaluation
SEE (Static Exchange Evaluation) is a critical component, so we might
indulge some tricks to make it faster. Another pull request #2469 showed
some speedup by removing templates, this version uses Ronald de Man
(@syzygy1) SEE implementation which also unrolls the for loop by
suppressing the min_attacker() helper function and exits as soon as
the last swap is conclusive.
See Ronald de Man version there:
https://github.com/syzygy1/Cfish/blob/master/src/position.c
Patch testes against pull request #2469:
LLR: 2.95 (-2.94,2.94) {-1.00,3.00}
Total: 19365 W: 3771 L: 3634 D: 11960
Ptnml(0-2): 241, 1984, 5099, 2092, 255
http://tests.stockfishchess.org/tests/view/5e10eb135e5436dd91b27ba3
And since we are using new SPRT statistics, and that both pull requests
finished with less than 20000 games I also tested against master as
a speed-up:
LLR: 2.99 (-2.94,2.94) {-1.00,3.00}
Total: 18878 W: 3674 L: 3539 D: 11665
Ptnml(0-2): 193, 1999, 4966, 2019, 250
http://tests.stockfishchess.org/tests/view/5e10febf12ef906c8b388745
Non functional change
2020-01-04 11:54:35 -07:00
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2020-06-24 14:19:58 -06:00
|
|
|
|
2015-03-17 10:42:29 -06:00
|
|
|
/// Position::is_draw() tests whether the position is drawn by 50-move rule
|
|
|
|
/// or by repetition. It does not detect stalemates.
|
2013-12-03 03:37:29 -07:00
|
|
|
|
Threefold repetition detection
Implement a threefold repetition detection. Below are the examples of
problems fixed by this change.
Loosing move in a drawn position.
position fen 8/k7/3p4/p2P1p2/P2P1P2/8/8/K7 w - - 0 1 moves a1a2 a7a8 a2a1
The old code suggested a loosing move "bestmove a8a7", the new code suggests "bestmove a8b7" leading to a draw.
Incorrect evaluation (happened in a real game in TCEC Season 9).
position fen 4rbkr/1q3pp1/b3pn2/7p/1pN5/1P1BBP1P/P1R2QP1/3R2K1 w - - 5 31 moves e3d4 h8h6 d4e3
The old code evaluated it as "cp 0", the new code evaluation is around "cp -50" which is adequate.
Brings 0.5-1 ELO gain. Passes [-3.00,1.00].
STC: http://tests.stockfishchess.org/tests/view/584ece040ebc5903140c5aea
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 47744 W: 8537 L: 8461 D: 30746
LTC: http://tests.stockfishchess.org/tests/view/584f134d0ebc5903140c5b37
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 36775 W: 4739 L: 4639 D: 27397
Patch has been rewritten into current form for simplification and
logic slightly changed so that return a draw score if the position
repeats once earlier but after or at the root, or repeats twice
strictly before the root. In its original form, repetition at root
was not returned as an immediate draw.
After retestimng testing both version with SPRT[-3, 1], both passed
succesfully, but this version was chosen becuase more natural. There is
an argument about MultiPV in which an extended draw at root may be sensible.
See discussion here:
https://github.com/official-stockfish/Stockfish/pull/925
For documentation, current version passed both at STC and LTC:
STC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 51562 W: 9314 L: 9245 D: 33003
LTC
LLR: 2.96 (-2.94,2.94) [-3.00,1.00]
Total: 115663 W: 14904 L: 14906 D: 85853
bench: 5468995
2016-12-12 08:04:16 -07:00
|
|
|
bool Position::is_draw(int ply) const {
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2012-12-25 09:59:35 -07:00
|
|
|
if (st->rule50 > 99 && (!checkers() || MoveList<LEGAL>(*this).size()))
|
2008-10-25 02:06:52 -06:00
|
|
|
return true;
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2019-05-15 02:22:21 -06:00
|
|
|
// Return a draw score if a position repeats once earlier but strictly
|
|
|
|
// after the root, or repeats twice before or at the root.
|
2020-03-14 10:04:50 -06:00
|
|
|
return st->repetition && st->repetition < ply;
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-04-18 10:38:38 -06:00
|
|
|
// Position::has_repeated() tests whether there has been at least one repetition
|
|
|
|
// of positions since the last capture or pawn move.
|
|
|
|
|
|
|
|
bool Position::has_repeated() const {
|
|
|
|
|
|
|
|
StateInfo* stc = st;
|
2019-05-15 02:22:21 -06:00
|
|
|
int end = std::min(st->rule50, st->pliesFromNull);
|
|
|
|
while (end-- >= 4)
|
2018-04-18 10:38:38 -06:00
|
|
|
{
|
2019-05-15 02:22:21 -06:00
|
|
|
if (stc->repetition)
|
|
|
|
return true;
|
2018-04-18 10:38:38 -06:00
|
|
|
|
|
|
|
stc = stc->previous;
|
|
|
|
}
|
2019-05-15 02:22:21 -06:00
|
|
|
return false;
|
2018-04-18 10:38:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
/// Position::has_game_cycle() tests if the position has a move which draws by repetition,
|
|
|
|
/// or an earlier position has a move that directly reaches the current position.
|
|
|
|
|
|
|
|
bool Position::has_game_cycle(int ply) const {
|
|
|
|
|
2018-05-21 01:37:16 -06:00
|
|
|
int j;
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
|
|
|
|
int end = std::min(st->rule50, st->pliesFromNull);
|
|
|
|
|
|
|
|
if (end < 3)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Key originalKey = st->key;
|
|
|
|
StateInfo* stp = st->previous;
|
|
|
|
|
|
|
|
for (int i = 3; i <= end; i += 2)
|
|
|
|
{
|
2018-05-21 01:37:16 -06:00
|
|
|
stp = stp->previous->previous;
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
|
2018-05-21 01:37:16 -06:00
|
|
|
Key moveKey = originalKey ^ stp->key;
|
|
|
|
if ( (j = H1(moveKey), cuckoo[j] == moveKey)
|
|
|
|
|| (j = H2(moveKey), cuckoo[j] == moveKey))
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
{
|
2018-05-21 01:37:16 -06:00
|
|
|
Move move = cuckooMove[j];
|
2018-06-02 09:41:37 -06:00
|
|
|
Square s1 = from_sq(move);
|
|
|
|
Square s2 = to_sq(move);
|
2018-05-21 01:37:16 -06:00
|
|
|
|
Change definition of between_bb()
We remark that in current master, most of our use cases for between_bb() can be
optimized if the second parameter of the function is added to the segment. So we
change the definition of between_bb(s1, s2) such that it excludes s1 but includes s2.
We also use a precomputed array for between_bb() for another small speed gain
(see https://tests.stockfishchess.org/tests/view/604d09f72433018de7a389fb).
Passed STC:
LLR: 2.96 (-2.94,2.94) {-0.25,1.25}
Total: 18736 W: 1746 L: 1607 D: 15383
Ptnml(0-2): 61, 1226, 6644, 1387, 50
https://tests.stockfishchess.org/tests/view/60428c84ddcba5f0627bb6e4
Yellow LTC:
LTC:
LLR: -3.00 (-2.94,2.94) {0.25,1.25}
Total: 39144 W: 1431 L: 1413 D: 36300
Ptnml(0-2): 13, 1176, 17184, 1178, 21
https://tests.stockfishchess.org/tests/view/605128702433018de7a38ca1
Closes https://github.com/official-stockfish/Stockfish/pull/3397
---------
Verified for correctness by running perft on the following position:
./stockfish
position fen 4rrk1/1p1nq3/p7/2p1P1pp/3P2bp/3Q1Bn1/PPPB4/1K2R1NR w - - 40 21
go perft 6
Nodes searched: 6136386434
--------
No functional change
2021-03-15 13:06:42 -06:00
|
|
|
if (!((between_bb(s1, s2) ^ s2) & pieces()))
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
{
|
2018-05-21 01:37:16 -06:00
|
|
|
if (ply > i)
|
|
|
|
return true;
|
|
|
|
|
2019-05-02 11:36:25 -06:00
|
|
|
// For nodes before or at the root, check that the move is a
|
|
|
|
// repetition rather than a move to the current position.
|
|
|
|
// In the cuckoo table, both moves Rc1c5 and Rc5c1 are stored in
|
|
|
|
// the same location, so we have to select which square to check.
|
2019-04-14 06:50:37 -06:00
|
|
|
if (color_of(piece_on(empty(s1) ? s2 : s1)) != side_to_move())
|
|
|
|
continue;
|
|
|
|
|
2018-05-21 01:37:16 -06:00
|
|
|
// For repetitions before or at the root, require one more
|
2019-05-15 02:22:21 -06:00
|
|
|
if (stp->repetition)
|
|
|
|
return true;
|
Use cycle detection to bound search value
A position which has a move which draws by repetition, or which could have
been reached from an earlier position in the game tree, is considered to be
at least a draw for the side to move.
Cycle detection algorithm by Marcel van Kervink:
https://marcelk.net/2013-04-06/paper/upcoming-rep-v2.pdf
----------------------------
How does the algorithm work in practice? The algorithm is an efficient
method to detect if the side to move has a drawing move, without doing any
move generation, thus possibly giving a cheap cutoffThe most interesting
conditions are both on line 1195:
```
if ( originalKey == (progressKey ^ stp->key)
|| progressKey == Zobrist::side)
```
This uses the position keys as a sort-of Bloom filter, to avoid the expensive
checks which follow. For "upcoming repetition" consider the opening Nf3 Nf6 Ng1.
The XOR of this position's key with the starting position gives their difference,
which can be used to look up black's repeating move (Ng8). But that look-up is
expensive, so line 1195 checks that the white pieces are on their original squares.
This is the subtlest part of the algorithm, but the basic idea in the above game
is there are 4 positions (starting position and the one after each move). An XOR
of the first pair (startpos and after Nf3) gives a key matching Nf3. An XOR of
the second pair (after Nf6 and after Ng1) gives a key matching the move Ng1. But
since the difference in each pair is the location of the white knight those keys
are "identical" (not quite because while there are 4 keys the the side to move
changed 3 times, so the keys differ by Zobrist::side). The loop containing line
1195 does this pair-wise XOR-ing.
Continuing the example, after line 1195 determines that the white pieces are
back where they started we still need to make sure the changes in the black
pieces represents a legal move. This is done by looking up the "moveKey" to
see if it corresponds to possible move, and that there are no pieces blocking
its way. There is the additional complication that, to match the behavior of
is_draw(), if the repetition is not inside the search tree then there must be
an additional repetition in the game history. Since a position can have more
than one upcoming repetition a simple count does not suffice. So there is a
search loop ending on line 1215.
On the other hand, the "no-progress' is the same thing but offset by 1 ply.
I like the concept but think it currently has minimal or negative benefit,
and I'd be happy to remove it if that would get the patch accepted. This
will not, however, save many lines of code.
-----------------------------
STC:
LLR: 2.95 (-2.94,2.94) [0.00,5.00]
Total: 36430 W: 7446 L: 7150 D: 21834
http://tests.stockfishchess.org/tests/view/5afc123f0ebc591fdf408dfc
LTC:
LLR: 2.96 (-2.94,2.94) [0.00,5.00]
Total: 12998 W: 2045 L: 1876 D: 9077
http://tests.stockfishchess.org/tests/view/5afc2c630ebc591fdf408e0c
How could we continue after the patch:
• The code in search() that checks for cycles has numerous possible variants.
Perhaps the check need could be done in qsearch() too.
• The biggest improvement would be to get "no progress" to be of actual benefit,
and it would be helpful understand why it (probably) isn't. Perhaps there is an
interaction with the transposition table or the (fantastically complex) tree
search. Perhaps this would be hard to fix, but there may be a simple oversight.
Closes https://github.com/official-stockfish/Stockfish/pull/1575
Bench: 4550412
2018-05-16 14:47:41 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-04-02 11:21:17 -06:00
|
|
|
/// Position::flip() flips position with the white and black sides reversed. This
|
2013-12-02 11:04:09 -07:00
|
|
|
/// is only useful for debugging e.g. for finding evaluation symmetry bugs.
|
2011-04-26 03:19:57 -06:00
|
|
|
|
2012-04-02 11:21:17 -06:00
|
|
|
void Position::flip() {
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2013-08-05 03:06:23 -06:00
|
|
|
string f, token;
|
|
|
|
std::stringstream ss(fen());
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2014-04-06 02:50:27 -06:00
|
|
|
for (Rank r = RANK_8; r >= RANK_1; --r) // Piece placement
|
2013-08-05 03:06:23 -06:00
|
|
|
{
|
2014-04-06 02:50:27 -06:00
|
|
|
std::getline(ss, token, r > RANK_1 ? '/' : ' ');
|
2013-08-05 05:25:21 -06:00
|
|
|
f.insert(0, token + (f.empty() ? " " : "/"));
|
2013-08-05 03:06:23 -06:00
|
|
|
}
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2013-08-05 05:25:21 -06:00
|
|
|
ss >> token; // Active color
|
|
|
|
f += (token == "w" ? "B " : "W "); // Will be lowercased later
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2013-08-05 05:25:21 -06:00
|
|
|
ss >> token; // Castling availability
|
2013-08-05 03:06:23 -06:00
|
|
|
f += token + " ";
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2015-01-21 03:33:53 -07:00
|
|
|
std::transform(f.begin(), f.end(), f.begin(),
|
|
|
|
[](char c) { return char(islower(c) ? toupper(c) : tolower(c)); });
|
2013-08-05 05:25:21 -06:00
|
|
|
|
|
|
|
ss >> token; // En passant square
|
2013-08-05 03:06:23 -06:00
|
|
|
f += (token == "-" ? token : token.replace(1, 1, token[1] == '3' ? "6" : "3"));
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2013-08-05 05:25:21 -06:00
|
|
|
std::getline(ss, token); // Half and full moves
|
2013-08-05 03:06:23 -06:00
|
|
|
f += token;
|
2012-04-09 04:35:47 -06:00
|
|
|
|
2016-04-11 08:45:36 -06:00
|
|
|
set(f, is_chess960(), st, this_thread());
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2011-10-16 16:56:25 -06:00
|
|
|
assert(pos_is_ok());
|
2008-08-31 23:59:13 -06:00
|
|
|
}
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
/// Position::pos_is_ok() performs some consistency checks for the
|
|
|
|
/// position object and raises an asserts if something wrong is detected.
|
2008-08-31 23:59:13 -06:00
|
|
|
/// This is meant to be helpful when debugging.
|
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
bool Position::pos_is_ok() const {
|
2015-02-07 11:34:24 -07:00
|
|
|
|
2018-03-18 16:38:58 -06:00
|
|
|
constexpr bool Fast = true; // Quick (default) or full check?
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
if ( (sideToMove != WHITE && sideToMove != BLACK)
|
|
|
|
|| piece_on(square<KING>(WHITE)) != W_KING
|
|
|
|
|| piece_on(square<KING>(BLACK)) != B_KING
|
|
|
|
|| ( ep_square() != SQ_NONE
|
|
|
|
&& relative_rank(sideToMove, ep_square()) != RANK_6))
|
|
|
|
assert(0 && "pos_is_ok: Default");
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
if (Fast)
|
|
|
|
return true;
|
2008-09-23 16:32:53 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
if ( pieceCount[W_KING] != 1
|
|
|
|
|| pieceCount[B_KING] != 1
|
|
|
|
|| attackers_to(square<KING>(~sideToMove)) & pieces(sideToMove))
|
|
|
|
assert(0 && "pos_is_ok: Kings");
|
2014-03-14 02:43:19 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
if ( (pieces(PAWN) & (Rank1BB | Rank8BB))
|
|
|
|
|| pieceCount[W_PAWN] > 8
|
|
|
|
|| pieceCount[B_PAWN] > 8)
|
|
|
|
assert(0 && "pos_is_ok: Pawns");
|
2008-08-31 23:59:13 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
if ( (pieces(WHITE) & pieces(BLACK))
|
|
|
|
|| (pieces(WHITE) | pieces(BLACK)) != pieces()
|
|
|
|
|| popcount(pieces(WHITE)) > 16
|
|
|
|
|| popcount(pieces(BLACK)) > 16)
|
|
|
|
assert(0 && "pos_is_ok: Bitboards");
|
2010-01-24 08:09:32 -07:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
for (PieceType p1 = PAWN; p1 <= KING; ++p1)
|
|
|
|
for (PieceType p2 = PAWN; p2 <= KING; ++p2)
|
|
|
|
if (p1 != p2 && (pieces(p1) & pieces(p2)))
|
|
|
|
assert(0 && "pos_is_ok: Bitboards");
|
2011-06-27 09:06:15 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
StateInfo si = *st;
|
2021-04-19 11:50:19 -06:00
|
|
|
ASSERT_ALIGNED(&si, Eval::NNUE::CacheLineSize);
|
2020-11-03 03:23:35 -07:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
set_state(&si);
|
|
|
|
if (std::memcmp(&si, st, sizeof(StateInfo)))
|
|
|
|
assert(0 && "pos_is_ok: State");
|
2016-09-03 10:14:01 -06:00
|
|
|
|
2017-06-24 04:36:07 -06:00
|
|
|
for (Piece pc : Pieces)
|
|
|
|
if ( pieceCount[pc] != popcount(pieces(color_of(pc), type_of(pc)))
|
|
|
|
|| pieceCount[pc] != std::count(board, board + SQUARE_NB, pc))
|
|
|
|
assert(0 && "pos_is_ok: Pieces");
|
2015-02-07 06:34:35 -07:00
|
|
|
|
2019-07-16 06:08:58 -06:00
|
|
|
for (Color c : { WHITE, BLACK })
|
2019-08-12 08:42:28 -06:00
|
|
|
for (CastlingRights cr : {c & KING_SIDE, c & QUEEN_SIDE})
|
2017-06-24 04:36:07 -06:00
|
|
|
{
|
2019-08-12 08:42:28 -06:00
|
|
|
if (!can_castle(cr))
|
2017-06-24 04:36:07 -06:00
|
|
|
continue;
|
|
|
|
|
2019-08-12 08:42:28 -06:00
|
|
|
if ( piece_on(castlingRookSquare[cr]) != make_piece(c, ROOK)
|
2019-09-14 00:33:00 -06:00
|
|
|
|| castlingRightsMask[castlingRookSquare[cr]] != cr
|
|
|
|
|| (castlingRightsMask[square<KING>(c)] & cr) != cr)
|
2017-06-24 04:36:07 -06:00
|
|
|
assert(0 && "pos_is_ok: Castling");
|
|
|
|
}
|
|
|
|
|
2008-08-31 23:59:13 -06:00
|
|
|
return true;
|
|
|
|
}
|
2021-02-26 02:02:13 -07:00
|
|
|
|
|
|
|
} // namespace Stockfish
|