package Assignments.A1.view; import Assignments.A1.models.Board; import Assignments.A1.models.BoardGenerator; import Assignments.A1.models.BoardNode; import Assignments.A1.models.helper.Solver; import Assignments.A1.solving_algorithms.PriorityTraversal; import Assignments.A1.solving_algorithms.comparators.AStar; import Assignments.A1.solving_algorithms.comparators.BFS; import Assignments.A1.solving_algorithms.DFS; import Assignments.A1.solving_algorithms.comparators.UCS; import javafx.beans.property.*; import javafx.scene.control.Alert; import javafx.scene.control.TreeItem; import java.util.Date; import java.util.Stack; public class MainViewModel { /* Private Fields used to bind */ private final StringProperty currentBoardProperty, algSpeedProperty, iterationCounter; private final BooleanProperty expanded, DFS, UCS, BFS, AStar, solvingAlgErr, genBoardErr, disclaimer, openPerf, runPerf; private BoardNode current = new BoardNode(new Board(), null); private TreeItem solvedRootNode = null; /* Private Fields used for traversing/creating nodes. */ private BoardNode solvedRootBoardNode = null; private final long[][] hasRanPerformance; private String selectedAlg; /** * Initializes all the Properties and initial values for any fields. * * @precondition none * @postcondition a VM is created. */ public MainViewModel() { this.currentBoardProperty = new SimpleStringProperty(); this.algSpeedProperty = new SimpleStringProperty(); this.disclaimer = new SimpleBooleanProperty(); this.expanded = new SimpleBooleanProperty(); this.openPerf = new SimpleBooleanProperty(); this.runPerf = new SimpleBooleanProperty(); this.DFS = new SimpleBooleanProperty(); this.UCS = new SimpleBooleanProperty(); this.BFS = new SimpleBooleanProperty(); this.AStar = new SimpleBooleanProperty(); this.iterationCounter = new SimpleStringProperty(); this.solvingAlgErr = new SimpleBooleanProperty(); this.genBoardErr = new SimpleBooleanProperty(); this.solvingAlgErr.set(false); this.genBoardErr.set(false); this.hasRanPerformance = new long[4][102]; } /** * Generates the board and sets the board on view to new board. * * @precondition none * @postcondition none */ public void generateBoard() { this.current = new BoardNode(BoardGenerator.generateBoard(), null); currentBoardProperty.set(this.current.board.toString()); } /** * Solves the current board by checking for which alg is selected, then will solve that board * with the given alg that implements the Solver interface. * * @precondition menuItem != null && (board != solver || board != null) * @postcondition the board is solved. */ public void solveBoard() { if (selectedAlg == null || selectedAlg.isEmpty()) { this.solvingAlgErr.setValue(true); return; } this.solvingAlgErr.setValue(false); if (current.board.equals(new Board())) { this.genBoardErr.setValue(true); return; } this.genBoardErr.setValue(false); Solver solver = null; if (DFS.getValue()) { solver = new DFS(); } else if (UCS.getValue()) { solver = new PriorityTraversal(new UCS()); } else if (BFS.getValue()) { solver = new PriorityTraversal(new BFS()); } else if (AStar.getValue()) { solver = new PriorityTraversal(new AStar()); } Date start = new Date(); BoardNode solved = solver.traverse(this.current.board); Date end = new Date(); long runtime = end.getTime() - start.getTime(); this.algSpeedProperty.setValue(String.valueOf(runtime)); currentBoardProperty.setValue(solved.board.toString()); BoardNode root = solved; while (root.parent != null) { root = root.parent; } this.solvedRootNode = rebuildTree(root, this.expanded.getValue()); this.solvedRootBoardNode = root; this.current = solved; } /** * Sets the values of the data using the View's Property files and Bindings. * * @precondition none * @postcondition none * * @param value the menu item value. */ public void setSelectedAlg(String value) { this.selectedAlg = value; this.DFS.set(value.equals("DFS")); this.UCS.set(value.equals("UCS")); this.BFS.set(value.equals("BFS")); this.AStar.set(value.equals("AStar")); this.disclaimer.setValue(value.equals("DFS")); this.openPerf.setValue(this.hasRanPerformance[convertValueToInt(value)][0] == 1); this.runPerf.setValue(this.hasRanPerformance[convertValueToInt(value)][0] == 0); if (this.hasRanPerformance[convertValueToInt(value)][0] == 0) { this.iterationCounter.set(""); } } /** * Runs through the selected algorithm and will run it 100 times. It will collect data from these runs * and store them. It will then allow you to view the data in a new windows and the button will appear. * * @precondition none * @postcondition the performance is taken. */ public void runPerformanceCheck() { Solver solver = null; int alg = -1; if (this.selectedAlg.equals("DFS") && this.hasRanPerformance[0][0] == 0) { solver = new DFS(); alg = 0; } else if (this.selectedAlg.equals("UCS") && this.hasRanPerformance[1][0] == 0) { solver = new PriorityTraversal(new UCS()); alg = 1; } else if (this.selectedAlg.equals("BFS") && this.hasRanPerformance[2][0] == 0) { solver = new PriorityTraversal(new BFS()); alg = 2; } else if (this.selectedAlg.equals("AStar") && this.hasRanPerformance[3][0] == 0) { solver = new PriorityTraversal(new AStar()); alg = 3; } int averageNodes = 0; if (solver != null) { for (int i = 0; i < 100; i++) { Board board = BoardGenerator.generateBoard(); System.out.println(board); long start = new Date().getTime(); BoardNode node = solver.traverse(board); long end = new Date().getTime(); System.out.println(node.board + "\n"); if (node != null) { this.hasRanPerformance[alg][i + 2] = end - start; this.currentBoardProperty.setValue(node.board.toString()); averageNodes += solver.getNumOfNodes(); updateDisplay(); } else { this.hasRanPerformance[alg][i+2] = -1; } iterationCounter.setValue("Performing Analysis Iteration: " + (i+1) + "/100"); } this.hasRanPerformance[alg][0] = 1; this.hasRanPerformance[alg][1] = averageNodes; this.runPerf.setValue(false); this.openPerf.setValue(true); } for (int i = 1; i < this.hasRanPerformance[alg].length; i++) { System.out.println(this.hasRanPerformance[alg][i]); } } /** * Opens the performance files that were previously generated. Is only visible when performance is already generated. * * @precondition performance == generated * @postcondition performance is displayed in Alert. */ public void openPerf() { long totalTime = 0; int success = 0; long shortest = 1000000; long longest = -1; for (int i = 2; i < this.hasRanPerformance[convertValueToInt(this.selectedAlg)].length; i++) { long current = this.hasRanPerformance[convertValueToInt(this.selectedAlg)][i]; if (current != -1) { success++; totalTime += current; if (current < shortest) { shortest = current; } if (longest < current) { longest = current; } } } long average = totalTime / (long) success; Alert perf = new Alert(Alert.AlertType.INFORMATION); perf.setTitle(this.selectedAlg + " Performance Report"); perf.setHeaderText(success + "/" + 100 + " Succeeded."); String results = "Shortest Run: " + shortest + "ms\n" + "Longest Run: " + longest + "ms\n" + "Average Time: " + average + "ms" + "\n" + "Average Nodes: " + (this.hasRanPerformance[convertValueToInt(this.selectedAlg)][1]/success); perf.setContentText(results); perf.show(); } /** * Is used to update the display with the current board. * * @precondition none * @postcondition the dispaly is updated. */ public void updateDisplay() { TreeItem newRoot = null; newRoot = this.rebuildTree(this.solvedRootBoardNode, this.expanded.getValue()); this.solvedRootNode = newRoot; } /* Getters for bindings */ public TreeItem getSolvedRootNode() { return solvedRootNode; } public BooleanProperty DFSProperty() { return DFS; } public BooleanProperty UCSProperty() { return UCS; } public BooleanProperty BFSProperty() { return BFS; } public BooleanProperty AStarProperty() { return AStar; } public BooleanProperty solvingAlgErrProperty() { return solvingAlgErr; } public BooleanProperty genBoardErrProperty() { return genBoardErr; } public boolean getExpanded() { return expanded.get(); } public BooleanProperty expandedProperty() { return expanded; } public StringProperty algSpeedProperty() { return algSpeedProperty; } public StringProperty iterationCounterProperty() { return iterationCounter; } public boolean isDisclaimer() { return disclaimer.get(); } public BooleanProperty disclaimerProperty() { return disclaimer; } public BooleanProperty openPerfProperty() { return openPerf; } public BooleanProperty runPerfProperty() { return runPerf; } public StringProperty getCurrentBoardProperty() { return this.currentBoardProperty; } /* End of Public Methods. Private Fields Only */ private TreeItem rebuildTree(BoardNode root, boolean expanded) { if (this.selectedAlg != null) { if (this.selectedAlg.equals("DFS")) { Stack generations = new Stack<>(); BoardNode temp = this.current; while (temp != null) { generations.push(temp); temp = temp.parent; } TreeItem rootItem = new TreeItem<>(root); TreeItem currItem = rootItem; while (!generations.isEmpty()) { TreeItem child = new TreeItem<>(generations.pop()); currItem.getChildren().add(child); currItem.setExpanded(expanded); currItem = child; } return rootItem; } else { TreeItem treeItem = new TreeItem<>(root); for (BoardNode child : root.children) { TreeItem childItem = rebuildTree(child, expanded); treeItem.getChildren().add(childItem); } treeItem.setExpanded(expanded); return treeItem; } } return new TreeItem<>(); } private int convertValueToInt(String value) { if (value.equals("DFS")) return 0; if (value.equals("UCS")) return 1; if (value.equals("BFS")) return 2; return 3; } }