kokopu
Version:
A JavaScript/TypeScript library implementing the chess game rules and providing tools to read/write the standard chess file formats.
142 lines (123 loc) • 6.21 kB
text/typescript
/*!
* -------------------------------------------------------------------------- *
* *
* Kokopu - A JavaScript/TypeScript chess library. *
* <https://www.npmjs.com/package/kokopu> *
* Copyright (C) 2018-2025 Yoann Le Montagner <yo35 -at- melix.net> *
* *
* Kokopu is free software: you can redistribute it and/or *
* modify it under the terms of the GNU Lesser General Public License *
* as published by the Free Software Foundation, either version 3 of *
* the License, or (at your option) any later version. *
* *
* Kokopu is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General *
* Public License along with this program. If not, see *
* <http://www.gnu.org/licenses/>. *
* *
* -------------------------------------------------------------------------- */
import { PieceImpl, SpI } from './base_types_impl';
import { PositionImpl } from './impl';
/**
* Attack directions per colored piece.
*/
export const ATTACK_DIRECTIONS = [
[ -17, -16, -15, -1, 1, 15, 16, 17 ], // king/queen
[ -17, -16, -15, -1, 1, 15, 16, 17 ], // king/queen
[ -17, -16, -15, -1, 1, 15, 16, 17 ], // king/queen
[ -17, -16, -15, -1, 1, 15, 16, 17 ], // king/queen
[ -16, -1, 1, 16 ], // rook
[ -16, -1, 1, 16 ], // rook
[ -17, -15, 15, 17 ], // bishop
[ -17, -15, 15, 17 ], // bishop
[ -33, -31, -18, -14, 14, 18, 31, 33 ], // knight
[ -33, -31, -18, -14, 14, 18, 31, 33 ], // knight
[ 15, 17 ], // white pawn
[ -17, -15 ], // black pawn
];
// -----------------------------------------------------------------------------
// isAttacked
// -----------------------------------------------------------------------------
/**
* Check if any piece of the given color attacks a given square.
*/
export function isAttacked(position: PositionImpl, square: number, attackerColor: number) {
return isAttackedByNonSliding(position, square, PieceImpl.KING * 2 + attackerColor) ||
isAttackedByNonSliding(position, square, PieceImpl.KNIGHT * 2 + attackerColor) ||
isAttackedByNonSliding(position, square, PieceImpl.PAWN * 2 + attackerColor) ||
isAttackedBySliding(position, square, PieceImpl.ROOK * 2 + attackerColor, PieceImpl.QUEEN * 2 + attackerColor) ||
isAttackedBySliding(position, square, PieceImpl.BISHOP * 2 + attackerColor, PieceImpl.QUEEN * 2 + attackerColor);
}
function isAttackedByNonSliding(position: PositionImpl, square: number, nonSlidingAttacker: number) {
for (const attackDirection of ATTACK_DIRECTIONS[nonSlidingAttacker]) {
const sq = square - attackDirection;
if ((sq & 0x88) === 0 && position.board[sq] === nonSlidingAttacker) {
return true;
}
}
return false;
}
function isAttackedBySliding(position: PositionImpl, square: number, slidingAttacker: number, queenAttacker: number) {
for (const attackDirection of ATTACK_DIRECTIONS[slidingAttacker]) {
let sq = square;
while (true) {
sq -= attackDirection;
if ((sq & 0x88) === 0) {
const cp = position.board[sq];
if (cp === SpI.EMPTY) {
continue;
}
else if (cp === slidingAttacker || cp === queenAttacker) {
return true;
}
}
break;
}
}
return false;
}
// -----------------------------------------------------------------------------
// getAttacks
// -----------------------------------------------------------------------------
/**
* Return the squares from which a piece of the given color attacks a given square.
*/
export function getAttacks(position: PositionImpl, square: number, attackerColor: number) {
const result: number[] = [];
findNonSlidingAttacks(position, square, result, PieceImpl.KING * 2 + attackerColor);
findNonSlidingAttacks(position, square, result, PieceImpl.KNIGHT * 2 + attackerColor);
findNonSlidingAttacks(position, square, result, PieceImpl.PAWN * 2 + attackerColor);
findSlidingAttacks(position, square, result, PieceImpl.ROOK * 2 + attackerColor, PieceImpl.QUEEN * 2 + attackerColor);
findSlidingAttacks(position, square, result, PieceImpl.BISHOP * 2 + attackerColor, PieceImpl.QUEEN * 2 + attackerColor);
return result;
}
function findNonSlidingAttacks(position: PositionImpl, square: number, result: number[], nonSlidingAttacker: number) {
for (const attackDirection of ATTACK_DIRECTIONS[nonSlidingAttacker]) {
const sq = square - attackDirection;
if ((sq & 0x88) === 0 && position.board[sq] === nonSlidingAttacker) {
result.push(sq);
}
}
}
function findSlidingAttacks(position: PositionImpl, square: number, result: number[], slidingAttacker: number, queenAttacker: number) {
for (const attackDirection of ATTACK_DIRECTIONS[slidingAttacker]) {
let sq = square;
while (true) {
sq -= attackDirection;
if ((sq & 0x88) === 0) {
const cp = position.board[sq];
if (cp === SpI.EMPTY) {
continue;
}
else if (cp === slidingAttacker || cp === queenAttacker) {
result.push(sq);
}
}
break;
}
}
}