/** * Copyright (C) 2002-2012 The FreeCol Team * * This file is part of FreeCol. * * FreeCol 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 2 of the License, or * (at your option) any later version. * * FreeCol 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 FreeCol. If not, see <http://www.gnu.org/licenses/>. */ package net.sf.freecol.common.model; import net.sf.freecol.common.model.Map.Direction; /** * Represents a single <code>Tile</code> in a path. * * <br><br> * * You will most likely be using: {@link #next}, {@link #getDirection}, * {@link #getTile} and {@link #getTotalTurns}, when evaluating/following a path. */ public class PathNode implements Comparable<PathNode> { private Tile tile; private int cost; /** * This is <code>cost + heuristics</code>. The latter one is an estimate * for the cost from this <code>tile</code> and to the goal. */ private int f; private Direction direction; private int movesLeft; private int turns; private boolean onCarrier = false; /** * The next node in the path. */ public PathNode next = null; /** * The previous node in the path. */ public PathNode previous = null; /** * Creates a new <code>PathNode</code>. * * @param tile The <code>Tile</code> this <code>PathNode</code> * represents in the path. * @param cost The cost of moving to this <code>PathNode</code>'s * <code>Tile</code>, given in {@link Unit#getMovesLeft} * move points. * @param f This is <code>cost + heuristics</code>. The latter one is * an estimate for the cost from this <code>Tile</code> and * to the goal. * @param direction The direction to move on the map in order to * get to the next <code>Tile</code> in the path. * @param movesLeft The number of moves remaining at this point in the path. * @param turns The number of turns it takes to reach this * <code>PathNode</code>'s <code>Tile</code> from the * start of the path. */ public PathNode(Tile tile, int cost, int f, Direction direction, int movesLeft, int turns) { this.tile = tile; this.cost = cost; this.f = f; this.direction = direction; this.movesLeft = movesLeft; this.turns = turns; } /** * Returns the cost of moving to this <code>PathNode</code>'s tile. * * @return The cost of moving to this <code>PathNode</code>'s * <code>Tile</code>, given in {@link Unit#getMovesLeft} * move points. */ public int getCost() { return cost; } /** * Gets the <code>Tile</code> of this <code>PathNode</code>. * * <br><br> * * That is; the <code>Tile</code> you reach if you move in the direction * given by {@link #getDirection} from the previous tile. Explained by code: * <br><br> * <code>map.getNeighbourOrNull(getDirection(), * previous.getTile()) == getTile()</code> * * @return The <code>Tile</code> this <code>PathNode</code> * represents in the path. */ public Tile getTile() { return tile; } /** * Checks if the unit using this path is still onboard its transport. * * @return <code>true</code> if the unit is still onboard a * carrier when using this path. * @see #getTransportDropTurns */ public boolean isOnCarrier() { return onCarrier; } /** * Sets if the unit using this path is still onboard its transport. * * @param onCarrier Should be set to <code>true</code> in order to * indicate that the unit using this path is still onboard * the carrier on this path node. * @see #getTransportDropTurns */ public void setOnCarrier(boolean onCarrier) { this.onCarrier = onCarrier; } /** * Returns the number of turns it takes to reach the * {@link #getTransportDropNode transport node}. * * @return The number of turns in takes to get to the node where * the unit using this path should leave it's transport. */ public int getTransportDropTurns() { PathNode temp = this; while (temp.next != null && temp.isOnCarrier()) { temp = temp.next; } return temp.getTurns(); } /** * Returns the node where the unit using this path should * leave its transport. * * @return The node where the unit leaves it's carrier. */ public PathNode getTransportDropNode() { PathNode temp = this; while (temp.next != null && temp.isOnCarrier()) { temp = temp.next; } return temp; } /** * Returns the last node of this path. * * @return The last <code>PathNode</code>. */ public PathNode getLastNode() { PathNode temp = this; while (temp.next != null) { temp = temp.next; } return temp; } /** * Returns the estimated cost of the path at this stage. * * @return The <code>cost + heuristics</code>. The latter one is * an estimate for the cost from this <code>tile</code> * and to the goal. */ public int getF() { return f; } /** * Returns the direction to move in order to get closer towards the goal. * * @return The direction to move on the map in order to get to the * <code>Tile</code> returned by this <code>PathNode</code>'s * {@link #getTile} in the path. */ public Direction getDirection() { return direction; } /** * Returns the number of turns it will take to reach this * <code>PathNode</code>'s <code>Tile</code> in the path. * * @return The number of turns, using zero for the first * move. <code>-1</code> is returned if the number of * turns has not been calculated. */ public int getTurns() { return turns; } /** * Returns the number of turns it will take to move the entire path, * from the starting <code>PathNode</code> until the end. * * @return The number of turns, using zero for the first move. */ public int getTotalTurns() { PathNode temp = this; while (temp.next != null) { temp = temp.next; } return temp.getTurns(); } /** * Returns the number of moves remaining at this point in the path. * @return The number of moves remaining. <code>-1</code> is * returned if the number of moves left has not been calculated. */ public int getMovesLeft() { return movesLeft; } /** * Sets the number of moves remaining at this point in the path. * @param movesLeft The number of moves remaining. */ public void setMovesLeft(int movesLeft) { this.movesLeft = movesLeft; } /** * Compares this <code>PathNode</code>'s {@link #getF f} with the * <code>f</code> of the given object. * * <br><br> * * Note: this class has a natural ordering that is inconsistent with equals. * * @param o the object to be compared. * @return A negative integer, zero or a positive integer as this * object is less than, equal to, or greater than the * specified object. * @exception ClassCastException if the given object is not a * <code>PathNode</code>. */ public int compareTo(PathNode o) { return o.getF() - f; } /** * Checks if this <code>PathNode</code> is equal to another object. * * @param o The <code>Object</code> to compare with. * @return <code>true</code> if the given object is a * <code>PathNode</code> with the same {@link #getTile()} * tile as this one. */ public boolean equals(Object o) { if (!(o instanceof PathNode)) { return false; } else { return tile.getId().equals(((PathNode) o).getTile().getId()); } } /** * Returns the hashCode of this object. */ public int hashCode() { return tile.getX() * 10000 + tile.getY(); } /** * Debug helper. */ public String toString() { return "PathNode" + " tile=\"" + tile.getId() + "(" + Integer.toString(tile.getX()) + "," + Integer.toString(tile.getY()) + ")\"" + " cost=\"" + Integer.toString(cost) + "\"" + " f=\"" + Integer.toString(f) + "\"" + " direction=\"" + String.valueOf(direction) + "\"" + " movesLeft=\"" + Integer.toString(movesLeft) + "\"" + " turns=\"" + Integer.toString(turns) + "\"" + " onCarrier=\"" + Boolean.toString(onCarrier) + "\"" ; } /** * Another debug helper. * * @param A string describing the whole path. */ public String fullPathToString() { StringBuilder sb = new StringBuilder(500); PathNode p = this; while (p != null) { sb.append(p.toString()); sb.append("\n"); p = p.next; } return sb.toString(); } }