/**
*
*/
package iamrescue.agent.police;
import iamrescue.agent.ISimulationTimer;
import iamrescue.agent.police.goals.CivilianClearingGoal;
import iamrescue.agent.police.goals.ClearingGoalConfiguration;
import iamrescue.agent.police.goals.FireClearingGoal;
import iamrescue.agent.police.goals.PlatoonClearingGoal;
import iamrescue.agent.police.goals.PoliceClearingGoal;
import iamrescue.agent.police.goals.SimpleClearingGoal;
import iamrescue.belief.IAMWorldModel;
import iamrescue.routing.IRoutingModule;
import iamrescue.routing.costs.PassableRoutingCostFunction;
import iamrescue.routing.costs.SimpleDistanceRoutingCostFunction;
import iamrescue.routing.dijkstra.BidirectionalDijkstrasRoutingModule;
import iamrescue.util.OptimalAssignmentCalculator;
import iamrescue.util.comparators.IDComparator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import javolution.util.FastList;
import javolution.util.FastMap;
import javolution.util.FastSet;
import rescuecore2.standard.entities.Building;
import rescuecore2.standard.entities.Civilian;
import rescuecore2.standard.entities.PoliceForce;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.worldmodel.EntityListener;
import rescuecore2.worldmodel.Property;
import rescuecore2.worldmodel.WorldModel;
import rescuecore2.worldmodel.WorldModelListener;
/**
* @author Sebastian
*/
public class PoliceTaskModule implements EntityListener,
WorldModelListener<StandardEntity> {
private static final double VERY_LARGE_COST = 10000000000.0;
private static final double VERY_SMALL_COST = 0.00000000000001;
private SimpleClearingGoal myCurrentGoal = null;
private Set<SimpleClearingGoal> unassignedGoals = new FastSet<SimpleClearingGoal>();
private Map<SimpleClearingGoal, EntityID> assignedPolice = new FastMap<SimpleClearingGoal, EntityID>();;
private List<EntityID> unassignedAgents = new FastList<EntityID>();
private List<EntityID> refuges = new FastList<EntityID>();
private IAMWorldModel worldModel;
// private IRoutingModule normalRouting;
private IRoutingModule possibleRouting;
private EntityID myself;
private IRoutingModule clearingRouting;
private ClearingGoalConfiguration config;
public PoliceTaskModule(IAMWorldModel worldModel, EntityID myself,
IRoutingModule normalRoutingModule, ISimulationTimer timer) {
this.worldModel = worldModel;
this.myself = myself;
possibleRouting = new BidirectionalDijkstrasRoutingModule(worldModel,
new PassableRoutingCostFunction(worldModel), timer);
clearingRouting = new BidirectionalDijkstrasRoutingModule(worldModel,
new SimpleDistanceRoutingCostFunction(worldModel, true), timer);
config = new ClearingGoalConfiguration(possibleRouting,
clearingRouting, worldModel, false);
worldModel.addWorldModelListener(this);
}
public void initialise() {
for (StandardEntity se : worldModel
.getEntitiesOfType(StandardEntityURN.REFUGE)) {
refuges.add(se.getID());
}
for (StandardEntity se : worldModel
.getEntitiesOfType(StandardEntityURN.POLICE_FORCE)) {
unassignedAgents.add(se.getID());
}
// Sort by ID
Collections.sort(refuges, IDComparator.DEFAULT_INSTANCE);
Collections.sort(unassignedAgents, IDComparator.DEFAULT_INSTANCE);
generatePoliceGoals();
generateOtherAgentGoals();
generateFireGoals();
// generateRefugeGoals();
update();
}
/**
* This should be called every turn
*/
public void update() {
updateAssignedTasks();
// First check if goals have been achieved
removeDoneTasks();
updateAndFilterUnassignedTasks();
// Now do new assignment
allocateTasks();
}
private void updateAndFilterUnassignedTasks() {
Iterator<SimpleClearingGoal> iterator = unassignedGoals.iterator();
while (iterator.hasNext()) {
SimpleClearingGoal goal = iterator.next();
goal.evaluateCurrentState();
if (goal.isDone()) {
iterator.remove();
}
}
}
private void updateAssignedTasks() {
Iterator<Entry<SimpleClearingGoal, EntityID>> assignedIt = assignedPolice
.entrySet().iterator();
while (assignedIt.hasNext()) {
Entry<SimpleClearingGoal, EntityID> entry = assignedIt.next();
entry.getKey().evaluateCurrentState();
}
}
/**
* @return the myCurrentGoal
*/
public SimpleClearingGoal getMyCurrentGoal() {
return myCurrentGoal;
}
/**
* Removes all tasks that have been achieved.
*/
private void removeDoneTasks() {
Iterator<Entry<SimpleClearingGoal, EntityID>> assignedIt = assignedPolice
.entrySet().iterator();
while (assignedIt.hasNext()) {
Entry<SimpleClearingGoal, EntityID> entry = assignedIt.next();
if (entry.getKey().isDone()) {
EntityID agent = entry.getValue();
if (agent.equals(myself)) {
myCurrentGoal = null;
}
unassignedAgents.add(agent);
assignedIt.remove();
}
}
}
/**
* Allocates idle police agents to new tasks
*/
private void allocateTasks() {
if (unassignedAgents.size() == 0 || unassignedGoals.size() == 0) {
return;
}
List<PotentialGoal> goals = new ArrayList<PotentialGoal>(
unassignedGoals.size());
Iterator<SimpleClearingGoal> goalIt = unassignedGoals.iterator();
while (goalIt.hasNext()) {
SimpleClearingGoal goal = goalIt.next();
double utility = goal.getCurrentUtility();
if (utility > 0) {
goals.add(new PotentialGoal(goal, utility));
}
}
// Now sort by priority and select only top goals
Collections.sort(goals, GoalComparator.DEFAULT_INSTANCE);
goals = goals.subList(goals.size() - unassignedAgents.size(), goals
.size());
// Calculate assignment
double[][] costs = new double[goals.size()][unassignedAgents.size()];
int offset = 0;
for (int i = 0; i < costs.length; i++) {
SimpleClearingGoal goal = goals.get(i + offset).goal;
StandardEntity target = worldModel.getEntity(goal.getTarget());
// boolean foundFeasible = false;
for (int j = 0; j < costs[i].length; j++) {
StandardEntity agent = worldModel.getEntity(unassignedAgents
.get(j));
double cost = goal.getCost((PoliceForce) agent);
if (!possibleRouting
.areConnected(agent.getID(), target.getID())
|| cost == Double.POSITIVE_INFINITY) {
cost = VERY_LARGE_COST;
} else {
if (!target.getID().equals(agent.getID())) {
cost += VERY_SMALL_COST * agent.getID().getValue();
}
}
costs[i][j] = cost;
}
}
int[] assignment = OptimalAssignmentCalculator
.calculateOptimalAssignment(costs);
for (int i = assignment.length - 1; i >= 0; i--) {
EntityID agent = unassignedAgents.remove(assignment[i]);
SimpleClearingGoal goal = goals.get(i).goal;
unassignedGoals.remove(goal);
assignedPolice.put(goal, agent);
if (agent.equals(myself)) {
myCurrentGoal = goal;
}
}
}
/**
*
*/
// private void generateRefugeGoals() {
// Pick refuge with lowest ID
// unassignedGoals.add(new RefugeConnectionGoal(refuges.get(0)));
// }
/**
*
*/
private void generateFireGoals() {
Collection<StandardEntity> entitiesOfType = worldModel
.getEntitiesOfType(StandardEntityURN.BUILDING,
StandardEntityURN.FIRE_STATION,
StandardEntityURN.POLICE_OFFICE,
StandardEntityURN.AMBULANCE_CENTRE);
for (StandardEntity standardEntity : entitiesOfType) {
Building b = (Building) standardEntity;
b.addEntityListener(this);
processBuilding(b);
}
}
private void processBuilding(Building b) {
if (b.isFierynessDefined() && b.getFieryness() > 0) {
unassignedGoals.add(new FireClearingGoal(b.getID(), config));
}
b.removeEntityListener(this);
}
/**
*
*/
private void generateOtherAgentGoals() {
Collection<StandardEntity> others = worldModel.getEntitiesOfType(
StandardEntityURN.AMBULANCE_TEAM,
StandardEntityURN.FIRE_BRIGADE);
for (StandardEntity se : others) {
unassignedGoals.add(new PlatoonClearingGoal(se.getID(), config));
}
}
/**
*
*/
private void generatePoliceGoals() {
Collection<StandardEntity> police = worldModel
.getEntitiesOfType(StandardEntityURN.POLICE_FORCE);
for (StandardEntity se : police) {
unassignedGoals.add(new PoliceClearingGoal(se.getID(), config));
}
}
private static class PotentialGoal {
/**
* @param goal
* @param utility
*/
public PotentialGoal(SimpleClearingGoal goal, double utility) {
this.goal = goal;
this.utility = utility;
}
private SimpleClearingGoal goal;
private double utility;
}
private static class GoalComparator implements Comparator<PotentialGoal> {
public static final GoalComparator DEFAULT_INSTANCE = new GoalComparator();
private static final IDComparator ID_COMPARATOR = new IDComparator();
/*
* (non-Javadoc)
*
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
@Override
public int compare(PotentialGoal goal1, PotentialGoal goal2) {
if (goal1.utility < goal2.utility) {
return -1;
} else if (goal1.utility > goal2.utility) {
return 1;
} else {
return ID_COMPARATOR.compare(goal1.goal.getTarget(), goal2.goal
.getTarget());
}
}
}
@Override
public void propertyChanged(Entity e, Property p, Object oldValue,
Object newValue) {
if (e instanceof Building) {
processBuilding((Building) e);
} else if (e instanceof Civilian) {
processCivilian((Civilian) e);
}
}
@Override
public void entityAdded(WorldModel<? extends StandardEntity> model,
StandardEntity e) {
if (e instanceof Civilian) {
e.addEntityListener(this);
processCivilian((Civilian) e);
}
}
private void processCivilian(Civilian e) {
if (e.isPositionDefined()) {
unassignedGoals.add(new CivilianClearingGoal(e.getID(), config));
e.removeEntityListener(this);
}
}
@Override
public void entityRemoved(WorldModel<? extends StandardEntity> model,
StandardEntity e) {
e.removeEntityListener(this);
}
}