M build.zig => build.zig +7 -0
@@ 24,4 24,11 @@ pub fn build(b: *Builder) void {
const run_step = b.step("run", "Run the app");
run_step.dependOn(&run_cmd.step);
+
+ const attack_tests = b.addTest("test/attack.zig");
+ attack_tests.addPackagePath("attack.zig", "src/attack.zig");
+ attack_tests.setBuildMode(mode);
+
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&attack_tests.step);
}
A src/attack.zig => src/attack.zig +31 -0
@@ 0,0 1,31 @@
+pub usingnamespace @import("board.zig");
+
+const mask_not_a = 0xfefefefefefefefe;
+const mask_not_h = 0x7f7f7f7f7f7f7f7f;
+
+const pawn_attacks = initPawnAttacks();
+
+fn initPawnAttacks() [2][64]u64 {
+ var sq: u6 = 0;
+ var attacks: [2][64]u64 = undefined;
+ while (sq < 63) : (sq += 1) {
+ attacks[0][sq] = maskPawnAttacks(Side.White, @intToEnum(Square, sq));
+ }
+ sq = 0;
+ while (sq < 63) : (sq += 1) {
+ attacks[1][sq] = maskPawnAttacks(Side.Black, @intToEnum(Square, sq));
+ }
+ return attacks;
+}
+
+fn maskPawnAttacks(side: Side, pawn: Square) u64 {
+ const bitboard = bitboardFromSquare(pawn);
+ return switch (side) {
+ Side.White => (bitboard >> 9 & mask_not_h) | (bitboard >> 7 & mask_not_a),
+ Side.Black => (bitboard << 9 & mask_not_a) | (bitboard << 7 & mask_not_h),
+ };
+}
+
+pub fn pawnAttacks(side: Side, pawn: Square) u64 {
+ return pawn_attacks[@enumToInt(side)][@enumToInt(pawn)];
+}
M src/board.zig => src/board.zig +24 -2
@@ 1,5 1,10 @@
const std = @import("std");
+pub const Side = enum(u1) {
+ White,
+ Black,
+};
+
// zig fmt: off
pub const Square = packed enum(u6) {
a8, b8, c8, d8, e8, f8, g8, h8,
@@ 39,10 44,11 @@ pub fn printBitboard(bitboard: u64) void {
while (file < 8) : (file += 1) {
const square = 8 * rank + file;
const occupied: u8 = if (getBit(bitboard, square)) 1 else 0;
- std.debug.print("{d}, ", .{occupied});
+ std.debug.print(" {d} ", .{occupied});
}
std.debug.print("\n", .{});
}
+ std.debug.print("bitboard: {x}\n", .{bitboard});
}
fn getBit(bitboard: u64, square: u6) bool {
@@ 50,11 56,27 @@ fn getBit(bitboard: u64, square: u6) bool {
return bitboard & mask != 0;
}
-pub fn setBit(bitboard: u64, square: Square) u64 {
+fn setBit(bitboard: u64, square: Square) u64 {
const mask = @as(u64, 1) << @enumToInt(square);
return bitboard | mask;
}
+fn setBits(bitboard: u64, squares: []const Square) u64 {
+ var new_bitboard = bitboard;
+ for (squares) |square| {
+ new_bitboard = setBit(new_bitboard, square);
+ }
+ return new_bitboard;
+}
+
+pub fn bitboardFromSquare(square: Square) u64 {
+ return setBit(@as(u64, 0), square);
+}
+
+pub fn bitboardFromSquares(squares: []const Square) u64 {
+ return setBits(@as(u64, 0), squares);
+}
+
pub fn resetBit(bitboard: u64, square: Square) u64 {
const mask = @as(u64, 1) << @enumToInt(square);
return if (getBit(bitboard, @enumToInt(square)))
M src/main.zig => src/main.zig +2 -5
@@ 1,5 1,6 @@
const std = @import("std");
const board = @import("board.zig");
+const attack = @import("attack.zig");
const VERSION = "0.0.1";
@@ 24,9 25,5 @@ fn uciLoop() !void {
}
pub fn main() !void {
- //try uciLoop();
- var bitboard: u64 = 0;
- bitboard = board.setBit(bitboard, board.Square.e4);
- bitboard = board.setBit(bitboard, board.Square.d5);
- board.printBitboard(bitboard);
+ //uciLoop();
}
A test/attack.zig => test/attack.zig +62 -0
@@ 0,0 1,62 @@
+usingnamespace @import("attack.zig");
+
+const std = @import("std");
+const expectEqual = std.testing.expectEqual;
+
+test "white pawn attacks two squares in front" {
+ const side = Side.White;
+ const pawn = Square.e2;
+ const expected_attacked_squares = [2]Square{ Square.d3, Square.f3 };
+ const expected = bitboardFromSquares(expected_attacked_squares[0..]);
+ const actual = pawnAttacks(side, pawn);
+ expectEqual(expected, actual);
+}
+
+test "black pawn attacks two squares in front" {
+ const side = Side.Black;
+ const pawn = Square.c7;
+ const expected_attacked_squares = [2]Square{ Square.b6, Square.d6 };
+ const expected = bitboardFromSquares(expected_attacked_squares[0..]);
+ const actual = pawnAttacks(side, pawn);
+ expectEqual(expected, actual);
+}
+
+test "white pawn on a file attacks only one square" {
+ const side = Side.White;
+ const pawn = Square.a4;
+ const expected = bitboardFromSquare(Square.b5);
+ const actual = pawnAttacks(side, pawn);
+ expectEqual(expected, actual);
+}
+
+test "white pawn on h file attacks only one square" {
+ const side = Side.White;
+ const pawn = Square.h7;
+ const expected = bitboardFromSquare(Square.g8);
+ const actual = pawnAttacks(side, pawn);
+ expectEqual(expected, actual);
+}
+
+test "black pawn on a file attacks only one square" {
+ const side = Side.Black;
+ const pawn = Square.a8;
+ const expected = bitboardFromSquare(Square.b7);
+ const actual = pawnAttacks(side, pawn);
+ expectEqual(expected, actual);
+}
+
+test "white pawn on 8th rank attacks nothing" {
+ const side = Side.White;
+ const pawn = Square.f8;
+ const expected = @as(u64, 0);
+ const actual = pawnAttacks(side, pawn);
+ expectEqual(expected, actual);
+}
+
+test "black pawn on 1st rank attacks nothing" {
+ const side = Side.Black;
+ const pawn = Square.g1;
+ const expected = @as(u64, 0);
+ const actual = pawnAttacks(side, pawn);
+ expectEqual(expected, actual);
+}