Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
official-stockfish
GitHub Repository: official-stockfish/stockfish
Path: blob/master/src/bitboard.cpp
503 views
1
/*
2
Stockfish, a UCI chess playing engine derived from Glaurung 2.1
3
Copyright (C) 2004-2026 The Stockfish developers (see AUTHORS file)
4
5
Stockfish is free software: you can redistribute it and/or modify
6
it under the terms of the GNU General Public License as published by
7
the Free Software Foundation, either version 3 of the License, or
8
(at your option) any later version.
9
10
Stockfish is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
GNU General Public License for more details.
14
15
You should have received a copy of the GNU General Public License
16
along with this program. If not, see <http://www.gnu.org/licenses/>.
17
*/
18
19
#include "bitboard.h"
20
21
#include <algorithm>
22
#include <bitset>
23
#include <initializer_list>
24
25
#include "misc.h"
26
27
namespace Stockfish {
28
29
uint8_t PopCnt16[1 << 16];
30
uint8_t SquareDistance[SQUARE_NB][SQUARE_NB];
31
32
Bitboard LineBB[SQUARE_NB][SQUARE_NB];
33
Bitboard BetweenBB[SQUARE_NB][SQUARE_NB];
34
Bitboard RayPassBB[SQUARE_NB][SQUARE_NB];
35
36
alignas(64) Magic Magics[SQUARE_NB][2];
37
38
namespace {
39
40
Bitboard RookTable[0x19000]; // To store rook attacks
41
Bitboard BishopTable[0x1480]; // To store bishop attacks
42
43
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]);
44
}
45
46
// Returns an ASCII representation of a bitboard suitable
47
// to be printed to standard output. Useful for debugging.
48
std::string Bitboards::pretty(Bitboard b) {
49
50
std::string s = "+---+---+---+---+---+---+---+---+\n";
51
52
for (Rank r = RANK_8;; --r)
53
{
54
for (File f = FILE_A; f <= FILE_H; ++f)
55
s += b & make_square(f, r) ? "| X " : "| ";
56
57
s += "| " + std::to_string(1 + r) + "\n+---+---+---+---+---+---+---+---+\n";
58
59
if (r == RANK_1)
60
break;
61
}
62
s += " a b c d e f g h\n";
63
64
return s;
65
}
66
67
68
// Initializes various bitboard tables. It is called at
69
// startup and relies on global objects to be already zero-initialized.
70
void Bitboards::init() {
71
72
for (unsigned i = 0; i < (1 << 16); ++i)
73
PopCnt16[i] = uint8_t(std::bitset<16>(i).count());
74
75
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
76
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
77
SquareDistance[s1][s2] = std::max(distance<File>(s1, s2), distance<Rank>(s1, s2));
78
79
init_magics(ROOK, RookTable, Magics);
80
init_magics(BISHOP, BishopTable, Magics);
81
82
for (Square s1 = SQ_A1; s1 <= SQ_H8; ++s1)
83
{
84
for (PieceType pt : {BISHOP, ROOK})
85
for (Square s2 = SQ_A1; s2 <= SQ_H8; ++s2)
86
{
87
if (PseudoAttacks[pt][s1] & s2)
88
{
89
LineBB[s1][s2] = (attacks_bb(pt, s1, 0) & attacks_bb(pt, s2, 0)) | s1 | s2;
90
BetweenBB[s1][s2] =
91
(attacks_bb(pt, s1, square_bb(s2)) & attacks_bb(pt, s2, square_bb(s1)));
92
RayPassBB[s1][s2] =
93
attacks_bb(pt, s1, 0) & (attacks_bb(pt, s2, square_bb(s1)) | s2);
94
}
95
BetweenBB[s1][s2] |= s2;
96
}
97
}
98
}
99
100
namespace {
101
// Computes all rook and bishop attacks at startup. Magic
102
// bitboards are used to look up attacks of sliding pieces. As a reference see
103
// https://www.chessprogramming.org/Magic_Bitboards. In particular, here we use
104
// the so called "fancy" approach.
105
void init_magics(PieceType pt, Bitboard table[], Magic magics[][2]) {
106
107
#ifndef USE_PEXT
108
// Optimal PRNG seeds to pick the correct magics in the shortest time
109
int seeds[][RANK_NB] = {{8977, 44560, 54343, 38998, 5731, 95205, 104912, 17020},
110
{728, 10316, 55013, 32803, 12281, 15100, 16645, 255}};
111
112
Bitboard occupancy[4096];
113
int epoch[4096] = {}, cnt = 0;
114
#endif
115
Bitboard reference[4096];
116
int size = 0;
117
118
for (Square s = SQ_A1; s <= SQ_H8; ++s)
119
{
120
// Board edges are not considered in the relevant occupancies
121
Bitboard edges = ((Rank1BB | Rank8BB) & ~rank_bb(s)) | ((FileABB | FileHBB) & ~file_bb(s));
122
123
// Given a square 's', the mask is the bitboard of sliding attacks from
124
// 's' computed on an empty board. The index must be big enough to contain
125
// all the attacks for each possible subset of the mask and so is 2 power
126
// the number of 1s of the mask. Hence we deduce the size of the shift to
127
// apply to the 64 or 32 bits word to get the index.
128
Magic& m = magics[s][pt - BISHOP];
129
m.mask = Bitboards::sliding_attack(pt, s, 0) & ~edges;
130
#ifndef USE_PEXT
131
m.shift = (Is64Bit ? 64 : 32) - popcount(m.mask);
132
#endif
133
// Set the offset for the attacks table of the square. We have individual
134
// table sizes for each square with "Fancy Magic Bitboards".
135
m.attacks = s == SQ_A1 ? table : magics[s - 1][pt - BISHOP].attacks + size;
136
size = 0;
137
138
// Use Carry-Rippler trick to enumerate all subsets of masks[s] and
139
// store the corresponding sliding attack bitboard in reference[].
140
Bitboard b = 0;
141
do
142
{
143
#ifndef USE_PEXT
144
occupancy[size] = b;
145
#endif
146
reference[size] = Bitboards::sliding_attack(pt, s, b);
147
148
if (HasPext)
149
m.attacks[pext(b, m.mask)] = reference[size];
150
151
size++;
152
b = (b - m.mask) & m.mask;
153
} while (b);
154
155
#ifndef USE_PEXT
156
PRNG rng(seeds[Is64Bit][rank_of(s)]);
157
158
// Find a magic for square 's' picking up an (almost) random number
159
// until we find the one that passes the verification test.
160
for (int i = 0; i < size;)
161
{
162
for (m.magic = 0; popcount((m.magic * m.mask) >> 56) < 6;)
163
m.magic = rng.sparse_rand<Bitboard>();
164
165
// A good magic must map every possible occupancy to an index that
166
// looks up the correct sliding attack in the attacks[s] database.
167
// Note that we build up the database for square 's' as a side
168
// effect of verifying the magic. Keep track of the attempt count
169
// and save it in epoch[], little speed-up trick to avoid resetting
170
// m.attacks[] after every failed attempt.
171
for (++cnt, i = 0; i < size; ++i)
172
{
173
unsigned idx = m.index(occupancy[i]);
174
175
if (epoch[idx] < cnt)
176
{
177
epoch[idx] = cnt;
178
m.attacks[idx] = reference[i];
179
}
180
else if (m.attacks[idx] != reference[i])
181
break;
182
}
183
}
184
#endif
185
}
186
}
187
}
188
189
} // namespace Stockfish
190
191