UNPKG

xchess

Version:

Chess Engine

540 lines (418 loc) 9.74 kB
export { Move, Capture, PawnDoubleMove, Castling, KingsideCastling, QueensideCastling, EnPassant, Promotion, CaptureAndPromotion, PromotionRequest, CaptureAndPromotionRequest, } import {Square} from './square.js' import {Piece, Queen, Rook, Bishop, Knight, Pawn} from './piece.js' import {GetMoveDisambiguation, SetMoveDisambiguation, DISAMBIGUATION_NONE, DISAMBIGUATION_FILE, DISAMBIGUATION_RANK, DISAMBIGUATION_FULL} from './move-disambiguator.js' import {INVALID_PIECE_TYPE, MOVE_PROMOTION_NOT_AVAILABLE} from './errors.js' import {MoveEvent, InsertEvent, DeleteEvent, TransferEvent} from './events.js' function code(from, to){ return from << 6 | to; } function iccf(from, to){ return from.iccf + to.iccf; } function IS_PROMOTION_PIECE(piece){ return [Queen, Rook, Bishop, Knight].some(Type => Type.is(piece)); } function PROMOTION_PIECE(value, color){ const piece = Piece.fromColor(value, color); if(!IS_PROMOTION_PIECE(piece)) throw INVALID_PIECE_TYPE(piece); return piece; } class Move { static code(from, to){ return code(Square.from(from), Square.from(to)); } static is(value){ return value instanceof this; } #code; #target; #from; #to; constructor(target, from, to){ this.#target = Piece.from(target); this.#from = Square.from(from); this.#to = Square.from(to); this.#code = code(this.from, this.to); } get code(){ return this.#code; } get iccf(){ return iccf(this.from, this.to); } get target(){ return this.#target; } get from(){ return this.#from; } get to(){ return this.#to; } get color(){ return this.#target.color; } get kc(){ return false; } get qc(){ return false; } get disambiguation(){ return GetMoveDisambiguation(this); } get fromHint(){ switch(this.disambiguation){ case DISAMBIGUATION_NONE: return ''; case DISAMBIGUATION_FILE: return this.from.file.toString(); case DISAMBIGUATION_RANK: return this.from.rank.toString(); } return this.from.toString(); } valueOf(){ return this.code; } toString({pieceLetter = true} = {}){ if(pieceLetter) return `${this.target.moveLetter}${this.fromHint}${this.to}`; return `${this.fromHint}${this.to}`; } // stat match(cond){ const {kc, qc, to, from, file, rank, char, sign, capture, promotion} = cond; if(kc && !this.kc) return false; if(qc && !this.qc) return false; if(to && !this.to.eq(to)) return false; if(from && !this.from.eq(from)) return false; if(rank && (this.from.rank !== rank)) return false; if(file && (this.from.file !== file)) return false; if(char && (this.target.char !== char)) return false; if(sign && (this.target.sign !== sign)) return false; if(capture && !this.capture) return false; if(promotion && !this.isPromotionRequest) return false; return true; } resolve(cond){ if(this.match(cond)){ if(cond.promotion) return this.promote(cond.promotion); return this; } return null; } // rules get isIdle(){ return !Pawn.is(this.target); } get doubleMovePawn(){ return null; } get isPromotionRequest(){ return false; } check(board){ this.next(board); const check = board.check(this.color); this.prev(board); return check; } includes(piece){ return this.target === piece; } promote(piece){ throw MOVE_PROMOTION_NOT_AVAILABLE(this); } // board next(board){ board.set(this.to, this.target); } prev(board){ board.set(this.from, this.target); } // context do(context){ this.next(context.board); } redo(context){ this.next(context.board); } undo(context){ this.prev(context.board); } trigger(context){ context.dispatch(new TransferEvent(this.target, this.from, this.to)); } } class Capture extends Move { #capture; constructor(target, from, to, capture){ super(target, from, to); this.#capture = Piece.from(capture); } get capture(){ return this.#capture; } get captureAt(){ return this.to; } get fromHint(){ if(Pawn.is(this.target)) return this.from.file.toString(); return super.fromHint; } toString({pieceLetter = true} = {}){ if(pieceLetter) return `${this.target.moveLetter}${this.fromHint}x${this.to}`; return `${this.fromHint}x${this.to}`; } // rules get isIdle(){ return false; } includes(piece){ if(this.target === piece) return true; if(this.capture === piece) return true; return false; } // Board prev(board){ board.set(this.from, this.target); board.set(this.captureAt, this.capture); } // Context do(context){ this.next(context.board); context.captureList.add(this.capture); } redo(context){ this.next(context.board); context.captureList.add(this.capture); } undo(context){ this.prev(context.board); context.captureList.delete(this.capture); } trigger(context){ context.dispatch(new TransferEvent(this.target, this.from, this.to)); context.dispatch(new DeleteEvent(this.capture, this.captureAt)); context.dispatch(new MoveEvent('capture', this)); } } class PawnDoubleMove extends Move { get doubleMovePawn(){ return this.target; } } class Castling extends Move { #submove; constructor(target, from, to, submove){ super(target, from, to); this.#submove = submove; } get submove(){ return this.#submove; } // Board next(board){ super.next(board); this.submove.next(board); } prev(board){ this.submove.prev(board); super.prev(board); } // Rules includes(piece){ if(super.includes(piece)) return true; if(this.submove.includes(piece)) return true; return false; } // Context trigger(context){ context.dispatch(new TransferEvent(this.target, this.from, this.to)); context.dispatch(new TransferEvent(this.submove.target, this.submove.from, this.submove.to)); context.dispatch(new MoveEvent('castling', this)); } } class KingsideCastling extends Castling { get kc(){ return true; } toString(){ return '0-0'; } } class QueensideCastling extends Castling { get qc(){ return true; } toString(){ return '0-0-0'; } } class EnPassant extends Capture { #captureAt; constructor(target, from, to, capture, captureAt){ super(target, from, to, capture); this.#captureAt = Square.from(captureAt); } get captureAt(){ return this.#captureAt; } toString(args){ return `${super.toString(args)} e.p.`; } // Board next(board){ board.delete(this.captureAt); board.set(this.to, this.target); } // Context trigger(context){ super.trigger(context); context.dispatch(new MoveEvent('enpassant', this)); } } class PromotionRequest extends Move { get isPromotionRequest(){ return true; } respond(pieceVal){ const piece = PROMOTION_PIECE(pieceVal, this.color); const {target, from, to, disambiguation} = this; const move = new PromotionResponse(target, from, to, piece); SetMoveDisambiguation(move, disambiguation); return move; } promote(pieceVal){ const piece = PROMOTION_PIECE(pieceVal, this.color); const {target, from, to, disambiguation} = this; const move = new Promotion(target, from, to, piece); SetMoveDisambiguation(move, disambiguation); return move; } } class CaptureAndPromotionRequest extends Capture { get isPromotionRequest(){ return true; } respond(pieceVal){ const piece = PROMOTION_PIECE(pieceVal, this.color); const {target, from, to, capture, disambiguation} = this; const move = new CaptureAndPromotionResponse(target, from, to, capture, piece); SetMoveDisambiguation(move, disambiguation); return move; } promote(pieceVal){ const piece = PROMOTION_PIECE(pieceVal, this.color); const {target, from, to, capture, disambiguation} = this; const move = new CaptureAndPromotion(target, from, to, capture, piece); SetMoveDisambiguation(move, disambiguation); return move; } } class Promotion extends Move { #promoteTo; constructor(target, from, to, promoteTo){ super(target, from, to); this.#promoteTo = Piece.from(promoteTo); } get promoteTo(){ return this.#promoteTo; } toString(args){ return `${super.toString(args)}/${this.promoteTo.moveLetter}`; } // Board next(board){ board.delete(this.from); board.set(this.to, this.promoteTo); } prev(board){ board.delete(this.to); board.set(this.from, this.target); } // Context trigger(context){ super.trigger(context); context.dispatch(new DeleteEvent(this.target, this.to)); context.dispatch(new InsertEvent(this.promoteTo, this.to)); } } class CaptureAndPromotion extends Capture { #promoteTo; constructor(target, from, to, capture, promoteTo){ super(target, from, to, capture); this.#promoteTo = Piece.from(promoteTo); } get promoteTo(){ return this.#promoteTo; } toString(args){ return `${super.toString(args)}/${this.promoteTo.moveLetter}`; } // Board next(board){ board.delete(this.from); board.set(this.to, this.promoteTo); } prev(board){ board.set(this.from, this.target); board.set(this.captureAt, this.capture); } // Context trigger(context){ super.trigger(context); context.dispatch(new DeleteEvent(this.target, this.to)); context.dispatch(new InsertEvent(this.promoteTo, this.to)); } } class PromotionResponse extends Promotion { // Context do(context){ context.board.set(this.to, this.promoteTo); } trigger(context){ context.dispatch(new DeleteEvent(this.target, this.to)); context.dispatch(new InsertEvent(this.promoteTo, this.to)); } } class CaptureAndPromotionResponse extends CaptureAndPromotion { // Context do(context){ context.board.set(this.to, this.promoteTo); } trigger(context){ context.dispatch(new DeleteEvent(this.target, this.to)); context.dispatch(new InsertEvent(this.promoteTo, this.to)); } }