Java tutorial
package AntColonyPDP; /* * Copyright (C) 2011-2015 Rinde van Lon, iMinds-DistriNet, KU Leuven * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import static com.google.common.collect.Maps.newHashMap; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import javax.annotation.Nullable; import org.apache.commons.math3.random.RandomGenerator; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Monitor; import com.github.rinde.rinsim.core.Simulator; import com.github.rinde.rinsim.core.model.pdp.DefaultPDPModel; import com.github.rinde.rinsim.core.model.pdp.PDPModel.ParcelState; import com.github.rinde.rinsim.core.model.pdp.Parcel; import com.github.rinde.rinsim.core.model.pdp.Vehicle; import com.github.rinde.rinsim.core.model.road.RoadModel; import com.github.rinde.rinsim.core.model.road.RoadModelBuilders; import com.github.rinde.rinsim.core.model.road.RoadUser; import com.github.rinde.rinsim.core.model.time.TickListener; import com.github.rinde.rinsim.core.model.time.TimeLapse; import com.github.rinde.rinsim.event.Listener; import com.github.rinde.rinsim.geom.Graph; import com.github.rinde.rinsim.geom.MultiAttributeData; import com.github.rinde.rinsim.geom.Point; import com.github.rinde.rinsim.geom.io.DotGraphIO; import com.github.rinde.rinsim.geom.io.Filters; import com.github.rinde.rinsim.ui.View; import com.github.rinde.rinsim.ui.renderers.PDPModelRenderer; import com.github.rinde.rinsim.ui.renderers.PlaneRoadModelRenderer; import com.github.rinde.rinsim.ui.renderers.RoadUserRenderer; /** * Example showing a fleet of taxis that have to pickup and transport customers * around the city of Leuven. * <p> * If this class is run on MacOS it might be necessary to use * -XstartOnFirstThread as a VM argument. * @author Rinde van Lon */ public final class Environment { // Independent variables public static final int MAP_SCALE = 10; private static final int NUM_ANTS = 20; public static final boolean CENTRALIZED = false; private static final boolean TESTING = true; //Try out different strategies static final boolean BOLD_AGENTS = false; static final boolean DYNAMIC_AGENTS = true; private static final int NUM_COLONIES = (NUM_ANTS - 1) / 4 + 1; private static final int ANT_CAPACITY = 1; private static final double GF_THRESHOLD = MAP_SCALE * .4; static final double VEHICLE_SPEED_KMH = 5d * 10; private static final int NUM_FOOD_SOURCE = NUM_ANTS / 2; private static final int FOOD_SOURCE_SIZE = 100 / MAP_SCALE; private static final long SERVICE_DURATION = 60000; private static final double NEW_FOOD_SOURCE_PROB = .007 * NUM_ANTS / 5 / FOOD_SOURCE_SIZE; private static final int MAX_SOURCES = NUM_ANTS; //Maximum amount of food sources at the same time private static HashMap<CentralizedAnt, FoodSource> assignments = new HashMap<CentralizedAnt, FoodSource>(); //gui/simulator settings settings private static final Map<String, Graph<MultiAttributeData>> GRAPH_CACHE = newHashMap(); private static final int SPEED_UP = 4; private static final long TEST_STOP_TIME = 61 / 3 * 200 * 60 * 1000; private static final int TEST_SPEED_UP = 64; //plane params static final Point MIN_POINT = new Point(0, 0); static final Point MAX_POINT = new Point(MAP_SCALE, MAP_SCALE); //Gradient field parameters private static ArrayList<Colony> COLONIES = new ArrayList<Colony>(); private static ArrayList<GFAnt> ANTS = new ArrayList<GFAnt>(); private static ArrayList<CentralizedAnt> CENTRALIZED_ANTS = new ArrayList<CentralizedAnt>(); private static ArrayList<FoodSource> SOURCES = new ArrayList<FoodSource>(); private static HashMap<FoodElement, List<Point>> DROPPED_FOOD_ELEMENTS = new HashMap<FoodElement, List<Point>>(); private static ArrayList<FoodElement> destroyedFoods = new ArrayList<FoodElement>(); private static HashMap<Vehicle, Point> GRADIENT_VECTORS = new HashMap<Vehicle, Point>(); private static int SUCCESSFUL_DELIVERIES = 0; private static int EXPIRED_SOURCES = 0; private static int EXPIRED_ELEMENTS = 0; private Environment() { } /** * Starts the {@link TaxiExample}. * @param args The first option may optionally indicate the end time of the * simulation. */ public static void main(@Nullable String[] args) { final long endTime = args != null && args.length >= 1 ? Long.parseLong(args[0]) : Long.MAX_VALUE; run(TESTING, endTime, "gradient field", null /* new Display() */, null, null); } /** * Run the example. * @param testing If <code>true</code> enables the test mode. */ public static void run(boolean testing) { run(testing, Long.MAX_VALUE, "gradient field", null, null, null); } /** * Starts the example. * @param testing Indicates whether the method should run in testing mode. * @param endTime The time at which simulation should stop. * @param graphFile The graph that should be loaded. * @param display The display that should be used to show the ui on. * @param m The monitor that should be used to show the ui on. * @param list A listener that will receive callbacks from the ui. * @return The simulator instance. */ public static Simulator run(boolean testing, final long endTime, String setup, @Nullable Display display, @Nullable Monitor m, @Nullable Listener list) { final View.Builder view = createGui(testing, display, m, list); final Simulator simulator = Simulator.builder() .addModel(RoadModelBuilders.plane().withMinPoint(MIN_POINT).withMaxPoint(MAX_POINT) .withMaxSpeed(VEHICLE_SPEED_KMH)) .addModel(DefaultPDPModel.builder()).addModel(view).build(); final RandomGenerator rng = simulator.getRandomGenerator(); final RoadModel roadModel = simulator.getModelProvider().getModel(RoadModel.class); //Create the setup for the centralized or gradient field simulation if (CENTRALIZED) registerCentralizedSimulator(endTime, simulator, rng, roadModel); else registerGradientFieldSimulator(endTime, simulator, rng, roadModel); simulator.start(); return simulator; } //Settings for the centralized approach private static void registerCentralizedSimulator(final long endTime, final Simulator simulator, final RandomGenerator rng, final RoadModel roadModel) { //Adding and register the colonies in the environment for (int i = 0; i < NUM_COLONIES; i++) { Colony colony = new Colony(roadModel.getRandomPosition(rng), 0); simulator.register(colony); register(colony); } //Adding and register the ants in the environment for (int i = 0; i < NUM_ANTS; i++) { CentralizedAnt nca = new CentralizedAnt(roadModel.getRandomPosition(rng), ANT_CAPACITY); simulator.register(nca); register(nca); } //Adding and register the food sources in the environment for (int i = 0; i < NUM_FOOD_SOURCE; i++) { generateFoodSource(simulator, rng, roadModel); } simulator.addTickListener(new TickListener() { @Override public void tick(TimeLapse time) { if (time.getStartTime() > endTime) { simulator.stop(); } else { // 1: Generate new food sources if (rng.nextDouble() < NEW_FOOD_SOURCE_PROB * (MAX_SOURCES - SOURCES.size()) / MAX_SOURCES) { //&& SOURCES.size() < MAX_SOURCES) { generateFoodSource(simulator, rng, roadModel); } // 2: check expiration and empty food sources for (FoodSource source : new ArrayList<FoodSource>(SOURCES)) { if (source.isExpired()) { SOURCES.remove(source); } } assignments = new HashMap<CentralizedAnt, FoodSource>(); // 3: get the positions HashMap<FoodSource, Point> fsPositions = new HashMap<FoodSource, Point>(); for (FoodSource fs : SOURCES) fsPositions.put(fs, fs.getPickupLocation()); HashMap<CentralizedAnt, Point> caPositions = new HashMap<CentralizedAnt, Point>(); for (CentralizedAnt ca : CENTRALIZED_ANTS) caPositions.put(ca, ca.getPosition().get()); // 4: extract closest pair and find next ArrayList<CentralizedAnt> remainingAnts = new ArrayList<CentralizedAnt>(CENTRALIZED_ANTS); for (CentralizedAnt ant : CENTRALIZED_ANTS) { if (ant.isTaken()) remainingAnts.remove(ant); } // 5: Assign food elements with ants when it is possible ArrayList<FoodSource> remainingSources = new ArrayList<FoodSource>(SOURCES); Map<Pair, Double> shortestFound = new HashMap<Pair, Double>(); while (!remainingSources.isEmpty() && !remainingAnts.isEmpty()) { for (FoodSource fs : remainingSources) { Point pos = fsPositions.get(fs); Map<Pair, Double> distances = new HashMap<Pair, Double>(); for (CentralizedAnt ca : remainingAnts) { Point antPos = caPositions.get(ca); distances.put(new Pair(ca, fs), Point.distance(antPos, pos)); } distances = MapUtil.sortByValue(distances); Pair first = null; double firstDist = 10; for (Map.Entry<Pair, Double> entry : distances.entrySet()) { //this for loop is just a complicated way to extract the first element if (first == null) { first = entry.getKey(); firstDist = entry.getValue(); } } //found the shortest for current ant shortestFound.put(first, firstDist); } // found the shortest for each customer //now sort again shortestFound = MapUtil.sortByValue(shortestFound); Pair absoluteShortest = null; for (Map.Entry<Pair, Double> entry : shortestFound.entrySet()) { if (absoluteShortest == null) { absoluteShortest = entry.getKey(); } } //we found and now use the absolute minimal distance pair found //if the ant can reach it if (canDeliver(absoluteShortest.v, absoluteShortest.c)) { assignments.put(absoluteShortest.v, absoluteShortest.c); remainingSources.remove(absoluteShortest.c); } else absoluteShortest.v.groundAnt(); remainingAnts.remove(absoluteShortest.v); shortestFound = new HashMap<Pair, Double>(); } //printAssignments(assignments); // debug method } } @Override public void afterTick(TimeLapse timeLapse) { performanceAssessment(simulator, roadModel); } }); } private static void registerGradientFieldSimulator(final long endTime, final Simulator simulator, final RandomGenerator rng, final RoadModel roadModel) { for (int i = 0; i < NUM_COLONIES; i++) { Colony colony = new Colony(roadModel.getRandomPosition(rng), 0); simulator.register(colony); register(colony); } for (int i = 0; i < NUM_ANTS; i++) { GFAnt newAnt = new GFAnt(roadModel.getRandomPosition(rng), ANT_CAPACITY, BOLD_AGENTS, DYNAMIC_AGENTS); simulator.register(newAnt); register(newAnt); } for (int i = 0; i < NUM_FOOD_SOURCE; i++) { generateFoodSource(simulator, rng, roadModel); } simulator.addTickListener(new TickListener() { @Override public void tick(TimeLapse time) { if (time.getStartTime() > endTime) { simulator.stop(); } else { //if (rng.nextDouble() < NEW_FOOD_SOURCE_PROB && SOURCES.size() < MAX_SOURCES) { if (rng.nextDouble() < NEW_FOOD_SOURCE_PROB * (MAX_SOURCES - SOURCES.size()) / MAX_SOURCES) { generateFoodSource(simulator, rng, roadModel); } // 1: check starving and empty food sources for (FoodSource source : new ArrayList<FoodSource>(SOURCES)) { if (source.isExpired()) { SOURCES.remove(source); } } // 2: calculate gradient fields for (GFAnt ant : ANTS) { Point resultingVector = new Point(0, 0); if (!ant.wantToRest()) { // if ant doesn't want to rest calculate normal gradient field for (GFAnt other : ANTS) { resultingVector = MapUtil.addPoints(resultingVector, repulsiveField(ant, other)); } for (FoodSource food : SOURCES) { resultingVector = MapUtil.addPoints(resultingVector, attractiveField(ant, food)); } } else { //otherwise calculate to reach the colony for (Colony colony : COLONIES) { resultingVector = MapUtil.addPoints(resultingVector, attractiveField(ant, colony)); } for (GFAnt other : ANTS) { if (other.isResting()) resultingVector = MapUtil.addPoints(resultingVector, repulsiveField(ant, other)); } } resultingVector = MapUtil.normalize(resultingVector); if (((Double) resultingVector.x).isNaN()) resultingVector = new Point(0, 0); GRADIENT_VECTORS.put(ant, resultingVector); } for (FoodElement fe : new ArrayList<>(destroyedFoods)) { if (roadModel.containsObject(fe)) { roadModel.removeObject(fe); destroyedFoods.remove(fe); } } for (FoodElement foodElement : new HashSet<FoodElement>(DROPPED_FOOD_ELEMENTS.keySet())) { Point usedPosition = DROPPED_FOOD_ELEMENTS.get(foodElement).get(0); Point deliveryPosition = DROPPED_FOOD_ELEMENTS.get(foodElement).get(1); FoodElement nfe = new FoodElement(Parcel.builder(usedPosition, deliveryPosition) .serviceDuration(SERVICE_DURATION).neededCapacity(1).buildDTO(), Math.random() * 2 + 1); simulator.register(nfe); destroyedFoods.add(foodElement); DroppedFoodSource nfs = new DroppedFoodSource(Parcel.builder(usedPosition, deliveryPosition) .serviceDuration(SERVICE_DURATION).neededCapacity(1).buildDTO(), nfe); register(nfs); simulator.register(nfs); DROPPED_FOOD_ELEMENTS.remove(foodElement); } } } @Override public void afterTick(TimeLapse timeLapse) { // only at simulator minutes performanceAssessment(simulator, roadModel); } }); } static View.Builder createGui(boolean testing, @Nullable Display display, @Nullable Monitor m, @Nullable Listener list) { View.Builder view = View.builder().with(PlaneRoadModelRenderer.builder()) .with(RoadUserRenderer.builder().withImageAssociation(Colony.class, "/ant_colony.png") .withImageAssociation(GFAnt.class, "/small_ant.png") .withImageAssociation(CentralizedAnt.class, "/small_ant.png") .withImageAssociation(FoodSource.class, "/tiny_wheat.png") .withImageAssociation(DroppedFoodSource.class, "/tiny_wheat.png")) .with(PDPModelRenderer.builder()).withTitleAppendix("Ant colony Demo"); if (testing) { view = view.withAutoClose().withAutoPlay().withSimulatorEndTime(TEST_STOP_TIME) .withSpeedUp(TEST_SPEED_UP); } else if (m != null && list != null && display != null) { view = view.withMonitor(m).withSpeedUp(SPEED_UP) .withResolution(m.getClientArea().width, m.getClientArea().height).withDisplay(display) .withCallback(list).withAsync().withAutoPlay().withAutoClose(); } return view; } // load the graph file static Graph<MultiAttributeData> loadGraph(String name) { try { if (GRAPH_CACHE.containsKey(name)) { return GRAPH_CACHE.get(name); } final Graph<MultiAttributeData> g = DotGraphIO.getMultiAttributeGraphIO(Filters.selfCycleFilter()) .read(Environment.class.getResourceAsStream(name)); GRAPH_CACHE.put(name, g); return g; } catch (final FileNotFoundException e) { throw new IllegalStateException(e); } catch (final IOException e) { throw new IllegalStateException(e); } } private static void generateFoodSource(final Simulator simulator, final RandomGenerator rng, final RoadModel roadModel) { Point foodPosition = roadModel.getRandomPosition(rng); Point deliveryLocation = generateDeliveryLocation(); FoodSource nfs = new FoodSource(Parcel.builder(foodPosition, deliveryLocation) .serviceDuration(SERVICE_DURATION).neededCapacity(ANT_CAPACITY).buildDTO()); simulator.register(nfs); for (int j = 0; j < FOOD_SOURCE_SIZE; j++) { FoodElement nfe = new FoodElement(Parcel.builder(foodPosition, deliveryLocation) .serviceDuration(SERVICE_DURATION).neededCapacity(ANT_CAPACITY).buildDTO(), Math.random() * 2 + 1); simulator.register(nfe); nfs.putElement(nfe); } register(nfs); } /** * Gradient Field methods */ private static Point repulsiveField(GFAnt ant, GFAnt other) { if (ant.equals(other)) return new Point(0, 0); Point p1 = ant.getPosition().get(); Point p2 = other.getPosition().get(); double distance = Point.distance(p1, p2); if (distance == 0) return new Point(0, 0); if (distance > GF_THRESHOLD) return new Point(0, 0); Point result = Point.diff(p1, p2); result = MapUtil.normalize(result); result = MapUtil.rescale(result, 1.6 / distance / distance); return result; } private static Point attractiveField(GFAnt ant, FoodSource food) { Point p1 = ant.getPosition().get(); Point p2 = food.getPosition(); double distance = Point.distance(p1, p2); if (distance > GF_THRESHOLD) return new Point(0, 0); Point result = Point.diff(p2, p1); result = MapUtil.normalize(result); result = MapUtil.rescale(result, 1 / distance / distance); int remainingFoodModifier = food.getNumberElements(); result = MapUtil.rescale(result, Math.log(remainingFoodModifier + 1) + 1); return result; } private static Point attractiveField(GFAnt ant, Colony food) { Point p1 = ant.getPosition().get(); Point p2 = food.getPosition(); double distance = Point.distance(p1, p2); Point result = Point.diff(p2, p1); result = MapUtil.normalize(result); result = MapUtil.rescale(result, 1 / distance / distance); return result; } public static boolean canDeliver(CentralizedAnt v, FoodSource c) { double energy = v.getEnergy(); Point foodPos = c.getPosition(); double dist1 = Point.distance(v.getPosition().get(), foodPos); double dist2 = Point.distance(c.getDeliveryLocation(), foodPos); return (energy >= dist1 + 2 * dist2 + c.getElementCost()); } public static List<GFAnt> getAnts() { return ANTS; } private static void performanceAssessment(final Simulator simulator, final RoadModel roadModel) { if ((simulator.getCurrentTime() % (3600000 * 20)) == 0 && simulator.getCurrentTime() > 0) { System.out.println( "expired food sources: " + getExpiredSourceCount() + ", elements: " + getExpiredElementCount()); System.out.println("succesfulDeliveries: " + getDeliveryCount()); double antCount = ((double) getDeliveryCount()) / ((double) NUM_ANTS); System.out.println("ant efficiency: " + antCount); System.out.println("per simulation minute: " + antCount / (simulator.getCurrentTime() / 3600000)); Map<RoadUser, Point> debug = roadModel.getObjectsAndPositions(); for (RoadUser ru : new HashSet<RoadUser>(debug.keySet())) { if (ru instanceof Ant) debug.remove(ru); else if (ru instanceof Colony) debug.remove(ru); else if (ru instanceof FoodElement && (simulator.getModelProvider().getModel(DefaultPDPModel.class) .getParcelState((FoodElement) ru)).equals(ParcelState.DELIVERED)) debug.remove(ru); // else System.out.print(((FoodSource) ru).getPickupLocation()); } } } public static void register(GFAnt ant) { ANTS.add(ant); GRADIENT_VECTORS.put(ant, ant.getStartPosition()); } public static void register(CentralizedAnt ant) { CENTRALIZED_ANTS.add(ant); } public static void register(Colony colony) { COLONIES.add(colony); } public static void register(FoodSource source) { SOURCES.add(source); } public static FoodElement pickup(FoodSource source) { FoodElement food = source.pickup(); if (source.getNumberElements() == 0) { SOURCES.remove(source); } return food; } public static ArrayList<FoodSource> getFoodSources() { return SOURCES; } public static FoodSource getFoodFromVisual(GFAnt ant) { return nearestVisibleFood(ant); } public static FoodSource nearestVisibleFood(GFAnt ant) { // method for seeing nearby food double shortestDist = 99999999; FoodSource nearestFood = null; for (FoodSource foodSource : SOURCES) { double dist = Point.distance(foodSource.getPosition(), ant.getPosition().get()); if (dist < GFAnt.VISUAL_RANGE && dist < shortestDist) { shortestDist = dist; nearestFood = foodSource; } } return nearestFood; } public static Point getGradientField(GFAnt ant) { return GRADIENT_VECTORS.get(ant); } public static void notifyDelivery() { SUCCESSFUL_DELIVERIES++; } public static void notifyExpiring(int elements) { EXPIRED_SOURCES++; EXPIRED_ELEMENTS += elements; } private static int getExpiredSourceCount() { return EXPIRED_SOURCES; } private static int getExpiredElementCount() { return EXPIRED_ELEMENTS; } public static int getDeliveryCount() { return SUCCESSFUL_DELIVERIES; } public static Colony getNearestColony(Ant ant) { Colony closest = null; double dist = 99999; for (Colony colony : COLONIES) { double newDist = Point.distance(ant.getPosition().get(), colony.getPosition()); if (closest == null || newDist < dist) { closest = colony; dist = newDist; } } return closest; } public static void dropFood(FoodElement el, List<Point> positions) { DROPPED_FOOD_ELEMENTS.put(el, positions); } public static void remove(FoodSource parcel) { SOURCES.remove(parcel); } public static Parcel getAssignment(CentralizedAnt ca) { return assignments.get(ca); } //used for examining assignments // private static void printAssignments(HashMap<CentralizedAnt,FoodSource> assignments) { // for(Vehicle v : assignments.keySet()){ // Point taxipos = ((CentralizedAnt) v).getPosition().get(); // Point customerpos = assignments.get(v).getPickupLocation(); // System.out.println("taxi pos: "+taxipos+" customer pos : "+customerpos+" distance :"+Point.distance(taxipos, customerpos)); // } // } private static Point generateDeliveryLocation() { int index = (int) Math.floor(Math.random() * NUM_COLONIES); return COLONIES.get(index).getPosition(); } static class Pair { CentralizedAnt v; FoodSource c; Pair(CentralizedAnt v, FoodSource c) { this.v = v; this.c = c; } } public static boolean mayRest(Ant ant) { return getNearestColony(ant).occupyForResting(ant); } public static Colony getColonyFromVisual(GFAnt gfAnt) { // method for seeing nearby available colony double shortestDist = 99999999; Colony nearestColony = null; for (Colony colony : COLONIES) { if (!colony.isOccupiedByOtherAnt(gfAnt)) { double dist = Point.distance(colony.getPosition(), gfAnt.getPosition().get()); if (dist < GFAnt.VISUAL_RANGE && dist < shortestDist) { shortestDist = dist; nearestColony = colony; } } } return nearestColony; } }