package iamrescue.agent.ambulanceteam.ambulancetools; import iamrescue.agent.ISimulationTimer; import iamrescue.agent.ambulanceteam.IAMAmbulanceTeam; import iamrescue.agent.firebrigade.FastFireSite; import iamrescue.agent.firebrigade.FastImportanceModel; import iamrescue.agent.firebrigade.HeatTransferGraph; import iamrescue.agent.firebrigade.IAMStrategyFireBrigade; import iamrescue.belief.IAMWorldModel; 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.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; public class FireTracker implements EntityListener { private HeatTransferGraph heatTransferGraph; private IAMWorldModel model; private static final Logger LOGGER = Logger .getLogger(IAMAmbulanceTeam.class); private List<FastFireSite> fireSites; private ISimulationTimer timer; private boolean weMerge = true; /** * Constructor : initialise all the important data structures * * @param model * the current world model * */ public FireTracker(ISimulationTimer timer, IAMWorldModel model) { this.model = model; this.timer = timer; heatTransferGraph = new HeatTransferGraph(model); registerBuildingListeners(model); // new parameters fireSites = new ArrayList<FastFireSite>(); } /** * 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); } 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 List<FastFireSite> getFireSites() { return fireSites; } }