192 lines
5.9 KiB
Zig
192 lines
5.9 KiB
Zig
const std = @import("std");
|
|
const types = @import("types.zig");
|
|
|
|
pub fn moveTypeToStr(move: types.move, buf: []u8) void {
|
|
const xTo = @mod(move.To, 8);
|
|
const yTo = @divTrunc(move.To, 8);
|
|
const xFrom = @mod(move.From, 8);
|
|
const yFrom = @divTrunc(move.From, 8);
|
|
|
|
buf[0] = @intCast(xFrom + 'a');
|
|
buf[1] = @intCast(yFrom + '0' + 1);
|
|
buf[2] = @intCast(xTo + 'a');
|
|
buf[3] = @intCast(yTo + '0' + 1);
|
|
buf[4] = move.Promo;
|
|
}
|
|
|
|
pub fn playMoves(game: *types.game, str: []const u8) *types.game {
|
|
if (str.len < 4) return game;
|
|
var splitItr = std.mem.splitSequence(u8, str, " ");
|
|
while (splitItr.next()) |moveString| {
|
|
if (moveString.len < 4 or moveString[0] > 'h')
|
|
continue;
|
|
var move: types.move = .{ .To = 0, .From = 0, .Promo = 0 };
|
|
if (moveString.len < 4) continue;
|
|
move.From = (moveString[0] - 'a') + (moveString[1] - '1') * 8;
|
|
move.To = (moveString[2] - 'a') + (moveString[3] - '1') * 8;
|
|
move.Promo = if (moveString.len == 5) moveString[4] else 0;
|
|
makeMove(@ptrCast(game), @ptrCast(&move));
|
|
}
|
|
return game;
|
|
}
|
|
|
|
fn fullSet(set: *types.set) u64 {
|
|
return set.bishops | set.king | set.knights | set.pawns | set.queen | set.rooks;
|
|
}
|
|
|
|
fn findSet(game: *types.game, bit: u64) ?*u64 {
|
|
if (game.white.pawns & bit != 0) {
|
|
return &game.white.pawns;
|
|
} else if (game.white.knights & bit != 0) {
|
|
return &game.white.knights;
|
|
} else if (game.white.bishops & bit != 0) {
|
|
return &game.white.bishops;
|
|
} else if (game.white.rooks & bit != 0) {
|
|
return &game.white.rooks;
|
|
} else if (game.white.queen & bit != 0) {
|
|
return &game.white.queen;
|
|
} else if (game.white.king & bit != 0) {
|
|
return &game.white.king;
|
|
} else if (game.black.pawns & bit != 0) {
|
|
return &game.black.pawns;
|
|
} else if (game.black.knights & bit != 0) {
|
|
return &game.black.knights;
|
|
} else if (game.black.bishops & bit != 0) {
|
|
return &game.black.bishops;
|
|
} else if (game.black.rooks & bit != 0) {
|
|
return &game.black.rooks;
|
|
} else if (game.black.queen & bit != 0) {
|
|
return &game.black.queen;
|
|
} else if (game.black.king & bit != 0) {
|
|
return &game.black.king;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
fn makeMove(game: *types.game, move: *types.move) void {
|
|
const from = @as(u64, 1) << @truncate(move.From);
|
|
const to = @as(u64, 1) << @truncate(move.To);
|
|
const set = findSet(game, from);
|
|
if (set == null)
|
|
return;
|
|
set.?.* &= ~from;
|
|
|
|
const cap = findSet(game, to);
|
|
if (cap != null)
|
|
cap.?.* &= ~to;
|
|
|
|
if (move.Promo != 0) {
|
|
charToSet(game, move.Promo).* |= to;
|
|
} else {
|
|
set.?.* |= to;
|
|
}
|
|
|
|
game.whiteToMove = !game.whiteToMove;
|
|
}
|
|
|
|
pub fn charToSet(g: *types.game, chr: u8) *u64 {
|
|
return switch (chr) {
|
|
'P' => &g.white.pawns,
|
|
'N' => &g.white.knights,
|
|
'B' => &g.white.bishops,
|
|
'R' => &g.white.rooks,
|
|
'Q' => &g.white.queen,
|
|
'K' => &g.white.king,
|
|
'p' => &g.black.pawns,
|
|
'n' => &g.black.knights,
|
|
'b' => &g.black.bishops,
|
|
'r' => &g.black.rooks,
|
|
'q' => &g.black.queen,
|
|
'k' => &g.black.king,
|
|
else => {
|
|
std.log.err("you should not be here ${c}$", .{chr});
|
|
unreachable;
|
|
},
|
|
};
|
|
}
|
|
|
|
// pub fn pawnMove(arr: std.ArrayList(c.move)) void {
|
|
// const move = c.move{ .From = 8, .To = 16, .Promo = 0 };
|
|
// }
|
|
|
|
pub fn sqrScan(rook: u64, hit: u64, flip: bool) u64 {
|
|
const vecLut: [64]u64 = undefined; //todo comptime init
|
|
|
|
var center = rook;
|
|
var hits = hit;
|
|
if (flip) {
|
|
center = @bitReverse(rook);
|
|
hits = @bitReverse(hit);
|
|
}
|
|
|
|
const vec = vecLut[@ctz(center)];
|
|
|
|
var attackedBits = hits & vec;
|
|
const n = @popCount(attackedBits);
|
|
var min: u64 = vec;
|
|
for (0..n) |_| {
|
|
const pos = @ctz(attackedBits);
|
|
const bit = @as(u64, 1) << pos;
|
|
attackedBits = attackedBits & ~bit;
|
|
if (bit - 1 < min) min = bit - 1;
|
|
}
|
|
|
|
return min;
|
|
}
|
|
|
|
pub fn knightMove(g: *types.game, arr: *std.ArrayList(types.move)) void {
|
|
const moveLut: [64]u64 = comptime blk: {
|
|
var value: [64]u64 = undefined;
|
|
for (0..64) |i| {
|
|
value[i] = knightCalc(@truncate(i));
|
|
}
|
|
break :blk value;
|
|
};
|
|
const set = if (g.whiteToMove) &g.white.knights else &g.black.knights;
|
|
const fset: u64 = if (g.whiteToMove) @as(u64, fullSet(@ptrCast(&g.white))) else @as(u64, fullSet(@ptrCast(&g.black)));
|
|
var val = set.*; //local copy
|
|
const n = @popCount(val);
|
|
for (0..n) |_| {
|
|
const pos = @ctz(val);
|
|
const moves = moveLut[pos] & ~fset;
|
|
val = val ^ (@as(u64, 1) << @truncate(pos));
|
|
bitboardToMoves(pos, moves, arr);
|
|
}
|
|
}
|
|
|
|
fn knightCalc(index: u8) u64 {
|
|
var moves: u64 = 0;
|
|
const offsets = [_]i8{ 17, -17, 15, -15, 10, -10, 6, -6 };
|
|
var cnt: u8 = undefined;
|
|
for (offsets) |off| {
|
|
if (off > 0) {
|
|
cnt = index + @abs(off);
|
|
} else if (index > @abs(off)) { //Icky bad that zig makes me do
|
|
cnt = index - @abs(off);
|
|
}
|
|
const fromFile: i32 = index % 8;
|
|
const toFile: i32 = cnt % 8;
|
|
const fromRank: i32 = index / 8;
|
|
const toRank: i32 = cnt / 8;
|
|
|
|
const fileDiff = @abs(fromFile - toFile);
|
|
const rankDiff = @abs(fromRank - toRank);
|
|
if (!((fileDiff == 1 and rankDiff == 2) or
|
|
(fileDiff == 2 and rankDiff == 1)) or cnt > 63)
|
|
continue;
|
|
moves = moves | @as(u64, 1) << @truncate(cnt);
|
|
}
|
|
return moves;
|
|
}
|
|
|
|
fn bitboardToMoves(start: u8, moves: u64, arr: *std.ArrayList(types.move)) void {
|
|
var lmoves = moves;
|
|
while (lmoves != 0) {
|
|
const pos = @ctz(lmoves);
|
|
const m: types.move = .{ .From = start, .Promo = 0, .To = pos };
|
|
lmoves = lmoves & ~@as(u64, 1) << @truncate(pos);
|
|
arr.append(m) catch unreachable;
|
|
}
|
|
}
|