xchess
Version:
Chess Engine
314 lines (264 loc) • 5.6 kB
JavaScript
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 './standart-piece.js'
import {Castling} from './castling.js'
import {fenToState} from './fen.js'
import {IdleDrawOffer} from './draw-offer.js'
import {all} from './piece-index.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.context.pieces);
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({
pieces = all,
board = null,
captureList = [],
stateCounter = [],
stateHash,
color = white,
count = 0,
halfmoveClock = 0,
enPassantTarget = null,
doubleMovePawn = null,
wk = true,
wq = true,
bk = true,
bq = true,
}){
this.context.setPieceList(pieces);
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.first = this.state;
this.state.execute();
this.state.emit('play');
this.state.trigger();
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();
}
}