package iamrescue.agent.firebrigade;
import iamrescue.agent.ISimulationTimer;
import iamrescue.belief.IAMWorldModel;
import iamrescue.belief.provenance.IProvenanceInformation;
import iamrescue.belief.provenance.ProvenanceLogEntry;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javolution.util.FastMap;
import javolution.util.FastSet;
import org.apache.log4j.Logger;
import rescuecore2.misc.Pair;
import rescuecore2.standard.entities.Building;
import rescuecore2.standard.entities.Civilian;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.standard.entities.StandardPropertyURN;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.worldmodel.EntityListener;
import rescuecore2.worldmodel.Property;
import rescuecore2.worldmodel.WorldModel;
import rescuecore2.worldmodel.WorldModelListener;
public class FastFirePredictor implements EntityListener,
WorldModelListener<StandardEntity> {
private HeatTransferGraph heatTransferGraph;
private IAMWorldModel model;
private static final int TIME_STEP_DISCOUNT = 500;
private static final double DISCOUNT_FACTOR = 0.95;
private int totalCivilians = 0;
private static final Logger LOGGER = Logger
.getLogger(IAMStrategyFireBrigade.class);
private FastImportanceModel importanceModel;
// new parameters
private FastSet<Building> buildingsOnFire;
private List<FastFireSite> fireSites;
private ISimulationTimer timer;
private boolean weMerge = false;
private EntityID myID;
private double[] discountPowers;
/**
* Constructor : initialise all the important data structures
*
* @param model
* the current world model
*
*/
public FastFirePredictor(ISimulationTimer timer, IAMWorldModel model,
EntityID myID) {
this.model = model;
this.timer = timer;
this.myID = myID;
heatTransferGraph = new HeatTransferGraph(model);
importanceModel = new FastImportanceModel(model, heatTransferGraph);
registerBuildingListeners(model);
// new parameters
fireSites = new ArrayList<FastFireSite>();
buildingsOnFire = new FastSet<Building>();
discountPowers = new double[TIME_STEP_DISCOUNT];
discountPowers[0] = 1;
discountPowers[1] = DISCOUNT_FACTOR;
for (int i = 2; i < discountPowers.length; i++) {
if (discountPowers[i - 1] == 0) {
discountPowers[i] = 0;
} else {
discountPowers[i] = discountPowers[i - 1] * DISCOUNT_FACTOR;
}
}
totalCivilians = model.getEntitiesOfType(StandardEntityURN.CIVILIAN)
.size();
model.addWorldModelListener(this);
}
/**
* register listeners to buildings
*
* @param model
*
*/
private void registerBuildingListeners(IAMWorldModel model) {
// TODO only select buildings that can catch fire
Collection<StandardEntity> entitiesOfType = model
.getEntitiesOfType(StandardEntityURN.BUILDING,
StandardEntityURN.REFUGE,
StandardEntityURN.AMBULANCE_CENTRE,
StandardEntityURN.POLICE_OFFICE,
StandardEntityURN.FIRE_STATION);
for (StandardEntity standardEntity : entitiesOfType) {
standardEntity.addEntityListener(this);
}
}
/**
* Update temperature (and thus energy) based on new measured (or
* communicated value)
*/
@Override
public void propertyChanged(Entity e, Property p, Object oldValue,
Object newValue) {
/**
* add buildings on fire
*/
if (p.getURN().equals(StandardPropertyURN.FIERYNESS.toString())) {
if (e instanceof Building) {
Building building = (Building) e;
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(building.getFullDescription());
}
if (building.isFierynessDefined()) {
// new building burning, add it to fireSites
if (building.getFieryness() >= 1
&& building.getFieryness() <= 3) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("spotted a building on fire at time "
+ timer.getTime() + " for: ");
}
if (belongsToAFireSite(building)) {
// the building has already being considered
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("building on fire belongs to a fire site");
}
return;
}
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("building does not belong to any fire site, we check its neighbours");
}
Collection<Building> neighbours = heatTransferGraph
.getNeighbouringBuildings(building);
List<FastFireSite> containedFireSites = atLeastOneBelongsToAFireSite(neighbours);
if (containedFireSites != null) {
if (containedFireSites.size() == 1) {
// new building to be added to a fireSite
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("building belongs only to a fire site we add it");
}
containedFireSites.get(0).addBuildingOnFire(
building);
return;
}
// neighbours contained in more than one fireSite
// they need to be merged
if (weMerge) {
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("building belongs to many fire sites we need to merge them");
}
merge(containedFireSites, building);
return;
} else {
// we decide not to merge fireBuildings
for (FastFireSite fastFireSite : containedFireSites) {
fastFireSite.addBuildingOnFire(building);
break;
}
return;
}
} else {
// it is a NEW ISOLATED FIRESITE
if (LOGGER.isDebugEnabled()) {
LOGGER
.debug("building on fire belongs to a NEW fire site");
}
fireSites.add(new FastFireSite(timer, building,
model, heatTransferGraph));
}
} else {
if (building.getFieryness() == 8) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("building has burnt out");
}
removeFromFireSites(building);
}
}
}
}
}
}
private void merge(List<FastFireSite> containedFireSites, Building building) {
List<FastFireSite> newFastFireSites = new ArrayList<FastFireSite>();
// first we remove all the fireSites that need to be merged
fireSites.removeAll(containedFireSites);
// we add the remaining fireSites to the new lits
newFastFireSites.addAll(fireSites);
// now we merge the fireSites
// 1 - we create a new set of buildings
Map<EntityID, Building> allBuildings = new FastMap<EntityID, Building>();
// 2 - we add the building that is contained in all the previous
// fireSites
allBuildings.put(building.getID(), building);
// 3 - we add to this map all the building on fire
for (FastFireSite fastFireSite : containedFireSites) {
allBuildings.putAll(fastFireSite.getBuildingsOnFire());
}
// 4 - we compute the center of this set
Building center = computeCenter(allBuildings.values());
// 5 - we create the new fireSite
FastFireSite mergedFireSite = new FastFireSite(timer, center, model,
heatTransferGraph);
// 6 - we add to it the new Buildings
mergedFireSite.setBuildingsOnFire(allBuildings);
// 7 - we update the fireSites
mergedFireSite.updateArraysSpeed(containedFireSites);
newFastFireSites.add(mergedFireSite);
// 6 - we update the fireSites list
fireSites = newFastFireSites;
}
private void removeFromFireSites(Building building) {
for (FastFireSite fireSite : fireSites) {
fireSite.removeBuilding(building);
}
}
private List<FastFireSite> atLeastOneBelongsToAFireSite(
Collection<Building> neighbours) {
List<FastFireSite> containedFireSites = new ArrayList<FastFireSite>();
for (Building building : neighbours) {
for (FastFireSite fireSite : fireSites) {
if (fireSite.containsBuilding(building))
if (!containedFireSites.contains(fireSite))
containedFireSites.add(fireSite);
}
}
if (containedFireSites.size() == 0)
return null;
return containedFireSites;
}
private boolean belongsToAFireSite(Building building) {
for (FastFireSite fireSite : fireSites)
if (fireSite.containsBuilding(building))
return true;
return false;
}
/**
* this method finds the center of a Set of Buildings by computing the the
* median of all the buildings to be considered
*
* @param buildings
* the buildings for which we need the centre
*
* @return centre of the set of buildings
*
*/
public Building computeCenter(Collection<Building> buildings) {
Building center = null;
FastMap<Integer, FastMap<Integer, Building>> xMap = new FastMap<Integer, FastMap<Integer, Building>>();
for (Building building : buildings) {
FastMap<Integer, Building> yMap = (FastMap<Integer, Building>) xMap
.get(new Integer(building.getX()));
if (yMap == null) {
yMap = new FastMap<Integer, Building>();
yMap.put(new Integer(building.getY()), building);
xMap.put(new Integer(building.getX()), yMap);
} else
yMap.put(new Integer(building.getY()), building);
}
// we can now sort everything
ArrayList<Integer> xs = new ArrayList<Integer>(xMap.keySet());
Collections.sort(xs);
int xMapIndex = xs.size() / 2;
// int xMapIndex = (int) Math.floor(xIndex);
if (xs.size() % 2 == 0) {
xMapIndex--;
}
FastMap<Integer, Building> medianSet = (FastMap<Integer, Building>) xMap
.get(xs.get(xMapIndex));
ArrayList<Integer> ys = new ArrayList<Integer>(medianSet.keySet());
Collections.sort(ys);
int yMapIndex = ys.size() / 2;
if (ys.size() % 2 == 0) {
yMapIndex--;
}
center = medianSet.get(ys.get(yMapIndex));
return center;
}
public HeatTransferGraph getHeatTransferGraph() {
return heatTransferGraph;
}
public void updateImportanceModel() {
importanceModel.update();
}
public List<FastFireSite> getFireSites() {
return fireSites;
}
public Double getTotalImportance(List<Building> buildings) {
Map<Double, Set<Building>> importances = this.getImportances(buildings);
Double total = 0.0;
Set<Double> imps = importances.keySet();
for (Double i : imps) {
Set<Building> bs = importances.get(i);
total += i * bs.size();
}
return total;
}
private int getTimeLastObserved(Building b) {
IProvenanceInformation provenance = model.getProvenance(b.getID(),
StandardPropertyURN.FIERYNESS);
if (provenance == null) {
return -100;
}
ProvenanceLogEntry lastDefined = provenance.getLastDefined();
if (lastDefined == null) {
return -100;
}
return lastDefined.getTimeStep();
}
protected Map<Double, Set<Building>> getImportances(
Collection<Building> fireBuildings) {
Map<Double, Set<Building>> pairs = new FastMap<Double, Set<Building>>();
for (Building b : fireBuildings) {
double importance = calculateImportance(b);
Set<Building> buildings;
if (pairs.containsKey(importance)) {
buildings = pairs.get(importance);
} else {
buildings = new HashSet<Building>();
}
buildings.add(b);
pairs.put(importance, buildings);
}
return pairs;
}
public double calculateContextDependentImportance(Building b) {
int civilianNumber = importanceModel.getCiviliansAroundBuilding(b);
double civilianImportance = 100 * ((0.1 + civilianNumber) / (0.1 + totalCivilians));
return civilianImportance;
}
public double calculateImportance(Building b) {
Rectangle2D bounds = model.getBounds();
double centerX = (bounds.getMinX() + bounds.getMaxX()) / 2;
double centerY = (bounds.getMinY() + bounds.getMaxY()) / 2;
double dx = b.getX() - centerX;
double dy = b.getY() - centerY;
double distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
if (distanceFromCenter < 1) {
distanceFromCenter = 1;
}
/*
* int numberOfHeatNeighbours =
* heatTransferGraph.getNeighbouringBuildings(b).size();
* if(numberOfHeatNeighbours == 0){ numberOfHeatNeighbours = 1; }
*/
Pair<Integer, Integer> myLocation = model.getEntity(myID).getLocation(
model);
dx = myLocation.first() - b.getX();
dy = myLocation.second() - b.getY();
double distanceFromMe = Math.sqrt(dx * dx + dy * dy);
double distanceSum = 10 * distanceFromCenter + distanceFromMe;
double intrinsicImportance = importanceModel.getImportance(b);
double civilianImportance= calculateContextDependentImportance(b);
double importance = intrinsicImportance * civilianImportance
/ distanceSum;
/*
* double importance = importanceModel.getContextImportance(b) /
* distanceSum ;//* numberOfHeatNeighbours);
*/
int timeAgoLastObserved = timer.getTime() - getTimeLastObserved(b);
double discountFactor;
if (timeAgoLastObserved == 0) {
discountFactor = 1;
} else {
if (timeAgoLastObserved < TIME_STEP_DISCOUNT) {
discountFactor = discountPowers[timeAgoLastObserved];
} else {
if (discountPowers[TIME_STEP_DISCOUNT - 1] == 0) {
discountFactor = 0;
} else {
discountFactor = Math.pow(DISCOUNT_FACTOR,
timeAgoLastObserved);
}
}
}
importance *= discountFactor;
return importance;
}
public List<Building> getOrderOfImportantBuildingsToExtinguish(
List<Building> targetBuildings) {
Map<Double, Set<Building>> importances = this
.getImportances(targetBuildings);
ArrayList<Building> orderedBuildings = new ArrayList<Building>();
ArrayList<Double> sortedImportances = new ArrayList<Double>(importances
.keySet());
Collections.sort(sortedImportances);
for (Double importance : sortedImportances) {
Set<Building> buildings = importances.get(importance);
for (Building b : buildings) {
orderedBuildings.add(b);
}
}
return orderedBuildings;
}
// public List<Building> getBuildingsToExtinguish(int noRequired, int
// numberOfStepsAhead) {
//
// FirePredictor predictedFires = predictFires(numberOfStepsAhead); //
// update
//
// Collection<Building> buildingsOnFire = predictedFires
// .getAllPredictedBuildingsOnFire();
// Map<Double, Set<Building>> importances = getImportances(buildingsOnFire);
// List<Building> buildingsToExtinguish =
// getNumberOfMostImportantBuildingsToExtinguish(
// importances, noRequired);
//
// return buildingsToExtinguish;
// }
// private FireSite merge(int time, FireSite fireSite, FireSite otherSite) {
//
// Collection<Building> buildings = fireSite.getBuildingsOnFire();
//
// buildings.addAll(otherSite.getBuildingsOnFire());
//
// Building center = computeCenter(buildings);
//
// FireSite newFireSite = new FireSite(center, heatTransferGraph, model);
//
// newFireSite.update(time);
//
// return newFireSite;
// }
//
//
// private void updateFireSites(int time) {
//
// // FIRST: we update the FireSites
// for (FireSite fireSite : fireSites)
// fireSite.update(time);
//
// // SECOND: we look for new FireSites:
// for (Building building : buildingsOnFire) {
//
// int firecounter = fireSites.size();
// for (FireSite fireSite : fireSites)
// if (!fireSite.containsBuilding(building))
// firecounter--;
//
// if (firecounter == 0) {
//
// // This is a NEW FIRE SITE STARTING
// FireSite newSite = new FireSite(building,this.heatTransferGraph,
// this.model);
// newSite.update(time);
//
// fireSites.add(newSite);
// }
// }
// }
//
//
// /**
// *
// * @param t
// * represents the number of time steps in the future
// * @return
// */
// public FirePredictor predictFires(int time) {
// FirePredictor firePredictor = this.copy();
//
// firePredictor.predictFireSites(time);
//
// firePredictor.mergeFireSites(time);
//
// return firePredictor;
// }
//
// private void predictFireSites(int time) {
// for (FireSite fSite : fireSites) {
// fSite.predictFireArrays(time);
// }
// }
//
// public FirePredictor copy() {
// FirePredictor firePredictorCopy = new FirePredictor(model);
//
// // no need for deep copy
// firePredictorCopy.heatTransferGraph = heatTransferGraph;
// firePredictorCopy.importanceModel = importanceModel;
// firePredictorCopy.buildingsOnFire = buildingsOnFire;
//
// firePredictorCopy.fireSites = cloneFireSites();
//
// return firePredictorCopy;
// }
//
// private List<FireSite> cloneFireSites() {
// List<FireSite> result = new ArrayList<FireSite>();
//
// for (FireSite fireSite : fireSites) {
// try {
// result.add((FireSite) fireSite.clone());
// } catch (CloneNotSupportedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
//
// return result;
// }
//
//
// protected List<Building> getNumberOfMostImportantBuildingsToExtinguish(
// Map<Double, Set<Building>> importances, int numberOf) {
// ArrayList<Building> topBuildings = new ArrayList<Building>();
// ArrayList<Double> sortedImportances = new ArrayList<Double>(importances
// .keySet());
//
// Collections.sort(sortedImportances);
// for (Double importance : sortedImportances) {
// Set<Building> buildings = importances.get(importance);
// for (Building b : buildings) {
// if (topBuildings.size() < numberOf) {
// topBuildings.add(b);
// }
// }
// if (topBuildings.size() == numberOf) {
// break;
// }
// }
// return topBuildings;
// }
//
//
//
//
// private Collection<Building> getAllPredictedBuildingsOnFire() {
// ArrayList<Building> predictedBuildingsOnFire = new ArrayList<Building>();
//
// for (FireSite fireSite : fireSites) {
// predictedBuildingsOnFire.addAll(fireSite.getBuildingsOnFire());
// }
//
// return predictedBuildingsOnFire;
// }
//
//
// public IAMWorldModel getWorldModel() {
// return model;
// }
//
//
// // public BuildingImportanceModel getImportanceModel() {
// // return importanceModel;
// // }
public FastImportanceModel getImportanceModel() {
return importanceModel;
}
//
//
// public void addBuildingsOnFire(FastSet<Building> buildingsOnFire2) {
// this.buildingsOnFire.addAll(buildingsOnFire2);
// }
//
//
// public void setFireSites(List<FastFireSite> fireSites) {
// this.fireSites = fireSites;
// }
//
// public FastSet<Building> getBuildingsOnFire() {
// return buildingsOnFire;
// }
//
// public FastSet<Building> getBurnOutBuildings() {
// return buildingsOnFire;
// }
@Override
public void entityAdded(WorldModel<? extends StandardEntity> model,
StandardEntity e) {
if (e instanceof Civilian) {
totalCivilians++;
}
}
@Override
public void entityRemoved(WorldModel<? extends StandardEntity> model,
StandardEntity e) {
if (e instanceof Civilian) {
totalCivilians--;
}
}
}