const std = @import("std");
const Chess = @import("Chess.zig");
const Board = @import("Board.zig");
pub const Attacks = struct {
// leaper pieces
pawn_attacks: [2][64]Board.BoardType = genPawnAttacks(),
king_attacks: [64]Board.BoardType = genKingAttacks(),
knight_attacks: [64]Board.BoardType = genKnightAttacks(),
// rider pieces
const bishop_magic: [64]Board.BoardType = .{ 9241390835893862432, 9234710240556097536, 1227239730261824640, 577604527957049348, 6918659473418780672, 351888113804529668, 2328924575914001472, 4614013701991826049, 2331927815258656, 9016004274033184, 2305860618585916944, 648597650767120384, 577061163230053010, 579277706207166480, 4614220409926336512, 580964498095089664, 4616199239888937985, 1126041776325888, 616996207142899796, 10141929783173120, 5630608183136385, 36873230555873632, 181275456685879328, 576743335520240152, 2603115769260982529, 2380875882396914688, 2289189183030272, 171145581949894664, 1153203048319827969, 2305913930985283584, 72657961755345152, 1127003718435328, 148904939935000608, 282608955041792, 1153062518049211400, 10378688279935975552, 290279660069376, 290279660069376, 6922619801915492428, 6922619801915492428, 1158067358212916246, 2328924575914001472, 36319360414908928, 4611703757044990977, 1224980203021800448, 4521260535005248, 290561443953836160, 2310347987528229122, 2328924575914001472, 564053894369288, 1585408924206502016, 4675139272704, 144185593598050818, 7440091971840, 4612816883383795840, 9234710240556097536, 4614013701991826049, 580964498095089664, 83334186434036744, 648597650767120384, 17592766972164, 9439545093914103944, 2331927815258656, 9241390835893862432 };
const rook_magic: [64]Board.BoardType = .{ 9259401108760043536, 594475563268280320, 144124055069409314, 72068610631143424, 72076285769418752, 108090789187420288, 324265770315940096, 9367487500631408770, 2305983747248373760, 14051934662285148160, 4612249107028804096, 72198881416318976, 9223653529145639440, 4648277836300289160, 1153204079162360320, 167055404054872320, 4791830278402211904, 13511073762115648, 10090880114674926608, 18332157638541321, 4902733343504729120, 141287311278592, 876517475657892112, 573945342918788, 35804994879488, 9223688714458236480, 72906490033217536, 844463587201024, 290486580455932032, 144117389246857344, 18295937913061888, 5406589228312428673, 9223442407754825769, 4503874543034369, 48431846559596576, 9295438429141602308, 18018869587216385, 3377768473690120, 2233852801025, 4785075694604450, 36030189125550082, 9227875774059790342, 9223407221495332992, 18332157638541321, 1155182100580696192, 35201820918080, 4612820723034226768, 45041495719411716, 603668391936256, 18084767791612032, 4612249107028804096, 15276649741245483264, 288371148000592256, 14628817498200408128, 1127068272296960, 4657003602455658752, 88510690829569, 2535612326510617, 144124055069409314, 2377918195506874625, 4611967527763988485, 1181631959278293026, 9232412290735146116, 261213468561048706 };
const bishop_masks = genBishopMasks();
const rook_masks = genRookMasks();
const bishop_relevant_bits = genRelevantOccupancy(.bishop);
const rook_relevant_bits = genRelevantOccupancy(.rook);
var bishop_attacks: [64][512]Board.BoardType = undefined;
var rook_attacks: [64][4096]Board.BoardType = undefined;
pub fn init() @This() {
var attacks: @This() = .{};
// queen attacks are generated by merging bishop and rook attacks
initSliderAttacks(.bishop);
initSliderAttacks(.rook);
return attacks;
}
fn initSliderAttacks(piece: Chess.Pieces) void {
for (std.enums.values(Board.Square)) |square| {
const square_int = @intFromEnum(square);
const attack_mask = switch (piece) {
.bishop => bishop_masks[square_int],
.rook => rook_masks[square_int],
else => unreachable,
};
// init relevant occupancy bit count
const relevant_bits = @popCount(attack_mask);
const occupancy_idx = @as(Board.BoardType, 1) << @as(Board.SquareType, @intCast(relevant_bits));
var idx: usize = 0;
while (idx < occupancy_idx) : (idx += 1) {
switch (piece) {
.bishop => {
const occupancy = setOccupancy(idx, relevant_bits, attack_mask);
const res = @mulWithOverflow(occupancy, bishop_magic[square_int])[0];
const magic_index = res >>
@as(Board.SquareType, @intCast(@as(u7, 64) - bishop_relevant_bits[square_int]));
bishop_attacks[square_int][magic_index] =
attackWithBlocker(occupancy, square, .bishop);
},
.rook => {
const occupancy = setOccupancy(idx, relevant_bits, attack_mask);
const res = @mulWithOverflow(occupancy, rook_magic[@intFromEnum(square)])[0];
const magic_index = res >>
@as(Board.SquareType, @intCast(@as(u7, 64) - rook_relevant_bits[square_int]));
rook_attacks[square_int][magic_index] =
attackWithBlocker(occupancy, square, .rook);
},
else => unreachable,
}
}
}
}
pub fn getBishopAttacks(self: @This(), square: Board.SquareType, occupancy: Board.BoardType) *Board.BoardType {
_ = self;
var mut_occupancy: Board.BoardType = occupancy;
mut_occupancy &= bishop_masks[square];
mut_occupancy = @mulWithOverflow(mut_occupancy, bishop_magic[square])[0];
mut_occupancy >>= @as(Board.SquareType, @intCast(@as(u7, 64) -
bishop_relevant_bits[square]));
return &bishop_attacks[square][mut_occupancy];
}
pub fn getRookAttacks(self: @This(), square: Board.SquareType, occupancy: Board.BoardType) *Board.BoardType {
_ = self;
var mut_occupancy: Board.BoardType = occupancy;
mut_occupancy &= rook_masks[square];
mut_occupancy = @mulWithOverflow(mut_occupancy, rook_magic[square])[0];
mut_occupancy >>= @as(Board.SquareType, @intCast(@as(u7, 64) -
rook_relevant_bits[square]));
return &rook_attacks[square][mut_occupancy];
}
pub fn getQueenAttacks(self: @This(), square: Board.SquareType, occupancy: Board.BoardType) Board.BoardType {
return getBishopAttacks(self, square, occupancy).* |
getRookAttacks(self, square, occupancy).*;
}
};
test "getQueenAttacks" {
var attack = Attacks.init();
var occupancy = Board.BitBoard{};
occupancy.setSlice(&[_]Board.Square{ .d7, .f2, .c3, .f4 });
const got = attack.getQueenAttacks(
@intFromEnum(Board.Square.d4),
@as(Board.BoardType, @bitCast(occupancy)),
);
var expected = Board.BitBoard{};
expected.setSlice(&[_]Board.Square{ .h8, .a7, .d7, .g7, .b6, .d6, .f6, .c5, .d5, .e5, .a4, .b4, .c4, .e4, .f4, .c3, .d3, .e3, .d2, .f2, .d1 });
try std.testing.expectEqual(@as(Board.BoardType, @bitCast(expected)), got);
}
fn genPawnAttacks() [@typeInfo(Chess.Colors).Enum.fields.len][@typeInfo(Board.Square).Enum.fields.len]Board.BoardType {
var ret: [2][64]Board.BoardType = [_][64]Board.BoardType{[_]Board.BoardType{0} ** 64} ** 2;
for (std.enums.values(Chess.Colors)) |color| {
for (std.enums.values(Board.Square)) |square| {
ret[@intFromEnum(color)][@intFromEnum(square)] = genSteps(square, .pawn, color);
}
}
return ret;
}
test "PawnAttacks" {
const pa = genPawnAttacks();
{ // White
{
var bb = Board.BitBoard{};
bb.setSlice(&[2]Board.Square{ .c5, .e5 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
pa[@intFromEnum(Chess.Colors.white)][@intFromEnum(Board.Square.d4)],
);
}
{
// no left attack
var bb = Board.BitBoard{};
bb.b5 = true;
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
pa[@intFromEnum(Chess.Colors.white)][@intFromEnum(Board.Square.a4)],
);
}
{
// no right attack
var bb = Board.BitBoard{};
bb.g5 = true;
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
pa[@intFromEnum(Chess.Colors.white)][@intFromEnum(Board.Square.h4)],
);
}
}
{ // Black
{
var bb = Board.BitBoard{};
bb.setSlice(&[2]Board.Square{ .c3, .e3 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
pa[@intFromEnum(Chess.Colors.black)][@intFromEnum(Board.Square.d4)],
);
}
{
// no left attack
var bb = Board.BitBoard{};
bb.b3 = true;
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
pa[@intFromEnum(Chess.Colors.black)][@intFromEnum(Board.Square.a4)],
);
}
{
// no right attack
var bb = Board.BitBoard{};
bb.g3 = true;
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
pa[@intFromEnum(Chess.Colors.black)][@intFromEnum(Board.Square.h4)],
);
}
}
}
fn genKnightAttacks() [@typeInfo(Board.Square).Enum.fields.len]Board.BoardType {
var ret: [64]Board.BoardType = [_]Board.BoardType{0} ** 64;
for (std.enums.values(Board.Square)) |square| {
ret[@intFromEnum(square)] = genSteps(square, .knight, .white);
}
return ret;
}
test "KnightAttacks" {
const ka = genKnightAttacks();
{ // full move
var bb = Board.BitBoard{};
bb.setSlice(&[8]Board.Square{ .c7, .e7, .b6, .f6, .c3, .e3, .b4, .f4 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ka[@intFromEnum(Board.Square.d5)],
);
}
{ // only 6 move
var bb = Board.BitBoard{};
bb.setSlice(&[6]Board.Square{ .f7, .h7, .e6, .e4, .f3, .h3 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ka[@intFromEnum(Board.Square.g5)],
);
}
{ // only 4 move
var bb = Board.BitBoard{};
bb.setSlice(&[4]Board.Square{ .b7, .c6, .c4, .b3 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ka[@intFromEnum(Board.Square.a5)],
);
}
}
fn genKingAttacks() [@typeInfo(Board.Square).Enum.fields.len]Board.BoardType {
var ret: [64]Board.BoardType = [_]Board.BoardType{0} ** 64;
for (std.enums.values(Board.Square)) |square| {
ret[@intFromEnum(square)] = genSteps(square, .king, .white);
}
return ret;
}
test "KingAttacks" {
const ka = genKingAttacks();
{ // full move
var bb = Board.BitBoard{};
bb.setSlice(&[8]Board.Square{ .c5, .d5, .e5, .c3, .d3, .e3, .c4, .e4 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ka[@intFromEnum(Board.Square.d4)],
);
}
{ // sides - 5 moves
var bb = Board.BitBoard{};
bb.setSlice(&[5]Board.Square{ .a5, .a3, .b5, .b4, .b3 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ka[@intFromEnum(Board.Square.a4)],
);
}
{ // sides - 5 moves
var bb = Board.BitBoard{};
bb.setSlice(&[5]Board.Square{ .c8, .e8, .c7, .d7, .e7 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ka[@intFromEnum(Board.Square.d8)],
);
}
{ // corner - 3 moves
var bb = Board.BitBoard{};
bb.setSlice(&[3]Board.Square{ .g1, .g2, .h2 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ka[@intFromEnum(Board.Square.h1)],
);
}
}
fn genBishopMasks() [@typeInfo(Board.Square).Enum.fields.len]Board.BoardType {
var ret: [64]Board.BoardType = [_]Board.BoardType{0} ** 64;
for (std.enums.values(Board.Square)) |square| {
ret[@intFromEnum(square)] = genSteps(square, .bishop, .white);
}
return ret;
}
test "BishopMasks" {
const ba = genBishopMasks();
{
var bb = Board.BitBoard{};
bb.setSlice(&[_]Board.Square{ .b2, .c3, .d4, .e5, .f6, .g7 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ba[@intFromEnum(Board.Square.a1)],
);
}
}
fn genRookMasks() [@typeInfo(Board.Square).Enum.fields.len]Board.BoardType {
var ret: [64]Board.BoardType = [_]Board.BoardType{0} ** 64;
for (std.enums.values(Board.Square)) |square| {
ret[@intFromEnum(square)] = genSteps(square, .rook, .white);
}
return ret;
}
test "RookMasks" {
const ra = genRookMasks();
{
var bb = Board.BitBoard{};
bb.setSlice(&[_]Board.Square{ .b1, .c1, .d1, .e1, .f1, .g2, .g3, .g4, .g5, .g6, .g7 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ra[@intFromEnum(Board.Square.g1)],
);
}
{
var bb = Board.BitBoard{};
bb.setSlice(&[_]Board.Square{ .b1, .c1, .d1, .e1, .f1, .g1, .h2, .h3, .h4, .h5, .h6, .h7 });
try std.testing.expectEqual(
@as(Board.BoardType, @bitCast(bb)),
ra[@intFromEnum(Board.Square.h1)],
);
}
}
fn genSteps(square: Board.Square, piece: Chess.Pieces, color: Chess.Colors) Board.BoardType {
const rank = @as(isize, @intCast(square.rank()));
const file = @as(isize, @intCast(square.file()));
const rider = switch (piece) {
.pawn => false,
.knight => false,
.bishop => true,
.rook => true,
.king => false,
.queen => unreachable,
};
// TODO: I repeat some moves here to make zig happy, but ugly.
const moves = switch (piece) {
.pawn => switch (color) {
.white => [_][2]i3{ .{ -1, -1 }, .{ -1, 1 }, .{ -1, -1 }, .{ -1, 1 }, .{ -1, -1 }, .{ -1, 1 }, .{ -1, -1 }, .{ -1, 1 } },
.black => [_][2]i3{ .{ 1, -1 }, .{ 1, 1 }, .{ 1, -1 }, .{ 1, 1 }, .{ 1, -1 }, .{ 1, 1 }, .{ 1, -1 }, .{ 1, 1 } },
},
.knight => [_][2]i3{ .{ -2, -1 }, .{ -2, 1 }, .{ 2, -1 }, .{ 2, 1 }, .{ -1, -2 }, .{ 1, 2 }, .{ -1, 2 }, .{ 1, -2 } },
.bishop => [_][2]i3{ .{ -1, -1 }, .{ -1, 1 }, .{ 1, -1 }, .{ 1, 1 }, .{ -1, -1 }, .{ -1, 1 }, .{ 1, -1 }, .{ 1, 1 } },
.king => [_][2]i3{ .{ -1, -1 }, .{ -1, 1 }, .{ 1, -1 }, .{ 1, 1 }, .{ -1, 0 }, .{ 0, 1 }, .{ 1, 0 }, .{ 0, -1 } },
.rook => [_][2]i3{ .{ -1, 0 }, .{ 1, 0 }, .{ 0, -1 }, .{ 0, 1 }, .{ -1, 0 }, .{ 1, 0 }, .{ 0, -1 }, .{ 0, 1 } },
.queen => unreachable,
};
var attacks: Board.BoardType = 0;
@setEvalBranchQuota(10_000);
var step: i5 = 1;
while (step < Board.SIZE - 1) : (step += 1) {
for (moves) |m| {
const dr = rank + m[0] * step;
const df = file + m[1] * step;
switch (rider) {
true => {
if (m[0] > 0 and dr > 6) continue;
if (m[0] < 0 and dr < 1) continue;
if (m[1] > 0 and df > 6) continue;
if (m[1] < 0 and df < 1) continue;
},
false => {
if (dr < 0 or dr > 7) continue;
if (df < 0 or df > 7) continue;
},
}
const s = @as(Board.SquareType, @intCast(dr * Board.SIZE + df));
attacks |= @as(Board.BoardType, 1) << s;
}
if (!rider) break;
}
return attacks;
}
fn attackWithBlocker(blocks: Board.BoardType, square: Board.Square, piece: Chess.Pieces) Board.BoardType {
const rank = @as(isize, @intCast(square.rank()));
const file = @as(isize, @intCast(square.file()));
const moves = switch (piece) {
.bishop => [_][2]i3{
.{ -1, -1 },
.{ -1, 1 },
.{ 1, -1 },
.{ 1, 1 },
},
.rook => [_][2]i3{
.{ -1, 0 },
.{ 1, 0 },
.{ 0, -1 },
.{ 0, 1 },
},
else => unreachable,
};
var attacks: Board.BoardType = 0;
for (moves) |m| {
var step: i5 = 1;
while (step < Board.SIZE) : (step += 1) {
const dr = rank + m[0] * step;
const df = file + m[1] * step;
if (m[0] > 0 and dr > 7) break;
if (m[0] < 0 and dr < 0) break;
if (m[1] > 0 and df > 7) break;
if (m[1] < 0 and df < 0) break;
const s = @as(Board.SquareType, @intCast(dr * Board.SIZE + df));
const mask = @as(Board.BoardType, 1) << s;
attacks |= mask;
// stop when we hit a blocker
if (mask & blocks != 0) break;
}
}
return attacks;
}
test "attackWithBlocker" {
{ // bishop d4
var blocks = Board.BitBoard{};
blocks.setSlice(&[_]Board.Square{ .c3, .b6, .f2 });
const got = attackWithBlocker(@as(Board.BoardType, @bitCast(blocks)), .d4, .bishop);
var bb = Board.BitBoard{};
bb.setSlice(&[_]Board.Square{ .c3, .f2, .e3, .c5, .b6, .e5, .f6, .g7, .h8 });
try std.testing.expectEqual(bb, @as(Board.BitBoard, @bitCast(got)));
}
{ // rook d4
var blocks = Board.BitBoard{};
blocks.setSlice(&[_]Board.Square{ .b4, .d3, .d5 });
const got = attackWithBlocker(@as(Board.BoardType, @bitCast(blocks)), .d4, .rook);
var bb = Board.BitBoard{};
bb.setSlice(&[_]Board.Square{ .c4, .b4, .d3, .d5, .e4, .f4, .g4, .h4 });
try std.testing.expectEqual(bb, @as(Board.BitBoard, @bitCast(got)));
}
{ // rook h8
var blocks = Board.BitBoard{};
blocks.setSlice(&[_]Board.Square{.b8});
const got = attackWithBlocker(@as(Board.BoardType, @bitCast(blocks)), .h8, .rook);
var bb = Board.BitBoard{};
bb.setSlice(&[_]Board.Square{ .b8, .c8, .d8, .e8, .f8, .g8, .h7, .h6, .h5, .h4, .h3, .h2, .h1 });
try std.testing.expectEqual(bb, @as(Board.BitBoard, @bitCast(got)));
}
}
fn setOccupancy(index: usize, bits_in_mask: Board.BoardType, attack_mask: Board.BoardType) Board.BoardType {
var occupancy: Board.BoardType = 0;
var attack: Board.BoardType = attack_mask;
var count: Board.SquareType = 0;
while (count < bits_in_mask) : (count += 1) {
const square = @as(Board.SquareType, @intCast(@ctz(attack)));
// pop square bit
// if (attack & @as(Board.BoardType, 1) << square != 0)
attack ^= @as(Board.BoardType, 1) << square;
if (index & @as(Board.BoardType, 1) << count != 0) {
occupancy |= @as(Board.BoardType, 1) << square;
}
}
return occupancy;
}
test "occupancy bits" {
const attack_mask = genSteps(.a1, .rook, .white);
var occupancy: Board.BoardType = 0;
occupancy = setOccupancy(4095, @popCount(attack_mask), attack_mask);
var expected: Board.BitBoard = .{};
expected.setSlice(&[_]Board.Square{ .a2, .a3, .a4, .a5, .a6, .a7, .b1, .c1, .d1, .e1, .f1, .g1 });
try std.testing.expectEqual(expected, @as(Board.BitBoard, @bitCast(occupancy)));
}
fn genRelevantOccupancy(piece: Chess.Pieces) [@typeInfo(Board.Square).Enum.fields.len]u4 {
var ret: [64]u4 = [_]u4{0} ** 64;
for (std.enums.values(Board.Square)) |square| {
const count = @popCount(genSteps(square, piece, .white));
ret[@intFromEnum(square)] = @as(u4, @intCast(count));
}
return ret;
}
test "genRelevantOccupancy" {
{ // bishop
const bishop = genRelevantOccupancy(.bishop);
// zig fmt: off
const expected = [64]u4{
6, 5, 5, 5, 5, 5, 5, 6,
5, 5, 5, 5, 5, 5, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 9, 9, 7, 5, 5,
5, 5, 7, 7, 7, 7, 5, 5,
5, 5, 5, 5, 5, 5, 5, 5,
6, 5, 5, 5, 5, 5, 5, 6,
};
// zig fmt: on
try std.testing.expectEqualSlices(u4, &expected, &bishop);
}
{ // rook
const rook = genRelevantOccupancy(.rook);
// zig fmt: off
const expected = [64]u4{
12, 11, 11, 11, 11, 11, 11, 12,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
11, 10, 10, 10, 10, 10, 10, 11,
12, 11, 11, 11, 11, 11, 11, 12,
};
// zig fmt: on
try std.testing.expectEqualSlices(u4, &expected, &rook);
}
}
fn findMagicNumber(square: Board.Square, relevant: u4, piece: Chess.Pieces) Board.BoardType {
// this is for rooks, bishop needs less
var occupancies: [4096]Board.BoardType = undefined;
var attacks: [4096]Board.BoardType = undefined;
var used: [4096]Board.BoardType = undefined;
// attack mask for a current piece
const attack_mask = genSteps(square, piece, .white);
const occupancy_idx = @as(Board.BoardType, 1) << relevant;
var idx: usize = 0;
while (idx < occupancy_idx) : (idx += 1) {
occupancies[idx] = setOccupancy(idx, relevant, attack_mask);
attacks[idx] = attackWithBlocker(occupancies[idx], square, piece);
}
var rnd = std.rand.DefaultPrng.init(0);
// test the generated number
var loop: usize = 0;
while (loop < 100_000_000) : (loop += 1) {
const magic = rnd.next() & rnd.next() & rnd.next();
// skip inappropriate numbers FIXME: ugly
const tmp1 = @mulWithOverflow(attack_mask, magic)[0];
if (@popCount(tmp1 & 0xFF00000000000000) < 6) continue;
used = [_]Board.BoardType{0} ** 4096;
var index: usize = 0;
var fail: bool = false;
while (!fail and index < occupancy_idx) : (index += 1) {
const tmp2 = @mulWithOverflow(occupancies[index], magic)[0];
var magic_index: usize = @as(usize, @intCast(tmp2 >>
@as(Board.SquareType, @intCast(@as(u7, 64) - relevant))));
if (used[magic_index] == 0) {
// magic index works
used[magic_index] = attacks[index];
} else if (used[magic_index] != attacks[index]) {
// magic index does not work
fail = true;
}
}
if (!fail) {
return magic;
}
}
unreachable;
}
test "BishopMagicNumbers" {
if (true) return error.SkipZigTest; // slow
var bishop_magic: [64]Board.BoardType = undefined;
for (std.enums.values(Board.Square), 0..) |square, idx| {
bishop_magic[idx] = findMagicNumber(
square,
genRelevantOccupancy(.bishop)[@intFromEnum(square)],
.bishop,
);
}
const expected_bishop_magic: [64]Board.BoardType = .{ 9241390835893862432, 9234710240556097536, 1227239730261824640, 577604527957049348, 6918659473418780672, 351888113804529668, 2328924575914001472, 4614013701991826049, 2331927815258656, 9016004274033184, 2305860618585916944, 648597650767120384, 577061163230053010, 579277706207166480, 4614220409926336512, 580964498095089664, 4616199239888937985, 1126041776325888, 616996207142899796, 10141929783173120, 5630608183136385, 36873230555873632, 181275456685879328, 576743335520240152, 2603115769260982529, 2380875882396914688, 2289189183030272, 171145581949894664, 1153203048319827969, 2305913930985283584, 72657961755345152, 1127003718435328, 148904939935000608, 282608955041792, 1153062518049211400, 10378688279935975552, 290279660069376, 290279660069376, 6922619801915492428, 6922619801915492428, 1158067358212916246, 2328924575914001472, 36319360414908928, 4611703757044990977, 1224980203021800448, 4521260535005248, 290561443953836160, 2310347987528229122, 2328924575914001472, 564053894369288, 1585408924206502016, 4675139272704, 144185593598050818, 7440091971840, 4612816883383795840, 9234710240556097536, 4614013701991826049, 580964498095089664, 83334186434036744, 648597650767120384, 17592766972164, 9439545093914103944, 2331927815258656, 9241390835893862432 };
try std.testing.expectEqualSlices(Board.BoardType, &expected_bishop_magic, &bishop_magic);
}
test "RookMagicNumbers" {
if (true) return error.SkipZigTest; // slow
var rook_magic: [64]Board.BoardType = undefined;
for (std.enums.values(Board.Square), 0..) |square, idx| {
rook_magic[idx] = findMagicNumber(
square,
genRelevantOccupancy(.rook)[@intFromEnum(square)],
.rook,
);
}
const expected_rook_magic: [64]Board.BoardType = .{ 9259401108760043536, 594475563268280320, 144124055069409314, 72068610631143424, 72076285769418752, 108090789187420288, 324265770315940096, 9367487500631408770, 2305983747248373760, 14051934662285148160, 4612249107028804096, 72198881416318976, 9223653529145639440, 4648277836300289160, 1153204079162360320, 167055404054872320, 4791830278402211904, 13511073762115648, 10090880114674926608, 18332157638541321, 4902733343504729120, 141287311278592, 876517475657892112, 573945342918788, 35804994879488, 9223688714458236480, 72906490033217536, 844463587201024, 290486580455932032, 144117389246857344, 18295937913061888, 5406589228312428673, 9223442407754825769, 4503874543034369, 48431846559596576, 9295438429141602308, 18018869587216385, 3377768473690120, 2233852801025, 4785075694604450, 36030189125550082, 9227875774059790342, 9223407221495332992, 18332157638541321, 1155182100580696192, 35201820918080, 4612820723034226768, 45041495719411716, 603668391936256, 18084767791612032, 4612249107028804096, 15276649741245483264, 288371148000592256, 14628817498200408128, 1127068272296960, 4657003602455658752, 88510690829569, 2535612326510617, 144124055069409314, 2377918195506874625, 4611967527763988485, 1181631959278293026, 9232412290735146116, 261213468561048706 };
try std.testing.expectEqualSlices(Board.BoardType, &expected_rook_magic, &rook_magic);
}