/** * Static functions and utilities */ var Util = { /** * Constants & globals */ Constants: { WALL: "###", BLANK: "...", PUZZLE_SIZES: ['S22', 'S32', 'S33', 'S43', 'S44', 'S54', 'S55', 'S65', 'S66', 'S76'] }, Globals: { currentLevelDistribution: null, levelType: -1 }, /** * Create a clone of a given puzzle */ createPuzzleCopy: function (puzzle) { var new_puzzle = []; var num_rows = puzzle.length, num_cols = puzzle[0].length; for (var i = 0; i < num_rows; i++) { var row = []; for (var j = 0; j < num_cols; j++) { row.push(puzzle[i][j]); } new_puzzle.push(row); } return new_puzzle; }, /** * Array shuffle. */ arrayShuffle: function (array) { var j, x, i; for (i = array.length - 1; i > 0; i--) { j = Math.floor(Math.random() * (i + 1)); x = array[i]; array[i] = array[j]; array[j] = x; } }, /** * Array shuffle. */ arrayShuffleWithNeedle: function (array, needle) { var j, x, i; for (i = array.length - 1; i > 0; i--) { /*do { j = Math.floor(Math.random() * (i + 1)); } while (array[j] === needle);*/ j = Math.floor(Math.random() * (i + 1)); if (array[i] !== needle && array[j] !== needle) { x = array[i]; array[i] = array[j]; array[j] = x; } } }, /** * Get Array from matrix * @param matrix * @returns {Array} */ arrayFromMatrix: function(matrix) { var array = []; for (var i = 0; i < matrix.length; i++) { for (var j = 0; j < matrix[0].length; j++) { array.push(matrix[i][j]); } } return array; }, /** * Matrix shuffle * @param matrix */ matrixShuffleWithNeedle: function(matrix) { var array = this.arrayFromMatrix(matrix); this.arrayShuffleWithNeedle(array, this.Constants.WALL); var cont = 0; for (var i = 0; i < matrix.length; i++) { for (var j = 0; j < matrix[0].length; j++) { matrix[i][j] = array[cont++]; } } }, /** * Add successor to state successors * * @private */ addSuccessor: function (newPuzzle, successors, action, row, col) { // First of all the Action is created var newAction = new Action(action.getDirection(), action.getValue(), [row, col]); // The action object allows us to create the successor var successor = new State(newPuzzle, Infinity, null, newAction); // check if the puzzle is already in successors if (!this.checkIfArrayContainsMatrix(successors, successor)) { successors.push(successor); } }, /** * Check if array contains matrix * * @param array * @param matrix * @returns {boolean} */ checkIfArrayContainsMatrix: function (array, matrix) { for (var i = 0; i < array.length; i++) { if (array[i].equals(matrix)) { return true; } } return false; }, /** * Check whether or not a couple of matrices are equal * @param A * @param B * @returns {boolean} */ matrixEquals: function (A, B) { for (var i = 0; i < A.length; i++) { for (var j = 0; j < A[0].length; j++) { if (A[i][j] !== B[i][j]) { return false; } } } return true; }, /** * Check whether or not a couple of arrays are equal * @param A * @param B * @returns {boolean} */ arrayEquals: function(A, B) { for (var i = 0; i < A.length; i++) { if (A[i] !== B[i]) { return false; } } return true; }, /** * Make array copy * @param array */ arrayCopy: function(array) { var clone = []; for (var i = 0; i < array.length; i++) { clone[i] = array[i]; } return clone; }, /** * Get Array Color * @param array * @returns {Array} */ getArrayColor: function(array) { var color = []; for (var i = 0; i < array.length; i++) { color[i] = array[i][2]; } return color; }, /** * Add edges to puzzle * @param graph * @param state */ addPuzzleEdges : function(graph, state) { var puzzle = state.getPuzzle(); for (var row = 0; row < puzzle.getNumRows(); row++) { for (var col = 0; col < puzzle.getNumCols(); col++) { if (!puzzle.containsWall(row, col)) { // perpendicular if (!puzzle.containsWall(row - 1, col) && !graph.hasEdge(row.toString() + col.toString(), (row - 1).toString() + col.toString())) graph.addEdge(row.toString() + col.toString(), (row - 1).toString() + col.toString()); if (!puzzle.containsWall(row + 1, col) && !graph.hasEdge(row.toString() + col.toString(), (row + 1).toString() + col.toString())) graph.addEdge(row.toString() + col.toString(), (row + 1).toString() + col.toString()); if (!puzzle.containsWall(row, col - 1) && !graph.hasEdge(row.toString() + col.toString(), row.toString() + (col - 1).toString())) graph.addEdge(row.toString() + col.toString(), row.toString() + (col - 1).toString()); if (!puzzle.containsWall(row, col + 1) && !graph.hasEdge(row.toString() + col.toString(), row.toString() + (col + 1).toString())) graph.addEdge(row.toString() + col.toString(), row.toString() + (col + 1).toString()); // diagonals if (Util.Globals.levelType === 1) { if (!puzzle.containsWall(row + 1, col + 1) && !graph.hasEdge(row.toString() + col.toString(), (row + 1).toString() + (col + 1).toString())) graph.addEdge(row.toString() + col.toString(), (row + 1).toString() + (col + 1).toString()); if (!puzzle.containsWall(row + 1, col - 1) && !graph.hasEdge(row.toString() + col.toString(), (row + 1).toString() + (col - 1).toString())) graph.addEdge(row.toString() + col.toString(), (row + 1).toString() + (col - 1).toString()); if (!puzzle.containsWall(row - 1, col + 1) && !graph.hasEdge(row.toString() + col.toString(), (row - 1).toString() + (col + 1).toString())) graph.addEdge(row.toString() + col.toString(), (row - 1).toString() + (col + 1).toString()); if (!puzzle.containsWall(row - 1, col - 1) && !graph.hasEdge(row.toString() + col.toString(), (row - 1).toString() + (col - 1).toString())) graph.addEdge(row.toString() + col.toString(), (row - 1).toString() + (col - 1).toString()); } } } } }, /** * Get puzzle size * @param size * @returns {[number,number]} */ getPuzzleSize: function(size) { var puzzleSize = [6, 6]; switch (size) { case 'S22': puzzleSize = [2, 2]; break; case 'S32': puzzleSize = [[3, 2], [2, 3]][Math.round(Math.random())]; break; case 'S33': puzzleSize = [3, 3]; break; case 'S43': puzzleSize = [[4, 3], [3, 4]][Math.round(Math.random())]; break; case 'S44': puzzleSize = [4, 4]; break; case 'S54': puzzleSize = [[5, 4], [4, 5]][Math.round(Math.random())]; break; case 'S55': puzzleSize = [5, 5]; break; case 'S65': puzzleSize = [[6, 5], [5, 6]][Math.round(Math.random())]; break; case 'S66': puzzleSize = [6, 6]; break; case 'S76': puzzleSize = [7, 6]; break; default: break; } return puzzleSize; } }; /** * Returns the possible actions from an specific state * * @param state */ function getActions(state) { // Get the state puzzle var puzzle = state.getPuzzle(); // Get both number of rows and number of columns var numRows = puzzle.getNumRows(), numCols = puzzle.getNumCols(); // A dictionary is gonna hold the actions depending on the key // In this way the same key will be able to hold different actions var actions = {}; // Traverse the puzzle of the state for (var row = 0; row < numRows; row++) { for (var col = 0; col < numCols; col++) { // The square is just the string extracted from the coordinates var square = puzzle.getPuzzle()[row][col]; // An array is gonna hold the possible actions for each key actions[[row, col]] = []; // Straight actions if (puzzle.isTower(square) || puzzle.isQueen(square)) { // Adding distance 1 if (puzzle.getScope(square) === '1' || puzzle.getScope(square) === '2' || puzzle.getScope(square) === '3' || puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 0 && !puzzle.containsWall(row - 1, col)) { actions[[row, col]].push(new Action("up", 1)); } if (row < numRows - 1 && !puzzle.containsWall(row + 1, col)) { actions[[row, col]].push(new Action("down", 1)); } if (col > 0 && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("left", 1)); } if (col < numCols - 1 && !puzzle.containsWall(row, col + 1)) { actions[[row, col]].push(new Action("right", 1)); } } // Adding distance 2 if (puzzle.getScope(square) === '2' || puzzle.getScope(square) === '3' || puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 1 && !puzzle.containsWall(row - 2, col) && !puzzle.containsWall(row - 1, col)) { actions[[row, col]].push(new Action("up", 2)); } if (row < numRows - 2 && !puzzle.containsWall(row + 2, col) && !puzzle.containsWall(row + 1, col)) { actions[[row, col]].push(new Action("down", 2)); } if (col > 1 && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("left", 2)); } if (col < numCols - 2 && !puzzle.containsWall(row, col + 2) && !puzzle.containsWall(row, col + 1)) { actions[[row, col]].push(new Action("right", 2)); } } // Adding distance 3 if (puzzle.getScope(square) === '3' || puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 2 && !puzzle.containsWall(row - 3, col) && !puzzle.containsWall(row - 2, col) && !puzzle.containsWall(row - 1, col)) { actions[[row, col]].push(new Action("up", 3)); } if (row < numRows - 3 && !puzzle.containsWall(row + 3, col) && !puzzle.containsWall(row + 2, col) && !puzzle.containsWall(row + 1, col)) { actions[[row, col]].push(new Action("down", 3)); } if (col > 2 && !puzzle.containsWall(row, col - 3) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("left", 3)); } if (col < numCols - 3 && !puzzle.containsWall(row, col + 3) && !puzzle.containsWall(row, col + 2) && !puzzle.containsWall(row, col + 1)) { actions[[row, col]].push(new Action("right", 3)); } } // Adding distance 4 if (puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 3 && !puzzle.containsWall(row - 4, col) && !puzzle.containsWall(row - 3, col) && !puzzle.containsWall(row - 2, col) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("up", 4)); } if (row < numRows - 4 && !puzzle.containsWall(row + 4, col) && !puzzle.containsWall(row + 3, col) && !puzzle.containsWall(row + 2, col) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("down", 4)); } if (col > 3 && !puzzle.containsWall(row, col - 4) && !puzzle.containsWall(row, col - 3) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("left", 4)); } if (col < numCols - 4 && !puzzle.containsWall(row, col + 4) && !puzzle.containsWall(row, col + 3) && !puzzle.containsWall(row, col + 2) && !puzzle.containsWall(row, col + 1)) { actions[[row, col]].push(new Action("right", 4)); } } // Adding distance 5 if (puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 4 && !puzzle.containsWall(row - 5, col) && !puzzle.containsWall(row - 4, col) && !puzzle.containsWall(row - 3, col) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("up", 5)); } if (row < numRows - 5 && !puzzle.containsWall(row + 5, col) && !puzzle.containsWall(row + 4, col) && !puzzle.containsWall(row + 3, col) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("down", 5)); } if (col > 4 && !puzzle.containsWall(row, col - 5) && !puzzle.containsWall(row, col - 4) && !puzzle.containsWall(row, col - 3) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("left", 5)); } if (col < numCols - 5 && !puzzle.containsWall(row, col + 5) && !puzzle.containsWall(row, col + 4) && !puzzle.containsWall(row, col + 3) && !puzzle.containsWall(row, col + 2) && !puzzle.containsWall(row, col + 1)) { actions[[row, col]].push(new Action("right", 5)); } } // Adding distance 5 if (puzzle.getScope(square) === '6') { if (row > 5 && !puzzle.containsWall(row - 6, col) && !puzzle.containsWall(row - 5, col) && !puzzle.containsWall(row - 4, col) && !puzzle.containsWall(row - 3, col) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("up", 6)); } if (row < numRows - 6 && !puzzle.containsWall(row + 6, col) && !puzzle.containsWall(row + 5, col) && !puzzle.containsWall(row + 4, col) && !puzzle.containsWall(row + 3, col) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1)) { actions[[row, col]].push(new Action("down", 6)); } if (col > 5 && !puzzle.containsWall(row, col - 6) && !puzzle.containsWall(row, col - 5 && !puzzle.containsWall(row, col - 4) && !puzzle.containsWall(row, col - 3) && !puzzle.containsWall(row, col - 2) && !puzzle.containsWall(row, col - 1))) { actions[[row, col]].push(new Action("left", 6)); } if (col < numCols - 6 && !puzzle.containsWall(row, col + 6) && !puzzle.containsWall(row, col + 5) && !puzzle.containsWall(row, col + 4) && !puzzle.containsWall(row, col + 3) && !puzzle.containsWall(row, col + 2) && !puzzle.containsWall(row, col + 1)) { actions[[row, col]].push(new Action("right", 6)); } } } // Diagonal actions if (puzzle.isBishop(square) || puzzle.isQueen(square)) { // Adding distance 1 if (puzzle.getScope(square) === '1' || puzzle.getScope(square) === '2' || puzzle.getScope(square) === '3' || puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 0 && col > 0 && !puzzle.containsWall(row - 1, col - 1)) { actions[[row, col]].push(new Action("up-left", 1)); } if (row < numRows - 1 && col > 0 && !puzzle.containsWall(row + 1, col - 1)) { actions[[row, col]].push(new Action("down-left", 1)); } if (col < numCols - 1 && row > 0 && !puzzle.containsWall(row - 1, col + 1)) { actions[[row, col]].push(new Action("up-right", 1)); } if (col < numCols - 1 && row < numRows - 1 && !puzzle.containsWall(row + 1, col + 1)) { actions[[row, col]].push(new Action("down-right", 1)); } } // Adding distance 2 if (puzzle.getScope(square) === '2' || puzzle.getScope(square) === '3' || puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 1 && col > 1 && !puzzle.containsWall(row - 2, col - 2) && !puzzle.containsWall(row - 1, col - 1)) { actions[[row, col]].push(new Action("up-left", 2)); } if (row < numRows - 2 && col > 1 && !puzzle.containsWall(row + 2, col - 2) && !puzzle.containsWall(row + 1, col - 1)) { actions[[row, col]].push(new Action("down-left", 2)); } if (col < numCols - 2 && row > 1 && !puzzle.containsWall(row - 2, col + 2) && !puzzle.containsWall(row - 1, col + 1)) { actions[[row, col]].push(new Action("up-right", 2)); } if (col < numCols - 2 && row < numRows - 2 && !puzzle.containsWall(row + 2, col + 2) && !puzzle.containsWall(row + 1, col + 1)) { actions[[row, col]].push(new Action("down-right", 2)); } } // Adding distance 3 if (puzzle.getScope(square) === '3' || puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 2 && col > 2 && !puzzle.containsWall(row - 3, col - 3) && !puzzle.containsWall(row - 2, col - 2) && !puzzle.containsWall(row - 1, col - 1)) { actions[[row, col]].push(new Action("up-left", 3)); } if (row < numRows - 3 && col > 2 && !puzzle.containsWall(row + 3, col - 3) && !puzzle.containsWall(row + 2, col - 2) && !puzzle.containsWall(row + 1, col - 1)) { actions[[row, col]].push(new Action("down-left", 3)); } if (col < numCols - 3 && row > 2 && !puzzle.containsWall(row - 3, col + 3) && !puzzle.containsWall(row - 2, col + 2) && !puzzle.containsWall(row - 1, col + 1)) { actions[[row, col]].push(new Action("up-right", 3)); } if (col < numCols - 3 && row < numRows - 3 && !puzzle.containsWall(row + 3, col + 3) && !puzzle.containsWall(row + 2, col + 2) && !puzzle.containsWall(row + 1, col + 1)) { actions[[row, col]].push(new Action("down-right", 3)); } } // Adding distance 4 if (puzzle.getScope(square) === '4' || puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 3 && col > 3 && !puzzle.containsWall(row - 4, col - 4) && !puzzle.containsWall(row - 3, col - 3) && !puzzle.containsWall(row - 2, col - 2) && !puzzle.containsWall(row - 1, col - 1)) { actions[[row, col]].push(new Action("up-left", 4)); } if (row < numRows - 4 && col > 3 && !puzzle.containsWall(row + 4, col - 4) && !puzzle.containsWall(row + 3, col - 3) && !puzzle.containsWall(row + 2, col - 2) && !puzzle.containsWall(row + 1, col - 1)) { actions[[row, col]].push(new Action("down-left", 4)); } if (col < numCols - 4 && row > 3 && !puzzle.containsWall(row - 4, col + 4) && !puzzle.containsWall(row - 3, col + 3) && !puzzle.containsWall(row - 2, col + 2) && !puzzle.containsWall(row - 1, col + 1)) { actions[[row, col]].push(new Action("up-right", 4)); } if (col < numCols - 4 && row < numRows - 4 && !puzzle.containsWall(row + 4, col + 4) && !puzzle.containsWall(row + 3, col + 3) && !puzzle.containsWall(row + 2, col + 2) && !puzzle.containsWall(row + 1, col + 1)) { actions[[row, col]].push(new Action("down-right", 4)); } } // Adding distance 5 if (puzzle.getScope(square) === '5' || puzzle.getScope(square) === '6') { if (row > 4 && col > 4 && !puzzle.containsWall(row - 5, col - 5) && !puzzle.containsWall(row - 4, col - 4) && !puzzle.containsWall(row - 3, col - 3) && !puzzle.containsWall(row - 2, col - 2) && !puzzle.containsWall(row - 1, col - 1)) { actions[[row, col]].push(new Action("up-left", 5)); } if (row < numRows - 5 && col > 4 && !puzzle.containsWall(row + 5, col - 5) && !puzzle.containsWall(row + 4, col - 4) && !puzzle.containsWall(row + 3, col - 3) && !puzzle.containsWall(row + 2, col - 2) && !puzzle.containsWall(row + 1, col - 1)) { actions[[row, col]].push(new Action("down-left", 5)); } if (col < numCols - 5 && row > 4 && !puzzle.containsWall(row - 5, col + 5) && !puzzle.containsWall(row - 4, col + 4) && !puzzle.containsWall(row - 3, col + 3) && !puzzle.containsWall(row - 2, col + 2) && !puzzle.containsWall(row - 1, col + 1)) { actions[[row, col]].push(new Action("up-right", 5)); } if (col < numCols - 5 && row < numRows - 5 && !puzzle.containsWall(row + 5, col + 5) && !puzzle.containsWall(row + 4, col + 4) && !puzzle.containsWall(row + 3, col + 3) && !puzzle.containsWall(row + 2, col + 2) && !puzzle.containsWall(row + 1, col + 1)) { actions[[row, col]].push(new Action("down-right", 5)); } } // Adding distance 5 if (puzzle.getScope(square) === '6') { if (row > 5 && col > 5 && !puzzle.containsWall(row - 6, col - 6) && !puzzle.containsWall(row - 5, col - 5) && !puzzle.containsWall(row - 4, col - 4) && !puzzle.containsWall(row - 3, col - 3) && !puzzle.containsWall(row - 2, col - 2) && !puzzle.containsWall(row - 1, col - 1)) { actions[[row, col]].push(new Action("up-left", 6)); } if (row < numRows - 6 && col > 5 && !puzzle.containsWall(row + 6, col - 6) && !puzzle.containsWall(row + 5, col - 5) && !puzzle.containsWall(row + 4, col - 4) && !puzzle.containsWall(row + 3, col - 3) && !puzzle.containsWall(row + 2, col - 2) && !puzzle.containsWall(row + 1, col - 1)) { actions[[row, col]].push(new Action("down-left", 6)); } if (col < numCols - 6 && row > 5 && !puzzle.containsWall(row - 6, col + 6) && !puzzle.containsWall(row - 5, col + 5) && !puzzle.containsWall(row - 4, col + 4) && !puzzle.containsWall(row - 3, col + 3) && !puzzle.containsWall(row - 2, col + 2) && !puzzle.containsWall(row - 1, col + 1)) { actions[[row, col]].push(new Action("up-right", 6)); } if (col < numCols - 6 && row < numRows - 6 && !puzzle.containsWall(row + 6, col + 6) && !puzzle.containsWall(row + 5, col + 5) && !puzzle.containsWall(row + 4, col + 4) && !puzzle.containsWall(row + 3, col + 3) && !puzzle.containsWall(row + 2, col + 2) && !puzzle.containsWall(row + 1, col + 1)) { actions[[row, col]].push(new Action("down-right", 6)); } } } } } return actions; } /** * Obtain the state successors * * @param state * @param actions * @returns {Array} */ function getSuccessors(state, actions) { // Array which will hold the successors of the state var successors = []; // Get the puzzle of the state var puzzle = state.getPuzzle().getPuzzle(); // Traversing the actions for (var key in actions) { if (actions.hasOwnProperty(key)) { // Get the matrix both row and column key = key.split(','); var row = parseInt(key[0]), col = parseInt(key[1]); // Add the successors depending on the actions for (var i = 0; i < actions[key].length; i++) { // Get a puzzle copy var newPuzzle = Util.createPuzzleCopy(puzzle); // Get the action var action = actions[key][i]; // Add straight successor if (action.direction === 'down') { newPuzzle[row][col] = puzzle[row + action.value][col]; newPuzzle[row + action.value][col] = puzzle[row][col]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } /// Add straight successor if (action.direction === 'up') { newPuzzle[row][col] = puzzle[row - action.value][col]; newPuzzle[row - action.value][col] = puzzle[row][col]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } // Add straight successor if (action.direction === 'left') { newPuzzle[row][col - action.value] = puzzle[row][col]; newPuzzle[row][col] = puzzle[row][col - action.value]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } // Add straight successor if (action.direction === 'right') { newPuzzle[row][col] = puzzle[row][col + action.value]; newPuzzle[row][col + action.value] = puzzle[row][col]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } // Add diagonal successor if (action.direction === 'down-left') { newPuzzle[row][col] = puzzle[row + action.value][col - action.value]; newPuzzle[row + action.value][col - action.value] = puzzle[row][col]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } // Add diagonal successor if (action.direction === 'down-right') { newPuzzle[row][col] = puzzle[row + action.value][col + action.value]; newPuzzle[row + action.value][col + action.value] = puzzle[row][col]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } // Add diagonal successor if (action.direction === 'up-left') { newPuzzle[row][col] = puzzle[row - action.value][col - action.value]; newPuzzle[row - action.value][col - action.value] = puzzle[row][col]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } // Add diagonal successor if (action.direction === 'up-right') { newPuzzle[row][col] = puzzle[row - action.value][col + action.value]; newPuzzle[row - action.value][col + action.value] = puzzle[row][col]; if (!state.equals(new State(newPuzzle))) { Util.addSuccessor(newPuzzle, successors, action, row, col); } } } } } Util.arrayShuffle(successors); return successors; } /** * Class level * @param start * @param goal * @param kind * @param actions * @param size * @param difficulty * @constructor */ function Level(start, goal, kind, actions, size) { // Level variables this.start = start; this.goal = goal; this.kind = kind; this.actions = actions; this.size = size; this.numRows = start.length; this.numCols = start[0].length; this.squares = Util.createPuzzleCopy(start); } /** * Class Level */ Level.prototype = { /** * Get kind * @returns {*} */ getKind: function() { return this.kind; }, /** * Get goal * @returns {*} */ getGoal: function() { return this.goal; }, /** * get start * @returns {*} */ getStart: function() { return this.start; }, /** * get actions * @returns {*} */ getActions: function() { return this.actions; }, /** * Get num actions */ getNumActions: function() { return this.actions.length; }, /** * Get size */ getSize: function() { return size(); }, /** * Get square * @param row * @param col * @returns {*} */ getSquare: function(row, col) { return this.squares[row][col]; }, /** * Get squares * @returns {*} */ getSquares: function() { return this.squares; }, /** * Swap square * @param startRow * @param startCol * @param goalRow * @param goalCol */ swapSquare: function(startRow, startCol, goalRow, goalCol) { // A manual bullshit change squares is needed or else this motherfucker does not work var newSquares = []; for (var row = 0; row < this.numRows; row++) { var newRows = []; for (var col = 0; col < this.numCols; col++) { if (row === startRow && col === startCol) { newRows.push(this.squares[goalRow][goalCol]); } else if (row === goalRow && col === goalCol) { newRows.push(this.squares[startRow][startCol]); } else { newRows.push(this.squares[row][col]); } } newSquares.push(newRows); } this.squares = newSquares; // This is the normal way to do things, but does not work here /*var tempSquare = this.squares[startRow][startCol]; this.squares[startRow][startCol] = this.squares[goalRow][goalCol]; this.squares[goalRow][goalCol] = tempSquare;*/ }, /** * Check whether or not is goal * @returns {boolean} */ isGoal: function() { var isGoal = true; // If level type is 1, compare the current state with goal state's color if (this.kind === 1) { for (var row = 0; row < this.numRows; row++) { for (var col = 0; col < this.numCols; col++) { isGoal &= this.squares[row][col].charAt(2) === this.goal[row][col]; } } } // Otherwise it uses the function in SearchAlgorithms class else { var puzzle = new Puzzle(this.squares); isGoal = SearchAlgorithms.isGoalState(puzzle); } return isGoal; } };