M src/attack.zig => src/attack.zig +89 -0
@@ 165,3 165,92 @@ pub fn maskRookOccupancy(rook: Square) u64 {
}
return resetBit(bitboard, rook);
}
+
+// Given a square occupied by a bishop and a bitboard of potentially blocking
+// pieces, return a bitboard of attack rays from the bishop which terminate if
+// the ray hits a block. The attack ray includes the blocking square.
+pub fn bishopAttackRays(bishop: Square, block: u64) u64 {
+ const sq = @enumToInt(bishop);
+ const block_minus_sq = resetBit(block, bishop);
+ const rank = @intCast(i8, sq / 8);
+ const file = @intCast(i8, sq % 8);
+ var bitboard: u64 = 0;
+ var r = rank;
+ var f = file;
+ while (r <= 7 and f <= 7) : ({
+ r += 1;
+ f += 1;
+ }) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ r = rank;
+ f = file;
+ while (r >= 0 and f <= 7) : ({
+ r -= 1;
+ f += 1;
+ }) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ r = rank;
+ f = file;
+ while (r <= 7 and f >= 0) : ({
+ r += 1;
+ f -= 1;
+ }) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ r = rank;
+ f = file;
+ while (r >= 0 and f >= 0) : ({
+ r -= 1;
+ f -= 1;
+ }) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ return resetBit(bitboard, bishop);
+}
+
+// Given a square occupied by a rook and a bitboard of potentially blocking
+// pieces, return a bitboard of attack rays from the rook which terminate if
+// the ray hits a block. The attack ray includes the blocking square.
+pub fn rookAttackRays(rook: Square, block: u64) u64 {
+ const sq = @enumToInt(rook);
+ const block_minus_sq = resetBit(block, rook);
+ const rank = @intCast(i8, sq / 8);
+ const file = @intCast(i8, sq % 8);
+ var bitboard: u64 = 0;
+ var f = file;
+ var r = rank;
+ while (r <= 7) : (r += 1) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ r = rank;
+ while (r >= 0) : (r -= 1) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ r = rank;
+ while (f >= 0) : (f -= 1) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ f = file;
+ while (f <= 7) : (f += 1) {
+ const bit = @as(u64, 1) << @intCast(u6, 8 * r + f);
+ bitboard |= bit;
+ if (bit & block_minus_sq != 0) break;
+ }
+ return resetBit(bitboard, rook);
+}
M test/attack.zig => test/attack.zig +51 -0
@@ 220,3 220,54 @@ test "rook in corner relevant occupancy bits" {
expectEqual(bitboardFromSquares(expected_squares[0..]), maskRookOccupancy(rook));
}
+
+test "bishop attack rays with blockers" {
+ const bishop = Square.e5;
+ const block_squares = [_]Square{
+ Square.g3,
+ Square.a1,
+ Square.d6,
+ };
+ const block = bitboardFromSquares(block_squares[0..]);
+
+ const expected_squares = [_]Square{
+ Square.f6,
+ Square.g7,
+ Square.h8,
+ Square.f4,
+ Square.g3,
+ Square.d4,
+ Square.c3,
+ Square.b2,
+ Square.a1,
+ Square.d6,
+ };
+
+ expectEqual(bitboardFromSquares(expected_squares[0..]), bishopAttackRays(bishop, block));
+}
+
+test "rook attack rays with blockers" {
+ const rook = Square.d5;
+ const block_squares = [_]Square{
+ Square.d8,
+ Square.g5,
+ Square.c5,
+ };
+ const block = bitboardFromSquares(block_squares[0..]);
+
+ const expected_squares = [_]Square{
+ Square.d6,
+ Square.d7,
+ Square.d8,
+ Square.e5,
+ Square.f5,
+ Square.g5,
+ Square.d4,
+ Square.d3,
+ Square.d2,
+ Square.d1,
+ Square.c5,
+ };
+
+ expectEqual(bitboardFromSquares(expected_squares[0..]), rookAttackRays(rook, block));
+}