|
| 1 | +import java.util.Comparator; |
| 2 | +import java.util.Iterator; |
| 3 | +import java.util.LinkedList; |
| 4 | +import java.util.PriorityQueue; |
| 5 | +import java.util.Queue; |
| 6 | + |
| 7 | +public class AStarSolver |
| 8 | +{ |
| 9 | + private Maze maze; |
| 10 | + private String result; |
| 11 | + private Queue<Square> openNodes; |
| 12 | + private Queue<Node<Square>> dynTreeNodes; |
| 13 | + private int nodesCounter; |
| 14 | + private int pathLength; |
| 15 | + |
| 16 | + /* |
| 17 | + * Constructor |
| 18 | + * m: The maze to solve |
| 19 | + */ |
| 20 | + public AStarSolver(Maze m) |
| 21 | + { |
| 22 | + this.maze = m; |
| 23 | + this.result = ""; |
| 24 | + this.openNodes = new PriorityQueue<Square>(new Comparator<Square>() |
| 25 | + { |
| 26 | + public int compare(Square s1, Square s2) |
| 27 | + { |
| 28 | + Double cs1 = s1.getF(); |
| 29 | + Double cs2 = s2.getF(); |
| 30 | + |
| 31 | + if(cs1 > cs2) |
| 32 | + return 1; |
| 33 | + else if(cs1 == cs2) |
| 34 | + return 0; |
| 35 | + else |
| 36 | + return -1; |
| 37 | + } |
| 38 | + }); |
| 39 | + this.dynTreeNodes = new PriorityQueue<Node<Square>>(new Comparator<Node<Square>>() |
| 40 | + { |
| 41 | + public int compare(Node<Square> s1, Node<Square> s2) |
| 42 | + { |
| 43 | + Double cs1 = s1.getContent().getF(); |
| 44 | + Double cs2 = s2.getContent().getF(); |
| 45 | + |
| 46 | + if(cs1 > cs2) |
| 47 | + return 1; |
| 48 | + else if(cs1 == cs2) |
| 49 | + return 0; |
| 50 | + else |
| 51 | + return -1; |
| 52 | + } |
| 53 | + }); |
| 54 | + } |
| 55 | + |
| 56 | + /* |
| 57 | + * Solves the maze with the A* algorithm |
| 58 | + * manhattan: Set as true to use the MANHATTAN DISTANCE instead of EUCLIDEAN DISTANCE |
| 59 | + */ |
| 60 | + public void solve(boolean manhattan) |
| 61 | + { |
| 62 | + this.maze.initGrid(); //Re-init maze |
| 63 | + |
| 64 | + Boolean endfound = false; |
| 65 | + this.nodesCounter = 0; |
| 66 | + this.pathLength = 0; |
| 67 | + |
| 68 | + //Compute F value of Starting square |
| 69 | + if(manhattan) |
| 70 | + this.maze.getStart().calcManhattanH(this.maze.getEnd()); |
| 71 | + else |
| 72 | + this.maze.getStart().calcEuclidH(this.maze.getEnd()); |
| 73 | + |
| 74 | + this.maze.getStart().calcF(); |
| 75 | + |
| 76 | + //Init data structures |
| 77 | + this.openNodes.clear(); //Clear openNodes Queue |
| 78 | + this.openNodes.offer(maze.getStart()); //Adding the first node (Start node) (G is at 0, Start to Start = 0) |
| 79 | + this.maze.closedNodes.clear(); //Clear closedNodes |
| 80 | + |
| 81 | + //Init Tree nodes |
| 82 | + this.dynTreeNodes.clear(); |
| 83 | + this.dynTreeNodes.offer(new Node<Square>(this.maze.getStart())); |
| 84 | + Node<Square> revertedTree = null; |
| 85 | + |
| 86 | + //Measure run time |
| 87 | + long startTime = System.currentTimeMillis(); |
| 88 | + |
| 89 | + while(!endfound) |
| 90 | + { |
| 91 | + if(this.openNodes.isEmpty()) |
| 92 | + break; |
| 93 | + |
| 94 | + else |
| 95 | + { |
| 96 | + Square current = this.openNodes.remove(); |
| 97 | + |
| 98 | + if(current.getCol() == this.maze.getEnd().getCol() && current.getLine() == this.maze.getEnd().getLine()) |
| 99 | + endfound = true; |
| 100 | + |
| 101 | + else |
| 102 | + { |
| 103 | + revertedTree = this.dynTreeNodes.remove(); |
| 104 | + |
| 105 | + LinkedList<Square> nexts = this.getNextSquares(current, manhattan); |
| 106 | + this.maze.closedNodes.add(current); |
| 107 | + |
| 108 | + Iterator<Square> it = nexts.iterator(); |
| 109 | + while(it.hasNext()) |
| 110 | + { |
| 111 | + Square neighbor = it.next(); |
| 112 | + |
| 113 | + if(this.maze.closedNodes.contains(neighbor)) |
| 114 | + continue; //Ignore if already evaluated |
| 115 | + else |
| 116 | + { |
| 117 | + if(!this.openNodes.contains(neighbor)) |
| 118 | + { |
| 119 | + this.openNodes.offer(neighbor); |
| 120 | + this.nodesCounter++; |
| 121 | + |
| 122 | + Node<Square> temp = new Node<Square>(neighbor); |
| 123 | + temp.setFather(revertedTree); |
| 124 | + this.dynTreeNodes.offer(temp); |
| 125 | + } |
| 126 | + } |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + } |
| 131 | + long endTime = System.currentTimeMillis(); |
| 132 | + |
| 133 | + this.setResult(endfound, manhattan, endTime - startTime); |
| 134 | + } |
| 135 | + |
| 136 | + /* |
| 137 | + * Sets the result in this format : |
| 138 | + * - Path trace |
| 139 | + * - Path length |
| 140 | + * - Number of nodes created |
| 141 | + * - The maze with the path written |
| 142 | + * |
| 143 | + * PRIVATE: This method must be called only at the end of the solve method. Any other call may throw errors. |
| 144 | + */ |
| 145 | + private void setResult(boolean success, boolean manhattan, long time) |
| 146 | + { |
| 147 | + if(this.maze.unicodeIsTheNewBlack()) |
| 148 | + { |
| 149 | + if(manhattan) |
| 150 | + this.result = " ___ __ ___ __ __ __ \r\n" + |
| 151 | + " / | __/|_ / |/ /___ _____ / /_ ____ _/ /_/ /_____ _____ \r\n" + |
| 152 | + " / /| || / ______ / /|_/ / __ `/ __ \\/ __ \\/ __ `/ __/ __/ __ `/ __ \\\r\n" + |
| 153 | + " / ___ /_ __| /_____/ / / / / /_/ / / / / / / / /_/ / /_/ /_/ /_/ / / / /\r\n" + |
| 154 | + "/_/ |_||/ /_/ /_/\\__,_/_/ /_/_/ /_/\\__,_/\\__/\\__/\\__,_/_/ /_/ \n"; |
| 155 | + else |
| 156 | + this.result = " ___ ______ ___ __\r\n" + |
| 157 | + " / | __/|_ / ____/_ _______/ (_)___/ /\r\n" + |
| 158 | + " / /| || / ______ / __/ / / / / ___/ / / __ / \r\n" + |
| 159 | + " / ___ /_ __| /_____/ / /___/ /_/ / /__/ / / /_/ / \r\n" + |
| 160 | + "/_/ |_||/ /_____/\\__,_/\\___/_/_/\\__,_/ \n"; |
| 161 | + } |
| 162 | + else |
| 163 | + { |
| 164 | + this.result = "/*********************/\nA* ALGORITHM"; |
| 165 | + if(manhattan) |
| 166 | + this.result += " - MANHATTAN DISTANCE\n"; |
| 167 | + else |
| 168 | + this.result += " - EUCLIDEAN DISTANCE\n"; |
| 169 | + } |
| 170 | + |
| 171 | + if(success) |
| 172 | + { |
| 173 | + Node<Square> revertedTree = this.dynTreeNodes.remove(); |
| 174 | + this.maze.initGrid(); |
| 175 | + |
| 176 | + this.result += "Path: " + this.maze.getEnd().toString() + "(End) <- "; |
| 177 | + |
| 178 | + revertedTree = revertedTree.getFather(); |
| 179 | + this.pathLength++; |
| 180 | + |
| 181 | + while(revertedTree.hasFather()) |
| 182 | + { |
| 183 | + if(!revertedTree.getContent().equals(this.maze.getEnd())) |
| 184 | + { |
| 185 | + result += revertedTree.getContent().toString() + " <- "; |
| 186 | + this.maze.getGrid()[revertedTree.getContent().getLine()][revertedTree.getContent().getCol()].setAttribute("*"); |
| 187 | + this.pathLength++; |
| 188 | + } |
| 189 | + revertedTree = revertedTree.getFather(); |
| 190 | + } |
| 191 | + |
| 192 | + this.result += this.maze.getStart().toString() + "(Start) \n" + "Path length: " + this.pathLength + "\nNumber of nodes created: " + this.nodesCounter + "\nExecution time: " + time/1000d + " seconds\n"; |
| 193 | + this.result += this.maze.toString(); |
| 194 | + } |
| 195 | + else |
| 196 | + { |
| 197 | + this.result += "Failed : Unable to go further and/or end is unreachable."; |
| 198 | + } |
| 199 | + } |
| 200 | + |
| 201 | + /* |
| 202 | + * Get the next ("walkables") squares from the given square |
| 203 | + * c: Square from where to get the nexts squares |
| 204 | + * manhattan: If True, the distances computed will use the MANHATTAN DISTANCE instead of EUCLIDEAN DISTANCE |
| 205 | + */ |
| 206 | + public LinkedList<Square> getNextSquares(Square c, Boolean manhattan) |
| 207 | + { |
| 208 | + LinkedList<Square> res = new LinkedList<Square>(); |
| 209 | + |
| 210 | + //Get 4 next squares |
| 211 | + Square[] nextsquares = this.maze.getNexts(c); |
| 212 | + |
| 213 | + Square start = this.maze.getStart(); |
| 214 | + Square end = this.maze.getEnd(); |
| 215 | + |
| 216 | + for(Square s : nextsquares) |
| 217 | + { |
| 218 | + if(s != null && !s.isWall()) //Check if the square at next position is not null and if it's not a wall |
| 219 | + { |
| 220 | + //Calc G / H / F |
| 221 | + if(manhattan) |
| 222 | + { |
| 223 | + //Manhattan |
| 224 | + s.calcManhattanG(start); |
| 225 | + s.calcManhattanH(end); |
| 226 | + } |
| 227 | + else |
| 228 | + { |
| 229 | + //Euclid |
| 230 | + s.calcEuclidG(start); |
| 231 | + s.calcEuclidH(end); |
| 232 | + } |
| 233 | + |
| 234 | + s.calcF(); //Calc F value |
| 235 | + |
| 236 | + res.add(s); //Add the square |
| 237 | + } |
| 238 | + } |
| 239 | + return res; |
| 240 | + } |
| 241 | + |
| 242 | + /* |
| 243 | + * Returns the result from the last solving |
| 244 | + */ |
| 245 | + public String getResult() |
| 246 | + { |
| 247 | + if(this.result == "") |
| 248 | + return "No resolution computed, use the solve method first"; |
| 249 | + else |
| 250 | + return this.result; |
| 251 | + } |
| 252 | +} |
0 commit comments