├── Board.js ├── Game.js ├── History.js ├── README.md ├── SimulationGame.js ├── ai.js ├── chess.css ├── chess.html ├── favicon.ico ├── img ├── black-bishop.webp ├── black-king.webp ├── black-knight.webp ├── black-pawn.webp ├── black-queen.webp ├── black-rook.webp ├── share.jpeg ├── white-bishop.webp ├── white-king.webp ├── white-knight.webp ├── white-pawn.webp ├── white-queen.webp └── white-rook.webp └── piece.js /Board.js: -------------------------------------------------------------------------------- 1 | const startBoard = (game, options = { playAgainst: 'human', aiColor: 'black', aiLevel: 'dumb' }) => { 2 | 3 | const aiPlayer = options.playAgainst === 'ai' ? ai(options.aiColor) : null; 4 | 5 | const board = document.getElementById('board'); 6 | const squares = board.querySelectorAll('.square'); 7 | const whiteSematary = document.getElementById('whiteSematary'); 8 | const blackSematary = document.getElementById('blackSematary'); 9 | const turnSign = document.getElementById('turn'); 10 | let clickedPieceName; 11 | 12 | const resetSematary = () => { 13 | whiteSematary.querySelectorAll('div').forEach(div => div.innerHTML = ''); 14 | blackSematary.querySelectorAll('div').forEach(div => div.innerHTML = ''); 15 | } 16 | 17 | const resetBoard = () => { 18 | resetSematary(); 19 | 20 | for (const square of squares) { 21 | square.innerHTML = ''; 22 | } 23 | 24 | for (const piece of game.pieces) { 25 | const square = document.getElementById(piece.position); 26 | square.innerHTML = ``; 27 | } 28 | 29 | document.getElementById('endscene').classList.remove('show'); 30 | } 31 | 32 | resetBoard(); 33 | 34 | const setGameState = state => { 35 | gameState = state; 36 | if (gameState === 'ai_thinking') { 37 | turnSign.innerHTML += ' (thinking...)'; 38 | } 39 | } 40 | 41 | const setAllowedSquares = (pieceImg) => { 42 | clickedPieceName = pieceImg.id; 43 | const allowedMoves = game.getPieceAllowedMoves(clickedPieceName); 44 | if (allowedMoves) { 45 | const clickedSquare = pieceImg.parentNode; 46 | clickedSquare.classList.add('clicked-square'); 47 | 48 | allowedMoves.forEach( allowedMove => { 49 | if (document.contains(document.getElementById(allowedMove))) { 50 | document.getElementById(allowedMove).classList.add('allowed'); 51 | } 52 | }); 53 | } 54 | else{ 55 | clearSquares(); 56 | } 57 | } 58 | 59 | const clearSquares = () => { 60 | board.querySelectorAll('.allowed').forEach( allowedSquare => allowedSquare.classList.remove('allowed') ); 61 | 62 | const clickedSquare = document.getElementsByClassName('clicked-square')[0]; 63 | if (clickedSquare) { 64 | clickedSquare.classList.remove('clicked-square'); 65 | } 66 | } 67 | 68 | const setLastMoveSquares = (from, to) => { 69 | document.querySelectorAll('.last-move').forEach( lastMoveSquare => lastMoveSquare.classList.remove('last-move') ); 70 | from.classList.add('last-move'); 71 | to.classList.add('last-move'); 72 | } 73 | 74 | function movePiece(square) { 75 | if (gameState === 'ai_thinking') { 76 | return; 77 | } 78 | 79 | const position = square.getAttribute('id'); 80 | const existedPiece = game.getPieceByPos(position); 81 | 82 | if (existedPiece && existedPiece.color === game.turn) { 83 | const pieceImg = document.getElementById(existedPiece.name); 84 | clearSquares(); 85 | return setAllowedSquares(pieceImg); 86 | } 87 | 88 | game.movePiece(clickedPieceName, position); 89 | } 90 | 91 | squares.forEach( square => { 92 | square.addEventListener("click", function () { 93 | movePiece(this); 94 | }); 95 | square.addEventListener("dragover", function(event){ 96 | event.preventDefault(); 97 | }); 98 | square.addEventListener("drop", function () { 99 | movePiece(this); 100 | }); 101 | }); 102 | 103 | game.pieces.forEach( piece => { 104 | const pieceImg = document.getElementById(piece.name); 105 | pieceImg.addEventListener("drop", function () { 106 | const square = document.getElementById(piece.position); 107 | movePiece(square); 108 | }); 109 | }); 110 | 111 | document.querySelectorAll('img.piece').forEach( pieceImg => { 112 | pieceImg.addEventListener("dragstart", function(event) { 113 | if (gameState === 'ai_thinking') { 114 | return; 115 | } 116 | event.stopPropagation(); 117 | event.dataTransfer.setData("text", event.target.id); 118 | clearSquares(); 119 | setAllowedSquares(event.target) 120 | }); 121 | pieceImg.addEventListener("drop", function(event) { 122 | if (gameState === 'ai_thinking') { 123 | return; 124 | } 125 | event.stopPropagation(); 126 | clearSquares(); 127 | setAllowedSquares(event.target) 128 | }); 129 | }); 130 | 131 | const startTurn = turn => { 132 | gameState = turn + '_turn'; 133 | turnSign.innerHTML = turn === 'white' ? "White's Turn" : "Black's Turn"; 134 | 135 | if (gameState !== 'checkmate' && options.playAgainst === 'ai' && turn === options.aiColor) { 136 | setGameState('ai_thinking'); 137 | aiPlayer.play(game.pieces, aiPlay => { 138 | setGameState('human_turn'); 139 | game.movePiece(aiPlay.move.pieceName, aiPlay.move.position); 140 | }); 141 | } 142 | } 143 | 144 | game.on('pieceMove', move => { 145 | const from = document.getElementById(move.from); 146 | const to = document.getElementById(move.piece.position); 147 | to.append( document.getElementById(move.piece.name) ); 148 | clearSquares(); 149 | 150 | setLastMoveSquares(from, to); 151 | }); 152 | 153 | game.on('turnChange', startTurn); 154 | 155 | game.on('promotion', queen => { 156 | const square = document.getElementById(queen.position); 157 | square.innerHTML = ``; 158 | }) 159 | 160 | game.on('kill', piece => { 161 | const pieceImg = document.getElementById(piece.name); 162 | pieceImg.parentNode.removeChild(pieceImg); 163 | pieceImg.className = ''; 164 | 165 | const sematary = piece.color === 'white' ? whiteSematary : blackSematary; 166 | sematary.querySelector('.'+piece.rank).append(pieceImg); 167 | }); 168 | 169 | game.on('checkMate', color => { 170 | const endScene = document.getElementById('endscene'); 171 | endScene.getElementsByClassName('winning-sign')[0].innerHTML = color + ' Wins'; 172 | endScene.classList.add('show'); 173 | setGameState('checkmate'); 174 | }); 175 | 176 | startTurn('white'); 177 | } 178 | 179 | const pieces = [ 180 | { rank: 'knight', position: 12, color: 'white', name: 'whiteKnight1' }, 181 | { rank: 'knight', position: 17, color: 'white', name: 'whiteKnight2' }, 182 | { rank: 'queen', position: 14, color: 'white', name: 'whiteQueen' }, 183 | { rank: 'bishop', position: 13, color: 'white', name: 'whiteBishop1' }, 184 | { rank: 'bishop', position: 16, color: 'white', name: 'whiteBishop2' }, 185 | { rank: 'pawn', position: 24, color: 'white', name: 'whitePawn4' }, 186 | { rank: 'pawn', position: 25, color: 'white', name: 'whitePawn5' }, 187 | { rank: 'pawn', position: 26, color: 'white', name: 'whitePawn6' }, 188 | { rank: 'pawn', position: 21, color: 'white', name: 'whitePawn1' }, 189 | { rank: 'pawn', position: 22, color: 'white', name: 'whitePawn2' }, 190 | { rank: 'pawn', position: 23, color: 'white', name: 'whitePawn3' }, 191 | { rank: 'pawn', position: 27, color: 'white', name: 'whitePawn7' }, 192 | { rank: 'pawn', position: 28, color: 'white', name: 'whitePawn8' }, 193 | { rank: 'rook', position: 11, color: 'white', name: 'whiteRook1', ableToCastle: true }, 194 | { rank: 'rook', position: 18, color: 'white', name: 'whiteRook2', ableToCastle: true }, 195 | { rank: 'king', position: 15, color: 'white', name: 'whiteKing', ableToCastle: true }, 196 | 197 | { rank: 'knight', position: 82, color: 'black', name: 'blackKnight1' }, 198 | { rank: 'knight', position: 87, color: 'black', name: 'blackKnight2' }, 199 | { rank: 'queen', position: 84, color: 'black', name: 'blackQueen' }, 200 | { rank: 'bishop', position: 83, color: 'black', name: 'blackBishop1' }, 201 | { rank: 'bishop', position: 86, color: 'black', name: 'blackBishop2' }, 202 | { rank: 'pawn', position: 74, color: 'black', name: 'blackPawn4' }, 203 | { rank: 'pawn', position: 75, color: 'black', name: 'blackPawn5' }, 204 | { rank: 'pawn', position: 76, color: 'black', name: 'blackPawn6' }, 205 | { rank: 'pawn', position: 71, color: 'black', name: 'blackPawn1' }, 206 | { rank: 'pawn', position: 72, color: 'black', name: 'blackPawn2' }, 207 | { rank: 'pawn', position: 73, color: 'black', name: 'blackPawn3' }, 208 | { rank: 'pawn', position: 77, color: 'black', name: 'blackPawn7' }, 209 | { rank: 'pawn', position: 78, color: 'black', name: 'blackPawn8' }, 210 | { rank: 'rook', position: 81, color: 'black', name: 'blackRook1', ableToCastle: true }, 211 | { rank: 'rook', position: 88, color: 'black', name: 'blackRook2', ableToCastle: true }, 212 | { rank: 'king', position: 85, color: 'black', name: 'blackKing', ableToCastle: true }, 213 | ]; 214 | const game = new Game(pieces, 'white'); 215 | 216 | const startNewGame = () => { 217 | document.querySelectorAll('.scene').forEach( scene => scene.classList.remove('show') ); 218 | 219 | const playAgainst = document.querySelector('input[name="oponent"]:checked').value; 220 | const humanColor = document.querySelector('input[name="human_color"]:checked')?.value; 221 | const aiColor = humanColor === 'white' ? 'black' : 'white'; 222 | const aiLevel = 'dumb'; 223 | 224 | startBoard(game, {playAgainst, aiColor, aiLevel}); 225 | } 226 | 227 | const showColorSelect = () => document.querySelector('.select-color-container').classList.add('show'); 228 | const hideColorSelect = () => document.querySelector('.select-color-container').classList.remove('show'); -------------------------------------------------------------------------------- /Game.js: -------------------------------------------------------------------------------- 1 | class Game { 2 | constructor(pieces, turn) { 3 | this.startNewGame(pieces, turn); 4 | } 5 | 6 | startNewGame(pieces, turn) { 7 | this._setPieces(pieces); 8 | 9 | this.turn = turn; 10 | this.clickedPiece = null; 11 | this._events = { 12 | pieceMove: [], 13 | kill: [], 14 | check: [], 15 | promotion: [], 16 | checkMate: [], 17 | turnChange: [] 18 | } 19 | this.history = new History(); 20 | } 21 | 22 | _setPieces(pieces) { 23 | this.pieces = Array(pieces.length); 24 | pieces.forEach( (piece, i) => { 25 | this.pieces[i] = { rank: piece.rank, position: piece.position, color: piece.color, name: piece.name, ableToCastle: piece.ableToCastle } 26 | }); 27 | this.playerPieces = { 28 | white: this.pieces.filter(piece => piece.color === 'white'), 29 | black: this.pieces.filter(piece => piece.color === 'black') 30 | } 31 | } 32 | 33 | _removePiece(piece) { 34 | this.pieces.splice(this.pieces.indexOf(piece), 1); 35 | this.playerPieces[piece.color].splice(this.playerPieces[piece.color].indexOf(piece), 1) 36 | } 37 | 38 | _addPiece(piece) { 39 | this.pieces.push(piece); 40 | this.playerPieces[piece.color].push(piece); 41 | } 42 | 43 | saveHistory() { 44 | this.history.save(); 45 | } 46 | 47 | addToHistory(move) { 48 | this.history.add(move); 49 | } 50 | 51 | clearEvents() { 52 | this._events = {}; 53 | } 54 | 55 | undo() { 56 | const step = this.history.pop(); 57 | 58 | if (!step) { 59 | return false; 60 | } 61 | 62 | for (const subStep of step) { 63 | changePosition(subStep.piece, subStep.from); 64 | if (subStep.from !== 0) { 65 | if (subStep.to === 0) { 66 | this._addPiece(subStep.piece); 67 | } 68 | else if (subStep.castling) { 69 | subStep.piece.ableToCastle = true; 70 | } 71 | this.triggerEvent('pieceMove', subStep); 72 | } 73 | else { 74 | this._removePiece(subStep.piece); 75 | this.triggerEvent('kill', subStep.piece); 76 | } 77 | 78 | if (subStep.from !== 0 && subStep.to !== 0 && (!subStep.castling || subStep.piece.rank === 'king') ) { 79 | this.softChangeTurn(); 80 | } 81 | } 82 | } 83 | 84 | on(eventName, callback) { 85 | if (this._events[eventName] && typeof callback === 'function') { 86 | this._events[eventName].push(callback); 87 | } 88 | } 89 | 90 | softChangeTurn() { 91 | this.turn = this.turn === 'white' ? 'black' : 'white'; 92 | this.triggerEvent('turnChange', this.turn); 93 | } 94 | 95 | changeTurn() { 96 | this.softChangeTurn(); 97 | this.saveHistory(); 98 | } 99 | 100 | getPiecesByColor(color) { 101 | return this.playerPieces[color]; 102 | } 103 | 104 | getPlayerPositions(color){ 105 | return this.getPiecesByColor(color).map(piece => piece.position); 106 | } 107 | 108 | filterPositions(positions) { 109 | return positions.filter(pos => { 110 | const x = pos % 10; 111 | return pos > 10 && pos < 89 && x !== 9 && x !== 0; 112 | }); 113 | }; 114 | 115 | unblockedPositions(piece, allowedPositions, checking=true) { 116 | const unblockedPositions = []; 117 | 118 | const myColor = piece.color; 119 | const otherColor = piece.color === 'white' ? 'black' : 'white'; 120 | 121 | const myBlockedPositions = this.getPlayerPositions(myColor); 122 | const otherBlockedPositions = this.getPlayerPositions(otherColor); 123 | 124 | if (piece.rank === 'pawn') { 125 | for (const move of allowedPositions[0]) { //attacking moves 126 | if (checking && this.myKingChecked(move)) continue; 127 | if (otherBlockedPositions.indexOf(move) !== -1) unblockedPositions.push(move); 128 | } 129 | 130 | for (const move of allowedPositions[1]) { //moving moves 131 | if (myBlockedPositions.indexOf(move) !== -1 || otherBlockedPositions.indexOf(move) !== -1) { 132 | break; 133 | } 134 | else if (checking && this.myKingChecked(move, false)) continue; 135 | unblockedPositions.push(move); 136 | } 137 | } 138 | else{ 139 | allowedPositions.forEach( allowedPositionsGroup => { 140 | for (const move of allowedPositionsGroup) { 141 | if (myBlockedPositions.indexOf(move) !== -1) { 142 | break; 143 | } 144 | else if ( checking && this.myKingChecked(move) ) { 145 | if (otherBlockedPositions.indexOf(move) !== -1) { 146 | break; 147 | } 148 | continue; 149 | } 150 | unblockedPositions.push(move); 151 | 152 | if (otherBlockedPositions.indexOf(move) !== -1) { 153 | break; 154 | } 155 | } 156 | }); 157 | } 158 | 159 | return this.filterPositions(unblockedPositions); 160 | } 161 | 162 | getPieceAllowedMoves(pieceName){ 163 | const piece = this.getPieceByName(pieceName); 164 | if(this.turn === piece.color){ 165 | this.setClickedPiece(piece); 166 | 167 | let pieceAllowedMoves = getAllowedMoves(piece); 168 | if (piece.rank === 'king') { 169 | pieceAllowedMoves = this.getCastlingSquares(piece, pieceAllowedMoves); 170 | } 171 | 172 | return this.unblockedPositions(piece, pieceAllowedMoves, true); 173 | } 174 | else{ 175 | return []; 176 | } 177 | } 178 | 179 | getCastlingSquares(king, allowedMoves) { 180 | if ( !king.ableToCastle || this.king_checked(this.turn) ) return allowedMoves; 181 | const rook1 = this.getPieceByName(this.turn+'Rook1'); 182 | const rook2 = this.getPieceByName(this.turn+'Rook2'); 183 | if (rook1 && rook1.ableToCastle) { 184 | const castlingPosition = rook1.position + 2 185 | if( 186 | !this.positionHasExistingPiece(castlingPosition - 1) && 187 | !this.positionHasExistingPiece(castlingPosition) && !this.myKingChecked(castlingPosition, true) && 188 | !this.positionHasExistingPiece(castlingPosition + 1) && !this.myKingChecked(castlingPosition + 1, true) 189 | ) 190 | allowedMoves[1].push(castlingPosition); 191 | } 192 | if (rook2 && rook2.ableToCastle) { 193 | const castlingPosition = rook2.position - 1; 194 | if( 195 | !this.positionHasExistingPiece(castlingPosition - 1) && !this.myKingChecked(castlingPosition - 1, true) && 196 | !this.positionHasExistingPiece(castlingPosition) && !this.myKingChecked(castlingPosition, true) 197 | ) 198 | allowedMoves[0].push(castlingPosition); 199 | } 200 | return allowedMoves; 201 | } 202 | 203 | getPieceByName(piecename) { 204 | for (const piece of this.pieces) { 205 | if (piece.name === piecename) { 206 | return piece; 207 | } 208 | } 209 | } 210 | 211 | getPieceByPos(position) { 212 | for (const piece of this.pieces) { 213 | if (piece.position == position) { 214 | return piece; 215 | } 216 | } 217 | } 218 | 219 | positionHasExistingPiece(position) { 220 | return this.getPieceByPos(position) !== undefined; 221 | } 222 | 223 | setClickedPiece(piece) { 224 | this.clickedPiece = piece; 225 | } 226 | 227 | triggerEvent(eventName, params) { 228 | if (this._events[eventName]) { 229 | for (const cb of this._events[eventName]) { 230 | cb(params); 231 | } 232 | } 233 | } 234 | 235 | movePiece(pieceName, position) { 236 | const piece = this.getPieceByName(pieceName); 237 | 238 | position = parseInt(position); 239 | 240 | if (piece && this.getPieceAllowedMoves(piece.name).indexOf(position) !== -1) { 241 | const prevPosition = piece.position; 242 | const existedPiece = this.getPieceByPos(position) 243 | 244 | if (existedPiece) { 245 | this.kill(existedPiece); 246 | } 247 | 248 | const castling = !existedPiece && piece.rank === 'king' && piece.ableToCastle === true; 249 | 250 | if (castling) { 251 | if (position - prevPosition === 2) { 252 | this.castleRook(piece.color + 'Rook2'); 253 | } 254 | else if (position - prevPosition === -2) { 255 | this.castleRook(piece.color + 'Rook1'); 256 | } 257 | changePosition(piece, position, true); 258 | } 259 | else { 260 | changePosition(piece, position); 261 | } 262 | 263 | const move = { from: prevPosition, to: position, piece: piece, castling }; 264 | this.addToHistory(move); 265 | this.triggerEvent('pieceMove', move); 266 | 267 | if (piece.rank === 'pawn' && (position > 80 || position < 20)) { 268 | this.promote(piece); 269 | } 270 | 271 | this.changeTurn(); 272 | 273 | if (this.king_checked(this.turn)) { 274 | this.triggerEvent('check', this.turn); 275 | 276 | if (this.king_dead(this.turn)) { 277 | this.checkmate(piece.color); 278 | } 279 | else{ 280 | // alert('check'); 281 | } 282 | } 283 | 284 | return true; 285 | } 286 | else{ 287 | return false; 288 | } 289 | } 290 | 291 | kill(piece) { 292 | this._removePiece(piece); 293 | this.addToHistory({from: piece.position, to: 0, piece: piece}); 294 | this.triggerEvent('kill', piece); 295 | } 296 | 297 | castleRook(rookName) { 298 | const rook = this.getPieceByName(rookName); 299 | const prevPosition = rook.position; 300 | const newPosition = rookName.indexOf('Rook2') !== -1 ? rook.position - 2 : rook.position + 3; 301 | 302 | changePosition(rook, newPosition); 303 | const move = {from: prevPosition, to: newPosition, piece: rook, castling: true}; 304 | this.triggerEvent('pieceMove', move); 305 | this.addToHistory(move); 306 | } 307 | 308 | promote(pawn) { 309 | pawn.name = pawn.name.replace('Pawn', 'Queen'); 310 | pawn.rank = 'queen'; 311 | this.addToHistory({from: 0, to: pawn.position, piece: pawn}); 312 | this.triggerEvent('promotion', pawn); 313 | } 314 | 315 | myKingChecked(pos, kill=true){ 316 | const piece = this.clickedPiece; 317 | const originalPosition = piece.position; 318 | const otherPiece = this.getPieceByPos(pos); 319 | const should_kill_other_piece = kill && otherPiece && otherPiece.rank !== 'king'; 320 | changePosition(piece, pos); 321 | if (should_kill_other_piece) { 322 | this._removePiece(otherPiece); 323 | } 324 | if (this.king_checked(piece.color)) { 325 | changePosition(piece, originalPosition); 326 | if (should_kill_other_piece) { 327 | this._addPiece(otherPiece); 328 | } 329 | return 1; 330 | } 331 | else{ 332 | changePosition(piece, originalPosition); 333 | if (should_kill_other_piece) { 334 | this._addPiece(otherPiece); 335 | } 336 | return 0; 337 | } 338 | } 339 | 340 | king_dead(color) { 341 | const pieces = this.getPiecesByColor(color); 342 | for (const piece of pieces) { 343 | this.setClickedPiece(piece); 344 | const allowedMoves = this.unblockedPositions(piece, getAllowedMoves(piece), true); 345 | if (allowedMoves.length) { 346 | this.setClickedPiece(null); 347 | return 0; 348 | } 349 | } 350 | this.setClickedPiece(null); 351 | return 1; 352 | } 353 | 354 | king_checked(color) { 355 | const piece = this.clickedPiece; 356 | const king = this.getPieceByName(color + 'King'); 357 | const enemyColor = (color === 'white') ? 'black' : 'white'; 358 | const enemyPieces = this.getPiecesByColor(enemyColor); 359 | for (const enemyPiece of enemyPieces) { 360 | this.setClickedPiece(enemyPiece); 361 | const allowedMoves = this.unblockedPositions(enemyPiece, getAllowedMoves(enemyPiece), false); 362 | if (allowedMoves.indexOf(king.position) !== -1) { 363 | this.setClickedPiece(piece); 364 | return 1; 365 | } 366 | } 367 | this.setClickedPiece(piece); 368 | return 0; 369 | } 370 | 371 | checkmate(color){ 372 | this.triggerEvent('checkMate', color); 373 | this.clearEvents(); 374 | } 375 | } -------------------------------------------------------------------------------- /History.js: -------------------------------------------------------------------------------- 1 | class History { 2 | 3 | constructor() { 4 | this._lastStep = []; 5 | this._history = []; 6 | } 7 | 8 | add(step) { 9 | this._lastStep.push(step); 10 | } 11 | 12 | save() { 13 | this._history.push(this._lastStep); 14 | this._lastStep = []; 15 | } 16 | 17 | pop() { 18 | return this._history.pop(); 19 | } 20 | 21 | lastMove() { 22 | return this._history[ this._history.length - 1 ]; 23 | } 24 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a complete well automated chess game made by object oriented javascript I hope you like it. 2 | 3 | Play it from here: https://ahmadalkholy.github.io/Javascript-Chess-Game/chess.html 4 | 5 | ------------ 6 | 7 | The chess pieces images are originaly made by Clker-Free-Vector-Images from Pixabay you can get get it from here. -------------------------------------------------------------------------------- /SimulationGame.js: -------------------------------------------------------------------------------- 1 | class SimulationGame extends Game { 2 | 3 | startNewGame(pieces, turn) { 4 | this._setPieces(pieces); 5 | this.turn = turn; 6 | this.clickedPiece = null; 7 | } 8 | 9 | saveHistory() {} 10 | 11 | addToHistory(move) {} 12 | 13 | triggerEvent(eventName, params) {} 14 | 15 | clearEvents() {} 16 | 17 | undo() {} 18 | 19 | getPieceAllowedMoves(pieceName){ 20 | const piece = this.getPieceByName(pieceName); 21 | if(piece && this.turn === piece.color){ 22 | this.setClickedPiece(piece); 23 | 24 | let pieceAllowedMoves = getAllowedMoves(piece); 25 | if (piece.rank === 'king') { 26 | pieceAllowedMoves = this.getCastlingSquares(piece, pieceAllowedMoves); 27 | } 28 | 29 | return this.unblockedPositions(piece, pieceAllowedMoves, true); 30 | } 31 | else{ 32 | return []; 33 | } 34 | } 35 | 36 | movePiece(pieceName, position) { 37 | const piece = this.getPieceByName(pieceName); 38 | 39 | /*if (!piece) { 40 | return false; 41 | }*/ 42 | 43 | const prevPosition = piece.position; 44 | const existedPiece = this.getPieceByPos(position) 45 | 46 | if (existedPiece) { 47 | this.kill(existedPiece); 48 | } 49 | 50 | const castling = !existedPiece && piece.rank === 'king' && piece.ableToCastle === true; 51 | 52 | if (castling) { 53 | if (position - prevPosition === 2) { 54 | this.castleRook(piece.color + 'Rook2'); 55 | } 56 | else if (position - prevPosition === -2) { 57 | this.castleRook(piece.color + 'Rook1'); 58 | } 59 | changePosition(piece, position, true); 60 | } 61 | else { 62 | changePosition(piece, position); 63 | } 64 | 65 | if (piece.rank === 'pawn' && (position > 80 || position < 20)) { 66 | this.promote(piece); 67 | } 68 | 69 | // this.history.add({ from: prevPosition, to: position, piece: piece, castling }); 70 | this.changeTurn(); 71 | 72 | return true; 73 | } 74 | 75 | king_checked(color) { 76 | const piece = this.clickedPiece; 77 | const king = this.getPieceByName(color + 'King'); 78 | if (!king) { 79 | return true; 80 | } 81 | const enemyColor = (color === 'white') ? 'black' : 'white'; 82 | const enemyPieces = this.getPiecesByColor(enemyColor); 83 | for (const enemyPiece of enemyPieces) { 84 | this.setClickedPiece(enemyPiece); 85 | const allowedMoves = this.unblockedPositions(enemyPiece, getAllowedMoves(enemyPiece), false); 86 | if (allowedMoves.indexOf(king.position) !== -1) { 87 | this.setClickedPiece(piece); 88 | return 1; 89 | } 90 | } 91 | this.setClickedPiece(piece); 92 | return 0; 93 | } 94 | 95 | checkmate(color){} 96 | } -------------------------------------------------------------------------------- /ai.js: -------------------------------------------------------------------------------- 1 | const ai = (aiTurn) => { 2 | const ranks = { pawn: 1, king: 2, bishop: 3, knight: 3, rook: 5, queen: 9 }; 3 | 4 | const simulationGame = new SimulationGame([], 'white'); 5 | 6 | const deepest = 3; 7 | 8 | const humanTurn = aiTurn === 'white' ? 'black' : 'white'; 9 | 10 | const middleSquares = [44, 45, 54, 55]; 11 | const widerMiddleSquares = [43, 46, 53, 56]; 12 | 13 | const isPieceInMiddle = piece => middleSquares.indexOf(piece.position) !== -1; 14 | const isPieceInWiderMiddle = piece => widerMiddleSquares.indexOf(piece.position) !== -1; 15 | 16 | const score = pieces => { 17 | return pieces.reduce( (total, piece) => { 18 | let weight = piece.color === aiTurn ? ranks[piece.rank] : -1 * ranks[piece.rank]; 19 | if ( isPieceInMiddle(piece) ) { 20 | weight *= 1.05; 21 | } 22 | else if ( isPieceInWiderMiddle(piece) ) { 23 | weight *= 1.02; 24 | } 25 | total += weight; 26 | return total; 27 | }, 0 ); 28 | } 29 | 30 | const isBetterScore = (score1, score2, turn) => turn === aiTurn ? score1 >= score2 : score1 <= score2; 31 | 32 | const isScoreGoodEnough = (score, turn) => turn === aiTurn ? score > 7 : score < -7; 33 | 34 | const minimax = (pieces, turn, depth = 0) => { 35 | simulationGame.startNewGame(pieces, turn); 36 | 37 | if ( !simulationGame.getPieceByName(humanTurn + 'King') || simulationGame.king_dead(humanTurn ) ) { 38 | return {score: -Infinity, depth}; 39 | } 40 | if ( !simulationGame.getPieceByName(aiTurn + 'King') || simulationGame.king_dead(aiTurn ) ) { 41 | return {score: Infinity, depth}; 42 | } 43 | 44 | let bestPlay = { move: null, score: turn === aiTurn ? -Infinity : Infinity }; 45 | 46 | for (const piece of pieces) { 47 | const allowedMoves = simulationGame.getPieceAllowedMoves(piece.name); 48 | 49 | for (const move of allowedMoves) { 50 | 51 | const currentTestPlayInfo = {}; 52 | 53 | currentTestPlayInfo.move = {pieceName: piece.name, position: move}; 54 | simulationGame.movePiece(piece.name, move); 55 | 56 | const curScore = score(simulationGame.pieces); 57 | 58 | if ( depth === deepest || isBetterScore(bestPlay.score, curScore, turn) || isScoreGoodEnough(curScore, turn) ) { 59 | currentTestPlayInfo.score = curScore; 60 | } 61 | else if (turn === aiTurn) { 62 | const result = minimax(simulationGame.pieces, humanTurn, depth + 1); 63 | currentTestPlayInfo.score = result.score; 64 | } else { 65 | const result = minimax(simulationGame.pieces, aiTurn, depth + 1); 66 | currentTestPlayInfo.score = result.score; 67 | } 68 | 69 | if ( isBetterScore(currentTestPlayInfo.score, bestPlay.score, turn) ){ 70 | bestPlay.move = currentTestPlayInfo.move; 71 | bestPlay.score = currentTestPlayInfo.score; 72 | } 73 | 74 | simulationGame.startNewGame(pieces, turn); 75 | } 76 | } 77 | 78 | return bestPlay; 79 | } 80 | 81 | let play; 82 | 83 | if ( isTestEnv() ) { 84 | play = (pieces, callback) => { 85 | setTimeout( () => { 86 | testFuncTime( () => { 87 | const aiPlay = minimax(pieces, aiTurn); 88 | callback(aiPlay); 89 | }); 90 | }, 100); 91 | } 92 | } 93 | else { 94 | play = (pieces, callback) => { 95 | setTimeout( () => { 96 | const aiPlay = minimax(pieces, aiTurn); 97 | callback(aiPlay); 98 | }, 100); 99 | } 100 | } 101 | 102 | return { 103 | play 104 | } 105 | } 106 | 107 | const isTestEnv = function() { 108 | const url = new URL(location.href); 109 | const params = new URLSearchParams(url.searchParams); 110 | return Boolean(params.get('testing')) 111 | } 112 | 113 | const testFuncTime = func => { 114 | const label = 'Timer ' + Date.now(); 115 | console.time(label); 116 | console.log( 'Output:', func() ); 117 | console.timeLog(label); 118 | } -------------------------------------------------------------------------------- /chess.css: -------------------------------------------------------------------------------- 1 | *{ 2 | margin: 0; 3 | padding: 0; 4 | } 5 | body{ 6 | background: #ddd; 7 | } 8 | .board-container { 9 | width: 600px; 10 | max-width: 90%; 11 | justify-content: center; 12 | margin:auto; 13 | position:relative; 14 | } 15 | .semataries { 16 | width: 100%; 17 | margin:auto; 18 | display: flex; 19 | justify-content: center; 20 | } 21 | .sematary { 22 | width: 50%; 23 | } 24 | #blackSematary { 25 | text-align: right; 26 | } 27 | #board{ 28 | aspect-ratio: 1 / 1; 29 | margin: auto; 30 | border:3px #333 solid; 31 | } 32 | #board div div{ 33 | float: left; 34 | width: calc(100% / 8); 35 | aspect-ratio: 1 / 1; 36 | box-sizing: border-box; 37 | border: #000 solid .01cm; 38 | } 39 | 40 | #board .even div:nth-child(even){ 41 | background: #ccd; 42 | } 43 | 44 | #board .even div:nth-child(odd){ 45 | background: rgb(112,112,112);/*621700*/ 46 | } 47 | 48 | #board .odd div:nth-child(even){ 49 | background: rgb(112,112,112);/*621700*/ 50 | } 51 | 52 | #board .odd div:nth-child(odd){ 53 | background: #ccd; 54 | } 55 | 56 | .animate{ 57 | animation: rotateBoard 1s ease-out; 58 | animation-fill-mode: both; 59 | } 60 | @keyframes rotateBoard { 61 | 0% { 62 | transform: rotateZ(0); 63 | } 64 | 100%{ 65 | transform: rotateZ(-180deg); 66 | } 67 | 68 | } 69 | .forward{ 70 | transform: rotateZ(-180deg); 71 | } 72 | .backward{ 73 | transform: rotateZ(0); 74 | } 75 | .animate-backward{ 76 | animation: rotateBoardBackward 1s ease-out; 77 | animation-fill-mode: both; 78 | } 79 | @keyframes rotateBoardBackward { 80 | 0% { 81 | transform: rotateZ(-180deg); 82 | } 83 | 100%{ 84 | transform: rotateZ(0); 85 | } 86 | 87 | } 88 | img.piece{ 89 | width: 100%; 90 | height: 100%; 91 | float: left; 92 | } 93 | .allowed{ 94 | opacity: .8; 95 | background: radial-gradient(#333,#222 )!important; 96 | /*-webkit-box-shadow: inset 1px -4px 92px 0px rgba(0,0,0,0.75); 97 | -moz-box-shadow: inset 1px -4px 92px 0px rgba(0,0,0,0.75); 98 | box-shadow: inset 1px -4px 92px 0px rgba(0,0,0,0.75);*/ 99 | border:1px solid black !important; 100 | } 101 | .last-move { 102 | background: #30b030 !important; 103 | } 104 | .clicked-square{ 105 | background: radial-gradient(#333,#222 )!important; 106 | border:1px solid black !important; 107 | } 108 | .sematary img { 109 | transform: rotateZ(0); 110 | width: 1.8rem; 111 | height: 1.8rem; 112 | } 113 | #blackSematary div{ 114 | overflow-y: auto; 115 | margin-bottom: 2px; 116 | } 117 | .scene{ 118 | position: relative; 119 | opacity: 0; 120 | display: none; 121 | z-index: 1; 122 | } 123 | .overlay{ 124 | position: fixed; 125 | width: 100%; 126 | height: 100%; 127 | background: #000; 128 | opacity: .7; 129 | z-index: 1; 130 | } 131 | .scene .scene-content{ 132 | position: fixed; 133 | color:#fff; 134 | z-index: 2; 135 | width: 100%; 136 | text-align: center; 137 | margin-top: 40vh; 138 | font-size: 40px; 139 | height: 100vh; 140 | } 141 | 142 | .scene-content h2 { 143 | font-weight: 500; 144 | margin-bottom: 15px; 145 | } 146 | 147 | @media screen and (max-width: 600px) { 148 | .scene-content h2 { 149 | font-size: 2rem; 150 | } 151 | } 152 | 153 | .show{ 154 | display: block !important; 155 | animation: showMessage 1s ease-out; 156 | animation-fill-mode: both; 157 | } 158 | 159 | .hidden { 160 | display: none; 161 | } 162 | 163 | @keyframes showMessage { 164 | 0% { 165 | opacity: 0; 166 | } 167 | 100%{ 168 | opacity: 1; 169 | } 170 | 171 | } 172 | #turn{ 173 | text-align: center; 174 | font-size: 18px; 175 | } 176 | .winning-sign:first-letter{ 177 | text-transform: uppercase; 178 | } 179 | 180 | .flip-board{ 181 | padding: 10px 20px; 182 | border-radius: 5px !important; 183 | outline: 0; 184 | background: #7f979e; 185 | color: white; 186 | border: 0; 187 | } 188 | 189 | 190 | input[type="radio"] { 191 | display: none; 192 | } 193 | label { 194 | background-color: rgb(112,112,112); 195 | position: relative; 196 | font-family: "Poppins", sans-serif; 197 | cursor: pointer; 198 | display: inline-flex; 199 | align-items: center; 200 | gap: 0.8em; 201 | padding: 1em 2em; 202 | border-radius: 0.5em; 203 | font-size: 23px; 204 | } 205 | 206 | input[type="radio"]:checked + label { 207 | background-color: #4189e0; 208 | color: #ffffff; 209 | } 210 | 211 | .button { 212 | background-color: rgb(112,112,112); 213 | border-radius: 10px; 214 | border: none; 215 | color: white; 216 | padding: 20px 40px; 217 | text-align: center; 218 | text-decoration: none; 219 | display: inline-block; 220 | font-size: 20px; 221 | cursor: pointer; 222 | } 223 | 224 | .button:hover, label:hover { 225 | background-color: rgb(160, 160, 160); 226 | } 227 | 228 | .button-big { 229 | padding: 30px 60px; 230 | font-size: 25px; 231 | } -------------------------------------------------------------------------------- /chess.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chess Game 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |

Play Against

18 | 19 | 20 |   21 | 22 | 23 |
24 | 25 | 33 | 34 |
35 | 36 |
37 |
38 |
39 |
40 | 41 |
42 |

43 |
44 |
45 | 46 |
47 |
48 | 49 |
50 |
51 |
52 | 53 |
54 |
55 | 56 |
57 |
58 | 59 |
60 |
61 | 62 |
63 |
64 | 65 |
66 |
67 | 68 |
69 |
70 | 71 |
72 |
73 | 74 |
75 |
76 |
77 |
78 | 79 |
80 |
81 | 82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 |
90 | 91 |
92 |
93 | 94 |
95 |
96 | 97 |
98 |
99 | 100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | 145 |
146 |
147 | 148 |
149 |
150 | 151 |
152 |
153 | 154 |
155 |
156 | 157 |
158 |
159 | 160 |
161 |
162 | 163 |
164 |
165 | 166 |
167 |
168 |
169 |
170 | 171 |
172 |
173 | 174 |
175 |
176 | 177 |
178 |
179 | 180 |
181 |
182 | 183 |
184 |
185 | 186 |
187 |
188 | 189 |
190 |
191 | 192 |
193 |
194 |
195 | 196 |

White's Turn

197 | 198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 | 207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 | 216 |
217 | 218 |
219 | 220 |
221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 251 | 252 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/favicon.ico -------------------------------------------------------------------------------- /img/black-bishop.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/black-bishop.webp -------------------------------------------------------------------------------- /img/black-king.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/black-king.webp -------------------------------------------------------------------------------- /img/black-knight.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/black-knight.webp -------------------------------------------------------------------------------- /img/black-pawn.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/black-pawn.webp -------------------------------------------------------------------------------- /img/black-queen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/black-queen.webp -------------------------------------------------------------------------------- /img/black-rook.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/black-rook.webp -------------------------------------------------------------------------------- /img/share.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/share.jpeg -------------------------------------------------------------------------------- /img/white-bishop.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/white-bishop.webp -------------------------------------------------------------------------------- /img/white-king.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/white-king.webp -------------------------------------------------------------------------------- /img/white-knight.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/white-knight.webp -------------------------------------------------------------------------------- /img/white-pawn.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/white-pawn.webp -------------------------------------------------------------------------------- /img/white-queen.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/white-queen.webp -------------------------------------------------------------------------------- /img/white-rook.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AhmadAlkholy/Javascript-Chess-Game/9892f3dbbc23f30e738bc4661fea25621d8dbfc0/img/white-rook.webp -------------------------------------------------------------------------------- /piece.js: -------------------------------------------------------------------------------- 1 | const changePosition = (piece, position, castle=false) => { 2 | piece.position = position; 3 | 4 | if (piece.rank === 'king') { 5 | if (castle) { 6 | piece.ableToCastle = false; 7 | } 8 | } 9 | 10 | if (piece.rank === 'rook') { 11 | piece.ableToCastle = false; 12 | } 13 | 14 | } 15 | 16 | const getMovesTop = (piece) => { 17 | const movesTop = []; 18 | for (let move = piece.position+10; move <= 88; move+=10) movesTop.push(move); 19 | return movesTop; 20 | } 21 | 22 | const getMovesBottom = (piece) => { 23 | const movesBottom = []; 24 | for (let move = piece.position-10; move >= 11 ; move-=10) movesBottom.push(move); 25 | return movesBottom; 26 | } 27 | 28 | const getMovesRight = (piece) => { 29 | const num = piece.position+''; 30 | const movesRight = []; 31 | for (let move = piece.position+1; move <= parseInt(num[0]+'8'); move++) movesRight.push(move); 32 | return movesRight; 33 | } 34 | 35 | const getMovesLeft = (piece) => { 36 | const num = piece.position+''; 37 | const movesLeft = []; 38 | for (let move = piece.position-1; move >= parseInt(num[0]+'1'); move--) movesLeft.push(move); 39 | return movesLeft; 40 | } 41 | 42 | const getMovesTopRight = (piece) => { 43 | const movesTopRight = []; 44 | for (let move = piece.position+11; move <= 88; move+=11) { 45 | const firstDigit = (''+move)[1]; 46 | if (firstDigit > 8 || firstDigit < 1) break; 47 | movesTopRight.push(move); 48 | } 49 | return movesTopRight; 50 | } 51 | 52 | const getMovesTopLeft = (piece) => { 53 | const movesTopLeft = []; 54 | for (let move = piece.position+9; move <= 88; move+=9) { 55 | const firstDigit = (''+move)[1]; 56 | if (firstDigit > 8 || firstDigit < 1) break; 57 | movesTopLeft.push(move); 58 | } 59 | return movesTopLeft; 60 | } 61 | 62 | const getMovesBottomRight = (piece) => { 63 | const movesBottomRight = []; 64 | for (let move = piece.position-9; move >= 11 ; move-=9) { 65 | const firstDigit = (''+move)[1]; 66 | if (firstDigit > 8 || firstDigit < 1) break; 67 | movesBottomRight.push(move); 68 | } 69 | return movesBottomRight; 70 | } 71 | 72 | const getMovesBottomLeft = (piece) => { 73 | const movesBottomLeft = []; 74 | for (let move = piece.position-11; move >= 11 ; move-=11) { 75 | const firstDigit = (''+move)[1]; 76 | if (firstDigit > 8 || firstDigit < 1) break; 77 | movesBottomLeft.push(move); 78 | } 79 | return movesBottomLeft; 80 | } 81 | 82 | const getPawnAllowedMoves = (pawn) => { 83 | const position = pawn.position; 84 | const mathSign = (pawn.color === 'white') ? 1: -1; 85 | const allowedMoves = [position + mathSign * 10]; 86 | 87 | if ( (position >20 && position < 29) || (position >70 && position < 79) ) { 88 | allowedMoves.push(position + mathSign * 20); 89 | } 90 | 91 | const attackMoves = [position + mathSign * 9, position + mathSign * 11]; 92 | return [ attackMoves, allowedMoves ]; 93 | } 94 | 95 | const getKnightAllowedMoves = (knight) => { 96 | const position = knight.position; 97 | return [ 98 | [position + 21], 99 | [position - 21], 100 | [position + 19], 101 | [position - 19], 102 | [position + 12], 103 | [position - 12], 104 | [position + 8], 105 | [position - 8] 106 | ]; 107 | } 108 | 109 | const getKingAllowedMoves = king => { 110 | const position = king.position; 111 | return [ 112 | [position + 1], 113 | [position - 1], 114 | [position + 10], 115 | [position - 10], 116 | [position + 11], 117 | [position - 11], 118 | [position + 9], 119 | [position - 9] 120 | ]; 121 | } 122 | 123 | const getBishopAllowedMoves = (bishop) => { 124 | return [ getMovesTopRight(bishop), getMovesTopLeft(bishop), getMovesBottomRight(bishop), getMovesBottomLeft(bishop) ]; 125 | } 126 | 127 | const getRookAllowedMoves = (rook) => { 128 | return [ getMovesTop(rook), getMovesBottom(rook), getMovesRight(rook), getMovesLeft(rook) ]; 129 | } 130 | 131 | const getQueenAllowedMoves = queen => { 132 | return [ 133 | getMovesTop(queen), 134 | getMovesTopRight(queen), 135 | getMovesTopLeft(queen), 136 | getMovesBottom(queen), 137 | getMovesBottomRight(queen), 138 | getMovesBottomLeft(queen), 139 | getMovesRight(queen), 140 | getMovesLeft(queen) 141 | ]; 142 | } 143 | 144 | const getAllowedMoves = (piece) => { 145 | let allowedMoves; 146 | 147 | switch (piece.rank) { 148 | case 'pawn': 149 | allowedMoves = getPawnAllowedMoves(piece); 150 | break; 151 | case 'knight': 152 | allowedMoves = getKnightAllowedMoves(piece); 153 | break; 154 | case 'king': 155 | allowedMoves = getKingAllowedMoves(piece); 156 | break; 157 | case 'bishop': 158 | allowedMoves = getBishopAllowedMoves(piece); 159 | break; 160 | case 'rook': 161 | allowedMoves = getRookAllowedMoves(piece); 162 | break; 163 | case 'queen': 164 | allowedMoves = getQueenAllowedMoves(piece); 165 | break; 166 | default: 167 | throw "Unknown rank: " + piece.type; 168 | } 169 | 170 | return allowedMoves; 171 | } --------------------------------------------------------------------------------