onix-chess
Version:
Onix chess library
392 lines (351 loc) • 15.1 kB
text/typescript
import { Colors, Squares, Directions } from '../types/Types';
import { Color } from './Color';
import { Direction } from './Direction';
export namespace Square {
/**
* Zero (invalid) square
*/
const ns = undefined;
/**
* Fyle's names
*/
const hqs: string[] = ["a", "b", "c", "d", "e", "f", "g", "h"];
/**
* Conver square number to name
*/
function notation_Pos2Note(pos: Squares.Square): string {
return hqs[pos % 8] + Math.ceil((pos + 1) / 8).toString();
}
/**
* Last square for moves.
*/
const __squareLast: (Squares.Square | Squares.Empty)[][] = [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]];
/**
* Knight moves
*/
const __knightAttacks: (Squares.Square | Squares.Empty)[][] = [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]];
/**
* King moves
*/
const __kingAttacks: (Squares.Square | Squares.Empty)[][] = [
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[],
[]];
const __rankFyleDist: number[] = [
0, 1, 2, 3, 4, 5, 6, 7,
1, 0, 1, 2, 3, 4, 5, 6,
2, 1, 0, 1, 2, 3, 4, 5,
3, 2, 1, 0, 1, 2, 3, 4,
4, 3, 2, 1, 0, 1, 2, 3,
5, 4, 3, 2, 1, 0, 1, 2,
6, 5, 4, 3, 2, 1, 0, 1,
7, 6, 5, 4, 3, 2, 1, 0];
const squareLast = (sq: Squares.Square, dir: number) => {
return __squareLast[sq][dir];
};
export const NullSquare: Squares.Empty = ns;
export function isFyle(f?: number): f is Squares.Fyle {
return (f !== undefined) && (f >= 0) && (f <= 7);
}
export function isRank(r?: number): r is Squares.Rank {
return (r !== undefined) && (r >= 0) && (r <= 7)
}
export function isSquare(sq?: number): sq is Squares.Square {
return (sq !== undefined) && (sq >= 0) && (sq <= 63)
}
export function notEmpty(sq?: Squares.Square): Squares.Square {
return sq!;
}
export function fyle(sq: Squares.Square): Squares.Fyle {
return (sq & 0x7) as Squares.Fyle;
}
export function rank(sq: Squares.Square): Squares.Rank {
return ((sq >> 3) & 0x7) as Squares.Rank;
}
export function create(f: Squares.Fyle, r: Squares.Rank): Squares.Square {
return ((r << 3) | f) as Squares.Square;
}
export function rankFromChar(c: string): Squares.Rank | undefined {
return (c < "1" || c > "8") ? ns : (c.charCodeAt(0) - "1".charCodeAt(0)) as Squares.Rank;
}
export function fyleFromChar(c: string): Squares.Fyle | undefined {
return (c < "a" || c > "h") ? ns : (c.charCodeAt(0) - "a".charCodeAt(0)) as Squares.Fyle;
}
export function leftDiag(sq: Squares.Square): number {
return rank(sq) + fyle(sq);
}
export function rightDiag(sq: Squares.Square): number {
return (7 + rank(sq) - fyle(sq));
}
export function color(sq: Squares.Square): Colors.BW {
return 1 - (leftDiag(sq) & 1) as Colors.BW;
}
export function colorChar(sq: Squares.Square): Colors.Char {
return (color(sq) === Color.Black) ? Color.BlackChar : Color.WhiteChar;
}
export function rankChar(sq: Squares.Square): string {
return (rank(sq) + 1).toString();
}
export function fyleChar(sq: Squares.Square) {
return hqs[fyle(sq)];
}
export function parse(sq: string) {
if (sq && sq.length === 2) {
const f = fyleFromChar(sq[0]);
const r = rankFromChar(sq[1]);
if (isFyle(f) && isRank(r)) {
return create(f, r);
}
}
return ns;
}
export function name(s: Squares.Square) {
return notation_Pos2Note(s);
}
/**
* returns true if the two squares are adjacent.
* Note that diagonal adjacency is included: a1 and b2 are adjacent.
* Also note that a square is adjacent to itself.
*/
export function adjacent(from: Squares.Square, to: Squares.Square): boolean {
const fromRank = rank(from);
const toRank = rank(to);
const rdist = fromRank - toRank;
if (rdist < -1 || rdist > 1) {
return false;
}
const fromFyle = fyle(from);
const toFyle = fyle(to);
const fdist = fromFyle - toFyle;
if (fdist < -1 || fdist > 1) {
return false;
}
return true;
}
/**
* Get direction for move from square @fr to square @to.
*/
export function direction(fr: Squares.Square, to: Squares.Square): Directions.Direction {
return Direction.squareDirection(fr, to);
}
export function move(sq: Squares.Square, dir: Directions.Direction) {
return Direction.squareMove(sq, dir);
}
export function last(sq: Squares.Square, dir: Directions.Direction) {
return squareLast(sq, dir);
}
export function isKnightHop(from: Squares.Square, to: Squares.Square) {
const rdist = __rankFyleDist[(rank(from) << 3) | rank(to)];
const fdist = __rankFyleDist[(fyle(from) << 3) | fyle(to)];
// it is a knight hop only if one distance is two squares and the
// other is one square -- that is, only if their product equals two.
return ((rdist * fdist) === 2);
}
export function knightAttack(from: Squares.Square, to: Squares.Square): Squares.Square | Squares.Empty {
return __knightAttacks[from][to];
}
export function knightAttacks(from: Squares.Square): (Squares.Square | Squares.Empty)[] {
return __knightAttacks[from];
}
export function kingAttack(from: Squares.Square, to: Squares.Square): Squares.Square | Squares.Empty {
return __kingAttacks[from][to];
}
export function kingAttacks(from: Squares.Square): (Squares.Square | Squares.Empty)[] {
return __kingAttacks[from];
}
}