UNPKG

chess-easy

Version:

Chess engine that makes writing chessgame easier than writing a calculator

314 lines 13.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MovesGenerator = void 0; const common_1 = require("../types/common"); const utils_1 = require("../utils"); const MoveMaker_1 = require("./MoveMaker"); class MovesGenerator { constructor(gameState) { this.startingPawnIndexes = { [common_1.Colors.BLACK]: 6, [common_1.Colors.WHITE]: 1 }; this.movesNext = common_1.Colors.WHITE; this.kingField = ""; this.gameState = gameState; } getEnemyColor(color) { return color === common_1.Colors.WHITE ? common_1.Colors.BLACK : common_1.Colors.WHITE; } getPossiblePawnMoves(row, column, color) { var _a, _b; const colorMultiplier = color === common_1.Colors.BLACK ? -1 : 1; const possibleMoves = []; const enemyColor = this.getEnemyColor(color); const nextRow = row + colorMultiplier * 1; if (nextRow > 7 || nextRow < 0) { return []; } if (!this.gameState[nextRow][column]) { possibleMoves.push((0, utils_1.indexesToField)(nextRow, column)); } const rowPlusTwo = row + colorMultiplier * 2; if (row === this.startingPawnIndexes[color] && !this.gameState[nextRow][column] && !this.gameState[rowPlusTwo][column]) { possibleMoves.push((0, utils_1.indexesToField)(rowPlusTwo, column)); } if (column < 7 && ((_a = this.gameState[nextRow][column + 1]) === null || _a === void 0 ? void 0 : _a.color) === enemyColor) { possibleMoves.push((0, utils_1.indexesToField)(nextRow, column + 1)); } if (column > 0 && ((_b = this.gameState[nextRow][column - 1]) === null || _b === void 0 ? void 0 : _b.color) === enemyColor) { possibleMoves.push((0, utils_1.indexesToField)(nextRow, column - 1)); } if (this.enPassantPossibility !== "-") { const [enPassantColumn] = this.enPassantPossibility.split(""); const enPassantColumnIndex = (0, utils_1.mapLetterToColumnIndex)(enPassantColumn); const isPieceColumnNextToEnPassant = Math.abs(enPassantColumnIndex - column) === 1; const isPieceRowNextToEnPassant = color === common_1.Colors.WHITE ? row === 4 : row === 3; if (isPieceColumnNextToEnPassant && isPieceRowNextToEnPassant) { possibleMoves.push(this.enPassantPossibility); } } return possibleMoves; } getPossibleFigureMoves(row, column, color, patterns, oneMove = false) { var _a, _b; const possibleMoves = []; const enemyColor = this.getEnemyColor(color); for (let n in patterns) { let nextRow = row + patterns[n][0]; let nextColumn = column + patterns[n][1]; while (true) { if (nextRow >= 0 && nextRow <= 7 && nextColumn >= 0 && nextColumn <= 7) { if (((_a = this.gameState[nextRow][nextColumn]) === null || _a === void 0 ? void 0 : _a.color) === enemyColor) { possibleMoves.push((0, utils_1.indexesToField)(nextRow, nextColumn)); break; } else if (((_b = this.gameState[nextRow][nextColumn]) === null || _b === void 0 ? void 0 : _b.color) === color) { break; } else { possibleMoves.push((0, utils_1.indexesToField)(nextRow, nextColumn)); } } else { break; } nextRow += patterns[n][0]; nextColumn += patterns[n][1]; if (oneMove) { break; } } } return possibleMoves; } getPossibleMoves(row, column, field) { if (!field) return []; switch (field.piece) { case common_1.ChessPieces.PAWN: { return this.getPossiblePawnMoves(row, column, field.color); } case common_1.ChessPieces.KING: { if (field.color === this.movesNext) { this.kingField = (0, utils_1.indexesToField)(row, column); } return this.getPossibleFigureMoves(row, column, field.color, [ [0, 1], [1, 0], [1, 1], [-1, -1], [0, -1], [-1, 0], [-1, 1], [1, -1], ], true); } case common_1.ChessPieces.QUEEN: { return this.getPossibleFigureMoves(row, column, field.color, [ [1, 1], [1, -1], [-1, 1], [-1, -1], [0, 1], [1, 0], [-1, 0], [0, -1], ]); } case common_1.ChessPieces.BISHOP: { return this.getPossibleFigureMoves(row, column, field.color, [ [1, 1], [1, -1], [-1, 1], [-1, -1], ]); } case common_1.ChessPieces.ROOK: { return this.getPossibleFigureMoves(row, column, field.color, [ [0, 1], [1, 0], [-1, 0], [0, -1], ]); } case common_1.ChessPieces.KNIGHT: { return this.getPossibleFigureMoves(row, column, field.color, [ [2, 1], [2, -1], [-2, 1], [-2, -1], [1, 2], [-1, 2], [1, -2], [-1, -2], ], true); } default: { return []; } } } areFieldsBetweenPiecesInRowEmptyAndNotAttacked(row, column1, column2, enemyMoves) { const fieldsToCheck = this.gameState[row].slice(column1, column2); return fieldsToCheck.every((field, index) => !field && !this.isFieldAttacked((0, utils_1.indexesToField)(row, index + column1), enemyMoves)); } addCastlingMoves(allPossibleMoves, movesNext, possibleEnemyMoves) { const movesToUpdate = { ...allPossibleMoves }; if (this.castlingAvailability.length) { const rowToCheck = movesNext === common_1.Colors.WHITE ? 0 : 7; const isQueenSidePossible = this.castlingAvailability.includes(movesNext === common_1.Colors.WHITE ? "Q" : "q") && this.areFieldsBetweenPiecesInRowEmptyAndNotAttacked(rowToCheck, 1, 4, possibleEnemyMoves); const isKingSidePossible = this.castlingAvailability.includes(movesNext === common_1.Colors.WHITE ? "K" : "k") && this.areFieldsBetweenPiecesInRowEmptyAndNotAttacked(rowToCheck, 5, 7, possibleEnemyMoves); const rookColumns = [ ...(isQueenSidePossible ? ["a"] : []), ...(isKingSidePossible ? ["h"] : []), ]; const kingField = `e${rowToCheck + 1}`; rookColumns.forEach(column => { const rookField = `${column}${rowToCheck + 1}`; movesToUpdate[rookField] = [ ...(movesToUpdate[rookField] || []), kingField, ]; movesToUpdate[kingField] = [ ...(movesToUpdate[kingField] || []), rookField, ]; }); } return movesToUpdate; } getAllPossibleBasicMoves(moves) { const allPossibleMoves = {}; this.gameState.forEach((row, row_index) => { row.forEach((field, column_index) => { const piecePosition = `${(0, utils_1.mapColumnIndexToLetter)(column_index)}${row_index + 1}`; if ((field === null || field === void 0 ? void 0 : field.color) !== moves) { allPossibleMoves[piecePosition] = []; return; } allPossibleMoves[piecePosition] = this.getPossibleMoves(row_index, column_index, field); }); }); return allPossibleMoves; } isFieldAttacked(field, enemyMoves) { return Object.values(enemyMoves).some(moves => moves.includes(field)); } filterIllegalInCheckMoves(allMoves) { const allFields = Object.keys(allMoves); const moveMaker = new MoveMaker_1.MoveMaker(this.gameState, this.enPassantPossibility, this.movesNext, this.castlingAvailability); allFields.forEach(field => { const moves = allMoves[field]; const [column_from, row_from] = (0, utils_1.fieldToIndexes)(field); const newMoves = moves.filter(move => { var _a; const newKingField = ((_a = this.gameState[row_from][column_from]) === null || _a === void 0 ? void 0 : _a.piece) === common_1.ChessPieces.KING ? move : this.kingField; const { gameState } = moveMaker.move(field, move); const initialGameState = this.gameState; this.gameState = gameState; const currentEnemyMoves = this.getAllPossibleBasicMoves(this.getEnemyColor(this.movesNext)); this.gameState = initialGameState; return !this.isFieldAttacked(newKingField, currentEnemyMoves); }); allMoves[field] = newMoves; }); return allMoves; } isInsufficientMaterial() { const flattenGameState = this.gameState.flat(); const starterGameSituation = { black: { bishops: 0, knights: 0, otherPieces: 0, }, white: { bishops: 0, knights: 0, otherPieces: 0, }, }; const gameSituation = flattenGameState.reduce((acc, curr) => { if (curr) { const { color, piece } = curr; if (piece === common_1.ChessPieces.KING) { return acc; } const colorToUpdate = color === common_1.Colors.BLACK ? "black" : "white"; if (piece === common_1.ChessPieces.KNIGHT) { return { ...acc, [colorToUpdate]: { ...acc[colorToUpdate], knights: acc[colorToUpdate].knights + 1, }, }; } else if (piece === common_1.ChessPieces.BISHOP) { return { ...acc, [colorToUpdate]: { ...acc[colorToUpdate], bishops: acc[colorToUpdate].bishops + 1, }, }; } else { return { ...acc, [colorToUpdate]: { ...acc[colorToUpdate], otherPieces: acc[colorToUpdate].otherPieces + 1, }, }; } } return acc; }, starterGameSituation); const areOtherPiecesPresent = gameSituation.black.otherPieces > 0 || gameSituation.white.otherPieces > 0; if (areOtherPiecesPresent) { return false; } const blackMinorPieces = gameSituation.black.bishops + gameSituation.black.knights; const whiteMinorPieces = gameSituation.white.bishops + gameSituation.white.knights; if (blackMinorPieces <= 1 && whiteMinorPieces <= 1) { return true; } return ((gameSituation.white.knights === 2 && blackMinorPieces === 0) || (gameSituation.black.knights === 2 && whiteMinorPieces === 0)); } getAllPossibleMoves(movesNext, castlingAvailability, enPassantPossibility) { this.enPassantPossibility = enPassantPossibility; this.castlingAvailability = castlingAvailability; this.movesNext = movesNext; const basicMoves = this.getAllPossibleBasicMoves(movesNext); const enemyBasicMoves = this.getAllPossibleBasicMoves(this.getEnemyColor(movesNext)); const movesWithCastling = this.addCastlingMoves(basicMoves, movesNext, enemyBasicMoves); const isCheck = this.isFieldAttacked(this.kingField, enemyBasicMoves); const allMoves = this.filterIllegalInCheckMoves(movesWithCastling); const canPlayerMove = Object.values(allMoves).some(moves => moves.length); const isCheckmate = isCheck && !canPlayerMove; const isStalemate = !isCheck && !canPlayerMove; const isInsufficientMaterial = this.isInsufficientMaterial(); return { allMoves: this.filterIllegalInCheckMoves(movesWithCastling), isCheck, isCheckmate, isStalemate, isInsufficientMaterial, }; } } exports.MovesGenerator = MovesGenerator; //# sourceMappingURL=MovesGenerator.js.map