Skip to content

Instantly share code, notes, and snippets.

@niklasf
Created March 19, 2021 18:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save niklasf/6041a4877946c677f58be623b6911287 to your computer and use it in GitHub Desktop.
Save niklasf/6041a4877946c677f58be623b6911287 to your computer and use it in GitHub Desktop.
type Square = number;
type Piece = 'p' | 'n' | 'b' | 'r' | 'q' | 'k' |
'P' | 'N' | 'B' | 'R' | 'Q' | 'K';
interface Board {
turn: boolean;
fmvn: number;
pieces: {
[s: number]: Piece
};
k?: Square;
K?: Square;
}
function square(name: string): Square {
return name.charCodeAt(0) - 97 + (name.charCodeAt(1) - 49) * 8;
}
function squareDist(a: Square, b: Square): number {
let x1 = a & 7, x2 = b & 7;
let y1 = a >> 3, y2 = b >> 3;
return Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2));
}
function isBlack(p: Piece): boolean {
return p === p.toLowerCase();
}
function readFen(fen: string): Board {
let parts = fen.split(' ');
let board: Board = {
pieces: {},
turn: parts[1] === 'w',
fmvn: parseInt(parts[5], 10) || 1
};
parts[0].split('/').slice(0, 8).forEach((row, y) => {
let x = 0;
row.split('').forEach(v => {
if (v == '~') return;
let nb = parseInt(v, 10);
if (nb) x += nb;
else {
let square = (7 - y) * 8 + x;
board.pieces[square] = v as Piece;
if (v === 'k' || v === 'K') board[v] = square;
x++;
}
});
});
return board;
}
function kingMovesTo(s: Square): Square[] {
return [s - 1, s - 9, s - 8, s - 7, s + 1, s + 9, s + 8, s + 7].filter(
o => o >= 0 && o < 64 && squareDist(s, o) === 1);
}
function knightMovesTo(s: Square): Square[] {
return [s + 17, s + 15, s + 10, s + 6, s - 6, s - 10, s - 15, s - 17].filter(
o => o >= 0 && o < 64 && squareDist(s, o) <= 2);
}
function pawnAttacksTo(turn: boolean, s: Square): Square[] {
let left = turn ? 7 : -7;
let right = turn ? 9 : -9;
return [s + left, s + right].filter(
o => o >= 0 && o < 64 && squareDist(s, o) === 1);
}
const ROOK_DELTAS = [8, 1, -8, -1];
const BISHOP_DELTAS = [9, -9, 7, -7];
const QUEEN_DELTAS = ROOK_DELTAS.concat(BISHOP_DELTAS);
function slidingMovesTo(s: Square, deltas: number[], board: Board): Square[] {
let result: Square[] = [];
deltas.forEach(function (delta) {
for (let square = s + delta;
square >= 0 && square < 64 && squareDist(square, square - delta) === 1;
square += delta) {
result.push(square);
if (board.pieces[square]) break;
}
});
return result;
}
function isCheck(variant: VariantKey, board: Board): boolean {
if (variant === 'antichess' || variant == 'racingKings') return false;
const turn = board.turn,
ksq = turn ? board.K : board.k;
if (typeof ksq !== 'number') return false;
// no check when kings are touching in atomic
if (variant === 'atomic' &&
typeof board.k !== 'undefined' &&
typeof board.K !== 'undefined' &&
squareDist(board.k, board.K) <= 1)
return false;
const p = turn ? 'p' : 'P',
n = turn ? 'n' : 'N',
r = turn ? 'r' : 'R',
b = turn ? 'b' : 'B',
q = turn ? 'q' : 'Q';
return (
pawnAttacksTo(turn, ksq).some(o => board.pieces[o] === p) ||
knightMovesTo(ksq).some(o => board.pieces[o] === n) ||
slidingMovesTo(ksq, ROOK_DELTAS, board).some(o => board.pieces[o] === r || board.pieces[o] === q) ||
slidingMovesTo(ksq, BISHOP_DELTAS, board).some(o => board.pieces[o] === b || board.pieces[o] === q));
}
function makeMove(variant: VariantKey, board: Board, uci: string) {
if (!board.turn) board.fmvn++;
const turn = board.turn = !board.turn;
if (uci.includes('@')) {
board.pieces[square(uci.slice(2, 4))] = (turn ? uci[0].toLowerCase() : uci[0]) as Piece;
return;
}
const move = decomposeUci(uci),
from = square(move[0]),
p = board.pieces[from];
let to = square(move[1]),
capture = board.pieces[to];
if (p === 'p' || p === 'P') {
if (uci[0] !== uci[2] && !capture) {
// en passant
delete board.pieces[to + (turn ? 8 : -8)];
capture = turn ? 'p' : 'P';
}
}
if (p === 'k' || p === 'K') {
// castling
let frCastle = capture && isBlack(p) === isBlack(capture);
if (frCastle || squareDist(from, to) > 1) {
delete board.pieces[from];
if (frCastle) delete board.pieces[to];
if (to < from) {
if (!frCastle) delete board.pieces[turn ? square('a8') : square('a1')];
to = turn ? square('c8') : square('c1');
board.pieces[to + 1] = turn ? 'r' : 'R';
}
else {
if (!frCastle) delete board.pieces[turn ? square('h8') : square('h1')];
to = turn ? square('g8') : square('g1');
board.pieces[to - 1] = turn ? 'r' : 'R';
}
board.pieces[to] = p;
board[p] = to;
return;
}
board[p] = to;
}
if (move[2]) board.pieces[to] = (turn ? move[2] : move[2].toUpperCase()) as Piece;
else board.pieces[to] = p;
delete board.pieces[from];
// atomic explosion
if (variant === 'atomic' && capture) {
delete board.pieces[to];
kingMovesTo(to).forEach(function (o) {
if (board.pieces[o] !== 'p' && board.pieces[o] !== 'P') delete board.pieces[o];
});
}
}
export default givesCheck(variant: VariantKey, fen: string, move: string): bool {
const board = readFen(fen);
makeMove(variant, board, uci);
return isCheck(variant, board);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment