~macaptain/lkdo-chess

0bf3bc692f7cf1486a36d984db92c580feb6634c — Michael Captain 8 months ago b56c0cd
Add pawn attack tables and tests
5 files changed, 126 insertions(+), 7 deletions(-)

M build.zig
A src/attack.zig
M src/board.zig
M src/main.zig
A test/attack.zig
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);
}