export const WHITE="w";export const BLACK="b";export const PAWN="p";export const KNIGHT="n";export const BISHOP="b";export const ROOK="r";export const QUEEN="q";export const KING="k";export const PIECE_SYMBOLS=["p","n","b","r","q","k"];export const DEFAULT_POSITION="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";const EMPTY=-1;const FLAGS={NORMAL:"n",CAPTURE:"c",BIG_PAWN:"b",EP_CAPTURE:"e",PROMOTION:"p",KSIDE_CASTLE:"k",QSIDE_CASTLE:"q"};export const SQUARES=["a8","b8","c8","d8","e8","f8","g8","h8","a7","b7","c7","d7","e7","f7","g7","h7","a6","b6","c6","d6","e6","f6","g6","h6","a5","b5","c5","d5","e5","f5","g5","h5","a4","b4","c4","d4","e4","f4","g4","h4","a3","b3","c3","d3","e3","f3","g3","h3","a2","b2","c2","d2","e2","f2","g2","h2","a1","b1","c1","d1","e1","f1","g1","h1"];const BITS={NORMAL:1,CAPTURE:2,BIG_PAWN:4,EP_CAPTURE:8,PROMOTION:16,KSIDE_CASTLE:32,QSIDE_CASTLE:64};export const Ox88={a8:0,b8:1,c8:2,d8:3,e8:4,f8:5,g8:6,h8:7,a7:16,b7:17,c7:18,d7:19,e7:20,f7:21,g7:22,h7:23,a6:32,b6:33,c6:34,d6:35,e6:36,f6:37,g6:38,h6:39,a5:48,b5:49,c5:50,d5:51,e5:52,f5:53,g5:54,h5:55,a4:64,b4:65,c4:66,d4:67,e4:68,f4:69,g4:70,h4:71,a3:80,b3:81,c3:82,d3:83,e3:84,f3:85,g3:86,h3:87,a2:96,b2:97,c2:98,d2:99,e2:100,f2:101,g2:102,h2:103,a1:112,b1:113,c1:114,d1:115,e1:116,f1:117,g1:118,h1:119};const PAWN_OFFSETS={b:[16,32,17,15],w:[-16,-32,-17,-15]};const PIECE_OFFSETS={n:[-18,-33,-31,-14,18,33,31,14],b:[-17,-15,17,15],r:[-16,1,16,-1],q:[-17,-16,-15,1,17,16,15,-1],k:[-17,-16,-15,1,17,16,15,-1]};const ATTACKS=[20,0,0,0,0,0,0,24,0,0,0,0,0,0,20,0,0,20,0,0,0,0,0,24,0,0,0,0,0,20,0,0,0,0,20,0,0,0,0,24,0,0,0,0,20,0,0,0,0,0,0,20,0,0,0,24,0,0,0,20,0,0,0,0,0,0,0,0,20,0,0,24,0,0,20,0,0,0,0,0,0,0,0,0,0,20,2,24,2,20,0,0,0,0,0,0,0,0,0,0,0,2,53,56,53,2,0,0,0,0,0,0,24,24,24,24,24,24,56,0,56,24,24,24,24,24,24,0,0,0,0,0,0,2,53,56,53,2,0,0,0,0,0,0,0,0,0,0,0,20,2,24,2,20,0,0,0,0,0,0,0,0,0,0,20,0,0,24,0,0,20,0,0,0,0,0,0,0,0,20,0,0,0,24,0,0,0,20,0,0,0,0,0,0,20,0,0,0,0,24,0,0,0,0,20,0,0,0,0,20,0,0,0,0,0,24,0,0,0,0,0,20,0,0,20,0,0,0,0,0,0,24,0,0,0,0,0,0,20];const RAYS=[17,0,0,0,0,0,0,16,0,0,0,0,0,0,15,0,0,17,0,0,0,0,0,16,0,0,0,0,0,15,0,0,0,0,17,0,0,0,0,16,0,0,0,0,15,0,0,0,0,0,0,17,0,0,0,16,0,0,0,15,0,0,0,0,0,0,0,0,17,0,0,16,0,0,15,0,0,0,0,0,0,0,0,0,0,17,0,16,0,15,0,0,0,0,0,0,0,0,0,0,0,0,17,16,15,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,-15,-16,-17,0,0,0,0,0,0,0,0,0,0,0,0,-15,0,-16,0,-17,0,0,0,0,0,0,0,0,0,0,-15,0,0,-16,0,0,-17,0,0,0,0,0,0,0,0,-15,0,0,0,-16,0,0,0,-17,0,0,0,0,0,0,-15,0,0,0,0,-16,0,0,0,0,-17,0,0,0,0,-15,0,0,0,0,0,-16,0,0,0,0,0,-17,0,0,-15,0,0,0,0,0,0,-16,0,0,0,0,0,0,-17];const PIECE_MASKS={p:1,n:2,b:4,r:8,q:16,k:32};const SYMBOLS="pnbrqkPNBRQK";const PROMOTIONS=[KNIGHT,BISHOP,ROOK,QUEEN];const RANK_1=7;const RANK_2=6;const RANK_7=1;const RANK_8=0;const ROOKS={w:[{square:Ox88.a1,flag:BITS.QSIDE_CASTLE},{square:Ox88.h1,flag:BITS.KSIDE_CASTLE}],b:[{square:Ox88.a8,flag:BITS.QSIDE_CASTLE},{square:Ox88.h8,flag:BITS.KSIDE_CASTLE}]};const SECOND_RANK={b:RANK_7,w:RANK_2};const TERMINATION_MARKERS=["1-0","0-1","1/2-1/2","*"];export function rank(square){return square>>4}export function file(square){return square&15}function isDigit(c){return"0123456789".indexOf(c)!==-1}function algebraic(square){const f=file(square);const r=rank(square);return"abcdefgh".substring(f,f+1)+"87654321".substring(r,r+1)}function swapColor(color){return color===WHITE?BLACK:WHITE}export function validateFen(fen){const tokens=fen.split(/\s+/);if(tokens.length!==6){return{ok:false,error:"Invalid FEN: must contain six space-delimited fields"}}const moveNumber=parseInt(tokens[5],10);if(isNaN(moveNumber)||moveNumber<=0){return{ok:false,error:"Invalid FEN: move number must be a positive integer"}}const halfMoves=parseInt(tokens[4],10);if(isNaN(halfMoves)||halfMoves<0){return{ok:false,error:"Invalid FEN: half move counter number must be a non-negative integer"}}if(!/^(-|[abcdefgh][36])$/.test(tokens[3])){return{ok:false,error:"Invalid FEN: en-passant square is invalid"}}if(/[^kKqQ-]/.test(tokens[2])){return{ok:false,error:"Invalid FEN: castling availability is invalid"}}if(!/^(w|b)$/.test(tokens[1])){return{ok:false,error:"Invalid FEN: side-to-move is invalid"}}const rows=tokens[0].split("/");if(rows.length!==8){return{ok:false,error:"Invalid FEN: piece data does not contain 8 '/'-delimited rows"}}for(let i=0;i1){return{ok:false,error:`Invalid FEN: too many ${color} kings`}}}return{ok:true}}function getDisambiguator(move,moves){const from=move.from;const to=move.to;const piece=move.piece;let ambiguities=0;let sameRank=0;let sameFile=0;for(let i=0,len=moves.length;i0){if(sameRank>0&&sameFile>0){return algebraic(from)}else if(sameFile>0){return algebraic(from).charAt(1)}else{return algebraic(from).charAt(0)}}return""}function addMove(moves,color,from,to,piece,captured=undefined,flags=BITS.NORMAL){const r=rank(to);if(piece===PAWN&&(r===RANK_1||r===RANK_8)){for(let i=0;i="a"&&pieceType<="h"){const matches=san.match(/[a-h]\d.*[a-h]\d/);if(matches){return undefined}return PAWN}pieceType=pieceType.toLowerCase();if(pieceType==="o"){return KING}return pieceType}function strippedSan(move){return move.replace(/=/,"").replace(/[+#]?[?!]*$/,"")}export class Chess{constructor(fen=DEFAULT_POSITION){this._board=new Array(128);this._turn=WHITE;this._header={};this._kings={w:EMPTY,b:EMPTY};this._epSquare=-1;this._halfMoves=0;this._moveNumber=0;this._history=[];this._comments={};this._castling={w:0,b:0};this.load(fen)}clear(keepHeaders=false){this._board=new Array(128);this._kings={w:EMPTY,b:EMPTY};this._turn=WHITE;this._castling={w:0,b:0};this._epSquare=EMPTY;this._halfMoves=0;this._moveNumber=1;this._history=[];this._comments={};this._header=keepHeaders?this._header:{};this._updateSetup(this.fen())}load(fen,keepHeaders=false){let tokens=fen.split(/\s+/);if(tokens.length>=2&&tokens.length<6){const adjustments=["-","-","0","1"];fen=tokens.concat(adjustments.slice(-(6-tokens.length))).join(" ")}tokens=fen.split(/\s+/);const{ok,error}=validateFen(fen);if(!ok){throw new Error(error)}const position=tokens[0];let square=0;this.clear(keepHeaders);for(let i=0;i-1){this._castling.w|=BITS.KSIDE_CASTLE}if(tokens[2].indexOf("Q")>-1){this._castling.w|=BITS.QSIDE_CASTLE}if(tokens[2].indexOf("k")>-1){this._castling.b|=BITS.KSIDE_CASTLE}if(tokens[2].indexOf("q")>-1){this._castling.b|=BITS.QSIDE_CASTLE}this._epSquare=tokens[3]==="-"?EMPTY:Ox88[tokens[3]];this._halfMoves=parseInt(tokens[4],10);this._moveNumber=parseInt(tokens[5],10);this._updateSetup(this.fen())}fen(){var _a,_b;let empty=0;let fen="";for(let i=Ox88.a8;i<=Ox88.h1;i++){if(this._board[i]){if(empty>0){fen+=empty;empty=0}const{color,type:piece}=this._board[i];fen+=color===WHITE?piece.toUpperCase():piece.toLowerCase()}else{empty++}if(i+1&136){if(empty>0){fen+=empty}if(i!==Ox88.h1){fen+="/"}empty=0;i+=8}}let castling="";if(this._castling[WHITE]&BITS.KSIDE_CASTLE){castling+="K"}if(this._castling[WHITE]&BITS.QSIDE_CASTLE){castling+="Q"}if(this._castling[BLACK]&BITS.KSIDE_CASTLE){castling+="k"}if(this._castling[BLACK]&BITS.QSIDE_CASTLE){castling+="q"}castling=castling||"-";let epSquare="-";if(this._epSquare!==EMPTY){const bigPawnSquare=this._epSquare+(this._turn===WHITE?16:-16);const squares=[bigPawnSquare+1,bigPawnSquare-1];for(const square of squares){if(square&136){continue}const color=this._turn;if(((_a=this._board[square])===null||_a===void 0?void 0:_a.color)===color&&((_b=this._board[square])===null||_b===void 0?void 0:_b.type)===PAWN){this._makeMove({color:color,from:square,to:this._epSquare,piece:PAWN,captured:PAWN,flags:BITS.EP_CAPTURE});const isLegal=!this._isKingAttacked(color);this._undoMove();if(isLegal){epSquare=algebraic(this._epSquare);break}}}}return[fen,this._turn,castling,epSquare,this._halfMoves,this._moveNumber].join(" ")}_updateSetup(fen){if(this._history.length>0)return;if(fen!==DEFAULT_POSITION){this._header["SetUp"]="1";this._header["FEN"]=fen}else{delete this._header["SetUp"];delete this._header["FEN"]}}reset(){this.load(DEFAULT_POSITION)}get(square){var _a;return(_a=this._board[Ox88[square]])!==null&&_a!==void 0?_a:false}put({type,color},square){if(SYMBOLS.indexOf(type.toLowerCase())===-1){return false}if(!(square in Ox88)){return false}const sq=Ox88[square];if(type==KING&&!(this._kings[color]==EMPTY||this._kings[color]==sq)){return false}this._board[sq]={type:type,color:color};if(type===KING){this._kings[color]=sq}this._updateSetup(this.fen());return true}remove(square){const piece=this.get(square);delete this._board[Ox88[square]];if(piece&&piece.type===KING){this._kings[piece.color]=EMPTY}this._updateSetup(this.fen());return piece}_attacked(color,square){for(let i=Ox88.a8;i<=Ox88.h1;i++){if(i&136){i+=7;continue}if(this._board[i]===undefined||this._board[i].color!==color){continue}const piece=this._board[i];const difference=i-square;if(difference===0){continue}const index=difference+119;if(ATTACKS[index]&PIECE_MASKS[piece.type]){if(piece.type===PAWN){if(difference>0){if(piece.color===WHITE)return true}else{if(piece.color===BLACK)return true}continue}if(piece.type==="n"||piece.type==="k")return true;const offset=RAYS[index];let j=i+offset;let blocked=false;while(j!==square){if(this._board[j]!=null){blocked=true;break}j+=offset}if(!blocked)return true}}return false}_isKingAttacked(color){return this._attacked(swapColor(color),this._kings[color])}isAttacked(square,attackedBy){return this._attacked(attackedBy,Ox88[square])}isCheck(){return this._isKingAttacked(this._turn)}inCheck(){return this.isCheck()}isCheckmate(){return this.isCheck()&&this._moves().length===0}isStalemate(){return!this.isCheck()&&this._moves().length===0}isInsufficientMaterial(){const pieces={b:0,n:0,r:0,q:0,k:0,p:0};const bishops=[];let numPieces=0;let squareColor=0;for(let i=Ox88.a8;i<=Ox88.h1;i++){squareColor=(squareColor+1)%2;if(i&136){i+=7;continue}const piece=this._board[i];if(piece){pieces[piece.type]=piece.type in pieces?pieces[piece.type]+1:1;if(piece.type===BISHOP){bishops.push(squareColor)}numPieces++}}if(numPieces===2){return true}else if(numPieces===3&&(pieces[BISHOP]===1||pieces[KNIGHT]===1)){return true}else if(numPieces===pieces[BISHOP]+2){let sum=0;const len=bishops.length;for(let i=0;i=3){repetition=true}const move=moves.pop();if(!move){break}else{this._makeMove(move)}}return repetition}isDraw(){return this._halfMoves>=100||this.isStalemate()||this.isInsufficientMaterial()||this.isThreefoldRepetition()}isGameOver(){return this.isCheckmate()||this.isStalemate()||this.isDraw()}moves({verbose=false,square=undefined,piece=undefined}={}){const moves=this._moves({square:square,piece:piece});if(verbose){return moves.map(move=>this._makePretty(move))}else{return moves.map(move=>this._moveToSan(move,moves))}}_moves({legal=true,piece=undefined,square=undefined}={}){var _a;const forSquare=square?square.toLowerCase():undefined;const forPiece=piece===null||piece===void 0?void 0:piece.toLowerCase();const moves=[];const us=this._turn;const them=swapColor(us);let firstSquare=Ox88.a8;let lastSquare=Ox88.h1;let singleSquare=false;if(forSquare){if(!(forSquare in Ox88)){return[]}else{firstSquare=lastSquare=Ox88[forSquare];singleSquare=true}}for(let from=firstSquare;from<=lastSquare;from++){if(from&136){from+=7;continue}if(!this._board[from]||this._board[from].color===them){continue}const{type}=this._board[from];let to;if(type===PAWN){if(forPiece&&forPiece!==type)continue;to=from+PAWN_OFFSETS[us][0];if(!this._board[to]){addMove(moves,us,from,to,PAWN);to=from+PAWN_OFFSETS[us][1];if(SECOND_RANK[us]===rank(from)&&!this._board[to]){addMove(moves,us,from,to,PAWN,undefined,BITS.BIG_PAWN)}}for(let j=2;j<4;j++){to=from+PAWN_OFFSETS[us][j];if(to&136)continue;if(((_a=this._board[to])===null||_a===void 0?void 0:_a.color)===them){addMove(moves,us,from,to,PAWN,this._board[to].type,BITS.CAPTURE)}else if(to===this._epSquare){addMove(moves,us,from,to,PAWN,PAWN,BITS.EP_CAPTURE)}}}else{if(forPiece&&forPiece!==type)continue;for(let j=0,len=PIECE_OFFSETS[type].length;j{const comment=this._comments[this.fen()];if(typeof comment!=="undefined"){const delimiter=moveString.length>0?" ":"";moveString=`${moveString}${delimiter}{${comment}}`}return moveString};const reversedHistory=[];while(this._history.length>0){reversedHistory.push(this._undoMove())}const moves=[];let moveString="";if(reversedHistory.length===0){moves.push(appendComment(""))}while(reversedHistory.length>0){moveString=appendComment(moveString);const move=reversedHistory.pop();if(!move){break}if(!this._history.length&&move.color==="b"){const prefix=`${this._moveNumber}. ...`;moveString=moveString?`${moveString} ${prefix}`:prefix}else if(move.color==="w"){if(moveString.length){moves.push(moveString)}moveString=this._moveNumber+"."}moveString=moveString+" "+this._moveToSan(move,this._moves({legal:true}));this._makeMove(move)}if(moveString.length){moves.push(appendComment(moveString))}if(typeof this._header.Result!=="undefined"){moves.push(this._header.Result)}if(maxWidth===0){return result.join("")+moves.join(" ")}const strip=function(){if(result.length>0&&result[result.length-1]===" "){result.pop();return true}return false};const wrapComment=function(width,move){for(const token of move.split(" ")){if(!token){continue}if(width+token.length>maxWidth){while(strip()){width--}result.push(newline);width=0}result.push(token);width+=token.length;result.push(" ");width++}if(strip()){width--}return width};let currentWidth=0;for(let i=0;imaxWidth){if(moves[i].includes("{")){currentWidth=wrapComment(currentWidth,moves[i]);continue}}if(currentWidth+moves[i].length>maxWidth&&i!==0){if(result[result.length-1]===" "){result.pop()}result.push(newline);currentWidth=0}else if(i!==0){result.push(" ");currentWidth++}result.push(moves[i]);currentWidth+=moves[i].length}return result.join("")}header(...args){for(let i=0;i0){headerObj[key]=value}}return headerObj}pgn=pgn.trim();const headerRegex=new RegExp("^(\\[((?:"+mask(newlineChar)+")|.)*\\])"+"((?:\\s*"+mask(newlineChar)+"){2}|(?:\\s*"+mask(newlineChar)+")*$)");const headerRegexResults=headerRegex.exec(pgn);const headerString=headerRegexResults?headerRegexResults.length>=2?headerRegexResults[1]:"":"";this.reset();const headers=parsePgnHeader(headerString);let fen="";for(const key in headers){if(key.toLowerCase()==="fen"){fen=headers[key]}this.header(key,headers[key])}if(!strict){if(fen){this.load(fen,true)}}else{if(headers["SetUp"]==="1"){if(!("FEN"in headers)){throw new Error("Invalid PGN: FEN tag must be supplied with SetUp tag")}this.load(headers["FEN"],true)}}function toHex(s){return Array.from(s).map(function(c){return c.charCodeAt(0)<128?c.charCodeAt(0).toString(16):encodeURIComponent(c).replace(/%/g,"").toLowerCase()}).join("")}function fromHex(s){return s.length==0?"":decodeURIComponent("%"+(s.match(/.{1,2}/g)||[]).join("%"))}const encodeComment=function(s){s=s.replace(new RegExp(mask(newlineChar),"g")," ");return`{${toHex(s.slice(1,s.length-1))}}`};const decodeComment=function(s){if(s.startsWith("{")&&s.endsWith("}")){return fromHex(s.slice(1,s.length-1))}};let ms=pgn.replace(headerString,"").replace(new RegExp(`({[^}]*})+?|;([^${mask(newlineChar)}]*)`,"g"),function(_match,bracket,semicolon){return bracket!==undefined?encodeComment(bracket):" "+encodeComment(`{${semicolon.slice(1)}}`)}).replace(new RegExp(mask(newlineChar),"g")," ");const ravRegex=/(\([^()]+\))+?/g;while(ravRegex.test(ms)){ms=ms.replace(ravRegex,"")}ms=ms.replace(/\d+\.(\.\.)?/g,"");ms=ms.replace(/\.\.\./g,"");ms=ms.replace(/\$\d+/g,"");let moves=ms.trim().split(new RegExp(/\s+/));moves=moves.filter(move=>move!=="");let result="";for(let halfMove=0;halfMove-1){result=moves[halfMove]}else{throw new Error(`Invalid move in PGN: ${moves[halfMove]}`)}}else{result="";this._makeMove(move)}}if(result&&Object.keys(this._header).length&&!this._header["Result"]){this.header("Result",result)}}_moveToSan(move,moves){let output="";if(move.flags&BITS.KSIDE_CASTLE){output="O-O"}else if(move.flags&BITS.QSIDE_CASTLE){output="O-O-O"}else{if(move.piece!==PAWN){const disambiguator=getDisambiguator(move,moves);output+=move.piece.toUpperCase()+disambiguator}if(move.flags&(BITS.CAPTURE|BITS.EP_CAPTURE)){if(move.piece===PAWN){output+=algebraic(move.from)[0]}output+="x"}output+=algebraic(move.to);if(move.promotion){output+="="+move.promotion.toUpperCase()}}this._makeMove(move);if(this.isCheck()){if(this.isCheckmate()){output+="#"}else{output+="+"}}this._undoMove();return output}_moveFromSan(move,strict=false){const cleanMove=strippedSan(move);let pieceType=inferPieceType(cleanMove);let moves=this._moves({legal:true,piece:pieceType});for(let i=0,len=moves.length;i0){nodes+=this.perft(depth-1)}else{nodes++}}this._undoMove()}return nodes}_makePretty(uglyMove){const{color,piece,from,to,flags,captured,promotion}=uglyMove;let prettyFlags="";for(const flag in BITS){if(BITS[flag]&flags){prettyFlags+=FLAGS[flag]}}const fromAlgebraic=algebraic(from);const toAlgebraic=algebraic(to);const move={color:color,piece:piece,from:fromAlgebraic,to:toAlgebraic,san:this._moveToSan(uglyMove,this._moves({legal:true})),flags:prettyFlags,lan:fromAlgebraic+toAlgebraic,before:this.fen(),after:""};this._makeMove(uglyMove);move.after=this.fen();this._undoMove();if(captured){move.captured=captured}if(promotion){move.promotion=promotion;move.lan+=promotion}return move}turn(){return this._turn}board(){const output=[];let row=[];for(let i=Ox88.a8;i<=Ox88.h1;i++){if(!this._board[i]){row.push(false)}else{row.push({square:algebraic(i),type:this._board[i].type,color:this._board[i].color})}if(i+1&136){output.push(row);row=[];i+=8}}return output}squareColor(square){if(square in Ox88){const sq=Ox88[square];return(rank(sq)+file(sq))%2===0?"w":"b"}return null}history({verbose=false}={}){const reversedHistory=[];const moveHistory=[];while(this._history.length>0){reversedHistory.push(this._undoMove())}while(true){const move=reversedHistory.pop();if(!move){break}if(verbose){moveHistory.push(this._makePretty(move))}else{moveHistory.push(this._moveToSan(move,this._moves()))}this._makeMove(move)}return moveHistory}_pruneComments(){const reversedHistory=[];const currentComments={};const copyComment=fen=>{if(fen in this._comments){currentComments[fen]=this._comments[fen]}};while(this._history.length>0){reversedHistory.push(this._undoMove())}copyComment(this.fen());while(true){const move=reversedHistory.pop();if(!move){break}this._makeMove(move);copyComment(this.fen())}this._comments=currentComments}getComment(){return this._comments[this.fen()]}setComment(comment){this._comments[this.fen()]=comment.replace("{","[").replace("}","]")}deleteComment(){const comment=this._comments[this.fen()];delete this._comments[this.fen()];return comment}getComments(){this._pruneComments();return Object.keys(this._comments).map(fen=>{return{fen:fen,comment:this._comments[fen]}})}deleteComments(){this._pruneComments();return Object.keys(this._comments).map(fen=>{const comment=this._comments[fen];delete this._comments[fen];return{fen:fen,comment:comment}})}}