7SQSGQOZRCHN3GGWPNFI36K2R7YZMOPXZP3FP4PYDDOZML5YIYOQC
IBUD4SUGA4IS6HUQIHVJJOFS4CMTWJMTMXS4LWGFVK4PFUFKE5LAC
XCZABYMDLHLHXNTHZFCXPNP6VOCNYUROAQACE5SWBL5FJK2EB7WQC
WTTQGET65BQV7NSFOI75JGCTWFS6R73OFZE4V63JYWAODNIOUAHQC
BL3ZR4OWJM54HFXUNMUZKB5YQYVBT7ETFIXCOXWL6S5SZFM6IFDQC
GZPVUGLJGD3VGLIK7JDABQ24E5SWN5CYRDGI6PIAFZ2BIMRDMANQC
YCH5WGGP3PPG2PUNVJIJ6SQXDPYU7NGCVQEJYN27B6FYV5JYQOMQC
TDHJSFFSJZBEZFMDHPLRSVB6CIVJ7AWGGNIC3WUCADQLQSIQKXDAC
QYOCQUIWNSZNLCPDJCKERKC6BXBUNTE6AJCFYL4WDRU32LFX37XQC
7T5U3ARTEEAORKCSJY763X5TJRGHCEQK3OW323YO5CZIM77EH77AC
O6734I3LMVTKXE5Y7FR3EDGSVIJQHFATUPJJCMRYXD7KLAN6E4UQC
.P, .p => {
while (board != 0) {
const s = @intCast(u6, @ctz(board));
source_square = @as(BoardType, 1) << s;
std.debug.print("pawn: {any}\n", .{@intToEnum(Square, s)});
.P, .p => self.pawnMoves(board),
else => {},
}
}
// generate move for rest of pieces
}
fn pawnMoves(self: @This(), board_in: BoardType) void {
var source_square: SquareType = undefined;
var target_square: SquareType = undefined;
var board = board_in;
while (board != 0) {
source_square = @intCast(SquareType, @ctz(board));
// std.debug.print("pawn: {any}\n", .{@intToEnum(Square, source_square)});
// early exit on underflow/overflow
switch (self.side) {
.white => if (@subWithOverflow(SquareType, source_square, 8, &target_square)) continue,
.black => if (@addWithOverflow(SquareType, source_square, 8, &target_square)) continue,
}
// early exit on underflow/overflow
switch (piece) {
.P => if (@subWithOverflow(BoardType, source_square, 8, &target_square)) continue,
.p => if (@addWithOverflow(BoardType, source_square, 8, &target_square)) continue,
else => unreachable,
}
const promote_condition = switch (self.side) {
.white => source_square >= @enumToInt(Square.a7) and
source_square <= @enumToInt(Square.h7),
.black => source_square >= @enumToInt(Square.a2) and
source_square <= @enumToInt(Square.h2),
};
// generate quiet pawn moves
if (self.occupBoth() & target_square == 0) {
const promote_condition = switch (piece) {
.P => source_square >= @enumToInt(Square.a7) and
source_square <= @enumToInt(Square.h7),
.p => source_square >= @enumToInt(Square.a2) and
source_square <= @enumToInt(Square.h2),
else => unreachable,
};
const double_step_condition = switch (piece) {
.P => source_square >= @enumToInt(Square.a2) and
source_square <= @enumToInt(Square.h2) and
self.occupBoth() & (target_square - 8) == 0,
.p => source_square >= @enumToInt(Square.a7) and
source_square <= @enumToInt(Square.h7) and
self.occupBoth() & (target_square + 8) == 0,
else => unreachable,
};
// generate quiet moves
if (self.occupBoth() & (@as(BoardType, 1) << target_square) == 0) {
const double_step_condition = switch (self.side) {
.white => source_square >= @enumToInt(Square.a2) and
source_square <= @enumToInt(Square.h2) and
self.occupBoth() & (@as(BoardType, 1) << (target_square - 8)) == 0,
.black => source_square >= @enumToInt(Square.a7) and
source_square <= @enumToInt(Square.h7) and
self.occupBoth() & (@as(BoardType, 1) << (target_square + 8)) == 0,
};
if (promote_condition) {
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,
});
}
} else {
std.debug.print("pawn push: {any}->{any}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, target_square),
});
if (promote_condition) {
// TODO: add move to move list
} else {
// TODO: single step
// TODO: check: if single step is no-go, double can not work
if (double_step_condition) {
const double_target = switch (self.side) {
.white => target_square - 8,
.black => target_square + 8,
};
std.debug.print("pawn push double: {any}->{any}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, double_target),
});
}
}
}
// TODO: double step
// FIXME: if single step is no-go, double can not work
if (double_step_condition) {}
}
}
// generate attacks
const other = switch (self.side) {
.white => Colors.black,
.black => Colors.white,
};
var attacks =
self.attacks.pawn_attacks[@enumToInt(self.side)][source_square] &
self.occupancies[@enumToInt(other)];
while (attacks != 0) {
target_square = @intCast(SquareType, @ctz(attacks));
// pop least significant bit
board ^= source_square;
if (promote_condition) {
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,
});
// generate move for rest of pieces
}
};
// generate enpassant captures
if (self.enpassant != null) {
const enpassant_attacks = self.attacks.pawn_attacks[@enumToInt(self.side)][source_square] &
@as(BoardType, 1) << self.enpassant.?.int();
test "parseFEN" {
const empty_board = "8/8/8/8/8/8/8/8 w - -";
const start_position = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
const tricky_position = "r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1";
const killer_position = "rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1";
const cmk_position = "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9";
if (enpassant_attacks != 0) {
const target_enpassant: SquareType = @intCast(u6, @ctz(enpassant_attacks));
std.debug.print("pawn enpassant capture: {any}->{any}\n", .{
@intToEnum(Square, source_square),
@intToEnum(Square, target_enpassant),
});
}
}
var game = Game{ .attacks = Attacks.init() };
try game.parseFEN(empty_board);
try game.parseFEN(start_position);
try game.parseFEN(tricky_position);
try game.parseFEN(killer_position);
try game.parseFEN(cmk_position);
}
// pop processed board bit
board ^= @as(BoardType, 1) << source_square;
}
}
};