ECS 170 Project 1

25
ECS 170 Project 1 Michael Riedlin Oleg Lokhvitsky Cyprian Bergonia 993807860 994557753 994627191 Table of Contents Table of Contents Heuristic Admissibility Division Cost Function Terminology Cost Function Heuristic Function Heuristic Path Diagram Heuristic Explanation Heuristic Admissibility Proof Exponential Cost Function Additional Terminology Cost Function Heuristic Function Heuristic Explanation Heuristic Path Diagram Heuristic Admissibility Proof Algorithms Division Cost Function A * Bidirectional A * Dual Heuristic Bidirectional A * Exponential Cost Function Metrics Division Cost Function Exponential Cost Function Code Division Cost Function Exponential Cost Function

description

A* for Mount St. Helens

Transcript of ECS 170 Project 1

Page 1: ECS 170 Project 1

ECS 170 Project 1Michael Riedlin Oleg Lokhvitsky Cyprian Bergonia

993807860 994557753 994627191

Table of ContentsTable of ContentsHeuristic Admissibility

Division Cost FunctionTerminologyCost FunctionHeuristic FunctionHeuristic Path DiagramHeuristic ExplanationHeuristic Admissibility Proof

Exponential Cost FunctionAdditional TerminologyCost FunctionHeuristic FunctionHeuristic ExplanationHeuristic Path DiagramHeuristic Admissibility Proof

AlgorithmsDivision Cost Function

A*Bidirectional A*Dual Heuristic Bidirectional A*

Exponential Cost FunctionMetrics

Division Cost FunctionExponential Cost Function

CodeDivision Cost FunctionExponential Cost Function

Page 2: ECS 170 Project 1

Heuristic Admissibility

Division Cost Function

TerminologyC is the height of the current nodeN is the height of the node being consideredE is the height of the end nodeS is the Chebyshev distance between the current node and the end node

S = max( | x1 - x2 |, | y1 - y2 | )S’ is the Chebyshev distance between the node being considered and the end node

S’ can be either S - 1, S, or S + 1

Cost Function

(1)

Heuristic Function

(2a)(2b)

Heuristic Path Diagram

Heuristic ExplanationThe idea is to tunnel at a height of 1 to the endpoint. Barring 0, a half is the minimum value a cost can take and spaces represents the minimum number of terms in the final cost function for the path. The reason we can ignore zeros is because we know that no two adjacent spaces can be zero. Climbing up from a zero height will cost at least one giving a minimum combined term for the two spaces equal to 1, the same cost as though we had tunneled.

Heuristic Admissibility ProofWe will prove that this heuristic is admissible by proving that it is consistent.First, we prove that equation 2a is consistent.

(3a)

(3b)

Page 3: ECS 170 Project 1

(3c)(3d)

Equation 3d will always hold making the heuristic consistent and therefore admissible. Now, we prove that equation 2b is consistent.

(3e)(3f)(3g)(3h)

(3i)

(3j)

(3k)If N and C are small (N = C = 1)

(3l)(3m)

If N and C are large (N = C = 256)

(3n)(3o)

If N is small and C is large (N = 1, C = 256)

(3p)(3q)

If N is large and C is small (N = 256, C = 1)

(3r)(3s)

Exponential Cost Function

Additional TerminologyD is the difference in height between current node and end node ( ).R1 is the remainder after dividing S by the absolute value of D ( )

R1 represents the steeper part of the path generated by our heuristic.R2 is the difference between S and R1 ( )

R2 represents the less steep part of the path generated by our heuristic.

Cost Function

(4)

Heuristic Function

Page 4: ECS 170 Project 1

(5)

(6a)

(6b)

(6c)

(6d)

(7a)(7b)

(7c)

(7d)Please note: Equations 5, 6a, 6b, 7a, 7b are derived from equations 6c and 7c (which are really the same equation) by plugging in the given conditions.

Heuristic ExplanationThis heuristic represents a scenario where we descend or climb as smoothly as possible (as in the following picture), but only climb or descend an integer height per step. Because the heights are represented in integer values from 0 to 255, the smallest step we can take (that also changes height) will have a height difference of

. The best case scenario involves taking steps with the smallest increments.

Heuristic Path Diagram

Heuristic Admissibility ProofFirst, we prove that it is admissible when .

