xchess
Version:
Chess Engine
420 lines (331 loc) • 6.13 kB
JavaScript
export {Square, squares, at}
import {files} from './file.js'
import {ranks} from './rank.js'
import {white, black} from './color.js'
import {INVALID_SQUARE} from './errors.js'
import {ConstNumberObject} from './ansi-style.js'
import {inspect} from './inspect.js'
const squares = [];
const ReverseSquares = [];
const map = new Map();
function codeAt({x, y}){
return x + y * files.length;
}
function iccfAt({file, rank}){
return String(file.iccf + rank.iccf);
}
function nameAt({file, rank}){
return `${file}${rank}`;
}
function colorAt({x, y}){
return ((x + y) & 1) ? black : white;
}
function A({x, y}){
return x + y;
}
function B({x, y}){
return x - y;
}
function at(x, y){
return files[x]?.squares[y] ?? null;
}
function from(value){
const square = map.get(value);
if(square)
return square;
throw INVALID_SQUARE(value);
}
function is(value){
return map.has(value);
}
function list(... squares){
const list = [];
for(const square of squares)
if(square)
list.push(square);
return Object.freeze(list);
}
function near(target){
return list(
target.topLeft,
target.top,
target.topRight,
target.left,
target.right,
target.bottomLeft,
target.bottom,
target.bottomRight,
);
}
function L(target){
return list(
target.to(2, 1),
target.to(1, 2),
target.to(-1, 2),
target.to(-2, 1),
target.to(-2, -1),
target.to(-1, -2),
target.to(1, -2),
target.to(2, -1),
);
}
function topD(target){
return list(target.topLeft, target.topRight);
}
function bottomD(target){
return list(target.bottomLeft, target.bottomRight);
}
class Square {
static from(value){
return from(value);
}
static is(value){
return is(value);
}
static at(x, y){
return at(x, y);
}
static all(){
return squares;
}
static reverse(){
return ReverseSquares;
}
static InitSquares(){
// check once
if(squares.length > 0)
throw Error('do not use this method!');
// generate squares
for(const rank of ranks){
for(const file of files){
const square = new Square(file, rank);
rank.squares.push(square);
file.squares.push(square);
map.set(square, square);
map.set(square.code, square);
map.set(square.iccf, square);
map.set(square.name, square);
map.set(square.name.toUpperCase(), square);
squares.push(square);
ReverseSquares.unshift(square);
}
}
// bind squares
for(const square of squares){
square.#topLeft = square.to(-1, -1),
square.#top = square.to(0, -1),
square.#topRight = square.to(1, -1),
square.#left = square.to(-1, 0),
square.#right = square.to(1, 0),
square.#bottomLeft = square.to(-1, 1),
square.#bottom = square.to(0, 1),
square.#bottomRight = square.to(1, 1),
square.#near = near(square);
square.#L = L(square);
square.#topD = topD(square);
square.#bottomD = bottomD(square);
}
// freeze collections
for(const rank of ranks)
Object.freeze(rank.squares);
for(const file of files)
Object.freeze(file.squares);
Object.freeze(squares);
}
// Stat
#file;
#rank;
#code;
#iccf;
#name;
#color;
// Geo
#a; #b;
#top;
#left;
#right;
#bottom;
#topLeft;
#topRight;
#bottomLeft;
#bottomRight;
#L;
#near;
#topD;
#bottomD;
constructor(file, rank){
this.#file = file;
this.#rank = rank;
this.#code = codeAt(this);
this.#iccf = iccfAt(this);
this.#name = nameAt(this);
this.#color = colorAt(this);
this.#a = A(this);
this.#b = B(this);
}
get file(){
return this.#file;
}
get rank(){
return this.#rank;
}
get code(){
return this.#code;
}
get iccf(){
return this.#iccf;
}
get name(){
return this.#name;
}
get color(){
return this.#color;
}
valueOf(){
return this.code;
}
toString(){
return this.name;
}
toJSON(){
return this.name;
}
[inspect.custom](depth, opts){
return ConstNumberObject(`[Square ${this.name.toUpperCase()}]`);
}
// Geo
get x(){
return this.file.x;
}
get y(){
return this.rank.y;
}
get a(){
return this.#a;
}
get b(){
return this.#b;
}
eq(square){
return this === from(square);
}
dx(square){
return from(square).x - this.x;
}
dy(square){
return from(square).y - this.y;
}
compare(square){
return [this.dx(square), this.dy(square)];
}
isNear(square){
return this.#near.includes(from(square));
}
isL(square){
return this.#L.includes(from(square));
}
isTopD(square){
return this.#topD.includes(from(square));
}
isBottomD(square){
return this.#bottomD.includes(from(square));
}
// square
to(dx, dy){
return at(this.x + dx, this.y + dy);
}
get topLeft(){
return this.#topLeft;
}
get top(){
return this.#top;
}
get topRight(){
return this.#topRight;
}
get left(){
return this.#left;
}
get right(){
return this.#right;
}
get bottomLeft(){
return this.#bottomLeft;
}
get bottom(){
return this.#bottom;
}
get bottomRight(){
return this.#bottomRight;
}
// squares
get near(){
return this.#near;
}
get L(){
return this.#L;
}
get topD(){
return this.#topD;
}
get bottomD(){
return this.#bottomD;
}
* walkTopLeft(){
let next = this;
while(next = next.topLeft)
yield next;
}
* walkTop(){
let next = this;
while(next = next.top)
yield next;
}
* walkTopRight(){
let next = this;
while(next = next.topRight)
yield next;
}
* walkLeft(){
let next = this;
while(next = next.left)
yield next;
}
* walkRight(){
let next = this;
while(next = next.right)
yield next;
}
* walkBottomLeft(){
let next = this;
while(next = next.bottomLeft)
yield next;
}
* walkBottom(){
let next = this;
while(next = next.bottom)
yield next;
}
* walkBottomRight(){
let next = this;
while(next = next.bottomRight)
yield next;
}
* walkT(){
yield this.walkTop();
yield this.walkRight();
yield this.walkBottom();
yield this.walkLeft();
}
* walkX(){
yield this.walkTopRight();
yield this.walkBottomRight();
yield this.walkBottomLeft();
yield this.walkTopLeft();
}
* walkAll(){
yield * this.walkT();
yield * this.walkX();
}
}
Square.InitSquares();