JPK7SJDQWLCNDXJQEQQHXBN4Y677BPQ5QBL55GRZ55RSP4GK4L4AC
EHBLDMND5ASGZTTLDLEA5RGQIAZTXZN4HBPGD5PB6FDGZGTJ33SQC
BL3ZR4OWJM54HFXUNMUZKB5YQYVBT7ETFIXCOXWL6S5SZFM6IFDQC
FR6N5DIUYYWT5BBOIBOX3GCSEKZIINSGT4F4CWM6YA2XLQEOU4ZQC
7SQSGQOZRCHN3GGWPNFI36K2R7YZMOPXZP3FP4PYDDOZML5YIYOQC
7T5U3ARTEEAORKCSJY763X5TJRGHCEQK3OW323YO5CZIM77EH77AC
3MEBL2ZUY2LI5LW6MK6B32F62TSVF46QVNIDB3Y4OWLAN4NT7KTAC
QYOCQUIWNSZNLCPDJCKERKC6BXBUNTE6AJCFYL4WDRU32LFX37XQC
O6734I3LMVTKXE5Y7FR3EDGSVIJQHFATUPJJCMRYXD7KLAN6E4UQC
XCZABYMDLHLHXNTHZFCXPNP6VOCNYUROAQACE5SWBL5FJK2EB7WQC
fn init(self: *@This()) void {
self.parseFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1") catch unreachable;
fn init(FEN: ?Str) !@This() {
var gs = GameState{ .attacks = Attacks.init() };
if (FEN != null)
try gs.parseFEN(FEN.?)
else
try gs.parseFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
return gs;
.N, .n => self.genMoves(board, piece),
.B, .b => self.genMoves(board, piece),
.R, .r => self.genMoves(board, piece),
.Q, .q => self.genMoves(board, piece),
.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),
.none => unreachable,
for ([_]u8{ 'q', 'r', 'b', 'n' }) |prom| {
std.debug.print("pawn promotions {any}->{any}{c}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, target_square),
prom,
for (promote_options) |prom| {
try self.moves.append(BitMove{
.source = @intToEnum(Square, source_square),
.target = @intToEnum(Square, target_square),
.piece = piece,
.prom = prom,
std.debug.print("pawn push double: {any}->{any}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, double_target),
try self.moves.append(BitMove{
.source = @intToEnum(Square, source_square),
.target = @intToEnum(Square, double_target),
.piece = piece,
.double = true,
for ([_]u8{ 'q', 'r', 'b', 'n' }) |prom| {
std.debug.print("pawn promotion attacks {any}->{any}{c}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, target_square),
prom,
for (promote_options) |prom| {
try self.moves.append(BitMove{
.source = @intToEnum(Square, source_square),
.target = @intToEnum(Square, target_square),
.piece = piece,
.prom = prom,
.capture = true,
std.debug.print("pawn attack: {any} -> {any}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, target_square),
try self.moves.append(BitMove{
.source = @intToEnum(Square, source_square),
.target = @intToEnum(Square, target_square),
.piece = piece,
.capture = true,
std.debug.print("pawn enpassant capture: {any}->{any}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, target_enpassant),
try self.moves.append(BitMove{
.source = @intToEnum(Square, source_square),
.target = @intToEnum(Square, target_enpassant),
.piece = piece,
.capture = true,
fn castlingMoves(self: @This()) void {
const king_side = switch (self.side) {
.white => [_]Square{ .f1, .g1 },
.black => [_]Square{ .f8, .g8 },
};
const queen_side = switch (self.side) {
.white => [_]Square{ .d1, .c1, .b1 },
.black => [_]Square{ .d8, .c8, .b8 },
};
const attacked_king = switch (self.side) {
.white => [_]Square{ .e1, .f1 },
.black => [_]Square{ .e8, .f8 },
};
const attacked_queen = switch (self.side) {
.white => [_]Square{ .e1, .d1 },
.black => [_]Square{ .e8, .d8 },
};
fn castlingMoves(self: *@This(), piece: PE) !void {
{ // king-side castling
const king_side = switch (self.side) {
.white => [_]Square{ .f1, .g1 },
.black => [_]Square{ .f8, .g8 },
};
const attacked_king = switch (self.side) {
.white => [_]Square{ .e1, .f1 },
.black => [_]Square{ .e8, .f8 },
};
// king-side castling
if ((self.side == .white and self.castling.WK) or
(self.side == .black and self.castling.BK))
{
// make sure king side is emptry
for (king_side) |square| {
if (self.occupBoth() & @as(BoardType, 1) << square.int() != 0) return;
}
// check that castling squares are not attacked
for (attacked_king) |square| {
if (self.isSquareAttacked(square, self.enemy)) return;
}
if ((self.side == .white and self.castling.WK) or
(self.side == .black and self.castling.BK))
{
const OK = blk: {
// make sure king side is emptry
for (king_side) |square| {
if (self.occupBoth() & @as(BoardType, 1) << square.int() != 0) break :blk false;
}
// queen-side castling
if ((self.side == .white and self.castling.WQ) or
(self.side == .black and self.castling.BQ))
{
// make sure queen side is emptry
for (queen_side) |square| {
if (self.occupBoth() & @as(BoardType, 1) << square.int() != 0) return;
}
// check that castling squares are not attacked
for (attacked_queen) |square| {
if (self.isSquareAttacked(square, self.enemy)) return;
break :blk true;
};
if (OK) {
try self.moves.append(BitMove{
.source = attacked_king[0],
.target = king_side[1],
.piece = piece,
});
}
std.debug.print("queen side castling\n", .{});
{ // queen-side castling
const queen_side = switch (self.side) {
.white => [_]Square{ .d1, .c1, .b1 },
.black => [_]Square{ .d8, .c8, .b8 },
};
const attacked_queen = switch (self.side) {
.white => [_]Square{ .e1, .d1 },
.black => [_]Square{ .e8, .d8 },
};
if ((self.side == .white and self.castling.WQ) or
(self.side == .black and self.castling.BQ))
{
const OK = blk: {
// make sure queen side is emptry
for (queen_side) |square| {
if (self.occupBoth() & @as(BoardType, 1) << square.int() != 0) break :blk false;
}
// check that castling squares are not attacked
for (attacked_queen) |square| {
if (self.isSquareAttacked(square, self.enemy)) break :blk false;
}
break :blk true;
};
if (OK) {
try self.moves.append(BitMove{
.source = attacked_queen[0],
.target = queen_side[1],
.piece = piece,
});
}
}
std.debug.print("{any} capture: {any}->{any}\n", .{
piece,
@intToEnum(Square, source_square),
@intToEnum(Square, target_square),
try self.moves.append(BitMove{
.source = @intToEnum(Square, source_square),
.target = @intToEnum(Square, target_square),
.piece = piece,
.capture = true,
std.debug.print("{any} move: {any}->{any}\n", .{
piece,
@intToEnum(Square, source_square),
@intToEnum(Square, target_square),
try self.moves.append(BitMove{
.source = @intToEnum(Square, source_square),
.target = @intToEnum(Square, target_square),
.piece = piece,
var game = Game{ .attacks = Attacks.init() };
game.init();
try game.parseFEN("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1");
var game = try GameState.init("r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1");
var game = Game{ .attacks = Attacks.init() };
try game.parseFEN("r3k2r/p11pqpbp/bn2pnp1/2pPN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R b Kkq c6 0 1");
var gs = try GameState.init("r3k2r/p11pqpbp/bn2pnp1/2pPN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w Kkq c6 0 1");
try gs.generateMoves();
game.generateMoves();
const Game = struct {
state: GameState,
history: std.ArrayList(BitMove),
fn init(allocator: std.mem.Allocator) @This() {
var game: Game = undefined;
game.state = GameState.init(null) catch unreachable;
game.history = std.ArrayList(BitMove).init(allocator);
return game;
}
fn deinit(self: @This()) void {
self.history.deinit();
}
};
test "Game" {
var game = Game.init(std.testing.allocator);
defer game.deinit();