Page 5: ECS 170 Project 1

We can prove that the optimal path to take when is a flat path through a proof by contradiction. Imagine that there is a better path from C to E than a flat path. Then, that path must change height, either up and then down, or down and then up. Going down will help save cost over going flat, but going up is going to increase cost over going flat. By going down, we save

(8a)By going up, we lose

(8b)So, both operations together increase our path cost by

(8c)This path will then cost more than going flat - that’s a contradiction. What if we went down and up by more than 1 height unit?By going down, we save

(8d)By going up, we lose

(8e)So, both operations together increase our path cost by

(8f)This path will then cost more than going flat - that’s a contradiction.

Second, we prove that it is admissible when .There are four possible alternate paths that we must disprove: 1. We climb extra on leg 1, and climb less somewhere on leg 1.

2. We climb extra on leg 1, and climb less somewhere on leg 2.

Page 6: ECS 170 Project 1

3. We climb extra on leg 2, and climb less somewhere on leg 1.

4. We climb extra on leg 2, and climb less somewhere on leg 2.

We will prove that our heuristic is admissible by showing that any alteration to our path produces a longer path, and thus our heuristic must be admissible. The key of this proof is that 2 path segments (one steeper, one less steep) are worse than a single path segment whose steepness is in the middle. For any path segment P with a slope of M and a length of L we can divide it into two path segments:

● P1 with a slope of M1 < M and a length of L1● P2 with a slope of M2 > M and a length of L2

We can infer:(9a)(9b)(9c)

If we start at height H, then the cost of P is

(10a)Likewise, if we start at height H, then the cost of P1 is

(10b)And, if we start at height H, then the cost of P2 is

(10c)So, now we will show than the cost of P is smaller than the sum of the costs of P1 and P2.

Page 7: ECS 170 Project 1

(11a)

(11b)

(11c)

(11d)We can see that

(12a)

(12b)

(12c)

(12d)Finally, we can deduce that if the cost savings for P1 is less than the cost deficit for P2, then P is better than the combined path of P1 and P2.

(13a)

(13b)

(13c)(13d)

Taking the worse case scenario(14a)(14b)

Clearly, equation 14b always holds. So, we have proved that any path P is at least as good as any two paths P1 and P2 that can be derived from it. That directly proves our cases #1 and #4 above. Cases #2 and #3 are proved throughly simply extending this idea: If breaking a single path into 2 is bad, then breaking each of those 2 paths into more pieces (or breaking the original into 3 or more pieces) is even worse.

Finally, we prove that it is admissible when .However, this case is exactly symmetric to the case above (just swap the start and end points!), so, the proof is identical to the one given above.

Algorithms

Division Cost Function

A*The first algorithm we implemented was a simple A* algorithm using a PriorityQueue for the open list, a HashSet for the closed list and a HashMap to keep track of the g-values. This algorithm works functionally identical to what was described in class. It gives us an average time savings of 75% and nodes expanded savings of 80%.

Bidirectional A*Our next step in refining the algorithm was running the A* bidirectionally.The basic idea is to just start one A* that searches for a path from start to end, and start another A* that searches for a path from end to start, and then wait until they meet.

Page 8: ECS 170 Project 1

Unfortunately, the node as which they meet (defined as a node that one algorithm tries to expand that is already in the closed (or open) list of the other algorithm) has no guarantee of being on the shortest path.So, how the Bidirectional search works is by using this (possibly) sub-optimal path as an upper bound on the f-values of nodes and expands all nodes of the reverse A* search that have an f-value that’s smaller than this upper bound. After this is done, the forward A* is run again with the limitation that it is only allowed to expand nodes that are in the closed list on the reverse A* search. The outline of the algorithm is as follows:

1. Run both forward A* and reverse A* (expanding one node at a time from either forward A* or reverse A* depending on which node has the smaller f-value). Exit condition is that the node being examined already exists in the open or closed list of the other A*.

2. Run reverse A*. Exit condition is that the node being examined has a higher f-value than the path cost of the path discovered in step 1.

3. Run forward A*, expanding only nodes that are in reverse A*’s closed list. Exit condition is the same as for regular A*.

