stego.git
moves.c

/* stego - chess engine
* Copyright (C) 2025 ArcNyxx <me@arcnyxx.net>
* see LICENCE file for licensing information */
#include "attack.h"
#include "bits.h"
#include "moves.h"
#include "stego.h"
static int
mv_serialise(move_t moves[256], const sq_t from, u64 mask,
const piece_t piece, const flag_t flag)
{
}
/* all non-king pieces attacking a certain square */
static u64
mv_checkers_white(sq_t sq, u64 occ)
{
return (atk_rook(sq, occ) & (bd->bqueen | bd->brook)) |
(atk_bishop(sq, occ) & (bd->bqueen | bd->bbishop)) |
(atk_knight(sq) & bd->bknight) | (atk_wpawn(sq) & bd->bpawn);
}
static u64
mv_checkers_black(sq_t sq, u64 occ)
{
return (atk_rook(sq, occ) & (bd->wqueen | bd->wrook)) |
(atk_bishop(sq, occ) & (bd->wqueen | bd->wbishop)) |
(atk_knight(sq) & bd->wknight) | (atk_bpawn(sq) & bd->wpawn);
}
/* all pieces attacking a certain square */
static u64
mv_attackers_white(sq_t sq, u64 occ)
{
return (atk_king(sq) & bd->bking) | mv_checkers_white(sq, occ);
}
static u64
mv_attackers_black(sq_t sq, u64 occ)
{
return (atk_king(sq) & bd->wking) | mv_checkers_black(sq, occ);
}
/* spaces between a checking slider and friendly king */
static u64
mv_quiet(board_t *bd, sq_t king, u64 checker)
{
/* anding together the slider attacks from the king's and slider's
* perspectives gets the spaces between them */
if (rotab[king] & checker)
return atk_rook(king, bd->occupied) &
atk_rook(lsb(checker), bd->occupied);
return atk_bishop(king, bd->occupied) &
atk_bishop(lsb(checker), bd->occupied);
}
static u64
mv_king_white(const board_t *bd, const sq_t king)
{
u64 mask = kitab[king] & ~bd->white, iter = mask;
while (iter != 0) {
sq_t move = lsb(iter);
/* remove the king from the mask of occupied squares so enemy
* sliding pieces can "see through" it and attack sqaures
* blocked by the king which, if moved to, would have the king
* remain in check */
if (mv_attackers_white(bd, move, bd->occupied ^ bd->wking))
mask ^= 1ULL << move;
iter ^= 1ULL << move;
}
return mask;
}
static u64
mv_king_black(const board_t *bd, const sq_t king)
{
u64 mask = kitab[king] & ~bd->black, iter = mask;
while (iter != 0) {
sq_t move = lsb(iter);
/* remove the king from the mask of occupied squares so enemy
* sliding pieces can "see through" it and attack sqaures
* blocked by the king which, if moved to, would have the king
* remain in check */
if (mv_attackers_black(bd, move, bd->occupied ^ bd->bking))
mask ^= 1ULL << move;
iter ^= 1ULL << move;
}
return mask;
}
#define CASTLE(type, mask, move, color) \
if (type && !(bd->occupied & (mask)) && (king_mask & (mask)) && \
!mv_attackers_##color(move, bd->occupied)) \
mask |= 1ULL << move;
static u64
mv_king_castle_white(const board_t *bd, const u64 king_mask)
{
u64 mask = 0;
CASTLE(bd->wkcastle, 1ULL << 61 | 1ULL << 62, 62, white);
CASTLE(bd->wqcastle, 1ULL << 57 | 1ULL << 58 | 1ULL << 59, 59, white);
return mask;
}
static u64
mv_king_castle_black(const board_t *bd, const u64 king_mask)
{
u64 mask = 0;
CASTLE(bd->bkcastle, 1ULL << 5 | 1ULL << 6, 6, black);
CASTLE(bd->bqcastle, 1ULL << 1 | 1ULL << 2 | 1ULL << 3, 2, white);
return mask;
}
static u64
mv_pawn_push_white(sq_t pawn, u64 occupied, u64 allowed, u64 pins[64])
{
return (wptab[pawn] & ~occupied) | (wltab[pawn] & ~occupied &
~(occupied >> 8)) & allowed & ~pins[pawn];
}
static u64
mv_pawn_push_black(sq_t pawn, u64 occupied, u64 allowed, u64 pins[64])
{
return (bptab[pawn] & ~occupied) | (bltab[pawn] & ~occupied &
~(occupied << 8)) & allowed & ~pins[pawn];
}
static u64
mv_pieces(move_t *moves, u64 queen, u64 rook, u64 bishop, u64 knight,
u64 occupied, u64 allowed, u64 pins[64])
{
int index = 0;
while (queen != 0) {
const sq_t sq = lsb(queen);
const u64 mask = (atk_rook(sq, occupied) | atk_bishop(sq,
occupied)) & allowed & ~pins[sq];
index += mv_serialise(&moves[index], sq, mask, QUEEN, NONE);
queen ^= 1ULL << sq;
}
while (rook != 0) {
const sq_t sq = lsb(rook);
const u64 mask = atk_rook(sq, occupied) & allowed & ~pins[sq];
index += mv_serialise(&moves[index], sq, mask, ROOK, NONE);
rook ^= 1ULL << sq;
}
while (bishop != 0) {
const sq_t sq = lsb(bishop);
const u64 mask = atk_bishop(sq, occupied) & allowed & ~pins[sq];
index += mv_serialise(&moves[index], sq, mask, BISHOP, NONE);
bishop ^= 1ULL << sq;
}
while (knight != 0) {
const sq_t sq = lsb(knight);
const u64 mask = atk_knight(sq, occupied) & allowed & ~pins[sq];
index += mv_serialise(&moves[index], sq, mask, KNIGHT, NONE);
bishop ^= 1ULL << sq;
}
}
int
mv_white(const board_t *bd, move_t moves[256])
{
int index = 0;
const sq_t king = lsb(bd->wking);
const u64 king_mask = mv_king_white(bd, king);
index += mv_serialise(&moves[index], king, king_mask, KING, NONE);
u64 checkers = mv_checkers_white(bd, king, bd->occupied);
if (popcount(checkers) == 2)
return; /* double check, only king moves legal */
/* set up capture and quiet move masks */
u64 capture = ~0ULL, quiet = ~0ULL;
if (checkers != 0) {
/* single check, only capture attacker or quiet move to block
* sliding piece */
capture = checkers;
quiet = (checkers | bd->bqueen | bd->brook | bd->bbishop) ?
mv_quiet(bd, king, checkers) : 0;
} else {
/* castles only while not in check */
const u64 mask = mv_king_castle_white(bd, king, king_mask);
index += mv_serialise(&moves[index], king, mask, KING, NONE);
}
u64 friend = bd->white, enemy = bd->black;
/* pin evaluation */
u64 pin_moves, pin_map[64];
sq_t slider;
/* for each direction, find the nearest enemy piece (if it exists on a
* ray originating from the king), stop the ray on the enemy piece, and,
* if the piece is the correct type of slider and there is only one
* friendly piece between it and the king, apply a mask to the friendly
* piece moves */
#define PIN(tab, rtab, fn, mask, type) \
pin_moves = tab[king]; \
slider = fn(pin_moves & (enemy | mask)); \
pin_moves &= rtab[slider]; \
if ((slider & (type)) && popcount(pin_moves & friend) == 1) \
pin_map[lsb(pin_moves & friend)] = ~pin_moves;
PIN(nobtab, sotab, msb, 1, bd->bqueen | bd->brook);
PIN(nebtab, swtab, msb, 1, bd->bqueen | bd->bbishop);
PIN(eabtab, wetab, lsb, 1ULL << 63, bd->bqueen | bd->brook);
PIN(sebtab, nwtab, lsb, 1ULL << 63, bd->bqueen | bd->bbishop);
PIN(sobtab, notab, lsb, 1ULL << 63, bd->bqueen | bd->brook);
PIN(swbtab, netab, lsb, 1ULL << 63, bd->bqueen | bd->bbishop);
PIN(webtab, eatab, msb, 1, bd->bqueen | bd->brook);
PIN(nwbtab, setab, msb, 1, bd->bqueen | bd->bbishop);
index += mv_pieces(&moves[index],
bd->wqueen, bd->wrook, bd->wbishop, bd->wknight,
bd->occupied, ~bd->white | capture | quiet, pin_map);
for (u64 pawn = bd->wpawn; pawn != 0; ) {
sq_t sq = lsb(pawn);
u64 mask =
}
}