FOORIA7SEZCLKDBNMV6KEDTQOJJVAH57BQFBRSQMET6FERJPHCVQC
OF6NBX4Y6UWXBSSENWBBYH3AU4TI5FCD5S7WKYMINBUBCKA2MD4AC
A46B5KNQFPTZEIL2JZKD2CQELGU3COE6FGTV2ABS5U7DVTVDGEBQC
BL3ZR4OWJM54HFXUNMUZKB5YQYVBT7ETFIXCOXWL6S5SZFM6IFDQC
2CU67A3QXXPH722WBAL74O6P2N2RRXFBQVPT3A5QAXTUCQ3UUU5AC
TB4YBE4CMWCLSKJ43QF6IU5HVYUUO33BLXVG7XDRLJS3IFIBQLYAC
GDHXEMN5IXJDS63G3DUZH5PR2XH2SSXISABF2JHCYA3JSRXDPLMQC
XHCMZF25YBLNQLVKKBJJWWJ3G24VFUSLE3LAT2DRWL52RJX4Z3EAC
const std = @import("std");
const Board = @import("Board.zig");
pub const Perft = struct {
nodes: usize = 0,
gs: Board.GameState,
pub fn perftDriver(self: *@This(), depth: usize) void {
if (depth == 0) {
// count nodes
self.nodes += 1;
return;
}
var ml = Board.MoveList.init(0) catch unreachable;
self.gs.generateMoves(&ml) catch unreachable;
for (ml.slice()) |move| {
const bk = self.gs.backup();
if (!self.gs.makeMove(move, .all)) {
continue;
}
self.perftDriver(depth - 1);
self.gs.restore(bk);
}
}
fn perftTest(self: *@This(), depth: usize) void {
var ml = Board.MoveList.init(0) catch unreachable;
self.gs.generateMoves(&ml) catch unreachable;
for (ml.slice()) |move| {
const bk = self.gs.backup();
if (!self.gs.makeMove(move, .all)) {
std.debug.print("ILLEGAL MOVE\n", .{});
continue;
}
const cummulative_nodes = self.nodes;
self.perftDriver(depth - 1);
const old_nodes = self.nodes - cummulative_nodes;
self.gs.restore(bk);
move.show();
std.debug.print("nodes: {d}\n", .{old_nodes});
}
}
};
test "Perft" {
var gs = Board.GameState.init("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -") catch unreachable;
var p = Perft{ .gs = gs };
p.perftDriver(2);
try std.testing.expectEqual(@as(usize, 191), p.nodes);
}
var bit_board: Chess.BitBoard = .{ .board = 0 };
bit_board.show();
var gs = try Board.GameState.init("8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - -");
var p = Perft{ .gs = gs };
var timer = try std.time.Timer.start();
p.perftDriver(4);
std.debug.print("Nodes: {d} time: {d}\n", .{ p.nodes, timer.read() / std.time.ns_per_ms });
fn show(self: @This()) void {
std.debug.print("\nsource: {any}\n", .{self.source});
std.debug.print("target: {any}\n", .{self.target});
std.debug.print("piece: {any}\n", .{self.piece});
std.debug.print("promote: {any}\n", .{self.prom});
std.debug.print("capture: {any}\n", .{self.capture});
std.debug.print("double: {any}\n", .{self.double});
std.debug.print("enpassant: {any}\n", .{self.enpassant});
std.debug.print("castling: {any}\n", .{self.castling});
pub fn show(self: @This()) void {
// std.debug.print("source: {any}\n", .{self.source});
// std.debug.print("target: {any}\n", .{self.target});
// std.debug.print("piece: {any}\n", .{self.piece});
// std.debug.print("promote: {any}\n", .{self.prom});
// std.debug.print("capture: {any}\n", .{self.capture});
// std.debug.print("double: {any}\n", .{self.double});
// std.debug.print("enpassant: {any}\n", .{self.enpassant});
// std.debug.print("castling: {any}\n", .{self.castling});
std.debug.print("{s} {s}{s}\t", .{
@tagName(self.piece),
@tagName(self.source),
@tagName(self.target),
});
.N, .n => try self.genMoves(board, piece),
.B, .b => try self.genMoves(board, piece),
.R, .r => try self.genMoves(board, piece),
.Q, .q => try self.genMoves(board, piece),
.N, .n => try self.genMoves(ml, board, piece),
.B, .b => try self.genMoves(ml, board, piece),
.R, .r => try self.genMoves(ml, board, piece),
.Q, .q => try self.genMoves(ml, board, piece),
if (self.occupancies[@enumToInt(self.enemy)].isSet(@intToEnum(Square, target_square))) {
try self.moves.append(BitMove{
if (self.occupancies[@enumToInt(self.side.enemy())].isSet(@intToEnum(Square, target_square))) {
try ml.append(BitMove{
// handle occupancy arrays
{
// remove source target, one of them is no-op
self.occupancies[@enumToInt(Chess.Colors.white)].unSet(move.source);
self.occupancies[@enumToInt(Chess.Colors.black)].unSet(move.source);
// set target for the proper side
switch (self.side) {
.white => {
self.occupancies[@enumToInt(Chess.Colors.white)].set(move.target);
self.occupancies[@enumToInt(Chess.Colors.black)].unSet(move.target);
},
.black => {
self.occupancies[@enumToInt(Chess.Colors.black)].set(move.target);
self.occupancies[@enumToInt(Chess.Colors.white)].unSet(move.target);
},
}
}
// handle occupancy arrays TODO: perf test!
// {
// // remove source target, one of them is no-op
// self.occupancies[@enumToInt(Chess.Colors.white)].unSet(move.source);
// self.occupancies[@enumToInt(Chess.Colors.black)].unSet(move.source);
// TODO: reset enpassant
// self.enpassant = null;
// // set target for the proper side
// switch (self.side) {
// .white => {
// self.occupancies[@enumToInt(Chess.Colors.white)].set(move.target);
// self.occupancies[@enumToInt(Chess.Colors.black)].unSet(move.target);
// },
// .black => {
// self.occupancies[@enumToInt(Chess.Colors.black)].set(move.target);
// self.occupancies[@enumToInt(Chess.Colors.white)].unSet(move.target);
// },
// }
// }
const target = switch (self.side) {
.white => @intToEnum(Square, @enumToInt(move.target) + 8),
.black => @intToEnum(Square, @enumToInt(move.target) - 8),
};
self.bitboards[move.piece.int()].pop(target);
switch (self.side) {
.white => self.bitboards[Chess.PE.p.int()]
.pop(@intToEnum(Square, @enumToInt(move.target) + 8)),
.black => self.bitboards[Chess.PE.P.int()]
.pop(@intToEnum(Square, @enumToInt(move.target) - 8)),
}
self.occupancies[@enumToInt(Chess.Colors.white)].pop(Square.h1);
self.occupancies[@enumToInt(Chess.Colors.white)].set(Square.f1);
// self.occupancies[@enumToInt(Chess.Colors.white)].pop(Square.h1);
// self.occupancies[@enumToInt(Chess.Colors.white)].set(Square.f1);
self.occupancies[@enumToInt(Chess.Colors.white)].pop(Square.a1);
self.occupancies[@enumToInt(Chess.Colors.white)].set(Square.d1);
// self.occupancies[@enumToInt(Chess.Colors.white)].pop(Square.a1);
// self.occupancies[@enumToInt(Chess.Colors.white)].set(Square.d1);
self.occupancies[@enumToInt(Chess.Colors.black)].pop(Square.h1);
self.occupancies[@enumToInt(Chess.Colors.black)].set(Square.f1);
// self.occupancies[@enumToInt(Chess.Colors.black)].pop(Square.h1);
// self.occupancies[@enumToInt(Chess.Colors.black)].set(Square.f1);
self.occupancies[@enumToInt(Chess.Colors.black)].pop(Square.a8);
self.occupancies[@enumToInt(Chess.Colors.black)].set(Square.d8);
// self.occupancies[@enumToInt(Chess.Colors.black)].pop(Square.a8);
// self.occupancies[@enumToInt(Chess.Colors.black)].set(Square.d8);
// make sure the move is valid (no check)
// update occupancy boards
{
var white: BoardType = 0;
var black: BoardType = 0;
for ([_]Chess.PE{ .P, .N, .B, .R, .Q, .K }) |piece| {
white |= @bitCast(BoardType, self.bitboards[piece.int()]);
}
for ([_]Chess.PE{ .p, .n, .b, .r, .q, .k }) |piece| {
black |= @bitCast(BoardType, self.bitboards[piece.int()]);
}
self.occupancies[@enumToInt(Chess.Colors.white)] = @bitCast(BitBoard, white);
self.occupancies[@enumToInt(Chess.Colors.black)] = @bitCast(BitBoard, black);
}
// make sure the move is valid (king is not in check)
test "makeMove" {
const test_pos = "r3k2r/pPppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq c6 0 1";
var gs = try GameState.init(test_pos);
try gs.generateMoves();
for (gs.moves.slice()) |move| {
if (move.castling) {
gs.show();
move.show();
const bk = gs.backup();
_ = gs.makeMove(move, .all);
gs.show();
gs.restore(bk);
}
}
}