const std = @import("std"); const Chess = @import("Chess.zig"); const Board = @import("Board.zig"); // Scoring layout: // -infinity, -MATE_VALUE, -CHECKMATE_SCORE, score, CHECKMATE_SCORE, MATE_VALUE, infinity pub const INFINITY = 50_000; pub const MATE_VALUE = 49_000; pub const CHECKMATE_SCORE = 48000; pub const STALEMATE_SCORE = 0; // WARNING: this must be in sync with Chess.PE! pub const MATERIAL_SCORE: [12]i16 = .{ 100, 300, 350, 500, 1000, 10_000, -100, -300, -350, -500, -1000, -10_000, }; pub const PAWN_SCORE: [64]i8 = .{ // zig fmt: off 90, 90, 90, 90, 90, 90, 90, 90, 30, 30, 30, 40, 40, 30, 30, 30, 20, 20, 20, 30, 30, 30, 20, 20, 10, 10, 10, 20, 20, 10, 10, 10, 5, 5, 10, 20, 20, 5, 5, 5, 0, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, -10, -10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; // zig fmt: on pub const KNIGHT_SCORE: [64]i8 = .{ // zig fmt: off -5, 0, 0, 0, 0, 0, 0, -5, -5, 0, 0, 10, 10, 0, 0, -5, -5, 5, 20, 20, 20, 20, 5, -5, -5, 10, 20, 30, 30, 20, 10, -5, -5, 10, 20, 30, 30, 20, 10, -5, -5, 5, 20, 10, 10, 20, 5, -5, -5, 0, 0, 0, 0, 0, 0, -5, -5, -10, 0, 0, 0, 0, -10, -5 }; // zig fmt: on pub const BISHOP_SCORE: [64]i8 = .{ // zig fmt: off 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 0, 0, 0, 0, 0, 10, 20, 20, 10, 0, 0, 0, 0, 10, 20, 20, 10, 0, 0, 0, 10, 0, 0, 0, 0, 10, 0, 0, 30, 0, 0, 0, 0, 30, 0, 0, 0, -10, 0, 0, -10, 0, 0 }; // zig fmt: on pub const ROOK_SCORE: [64]i8 = .{ // zig fmt: off 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 0, 0, 10, 20, 20, 10, 0, 0, 0, 0, 10, 20, 20, 10, 0, 0, 0, 0, 10, 20, 20, 10, 0, 0, 0, 0, 10, 20, 20, 10, 0, 0, 0, 0, 10, 20, 20, 10, 0, 0, 0, 0, 0, 20, 20, 0, 0, 0 }; // zig fmt: on pub const KING_SCORE: [64]i8 = .{ // zig fmt: off 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 5, 5, 5, 0, 0, 0, 5, 5, 10, 10, 5, 5, 0, 0, 5, 10, 20, 20, 10, 5, 0, 0, 5, 10, 20, 20, 10, 5, 0, 0, 0, 5, 10, 10, 5, 0, 0, 0, 5, 5, -5, -5, 0, 5, 0, 0, 0, 5, 0, -15, 0, 10, 0 }; // zig fmt: on // mirror positional score tables for opposite side pub const MIRROR_SCORE: [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: on pub fn evaluate(gs: *const Board.GameState) isize { var score: isize = 0; for (gs.bitboards, 0..) |bb, idx| { var board = @as(Board.BoardType, @bitCast(bb)); const bits = @popCount(board); score += MATERIAL_SCORE[idx] * bits; while (board != 0) { const bit = @ctz(board); score += switch (@as(Chess.PE, @enumFromInt(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[@intFromEnum(MIRROR_SCORE[bit])], .n => -KNIGHT_SCORE[@intFromEnum(MIRROR_SCORE[bit])], .b => -BISHOP_SCORE[@intFromEnum(MIRROR_SCORE[bit])], .r => -ROOK_SCORE[@intFromEnum(MIRROR_SCORE[bit])], .k => -KING_SCORE[@intFromEnum(MIRROR_SCORE[bit])], .q => break, else => 0, }; board ^= @as(Board.BoardType, 1) << @as(u6, @intCast(bit)); } } switch (gs.side) { .white => return score, .black => return -score, } } test "evaluate" { { // remove white queen var gs = try Board.GameState.init( std.testing.allocator, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR w KQkq - 0 1", ); defer gs.deinit(); try std.testing.expectEqual(@as(isize, -1000), evaluate(&gs)); } { // remove white queen, but black comes var gs = try Board.GameState.init( std.testing.allocator, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNB1KBNR b KQkq - 0 1", ); defer gs.deinit(); try std.testing.expectEqual(@as(isize, 1000), evaluate(&gs)); } { // Pe2e4 var gs = try Board.GameState.init( std.testing.allocator, "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1", ); defer gs.deinit(); try std.testing.expectEqual(@as(isize, 30), evaluate(&gs)); } { // Pe2e4 pe7e5 Ng1f3 var gs = try Board.GameState.init( std.testing.allocator, "rnbqkbnr/pppp1ppp/8/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 0 1", ); defer gs.deinit(); // gs.show(); try std.testing.expectEqual(@as(isize, 30), evaluate(&gs)); } }