The optimality of this algorithm has been proved numerous times in various papers and we will just outline the way to think about it. It is basically a proof by contradiction. The algorithm is basically the same as A*, but it only allowed to expand nodes with an f-value that corresponds to some actual path. So, all the nodes on the optimal path will be expanded, since if there was a node that was on the optimal path, it has to have an f-value less than the cost of the optimal path, which is less than the cost of the suboptimal path found - so this node must be expanded in step 2.

Dual Heuristic Bidirectional A*We attempted to improve on the performance of Bidirectional A* by capitalizing on the fact that the first path found (in Step 1) does not have to be optimal. So, we don’t even need to use an admissible heuristic to find it. So, we implemented a version of the Bidirectional A* search where the reverse A* algorithm uses a secondary heuristic during step 1. The efficiency of this algorithm compared to the regular Bidirectional A* greatly depends on this secondary heuristic. Unfortunately, we could not find good secondary heuristics for the two cases we care about:

1. Division Cost Function on Seed Maps2. Exponential Cost Function on Mt. St. Helen Map

We did get a very good improvement (27.2% less nodes expanded, 20.3% less time taken) when using the algorithm on the map of Mt. St. Helen with the Division Cost Function. Since we couldn’t get an improvement for our two cases of interest, we did not end up using this algorithm, and used the regular Bidirectional A* search for the Division Cost Function.

Exponential Cost FunctionWe utilized all the same algorithms as described above for the Exponential Cost function but found that both Dual Heuristic Bidirectional A* search and regular Bidirectional A* search performed worse than simple A* search on the map of Mt. St. Helen with the Exponential Cost Function. So, we tried to optimize the A* search by utilizing our pre-existing knowledge of the geometry of this particular problem. We decided that we knew that certain areas of the map wouldn’t be used because they represent geometry that looks good at the beginning, but looks bad later. For example, one wouldn’t go into the crater of Mt. St. Helen even though it’s very easy to go in there since it’s difficult to get out.

Page 9: ECS 170 Project 1

So, we came up with a technique of height pruning. Basically, we defined radially expanding zones away from the end point and gave each one of those zones a minimum and a maximum height range within which nodes should be expanded. If a node’s height in one of those zones is outside this range, it is manually pruned. In effect, our algorithm is very fast on the map of Mt. St. Helen, but will not work properly (might not find the optimal path) on any other geometry. It also depends on the end point since we defined our zones around it.

Metrics

Division Cost Function (We used Bidirectional A*)

Seed 1 Dijkstra A* Bidirectional A*

Path Cost 198.59165501141644 198.59165501141644 198.59165501141644

Uncovered 162340 35590 19256

Time Taken 1217 321 117

Seed 2 Dijkstra A* Bidirectional A*

Path Cost 198.56141550095256 198.56141550095256 198.56141550095256

Uncovered 162364 35572 19362

Time Taken 1231 281 103

Seed 3 Dijkstra A* Bidirectional A*

Path Cost 198.4864317411443 198.4864317411443 198.4864317411443

Uncovered 162247 35570 19204

Time Taken 1205 292 117

Seed 4 Dijkstra A* Bidirectional A*

Path Cost 198.70066424826052 198.70066424826052 198.70066424826052

Uncovered 162263 35588 19391

Time Taken 1255 302 120

Seed 5 Dijkstra A* Bidirectional A*

Path Cost 198.25499446810397 198.25499446810397 198.25499446810397

Uncovered 161946 35567 19550

Time Taken 1235 284 107

Page 10: ECS 170 Project 1

Mt. St. Helen Dijkstra A* Bidirectional A*

Path Cost 548.3684300960452 548.3684300960452 548.3684300960452

Uncovered 1094119 334548 253619

Time Taken 11539 3801 2496

Exponential Cost Function (We used Height Pruned A*)

Mt. St. Helen Dijkstra A* (Height Pruned) Bidirectional A*

Path Cost 515.6645805015318 515.6645805015318 515.6645805015318

Uncovered 1203049 55232 113647

Time Taken 14754 328 1408

Code

Division Cost Functionimport java.awt.Point;import java.util.List;import java.util.LinkedList;import java.util.PriorityQueue;import java.util.HashSet;import java.util.HashMap;

public class MtStHelensDiv_994557753 extends AStarBiAI_div{ public final boolean printPath = false;}

