/* Copyright (c) 2010, Carl Burch. License information is located in the
* com.cburch.logisim.Main source code and at www.cburch.com/logisim/. */
package com.cburch.logisim.tools.move;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
class SearchNode implements Comparable<SearchNode> {
private static final int CROSSING_PENALTY = 20;
private static final int TURN_PENALTY = 50;
private final Location loc;
private final Direction dir;
private ConnectionData conn;
private final Location dest;
private int dist;
private int heur;
private boolean extendsWire;
private SearchNode prev;
public SearchNode(ConnectionData conn, Location src,
Direction srcDir, Location dst) {
this(src, srcDir, conn, dst, 0, srcDir != null, null);
}
private SearchNode(Location loc, Direction dir,
ConnectionData conn, Location dest, int dist, boolean extendsWire,
SearchNode prev) {
this.loc = loc;
this.dir = dir;
this.conn = conn;
this.dest = dest;
this.dist = dist;
this.heur = dist + this.getHeuristic();
this.extendsWire = extendsWire;
this.prev = prev;
}
private int getHeuristic() {
Location cur = loc;
Location dst = dest;
Direction curDir = dir;
int dx = dst.getX() - cur.getX();
int dy = dst.getY() - cur.getY();
int ret = -1;
if (extendsWire) {
ret = -1;
if (curDir == Direction.EAST) {
if (dx > 0) ret = dx / 10 * 9 + Math.abs(dy);
} else if (curDir == Direction.WEST) {
if (dx < 0) ret = -dx / 10 * 9 + Math.abs(dy);
} else if (curDir == Direction.SOUTH) {
if (dy > 0) ret = Math.abs(dx) + dy / 10 * 9;
} else if (curDir == Direction.NORTH) {
if (dy < 0) ret = Math.abs(dx) - dy / 10 * 9;
}
}
if (ret < 0) {
ret = Math.abs(dx) + Math.abs(dy);
}
boolean penalizeDoubleTurn = false;
if (curDir == Direction.EAST) {
penalizeDoubleTurn = dx < 0;
} else if (curDir == Direction.WEST) {
penalizeDoubleTurn = dx > 0;
} else if (curDir == Direction.NORTH) {
penalizeDoubleTurn = dy > 0;
} else if (curDir == Direction.SOUTH) {
penalizeDoubleTurn = dy < 0;
} else if (curDir == null) {
if (dx != 0 || dy != 0) ret += TURN_PENALTY;
}
if (penalizeDoubleTurn) {
ret += 2 * TURN_PENALTY;
} else if (dx != 0 && dy != 0) {
ret += TURN_PENALTY;
}
return ret;
}
public SearchNode next(Direction moveDir, boolean crossing) {
int newDist = dist;
Direction connDir = conn.getDirection();
Location nextLoc = loc.translate(moveDir, 10);
boolean exWire = extendsWire && moveDir == connDir;
if (exWire) {
newDist += 9;
} else {
newDist += 10;
}
if (crossing) newDist += CROSSING_PENALTY;
if (moveDir != dir) newDist += TURN_PENALTY;
if (nextLoc.getX() < 0 || nextLoc.getY() < 0) {
return null;
} else {
return new SearchNode(nextLoc, moveDir, conn, dest,
newDist, exWire, this);
}
}
public boolean isStart() {
return prev == null;
}
public boolean isDestination() {
return dest.equals(loc);
}
public SearchNode getPrevious() {
return prev;
}
public int getDistance() {
return dist;
}
public Location getLocation() {
return loc;
}
public Direction getDirection() {
return dir;
}
public int getHeuristicValue() {
return heur;
}
public Location getDestination() {
return dest;
}
public boolean isExtendingWire() {
return extendsWire;
}
public ConnectionData getConnection() {
return conn;
}
@Override
public boolean equals(Object other) {
if (other instanceof SearchNode) {
SearchNode o = (SearchNode) other;
return this.loc.equals(o.loc)
&& (this.dir == null ? o.dir == null : this.dir.equals(o.dir))
&& this.dest.equals(o.dest);
} else {
return false;
}
}
@Override
public int hashCode() {
int dirHash = dir == null ? 0 : dir.hashCode();
return ((loc.hashCode() * 31) + dirHash) * 31 + dest.hashCode();
}
public int compareTo(SearchNode o) {
int ret = this.heur - o.heur;
if (ret == 0) {
return this.hashCode() - o.hashCode();
} else {
return ret;
}
}
@Override
public String toString() {
return loc + "/" + (dir == null ? "null" : dir.toString())
+ (extendsWire ? "+" : "-")
+ "/" + dest + ":" + dist + "+" + (heur-dist);
}
}