G4HJL4QLASCZBWYGCEXYYRBYL7UVX6ENELHRRBFB5UAXXGVVGZGQC 5VEZOTVGONDXPL6WC7UWJ4UZXAVJDKOAPC6ATCLSWDDH7AML57UQC WZNDXPQM23LLYDZ3ZCTB4GOVRJLL7PMSEPSYBVFX6AOTJOZ5YYZQC A46B5KNQFPTZEIL2JZKD2CQELGU3COE6FGTV2ABS5U7DVTVDGEBQC GLTWBA5NKQIHAL23SWKCFOZQH62W6B3LX4RC5GFQUEQUIYN4OKSAC FOORIA7SEZCLKDBNMV6KEDTQOJJVAH57BQFBRSQMET6FERJPHCVQC BL3ZR4OWJM54HFXUNMUZKB5YQYVBT7ETFIXCOXWL6S5SZFM6IFDQC DKK2G2S3X7KFEJBHGLUEVTBBW6UBR36XR7ZBUWG5GO6JYXWGJOOAC TB4YBE4CMWCLSKJ43QF6IU5HVYUUO33BLXVG7XDRLJS3IFIBQLYAC THYIOCFC72V4ZMTLMGRGRIBARXT2LPSQCL2XNOIBI22QRVYUMIYQC NYVHTMLLUMBKBQSWO6SQDQ5KFACQLNCBRDDM5BH3OU6WWT54KC2AC LWF4H4TMNZYUKTCVDALXQLBE3U5AXQJ3LLNUJJ7YGTLG6T2O33JAC 6S6HHKOXBQ5ARL7S7PKMPDIAH3VTPOAPWWSMGY37RP2R2U662FBAC const std = @import("std");const Chess = @import("Chess.zig");const Board = @import("Board.zig");const nnue = @cImport({@cInclude("nnue.h");});// https://tests.stockfishchess.org/nnsconst NNUE_FILE = "nn-62ef826d1a6d.nnue";test "NNUE" {nnue.nnue_init(NNUE_FILE);try std.testing.expectEqual(@as(c_int, 57),nnue.nnue_evaluate_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"),);}// convert our piece codes to Stockfish NNUE codesconst NNUE_PIECES: [12]c_int = .{ 6, 5, 4, 3, 2, 1, 12, 11, 10, 9, 8, 7 };// convert our square indices to Stockfish NNUE indicesconst NNUE_SQUARES: [64]Board.Square = .{// zig fmt: off.a1, .b1, .c1, .d1, .e1, .f1, .g1, .h1,.a2, .b2, .c2, .d2, .e2, .f2, .g2, .h2,.a3, .b3, .c3, .d3, .e3, .f3, .g3, .h3,.a4, .b4, .c4, .d4, .e4, .f4, .g4, .h4,.a5, .b5, .c5, .d5, .e5, .f5, .g5, .h5,.a6, .b6, .c6, .d6, .e6, .f6, .g6, .h6,.a7, .b7, .c7, .d7, .e7, .f7, .g7, .h7,.a8, .b8, .c8, .d8, .e8, .f8, .g8, .h8,};// zig fmt: ontest "NNUE_SQUARES" {{const square = @enumToInt(Board.Square.a8);const nnue_square_enum = NNUE_SQUARES[square];std.debug.assert(nnue_square_enum == Board.Square.a1);}{const square = @enumToInt(Board.Square.f3);const nnue_square_enum = NNUE_SQUARES[square];std.debug.assert(nnue_square_enum == Board.Square.f6);}}// 2*(pawns(8), knights(2), bishops(2), rooks(2), queen, king)// +1 for the ending empty piece that Stockfish nnue expectsconst PIECE_COUNT = 2 * 8 + 2 * 2 + 2 * 2 + 2 * 2 + 2 + 2 + 1;var nnue_initialized = false;pub fn evaluate(gs: *const Board.GameState) isize {std.debug.assert(PIECE_COUNT == 33);var sf_pieces: [PIECE_COUNT]c_int = undefined;var sf_squares: [PIECE_COUNT]c_int = undefined;var index: usize = 2; // 0 and 1 are reserved for the two kingsvar bitboard: Board.BoardType = undefined;for (gs.bitboards) |bb, idx| {bitboard = @bitCast(Board.BoardType, bb);while (bitboard != 0) {const piece = @intToEnum(Chess.PE, idx);const square = @ctz(bitboard);switch (piece) {.K => {sf_pieces[0] = NNUE_PIECES[@enumToInt(piece)];sf_squares[0] = @enumToInt(NNUE_SQUARES[square]);},.k => {sf_pieces[1] = NNUE_PIECES[@enumToInt(piece)];sf_squares[1] = @enumToInt(NNUE_SQUARES[square]);},else => {sf_pieces[index] = NNUE_PIECES[@enumToInt(piece)];sf_squares[index] = @enumToInt(NNUE_SQUARES[square]);index += 1;},}bitboard ^= @as(Board.BoardType, 1) << @intCast(Board.SquareType, square);}}// zero shows Stockfish nnue that there are no more pieces.sf_pieces[index] = 0;sf_squares[index] = 0;if (!nnue_initialized) {nnue.nnue_init(NNUE_FILE);nnue_initialized = true;}return @intCast(isize, nnue.nnue_evaluate(@enumToInt(gs.side), &sf_pieces, &sf_squares));}test "nnue.evaluate" {var gs = try Board.GameState.init(null);try std.testing.expectEqual(@as(isize, 57), evaluate(&gs));}
score += MATERIAL_SCORE[idx] * bits;while (board != 0) {const bit = @ctz(board);score += switch (@intToEnum(Chess.PE, idx)) {.P => PAWN_SCORE[bit],.N => KNIGHT_SCORE[bit],.B => BISHOP_SCORE[bit],.R => ROOK_SCORE[bit],.K => KING_SCORE[bit],.Q => break,.p => -PAWN_SCORE[@enumToInt(MIRROR_SCORE[bit])],.n => -KNIGHT_SCORE[@enumToInt(MIRROR_SCORE[bit])],.b => -BISHOP_SCORE[@enumToInt(MIRROR_SCORE[bit])],.r => -ROOK_SCORE[@enumToInt(MIRROR_SCORE[bit])],.k => -KING_SCORE[@enumToInt(MIRROR_SCORE[bit])],.q => break,else => 0,};board ^= @as(Board.BoardType, 1) << @intCast(u6, bit);}}switch (gs.side) {.white => return score,.black => return -score,}}test "evaluate" {{// remove white queenvar gs = try Board.GameState.init("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR w KQkq - 0 1");try std.testing.expectEqual(@as(isize, -1000), evaluate(&gs));}{// remove white queen, but black comesvar gs = try Board.GameState.init("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR b KQkq - 0 1");try std.testing.expectEqual(@as(isize, 1000), evaluate(&gs));}{// Pe2e4var gs = try Board.GameState.init("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1");try std.testing.expectEqual(@as(isize, 30), evaluate(&gs));}{// Pe2e4 pe7e5 Ng1f3var gs = try Board.GameState.init("rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1");// gs.show();try std.testing.expectEqual(@as(isize, 30), evaluate(&gs));}}
fn evaluate(self: @This()) isize {var score: isize = 0;for (self.bitboards) |bb, idx| {var board = @bitCast(BoardType, bb);const bits = @popCount(board);score += eval.MATERIAL_SCORE[idx] * bits;while (board != 0) {const bit = @ctz(board);score += switch (@intToEnum(Chess.PE, idx)) {.P => eval.PAWN_SCORE[bit],.N => eval.KNIGHT_SCORE[bit],.B => eval.BISHOP_SCORE[bit],.R => eval.ROOK_SCORE[bit],.K => eval.KING_SCORE[bit],.Q => break,.p => -eval.PAWN_SCORE[@enumToInt(eval.MIRROR_SCORE[bit])],.n => -eval.KNIGHT_SCORE[@enumToInt(eval.MIRROR_SCORE[bit])],.b => -eval.BISHOP_SCORE[@enumToInt(eval.MIRROR_SCORE[bit])],.r => -eval.ROOK_SCORE[@enumToInt(eval.MIRROR_SCORE[bit])],.k => -eval.KING_SCORE[@enumToInt(eval.MIRROR_SCORE[bit])],.q => break,else => 0,};board ^= @as(BoardType, 1) << @intCast(u6, bit);}}
test "evaluate" {{// remove white queenvar gs = try GameState.init("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR w KQkq - 0 1");try std.testing.expectEqual(@as(isize, -1000), gs.evaluate());}{// remove white queen, but black comesvar gs = try GameState.init("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR b KQkq - 0 1");try std.testing.expectEqual(@as(isize, 1000), gs.evaluate());}{// Pe2e4var gs = try GameState.init("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1");try std.testing.expectEqual(@as(isize, 30), gs.evaluate());}{// Pe2e4 pe7e5 Ng1f3var gs = try GameState.init("rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1");// gs.show();try std.testing.expectEqual(@as(isize, 30), gs.evaluate());}}