/** * */ package iamrescue.agent.police.newstrategy; import iamrescue.agent.ISimulationTimer; import iamrescue.belief.IAMWorldModel; import iamrescue.util.PositionXY; import iamrescue.util.comparators.EntityIDComparator; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; import javolution.util.FastSet; import rescuecore2.misc.Pair; import rescuecore2.standard.entities.Building; import rescuecore2.standard.entities.Civilian; import rescuecore2.standard.entities.Human; import rescuecore2.standard.entities.PoliceForce; import rescuecore2.standard.entities.StandardEntity; import rescuecore2.standard.entities.StandardEntityURN; import rescuecore2.worldmodel.EntityID; /** * @author Sebastian * */ public class SimpleCoordinationTaskAllocator { private IAMWorldModel worldModel; private List<PoliceForce> policeAgents; private double agentUtility; private double civilianUtility; private double unsearchedUtility; private double burningUtility; private ISimulationTimer timer; // Assume clearing takes 2x the time of moving private double clearingProportion = 2; public SimpleCoordinationTaskAllocator(IAMWorldModel worldModel, ISimulationTimer timer) { this.worldModel = worldModel; Collection<StandardEntity> policeCollection = worldModel .getEntitiesOfType(StandardEntityURN.POLICE_FORCE); policeAgents = new ArrayList<PoliceForce>(policeCollection.size()); for (StandardEntity police : policeCollection) { policeAgents.add((PoliceForce) police); } Collections.sort(policeAgents, EntityIDComparator.DEFAULT_INSTANCE); this.timer = timer; setDefaultUtilities(); } /** * @param agentUtility * the agentUtility to set */ public void setAgentUtility(double agentUtility) { this.agentUtility = agentUtility; } /** * @param civilianUtility * the civilianUtility to set */ public void setCivilianUtility(double civilianUtility) { this.civilianUtility = civilianUtility; } /** * @param unsearchedUtility * the unsearchedUtility to set */ public void setUnsearchedUtility(double unsearchedUtility) { this.unsearchedUtility = unsearchedUtility; } /** * @param clearingProportion * the clearingProportion to set */ public void setClearingProportion(double clearingProportion) { this.clearingProportion = clearingProportion; } /** * @param burningUtility * the burningUtility to set */ public void setBurningUtility(double burningUtility) { this.burningUtility = burningUtility; } /** * */ private void setDefaultUtilities() { agentUtility = 10; civilianUtility = 5; burningUtility = 1; unsearchedUtility = .1; } public List<EntityID> getNextTargets(GoalContainer goals, PoliceForce police) { // Greedily assign tasks // Map<EntityID, Integer> clearingCosts = new FastMap<EntityID, // Integer>(); // Map<EntityID, EntityID> alreadyClearing = new FastMap<EntityID, // EntityID>(); // Police info List<PoliceInfo> policeInfo = new ArrayList<PoliceInfo>(policeAgents .size()); // Target info List<TargetInfo> targetInfo = new ArrayList<TargetInfo>(); for (int i = 0; i < policeAgents.size(); i++) { PoliceForce policeForce = policeAgents.get(i); if (policeForce.isPositionDefined()) { Pair<Integer, Integer> location = policeForce .getLocation(worldModel); if (location != null) { policeInfo.add(new PoliceInfo(policeForce.getID(), new PositionXY(location))); } } } // Now add target info List<Human> blockedAgents = goals.getBlockedAgents(); for (Human human : blockedAgents) { targetInfo.add(new TargetInfo(human.getID(), new PositionXY(human .getLocation(worldModel)), agentUtility)); } List<Civilian> blockedCivilians = goals.getBlockedCivilians(); for (Civilian civilian : blockedCivilians) { targetInfo.add(new TargetInfo(civilian.getID(), new PositionXY( civilian.getLocation(worldModel)), civilianUtility)); } List<Building> blockedUnsearchedBuildings = goals .getBlockedUnsearchedBuildings(); for (Building building : blockedUnsearchedBuildings) { targetInfo.add(new TargetInfo(building.getID(), new PositionXY( building.getLocation(worldModel)), unsearchedUtility)); } List<Building> blockedBurningBuildings = goals .getBlockedBurningBuildings(); for (Building building : blockedBurningBuildings) { targetInfo.add(new TargetInfo(building.getID(), new PositionXY( building.getLocation(worldModel)), burningUtility)); } List<ClearingPlan> plans = new ArrayList<ClearingPlan>(); Set<PoliceInfo> unallocatedPolice = new FastSet<PoliceInfo>(); unallocatedPolice.addAll(policeInfo); Set<TargetInfo> unallocatedTasks = new FastSet<TargetInfo>(); unallocatedTasks.addAll(targetInfo); boolean changed = true; double utility = 0; while (changed) { changed = false; for (int i = 0; i < policeInfo.size(); i++) { PoliceInfo p = policeInfo.get(i); // Is this allocated? if (unallocatedPolice.contains(p)) { // Unallocated // What happens if we add this to another plan? double bestUtilityGain = 0; ClearingPlan bestExistingPlan = null; for (int j = 0; j < plans.size(); j++) { double utilityRateBefore = plans.get(j).utilityRate; double utilityRateAfter = plans.get(j) .checkUtilityRateWithNewAgent(p); double improvement = utilityRateAfter - utilityRateBefore; if (improvement > bestUtilityGain) { bestUtilityGain = improvement; bestExistingPlan = plans.get(j); } } ClearingPlan bestNewPlan = null; // What if we create a new plan from any remaining target for (int j = 0; j < targetInfo.size(); j++) { TargetInfo info = targetInfo.get(j); if (unallocatedTasks.contains(info)) { // con } ClearingPlan plan = new ClearingPlan(p, info); double improvement = plan.utilityRate; if (improvement > bestUtilityGain) { bestUtilityGain = improvement; bestNewPlan = plan; } } if (bestUtilityGain > 0) { changed = true; if (bestNewPlan != null) { p.allocatedPlan = bestNewPlan; plans.add(bestNewPlan); utility += bestUtilityGain; unallocatedPolice.remove(p); // unallocated } } } else { // Already allocated! // Don't change for now } } } return null; } private class ClearingPlan { private List<TargetInfo> targets; private double mainDistance; private double furthestStartTravelClearingCost; private List<PoliceInfo> assignedPolice; private PoliceInfo mainAgent; private double utility; private double utilityRate; public ClearingPlan(PoliceInfo agent, TargetInfo target) { mainAgent = agent; assignedPolice = new ArrayList<PoliceInfo>(); assignedPolice.add(agent); targets = new ArrayList<TargetInfo>(); targets.add(target); // compute distance double distance = mainAgent.position.distanceTo(target.position); mainDistance = distance; furthestStartTravelClearingCost = 0; utility = target.utility; utilityRate = computeUtilityRate(); } public double checkUtilityRateWithNewAgent(PoliceInfo helping) { double costToStart = (clearingProportion + 1) * helping.position.distanceTo(mainAgent.position); double newCost = mainDistance + (clearingProportion * mainDistance) / (assignedPolice.size() + 1); if (costToStart < furthestStartTravelClearingCost) { newCost += costToStart; } return utility / newCost; } public double checkUtilityRateWithNewTargetAtEnd(TargetInfo info) { double extraDistance = info.position.distanceTo(targets.get(targets .size() - 1).position); double newDistance = mainDistance + extraDistance; double newCost = newDistance + newDistance * (clearingProportion / assignedPolice.size()); double newUtility = utility + info.utility; return newUtility / newCost; } public void addNewTargetAtEnd(TargetInfo info) { double extraDistance = info.position.distanceTo(targets.get(targets .size() - 1).position); mainDistance += extraDistance; utility += info.utility; targets.add(info); utilityRate = computeUtilityRate(); } public void addNewTargetAtStart(TargetInfo info) { double extraDistance = info.position .distanceTo(targets.get(0).position); extraDistance -= mainAgent.position .distanceTo(targets.get(0).position); extraDistance += mainAgent.position.distanceTo(info.position); mainDistance += extraDistance; utility += info.utility; targets.add(0, info); utilityRate = computeUtilityRate(); } public double checkUtilityRateWithNewTargetAtStart(TargetInfo info) { double extraDistance = info.position .distanceTo(targets.get(0).position); extraDistance -= mainAgent.position .distanceTo(targets.get(0).position); extraDistance += mainAgent.position.distanceTo(info.position); double newDistance = mainDistance + extraDistance; double newCost = newDistance + newDistance * (clearingProportion / assignedPolice.size()); double newUtility = utility + info.utility; return newUtility / newCost; } public void addNewAgent(PoliceInfo helping) { double costToStart = (clearingProportion + 1) * helping.position.distanceTo(mainAgent.position); if (costToStart < furthestStartTravelClearingCost) { furthestStartTravelClearingCost = costToStart; } assignedPolice.add(helping); utilityRate = computeUtilityRate(); } public double computeUtilityRate() { double cost = mainDistance + (clearingProportion * mainDistance) / assignedPolice.size(); cost += furthestStartTravelClearingCost; return utility / cost; } @Override public int hashCode() { return mainAgent.hashCode(); } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof ClearingPlan)) { return false; } ClearingPlan c = (ClearingPlan) obj; return mainAgent.id.equals(c.mainAgent.id); } } private class TargetInfo { private EntityID id; private PositionXY position; private double utility; public TargetInfo(EntityID id, PositionXY position, double utility) { this.id = id; this.position = position; this.utility = utility; } public int hashCode() { return id.hashCode(); } public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof TargetInfo)) { return false; } TargetInfo t = (TargetInfo) obj; return id.equals(t.id); } } private class PoliceInfo { private EntityID id; private PositionXY position; private ClearingPlan allocatedPlan; public PoliceInfo(EntityID id, PositionXY position) { super(); this.id = id; this.position = position; this.allocatedPlan = null; } public int hashCode() { return id.hashCode(); } public boolean equals(Object obj) { if (obj == null) { return false; } if (!(obj instanceof PoliceInfo)) { return false; } PoliceInfo p = (PoliceInfo) obj; return id.equals(p.id); } } }