-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmovegen.cpp
More file actions
636 lines (564 loc) · 22 KB
/
Copy pathmovegen.cpp
File metadata and controls
636 lines (564 loc) · 22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
#include "movegen.h"
#include <cassert>
#include <iterator>
#include <array>
#include <vector>
#include <algorithm>
#include <iostream>
// Define global attack tables
U64 pawn_attacks[2][64];
U64 knight_attacks[64];
U64 king_attacks[64];
U64 bishop_masks[64];
U64 rook_masks[64];
int bishop_shifts[64];
int rook_shifts[64];
U64 bishop_magics[64];
U64 rook_magics[64];
U64* bishop_attacks[64];
U64* rook_attacks[64];
U64* bishop_pext_attacks[64];
U64* rook_pext_attacks[64];
bool use_pext_attacks = false;
U64 between_bb[64][64];
U64 line_bb[64][64];
// Flat tables for sliding attacks
U64 bishop_attacks_table[5248];
U64 rook_attacks_table[102400];
U64 bishop_pext_attacks_table[5248];
U64 rook_pext_attacks_table[102400];
bool pext_available() {
#if defined(__BMI2__)
return __builtin_cpu_supports("bmi2");
#else
return false;
#endif
}
void set_pext_enabled(bool enabled) {
use_pext_attacks = enabled && pext_available();
}
// Helper functions for magic bitboards generation
U64 bishop_attacks_on_the_fly(int sq, U64 block) {
U64 attacks = 0;
int r, f;
int target_r = sq / 8;
int target_f = sq % 8;
for (r = target_r + 1, f = target_f + 1; r <= 7 && f <= 7; r++, f++) {
int s = r * 8 + f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
for (r = target_r + 1, f = target_f - 1; r <= 7 && f >= 0; r++, f--) {
int s = r * 8 + f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
for (r = target_r - 1, f = target_f + 1; r >= 0 && f <= 7; r--, f++) {
int s = r * 8 + f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
for (r = target_r - 1, f = target_f - 1; r >= 0 && f >= 0; r--, f--) {
int s = r * 8 + f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
return attacks;
}
U64 rook_attacks_on_the_fly(int sq, U64 block) {
U64 attacks = 0;
int r, f;
int target_r = sq / 8;
int target_f = sq % 8;
for (r = target_r + 1; r <= 7; r++) {
int s = r * 8 + target_f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
for (r = target_r - 1; r >= 0; r--) {
int s = r * 8 + target_f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
for (f = target_f + 1; f <= 7; f++) {
int s = target_r * 8 + f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
for (f = target_f - 1; f >= 0; f--) {
int s = target_r * 8 + f;
attacks |= (1ULL << s);
if (block & (1ULL << s)) break;
}
return attacks;
}
U64 get_bishop_mask(int sq) {
U64 mask = 0;
int r = sq / 8;
int f = sq % 8;
for (int tr = r + 1, tf = f + 1; tr <= 6 && tf <= 6; tr++, tf++) mask |= (1ULL << (tr * 8 + tf));
for (int tr = r + 1, tf = f - 1; tr <= 6 && tf >= 1; tr++, tf--) mask |= (1ULL << (tr * 8 + tf));
for (int tr = r - 1, tf = f + 1; tr >= 1 && tf <= 6; tr--, tf++) mask |= (1ULL << (tr * 8 + tf));
for (int tr = r - 1, tf = f - 1; tr >= 1 && tf >= 1; tr--, tf--) mask |= (1ULL << (tr * 8 + tf));
return mask;
}
U64 get_rook_mask(int sq) {
U64 mask = 0;
int r = sq / 8;
int f = sq % 8;
for (int tr = r + 1; tr <= 6; tr++) mask |= (1ULL << (tr * 8 + f));
for (int tr = r - 1; tr >= 1; tr--) mask |= (1ULL << (tr * 8 + f));
for (int tf = f + 1; tf <= 6; tf++) mask |= (1ULL << (r * 8 + tf));
for (int tf = f - 1; tf >= 1; tf--) mask |= (1ULL << (r * 8 + tf));
return mask;
}
U64 set_occupancy_helper(int index, U64 mask) {
U64 occupancy = 0ULL;
int bit_count = count_bits(mask);
for (int i = 0; i < bit_count; i++) {
int sq = get_lsb(mask);
clear_bit(mask, sq);
if (index & (1 << i)) {
occupancy |= (1ULL << sq);
}
}
return occupancy;
}
// Generate magic numbers dynamically
U64 find_magic(int sq, int relevant_bits, bool bishop) {
U64 mask = bishop ? get_bishop_mask(sq) : get_rook_mask(sq);
int num_indices = 1 << relevant_bits;
std::vector<U64> blockers(num_indices);
std::vector<U64> attacks(num_indices);
for (int i = 0; i < num_indices; i++) {
blockers[i] = set_occupancy_helper(i, mask);
attacks[i] = bishop ? bishop_attacks_on_the_fly(sq, blockers[i]) : rook_attacks_on_the_fly(sq, blockers[i]);
}
U64 state = 1804289383ULL + sq * 987654321ULL;
auto next_random = [&]() {
state ^= state >> 12;
state ^= state << 25;
state ^= state >> 27;
return state * 2685821657736338717ULL;
};
std::vector<U64> used_attacks(num_indices, 0ULL);
int shift = 64 - relevant_bits;
while (true) {
U64 candidate = next_random() & next_random() & next_random();
if (count_bits((candidate * mask) & 0xFF00000000000000ULL) < 6) continue;
std::fill(used_attacks.begin(), used_attacks.end(), 0ULL);
bool fail = false;
for (int i = 0; i < num_indices; i++) {
int idx = (blockers[i] * candidate) >> shift;
if (used_attacks[idx] == 0) {
used_attacks[idx] = attacks[i];
} else if (used_attacks[idx] != attacks[i]) {
fail = fail = true;
break;
}
}
if (!fail) {
return candidate;
}
}
}
void init_all_attack_tables() {
// 1. Initialize leaps (pawns, knights, kings)
for (int sq = 0; sq < 64; sq++) {
int r = sq / 8;
int f = sq % 8;
// Pawns
pawn_attacks[WHITE][sq] = 0;
pawn_attacks[BLACK][sq] = 0;
if (r < 7) {
if (f > 0) set_bit(pawn_attacks[WHITE][sq], (r + 1) * 8 + (f - 1));
if (f < 7) set_bit(pawn_attacks[WHITE][sq], (r + 1) * 8 + (f + 1));
}
if (r > 0) {
if (f > 0) set_bit(pawn_attacks[BLACK][sq], (r - 1) * 8 + (f - 1));
if (f < 7) set_bit(pawn_attacks[BLACK][sq], (r - 1) * 8 + (f + 1));
}
// Knights
knight_attacks[sq] = 0;
int knight_offsets[8][2] = {
{2, 1}, {2, -1}, {1, 2}, {1, -2},
{-2, 1}, {-2, -1}, {-1, 2}, {-1, -2}
};
for (int i = 0; i < 8; i++) {
int tr = r + knight_offsets[i][0];
int tf = f + knight_offsets[i][1];
if (tr >= 0 && tr <= 7 && tf >= 0 && tf <= 7) {
set_bit(knight_attacks[sq], tr * 8 + tf);
}
}
// Kings
king_attacks[sq] = 0;
int king_offsets[8][2] = {
{1, 0}, {-1, 0}, {0, 1}, {0, -1},
{1, 1}, {1, -1}, {-1, 1}, {-1, -1}
};
for (int i = 0; i < 8; i++) {
int tr = r + king_offsets[i][0];
int tf = f + king_offsets[i][1];
if (tr >= 0 && tr <= 7 && tf >= 0 && tf <= 7) {
set_bit(king_attacks[sq], tr * 8 + tf);
}
}
}
// 2. Initialize sliding attack magics and table pointers
U64 bishop_offset = 0;
U64 rook_offset = 0;
for (int sq = 0; sq < 64; sq++) {
bishop_masks[sq] = get_bishop_mask(sq);
rook_masks[sq] = get_rook_mask(sq);
int b_bits = count_bits(bishop_masks[sq]);
int r_bits = count_bits(rook_masks[sq]);
bishop_shifts[sq] = 64 - b_bits;
rook_shifts[sq] = 64 - r_bits;
bishop_magics[sq] = find_magic(sq, b_bits, true);
rook_magics[sq] = find_magic(sq, r_bits, false);
assert(bishop_offset + (1ULL << b_bits) <= std::size(bishop_attacks_table));
assert(rook_offset + (1ULL << r_bits) <= std::size(rook_attacks_table));
bishop_attacks[sq] = &bishop_attacks_table[bishop_offset];
rook_attacks[sq] = &rook_attacks_table[rook_offset];
bishop_pext_attacks[sq] = &bishop_pext_attacks_table[bishop_offset];
rook_pext_attacks[sq] = &rook_pext_attacks_table[rook_offset];
// Fill attack tables
int b_indices = 1 << b_bits;
for (int i = 0; i < b_indices; i++) {
U64 block = set_occupancy_helper(i, bishop_masks[sq]);
int idx = (block * bishop_magics[sq]) >> bishop_shifts[sq];
assert(idx >= 0 && idx < b_indices);
bishop_attacks[sq][idx] = bishop_attacks_on_the_fly(sq, block);
bishop_pext_attacks[sq][i] = bishop_attacks_on_the_fly(sq, block);
}
int r_indices = 1 << r_bits;
for (int i = 0; i < r_indices; i++) {
U64 block = set_occupancy_helper(i, rook_masks[sq]);
int idx = (block * rook_magics[sq]) >> rook_shifts[sq];
assert(idx >= 0 && idx < r_indices);
rook_attacks[sq][idx] = rook_attacks_on_the_fly(sq, block);
rook_pext_attacks[sq][i] = rook_attacks_on_the_fly(sq, block);
}
bishop_offset += b_indices;
rook_offset += r_indices;
}
assert(bishop_offset == std::size(bishop_attacks_table));
assert(rook_offset == std::size(rook_attacks_table));
set_pext_enabled(true);
// 3. Initialize between_bb and line_bb
for (int sq1 = 0; sq1 < 64; sq1++) {
for (int sq2 = 0; sq2 < 64; sq2++) {
between_bb[sq1][sq2] = 0ULL;
line_bb[sq1][sq2] = 0ULL;
if (sq1 == sq2) continue;
int r1 = sq1 / 8, f1 = sq1 % 8;
int r2 = sq2 / 8, f2 = sq2 % 8;
int dr = r2 - r1;
int df = f2 - f1;
if (dr == 0) { // Same rank
line_bb[sq1][sq2] = 0xFFULL << (r1 * 8);
int step = (df > 0) ? 1 : -1;
for (int f = f1 + step; f != f2; f += step) {
between_bb[sq1][sq2] |= (1ULL << (r1 * 8 + f));
}
} else if (df == 0) { // Same file
line_bb[sq1][sq2] = 0x0101010101010101ULL << f1;
int step = (dr > 0) ? 1 : -1;
for (int r = r1 + step; r != r2; r += step) {
between_bb[sq1][sq2] |= (1ULL << (r * 8 + f1));
}
} else if (abs(dr) == abs(df)) { // Same diagonal
int step_r = (dr > 0) ? 1 : -1;
int step_f = (df > 0) ? 1 : -1;
// Precompute full line
for (int r = r1, f = f1; r >= 0 && r < 8 && f >= 0 && f < 8; r += step_r, f += step_f) {
line_bb[sq1][sq2] |= (1ULL << (r * 8 + f));
}
for (int r = r1 - step_r, f = f1 - step_f; r >= 0 && r < 8 && f >= 0 && f < 8; r -= step_r, f -= step_f) {
line_bb[sq1][sq2] |= (1ULL << (r * 8 + f));
}
for (int r = r1 + step_r, f = f1 + step_f; r != r2; r += step_r, f += step_f) {
between_bb[sq1][sq2] |= (1ULL << (r * 8 + f));
}
}
}
}
}
namespace {
void add_pawn_promotions(MoveList& list, int from, int to, bool capture) {
const int base = capture ? FLAG_PROMO_KNIGHT_CAP : FLAG_PROMO_KNIGHT;
list.add(Move(from, to, base));
list.add(Move(from, to, base + 1));
list.add(Move(from, to, base + 2));
list.add(Move(from, to, base + 3));
}
void generate_pawn_moves_oracle(const Board& board, MoveList& list) {
const Color us = board.get_side_to_move();
const Color them = Color(us ^ 1);
const U64 enemy = board.get_occupancy(them);
const U64 empty = ~board.get_occupancy(BOTH);
U64 pawns = board.get_pieces(us, PAWN);
while (pawns) {
const int from = pop_lsb(pawns);
const int rank = from / 8;
const int step = us == WHITE ? 8 : -8;
const int to = from + step;
if (to >= 0 && to < 64 && (empty & (1ULL << to))) {
if (to >= 56 || to <= 7)
add_pawn_promotions(list, from, to, false);
else
list.add(Move(from, to, FLAG_QUIET));
const int to2 = from + 2 * step;
if (((us == WHITE && rank == 1) || (us == BLACK && rank == 6))
&& (empty & (1ULL << to2)))
list.add(Move(from, to2, FLAG_DOUBLE_PAWN));
}
U64 attacks = pawn_attacks[us][from] & enemy;
while (attacks) {
const int target = pop_lsb(attacks);
if (target >= 56 || target <= 7)
add_pawn_promotions(list, from, target, true);
else
list.add(Move(from, target, FLAG_CAPTURE));
}
const int ep = board.get_en_passant_square();
if (ep != SQ_NONE && (pawn_attacks[us][from] & (1ULL << ep)))
list.add(Move(from, ep, FLAG_EP));
}
}
void generate_pawn_moves_setwise(const Board& board, MoveList& list) {
constexpr U64 RANK_3 = 0x0000000000FF0000ULL;
constexpr U64 RANK_6 = 0x0000FF0000000000ULL;
const Color us = board.get_side_to_move();
const Color them = Color(us ^ 1);
const U64 pawns = board.get_pieces(us, PAWN);
const U64 enemy = board.get_occupancy(them);
const U64 empty = ~board.get_occupancy(BOTH);
const int ep = board.get_en_passant_square();
const U64 single = us == WHITE ? (pawns << 8) & empty : (pawns >> 8) & empty;
const U64 doubles = us == WHITE ? ((single & RANK_3) << 8) & empty
: ((single & RANK_6) >> 8) & empty;
const U64 capture_targets = (us == WHITE ? pawn_attacks_white(pawns)
: pawn_attacks_black(pawns)) & enemy;
// Emit in the original source-square order. This retains tied move-order
// behavior while the availability masks themselves are computed setwise.
U64 sources = pawns;
while (sources) {
const int from = pop_lsb(sources);
const int step = us == WHITE ? 8 : -8;
const int to = from + step;
if (single & (1ULL << to)) {
if (to >= 56 || to <= 7)
add_pawn_promotions(list, from, to, false);
else
list.add(Move(from, to, FLAG_QUIET));
const int to2 = from + 2 * step;
if (to2 >= 0 && to2 < 64 && (doubles & (1ULL << to2)))
list.add(Move(from, to2, FLAG_DOUBLE_PAWN));
}
U64 attacks = pawn_attacks[us][from] & capture_targets;
while (attacks) {
const int target = pop_lsb(attacks);
if (target >= 56 || target <= 7)
add_pawn_promotions(list, from, target, true);
else
list.add(Move(from, target, FLAG_CAPTURE));
}
if (ep != SQ_NONE && (pawn_attacks[us][from] & (1ULL << ep)))
list.add(Move(from, ep, FLAG_EP));
}
}
#ifndef NDEBUG
bool same_move_set(const MoveList& lhs, const MoveList& rhs) {
if (lhs.count != rhs.count) return false;
std::array<bool, 65536> seen{};
for (int i = 0; i < lhs.count; ++i) {
if (seen[lhs.moves[i].value]) return false;
seen[lhs.moves[i].value] = true;
}
for (int i = 0; i < rhs.count; ++i) {
if (!seen[rhs.moves[i].value]) return false;
seen[rhs.moves[i].value] = false;
}
return true;
}
#endif
}
enum class GenerationMode : uint8_t { ALL, CAPTURES, QUIETS, NOISY };
inline bool includes_move(GenerationMode mode, Move move) {
switch (mode) {
case GenerationMode::CAPTURES: return move.is_capture();
case GenerationMode::QUIETS: return !move.is_capture();
case GenerationMode::NOISY: return move.is_capture() || move.is_promotion();
case GenerationMode::ALL: return true;
}
return false;
}
inline void emit_move(MoveList& list, GenerationMode mode, Move move) {
if (includes_move(mode, move))
list.add(move);
}
void generate_moves(const Board& board, MoveList& move_list, GenerationMode mode) {
Color us = board.get_side_to_move();
Color them = (Color)(us ^ 1);
U64 own_occ = board.get_occupancy(us);
U64 enemy_occ = board.get_occupancy(them);
U64 empty = ~board.get_occupancy(BOTH);
// 1. The setwise candidate is retained only as a debug differential
// oracle: it regressed release NPS, so production keeps the proven
// per-pawn emission path.
#ifndef NDEBUG
MoveList pawn_setwise;
generate_pawn_moves_setwise(board, pawn_setwise);
MoveList pawn_oracle;
generate_pawn_moves_oracle(board, pawn_oracle);
assert(same_move_set(pawn_setwise, pawn_oracle));
for (int i = 0; i < pawn_oracle.count; ++i)
emit_move(move_list, mode, pawn_oracle.moves[i]);
#else
if (mode == GenerationMode::ALL) {
generate_pawn_moves_oracle(board, move_list);
} else {
MoveList pawn_moves;
generate_pawn_moves_oracle(board, pawn_moves);
for (int i = 0; i < pawn_moves.count; ++i)
emit_move(move_list, mode, pawn_moves.moves[i]);
}
#endif
// 2. Knight Moves
U64 knights = board.get_pieces(us, KNIGHT);
while (knights) {
int from = pop_lsb(knights);
U64 attacks = knight_attacks[from] & ~own_occ;
U64 captures = attacks & enemy_occ;
U64 quiets = attacks & empty;
while (captures) {
int to = pop_lsb(captures);
emit_move(move_list, mode, Move(from, to, FLAG_CAPTURE));
}
while (quiets) {
int to = pop_lsb(quiets);
emit_move(move_list, mode, Move(from, to, FLAG_QUIET));
}
}
// 3. Bishop Moves
U64 bishops = board.get_pieces(us, BISHOP);
while (bishops) {
int from = pop_lsb(bishops);
U64 attacks = get_bishop_attacks(from, board.get_occupancy(BOTH)) & ~own_occ;
U64 captures = attacks & enemy_occ;
U64 quiets = attacks & empty;
while (captures) {
int to = pop_lsb(captures);
emit_move(move_list, mode, Move(from, to, FLAG_CAPTURE));
}
while (quiets) {
int to = pop_lsb(quiets);
emit_move(move_list, mode, Move(from, to, FLAG_QUIET));
}
}
// 4. Rook Moves
U64 rooks = board.get_pieces(us, ROOK);
while (rooks) {
int from = pop_lsb(rooks);
U64 attacks = get_rook_attacks(from, board.get_occupancy(BOTH)) & ~own_occ;
U64 captures = attacks & enemy_occ;
U64 quiets = attacks & empty;
while (captures) {
int to = pop_lsb(captures);
emit_move(move_list, mode, Move(from, to, FLAG_CAPTURE));
}
while (quiets) {
int to = pop_lsb(quiets);
emit_move(move_list, mode, Move(from, to, FLAG_QUIET));
}
}
// 5. Queen Moves
U64 queens = board.get_pieces(us, QUEEN);
while (queens) {
int from = pop_lsb(queens);
U64 attacks = get_queen_attacks(from, board.get_occupancy(BOTH)) & ~own_occ;
U64 captures = attacks & enemy_occ;
U64 quiets = attacks & empty;
while (captures) {
int to = pop_lsb(captures);
emit_move(move_list, mode, Move(from, to, FLAG_CAPTURE));
}
while (quiets) {
int to = pop_lsb(quiets);
emit_move(move_list, mode, Move(from, to, FLAG_QUIET));
}
}
// 6. King Moves & Castling
U64 king = board.get_pieces(us, KING);
if (king) {
int from = get_lsb(king);
U64 attacks = king_attacks[from] & ~own_occ;
U64 captures = attacks & enemy_occ;
U64 quiets = attacks & empty;
while (captures) {
int to = pop_lsb(captures);
emit_move(move_list, mode, Move(from, to, FLAG_CAPTURE));
}
while (quiets) {
int to = pop_lsb(quiets);
emit_move(move_list, mode, Move(from, to, FLAG_QUIET));
}
// Castling rights checking
int rights = board.get_castling_rights();
if (us == WHITE) {
// White King Castle
if (rights & WHITE_OO) {
if (!(board.get_occupancy(BOTH) & ((1ULL << SQ_F1) | (1ULL << SQ_G1)))) {
if (!board.is_square_attacked(SQ_E1, BLACK) && !board.is_square_attacked(SQ_F1, BLACK)) {
emit_move(move_list, mode, Move(SQ_E1, SQ_G1, FLAG_KING_CASTLE));
}
}
}
// White Queen Castle
if (rights & WHITE_OOO) {
if (!(board.get_occupancy(BOTH) & ((1ULL << SQ_D1) | (1ULL << SQ_C1) | (1ULL << SQ_B1)))) {
if (!board.is_square_attacked(SQ_E1, BLACK) && !board.is_square_attacked(SQ_D1, BLACK)) {
emit_move(move_list, mode, Move(SQ_E1, SQ_C1, FLAG_QUEEN_CASTLE));
}
}
}
} else { // us == BLACK
// Black King Castle
if (rights & BLACK_OO) {
if (!(board.get_occupancy(BOTH) & ((1ULL << SQ_F8) | (1ULL << SQ_G8)))) {
if (!board.is_square_attacked(SQ_E8, WHITE) && !board.is_square_attacked(SQ_F8, WHITE)) {
emit_move(move_list, mode, Move(SQ_E8, SQ_G8, FLAG_KING_CASTLE));
}
}
}
// Black Queen Castle
if (rights & BLACK_OOO) {
if (!(board.get_occupancy(BOTH) & ((1ULL << SQ_D8) | (1ULL << SQ_C8) | (1ULL << SQ_B8)))) {
if (!board.is_square_attacked(SQ_E8, WHITE) && !board.is_square_attacked(SQ_D8, WHITE)) {
emit_move(move_list, mode, Move(SQ_E8, SQ_C8, FLAG_QUEEN_CASTLE));
}
}
}
}
}
}
void generate_pseudo_legal_moves(const Board& board, MoveList& move_list) {
generate_moves(board, move_list, GenerationMode::ALL);
}
void generate_capture_moves(const Board& board, MoveList& move_list) {
generate_moves(board, move_list, GenerationMode::CAPTURES);
}
void generate_quiet_moves(const Board& board, MoveList& move_list) {
generate_moves(board, move_list, GenerationMode::QUIETS);
}
void generate_noisy_moves(const Board& board, MoveList& move_list) {
generate_moves(board, move_list, GenerationMode::NOISY);
}
void generate_evasion_moves(const Board& board, MoveList& move_list) {
// Legality filtering in make_move()/is_move_legal() removes candidates
// that do not evade check. Keeping the oracle result here provides a
// correctness boundary before introducing the dedicated fast backend.
generate_moves(board, move_list, GenerationMode::ALL);
}