/** * 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.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> only considering the number of tiles * visited when determining the cost. */ private static final CostDecider tileCostDecider = new CostDecider() { public int getCost(Unit unit, Tile oldTile, Tile newTile, int movesLeft) { if (unit.isNaval()) { if (!newTile.isLand()) return 1; Settlement settlement = newTile.getSettlement(); return (settlement != null && settlement.getOwner().equals(unit.getOwner())) ? 1 : ILLEGAL_MOVE; } else { return (newTile.isLand()) ? 1 : ILLEGAL_MOVE; } } public int getMovesLeft() { return 0; } public boolean isNewTurn() { return false; } }; /** * A <code>CostDecider</code> that costs unit moves normally. */ private static final CostDecider avoidIllegalCostDecider = new BaseCostDecider(); /** * A <code>CostDecider</code> that uses server-side knowledge of where * a player has explored to searches. */ private static class ServerBaseCostDecider extends BaseCostDecider { @Override public int getCost(Unit unit, Tile oldTile, Tile newTile, int movesLeft) { int cost = super.getCost(unit, oldTile, newTile, movesLeft); if (cost != ILLEGAL_MOVE && cost != Map.COST_INFINITY) { if (!newTile.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, Tile oldTile, Tile newTile, int movesLeft) { int cost = super.getCost(unit, oldTile, newTile, movesLeft); if (cost != ILLEGAL_MOVE && cost != Map.COST_INFINITY) { Settlement settlement = newTile.getSettlement(); if (settlement != null && settlement.getOwner() != unit.getOwner()) { return ILLEGAL_MOVE; } } return cost; } }; 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 BaseCostDecider { @Override public int getCost(Unit unit, Tile oldTile, Tile newTile, int movesLeft) { int cost = super.getCost(unit, oldTile, newTile, movesLeft); if (cost != ILLEGAL_MOVE && cost != Map.COST_INFINITY) { Settlement settlement = newTile.getSettlement(); if (settlement != null && settlement.getOwner() != unit.getOwner()) { return ILLEGAL_MOVE; } final Unit defender = newTile.getFirstUnit(); if (defender != null && defender.getOwner() != unit.getOwner()) { return ILLEGAL_MOVE; } else if (newTile.isLand() && newTile.getFirstUnit() != null && newTile.getFirstUnit().isNaval() && newTile.getFirstUnit().getOwner() != unit.getOwner()) { // An enemy ship in land tile without a settlement // is blocking the path: return ILLEGAL_MOVE; } else if (unit.getTradeRoute() != null && unit.getMoveType(newTile) == Unit.MoveType.EXPLORE_LOST_CITY_RUMOUR) { return ILLEGAL_MOVE; } } return cost; } }; private static final AvoidSettlementsAndBlockingUnitsCostDecider avoidSettlementsAndBlockingUnitsCostDecider = new AvoidSettlementsAndBlockingUnitsCostDecider(); /** * Selects a default <code>CostDecider</code> for the given unit * depending on the owner of the unit (ai/human) 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.getOwner().isAI() || !unit.isOffensiveUnit()) ? avoidSettlementsAndBlockingUnits() : avoidSettlements(); } /** * A <code>CostDecider</code> only considering the number of tiles * visited when determining the cost. * * @return The <code>CostDecider</code>. */ public static CostDecider numberOfTiles() { 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 tiles 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 tiles 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; } }