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(@as(u64, @intCast(std.time.timestamp()))); for (piece_hashes, 0..) |pieces, i| { for (pieces, 0..) |_, 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 point for (gs.bitboards, 0..) |bb, piece| { var bitboard = @as(Board.BoardType, @bitCast(bb)); while (bitboard != 0) { const square = @ctz(bitboard); gs.hash ^= piece_hashes[piece][square]; bitboard ^= @as(u64, 1) << @as(Board.SquareType, @intCast(square)); } } if (gs.enpassant != null) { gs.hash ^= enpassant_hashes[@intFromEnum(gs.enpassant.?)]; } gs.hash ^= castle_hashes[@as(Board.CastlingType, @bitCast(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 ); defer gs.deinit(); 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); }