/**
* 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.pathfinding;
import net.sf.freecol.common.model.Europe;
import net.sf.freecol.common.model.Location;
import net.sf.freecol.common.model.Map;
import net.sf.freecol.common.model.Tile;
import net.sf.freecol.common.model.Settlement;
import net.sf.freecol.common.model.Unit;
/**
* Cost deciders to be used while finding paths.
*/
public final class CostDeciders {
/**
* A <code>CostDecider</code> that costs unit moves normally.
*/
private static final CostDecider avoidIllegalCostDecider
= new BaseCostDecider();
/**
* A trivial <code>CostDecider</code> that only considers the
* number of locations visited when determining cost. Totally ignores
* the legality of the move.
*/
private static final CostDecider trivialCostDecider = new CostDecider() {
public int getCost(Unit unit, Location oldLocation,
Location newLocation, int movesLeft) {
return (newLocation == null) ? ILLEGAL_MOVE
: (newLocation instanceof Europe) ? 1
: (newLocation.getTile() == null) ? ILLEGAL_MOVE
: 1;
}
public int getMovesLeft() { return 0; }
public int getNewTurns() { return 0; }
};
/**
* A <code>CostDecider</code> that only considers the number of
* tiles visited when determining the cost, but differs from the
* trivialCostDecider in checking the legality of the move.
*/
private static final CostDecider tileCostDecider = new CostDecider() {
public int getCost(Unit unit, Location oldLocation,
Location newLocation, int movesLeft) {
return (newLocation == null) ? ILLEGAL_MOVE
: (newLocation instanceof Europe) ? 1
: (newLocation.getTile() == null) ? ILLEGAL_MOVE
: (unit.isTileAccessible(newLocation.getTile())) ? 1
: ILLEGAL_MOVE;
}
public int getMovesLeft() { return 0; }
public int getNewTurns() { return 0; }
};
/**
* A <code>CostDecider</code> that uses server-side knowledge of where
* a player has explored to limit searches.
*/
private static class ServerBaseCostDecider extends BaseCostDecider {
@Override
public int getCost(Unit unit, Location oldLocation,
Location newLocation, int movesLeft) {
int cost = super.getCost(unit, oldLocation, newLocation, movesLeft);
if (cost != ILLEGAL_MOVE && cost != Map.COST_INFINITY) {
if (newLocation instanceof Europe) {
; // ok
} else if (!newLocation.getTile().isExploredBy(unit.getOwner())) {
return ILLEGAL_MOVE;
}
}
return cost;
}
};
/**
* A server-side <code>CostDecider</code> that costs unit moves normally.
*/
private static final CostDecider
serverAvoidIllegalCostDecider = new ServerBaseCostDecider();
/**
* A <code>CostDecider</code> that costs unit moves normally while
* avoiding other player settlements.
*/
private static class AvoidSettlementsCostDecider extends BaseCostDecider {
@Override
public int getCost(Unit unit, Location oldLocation,
Location newLocation, int movesLeft) {
int cost = super.getCost(unit, oldLocation, newLocation, movesLeft);
if (cost != ILLEGAL_MOVE && cost != Map.COST_INFINITY) {
Settlement settlement = newLocation.getSettlement();
if (settlement != null
&& settlement.getOwner() != unit.getOwner()) {
return ILLEGAL_MOVE;
}
}
return cost;
}
};
/**
* An instance of the cost decider for avoiding settlements.
*/
private static final AvoidSettlementsCostDecider
avoidSettlementsCostDecider = new AvoidSettlementsCostDecider();
/**
* A <code>CostDecider</code> that costs unit moves normally while
* avoiding other player settlements and units, and does not explore
* if it is trading.
*/
private static class AvoidSettlementsAndBlockingUnitsCostDecider
extends AvoidSettlementsCostDecider {
@Override
public int getCost(Unit unit, Location oldLocation,
Location newLocation, int movesLeft) {
int cost = super.getCost(unit, oldLocation, newLocation, movesLeft);
Tile tile = newLocation.getTile();
if (cost != ILLEGAL_MOVE && cost != Map.COST_INFINITY
&& tile != null) {
final Unit defender = tile.getFirstUnit();
if (defender != null
&& defender.getOwner() != unit.getOwner()) {
return ILLEGAL_MOVE;
} else if (unit.getTradeRoute() != null
&& (unit.getMoveType(tile)
== Unit.MoveType.EXPLORE_LOST_CITY_RUMOUR)) {
return ILLEGAL_MOVE;
}
}
return cost;
}
};
/**
* An instance of the settlement+unit avoiding cost decider.
*/
private static final AvoidSettlementsAndBlockingUnitsCostDecider
avoidSettlementsAndBlockingUnitsCostDecider
= new AvoidSettlementsAndBlockingUnitsCostDecider();
// Public interface
/**
* Selects a default <code>CostDecider</code> for the given unit
* depending on the owner of the unit and if the unit can attack
* other units.
*
* @param unit The <code>Unit</code> to choose a CostDecider for.
* @return A suitable <code>CostDecider</code>.
*/
public static CostDecider defaultCostDeciderFor(final Unit unit) {
return (unit == null || !unit.isOffensiveUnit())
? avoidSettlementsAndBlockingUnits()
: avoidSettlements();
}
/**
* The trivial <code>CostDecider</code>.
*
* @return The <code>CostDecider</code>.
*/
public static CostDecider numberOfTiles() {
return trivialCostDecider;
}
/**
* A <code>CostDecider</code> only considering the number of tiles
* visited when determining the cost.
*
* @return The <code>CostDecider</code>.
*/
public static CostDecider numberOfLegalTiles() {
return tileCostDecider;
}
/**
* A <code>CostDecider</code> that returns the cost of moving
* across the terrain, excluding only illegal moves.
*
* @return The <code>CostDecider</code>.
*/
public static CostDecider avoidIllegal() {
return avoidIllegalCostDecider;
}
/**
* A <code>CostDecider</code> that returns the cost of moving
* across the terrain, excluding only illegal moves, and works correctly
* on the server side by refusing to consider locations unexplored by the
* player.
*
* @return The <code>CostDecider</code>.
*/
public static CostDecider serverAvoidIllegal() {
return serverAvoidIllegalCostDecider;
}
/**
* A <code>CostDecider</code> returning only the cost of moving
* across the terrain (no additional cost for blocking enemy units
* etc) but excluding settlements.
*
* @return The <code>CostDecider</code>.
*/
public static CostDecider avoidSettlements() {
return avoidSettlementsCostDecider;
}
/**
* A <code>CostDecider</code> for avoiding using locations which have
* blocking enemy units on them. Paths containing an enemy
* settlement are considered illegal, and so are paths where the
* next move has an enemy unit on it.
*
* @return The <code>CostDecider</code>.
*/
public static CostDecider avoidSettlementsAndBlockingUnits() {
return avoidSettlementsAndBlockingUnitsCostDecider;
}
}