abstract class AIModuleH implements AIModule { public TerrainMap map; public Point start; public Point end; public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { return 0.0; } public final double getHeuristic(final Point pt1, final Point pt2) { return this.getHeuristic(this.map, pt1, pt2); } public final double getHeuristic(final Point p) { return this.getHeuristic(this.map, p, this.end); } public final double getHeuristic() { return this.getHeuristic(this.map, this.start, this.end); } public final void printPath(List<Point> path) { System.out.println(this.start.x + ", " + this.start.y); System.out.println("========================="); for (int i = 0; i < path.size(); i ++) {

Page 11: ECS 170 Project 1

Point p = path.get(i); System.out.println(p.x + ", " + p.y + ", " + map.getTile(p) + ", " + Node.distanceTo(p, this.end)); } System.out.println("========================="); System.out.println(end.x + ", " + end.y); }}

class Node extends Point implements Comparable { private SearchAlgorithm search; public Node parent; // public int x; // public int y; public int z; private double g; private double h; public Node(SearchAlgorithm search, int x, int y, double g, Node parent) { this.search = search; this.x = x; this.y = y; this.z = (int)this.search.ai.map.getTile(this); this.g = g; this.h = -1; this.parent = parent; } public Node(SearchAlgorithm search, Point p, double g, Node parent) { this(search, p.x, p.y, g, parent); } public double f() { return this.g() + this.h(); } public double g() { return this.g; } public double h() { if (this.h != -1) return this.h; return this.h = this.search.getHeuristic(this, this.search.end); } public static int distanceTo(Point pt1, Point pt2) { return Math.max( Math.abs(pt1.x - pt2.x), Math.abs(pt1.y - pt2.y) ); } public int distanceTo(Point p) { return Node.distanceTo(this, p); } public List<Point> tracePath() { LinkedList<Point> path = new LinkedList<Point>(); Node node = this; while (node != null) { path.addFirst(node); node = node.parent; } return path;

Page 12: ECS 170 Project 1

} public List<Point> traceReversePath() { LinkedList<Point> path = new LinkedList<Point>(); Node node = this; if (node != null) node = node.parent; while (node != null) { path.add(node); node = node.parent; } return path; } @Override public int compareTo(Object node) { double a = this.f(); double b = ((Node)node).f();

if (a < b) return -1; if (a > b) return 1; return 0; }}

abstract class SearchAlgorithm { public AIModuleH ai; public Node start, end, current; public abstract Node step(); public abstract double getCost(Point pt1, Point pt2); public abstract double getHeuristic(Point pt1, Point pt2); public boolean allowExpand(Point p) {return true;}}

