UNPKG

xchess

Version:

Chess Engine

328 lines (267 loc) 5.63 kB
export {MovementState, PlayState, MoveState} import {EmptyMoveMap} from './move-map.js' import {MoveEvent} from './events.js' import {stateToHash} from './hash.js' import {MOVE_NOT_AVAILABLE} from './errors.js' import {GameState} from './game-state.js' import {PromotionState} from './promotion-state.js' import {Transitional} from './transitional-state.js' import { CheckmateState, StalemateState, DeadPositionState, FivefoldRepetitionState, SeventyFiveMovesState, } from './end-state.js' class MovementState extends Transitional(GameState) { #gameColor; #count; #halfmoveClock; #doubleMovePawn; #castling; #prevCastling; #moves; #isCheck; #checkKing = null; #hash; #repetition; #newRepetition; #drawOffer; constructor({ context, prev, gameColor, count, halfmoveClock, repetition, doubleMovePawn, castling, prevCastling, drawOffer, }){ super(context, prev); this.#gameColor = gameColor; this.#count = count; this.#halfmoveClock = halfmoveClock; this.#doubleMovePawn = doubleMovePawn; this.#castling = castling; this.#prevCastling = prevCastling; this.#moves = this.board.moves(this); const king = this.board.check(this.color); if(king){ this.#isCheck = true; this.#checkKing = king; } else { this.#isCheck = false; this.#checkKing = null; } this.#hash = stateToHash(this); this.#drawOffer = drawOffer; this.#nextRepetition(repetition); } // Config get color(){ return this.#gameColor.color; } get gameColor(){ return this.#gameColor; } get count(){ return this.#count; } get halfmoveClock(){ return this.#halfmoveClock; } get doubleMovePawn(){ return this.#doubleMovePawn; } get castling(){ return this.#castling; } get prevCastling(){ return this.#prevCastling; } get moves(){ return this.#moves; } get isCheck(){ return this.#isCheck; } get checkKing(){ return this.#checkKing; } get checkKingTarget(){ if(this.checkKing) return this.board.find(this.checkKing); return null; } get hash(){ return this.#hash; } get repetition(){ return this.#repetition; } get ownRepetition(){ return this.stateCounter.count(this.hash); } get newRepetition(){ return this.#newRepetition; } get drawOffer(){ return this.#drawOffer; } get wk(){ return this.gameColor.wk(this); } get wq(){ return this.gameColor.wq(this); } get bk(){ return this.gameColor.bk(this); } get bq(){ return this.gameColor.bq(this); } setDrawOffer(drawOffer){ this.#drawOffer = drawOffer; } // Stat get isMovement(){ return true; } get isDrawOffer(){ return this.drawOffer.isDrawOffer; } // Move Config #nextRepetition(prev){ const own = this.stateCounter.add(this.hash); if(own > prev){ this.#repetition = own; this.#newRepetition = own; } else { this.#repetition = prev; this.#newRepetition = 0; } } #nextClock(isIdle){ if(isIdle) return this.halfmoveClock + 1; return 0; } // Game Events move(moveVal, prev = this){ const move = this.moves.get(moveVal); if(move){ if(move.isPromotionRequest) this.#promotion(move, prev); else this.do(move, prev); return move; } else throw MOVE_NOT_AVAILABLE(moveVal); } // Movement do(move, prev = this){ const {context} = this; move.do(context); const {doubleMovePawn, isIdle} = move; const gameColor = this.gameColor.invert(); const count = this.count + 1; const halfmoveClock = this.#nextClock(isIdle); const repetition = this.repetition; const castling = this.prevCastling.next(move); const prevCastling = this.castling.next(move); const drawOffer = this.drawOffer.next(); this.state = new MoveState(move, { context, prev, gameColor, count, halfmoveClock, repetition, doubleMovePawn, castling, prevCastling, drawOffer, }); this.state.execute(); this.next = this.state; this.drawTrigger(drawOffer); move.trigger(this.context); this.dispatch(new MoveEvent('move', move)); this.state.trigger(); } #promotion(move, prev){ move.do(this.context); this.state = new PromotionState(move, this, prev); this.next = this.state; move.trigger(this.context); this.state.trigger(); } #execute(){ if(this.moves.size < 1){ if(this.isCheck) return new CheckmateState(this, this.prev, this.color); else return new StalemateState(this, this.prev); } if(this.newRepetition == 5){ return new FivefoldRepetitionState(this, this.prev); } if(this.halfmoveClock == 150) return new SeventyFiveMovesState(this, this.prev); if(this.isDeadPosition()) return new DeadPositionState(this, this.prev); } execute(){ const FinalState = this.#execute(); if(FinalState){ this.state = FinalState; this.next = FinalState; } } drawTrigger(drawOffer){ if(drawOffer !== this.drawOffer) this.emit('rejected-draw'); } trigger(){ if(this.isCheck) this.emit('check'); if(this.halfmoveClock == 100) this.emit('50-move'); if(this.newRepetition > 0){ this.emit('repeat'); if(this.newRepetition == 3) this.emit('3-repeat'); } } } class PlayState extends MovementState { // Stat get status(){ return 'play'; } } class MoveState extends MovementState { #move; constructor(move, stateConfig){ super(stateConfig); this.#move = move; } // Stat get status(){ return 'move'; } // Config get lastMove(){ return this.#move; } // Log Event undo(){ this.stateCounter.delete(this.hash); this.#move.undo(this.context); } redo(){ this.stateCounter.add(this.hash); this.#move.redo(this.context); } }