UNPKG

xchess

Version:

Chess Engine

311 lines (261 loc) 5.48 kB
export {SetupState} import {GameState} from './game-state.js' import {PlayState} from './movement-state.js' import {white, Color} from './color.js' import {GameColor} from './game-color.js' import {Square} from './square.js' import {Pawn} from './piece.js' import {Castling} from './castling.js' import {fenToState} from './fen.js' import {IdleDrawOffer} from './draw-offer.js' import { IS_EMPTY, IS_STRING, IS_OBJECT, } from './types.js' import { GAME_NOT_STARTED, INVALID_MOVE_COUNT, INVALID_FULLMOVE_NUMBER, INVALID_HALFMOVE_CLOCK, INVALID_DOUBLE_MOVE_PAWN, FOREIGN_PAWN, INVALID_GAME_CONFIG, PAWN_REQUERED, SQUARE_IS_EMPTY, } from './errors.js' function IS_COUNT(value){ if(!Number.isSafeInteger(value)) return false; return true; } function VALIDATE_MOVE_COUNT(count){ if(IS_COUNT(count)) return true; throw INVALID_MOVE_COUNT(count); } function VALIDATE_FULLMOVE_NUMBER(value){ if(IS_COUNT(value)) return true; throw INVALID_FULLMOVE_NUMBER(value); } function VALIDATE_HALFMOVE_CLOCK(value){ if(IS_COUNT(value)) return true; throw INVALID_HALFMOVE_CLOCK(value); } function VALIDATE_DOUBLE_MOVE_PAWN(pawn){ if(pawn === null) return true; if(Pawn.is(pawn)) return true; throw INVALID_DOUBLE_MOVE_PAWN(pawn); } class SetupState extends GameState { #gameColor = GameColor.white; #count = 0; #halfmoveClock = 0; #doubleMovePawn = null; #wk = true; #wq = true; #bk = true; #bq = true; // 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 wk(){ return this.#wk; } get wq(){ return this.#wq; } get bk(){ return this.#bk; } get bq(){ return this.#bq; } // Setup access(){ // do nothing } setCount(count){ VALIDATE_MOVE_COUNT(count); this.#count = Math.trunc(count); } setFullmoveNumber(value){ VALIDATE_FULLMOVE_NUMBER(value); this.#count = ((Math.trunc(value) - 1) << 1) | this.color; } setHalfmoveClock(value){ VALIDATE_HALFMOVE_CLOCK(value); this.#halfmoveClock = Math.trunc(value); } setColor(value){ this.#gameColor = GameColor.from(value); } setDoubleMovePawn(pawn){ VALIDATE_DOUBLE_MOVE_PAWN(pawn); if(this.board.contains(pawn)) this.#doubleMovePawn = pawn; else throw FOREIGN_PAWN(pawn); } setEnPassantTarget(square){ if(square === null){ this.#doubleMovePawn = null; } else { const pawnAt = Square.from(square); const piece = this.board.get(pawnAt); if(!piece) throw SQUARE_IS_EMPTY(pawnAt); if(!Pawn.is(piece)) throw PAWN_REQUERED(piece); this.#doubleMovePawn = piece; } } setWK(value){ this.#wk = Boolean(value); } setWQ(value){ this.#wq = Boolean(value); } setBK(value){ this.#bk = Boolean(value); } setBQ(value){ this.#bq = Boolean(value); } setBoard(board){ this.game.board.setAll(board); } setCaptureList(pieceList){ this.captureList.setAll(pieceList); } setFEN(fen){ const { board, color, fullmoveNumber, halfmoveClock, doubleMovePawn, castling, } = fenToState(fen); this.setBoard(board); this.#gameColor = GameColor.from(color); this.setFullmoveNumber(fullmoveNumber); this.#halfmoveClock = halfmoveClock; this.#wk = castling.has('K'); this.#wq = castling.has('Q'); this.#bk = castling.has('k'); this.#bq = castling.has('q'); if(Pawn.is(doubleMovePawn)) this.#doubleMovePawn = doubleMovePawn; else this.#doubleMovePawn = null; } setAll(config){ if(IS_STRING(config)) this.setFEN(config); else if(IS_OBJECT(config)) this.setConfig(config); else if(!IS_EMPTY(config)) throw INVALID_GAME_CONFIG(config); } setConfig({ board = null, captureList = [], stateCounter = [], stateHash, color = white, count = 0, halfmoveClock = 0, enPassantTarget = null, doubleMovePawn = null, wk = true, wq = true, bk = true, bq = true, }){ this.setBoard(board); this.captureList.setAll(captureList); this.stateCounter.setAll(stateCounter); this.stateCounter.delete(stateHash); this.setCount(count); this.setHalfmoveClock(halfmoveClock); this.setColor(color); this.setWK(wk); this.setWQ(wq); this.setBK(bk); this.setBQ(bq); if(doubleMovePawn) this.setDoubleMovePawn(doubleMovePawn); else if(enPassantTarget) this.setEnPassantTarget(enPassantTarget); } // Stat get status(){ return 'setup'; } get isSetup(){ return true; } // Game Events play(){ const {context, gameColor, count, halfmoveClock, doubleMovePawn} = this; const repetition = 0; const castling = Castling.from(this.context); const prevCastling = Castling.prevFrom(this.context); const drawOffer = new IdleDrawOffer(this.context); const state = new PlayState({ context, gameColor, count, halfmoveClock, repetition, doubleMovePawn, castling, prevCastling, drawOffer, }); this.state = state; this.state.execute(); this.state.emit('play'); this.state.trigger(); this.first = this.state; return true; } forfeit(color){ throw GAME_NOT_STARTED(); } resign(color){ throw GAME_NOT_STARTED(); } draw(color){ throw GAME_NOT_STARTED(); } toDraw(color){ throw GAME_NOT_STARTED(); } flagFall(){ throw GAME_NOT_STARTED(); } move(move){ throw GAME_NOT_STARTED(); } promote(piece){ throw GAME_NOT_STARTED(); } }