BL3ZR4OWJM54HFXUNMUZKB5YQYVBT7ETFIXCOXWL6S5SZFM6IFDQC
RFXUGGUPAFJ7V6BTQZM4SQEAOFLDVWYI5ZIIK6K5EKFDXPPD2ZYQC
POPWRQGY3MKI5MYOLSI24NIBXDHRRX2OAH5UJF2N6MF6RHZQV5DAC
SC5KHHRYBEZ4RI4JL3MP34O44G3LXCEVVV5H5BB7XQEUJPNKA7HQC
A46B5KNQFPTZEIL2JZKD2CQELGU3COE6FGTV2ABS5U7DVTVDGEBQC
XCEYZZXOYMROQEI3QCO4N7JM2XPEOLSHE4ZNRWXGN3VFIO2XCBDQC
XLJOYA6SP5ICV4TTKWQ2PNTNZWYFCBAUEODKCBSYEVNREC3OKTOAC
const Colors = enum {
white,
black,
};
const Square = enum(BoardType) {
// zig fmt: off
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1,
// zig fmt: on
fn int(self: @This()) u6 {
return @intCast(u6, @enumToInt(self));
}
fn fileBits(file: u8) BoardType {
const offset = file - 'a';
std.debug.assert(offset < SIZE);
var ret: BoardType = 0;
var idx: u6 = 0;
while (idx < SIZE) : (idx += 1) {
ret += @as(BoardType, 1) << idx * SIZE + @intCast(u3, offset);
}
return ret;
}
fn rankBits(rank: u6) BoardType {
var ret: BoardType = 0;
std.debug.assert(rank > 0 and rank <= SIZE);
var idx: u6 = 0;
while (idx < SIZE) : (idx += 1) {
ret += @as(BoardType, 1) << idx + (SIZE - rank) * SIZE;
}
return ret;
}
};
const SIZE = 8;
const BoardType = u64;
const BitBoard = packed struct(BoardType) {
board: BoardType = 0,
fn set(self: *@This(), square: Square) void {
self.board |= @as(BoardType, 1) << square.int();
}
fn setSlice(self: *@This(), squares: []Square) void {
for (squares) |square| {
self.set(square);
}
}
fn isSet(self: @This(), square: Square) bool {
return (self.board & @as(BoardType, 1) << square.int() != 0);
}
fn unSet(self: *@This(), square: Square) void {
self.board &= @as(BoardType, 0) << square.int();
}
fn flip(self: *@This(), square: Square) void {
self.board ^= @as(BoardType, 1) << square.int();
}
// pop removes the bit if set
fn pop(self: *@This(), square: Square) void {
if (self.isSet(square))
self.board ^= @as(BoardType, 1) << square.int();
}
fn show(self: @This()) void {
std.debug.print("\n", .{});
var rank: usize = 0;
while (rank < SIZE) : (rank += 1) {
var file: usize = 0;
while (file < SIZE) : (file += 1) {
if (file == 0) std.debug.print("{d}| ", .{SIZE - rank});
const square = @intCast(u6, rank * SIZE + file);
const mask: usize = if (self.board & (@as(BoardType, 1) << square) != 0) 1 else 0;
std.debug.print("{d} ", .{mask});
}
std.debug.print("\n", .{});
}
std.debug.print("{s:>18}\n", .{"---------------"});
std.debug.print("{s:>18}\n", .{"a b c d e f g h"});
std.debug.print("\nBitBoard: {d}\n", .{self.board});
}
};
test "BitBoard" {
{ // set, unSet
var bb: BitBoard = .{};
try std.testing.expectEqual(@as(BoardType, 0), bb.board);
bb.set(.a8);
try std.testing.expectEqual(@as(BoardType, 1), bb.board);
bb.unSet(.a8);
bb.set(.h1);
const exp = @as(BoardType, 1) << @as(u6, 63);
try std.testing.expectEqual(exp, bb.board);
}
{ // flip
var bb: BitBoard = .{};
bb.set(.e4);
try std.testing.expect(bb.isSet(.e4));
bb.set(.f1);
try std.testing.expect(bb.isSet(.f1));
bb.flip(.e4);
bb.flip(.f1);
try std.testing.expectEqual(@as(BoardType, 0), bb.board);
bb.flip(.f1); // sets f1 again
try std.testing.expectEqual(
@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.f1)),
bb.board,
);
}
{ // pop
var bb: BitBoard = .{};
bb.set(.e4);
bb.set(.f1);
bb.pop(.e4);
bb.pop(.f1);
bb.set(.g3);
try std.testing.expectEqual(
@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.g3)),
bb.board,
);
bb.pop(.e4); // does nothing
try std.testing.expectEqual(
@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.g3)),
bb.board,
);
}
test "All" {
_ = @import("Chess.zig");
_ = @import("attacks.zig");
fn genPawnAttacks() [@typeInfo(Colors).Enum.fields.len][@typeInfo(Square).Enum.fields.len]BoardType {
var ret: [2][64]BoardType = [_][64]BoardType{[_]BoardType{0} ** 64} ** 2;
var bb: BitBoard = .{};
for (std.enums.values(Colors)) |color| {
for (std.enums.values(Square)) |square| {
bb.board = 0;
bb.set(square);
switch (color) {
.white => {
// towards lower file
if (bb.board & Square.fileBits('a') == 0)
ret[@enumToInt(color)][@enumToInt(square)] |= bb.board >> 9;
// towards higher file
if (bb.board & Square.fileBits('h') == 0)
ret[@enumToInt(color)][@enumToInt(square)] |= bb.board >> 7;
},
.black => {
// towards higher file
if (bb.board & Square.fileBits('h') == 0)
ret[@enumToInt(color)][@enumToInt(square)] |= bb.board << 9;
// towards lower file
if (bb.board & Square.fileBits('a') == 0)
ret[@enumToInt(color)][@enumToInt(square)] |= bb.board << 7;
},
}
}
}
return ret;
}
test "PawnAttacks" {
const pa = genPawnAttacks();
{ // White
{
var bb: BitBoard = .{};
bb.set(.c5);
bb.set(.e5);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Colors.white)][@enumToInt(Square.d4)],
);
}
{
// no left attack
var bb: BitBoard = .{};
bb.set(.b5);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Colors.white)][@enumToInt(Square.a4)],
);
}
{
// no right attack
var bb: BitBoard = .{};
bb.set(.g5);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Colors.white)][@enumToInt(Square.h4)],
);
}
}
{ // Black
{
var bb: BitBoard = .{};
bb.set(.c3);
bb.set(.e3);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Colors.black)][@enumToInt(Square.d4)],
);
}
{
// no left attack
var bb: BitBoard = .{};
bb.set(.b3);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Colors.black)][@enumToInt(Square.a4)],
);
}
{
// no right attack
var bb: BitBoard = .{};
bb.set(.g3);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Colors.black)][@enumToInt(Square.h4)],
);
}
}
}
fn genKnightAttacks() [@typeInfo(Square).Enum.fields.len]BoardType {
var ret: [64]BoardType = [_]BoardType{0} ** 64;
var bb: BitBoard = .{};
for (std.enums.values(Square)) |square| {
bb.board = 0;
bb.set(square);
// upward
if (bb.board & Square.fileBits('a') == 0)
ret[@enumToInt(square)] |= bb.board >> 17;
if (bb.board & Square.fileBits('h') == 0)
ret[@enumToInt(square)] |= bb.board >> 15;
if (bb.board & (Square.fileBits('a') | Square.fileBits('b')) == 0)
ret[@enumToInt(square)] |= bb.board >> 10;
if (bb.board & (Square.fileBits('h') | Square.fileBits('g')) == 0)
ret[@enumToInt(square)] |= bb.board >> 6;
// downward
if (bb.board & Square.fileBits('h') == 0)
ret[@enumToInt(square)] |= bb.board << 17;
if (bb.board & Square.fileBits('a') == 0)
ret[@enumToInt(square)] |= bb.board << 15;
if (bb.board & (Square.fileBits('h') | Square.fileBits('g')) == 0)
ret[@enumToInt(square)] |= bb.board << 10;
if (bb.board & (Square.fileBits('a') | Square.fileBits('b')) == 0)
ret[@enumToInt(square)] |= bb.board << 6;
}
return ret;
}
test "KnightAttacks" {
const ka = genKnightAttacks();
{ // full move
var bb: BitBoard = .{};
bb.set(.c7);
bb.set(.e7);
bb.set(.b6);
bb.set(.f6);
bb.set(.c3);
bb.set(.e3);
bb.set(.b4);
bb.set(.f4);
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Square.d5)],
);
}
{ // only 6 move
var bb: BitBoard = .{};
bb.set(.f7);
bb.set(.h7);
bb.set(.e6);
bb.set(.e4);
bb.set(.f3);
bb.set(.h3);
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Square.g5)],
);
}
{ // only 4 move
var bb: BitBoard = .{};
bb.set(.b7);
bb.set(.c6);
bb.set(.c4);
bb.set(.b3);
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Square.a5)],
);
}
}
fn genKingAttacks() [@typeInfo(Square).Enum.fields.len]BoardType {
var ret: [64]BoardType = [_]BoardType{0} ** 64;
var bb: BitBoard = .{};
for (std.enums.values(Square)) |square| {
bb.board = 0;
bb.set(square);
ret[@enumToInt(square)] |= bb.board >> 8;
if (bb.board & Square.fileBits('h') == 0)
ret[@enumToInt(square)] |= bb.board >> 7;
if (bb.board & Square.fileBits('a') == 0)
ret[@enumToInt(square)] |= (bb.board >> 9 | bb.board >> 1);
ret[@enumToInt(square)] |= bb.board << 8;
if (bb.board & Square.fileBits('a') == 0)
ret[@enumToInt(square)] |= bb.board << 7;
if (bb.board & Square.fileBits('h') == 0)
ret[@enumToInt(square)] |= (bb.board << 9 | bb.board << 1);
}
return ret;
}
test "KingAttacks" {
const ka = genKingAttacks();
{ // full move
var bb: BitBoard = .{};
bb.set(.c5);
bb.set(.d5);
bb.set(.e5);
bb.set(.c3);
bb.set(.d3);
bb.set(.e3);
bb.set(.c4);
bb.set(.e4);
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Square.d4)],
);
}
{ // sides - 5 moves
var bb: BitBoard = .{};
bb.set(.a5);
bb.set(.a3);
bb.set(.b5);
bb.set(.b4);
bb.set(.b3);
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Square.a4)],
);
}
{ // sides - 5 moves
var bb: BitBoard = .{};
bb.set(.c8);
bb.set(.e8);
bb.set(.c7);
bb.set(.d7);
bb.set(.e7);
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Square.d8)],
);
}
{ // corner - 3 moves
var bb: BitBoard = .{};
bb.set(.g1);
bb.set(.g2);
bb.set(.h2);
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Square.h1)],
);
}
}
fn genBishopAttacks() [@typeInfo(Square).Enum.fields.len]BoardType {
const moves = [_][2]i2{
.{ -1, -1 },
.{ -1, 1 },
.{ 1, -1 },
.{ 1, 1 },
};
var ret: [64]BoardType = [_]BoardType{0} ** 64;
for (std.enums.values(Square)) |square| {
const rank = @intCast(isize, SIZE - square.int() / SIZE);
const file = @intCast(isize, square.int() % SIZE);
var attacks: BoardType = 0;
var step: i5 = 1;
while (step < SIZE - 1) : (step += 1) {
for (moves) |m| {
const dr = rank + m[0] * step;
const df = file + m[1] * step;
if (dr < 2 or dr > 7) continue;
if (df < 1 or df > 7) continue;
const s = @intCast(u6, (SIZE - dr) * SIZE + df);
attacks |= @intCast(BoardType, @as(BoardType, 1) << s);
}
}
ret[@enumToInt(square)] = attacks;
}
return ret;
}
test "BishopAttacks" {
const ba = genBishopAttacks();
{
var bb: BitBoard = .{};
var set = [_]Square{ .b2, .c3, .d4, .e5, .f6, .g7 };
bb.setSlice(&set);
try std.testing.expectEqual(
bb.board,
ba[@enumToInt(Square.a1)],
);
}
}
const std = @import("std");
const Chess = @import("Chess.zig");
fn genPawnAttacks() [@typeInfo(Chess.Colors).Enum.fields.len][@typeInfo(Chess.Square).Enum.fields.len]Chess.BoardType {
var ret: [2][64]Chess.BoardType = [_][64]Chess.BoardType{[_]Chess.BoardType{0} ** 64} ** 2;
for (std.enums.values(Chess.Colors)) |color| {
for (std.enums.values(Chess.Square)) |square| {
ret[@enumToInt(color)][@enumToInt(square)] = genSteps(square, .pawn, color);
}
}
return ret;
}
test "PawnAttacks" {
var bb: Chess.BitBoard = .{};
const pa = genPawnAttacks();
{ // White
{
bb.board = 0;
bb.setSlice(&[2]Chess.Square{ .c5, .e5 });
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Chess.Colors.white)][@enumToInt(Chess.Square.d4)],
);
}
{
// no left attack
bb.board = 0;
bb.set(.b5);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Chess.Colors.white)][@enumToInt(Chess.Square.a4)],
);
}
{
// no right attack
bb.board = 0;
bb.set(.g5);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Chess.Colors.white)][@enumToInt(Chess.Square.h4)],
);
}
}
{ // Black
{
bb.board = 0;
bb.setSlice(&[2]Chess.Square{ .c3, .e3 });
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Chess.Colors.black)][@enumToInt(Chess.Square.d4)],
);
}
{
// no left attack
bb.board = 0;
bb.set(.b3);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Chess.Colors.black)][@enumToInt(Chess.Square.a4)],
);
}
{
// no right attack
bb.board = 0;
bb.set(.g3);
try std.testing.expectEqual(
bb.board,
pa[@enumToInt(Chess.Colors.black)][@enumToInt(Chess.Square.h4)],
);
}
}
}
fn genKnightAttacks() [@typeInfo(Chess.Square).Enum.fields.len]Chess.BoardType {
var ret: [64]Chess.BoardType = [_]Chess.BoardType{0} ** 64;
for (std.enums.values(Chess.Square)) |square| {
ret[@enumToInt(square)] = genSteps(square, .knight, .white);
}
return ret;
}
test "KnightAttacks" {
var bb: Chess.BitBoard = .{};
const ka = genKnightAttacks();
{ // full move
bb.board = 0;
bb.setSlice(&[8]Chess.Square{ .c7, .e7, .b6, .f6, .c3, .e3, .b4, .f4 });
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Chess.Square.d5)],
);
}
{ // only 6 move
bb.board = 0;
bb.setSlice(&[6]Chess.Square{ .f7, .h7, .e6, .e4, .f3, .h3 });
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Chess.Square.g5)],
);
}
{ // only 4 move
bb.board = 0;
bb.setSlice(&[4]Chess.Square{ .b7, .c6, .c4, .b3 });
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Chess.Square.a5)],
);
}
}
fn genKingAttacks() [@typeInfo(Chess.Square).Enum.fields.len]Chess.BoardType {
var ret: [64]Chess.BoardType = [_]Chess.BoardType{0} ** 64;
for (std.enums.values(Chess.Square)) |square| {
ret[@enumToInt(square)] = genSteps(square, .king, .white);
}
return ret;
}
test "KingAttacks" {
var bb: Chess.BitBoard = .{};
const ka = genKingAttacks();
{ // full move
bb.board = 0;
bb.setSlice(&[8]Chess.Square{ .c5, .d5, .e5, .c3, .d3, .e3, .c4, .e4 });
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Chess.Square.d4)],
);
}
{ // sides - 5 moves
bb.board = 0;
bb.setSlice(&[5]Chess.Square{ .a5, .a3, .b5, .b4, .b3 });
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Chess.Square.a4)],
);
}
{ // sides - 5 moves
bb.board = 0;
bb.setSlice(&[5]Chess.Square{ .c8, .e8, .c7, .d7, .e7 });
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Chess.Square.d8)],
);
}
{ // corner - 3 moves
bb.board = 0;
bb.setSlice(&[3]Chess.Square{ .g1, .g2, .h2 });
try std.testing.expectEqual(
bb.board,
ka[@enumToInt(Chess.Square.h1)],
);
}
}
fn genBishopAttacks() [@typeInfo(Chess.Square).Enum.fields.len]Chess.BoardType {
var ret: [64]Chess.BoardType = [_]Chess.BoardType{0} ** 64;
for (std.enums.values(Chess.Square)) |square| {
ret[@enumToInt(square)] = genSteps(square, .bishop, .white);
}
return ret;
}
test "BishopAttacks" {
const ba = genBishopAttacks();
{
var bb: Chess.BitBoard = .{};
var set = [_]Chess.Square{ .b2, .c3, .d4, .e5, .f6, .g7 };
bb.setSlice(&set);
try std.testing.expectEqual(
bb.board,
ba[@enumToInt(Chess.Square.a1)],
);
}
}
fn genSteps(square: Chess.Square, piece: Chess.Figures, color: Chess.Colors) Chess.BoardType {
const rank = @intCast(isize, square.rank());
const file = @intCast(isize, square.file());
const rider = switch (piece) {
.pawn => false,
.knight => false,
.bishop => true,
.king => false,
};
// 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 },
},
};
var attacks: Chess.BoardType = 0;
var step: i5 = 1;
while (step < Chess.SIZE - 1) : (step += 1) {
for (moves) |m| {
const dr = rank + m[0] * step;
const df = file + m[1] * step;
switch (rider) {
true => {
if (dr < 1 or dr > 6) continue;
if (df < 1 or df > 6) continue;
},
false => {
if (dr < 0 or dr > 7) continue;
if (df < 0 or df > 7) continue;
},
}
const s = @intCast(u6, dr * Chess.SIZE + df);
attacks |= @intCast(Chess.BoardType, @as(Chess.BoardType, 1) << s);
}
if (!rider) break;
}
return attacks;
}
const std = @import("std");
pub const SIZE = 8;
pub const BoardType = u64;
pub const Colors = enum {
white,
black,
};
pub const Figures = enum {
pawn,
knight,
bishop,
king,
};
pub const Square = enum(BoardType) {
// zig fmt: off
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1,
// zig fmt: on
pub fn int(self: @This()) u6 {
return @intCast(u6, @enumToInt(self));
}
pub fn rank(self: @This()) u3 {
return @intCast(u3, self.int() / SIZE);
}
pub fn file(self: @This()) u3 {
return @intCast(u3, self.int() % SIZE);
}
pub fn fileBits(fle: u8) BoardType {
const offset = fle - 'a';
std.debug.assert(offset < SIZE);
var ret: BoardType = 0;
var idx: u6 = 0;
while (idx < SIZE) : (idx += 1) {
ret += @as(BoardType, 1) << idx * SIZE + @intCast(u3, offset);
}
return ret;
}
pub fn rankBits(rnk: u6) BoardType {
var ret: BoardType = 0;
std.debug.assert(rnk > 0 and rnk <= SIZE);
var idx: u6 = 0;
while (idx < SIZE) : (idx += 1) {
ret += @as(BoardType, 1) << idx + (SIZE - rnk) * SIZE;
}
return ret;
}
};
pub const BitBoard = packed struct(BoardType) {
board: BoardType = 0,
pub fn set(self: *@This(), square: Square) void {
self.board |= @as(BoardType, 1) << square.int();
}
pub fn setSlice(self: *@This(), squares: []const Square) void {
for (squares) |square| {
self.set(square);
}
}
pub fn isSet(self: @This(), square: Square) bool {
return (self.board & @as(BoardType, 1) << square.int() != 0);
}
pub fn unSet(self: *@This(), square: Square) void {
self.board &= @as(BoardType, 0) << square.int();
}
pub fn flip(self: *@This(), square: Square) void {
self.board ^= @as(BoardType, 1) << square.int();
}
// pop removes the bit if set
pub fn pop(self: *@This(), square: Square) void {
if (self.isSet(square))
self.board ^= @as(BoardType, 1) << square.int();
}
pub fn show(self: @This()) void {
std.debug.print("\n", .{});
std.debug.print("{s:>18}\n", .{"0 1 2 3 4 5 6 7"});
std.debug.print("{s:>18}\n", .{"---------------"});
var rank: usize = 0;
while (rank < SIZE) : (rank += 1) {
var file: usize = 0;
while (file < SIZE) : (file += 1) {
if (file == 0) std.debug.print("{d}| ", .{rank});
const square = @intCast(u6, rank * SIZE + file);
const mask: usize = if (self.board & (@as(BoardType, 1) << square) != 0) 1 else 0;
std.debug.print("{d} ", .{mask});
}
std.debug.print("|{d}\n", .{SIZE - rank});
}
std.debug.print("{s:>18}\n", .{"---------------"});
std.debug.print("{s:>18}\n", .{"a b c d e f g h"});
std.debug.print("\nBitBoard: {d}\n", .{self.board});
}
};
test "BitBoard" {
{ // set, unSet
var bb: BitBoard = .{};
try std.testing.expectEqual(@as(BoardType, 0), bb.board);
bb.set(.a8);
try std.testing.expectEqual(@as(BoardType, 1), bb.board);
bb.unSet(.a8);
bb.set(.h1);
const exp = @as(BoardType, 1) << @as(u6, 63);
try std.testing.expectEqual(exp, bb.board);
}
{ // flip
var bb: BitBoard = .{};
bb.set(.e4);
try std.testing.expect(bb.isSet(.e4));
bb.set(.f1);
try std.testing.expect(bb.isSet(.f1));
bb.flip(.e4);
bb.flip(.f1);
try std.testing.expectEqual(@as(BoardType, 0), bb.board);
bb.flip(.f1); // sets f1 again
try std.testing.expectEqual(
@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.f1)),
bb.board,
);
}
{ // pop
var bb: BitBoard = .{};
bb.set(.e4);
bb.set(.f1);
bb.pop(.e4);
bb.pop(.f1);
bb.set(.g3);
try std.testing.expectEqual(
@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.g3)),
bb.board,
);
bb.pop(.e4); // does nothing
try std.testing.expectEqual(
@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.g3)),
bb.board,
);
}
}