@ayshrj/checkers.js
Version:
A TypeScript-based headless Checker game engine for simulating game logic, AI moves, and game state management.
2 lines (1 loc) • 4.85 kB
JavaScript
var k=Object.defineProperty,M=Object.defineProperties;var b=Object.getOwnPropertyDescriptors;var C=Object.getOwnPropertySymbols;var B=Object.prototype.hasOwnProperty,S=Object.prototype.propertyIsEnumerable;var P=(r,e,t)=>e in r?k(r,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):r[e]=t,p=(r,e)=>{for(var t in e||(e={}))B.call(e,t)&&P(r,t,e[t]);if(C)for(var t of C(e))S.call(e,t)&&P(r,t,e[t]);return r},m=(r,e)=>M(r,b(e));import{EventEmitter as y}from"events";var v=class r extends y{constructor(){super(),this._board=[],this._turn="red",this.reset()}reset(){this._board=Array.from({length:8},(e,t)=>Array.from({length:8},(o,n)=>{if((t+n)%2===1){if(t<3)return{color:"black",type:"man"};if(t>4)return{color:"red",type:"man"}}return null})),this._turn="red",this._emitStateChange()}getPiece(e){return e.row<0||e.row>=8||e.col<0||e.col>=8?null:this._board[e.row][e.col]}getCurrentState(){let e=r.getAllowedMovesForBoard(this._board,this._turn),t=e.length===0,o=t?`Game Over! ${this._turn==="red"?"Black":"Red"} wins!`:`${this._turn}'s turn`;return{board:r.cloneBoard(this._board),turn:this._turn,allowedMoves:e,gameState:t?"gameOver":this._turn==="red"?"redTurn":"blackTurn",boardStatus:o,isGameOver:t}}move(e){if(!this.validateMove(e))return!1;let o=this.getPiece(e.from);e.captures.forEach(({row:l,col:s})=>this._board[l][s]=null);let n=e.path[e.path.length-1];return this._board[e.from.row][e.from.col]=null,this._board[n.row][n.col]=this._promoteIfNeeded(o,n.row),this._turn=this._turn==="red"?"black":"red",this._emitStateChange(),!0}bestMove(e){return r.minimax(r.cloneBoard(this._board),e,!0,this._turn,this._turn).move}_emitStateChange(){this.emit("stateChange",this.getCurrentState())}_promoteIfNeeded(e,t){return e.type==="king"?e:e.color==="red"&&t===0?m(p({},e),{type:"king"}):e.color==="black"&&t===7?m(p({},e),{type:"king"}):e}validateMove(e){return r.getAllowedMovesForBoard(this._board,this._turn).some(o=>r.compareMoves(o,e))}static getAllowedMovesForBoard(e,t){let o=[],n=[];for(let l=0;l<8;l++)for(let s=0;s<8;s++){let c=e[l][s];if(c&&c.color===t){let a={row:l,col:s},h=r.getCapturesForPiece(e,a,c);if(h.length>0)o.push(...h);else{let i=r.getSimpleMovesForPiece(e,a,c);n.push(...i)}}}return o.length>0?o:n}static getCapturesForPiece(e,t,o,n){let l=[],s=r.getDirections(o),c=!1,a=n?m(p({},n),{path:[...n.path],captures:[...n.captures]}):{from:t,path:[],captures:[]};for(let h of s){let i={row:t.row+h[0],col:t.col+h[1]},u={row:t.row+2*h[0],col:t.col+2*h[1]};if(u.row<0||u.row>=8||u.col<0||u.col>=8)continue;let d=i.row>=0&&i.row<8&&i.col>=0&&i.col<8?e[i.row][i.col]:null;if(!d||d.color===o.color||e[u.row][u.col]!==null)continue;c=!0;let f=r.cloneBoard(e);f[i.row][i.col]=null,f[u.row][u.col]=o,f[t.row][t.col]=null;let w={from:a.from,path:a.path.concat([u]),captures:a.captures.concat([i])},g=r.getCapturesForPiece(f,u,o,w);g.length>0?l.push(...g):l.push(w)}return!c&&a.captures.length>0?[a]:l}static getSimpleMovesForPiece(e,t,o){let n=[],l=r.getDirections(o);for(let s of l){let c={row:t.row+s[0],col:t.col+s[1]};c.row<0||c.row>=8||c.col<0||c.col>=8||e[c.row][c.col]===null&&n.push({from:t,path:[c],captures:[]})}return n}static cloneBoard(e){return e.map(t=>t.map(o=>o?p({},o):null))}static getDirections(e){return e.type==="king"?[[-1,-1],[-1,1],[1,-1],[1,1]]:e.color==="red"?[[-1,-1],[-1,1]]:[[1,-1],[1,1]]}static updateBoard(e,t){let o=r.cloneBoard(e),n=o[t.from.row][t.from.col];if(!n)return o;o[t.from.row][t.from.col]=null;for(let c of t.captures)o[c.row][c.col]=null;let l=t.path[t.path.length-1],s=p({},n);return s.type==="man"&&(s.color==="red"&&l.row===0||s.color==="black"&&l.row===7)&&(s.type="king"),o[l.row][l.col]=s,o}static evaluateBoard(e,t){let o=0;for(let n=0;n<8;n++)for(let l=0;l<8;l++){let s=e[n][l];if(s){let c=s.type==="man"?10:50;s.color===t?o+=c:o-=c}}return o}static shuffle(e){for(let t=e.length-1;t>0;t--){let o=Math.floor(Math.random()*(t+1));[e[t],e[o]]=[e[o],e[t]]}return e}static minimax(e,t,o,n,l){let s=r.getAllowedMovesForBoard(e,n);if(t===0||s.length===0)return{score:r.evaluateBoard(e,l),move:null};let c=r.shuffle(s.slice());if(o){let a=-1/0,h=null;for(let i of c){let u=r.updateBoard(e,i),d=n==="red"?"black":"red",f=r.minimax(u,t-1,!1,d,l);f.score>a&&(a=f.score,h=i)}return{score:a,move:h}}else{let a=1/0,h=null;for(let i of c){let u=r.updateBoard(e,i),d=n==="red"?"black":"red",f=r.minimax(u,t-1,!0,d,l);f.score<a&&(a=f.score,h=i)}return{score:a,move:h}}}static compareMoves(e,t){if(e.from.row!==t.from.row||e.from.col!==t.from.col||e.path.length!==t.path.length)return!1;for(let o=0;o<e.path.length;o++)if(e.path[o].row!==t.path[o].row||e.path[o].col!==t.path[o].col)return!1;if(e.captures.length!==t.captures.length)return!1;for(let o=0;o<e.captures.length;o++)if(e.captures[o].row!==t.captures[o].row||e.captures[o].col!==t.captures[o].col)return!1;return!0}};export{v as Checkers};