stego.git

moves.c

espurr
/* 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 = 
	}
}