RNEXG5IFDKMHSUR6RMNTI3Y32ORLVMZ6UJYKHLV2XBMT2QONBTVQC WSRRWTTVBE2K6NKPS54CXMY3QYK5YTDRGEH3YJ53VQ2XW5UM5O2QC G4HJL4QLASCZBWYGCEXYYRBYL7UVX6ENELHRRBFB5UAXXGVVGZGQC A46B5KNQFPTZEIL2JZKD2CQELGU3COE6FGTV2ABS5U7DVTVDGEBQC BL3ZR4OWJM54HFXUNMUZKB5YQYVBT7ETFIXCOXWL6S5SZFM6IFDQC TB4YBE4CMWCLSKJ43QF6IU5HVYUUO33BLXVG7XDRLJS3IFIBQLYAC DKK2G2S3X7KFEJBHGLUEVTBBW6UBR36XR7ZBUWG5GO6JYXWGJOOAC THYIOCFC72V4ZMTLMGRGRIBARXT2LPSQCL2XNOIBI22QRVYUMIYQC 6S6HHKOXBQ5ARL7S7PKMPDIAH3VTPOAPWWSMGY37RP2R2U662FBAC NYVHTMLLUMBKBQSWO6SQDQ5KFACQLNCBRDDM5BH3OU6WWT54KC2AC 5VEZOTVGONDXPL6WC7UWJ4UZXAVJDKOAPC6ATCLSWDDH7AML57UQC const std = @import("std");const Str = []const u8;const Chess = @import("Chess.zig");const BoardType = @import("Board.zig").BoardType;const BitMove = @import("Board.zig").BitMove;const BitMoveType = @import("Board.zig").BitMoveType;const GameState = @import("Board.zig").GameState;const MoveList = @import("Board.zig").MoveList;const SquareType = @import("Board.zig").SquareType;const eval = @import("eval.zig");const nnue = @import("nnue.zig");var buf: [16]u8 = undefined;fn searchPosition(gs: *GameState, depth: usize) Str {const ALPHA = -5000;const BETA = 5000;const score = negamax(gs, ALPHA, BETA, depth) catch unreachable;std.debug.print("info score cp {d} depth {d}\n", .{ score, depth });if (@bitCast(BitMoveType, gs.best_move) != 0) {return std.fmt.bufPrint(&buf, "bestmove {s}{s}{s}\n", .{@tagName(gs.best_move.source),@tagName(gs.best_move.target),if (gs.best_move.prom != .none) @tagName(gs.best_move.prom) else "",}) catch unreachable;} else return "";}test "searchPosition" {var gs = try GameState.init(null);gs.show();std.debug.print("{s}", .{searchPosition(&gs, 4)});}fn negamax(gs: *GameState, alpha_orig: isize, beta: isize, depth: usize) !isize {if (depth == 0) return try quiescenceSearch(gs, alpha_orig, beta);var alpha = alpha_orig;// TODO// nodes += 1;const king = switch (gs.side) {.white => @intCast(SquareType, @ctz(@bitCast(BoardType, gs.bitboards[@enumToInt(Chess.PE.K)]))),.black => @intCast(SquareType, @ctz(@bitCast(BoardType, gs.bitboards[@enumToInt(Chess.PE.k)]))),};var in_check = gs.isSquareAttacked(king, gs.side.enemy());var legal_moves: usize = 0;var best_sofar: BitMove = @bitCast(BitMove, @as(BitMoveType, 0));var ml = try MoveList.init(0);try gs.generateMoves(&ml);for (ml.slice()) |move, idx| {var bck: GameState = undefined;gs.backup(&bck);gs.ply += 1;if (!gs.makeMove(move, .all)) {gs.ply -= 1;gs.restore(&bck);continue;}legal_moves += 1;const score = -try negamax(gs, -beta, -alpha, depth - 1);gs.ply -= 1;gs.restore(&bck);// fail-hard beta cutoffif (score >= beta) {// node (move) fails highreturn beta;}// found a better moveif (score > alpha) {// PV (principal validation) nodealpha = score;// if root moveif (gs.ply == 0) {best_sofar = ml.get(idx);}}}// Detect check- and stalemateif (legal_moves == 0) {if (in_check) {// Adding ply helps the engine finding fastest mates.return eval.CHECKMATE_SCORE + @intCast(isize, gs.ply);} else {return eval.STALEMATE_SCORE;}}if (alpha_orig != alpha) {// init best movegs.best_move = best_sofar;}// node (move) fails lowreturn alpha;}// quiescenceSearch helps the engine not thow away its pieces because of// a temporary seemingly good move. (Horizon effect)fn quiescenceSearch(gs: *GameState, alpha_orig: isize, beta: isize) !isize {var alpha = alpha_orig;const evaluation = eval.evaluate(gs);// fail-hard beta cutoffif (evaluation >= beta) {// node (move) fails highreturn beta;}// found a better moveif (evaluation > alpha) {// PV (principal validation) nodealpha = evaluation;}var ml = try MoveList.init(0);try gs.generateMoves(&ml);for (ml.slice()) |move| {var bck: GameState = undefined;gs.backup(&bck);gs.ply += 1;if (!gs.makeMove(move, .captures)) {gs.ply -= 1;gs.restore(&bck);continue;}const score = -try quiescenceSearch(gs, -beta, -alpha);gs.ply -= 1;gs.restore(&bck);// fail-hard beta cutoffif (score >= beta) {// node (move) fails highreturn beta;}// found a better moveif (score > alpha) {// PV (principal validation) nodealpha = score;}}// node (move) fails lowreturn alpha;}
}fn searchPosition(self: *@This(), depth: usize) Str {const ALPHA = -5000;const BETA = 5000;const score = self.negamax(ALPHA, BETA, depth) catch unreachable;std.debug.print("info score cp {d} depth {d}\n", .{ score, depth });if (@bitCast(BitMoveType, self.best_move) != 0) {return std.fmt.bufPrint(&buf, "bestmove {s}{s}{s}\n", .{@tagName(self.best_move.source),@tagName(self.best_move.target),if (self.best_move.prom != .none) @tagName(self.best_move.prom) else "",}) catch unreachable;} else return "";
fn negamax(self: *@This(), alpha_orig: isize, beta: isize, depth: usize) !isize {if (depth == 0) return try self.quiescenceSearch(alpha_orig, beta);var alpha = alpha_orig;// TODO// nodes += 1;const king = switch (self.side) {.white => @intCast(SquareType, @ctz(@bitCast(BoardType, self.bitboards[@enumToInt(Chess.PE.K)]))),.black => @intCast(SquareType, @ctz(@bitCast(BoardType, self.bitboards[@enumToInt(Chess.PE.k)]))),};var in_check = self.isSquareAttacked(king, self.side.enemy());var legal_moves: usize = 0;var best_sofar: BitMove = @bitCast(BitMove, @as(BitMoveType, 0));var ml = try MoveList.init(0);try self.generateMoves(&ml);for (ml.slice()) |move, idx| {var bck: GameState = undefined;self.backup(&bck);self.ply += 1;if (!self.makeMove(move, .all)) {self.ply -= 1;self.restore(&bck);continue;}legal_moves += 1;const score = -try self.negamax(-beta, -alpha, depth - 1);self.ply -= 1;self.restore(&bck);// fail-hard beta cutoffif (score >= beta) {// node (move) fails highreturn beta;}// found a better moveif (score > alpha) {// PV (principal validation) nodealpha = score;// if root moveif (self.ply == 0) {best_sofar = ml.get(idx);}}}
// Detect check- and stalemateif (legal_moves == 0) {if (in_check) {// Adding ply helps the engine finding fastest mates.return eval.CHECKMATE_SCORE + @intCast(isize, self.ply);} else {return eval.STALEMATE_SCORE;}}if (alpha_orig != alpha) {// init best moveself.best_move = best_sofar;}// node (move) fails lowreturn alpha;}// quiescenceSearch helps the engine not thow away its pieces because of// a temporary seemingly good move.fn quiescenceSearch(self: *@This(), alpha_orig: isize, beta: isize) !isize {var alpha = alpha_orig;const evaluation = nnue.evaluate(self);// fail-hard beta cutoffif (evaluation >= beta) {// node (move) fails highreturn beta;}// found a better moveif (evaluation > alpha) {// PV (principal validation) nodealpha = evaluation;}var ml = try MoveList.init(0);try self.generateMoves(&ml);for (ml.slice()) |move| {var bck: GameState = undefined;self.backup(&bck);self.ply += 1;if (!self.makeMove(move, .captures)) {self.ply -= 1;self.restore(&bck);continue;}const score = -try self.quiescenceSearch(-beta, -alpha);self.ply -= 1;self.restore(&bck);// fail-hard beta cutoffif (score >= beta) {// node (move) fails highreturn beta;}// found a better moveif (score > alpha) {// PV (principal validation) nodealpha = score;}}// node (move) fails lowreturn alpha;}