class AStarSearch extends SearchAlgorithm { protected PriorityQueue<Point> open; protected HashSet<Point> closed; protected HashMap<Point, Double> gvalues; public AStarSearch(AIModuleH ai, Point start, Point end) { // Initialize this.ai = ai; this.open = new PriorityQueue<Point>(); this.closed = new HashSet<Point>(); this.gvalues = new HashMap<Point, Double>(); // Start this.start = new Node(this, start, 0, null); this.open(this.start, 0); // End this.end = new Node(this, end, 0, null); } public Node step() { // 4. Expand Current Node this.expand(this.current); // 1. Get Next Best Node this.current = this.pop(); // 2. Close Current Node if (this.current != null) this.close(this.current, this.current.g());

Page 13: ECS 170 Project 1

// 3. Return Current Node for Examination return current; } protected void close(Point p, double g) { if (p == null) return; this.closed.add(p); } protected void unclose(Point p) { if (p == null) return; this.closed.remove(p); } public boolean isclosed(Point p) { if (p == null) return false; return this.closed.contains(p); } protected void open(Point p, double g) { if (p == null) return; this.open.add(p); this.gvalues.put(p, g); } public void unopen(Point p) { if (p == null) return; this.open.remove(p); } public boolean isopen(Point p) { if (p == null) return false; return this.open.contains(p); } public double getCost(Point p) { Double c = this.gvalues.get(p); return (c == null ? Double.MAX_VALUE : c.doubleValue()); } public double getCost(Point pt1, Point pt2) { return this.ai.map.getCost(pt1, pt2); } public double getHeuristic(Point pt1, Point pt2) { return this.ai.getHeuristic(pt1, pt2); } public Node top() { if (this.open == null || this.open.size() == 0) return null; return (Node)this.open.peek(); } protected Node pop() { if (this.open == null || this.open.size() == 0) return null; return (Node)this.open.poll(); } protected void expand(Node n) { if (n == null) return; for (Point neighbor : this.ai.map.getNeighbors(n)) { // If neighbor is closed (for consistent heuristics) if (this.isclosed(neighbor)) continue;

Page 14: ECS 170 Project 1

// Get potential new path value double cost = n.g() + this.getCost(n, neighbor); // If neighbor is closed (for non-consistnet heuristics) /* if (this.isclosed(neighbor)) { if (cost >= this.getCost(neighbor)) // If new path is worse continue; else // If new path is better this.unclose(neighbor); } */ // If neighbor is open if (cost >= this.getCost(neighbor)) continue; // Custom pruning if (!this.allowExpand(neighbor)) continue; // If neighbor has been open at one point in time before if (this.getCost(neighbor) != Double.MAX_VALUE) this.unopen(neighbor); // Open neighbor this.open(new Node( this, neighbor, cost, n ), cost); } }}

class AStarAI_div extends AIModuleH{ public final boolean printPath = false; @Override public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { double distance = Node.distanceTo(pt1, pt2); double h1 = map.getTile(pt1); double h2 = map.getTile(pt2); if (distance == 0) return 0; if (distance == 1) return h2 / (h1 + 1); return 0.5 * (distance - 2) + (1 / (h1 + 1)) + (h2 / 2); //return 0.5 * Node.distanceTo(pt1, pt2); } public List<Point> createPath(final TerrainMap map) { this.map = map; this.start = this.map.getStartPoint(); this.end = this.map.getEndPoint(); SearchAlgorithm search = new AStarSearch(this, this.start, this.end); Node current; while (true) { current = search.step();

Page 15: ECS 170 Project 1

if (current.equals(this.end)) { List<Point> path = current.tracePath(); if (printPath) this.printPath(path); return path; } } }}

class AStarBiAI_div extends AIModuleH{ public final boolean printPath = false; public int phase = 1; public double g = Double.MAX_VALUE; AStarSearch forward, reverse; Node fnode, rnode; @Override public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { double distance = Node.distanceTo(pt1, pt2); double h1 = map.getTile(pt1); double h2 = map.getTile(pt2); if (distance == 0) return 0; if (distance == 1) return h2 / (h1 + 1); return 0.5 * (distance - 2) + (1 / (h1 + 1)) + (h2 / 2); //return 0.5 * Node.distanceTo(pt1, pt2); } public List<Point> createPath(final TerrainMap map) { this.map = map; this.start = this.map.getStartPoint(); this.end = this.map.getEndPoint(); final AStarBiAI_div me = this; forward = new AStarSearch(this, this.start, this.end) { public AStarBiAI_div bistarai = me; public boolean allowExpand(Point p) { return !(this.bistarai.phase == 3 && !this.bistarai.reverse.isclosed(p)); } }; fnode = forward.step(); fnode = forward.step(); reverse = new AStarSearch(this, this.end, this.start) { public double getCost(Point pt1, Point pt2) { return super.getCost(pt2, pt1); } }; rnode = reverse.step(); rnode = reverse.step(); while (true) { if (phase == 1) { // Phase 1 if (forward.top().f() < reverse.top().f()) { fnode = forward.step(); if (reverse.isclosed(fnode)) { phase = 2; g = fnode.g() + reverse.getCost(fnode); }

Page 16: ECS 170 Project 1

} else { rnode = reverse.step(); if (forward.isclosed(rnode)) { phase = 2; g = rnode.g() + forward.getCost(rnode); } } } else if (phase == 2) { // Phase 2 rnode = reverse.step(); if (rnode.f() > g) { phase = 3; } } else if (phase == 3) { //while (!reverse.isclosed(forward.top())) forward.pop(); fnode = forward.step(); if (fnode.equals(this.end)) { List<Point> path = fnode.tracePath(); if (printPath) this.printPath(path); return path; } } } }}

Exponential Cost Functionimport java.awt.Point;import java.util.List;import java.util.LinkedList;import java.util.PriorityQueue;import java.util.HashSet;import java.util.HashMap;

public class MtStHelensExp_994557753 extends AStarAI_exp{ public final boolean printPath = false;}

abstract class AIModuleH implements AIModule { public TerrainMap map; public Point start; public Point end; public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { return 0.0; } public final double getHeuristic(final Point pt1, final Point pt2) { return this.getHeuristic(this.map, pt1, pt2); } public final double getHeuristic(final Point p) { return this.getHeuristic(this.map, p, this.end); } public final double getHeuristic() { return this.getHeuristic(this.map, this.start, this.end); } public final void printPath(List<Point> path) { System.out.println(this.start.x + ", " + this.start.y);

Page 17: ECS 170 Project 1

System.out.println("========================="); for (int i = 0; i < path.size(); i ++) { Point p = path.get(i); System.out.println(p.x + ", " + p.y + ", " + map.getTile(p) + ", " + Node.distanceTo(p, this.end)); } System.out.println("========================="); System.out.println(end.x + ", " + end.y); }}

class Node extends Point implements Comparable { private SearchAlgorithm search; public Node parent; // public int x; // public int y; public int z; private double g; private double h; public Node(SearchAlgorithm search, int x, int y, double g, Node parent) { this.search = search; this.x = x; this.y = y; this.z = (int)this.search.ai.map.getTile(this); this.g = g; this.h = -1; this.parent = parent; } public Node(SearchAlgorithm search, Point p, double g, Node parent) { this(search, p.x, p.y, g, parent); } public double f() { return this.g() + this.h(); } public double g() { return this.g; } public double h() { if (this.h != -1) return this.h; return this.h = this.search.getHeuristic(this, this.search.end); } public static int distanceTo(Point pt1, Point pt2) { return Math.max( Math.abs(pt1.x - pt2.x), Math.abs(pt1.y - pt2.y) ); } public int distanceTo(Point p) { return Node.distanceTo(this, p); } public List<Point> tracePath() { LinkedList<Point> path = new LinkedList<Point>(); Node node = this; while (node != null) { path.addFirst(node);

Page 18: ECS 170 Project 1

node = node.parent; } return path; } public List<Point> traceReversePath() { LinkedList<Point> path = new LinkedList<Point>(); Node node = this; if (node != null) node = node.parent; while (node != null) { path.add(node); node = node.parent; } return path; } @Override public int compareTo(Object node) { double a = this.f(); double b = ((Node)node).f();

if (a < b) return -1; if (a > b) return 1; return 0; }}

