@maoshizhong/chess
Version:
Simple code-only Chessboard written in TypeScript. Handles FEN and PGN.
5 lines (3 loc) • 15.7 kB
JavaScript
"use strict";var yt=Object.defineProperty;var Z=i=>{throw TypeError(i)};var wt=(i,t,e)=>t in i?yt(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var M=(i,t,e)=>wt(i,typeof t!="symbol"?t+"":t,e),q=(i,t,e)=>t.has(i)||Z("Cannot "+e);var h=(i,t,e)=>(q(i,t,"read from private field"),e?e.call(i):t.get(i)),w=(i,t,e)=>t.has(i)?Z("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(i):t.set(i,e),y=(i,t,e,s)=>(q(i,t,"write to private field"),s?s.call(i,e):t.set(i,e),e),k=(i,t,e)=>(q(i,t,"access private method"),e);var Q=(i,t,e,s)=>({set _(n){y(i,t,n,e)},get _(){return h(i,t,s)}});Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});class E{constructor(t,e){M(this,"letter");M(this,"colour");this.letter=e==="w"?t:t.toLowerCase(),this.colour=e}}const H={SHORT:"O-O",LONG:"O-O-O"};function _(i){const[t,e]=i.split("");return[v[Number(e)],g[t]]}function ot([i,t]){return`${"abcdefgh"[t]}${v[i]}`}function kt({from:i,to:t,promoteTo:e},s){const[n,o]=_(i),[a,r]=_(t),l=s.board[n][o];if(!l)return[!1,"",""];const c=l.letter.toUpperCase(),u=l.letter===c;let f=s.board[a][r]instanceof E;if(c==="P"){if(!f&&s.enPassant){const[p,O]=s.enPassant;f=p===a&&O===r}const P=u?v[8]:v[1];let m=f?`${i[0]}x${t}`:t;return e&&a===P&&(m+=`=${e}`),o===r||f?[!0,m,m]:[!1,"",""]}else{if(c==="K"&&o-r===2)return[!0,"O-O-O","O-O-O"];if(c==="K"&&o-r===-2)return[!0,"O-O","O-O"]}let d="",b="";const C=P=>a===P[0]&&r===P[1];return s.board.forEach((P,m)=>{P.forEach((p,O)=>{var A;if(p!==l&&(p==null?void 0:p.letter)===l.letter){if(!((A=s.getValidMoves({rank:m,file:O,isCapture:f}))==null?void 0:A.some(C)))return;n===m&&(b=i[0]),o===O&&(d=i[1])}})}),[!0,`${c}${i[0]}${i[1]}${f?"x":""}${t}`,`${c}${b}${d}${f?"x":""}${t}`]}function St(i){const t=i.includes("x"),e=t&&i[0]===i[0].toLowerCase(),s=i.replace("x",""),n=[];if(i===H.SHORT||i===H.LONG)n.push(...Et(s));else if(e){const o=tt(s.substring(1).split(""));o.piece.file=g[i[0]],n.push(o)}else n.push(tt(s.split("")));return{isCapture:t,piecesToMove:n}}function Et(i){const t={piece:{letter:"K"},destination:[0,0]},e={piece:{letter:"R"},destination:[0,0]};return i===H.SHORT?(t.destination[1]=g.g,e.destination[1]=g.f,e.piece.file=g.h):(t.destination[1]=g.c,e.destination[1]=g.d,e.piece.file=g.a),[t,e]}function tt(i){const t={piece:{letter:"P"},destination:[0,0]};switch(i.includes("=")&&(t.promoteTo=i.at(-1),i.splice(-2,2)),i.length){case 2:{const[e,s]=i;t.piece.letter="P",t.destination[0]=v[Number(s)],t.destination[1]=g[e];break}case 3:{const[e,s,n]=i;t.piece.letter=e,t.destination[0]=v[Number(n)],t.destination[1]=g[s];break}case 4:{const[e,s,n,o]=i;t.piece.letter=e,Number(s)?t.piece.rank=v[Number(s)]:t.piece.file=g[s],t.destination[0]=v[Number(o)],t.destination[1]=g[n];break}case 5:{const[e,s,n,o,a]=i;t.piece.letter=e,t.piece.rank=v[Number(n)],t.piece.file=g[s],t.destination[0]=v[Number(a)],t.destination[1]=g[o];break}}return t}class et extends E{constructor(t){super("B",t)}get maximumMoves(){return[[[1,1],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7]],[[1,-1],[2,-2],[3,-3],[4,-4],[5,-5],[6,-6],[7,-7]],[[-1,1],[-2,2],[-3,3],[-4,4],[-5,5],[-6,6],[-7,7]],[[-1,-1],[-2,-2],[-3,-3],[-4,-4],[-5,-5],[-6,-6],[-7,-7]]]}}class x extends E{constructor(e){super("K",e);M(this,"hasMoved",!1)}get maximumMoves(){const e=[[[0,1]],[[1,1]],[[1,0]],[[1,-1]],[[0,-1]],[[-1,-1]],[[-1,0]],[[-1,1]],[[0,2]],[[0,-2]]];return this.hasMoved?e.slice(0,-2):e}}class st extends E{constructor(t){super("N",t)}get maximumMoves(){return[[[1,2]],[[2,1]],[[2,-1]],[[1,-2]],[[-1,-2]],[[-2,-1]],[[-2,1]],[[-1,2]]]}}var j,rt;class T extends E{constructor(e){super("P",e);w(this,j);M(this,"hasMoved",!1)}get maximumMoves(){const e=[[[1,0],[2,0]],[[1,-1]],[[1,1]]];return this.hasMoved&&e[0].splice(-1,1),this.colour==="w"?e:k(this,j,rt).call(this,e)}}j=new WeakSet,rt=function(e){return e.map(s=>s.map(([n,o])=>[n*-1,o*-1]))};class it extends E{constructor(t){super("Q",t)}get maximumMoves(){return[[[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0]],[[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7]],[[-1,0],[-2,0],[-3,0],[-4,0],[-5,0],[-6,0],[-7,0]],[[0,-1],[0,-2],[0,-3],[0,-4],[0,-5],[0,-6],[0,-7]],[[1,1],[2,2],[3,3],[4,4],[5,5],[6,6],[7,7]],[[1,-1],[2,-2],[3,-3],[4,-4],[5,-5],[6,-6],[7,-7]],[[-1,1],[-2,2],[-3,3],[-4,4],[-5,5],[-6,6],[-7,7]],[[-1,-1],[-2,-2],[-3,-3],[-4,-4],[-5,-5],[-6,-6],[-7,-7]]]}}class B extends E{constructor(e){super("R",e);M(this,"hasMoved",!1)}get maximumMoves(){return[[[1,0],[2,0],[3,0],[4,0],[5,0],[6,0],[7,0]],[[0,1],[0,2],[0,3],[0,4],[0,5],[0,6],[0,7]],[[-1,0],[-2,0],[-3,0],[-4,0],[-5,0],[-6,0],[-7,0]],[[0,-1],[0,-2],[0,-3],[0,-4],[0,-5],[0,-6],[0,-7]]]}}const at={P:T,p:T,R:B,r:B,N:st,n:st,B:et,b:et,Q:it,q:it,K:x,k:x};function lt(i){return i.split("").map(s=>{const n=Rt(s)?"w":"b";return Number(s)?Array(Number(s)).fill(null):new at[s](n)}).flat()}function ct(i,t,e,s,n,o){const a=z(i);let r="";return e.w.short&&(r+="K"),e.w.long&&(r+="Q"),e.b.short&&(r+="k"),e.b.long&&(r+="q"),r||(r="-"),[a,t,r,s?ot(s):"-",n,o].join(" ")}function z(i){return i.map(t=>{let e="",s=0;for(const n of t)n instanceof E?(e+=s,s=0,e+=n.letter):s++;return e+=s,e.replaceAll("0","")}).join("/")}function W(i){const t=i.split(" ");Ct(i,t);const[e,s,n,o,a,r]=t,l={w:{short:n.includes("K"),long:n.includes("Q")},b:{short:n.includes("k"),long:n.includes("q")}},c=o==="-"?null:_(o);return[e,s,l,c,Number(a),Number(r)]}function Ct(i,[t,e,s,n,o,a]){const r=new TypeError(`${i} is not a valid FEN string.`),l=/^([rnbqkp1-8]{1,8}\/){7}[rnbqkp1-8]{1,8}$/i.test(t),c=/^(w|b)$/.test(e),u=/^(-|K?Q?k?q?)$/.test(s),f=/^(-|[a-h](3|6))$/.test(n),d=/^(\d{1,2}|100)$/.test(o),b=/^\d+$/.test(a);if(!l||!c||!u||!f||!d||!b)throw r;const C=t.split("/");for(const P of C)if(P.split("").map(p=>Number(p)?Array(Number(p)).fill(""):p).flat().length!==8)throw r}function Rt(i){return i===i.toUpperCase()}const Ft=Object.freeze(Object.defineProperty({__proto__:null,PIECES:at,serialise:ct,serialisePosition:z,split:W,toChessRow:lt},Symbol.toStringTag,{value:"Module"})),v=[8,7,6,5,4,3,2,1,0],g={a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7};var G,K,ht,ut,ft;const J=class J{constructor(t,e={w:{short:!0,long:!0},b:{short:!0,long:!0}},s=null){w(this,K);M(this,"board");M(this,"enPassant");w(this,G);y(this,G,e),this.board=k(this,K,ht).call(this,t,h(this,G)),this.enPassant=s}getValidMoves({rank:t,file:e,isCapture:s=!0,isForFindingChecks:n=!1}){const o=this.board[t][e];if(!o)return null;const r=o instanceof x&&!o.hasMoved,l=[];return o.maximumMoves.forEach((c,u,f)=>{const d=r&&u>=f.length-2&&!n;if(d&&k(this,K,ft).call(this,t,e,c[0])){const C=c[0][1];l.push([t,e+C])}else d||l.push(...k(this,K,ut).call(this,o,t,e,c,s,n))}),l}isSquareInCheck(t,e,s){return this.board.some((n,o)=>n.some((a,r)=>{if(a===null||a.colour===t)return!1;const l=this.getValidMoves({rank:o,file:r,isCapture:!0,isForFindingChecks:!0});return l==null?void 0:l.find(u=>u[0]===e&&u[1]===s)}))}isKingInCheck(t){const e=this.board.findIndex(n=>n.find(o=>o instanceof x&&o.colour===t)),s=this.board[e].findIndex(n=>n instanceof x&&n.colour===t);return this.isSquareInCheck(t,e,s)}canPlayContinue(t){const e=this.isKingInCheck(t),s=[];this.board.forEach((o,a)=>{o.forEach((r,l)=>{if(r===null||r.colour!==t)return;const c=this.getValidMoves({rank:a,file:l});c!=null&&c.length&&s.push(...c.map(u=>({from:[a,l],to:u})))})});const n=s.filter(o=>!this.simulateMove(o).isKingInCheck(t));return e&&!n.length?[!1,"checkmate"]:!e&&!n.length?[!1,"stalemate"]:[!0,void 0]}move({from:t,to:e,promoteTo:s}){const[n,o]=t,[a,r]=e,{PIECES:l}=Ft,[c,u]=this.enPassant??[],f=a===c&&r===u,d=(s==null?void 0:s.toUpperCase())===s?"w":"b",b=s?new l[s](d):this.board[n][o];this.board[a][r]=b,this.board[n][o]=null,f&&(this.board[d==="w"?a+1:a-1][r]=null),(b instanceof x||b instanceof T)&&(b.hasMoved=!0)}simulateMove(t){const e=new J(z(this.board),h(this,G));return e.move(t),e}flip(){this.board.reverse(),this.board.forEach(t=>t.reverse())}};G=new WeakMap,K=new WeakSet,ht=function(t,e){const n=t.split("/").map(o=>lt(o));return n.forEach((o,a)=>{o.forEach((r,l)=>{!(r instanceof B)&&!(r instanceof x)&&!(r instanceof T)||(r.hasMoved=(()=>{switch(r.constructor){case T:return r.colour==="w"&&a!==v[2]||r.colour==="b"&&a!==v[7];case x:return!e[r.colour].short&&!e[r.colour].long;case B:return l===g.a&&!e[r.colour].long||l===g.h&&!e[r.colour].short;default:return!1}})())})}),n},ut=function(t,e,s,n,o,a){var u,f,d;const r=t instanceof T,l=t instanceof E&&!(t instanceof T),c=[];for(const[b,C]of n){const P=e-b,m=s-C,p=(u=this.board[P])==null?void 0:u[m],O=p instanceof E&&p.colour===t.colour,A=p instanceof E&&p.colour!==t.colour||((f=this.enPassant)==null?void 0:f[0])===P&&((d=this.enPassant)==null?void 0:d[1])===m,X=l&&A,Pt=r&&m!==s&&o&&(A||a),Mt=r&&A&&m===s,Y=X||Pt,Nt=l&&p===null||r&&m===s&&p===null;if(O||Mt||((Nt||Y)&&c.push([P,m]),Y))break}return c},ft=function(t,e,[s,n]){const o=this.board[t],r=n>0?o[g.h]:o[g.a];if(!r||!(r instanceof B))return!1;const l=[[t,e],[t,e+n/2],[t,e+n]],u=[o[l[1][1]],o[l[2][1]]].some(d=>d instanceof E),f=l.some(([d,b])=>this.isSquareInCheck(r.colour,d,b));return!r.hasMoved&&!u&&!f};let D=J;var $,L,dt,gt;class nt{constructor(t,e,s){w(this,L);M(this,"colour");M(this,"castlingRights");w(this,$);this.colour=t,this.castlingRights=e,y(this,$,s)}move(t){var l;if(t==="O-O"&&!this.castlingRights.short||t==="O-O-O"&&!this.castlingRights.long)return[!1];let e=!1,s=null;const{isCapture:n,piecesToMove:o}=St(t),a=o.length===2,r=[];for(const c of o){a&&(c.destination[0]=this.colour==="w"?v[1]:v[8]),this.colour==="b"&&(c.piece.letter=c.piece.letter.toLowerCase(),c.promoteTo=(l=c.promoteTo)==null?void 0:l.toLowerCase());const[u,f,d]=k(this,L,dt).call(this,c,n);if(!u)return[!1];if(c.piece.letter.toUpperCase()==="P"){e=!0;const[P,m]=c.destination,p=m===g[t[0]],O=Math.abs(P-f)===2;switch(this.colour){case"w":P===v[4]&&p&&O&&(s=[v[3],m]);break;case"b":P===v[5]&&p&&O&&(s=[v[6],m]);break}}const b={from:[f,d],to:c.destination,promoteTo:c.promoteTo};if(h(this,$).simulateMove(b).isKingInCheck(this.colour))return[!1];r.push(b),k(this,L,gt).call(this,c.piece.letter,d)}for(const c of r)h(this,$).move(c);return[o.length>0,n||e,s,h(this,$).isKingInCheck(this.colour==="w"?"b":"w")]}}$=new WeakMap,L=new WeakSet,dt=function({piece:t,destination:e},s){const n=[];if(h(this,$).board.forEach((a,r)=>{a.forEach((l,c)=>{if(l===null||l.letter!==t.letter)return;const u=h(this,$).getValidMoves({rank:r,file:c,isCapture:s}),f=u==null?void 0:u.some(C=>C[0]===e[0]&&C[1]===e[1]),d=t.rank===void 0||t.rank===r,b=t.file===void 0||t.file===c;f&&d&&b&&n.push([r,c])})}),n.length===1)return[!0,...n[0]];const o=n.find(a=>t.rank&&t.file?a[0]===t.rank&&a[1]===t.file:a[0]===(t==null?void 0:t.rank)||a[1]===(t==null?void 0:t.file));return o?[!0,...o]:[!1,0,0]},gt=function(t,e){const s=t.toUpperCase();"KR".includes(s)&&(s==="K"?(this.castlingRights.short=!1,this.castlingRights.long=!1):e===g.h?this.castlingRights.short=!1:e===g.a&&(this.castlingRights.long=!1))};function Ot(i,t=!1){const[{FEN:e},...s]=i;let o=e==="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"||t?"":`[SetUp "1"]
[FEN "${e}"]
`;const a=Number(e.split(" ").at(-1))-1;return s.forEach((r,l)=>{const[c,u]=W(r.FEN);if(l===0&&u==="w")o+=`${a+1}... `;else if(u==="b"){const f=Math.ceil(l/2+1+a);o+=`${f}. `}o+=`${r.move} `,r.result&&(o+=r.result)}),o.trim()}function $t(i){var e;return((e=i.match(new RegExp('(?<=\\[FEN ").+(?="\\])')))==null?void 0:e[0])??"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"}function xt(i){var e;return(((e=i.match(/\d+\.+ .+/))==null?void 0:e[0])??"").split(" ").filter(s=>!/\.|1-0|0-1|1\/2-1\/2/.test(s)).map(s=>s.endsWith("#")||s.endsWith("+")?s.slice(0,-1):s)}var N,S,U,pt;class It{constructor(t){w(this,U);w(this,N);w(this,S);y(this,N,0),y(this,S,[{FEN:t}])}get length(){return h(this,S).length}get isAtLatest(){return h(this,N)===this.length-1}get currentState(){return this.getState(h(this,N))}get currentFEN(){return h(this,S)[h(this,N)].FEN}getState(t){const[e,s,n,o,a,r]=W(h(this,S)[t].FEN);return{board:new D(e,n).board,activeColour:s,castlingRights:n,enPassantTarget:o,halfMoves:a,fullMoves:r}}toNthState(t){return 0<=t&&t<this.length?y(this,N,t):t<0?y(this,N,0):y(this,N,this.length-1),this.currentState}toPreviousState(){return h(this,N)>0&&Q(this,N)._--,this.currentState}toNextState(){return h(this,N)<h(this,S).length-1&&Q(this,N)._++,this.currentState}record(t,e,s){const n=ct(...e);Q(this,N)._++,h(this,S).splice(h(this,N),1/0,{FEN:n,move:t,result:s})}markAsDraw(){h(this,S)[this.length-1].result="1/2-1/2"}isThreefoldRepetition(){return Object.values(h(this,U,pt)).some(t=>t===3)}toPGN(t){return Ot(h(this,S),t)}}N=new WeakMap,S=new WeakMap,U=new WeakSet,pt=function(){const t={};return h(this,S).forEach(e=>{const s=e.FEN.split(" ")[0];t[s]=t[s]+1||1}),t};var R,I,F,V,vt,mt;class bt{constructor(t="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1",{isPGN:e}={isPGN:!1}){w(this,F);M(this,"history");M(this,"board");M(this,"players");M(this,"activePlayer");M(this,"isGameInPlay");M(this,"result");w(this,R);w(this,I);const s=e?$t(t):t,[n,o,a,r,l,c]=W(s);this.history=new It(s),this.board=new D(n,a,r),this.players={w:new nt("w",a.w,this.board),b:new nt("b",a.b,this.board)},this.activePlayer=this.players[o],this.isGameInPlay=this.board.canPlayContinue(this.activePlayer.colour)[0],y(this,R,l),y(this,I,c),e&&k(this,F,mt).call(this,t)}get halfMoves(){return h(this,R)}get fullMoves(){return h(this,I)}get castlingRights(){return{w:this.players.w.castlingRights,b:this.players.b.castlingRights}}playMove(t){const[e,s,n]=typeof t=="string"?[!0,t,t]:kt(t,this.board),o=new Error(`${e?n:t} is not a valid move`);if(this.isGameInPlay=!this.history.isAtLatest||this.isGameInPlay,!e||!this.isGameInPlay)return[o,""];this.result=void 0;const[a,r,l,c]=this.activePlayer.move(s);if(!a)return[o,""];y(this,R,r?0:h(this,R)+1),y(this,I,h(this,I)+ +(this.activePlayer.colour==="b")),this.board.enPassant=l,k(this,F,vt).call(this);const u=[this.board.board,this.activePlayer.colour,this.castlingRights,l,h(this,R),h(this,I)],[f,d]=this.board.canPlayContinue(this.activePlayer.colour);return d==="stalemate"||h(this,R)>=100?this.result="1/2-1/2":d==="checkmate"&&(this.result=this.activePlayer.colour==="w"?"0-1":"1-0"),f?(this.history.record(c?`${n}+`:n,u),this.history.isThreefoldRepetition()&&(this.isGameInPlay=!1,this.history.markAsDraw(),this.result="1/2-1/2")):(this.isGameInPlay=!1,this.history.record(d==="checkmate"?`${n}#`:n,u,this.result)),[null,n]}getLegalMoves(t){const e=v[Number(t[1])],s=g[t[0]],n=this.board.board[e][s],o=!this.isGameInPlay&&this.history.isAtLatest;return!n||o?[]:(this.board.getValidMoves({rank:e,file:s})??[]).filter(l=>!this.board.simulateMove({from:[e,s],to:l}).isKingInCheck(n.colour)).map(l=>ot(l))}toPreviousPosition(){return k(this,F,V).call(this,this.history.toPreviousState()),this}toNextPosition(){return k(this,F,V).call(this,this.history.toNextState()),this}toNthPosition(t){return k(this,F,V).call(this,this.history.toNthState(t)),this}toPGN({movesOnly:t}={movesOnly:!1}){return this.history.toPGN(t)}toFEN(){return this.history.currentFEN}}R=new WeakMap,I=new WeakMap,F=new WeakSet,V=function({board:t,activeColour:e,castlingRights:s,enPassantTarget:n,halfMoves:o,fullMoves:a}){this.board.board.length=0,this.board.board.push(...t),this.board.enPassant=n,this.players.w.castlingRights=s.w,this.players.b.castlingRights=s.b,this.activePlayer=this.players[e],y(this,R,o),y(this,I,a)},vt=function(){this.activePlayer=this.activePlayer.colour==="w"?this.players.b:this.players.w},mt=function(t){const e=xt(t);for(const s of e){const[n]=this.playMove(s);if(n)throw new Error(`Invalid PGN - could not play ${s}`)}};module.exports={Chess:bt};exports.Chess=bt;