const std = @import("std");
const Chess = @import("Chess.zig");
const Board = @import("Board.zig");
const GameState = @import("Board.zig").GameState;
pub var piece_hashes: [@typeInfo(Chess.PE).Enum.fields.len][@typeInfo(Board.Square).Enum.fields.len]u64 = undefined;
pub var enpassant_hashes: [@typeInfo(Board.Square).Enum.fields.len]u64 = undefined;
// 1111 -> 16
pub var castle_hashes: [16]u64 = undefined;
pub var side_hash: u64 = undefined;
pub fn init() void {
var rnd = std.rand.DefaultPrng.init(@intCast(u64, std.time.timestamp()));
for (piece_hashes) |pieces, i| {
for (pieces) |_, j| {
piece_hashes[i][j] = rnd.next();
}
}
for (enpassant_hashes) |*ep| {
ep.* = rnd.next();
}
for (castle_hashes) |*cas| {
cas.* = rnd.next();
}
side_hash = rnd.next();
}
pub fn updateHash(gs: *GameState) void {
gs.hash = 0; // starting poing
for (gs.bitboards) |bb, piece| {
var bitboard = @bitCast(Board.BoardType, bb);
while (bitboard != 0) {
const square = @ctz(bitboard);
gs.hash ^= piece_hashes[piece][square];
bitboard ^= @as(u64, 1) << @intCast(Board.SquareType, square);
}
}
if (gs.enpassant != null) {
gs.hash ^= enpassant_hashes[@enumToInt(gs.enpassant.?)];
}
if (@bitCast(Board.CastlingType, gs.castling) != 0) {
gs.hash ^= castle_hashes[@bitCast(Board.CastlingType, gs.castling)];
}
if (gs.side == .black) {
gs.hash ^= side_hash;
}
}
test "zobrist - parseFEN" {
var gs = try GameState.init(
std.testing.allocator,
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1", // tricky
);
const orig = gs.hash;
// side
try gs.parseFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R b KQkq - 0 1");
try std.testing.expect(orig != gs.hash);
// castling
try gs.parseFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w kq - 0 1");
try std.testing.expect(orig != gs.hash);
// enpassant
try gs.parseFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq c6 0 1");
try std.testing.expect(orig != gs.hash);
// move
try gs.parseFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2PQ2/2N211p/PPPBBPPP/R3K2R b KQkq - 0 1");
try std.testing.expect(orig != gs.hash);
// reset original
try gs.parseFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1");
try std.testing.expectEqual(orig, gs.hash);
}