abstract class SearchAlgorithm { public AIModuleH ai; public Node start, end, current; public abstract Node step(); public abstract double getCost(Point pt1, Point pt2); public abstract double getHeuristic(Point pt1, Point pt2); public boolean allowExpand(Point p) {return true;}}

class AStarSearch extends SearchAlgorithm { protected PriorityQueue<Point> open; protected HashSet<Point> closed; protected HashMap<Point, Double> gvalues; public AStarSearch(AIModuleH ai, Point start, Point end) { // Initialize this.ai = ai; this.open = new PriorityQueue<Point>(); this.closed = new HashSet<Point>(); this.gvalues = new HashMap<Point, Double>(); // Start this.start = new Node(this, start, 0, null); this.open(this.start, 0); // End this.end = new Node(this, end, 0, null); } public Node step() { // 4. Expand Current Node this.expand(this.current); // 1. Get Next Best Node this.current = this.pop(); // 2. Close Current Node

Page 19: ECS 170 Project 1

if (this.current != null) this.close(this.current, this.current.g()); // 3. Return Current Node for Examination return current; } protected void close(Point p, double g) { if (p == null) return; this.closed.add(p); } protected void unclose(Point p) { if (p == null) return; this.closed.remove(p); } public boolean isclosed(Point p) { if (p == null) return false; return this.closed.contains(p); } protected void open(Point p, double g) { if (p == null) return; this.open.add(p); this.gvalues.put(p, g); } public void unopen(Point p) { if (p == null) return; this.open.remove(p); } public boolean isopen(Point p) { if (p == null) return false; return this.open.contains(p); } public double getCost(Point p) { Double c = this.gvalues.get(p); return (c == null ? Double.MAX_VALUE : c.doubleValue()); } public double getCost(Point pt1, Point pt2) { return this.ai.map.getCost(pt1, pt2); } public double getHeuristic(Point pt1, Point pt2) { return this.ai.getHeuristic(pt1, pt2); } public Node top() { if (this.open == null || this.open.size() == 0) return null; return (Node)this.open.peek(); } protected Node pop() { if (this.open == null || this.open.size() == 0) return null; return (Node)this.open.poll(); } protected void expand(Node n) { if (n == null) return; for (Point neighbor : this.ai.map.getNeighbors(n)) { // If neighbor is closed (for consistent heuristics)

Page 20: ECS 170 Project 1

if (this.isclosed(neighbor)) continue; // Get potential new path value double cost = n.g() + this.getCost(n, neighbor); // If neighbor is closed (for non-consistnet heuristics) /* if (this.isclosed(neighbor)) { if (cost >= this.getCost(neighbor)) // If new path is worse continue; else // If new path is better this.unclose(neighbor); } */ // If neighbor is open if (cost >= this.getCost(neighbor)) continue; // Custom pruning if (!this.allowExpand(neighbor)) continue; // If neighbor has been open at one point in time before if (this.getCost(neighbor) != Double.MAX_VALUE) this.unopen(neighbor); // Open neighbor this.open(new Node( this, neighbor, cost, n ), cost); } }}

class AStarAI_div extends AIModuleH{ public final boolean printPath = false; @Override public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { double distance = Node.distanceTo(pt1, pt2); double h1 = map.getTile(pt1); double h2 = map.getTile(pt2); if (distance == 0) return 0; if (distance == 1) return h2 / (h1 + 1); return 0.5 * (distance - 2) + (1 / (h1 + 1)) + (h2 / 2); //return 0.5 * Node.distanceTo(pt1, pt2); } public List<Point> createPath(final TerrainMap map) { this.map = map; this.start = this.map.getStartPoint(); this.end = this.map.getEndPoint(); SearchAlgorithm search = new AStarSearch(this, this.start, this.end); Node current;

Page 21: ECS 170 Project 1

while (true) { current = search.step(); if (current.equals(this.end)) { List<Point> path = current.tracePath(); if (printPath) this.printPath(path); return path; } } }}

class AStarBiAI_div extends AIModuleH{ public final boolean printPath = false; public int phase = 1; public double g = Double.MAX_VALUE; AStarSearch forward, reverse; Node fnode, rnode; @Override public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { double distance = Node.distanceTo(pt1, pt2); double h1 = map.getTile(pt1); double h2 = map.getTile(pt2); if (distance == 0) return 0; if (distance == 1) return h2 / (h1 + 1); return 0.5 * (distance - 2) + (1 / (h1 + 1)) + (h2 / 2); //return 0.5 * Node.distanceTo(pt1, pt2); } public List<Point> createPath(final TerrainMap map) { this.map = map; this.start = this.map.getStartPoint(); this.end = this.map.getEndPoint(); final AStarBiAI_div me = this; forward = new AStarSearch(this, this.start, this.end) { public AStarBiAI_div bistarai = me; public boolean allowExpand(Point p) { return !(this.bistarai.phase == 3 && !this.bistarai.reverse.isclosed(p)); } }; fnode = forward.step(); fnode = forward.step(); reverse = new AStarSearch(this, this.end, this.start) { public double getCost(Point pt1, Point pt2) { return super.getCost(pt2, pt1); } }; rnode = reverse.step(); rnode = reverse.step(); while (true) { if (phase == 1) { // Phase 1 if (forward.top().f() < reverse.top().f()) { fnode = forward.step(); if (reverse.isclosed(fnode)) {

Page 22: ECS 170 Project 1

phase = 2; g = fnode.g() + reverse.getCost(fnode); } } else { rnode = reverse.step(); if (forward.isclosed(rnode)) { phase = 2; g = rnode.g() + forward.getCost(rnode); } } } else if (phase == 2) { // Phase 2 rnode = reverse.step(); if (rnode.f() > g) { phase = 3; } } else if (phase == 3) { //while (!reverse.isclosed(forward.top())) forward.pop(); fnode = forward.step(); if (fnode.equals(this.end)) { List<Point> path = fnode.tracePath(); if (printPath) this.printPath(path); return path; } } } }}

class AStarAI_exp extends AStarAI_div{ public final boolean printPath = false; @Override public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { double distance = Node.distanceTo(pt1, pt2); double h1 = map.getTile(pt1); double h2 = map.getTile(pt2); double dh = h2 - h1; double d1 = Math.abs(dh) % distance; double d2 = distance - d1; double dh2 = (int)(dh / distance); double dh1 = dh >= 0 ? dh2 + 1 : dh2 - 1; double cost1 = d1 * Math.exp(dh1); double cost2 = d2 * Math.exp(dh2); return cost1 + cost2; } public List<Point> createPath(final TerrainMap map) { this.map = map; this.start = this.map.getStartPoint(); this.end = this.map.getEndPoint(); SearchAlgorithm search = new AStarSearch(this, this.start, this.end) { public boolean allowExpand(Point p) { int distance = Node.distanceTo(p, this.end);

Page 23: ECS 170 Project 1

int height = (int)this.ai.map.getTile(p); if (distance < 50) { if (height > 70 || height < 48) return false; } else if (distance < 100) { if (height > 82 || height < 48) return false; } else if (distance < 150) { if (height > 87 || height < 81) return false; } else if (distance < 200) { if (height > 108 || height < 87) return false; } else if (distance < 300) { if (height > 171 || height < 108) return false; } else if (distance < 400) { if (height > 203 || height < 171) return false; } else if (distance < 1000) { if (height > 246 || height < 203) return false; } return super.allowExpand(p); } }; Node current; while (true) { current = search.step(); if (current.equals(this.end)) { List<Point> path = current.tracePath(); if (printPath) this.printPath(path); return path; } } }}

class AStarBiAI_exp extends AStarBiAI_div{ public final boolean printPath = false; public int phase = 1; public double g = Double.MAX_VALUE; AStarSearch forward, reverse; Node fnode, rnode; @Override public double getHeuristic(final TerrainMap map, final Point pt1, final Point pt2) { double distance = Node.distanceTo(pt1, pt2); double h1 = map.getTile(pt1); double h2 = map.getTile(pt2); double dh = h2 - h1; double d1 = Math.abs(dh) % distance; double d2 = distance - d1; double dh2 = (int)(dh / distance); double dh1 = dh >= 0 ? dh2 + 1 : dh2 - 1; double cost1 = d1 * Math.exp(dh1); double cost2 = d2 * Math.exp(dh2); return cost1 + cost2; }

Page 24: ECS 170 Project 1

public double getReverseHeuristic(final TerrainMap map, final Point pt2, final Point pt1) { return this.getHeuristic(map, pt1, pt2); } public List<Point> createPath(final TerrainMap map) { this.map = map; this.start = this.map.getStartPoint(); this.end = this.map.getEndPoint(); final AStarBiAI_exp me = this; forward = new AStarSearch(this, this.start, this.end) { public AStarBiAI_exp bistarai = me; public boolean allowExpand(Point p) { return !(this.bistarai.phase == 3 && !this.bistarai.reverse.isclosed(p)); } }; fnode = forward.step(); fnode = forward.step(); reverse = new AStarSearch(this, this.end, this.start) { public double getCost(Point pt1, Point pt2) { return super.getCost(pt2, pt1); } public double getHeuristic(Point pt1, Point pt2) { return this.ai.getHeuristic(pt2, pt1); } }; rnode = reverse.step(); rnode = reverse.step(); while (true) { if (phase == 1) { // Phase 1 if (forward.top().f() < reverse.top().f()) { fnode = forward.step(); if (reverse.isclosed(fnode)) { phase = 2; g = fnode.g() + reverse.getCost(fnode); } } else { rnode = reverse.step(); if (forward.isclosed(rnode)) { phase = 2; g = rnode.g() + forward.getCost(rnode); } } } else if (phase == 2) { // Phase 2 rnode = reverse.step(); if (rnode.f() > g) { phase = 3; } } else if (phase == 3) { //while (!reverse.isclosed(forward.top())) forward.pop(); fnode = forward.step(); if (fnode.equals(this.end)) { List<Point> path = fnode.tracePath(); if (printPath) this.printPath(path); return path; }

Page 25: ECS 170 Project 1

} } }}