UNPKG

@real_one_chess_king/game-logic

Version:
171 lines 6.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MovesTree = void 0; const affect_types_1 = require("../affect/affect.types"); const color_1 = require("../color"); const moves_tree_utils_1 = require("./moves-tree.utils"); const affect_1 = require("../affect"); /** * This structure keep all possible moves for both players * Root keeps all moves for currentColor, each node keeps all possible moves for the next player. * It applies node marks from global rules. * Restrictions: checkmate/stalemate can not be detected on the first turn. * So all tests on check mate should contains at least one process turn cal */ class MovesTree { board; initialTurns; globalRules; length; root; constructor(board, // original b initialTurns, globalRules, length, currentColor) { this.board = board; this.initialTurns = initialTurns; this.globalRules = globalRules; this.length = length; this.root = this.createEmptyNode(currentColor); this.fillUpRoot(); } fillUpRoot() { this.fillUpNode(this.root, this.initialTurns); // contain all moves for the first turn let i = 1; while (i < this.length) { this.raiseTree(); i += 1; } this.applyGlobalRules(this.root); if (this.length > 1) { this.forEachChild(this.root, (node) => { this.applyGlobalRules(node, this.root); }); } this.treeShaking(this.root); if (this.length > 1) { this.forEachChild(this.root, (node) => { this.treeShaking(node); }); } } /** * Move root to the next level by turn data * @param fromCoordinate * @param fromCoordinate * @param selectedPieceType - using for transforming pawn to another piece */ processTurn(turn) { const fromCoordinate = turn.affects.find((a) => a.type === affect_types_1.AffectType.move && a.userSelected)?.from; if (!fromCoordinate) { throw new Error("From coordinate is not found"); } const from = (0, moves_tree_utils_1.serializeCoordinate)(fromCoordinate); const to = (0, moves_tree_utils_1.serializeAffects)(turn.affects); const movementResults = this.root.movements[from][to]; const nextNode = movementResults.next; const prevRoot = this.root; this.root = nextNode; this.updateBoard(movementResults.affects); this.raiseTree(); this.applyGlobalRules(this.root, prevRoot); this.forEachChild(this.root, (node) => { this.applyGlobalRules(node, this.root); }); this.treeShaking(this.root); } getRoot() { return this.root; } createEmptyNode(color) { return { color, movements: {}, }; } applyGlobalRules(node, prevNode) { for (const rule of this.globalRules) { rule.markNodeWithChilds(node, prevNode, this.board, this.initialTurns); } } raiseTree() { this.forEachSubTreeLeaf(this.root, this.initialTurns, (node, turns) => { this.fillUpNode(node, turns); }); } /** * It cuts off all invalid moves from the tree * Like moves that leads to check */ treeShaking(node) { Object.keys(node.movements).forEach((fromKey) => { Object.keys(node.movements[fromKey]).forEach((toKey) => { if (node.movements[fromKey][toKey].suisidal) { delete node.movements[fromKey][toKey]; } }); if (Object.keys(node.movements[fromKey]).length === 0) { delete node.movements[fromKey]; } }); } forEachChild({ movements }, callback) { Object.keys(movements).forEach((fromKey) => { Object.keys(movements[fromKey]).forEach((toKey) => { const movementResult = movements[fromKey][toKey]; const nextNode = movementResult.next; const movementResultAffects = movementResult.affects; this.updateBoard(movementResultAffects); callback(nextNode); this.board.revertMove(movementResultAffects); }); }); } forEachSubTreeLeaf({ movements }, turns, callback) { Object.keys(movements).forEach((fromKey) => { Object.keys(movements[fromKey]).forEach((toKey) => { const { next, affects } = movements[fromKey][toKey]; const moveAffect = affects.find((a) => (0, affect_1.isMoveAffect)(a) && a.userSelected); if (!moveAffect) { throw new Error("Move affect is not found"); } const pieceType = this.board.getPieceByCoordinate(moveAffect.from)?.type; if (!pieceType) { throw new Error("Piece type is not found"); } turns.push({ affects, pieceType }); this.updateBoard(affects); if (Object.keys(next.movements).length === 0) { callback(next, turns); } else { this.forEachSubTreeLeaf(next, turns, callback); } this.board.revertMove(affects); turns.pop(); }); }); } updateBoard(action) { this.board.updateCellsOnMove(action); } // it expects empty node which will be filled up by current state of this.squares fillUpNode(node, turns) { this.board.forEachPiece(node.color, (piece, x, y) => { const fromKey = (0, moves_tree_utils_1.serializeXY)(x, y); if (piece && piece.color === node.color) { node.movements[fromKey] = {}; const availableMoves = this.board.getPieceAvailableMoves(x, y, turns); const reversedColor = (0, color_1.reverseColor)(node.color); availableMoves.forEach((affects) => { const toKey = (0, moves_tree_utils_1.serializeAffects)(affects); const newNode = this.createEmptyNode(reversedColor); node.movements[fromKey][toKey] = { affects, next: newNode, }; }); } }); } } exports.MovesTree = MovesTree; //# sourceMappingURL=moves-tree.js.map