package iamrescue.agent.firebrigade;
import iamrescue.belief.IAMWorldModel;
import iamrescue.belief.spatial.ISpatialIndex;
import iamrescue.belief.spatial.SpatialIndex;
import iamrescue.belief.spatial.SpatialQuery;
import iamrescue.belief.spatial.SpatialQueryFactory;
import iamrescue.util.PositionXY;
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 rescuecore2.misc.geometry.GeometryTools2D;
import rescuecore2.misc.geometry.Point2D;
import rescuecore2.standard.entities.Building;
import rescuecore2.standard.entities.StandardEntity;
import rescuecore2.standard.entities.StandardEntityURN;
import rescuecore2.standard.entities.StandardPropertyURN;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.EntityListener;
import rescuecore2.worldmodel.Property;
/**
* This class represents our current model of fire spreading. We build a model
* that can measure the importance of fires, and clusters them into different
* fire sites.
*
* @version 0.1
* @author fmdf08r
*
*/
public class FirePredictor implements EntityListener {
private HeatTransferGraph heatTransferGraph;
private IAMWorldModel model;
// private BuildingImportanceModel importanceModel;
private FastImportanceModel importanceModel;
// new parameters
private FastSet<Building> buildingsOnFire;
private List<FireSite> fireSites;
/**
* Constructor : initialise all the important data structures
*
* @param model
* the current world model
*
*/
public FirePredictor(IAMWorldModel model) {
this.model = model;
heatTransferGraph = new HeatTransferGraph(model);
// importanceModel = new BuildingImportanceModel(model, heatTransferGraph);
importanceModel = new FastImportanceModel(model, heatTransferGraph);
registerBuildingListeners(model);
// new parameters
fireSites = new ArrayList<FireSite>();
buildingsOnFire = new FastSet<Building>();
}
// /**
// *
// * @return buildings of the world model
// */
// public HashSet<Building> getAllBuildings(){
// return allBuildings;
// }
//
// /**
// *
// * @return burn out buildings
// *
// */
// public HashSet<Building> getBurntOutBuildings(){
// return burntOutBuildings;
// }
//
// public HashSet<Building> getBuildingsThatMightBeOnFire(IAMWorldModel wm){
// HashSet<Building> mightBeOnFire = new HashSet<Building>();
// for (StandardEntity e:
// wm.getEntitiesOfType(StandardEntityURN.BUILDING)){
//
// Building b = (Building)e;
// if (!burntOutBuildings.contains(b)){
// mightBeOnFire.add(b);
// }
// }
// return mightBeOnFire;
// }
/**
* TODO CHECK IF IT WORKS!!!
*
* @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);
}
}
public void update(int time) {
updateFireSites(time);
// mergeFireSites(time);
}
public void merge(int time) {
mergeFireSites(time);
}
private void mergeFireSites(int time) {
ArrayList<FireSite> newFireSites = new ArrayList<FireSite>();
ArrayList<FireSite> erasedFireSites = new ArrayList<FireSite>();
for (FireSite fireSite : fireSites) {
if (erasedFireSites.contains(fireSite))
continue;
for (FireSite otherSite : fireSites) {
if (erasedFireSites.contains(otherSite))
continue;
if (fireSite.equals(otherSite))
continue;
if (fireSite.intersects(otherSite)) {
// merge the two fireSites
FireSite newSite = merge(time, fireSite, otherSite);
if (!newFireSites.contains(newSite)) {
newFireSites.add(newSite);
if (!erasedFireSites.contains(fireSite))
erasedFireSites.add(fireSite);
if (!erasedFireSites.contains(otherSite))
erasedFireSites.add(otherSite);
}
}
}
}
fireSites.removeAll(erasedFireSites);
fireSites.addAll(newFireSites);
}
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;
}
/**
* 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 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;
}
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);
}
}
}
public void updateImportanceModel() {
importanceModel.update();
}
/**
*
* @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 Map<Double, Set<Building>> getImportances(
Collection<Building> fireBuildings) {
Map<Double, Set<Building>> pairs = new FastMap<Double, Set<Building>>();
for (Building b : fireBuildings) {
double importance = importanceModel.getContextImportance(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;
}
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;
}
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 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;
}
// public static boolean isValidTarget(Building b){
//
// if(b == null){
// System.out.println("FIRE MODEL: isValidTarget failed b = null");
// return false;
// }
//
// if (b.getFierynessProperty().getValue() != null && b.getFieryness() ==
// 8){
// System.out.println("FIRE MODEL: isValidTarget failed fieryness = 8");
// return false;
// }
//
// if (! b.isOnFire()){
// System.out.println("FIRE MODEL: isValidTarget failed b is not on fire");
// return false;
// }
//
// if (b.isOnFire() && b.getIgnitionProperty().getValue() != null &&
// b.getFieryness() == 8){
// System.out.println("FIRE MODEL: isValidTarget failed b is not on fire");
// return false;
// }
//
// // if getignition is not undefined and is set to false
// if (b.getIgnitionProperty().getValue() != null && ! b.getIgnition()){
// System.out.println("FIRE MODEL: isValidTarget failed not ignited");
// return false;
// }
//
// System.out.println("FIRE MODEL: isValidTarget passed");
// return true;
// }
public List<Building> getBuildingsToExtinguish(int noRequired,
int numberOfStepsAhead) {
/**
* How about we make it MYOPIC??
*
* Do not do any prediction...
*/
FirePredictor predictedFires = predictFires(numberOfStepsAhead); // update
// model
// to
// the
// future
Collection<Building> buildingsOnFire = predictedFires
.getAllPredictedBuildingsOnFire();
Map<Double, Set<Building>> importances = getImportances(buildingsOnFire);
List<Building> buildingsToExtinguish = getNumberOfMostImportantBuildingsToExtinguish(
importances, noRequired);
return buildingsToExtinguish;
}
private Collection<Building> getAllPredictedBuildingsOnFire() {
ArrayList<Building> predictedBuildingsOnFire = new ArrayList<Building>();
for (FireSite fireSite : fireSites) {
predictedBuildingsOnFire.addAll(fireSite.getBuildingsOnFire());
}
return predictedBuildingsOnFire;
}
/**
* 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())) {
Building building = (Building) e;
if (building.isFierynessDefined()) {
if (building.getFieryness() >= 1) {
if (building.getFieryness() <= 3) {
buildingsOnFire.add(building);
}
} else {
buildingsOnFire.remove(building);
}
}
}
}
public IAMWorldModel getWorldModel() {
return model;
}
public HeatTransferGraph getHeatTransferGraph() {
return heatTransferGraph;
}
// public BuildingImportanceModel getImportanceModel() {
// return importanceModel;
// }
public FastImportanceModel getImportanceModel() {
return importanceModel;
}
public void addBuildingsOnFire(FastSet<Building> buildingsOnFire2) {
this.buildingsOnFire.addAll(buildingsOnFire2);
}
public List<FireSite> getFireSites() {
return fireSites;
}
public void setFireSites(List<FireSite> fireSites) {
this.fireSites = fireSites;
}
public FastSet<Building> getBuildingsOnFire() {
return buildingsOnFire;
}
public FastSet<Building> getBurnOutBuildings() {
return buildingsOnFire;
}
}