BL3ZR4OWJM54HFXUNMUZKB5YQYVBT7ETFIXCOXWL6S5SZFM6IFDQC RFXUGGUPAFJ7V6BTQZM4SQEAOFLDVWYI5ZIIK6K5EKFDXPPD2ZYQC POPWRQGY3MKI5MYOLSI24NIBXDHRRX2OAH5UJF2N6MF6RHZQV5DAC SC5KHHRYBEZ4RI4JL3MP34O44G3LXCEVVV5H5BB7XQEUJPNKA7HQC A46B5KNQFPTZEIL2JZKD2CQELGU3COE6FGTV2ABS5U7DVTVDGEBQC XCEYZZXOYMROQEI3QCO4N7JM2XPEOLSHE4ZNRWXGN3VFIO2XCBDQC XLJOYA6SP5ICV4TTKWQ2PNTNZWYFCBAUEODKCBSYEVNREC3OKTOAC const Colors = enum {white,black,};const Square = enum(BoardType) {// zig fmt: offa8, 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: onfn 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 setfn 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, unSetvar 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);}{ // flipvar 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 againtry std.testing.expectEqual(@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.f1)),bb.board,);}{ // popvar 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 nothingtry 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 fileif (bb.board & Square.fileBits('a') == 0)ret[@enumToInt(color)][@enumToInt(square)] |= bb.board >> 9;// towards higher fileif (bb.board & Square.fileBits('h') == 0)ret[@enumToInt(color)][@enumToInt(square)] |= bb.board >> 7;},.black => {// towards higher fileif (bb.board & Square.fileBits('h') == 0)ret[@enumToInt(color)][@enumToInt(square)] |= bb.board << 9;// towards lower fileif (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 attackvar bb: BitBoard = .{};bb.set(.b5);try std.testing.expectEqual(bb.board,pa[@enumToInt(Colors.white)][@enumToInt(Square.a4)],);}{// no right attackvar 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 attackvar bb: BitBoard = .{};bb.set(.b3);try std.testing.expectEqual(bb.board,pa[@enumToInt(Colors.black)][@enumToInt(Square.a4)],);}{// no right attackvar 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);// upwardif (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;// downwardif (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 movevar 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 movevar 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 movevar 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 movevar 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 movesvar 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 movesvar 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 movesvar 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 attackbb.board = 0;bb.set(.b5);try std.testing.expectEqual(bb.board,pa[@enumToInt(Chess.Colors.white)][@enumToInt(Chess.Square.a4)],);}{// no right attackbb.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 attackbb.board = 0;bb.set(.b3);try std.testing.expectEqual(bb.board,pa[@enumToInt(Chess.Colors.black)][@enumToInt(Chess.Square.a4)],);}{// no right attackbb.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 movebb.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 movebb.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 movebb.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 movebb.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 movesbb.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 movesbb.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 movesbb.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: offa8, 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: onpub 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 setpub 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, unSetvar 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);}{ // flipvar 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 againtry std.testing.expectEqual(@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.f1)),bb.board,);}{ // popvar 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 nothingtry std.testing.expectEqual(@as(BoardType, @as(BoardType, 1) << @enumToInt(Square.g3)),bb.board,);}}