/*******************************************************************************
* This file is part of logisim-evolution.
*
* logisim-evolution is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* logisim-evolution is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with logisim-evolution. If not, see <http://www.gnu.org/licenses/>.
*
* Original code by Carl Burch (http://www.cburch.com), 2011.
* Subsequent modifications by :
* + Haute École Spécialisée Bernoise
* http://www.bfh.ch
* + Haute École du paysage, d'ingénierie et d'architecture de Genève
* http://hepia.hesge.ch/
* + Haute École d'Ingénierie et de Gestion du Canton de Vaud
* http://www.heig-vd.ch/
* The project is currently maintained by :
* + REDS Institute - HEIG-VD
* Yverdon-les-Bains, Switzerland
* http://reds.heig-vd.ch
*******************************************************************************/
package com.cburch.logisim.tools.move;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.data.Location;
class SearchNode implements Comparable<SearchNode> {
final static Logger logger = LoggerFactory.getLogger(SearchNode.class);
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;
}
public int compareTo(SearchNode o) {
int ret = this.heur - o.heur;
if (ret == 0) {
return this.hashCode() - o.hashCode();
} else {
return ret;
}
}
@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
: (o.dir == null ? false : this.dir.equals(o.dir))) && this.dest
.equals(o.dest));
/*
* // This code causes a null pointer exception whenever this.dir is
* not // null but o.dir is null! 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;
}
}
public ConnectionData getConnection() {
return conn;
}
public Location getDestination() {
return dest;
}
public Direction getDirection() {
return dir;
}
public int getDistance() {
return dist;
}
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 int getHeuristicValue() {
return heur;
}
public Location getLocation() {
return loc;
}
public SearchNode getPrevious() {
return prev;
}
@Override
public int hashCode() {
int dirHash = dir == null ? 0 : dir.hashCode();
return ((loc.hashCode() * 31) + dirHash) * 31 + dest.hashCode();
}
public boolean isDestination() {
return dest.equals(loc);
}
public boolean isExtendingWire() {
return extendsWire;
}
public boolean isStart() {
return prev == null;
}
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);
}
}
@Override
public String toString() {
return loc + "/" + (dir == null ? "null" : dir.toString())
+ (extendsWire ? "+" : "-") + "/" + dest + ":" + dist + "+"
+ (heur - dist);
}
}