xchess
Version:
Chess Engine
311 lines (245 loc) • 5.42 kB
JavaScript
export {Castling}
import {white, black} from './color.js'
import {ranks} from './rank.js'
import {King, Rook} from './piece.js'
import {Move, KingsideCastling, QueensideCastling} from './move.js'
import {INVALID_COLOR} from './errors.js'
class Castling {
static from(context){
return CreateFrom(context.color, context);
}
static prevFrom(context){
return CreatePrevFrom(context.color, context);
}
// Config
get kingside(){
return false;
}
get queenside(){
return false;
}
// Rules
next(move){
return this;
}
* moves(context){
// abstract
}
}
class IdleCastling extends Castling {}
class SingleCastling extends Castling {}
const Idle = new IdleCastling();
const WhiteCreator = CastlingCreator(white, ranks[7]);
const BlackCreator = CastlingCreator(black, ranks[0]);
function CreateFrom(color, context){
if(white.eq(color))
return new CreateWhite(context);
if(black.eq(color))
return new CreateBlack(context);
throw INVALID_COLOR(color);
}
function CreatePrevFrom(color, context){
return CreateFrom(color.invert(), context);
}
function CreateWhite({board, wk, wq}){
return WhiteCreator(board, wk, wq);
}
function CreateBlack({board, bk, bq}){
return BlackCreator(board, bk, bq);
}
function CastlingCreator(color, rank){
const [A, B, C, D, E, F, G, H] = rank.squares;
function IsKing(piece){
return King.is(piece) && color.eq(piece.color);
}
function IsRook(piece){
return Rook.is(piece) && color.eq(piece.color);
}
function GetKing(board){
const king = board.get(E);
if(IsKing(king)) return king;
}
function GetKRook(board){
const rook = board.get(H);
if(IsRook(rook)) return rook;
}
function GetQRook(board){
const rook = board.get(A);
if(IsRook(rook)) return rook;
}
function TestAt(board, square){
if(board.has(square))
return false;
if(board.testAt(square, color))
return false;
return true;
}
function TestKingside(board){
if(!TestAt(board, F))
return false;
if(!TestAt(board, G))
return false;
return true;
}
function TestQueenside(board){
if(board.has(B))
return false;
if(!TestAt(board, C))
return false;
if(!TestAt(board, D))
return false;
return true;
}
function KingMove(king, rook){
const submove = new Move(rook, H, F);
return new KingsideCastling(king, E, G, submove);
}
function QueenMove(king, rook){
const submove = new Move(rook, A, D);
return new QueensideCastling(king, E, C, submove);
}
function * KingMoves({isCheck, board}, king, rook){
if(!isCheck && TestKingside(board))
yield KingMove(king, rook);
}
function * QueenMoves({isCheck, board}, king, rook){
if(!isCheck && TestQueenside(board))
yield QueenMove(king, rook);
}
class TwinCastling extends Castling {
#king;
#krook;
#qrook;
constructor(king, krook, qrook){
super();
this.#king = king;
this.#krook = krook;
this.#qrook = qrook;
}
get king(){
return this.#king;
}
get krook(){
return this.#krook;
}
get qrook(){
return this.#qrook;
}
get kingside(){
return true;
}
get queenside(){
return true;
}
// Rules
next(move){
if(move.includes(this.king))
return Idle;
if(move.includes(this.krook))
return new QueenCastling(this.king, this.qrook);
if(move.includes(this.qrook))
return new KingCastling(this.king, this.krook);
return this;
}
* moves(context){
yield * KingMoves(context, this.king, this.krook);
yield * QueenMoves(context, this.king, this.qrook);
}
}
class KingCastling extends Castling {
#king;
#rook;
constructor(king, rook){
super();
this.#king = king;
this.#rook = rook;
}
get king(){
return this.#king;
}
get rook(){
return this.#rook;
}
get kingside(){
return true;
}
// Rules
next(move){
if(move.includes(this.king))
return Idle;
if(move.includes(this.rook))
return Idle;
return this;
}
moves(context){
return KingMoves(context, this.king, this.rook);
}
}
class QueenCastling extends Castling {
#king;
#rook;
constructor(king, rook){
super();
this.#king = king;
this.#rook = rook;
}
get king(){
return this.#king;
}
get rook(){
return this.#rook;
}
get queenside(){
return true;
}
// Rules
next(move){
if(move.includes(this.king))
return Idle;
if(move.includes(this.rook))
return Idle;
return this;
}
moves(context){
return QueenMoves(context, this.king, this.rook);
}
}
function CreateTwin(board){
const king = GetKing(board);
if(king){
const krook = GetKRook(board);
const qrook = GetQRook(board);
if(krook){
if(qrook)
return new TwinCastling(king, krook, qrook);
return new KingCastling(king, krook);
} if(qrook)
return new QueenCastling(king, qrook);
} return Idle;
}
function CreateKing(board){
const king = GetKing(board);
const rook = GetKRook(board);
if(king && rook)
return new KingCastling(king, rook);
return Idle;
}
function CreateQueen(board){
const king = GetKing(board);
const rook = GetQRook(board);
if(king && rook)
return new QueenCastling(king, rook);
return Idle;
}
function CreateCastling(board, kingside, queenside){
if(kingside){
if(queenside)
return CreateTwin(board);
return CreateKing(board);
}
if(queenside)
return CreateQueen(board);
return Idle;
}
return CreateCastling;
}