├── README.md └── src └── com └── walmart └── linearroad └── generator ├── Accident.java ├── CLParser.java ├── Car.java ├── Environment.java ├── LinearGen.java ├── Route.java ├── TrafficCondition.java └── XWay.java /README.md: -------------------------------------------------------------------------------- 1 | *** 2 | # NOTICE: 3 | 4 | ## This repository has been archived and is not supported. 5 | 6 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 7 | *** 8 | NOTICE: SUPPORT FOR THIS PROJECT HAS ENDED 9 | 10 | This projected was owned and maintained by Walmart. This project has reached its end of life and Walmart no longer supports this project. 11 | 12 | We will no longer be monitoring the issues for this project or reviewing pull requests. You are free to continue using this project under the license terms or forks of this project at your own risk. This project is no longer subject to Walmart's bug bounty program or other security monitoring. 13 | 14 | 15 | ## Actions you can take 16 | 17 | We recommend you take the following action: 18 | 19 | * Review any configuration files used for build automation and make appropriate updates to remove or replace this project 20 | * Notify other members of your team and/or organization of this change 21 | * Notify your security team to help you evaluate alternative options 22 | 23 | ## Forking and transition of ownership 24 | 25 | For [security reasons](https://www.theregister.co.uk/2018/11/26/npm_repo_bitcoin_stealer/), Walmart does not transfer the ownership of our primary repos on Github or other platforms to other individuals/organizations. Further, we do not transfer ownership of packages for public package management systems. 26 | 27 | If you would like to fork this package and continue development, you should choose a new name for the project and create your own packages, build automation, etc. 28 | 29 | Please review the licensing terms of this project, which continue to be in effect even after decommission. 30 | 31 | # LinearGenerator 32 | Reworked data generator for LinearRoad streaming benchmark that no longer needs mitsim. 33 | 34 | To use the new generator compile the files and run: `java com.walmart.linearroad.generator.LinearGen [-o ] [-x ] [-m ]` 35 | 36 | The default settings in Environment.java create: 37 | * ~1 GB per xway files with 38 | * ~25M records per file 39 | * ~370K unique cars per xway 40 | 41 | Each xway takes roughly 5m30s to create. 42 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/Accident.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | /** 4 | * Accident 5 | * 6 | * Select cars randomly to be part of an accident and create the Accident. 7 | * The Accident will hold the essential information for an Accident. 8 | * 9 | * Created by Sung Kim on 6/28/2016. 10 | */ 11 | public class Accident { 12 | private Car c1; 13 | private Car c2; 14 | private int seg; 15 | private int dir; 16 | private int time; 17 | //private int clearTime; 18 | 19 | /** 20 | * Pass in two Cars and a time to create an Accident. 21 | * 22 | * @param c1 the first Car 23 | * @param c2 the second Car 24 | * @param time the simulation time, in seconds 25 | * @param seg the segment of the XWay this Accident occurred in 26 | * @param dir the direction of the XWay this Accident occurred in 27 | */ 28 | public Accident(Car c1, Car c2, int time, int seg, int dir) { 29 | this.c1 = c1; 30 | this.c2 = c2; 31 | this.seg = seg; 32 | this.dir = dir; 33 | this.time = time; 34 | //this.clearTime = -1; 35 | } 36 | 37 | /** 38 | * 39 | * @return the first Car that makes up the Accident 40 | */ 41 | public Car getC1() { 42 | return c1; 43 | } 44 | 45 | /** 46 | * 47 | * @return the second Car that makes up the Accident 48 | */ 49 | public Car getC2() { 50 | return c2; 51 | } 52 | 53 | /** 54 | * 55 | * @return the Accident segment 56 | */ 57 | public int getSeg() { return seg; } 58 | 59 | /** 60 | * 61 | * @return the direction 62 | */ 63 | public int getDir() { return dir;} 64 | 65 | /** 66 | * 67 | * @return the time the accident was created 68 | */ 69 | public int getTime() { return time; } 70 | } 71 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/CLParser.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * Parse command line arguments. Test commit. 7 | * Created by Sung Kim on 8/8/16. 8 | */ 9 | public class CLParser { 10 | /** 11 | * 12 | */ 13 | private static Set allowedFlags; 14 | 15 | static { 16 | allowedFlags = new HashSet<>(); 17 | allowedFlags.add("-x"); // Number of expressways. 18 | allowedFlags.add("-o"); // Output file name. 19 | allowedFlags.add("-m"); // Multi-thread 20 | ////////////////////////////////////////////////////////////////// 21 | // HDFS: comment out if not needed to remove hadoop dependencies 22 | //allowedFlags.add("-h"); // Hadoop output file name. Should be mutually exclusive with -o. 23 | // HDFS: END 24 | ////////////////////////////////////////////////////////////////// 25 | } 26 | 27 | private static Set mutuallyExclusiveFileFlags; 28 | 29 | static { 30 | mutuallyExclusiveFileFlags = new HashSet<>(); 31 | mutuallyExclusiveFileFlags.add("-o"); 32 | mutuallyExclusiveFileFlags.add("-h"); 33 | } 34 | 35 | /** 36 | * Ensure all command line flags are valid. 37 | * Flags must precede arguments. 38 | * All arguments must have a flag. 39 | * No standalone flags without an argument. 40 | * 41 | * @param args the command-line arguments 42 | * @return a List of Strings with the invalid flags 43 | */ 44 | private static List checkCommandLineArgs(String[] args) { 45 | List invalidFlags = new ArrayList<>(); 46 | for (int i = 0; i < args.length; i++) { 47 | if (i % 2 == 0) { 48 | if (!allowedFlags.contains(args[i])) { 49 | invalidFlags.add(args[i]); 50 | } 51 | } 52 | } 53 | return invalidFlags; 54 | } 55 | 56 | /** 57 | * Ensure that mutually exclusive flags are not present. 58 | * 59 | * @param flagMap a Map of the valid flags parsed from the command-line args 60 | * @return the List of mutually exclusive flags 61 | */ 62 | private static List checkMutuallyExclusiveFlags(Map flagMap) { 63 | List exclusiveFlags = new ArrayList<>(); 64 | 65 | // DEBUG 66 | //flagMap.keySet().forEach(System.out::println); 67 | // DEBUG END 68 | 69 | if (flagMap.keySet().containsAll(mutuallyExclusiveFileFlags)) { 70 | exclusiveFlags.addAll(mutuallyExclusiveFileFlags); 71 | } 72 | return exclusiveFlags; 73 | } 74 | 75 | /** 76 | * Simple func to print error messages from flags and exit. 77 | * 78 | * @param invalidFlags the List of invalid flags to print 79 | */ 80 | private static void printInvalidFlags(List invalidFlags) { 81 | StringBuilder sb = new StringBuilder(); 82 | System.err.print("Invalid flags: "); 83 | invalidFlags.forEach((flag) -> { 84 | sb.append(flag + ", "); 85 | }); 86 | System.err.print(new StringBuilder(sb.substring(0, (sb.lastIndexOf(","))))); 87 | System.err.println(" used."); 88 | System.exit(1); 89 | } 90 | 91 | /** 92 | * Simple func to print error messages from mutually exclusive flags and exit. 93 | * 94 | * @param mutuallyExclusiveFlags 95 | */ 96 | private static void printMutuallyExclusiveFlags(List mutuallyExclusiveFlags) { 97 | StringBuilder sb = new StringBuilder(); 98 | System.err.print("The following flags are mutually exclusive: "); 99 | mutuallyExclusiveFlags.forEach((flag) -> { 100 | sb.append(flag + ", "); 101 | }); 102 | System.err.print(new StringBuilder(sb.substring(0, (sb.lastIndexOf(","))))); 103 | System.err.println("."); 104 | System.exit(1); 105 | } 106 | 107 | /** 108 | * Look for flags and their associated value. 109 | * If no value follows a flag throw an error. 110 | * 111 | * @param args the command line argument String[] 112 | * @return a Map of flags and their values 113 | */ 114 | public static Map parseCommandLine(String[] args) { 115 | Map argMap = new HashMap<>(); 116 | 117 | // Only bother with non-default args if the number of command-line args are >= 2. 118 | if (args.length == 1 && allowedFlags.contains(args[0])) { 119 | System.err.println(args[0] + " requires an argument."); 120 | System.exit(1); 121 | } 122 | if (args.length == 1 && !allowedFlags.contains(args[0])) { 123 | System.err.println(args[0] + " is an invalid argument."); 124 | System.exit(1); 125 | } 126 | if (args.length > 1) { 127 | List invalidFlags = checkCommandLineArgs(args); 128 | if (invalidFlags.size() > 0) { 129 | printInvalidFlags(invalidFlags); 130 | } 131 | } 132 | 133 | // Parse into Map 134 | for (int i = 0; i < args.length; i += 2) { 135 | argMap.put(args[i], args[i + 1]); 136 | } 137 | 138 | // Check for mutually exclusive 139 | List mutuallyExclusiveFlags = checkMutuallyExclusiveFlags(argMap); 140 | if (mutuallyExclusiveFlags.size() > 0) { 141 | printMutuallyExclusiveFlags(mutuallyExclusiveFlags); 142 | } 143 | for (String k : argMap.keySet()) { 144 | System.out.println(argMap.get(k)); 145 | } 146 | 147 | return argMap; 148 | } 149 | 150 | public static void main(String[] args) { 151 | Map argMap = parseCommandLine(args); 152 | for (String k : argMap.keySet()) { 153 | System.out.println(k + "," + argMap.get(k)); 154 | } 155 | } 156 | // TODO: Doesn't yet account for java <> -o -h // Where -h becomes the arg for -o. 157 | } 158 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/Car.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * A Car that will travel the expressways. 7 | *

8 | * Created by Sung Kim on 6/28/2016. 9 | */ 10 | public class Car { 11 | 12 | /** 13 | * A global qid (Query Id) counter. 14 | */ 15 | private static long qid = 0; 16 | 17 | /** 18 | * Global counters for emitted notifications. 19 | */ 20 | private static long t0s = 0; 21 | private static long t2s = 0; 22 | private static long t3s = 0; 23 | private static long tAll = 0; 24 | 25 | /** 26 | * The CarId 27 | */ 28 | private int id; 29 | 30 | /** 31 | * The current position, in feet, where feet ranges from 0 - 5280 * Environment.NUM_SEGMENTS 32 | */ 33 | private double position; 34 | 35 | /** 36 | * currSpeed and lastSpeed are used to determine the distance traveled 37 | * currSpeed is what is emitted if emit() is called 38 | * Updated by updateSpeed() 39 | */ 40 | private int currSpeed; 41 | /** 42 | * currSpeed and lastSpeed are used to determine the distance traveled 43 | * Updated by updateSpeed() 44 | */ 45 | private int lastSpeed; 46 | 47 | /** 48 | * The current time 49 | */ 50 | private int currTime; 51 | 52 | /** 53 | * The time this Car began its run. 54 | * If reentrant, it will be the time it re-entered the expressway. 55 | */ 56 | private int startTime; 57 | 58 | /** 59 | * NOT USED: at the moment. 60 | */ 61 | private int lastTime; 62 | 63 | /** 64 | * The Route is a logical way to separate Car state from where the Car is trying to go. 65 | */ 66 | private Route route; 67 | 68 | /** 69 | * The car's current lane. 70 | */ 71 | private int currLane; 72 | 73 | /** 74 | * Whether this Car can be removed from the simulation 'world.' 75 | * Currently this is only due to moving to the exit lane. 76 | */ 77 | private boolean canRemove; 78 | 79 | /** 80 | * Whether this Car is an Accident Car, i.e. is involved in an accident. 81 | */ 82 | private boolean isAccidentCar; 83 | 84 | /** 85 | * When creating an Accident, this is the Car to target, its lane and position. 86 | * The targeted car will be the one that was stopped first by the simulation. 87 | */ 88 | private Car targetCar; 89 | 90 | /** 91 | * Go ahead and prevent a Car from being in an Accident twice. 92 | * Set once this car has been in an accident. 93 | */ 94 | private boolean hasBeenInAccident; 95 | 96 | /** 97 | * Create a Car with an id, a Route, and a startTime. 98 | * Route creation is external to enable custom Routes if necessary. 99 | * 100 | * @param id must be unique for the simulation 101 | * @param route see Route 102 | * @param startTime simulation time when the car enters the simulation 103 | */ 104 | public Car(int id, Route route, int startTime) { 105 | this.id = id; 106 | 107 | resetCar(route, startTime); 108 | 109 | // DEBUG 110 | //System.out.println(Environment.trafficConditions.get(route.getXway()).get(getSegFromPosition()).get(route.getDir()).getNumCars()); 111 | // END DEBUG 112 | } 113 | 114 | 115 | /** 116 | * Set the proper state for a reentrant Car with a new random Route. 117 | * 118 | * @param startTime 119 | * @return this Car with its state properly reset 120 | */ 121 | public Car reEnter(int startTime) { 122 | Route newRoute = Environment.createRoute(); 123 | 124 | resetCar(newRoute, startTime); 125 | 126 | // DEBUG 127 | //System.out.println("Hi, I'm car " + id + " and I'm reentering at time " + startTime + "."); 128 | // DEBUG END 129 | 130 | return this; 131 | } 132 | 133 | 134 | /** 135 | * Whether creating a new Car or restarting an existing Car (reenter) just give it a new Route and a start time. 136 | * 137 | * @param route the new Route 138 | * @param startTime the start time 139 | */ 140 | private void resetCar(Route route, int startTime) { 141 | this.route = route; 142 | this.currTime = startTime; 143 | this.startTime = startTime; 144 | this.currSpeed = Environment.ENTRY_SPEED; // This is the given Environment speed while entering on-ramp. 145 | lastSpeed = 0; // Of course the lastSpeed is zero when new, or reentrant. 146 | this.position = segToBeginningPosition(route.getEntranceSegIndex()); 147 | currLane = 0; // All new cars start in the entrance lane. 148 | canRemove = false; 149 | 150 | // This section is wrong. It shouldn't be here. 151 | // Add this Car to the Set of Cars in this given TrafficCondition 152 | // DEBUG 153 | //Environment.trafficConditions.get(route.getXway()); 154 | // DEBUG END 155 | // Having references to Environment.trafficConditions leads to tight coupling with the simulation as a whole. 156 | // It is possible to separate them, but at this point the value of separation isn't useful enough. 157 | Environment.trafficConditions.get(route.getXway()).get(getSegFromPosition()).get(route.getDir()).addCar(this); 158 | 159 | isAccidentCar = false; 160 | targetCar = null; 161 | hasBeenInAccident = false; 162 | } 163 | 164 | 165 | /** 166 | * When a car is created it is placed at the beginning of the segment if going right. 167 | * I.e. Dir 0, Seg 0 -> Position 0, Seg 1 -> Position 5280. 168 | * If a car is going left, it is placed at the end of the segment, in position-feet. 169 | * I.e. Dir 1, Seg 0 -> Position 5279, Seg 1 -> Position 10559. 170 | * 171 | * @param seg the segment from which to get a beginning position 172 | * @return the position, in feet, of a Car's starting point 173 | */ 174 | private int segToBeginningPosition(int seg) { 175 | if (route.getDir() == 0) { 176 | return seg * Environment.FEET_PER_SEGMENT; 177 | } else { 178 | return seg * Environment.FEET_PER_SEGMENT + Environment.FEET_PER_SEGMENT - 1; 179 | } 180 | } 181 | 182 | 183 | /** 184 | * Simple calculation to get segment from current position. 185 | * 186 | * @return the calculated segment from a given position 187 | */ 188 | private int getSegFromPosition() { 189 | return (int) Math.floor(position / Environment.FEET_PER_SEGMENT); 190 | } 191 | 192 | 193 | /** 194 | * This is our default method to handle speed to-be-used for distance. 195 | * Simply use the average of the curr and last speeds. 196 | * More complexity can be added to take acceleration into account. 197 | * 198 | * @return the average speed, as a double 199 | */ 200 | private double getAvgSpeed() { 201 | return (currSpeed + lastSpeed) / 2.0; 202 | } 203 | 204 | 205 | /** 206 | * The speed parameter is a double because that's what getAvgSpeed returns. 207 | * 208 | * @param speed the speed, as a double, to get how many feet were traveled 209 | * @return how many feet are traveled at the given speed in one second 210 | */ 211 | private double convertMphToFeetPerSec(double speed) { 212 | return speed / 3600 * 5280; 213 | } 214 | 215 | 216 | /** 217 | * This is just a function of speed, converted to feet per second, times seconds. 218 | * 219 | * @param seconds how many seconds of travel at the given speed function of the car 220 | * @return how much the car moves in the given seconds 221 | */ 222 | private double getDistanceTraveled(int seconds) { 223 | return convertMphToFeetPerSec(getAvgSpeed()) * seconds; 224 | } 225 | 226 | 227 | /** 228 | * Change lanes. 229 | * Lane changes will be random while traveling but must be in the travel lanes: 230 | * The travel lanes are: 1 to Environment.EXIT_LANE - 1 231 | * Allow for a speed up or speed down while changing lanes. Randomly chosen update speeds will be accompany 232 | * a lane change. 233 | */ 234 | private void changeLane() { 235 | int speedDelta = (int) (Math.random() * Environment.MAX_LANE_CHANGE_SPEED_DELTA); 236 | currSpeed += (Math.random() < 0.5) ? speedDelta : -speedDelta; 237 | currLane = new Random().nextInt(Environment.NUM_LANES - 2) + 1; // I.e. 5 lanes, [0-3) + 1 -> 1-3 travel lanes 238 | } 239 | 240 | /** 241 | * Set the lane and handle any attendant processing desired. 242 | * When the car hits the exit segment, based on its Route, then move to the exit lane: 243 | * Environment.EXIT_LANE. 244 | * 245 | * @param lane the lane the Car will move to 246 | */ 247 | private void setLane(int lane) { 248 | currLane = lane; 249 | if (currLane == Environment.EXIT_LANE) { 250 | exitXWay(); 251 | } 252 | } 253 | 254 | 255 | /** 256 | * Clean up the car. But also decide if this car will be a candidate for re-entrance later. 257 | * If the car won't be returning then it should be eligible for garbage collection. 258 | */ 259 | private void exitXWay() { 260 | if (!isRemoveable()) { 261 | setRemovable(true); 262 | // Put the car into the reentrants list at a random time in the future 263 | if (willBeReentrant()) { 264 | int newTime = currTime + (new Random().nextInt(1000) + 1000); // Add arbitrary delay. 265 | Environment.addToReentrants(newTime, this); 266 | 267 | } 268 | removeFromTrafficCondition(); 269 | } 270 | } 271 | 272 | 273 | /** 274 | * Set the canRemove status if the Car can be removed from the simulation ... 275 | * unless it's reentrant. 276 | */ 277 | private void setRemovable(boolean remove) { 278 | canRemove = remove; 279 | // If the car is reentrant then this must be set to false. 280 | // UPDATE: canRemove is used to decide whether a car can be removed from a traffic condition as well as the 281 | // working car set in the main simulation. But, the car can, and should, leave both. It will be kept in a 282 | // separate List/Map for reentrance when the right time comes along. 283 | } 284 | 285 | 286 | /** 287 | * Does this car have its canRemove flag set? 288 | * 289 | * @return whether this car can be removed from the simulation 290 | */ 291 | public boolean isRemoveable() { 292 | return canRemove; 293 | } 294 | 295 | 296 | /** 297 | * Determine if this Car will be eligible for reentrance. 298 | * 299 | * @return whether this Car will be eligible to be put into the reentrants list 300 | */ 301 | private boolean willBeReentrant() { 302 | if (Math.random() < Environment.REENTRANT_PERCENT) return true; 303 | return false; 304 | } 305 | 306 | 307 | /** 308 | * Return a day between two values for type 3 queries 309 | * 310 | * @return a random int between the Environment first and last days for historical, type 3, queries 311 | */ 312 | private int getHistoricalDay() { 313 | Random r = new Random(); 314 | return r.nextInt(Environment.HISTORICAL_DAY_LAST - Environment.HISTORICAL_DAY_FIRST) + Environment.HISTORICAL_DAY_FIRST; 315 | } 316 | 317 | 318 | /** 319 | * Adjust the max speed down on travel lanes based on road conditions. 320 | * TODO: this should be more continues than in steps. 321 | * 322 | * @return an integer representing how much to adjust a cars speed down based on traffic congestion 323 | */ 324 | private int getTCMaxSpeedAdjustment() { 325 | int numCars = Environment.trafficConditions.get(route.getXway()).get(getSegFromPosition()).get(route.getDir()).getNumCars(); 326 | int safeCars = Environment.FULL_SPEED_CARS_PER_LANE * (Environment.NUM_LANES - 2); 327 | int step = 5 * (Environment.NUM_LANES - 2); // Extra cars per lane * num travel lanes 328 | if (numCars > safeCars && numCars <= safeCars + step) { 329 | return 10; 330 | } else if (numCars > safeCars + step && numCars <= safeCars * step * 2) { 331 | return 20; 332 | } else if (numCars > safeCars + step * 2 && numCars <= safeCars * step * 3) { 333 | return 30; 334 | } else if (numCars > safeCars + step * 3 && numCars <= safeCars * step * 4) { 335 | return 40; 336 | } else if (numCars > safeCars + step * 4) { 337 | return 50; 338 | } 339 | return 0; 340 | } 341 | 342 | 343 | /** 344 | * The speed is increased: 345 | * 1) Up to 70 mph if it's not, if the TrafficCondition allows 346 | * 2) Must slow down around an accident 347 | * 3) Must generally flow with traffic based on TrafficCondition 348 | * i.e. based on the number of travel lanes and the number of cars currently in the segment 349 | * determines the max speed of an area 350 | * 4) (TODO) You want a random set of speedy cars, seeking to go Environment.MAX_SPEED 351 | *

352 | * This will also be 'world' controlled since the Cars are not autonomous threads. 353 | * This also spares us from having to have a TrafficCondition associated with each Car, although it would 354 | * simply be a reference to the same TrafficCondition for all cars in a xway-dir. 355 | *

356 | * 5) (TODO) Cars should slow down on the other side of an accident as well 357 | * 6) If the car is in the entry lane the max speed is forty and it will speed up, if possible, up to 40 358 | * 7) If the car is in the exit lane it will disappear after its first appearance in the exit lane, with 359 | * a speed of 10. 360 | *

361 | * This update will be run every UPDATE_INTERVAL. Default is 1 second. 362 | *

363 | */ 364 | private void updateSpeed() { 365 | lastSpeed = currSpeed; 366 | int fudge = 0; // Insert some "real-world" variety in speeds. 50-50 chance of a little faster/slower. 367 | 368 | // Entrance lane 369 | if (isAccidentCar) { 370 | currSpeed = 0; 371 | } else if (currLane == Environment.ENTRANCE_LANE && currSpeed < Environment.MAX_SPEED_ENTRANCE) { 372 | currSpeed += acceleration(Environment.ENTRANCE_LANE) * Environment.UPDATE_INTERVAL; 373 | if (currSpeed > Environment.MAX_SPEED_ENTRANCE - getTCMaxSpeedAdjustment()) { 374 | fudge = new Random().nextInt(Environment.SPEED_FUDGE / 2); 375 | currSpeed = Environment.MAX_SPEED_ENTRANCE - getTCMaxSpeedAdjustment() + (Math.random() < 0.5 ? fudge : -fudge); 376 | if (currSpeed < 0) currSpeed = 0; 377 | } 378 | 379 | // Travel lanes 380 | // We don't just max out speed but provide a wiggle 381 | } else if (currLane > Environment.ENTRANCE_LANE && currLane < Environment.EXIT_LANE) { 382 | // We only change travel lanes while traveling in the travel lanes 383 | if (Math.random() < Environment.TRAVEL_LANE_CHANGE_PROBABILITY) { 384 | changeLane(); // This belongs in 'moveCar()' 385 | } else { 386 | // The 1 is the lane index and is arbitrary--anything non-entrance and non-exit would work. 387 | currSpeed += acceleration(1) * Environment.UPDATE_INTERVAL; 388 | if (currSpeed > Environment.SPEED_LIMIT - getTCMaxSpeedAdjustment()) { 389 | fudge = new Random().nextInt(Environment.SPEED_FUDGE); 390 | currSpeed = Environment.SPEED_LIMIT - getTCMaxSpeedAdjustment() + (Math.random() < 0.5 ? fudge : -fudge); 391 | } 392 | } 393 | // Exit lane 394 | } else if (currLane == Environment.EXIT_LANE) { 395 | currSpeed = Environment.MAX_SPEED_EXIT; 396 | } 397 | } 398 | 399 | 400 | /** 401 | * Determine how much a Car's velocity should change based on the Environment.UPDATE_INTERVAL 402 | * and a given acceleration for the type of lane. 403 | * 404 | * @param laneType entrance lane, Travel lane 405 | * @return the amount of acceleration 406 | */ 407 | private int acceleration(int laneType) { 408 | if (laneType == Environment.ENTRANCE_LANE) { 409 | return (int) (Math.random() * Environment.ACCELERATION_ENTRANCE); 410 | } else { 411 | return (int) (Math.random() * Environment.ACCELERATION_TRAVEL); 412 | } 413 | } 414 | 415 | 416 | /** 417 | * Make sure a position doesn't go negative and also doesn't go beyond the segment limit. 418 | * Remember, 0-indexed. 419 | */ 420 | private void normalizePosition() { 421 | if (position < 0) position = 0; 422 | if (position > Environment.FEET_PER_SEGMENT * (Environment.NUM_SEGMENTS - 1)) 423 | position = Environment.FEET_PER_SEGMENT * (Environment.NUM_SEGMENTS - 1); 424 | // DEBUG 425 | if (getSegFromPosition() >= Environment.NUM_SEGMENTS) 426 | System.out.println(currTime + ": " + id + ": " + position); 427 | // DEBUG END 428 | } 429 | 430 | 431 | /** 432 | * Return the id of the car. 433 | * 434 | * @return This Car's id. 435 | */ 436 | public int getId() { 437 | return id; 438 | } 439 | 440 | 441 | /** 442 | * Move the car forward x feet based on speed. 443 | * The environment/context/executing function needs to call this function on each car. I.e. the cars are not 444 | * threads that update themselves. 445 | * At the moment cars can overlap. We may want to add dimensions to cars and prevent them from overlapping and 446 | * track distance from one car to the car in front of it, as well as make sure cars can't change lanes 447 | * if there's a car in the way. 448 | */ 449 | public void moveCar() { 450 | // We don't move if we're an accident car 451 | if (isAccidentCar) { 452 | return; 453 | } 454 | 455 | // We home in on the first accident car and then stop and also become an accident car 456 | if (targetCar != null) { 457 | currLane = targetCar.currLane; 458 | currSpeed = Environment.MAX_SPEED; // Just go ramming speed for now. 459 | position = getDistanceTraveled(Environment.UPDATE_INTERVAL); 460 | if (position > targetCar.position) { 461 | position = targetCar.position; 462 | stopCar(); 463 | } 464 | return; 465 | } 466 | 467 | int dir = route.getDir(); 468 | double delta = getDistanceTraveled(Environment.UPDATE_INTERVAL); // Update the car location every sec. emit 469 | TrafficCondition tc_prev = Environment.trafficConditions.get(route.getXway()).get(getSegFromPosition()).get(route.getDir()); 470 | 471 | // every UPDATE_INTERVAL 472 | position += dir == 0 ? delta : -delta; 473 | normalizePosition(); 474 | 475 | // Get the new segment based on the new position 476 | int seg = getSegFromPosition(); 477 | // See if you're in the same segment 478 | TrafficCondition tc_curr = Environment.trafficConditions.get(route.getXway()).get(seg).get(route.getDir()); 479 | 480 | // DEBUG 481 | //System.out.println(route.getXway() + "," + seg + "," + route.getDir() + ": " + tc_curr.getNumCars()); 482 | // END DEBUG 483 | 484 | // Update the TrafficCondition. 485 | // Check if the car has moved from one segment to another. 486 | if (tc_curr.getId() != tc_prev.getId()) { 487 | // Subtract from the previous and add to the current 488 | //tc_curr.incrNumCars(); 489 | tc_curr.addCar(this); 490 | //tc_prev.decrNumCars(); 491 | tc_prev.removeCar(this); 492 | } 493 | 494 | // Check if this car needs to exit and take appropriate exit, or non-exit, actions. 495 | if (seg == route.getExitSegIndex() && !isRemoveable()) { 496 | setLane(Environment.EXIT_LANE); 497 | // Check if the car has moved from entrance lane to a travel lane. 498 | } else if ((dir == 0 && seg == route.getEntranceSegIndex() + 1) || 499 | (dir == 1 && seg == route.getEntranceSegIndex() - 1)) { 500 | setLane(Environment.ENTRANCE_LANE + 1); 501 | } 502 | 503 | // Update the speed after moving the car 504 | updateSpeed(); 505 | } 506 | 507 | /** 508 | * Emit the current information, or position report, for a Car. 509 | * A report is as follows: 510 | * type, time, carid, speed, xway, lane, dir, seg, pos, qid, sinit, send, dow, tod, day 511 | * Cars emit upon request. 512 | * 513 | * @return a String with the above fields 514 | */ 515 | public String emit() { 516 | StringBuilder info = new StringBuilder(); 517 | // Type 0 518 | info.append(String.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", 519 | 0, currTime, id, currSpeed, route.getXway(), currLane, route.getDir(), getSegFromPosition(), (int) position, -1, -1, -1, -1, -1, -1)); 520 | t0s++; 521 | // Check chances for also emitting an "Other" report, and then what type each will have. 522 | if (Math.random() < 0.01) { 523 | double typeChance = Math.random(); 524 | if (typeChance < 0.5) { // Type 2 525 | // qid is a global counter attached to the Car class at the moment. 526 | info.append(String.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", 527 | 2, currTime, id, currSpeed, route.getXway(), currLane, route.getDir(), getSegFromPosition(), (int) position, qid++, -1, -1, -1, -1, -1)); 528 | t2s++; 529 | tAll++; 530 | } else if (typeChance >= 0.5 && typeChance <= 0.6) { // Type 3 531 | info.append(String.format("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n", 532 | 3, currTime, id, currSpeed, route.getXway(), currLane, route.getDir(), getSegFromPosition(), (int) position, qid++, -1, -1, -1, -1, getHistoricalDay())); 533 | t3s++; 534 | tAll++; 535 | } else if (typeChance >= 0.6) { // Type 4 536 | // Do nothing for now 537 | } 538 | } 539 | tAll++; 540 | 541 | return info.toString(); 542 | } 543 | 544 | /** 545 | * Add a delta to the current time of a car. 546 | * 547 | * @param delta amount of time in seconds to update Car's time. May not always be an UPDATE_INTERVAL 548 | */ 549 | public void updateTime(int delta) { 550 | currTime += delta; 551 | } 552 | 553 | 554 | /** 555 | * When a car exits an expressway we need to remove it from its corresponding TrafficCondition as well. 556 | * Another function tightly-coupled with the Environment.trafficConditions. Does have to be but realize that it is. 557 | */ 558 | public void removeFromTrafficCondition() { 559 | // DEBUG 560 | //System.out.println("Removing " + id + " from simulation at time " + currTime + "."); 561 | //System.out.println(Environment.trafficConditions.get(getCurrXway()).get(getSegFromPosition()).get(getCurrDir()).getNumCars()); 562 | // DEBUG END 563 | 564 | Environment.trafficConditions.get(getCurrXway()).get(getSegFromPosition()).get(getCurrDir()).removeCar(this); 565 | 566 | // DEBUG 567 | //System.out.println(Environment.trafficConditions.get(getCurrXway()).get(getSegFromPosition()).get(getCurrDir()).getNumCars()); 568 | // DEBUG END 569 | } 570 | 571 | /** 572 | * @return the Car's startTime 573 | */ 574 | public int getStartTime() { 575 | return startTime; 576 | } 577 | 578 | /** 579 | * @return the Car's currTime 580 | */ 581 | public int getCurrTime() { 582 | return currTime; 583 | } 584 | 585 | /** 586 | * @return the Car's current xway 587 | */ 588 | public int getCurrXway() { 589 | return route.getXway(); 590 | } 591 | 592 | /** 593 | * @return the Car's current lane 594 | */ 595 | public int getCurrLane() { 596 | return currLane; 597 | } 598 | 599 | /** 600 | * Place this car at the same lane and position as the target car. 601 | * Used for accident creation 602 | * 603 | * @param cr the target car 604 | */ 605 | public void targetStoppedCar(Car cr) { 606 | this.currLane = cr.currLane; 607 | this.position = cr.position; 608 | } 609 | 610 | /** 611 | * position is a double but we will return it as an int and base equalities as an int. 612 | * 613 | * @return the current position of the Car as an int 614 | */ 615 | public int getCurrPosition() { 616 | return (int) position; 617 | } 618 | 619 | /** 620 | * Get the segment. 621 | * 622 | * @return the Car's current segment based on its position 623 | */ 624 | public int getCurrSegment() { 625 | return getSegFromPosition(); 626 | } 627 | 628 | /** 629 | * Get the direction of travel. 630 | * 631 | * @return the Car's current direction of travel 632 | */ 633 | public int getCurrDir() { 634 | return route.getDir(); 635 | } 636 | 637 | 638 | /** 639 | * Not just stop a car but make it an accident car. No reason to 'officially' stop a car otherwise. 640 | * // OR, makeAccidentCar()... 641 | */ 642 | public void stopCar() { 643 | currSpeed = 0; 644 | lastSpeed = 0; // The last speed is necessary to to prevent car movement once the car is stopped. 645 | isAccidentCar = true; 646 | hasBeenInAccident = true; 647 | 648 | // DEBUG 649 | System.out.println("Car " + id + " is stopping at time " + currTime + " and position " + position); 650 | // DEBUG END 651 | } 652 | 653 | /** 654 | * Remove a car as an accident car and restart it on its journey. We don't forcibly remove the car, 655 | * although we can. 656 | */ 657 | public void startCar() { 658 | isAccidentCar = false; 659 | targetCar = null; 660 | 661 | // DEBUG 662 | System.out.println("Car " + id + " is starting at time " + currTime); 663 | // DEBUG END 664 | } 665 | 666 | /** 667 | * We don't want a car to be in an accident twice. 668 | * 669 | * @return 670 | */ 671 | public boolean hasBeenInAccident() { 672 | return hasBeenInAccident; 673 | } 674 | 675 | /* 676 | The following are global/Car-class level variables to track total numbers of notifications. 677 | */ 678 | /** 679 | * Get the global qid. 680 | * 681 | * @return the current qid of the cars 682 | */ 683 | public static long getQid() { 684 | return qid; 685 | } 686 | 687 | /** 688 | * @return the number of type 0 notifications emitted 689 | */ 690 | public static long getT0s() { 691 | return t0s; 692 | } 693 | 694 | /** 695 | * @return the number of type 2 notifications emitted 696 | */ 697 | public static long getT2s() { 698 | return t2s; 699 | } 700 | 701 | /** 702 | * @return the number of type 3 notifications emitted 703 | */ 704 | public static long getT3s() { 705 | return t3s; 706 | } 707 | 708 | /** 709 | * @return the number of all notifications emitted 710 | */ 711 | public static long getTAll() { 712 | return tAll; 713 | } 714 | 715 | /** 716 | * We set equality of Cars as the object itself or a matching id. 717 | * 718 | * @param o the other Car 719 | * @return whether the cars are the same object or have the same id 720 | */ 721 | @Override 722 | public boolean equals(Object o) { 723 | if (this == o) { 724 | return true; 725 | } 726 | if (o instanceof Car) { 727 | Car that = (Car) o; 728 | if (that.id == id) { 729 | return true; 730 | } 731 | } 732 | return false; 733 | } 734 | 735 | 736 | /** 737 | * The hash code will simply the car's id. 738 | * 739 | * @return the car's id 740 | */ 741 | @Override 742 | public int hashCode() { 743 | return id; 744 | } 745 | 746 | 747 | /** 748 | * A set of tests for the Car class. 749 | * 750 | * @param args 751 | */ 752 | public static void main(String[] args) { 753 | // Necessary to initialize trafficConditions because Car makes references to it. (Yes, tightly coupled.) 754 | Environment.initializeTrafficConditions(); 755 | Environment.initializeReentrants(); 756 | 757 | Route r = new Route(0, 0, 0); 758 | Car c = new Car(1, r, 0); 759 | assert c.segToBeginningPosition(0) == 0 : "segToBeginningPosition, seg 0, dir 0, failed"; 760 | r = new Route(0, 1, 0); 761 | c = new Car(1, r, 0); 762 | assert c.segToBeginningPosition(0) == Environment.FEET_PER_SEGMENT - 1 : "segToBeginningPosition, seg 0, dir 1, failed"; 763 | r = new Route(0, 0, 0); 764 | c = new Car(1, r, 0); 765 | assert c.getAvgSpeed() == 5 : "getAvgSpeed failed"; 766 | System.out.println(c.convertMphToFeetPerSec(5)); 767 | assert c.convertMphToFeetPerSec(5) == 7.333333333333334 : "convertMphToFeetPerSec failed"; 768 | System.out.println(c.getDistanceTraveled(30)); 769 | assert (int) c.getDistanceTraveled(30) == 220 : "getDistanceTraveled failed"; 770 | assert c.getSegFromPosition() == 0 : "getSegFromPosition, pos 0, failed"; 771 | c.position = 5280; 772 | assert c.getSegFromPosition() == 1 : "getSegFromPosition, pos 5280, failed"; 773 | c.position = Environment.FEET_PER_SEGMENT * 33 + 55; 774 | assert c.getSegFromPosition() == 33 : "getSegFromPosition, seg 33, failed"; 775 | c.position = Environment.FEET_PER_SEGMENT * 33 - 55; 776 | assert c.getSegFromPosition() == 32 : "getSegFromPosition, seg 32, failed"; 777 | 778 | // Test how often something with a probability of 1% happens. 779 | int count = 0; 780 | for (int i = 0; i < 500; i++) { 781 | double j = Math.random(); 782 | if (j < 0.01) { 783 | System.out.println(count++ + ": " + j); 784 | } 785 | } 786 | 787 | // Ensure days for t3 historical tolls are within the given day range. 788 | for (int i = 0; i < 1000; i++) { 789 | int hday = c.getHistoricalDay(); 790 | //System.out.println(hday); 791 | assert hday >= Environment.HISTORICAL_DAY_FIRST && hday <= Environment.HISTORICAL_DAY_LAST : "Bad historical day"; 792 | } 793 | 794 | // Test a single car movement, no time update 795 | System.out.println("---Test a single car, no time---"); 796 | r = new Route(0, 0, 0); 797 | c = new Car(1, r, 0); 798 | System.out.print(c.emit()); 799 | while (c.getSegFromPosition() <= c.route.getExitSegIndex()) { 800 | c.moveCar(); 801 | System.out.print(c.emit()); 802 | } 803 | 804 | // Test 10 cars moving, no time update 805 | System.out.println("---Test 10 cars, no time---"); 806 | int numTestCars = 10; 807 | Random rand = new Random(); 808 | List cars = new ArrayList<>(); 809 | for (int i = 0; i < numTestCars; i++) { 810 | r = new Route(rand.nextInt(Environment.NUM_SEGMENTS), rand.nextInt(2), rand.nextInt(Environment.NUM_XWAYS)); 811 | c = new Car(i, r, 0); 812 | cars.add(c); 813 | System.out.print(c.emit()); 814 | } 815 | // -- 816 | boolean carsStillRunning = true; 817 | while (carsStillRunning) { 818 | carsStillRunning = false; 819 | for (Car cr : cars) { 820 | //if (cr.getSegFromPosition() <= cr.route.getExitSegIndex()) { 821 | if (!cr.canRemove) { 822 | cr.moveCar(); 823 | System.out.print(cr.emit()); 824 | carsStillRunning = true; 825 | } 826 | } 827 | } 828 | 829 | System.out.println("---Test for SIM_LENGTH---"); 830 | cars = new ArrayList<>(); 831 | int carId = 0; 832 | Iterator iter; 833 | // For each second of the simulation insert BASE_CARS_PER_UPDATE_INTERVAL_PER_XWAY 834 | //for (int i = 0; i < Environment.SIM_LENGTH; i++) { 835 | for (int i = 0; i < 100; i++) { 836 | // Introduce new cars at this time 837 | for (int j = 0; j < (Math.random() * Environment.BASE_CARS_PER_UPDATE_INTERVAL_PER_XWAY); j++) { 838 | r = new Route(rand.nextInt(Environment.NUM_SEGMENTS), rand.nextInt(2), rand.nextInt(Environment.NUM_XWAYS)); 839 | c = new Car(carId++, r, i); 840 | cars.add(c); 841 | } 842 | // Update and emit from all cars if their TIME_INTERVAL marks are hit 843 | iter = cars.iterator(); 844 | while (iter.hasNext()) { 845 | Car cr = iter.next(); 846 | if (((cr.currTime - cr.startTime) % Environment.TIME_INTERVAL) == 0) { 847 | 848 | // DEBUG - for speed changes 849 | if (cr.getId() == 1) { 850 | System.out.print(cr.emit()); 851 | } 852 | if (cr.canRemove) { 853 | iter.remove(); 854 | } 855 | // DEBUG END 856 | } 857 | 858 | cr.moveCar(); 859 | cr.updateTime(Environment.UPDATE_INTERVAL); 860 | } 861 | } 862 | } 863 | } 864 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/Environment.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * This is the world, and its constants. Better set in a properties file. 7 | * The settings below create: 8 | * ~1 GB per xway files with 9 | * ~25M records per file 10 | * ~370K unique cars per xway 11 | * Each xway takes roughly 5m30s to create. 12 | * 13 | *

14 | * Created by Sung Kim on 6/28/2016. 15 | */ 16 | public class Environment { 17 | /** 18 | * The number of expressways in the simulation. No longer final. 19 | */ 20 | public static int NUM_XWAYS = 1; 21 | 22 | /** 23 | * The number of segments in the simulation. 24 | */ 25 | public static final int NUM_SEGMENTS = 100; 26 | 27 | /** 28 | * The length, in feet, of a segment. Defaults to 1 mile with 5280 feet. 29 | */ 30 | public static final int FEET_PER_SEGMENT = 5280; 31 | 32 | /** 33 | * Where downtown starts in this expressway. 34 | * This assumes every expressway has a 'downtown' section. 35 | * Set to 1/3 from the beginning of 0-based start of an expressway. 36 | */ 37 | public static final int DOWNTOWN_START = (int) (NUM_SEGMENTS / 3.0); 38 | 39 | /** 40 | * Where downtown ends in this expressway. 41 | * This assumes every expressway has a 'downtown' section. 42 | * Set to 1/3 from the end of a 0-based expressway. 43 | */ 44 | public static final int DOWNTOWN_END = (int) (NUM_SEGMENTS * 2 / 3.0); 45 | 46 | /** 47 | * The default entrance lane. 48 | */ 49 | public static final int ENTRANCE_LANE = 0; 50 | 51 | /** 52 | * The number of lanes. Affects how many Cars can travel without adjustments to speed in a segment. 53 | */ 54 | public static final int NUM_LANES = 5; 55 | 56 | /** 57 | * The exit lane of an expressway. This will always be the highest numbered lane: 0-indexed. 58 | */ 59 | public static final int EXIT_LANE = NUM_LANES - 1; 60 | 61 | /** 62 | * The max possible speed in the simulation. 63 | */ 64 | public static final int MAX_SPEED = 100; 65 | 66 | /** 67 | * The max speed of cars in the entrance lane of an expressway. 68 | */ 69 | public static final int MAX_SPEED_ENTRANCE = 40; 70 | 71 | /** 72 | * The start speed of cars when they first appear in the entry lane, in an expressway, whether it be its first 73 | * appearance in the simulation or for a reentrant car. 74 | */ 75 | public static final int ENTRY_SPEED = 10; 76 | 77 | /** 78 | * The max speed of cars in the exit lane of an expressway. 79 | */ 80 | public static final int MAX_SPEED_EXIT = 10; 81 | 82 | /** 83 | * The given update interval for the simulation. Default 1 second/tick. Updates happen every UPDATE_INTERVAL. 84 | */ 85 | public static final int UPDATE_INTERVAL = 1; 86 | 87 | /** 88 | * This represents how often cars emit reports. 89 | */ 90 | public static final int TIME_INTERVAL = 30; 91 | 92 | /** 93 | * This represents the likelihood of an exiting Car of coming back onto an expressway later in the simulation. 94 | */ 95 | public static final double REENTRANT_PERCENT = 0.10; 96 | 97 | /** 98 | * This is the base number of cars we put into the expressway per UPDATE_INTERVAL. 99 | */ 100 | public static final int BASE_CARS_PER_UPDATE_INTERVAL_PER_XWAY = 20; 101 | 102 | /** 103 | * For creating the historical (type 3) file. Starts at day 1 because day 0 is the current day and should be 104 | * answered by type 2 queries. 105 | */ 106 | public static final int HISTORICAL_DAY_FIRST = 1; 107 | 108 | /** 109 | * For creating the historical (type 3) file. 69 represents 10 weeks. 110 | */ 111 | public static final int HISTORICAL_DAY_LAST = 69; 112 | 113 | /** 114 | * The target speed of cars traveling in travel lanes. 115 | */ 116 | public static final int SPEED_LIMIT = 70; 117 | 118 | /** 119 | * A small adjustment to speed while changing lanes. 120 | */ 121 | public static final int MAX_LANE_CHANGE_SPEED_DELTA = 5; 122 | 123 | /** 124 | * How often cars change lanes while traveling. 125 | */ 126 | public static final double TRAVEL_LANE_CHANGE_PROBABILITY = 0.4; 127 | 128 | /** 129 | * A little wiggle in speed to provide variety. 130 | */ 131 | public static final int SPEED_FUDGE = 10; 132 | 133 | /** 134 | * Acceleration of Cars while entering an expressway. 135 | * In Feet/sec^2 136 | */ 137 | public static final int ACCELERATION_ENTRANCE = 10; 138 | 139 | /** 140 | * Acceleration of Cars while traveling in travel lanes. 141 | * In Feet/sec^2 142 | */ 143 | public static final int ACCELERATION_TRAVEL = 5; 144 | 145 | /** 146 | * How many seconds to run the simulation: from [0 - SIM_LENGTH) 147 | */ 148 | public static final int SIM_LENGTH = 10800; 149 | 150 | /** 151 | * How many cars can safely fit into a single lane without any affects on traffic 152 | */ 153 | public static final int FULL_SPEED_CARS_PER_LANE = 24; 154 | 155 | /** 156 | * How long to wait from start of simulation, or since last Accident, before allowing an Accident 157 | * In minutes 158 | */ 159 | public static final int ACCIDENT_INTERVAL = 10; 160 | 161 | /** 162 | * How long to wait before potentially clearing an Accident 163 | * In minutes 164 | */ 165 | public static final int ACCIDENT_CLEAR_WAIT_TIME = 10; 166 | 167 | /** 168 | * Offset for where to place Cars in an expressway. Makes Cars avoid entering at the ends of the expressway. 169 | * So, for a 100 segment expressway cars going direction 0 will enter anywhere from [0 - 97], and cars going 170 | * direction 1 will enter anywhere from [99-2], if the ROUTE_BUFFER is 2. 171 | *

172 | * Also plays a part in deciding on the exit segment (Route.createExitSegIndex) as the exit lane will be at least 173 | * ROUTE_BUFFER distance away from the entrance segment. 174 | */ 175 | public static final int ROUTE_BUFFER = 2; 176 | 177 | /** 178 | * Should generally be set to >= 0.50 as it is multiplied by Math.random(). 179 | */ 180 | public static double CAR_MULTIPLIER_FACTOR = 0.8; 181 | 182 | 183 | /** 184 | * A general function to create a Route to place into a Car (move to 185 | */ 186 | public static Route createRoute() { 187 | 188 | // DEBUG 189 | //System.out.println("createRoute:Environment.NUM_XWAYS: " + Environment.NUM_XWAYS); 190 | // DEBUG END 191 | 192 | Random rand = new Random(); 193 | Route r; 194 | int dir = rand.nextInt(2); 195 | if (dir == 0) { 196 | r = new Route(rand.nextInt(Environment.NUM_SEGMENTS - Environment.ROUTE_BUFFER), dir, rand.nextInt(Environment.NUM_XWAYS)); 197 | } else { 198 | r = new Route(rand.nextInt(Environment.NUM_SEGMENTS - Environment.ROUTE_BUFFER) + Environment.ROUTE_BUFFER, dir, rand.nextInt(Environment.NUM_XWAYS)); 199 | } 200 | return r; 201 | } 202 | 203 | /** 204 | * The 3-d List of List of List of TrafficConditions to hold all the traffic conditions during the simulation. 205 | * Thus a TrafficCondition could also have been called a Segment, or it could have been embedded in a Segment. 206 | */ 207 | public static List>> trafficConditions; 208 | 209 | /** 210 | * The NUM_XWAYS can be changed via a command-line so use a static initialization function vs. a block. 211 | */ 212 | public static void initializeTrafficConditions() { 213 | // This is a NUM_XWAYS * NUM_SEGMENTS * NUM_DIRS 3-D array/List of TrafficCondition(s) 214 | int counter = 0; 215 | trafficConditions = new ArrayList<>(); 216 | for (int i = 0; i < Environment.NUM_XWAYS; i++) { 217 | List> xway = new ArrayList<>(); 218 | trafficConditions.add(xway); 219 | for (int j = 0; j < Environment.NUM_SEGMENTS; j++) { 220 | List seg = new ArrayList<>(); 221 | xway.add(seg); 222 | for (int k = 0; k < 2; k++) { // 2 being the number of directions 223 | TrafficCondition tc = new TrafficCondition(counter++); 224 | seg.add(tc); 225 | } 226 | } 227 | } 228 | 229 | // DEBUG 230 | counter = 0; 231 | for (int i = 0; i < Environment.NUM_XWAYS; i++) { 232 | for (int j = 0; j < Environment.NUM_SEGMENTS; j++) { 233 | for (int k = 0; k < 2; k++) { 234 | System.out.println(counter++ + ": " + trafficConditions.get(i).get(j).get(k)); 235 | } 236 | } 237 | } 238 | //System.exit(1); 239 | // DEBUG END 240 | } 241 | 242 | /** 243 | * Use this to track time within the simulation. 244 | * Not used for anything at the moment that can't be tracked with the loop variable of the main simulation loop. 245 | */ 246 | public static int simTime = 0; 247 | 248 | /** 249 | * Our separate list to hold XWay-specific data, which at this moment is only Accident presence and timings. 250 | */ 251 | public static List xways; 252 | public static void initializeXWays() { 253 | xways = new ArrayList<>(); 254 | for (int i = 0; i < NUM_XWAYS; i++) { 255 | xways.add(new XWay(i)); 256 | } 257 | } 258 | 259 | /** 260 | * Re-entrant cars: map to time they will re-enter and store the actual Car instances. 261 | */ 262 | public static Map> reentrants; 263 | public static void initializeReentrants() { 264 | reentrants = new HashMap<>(); 265 | } 266 | /** 267 | * Add a car to the reentrants array 268 | * 269 | * @param time 270 | * @param c 271 | */ 272 | public static void addToReentrants(int time, Car c) { 273 | synchronized (Environment.reentrants) { 274 | Environment.reentrants.putIfAbsent(time, new ArrayList<>()); 275 | Environment.reentrants.get(time).add(c); 276 | } 277 | } 278 | 279 | // TODO: Most of the settings should, or can, be moved to config or properties file. 280 | 281 | /** 282 | * A convenience function for converting minutes to seconds 283 | * 284 | * @param min 285 | * @return 286 | */ 287 | public static int minToSec(int min) { 288 | return min * 60; 289 | } 290 | 291 | 292 | } 293 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/LinearGen.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.PrintWriter; 6 | import java.util.*; 7 | 8 | /** 9 | * The main class that sets up the environment, creates the Cars, and moves everything along. 10 | *

11 | * Created by Sung Kim on 6/28/2016. 12 | */ 13 | public class LinearGen { 14 | 15 | /** 16 | * Determine how many cars should/can be introduced into the simulation at given seconds based on 17 | * how long the simulation is, smoothing out the number of cars to match a sin wave from 0 - 180 degrees. 18 | * We normalize based on how many segments are made from 180 degrees and the number seconds. 19 | * 20 | * @param time the simulation time, in seconds. I.e. 3 hours will be 10800 seconds. The total simulation time is 21 | * taken from Environment.SIM_LENGTH 22 | * @return the multiplier for the Environment.BASE_CARS_PER_UPDATE_INTERVAL_PER_XWAY 23 | */ 24 | private static double getCarMultiplier(int time) { 25 | double step = 180.0 / Environment.SIM_LENGTH; 26 | return Math.sin(Math.toRadians(time * step)) + 1 * (Environment.CAR_MULTIPLIER_FACTOR + Math.random()); 27 | } 28 | 29 | /** 30 | * Each expressway should have an accident roughly every 20 minutes, either direction. 31 | * The accident should clear in 10-20 minutes (600-1200 seconds). 32 | *

33 | * Stop one Car and then find a candidate car in the same xway+seg+dir by investigating the TrafficCondition 34 | * in which the Car is traveling. 35 | *

36 | * Checking for a car to see if it could be part of an Accident created Accidents at exactly the times it could. 37 | * So, run per tick of the clock per XWay, not per Car per tick. 38 | * The same Accident is redundantly stored in a TrafficCondition and an XWay. 39 | * This is silly. Why redundantly store the Accident? Just have traffic conditions refer to accidents in the Xways. 40 | * 41 | * @param cars the Cars List to investigate to see if an accident can be created with it and its xway-seg-dir 42 | * @param currTime the current world time 43 | */ 44 | private static void checkToCreateOrClearAccident(List cars, int currTime) { 45 | for (XWay xway : Environment.xways) { 46 | checkToCreateOrClearAccidentMT(xway.getXWayNumber(), cars, currTime); 47 | /* 48 | if (xway.hasAccident()) { 49 | // Remember, the Accident reference in the TrafficCondition is only for adjusting traffic patterns. 50 | //if (currAccDuration > Environment.minToSec(Environment.ACCIDENT_CLEAR_WAIT_TIME) + (Math.random() * Environment.minToSec(10))) { 51 | if (currTime > xway.getClearAccidentTime()) { 52 | Accident acc = xway.getAccident(); 53 | 54 | // DEBUG 55 | System.out.println("Accident cleared. " + xway.getXWayNumber() + "," + currTime + "," + acc.getC1().getId() + "," + acc.getC2().getId()); 56 | // DEBUG END 57 | 58 | clearAccident(xway.getXWayNumber(), currTime); 59 | // Loop through all xways to see if there is an Accident. 60 | } 61 | } 62 | 63 | // See if it's been long enough since the last Accident to potentially add an accident. 64 | if (currTime > xway.getNextAccidentTime() && !xway.hasAccident()) { 65 | 66 | // Loop through the cars in the simulation that is in this XWay 67 | // to see if one fits the bill to create an Accident. 68 | for (Iterator iter = cars.iterator(); iter.hasNext(); ) { 69 | Car c = iter.next(); 70 | 71 | if (c.hasBeenInAccident()) continue; 72 | 73 | int xwayIndex = c.getCurrXway(); 74 | if (xwayIndex != xway.getXWayNumber()) continue; 75 | int seg = c.getCurrSegment(); 76 | int dir = c.getCurrDir(); 77 | 78 | // Try creating an Accident. 79 | Accident acc = createAccident(xwayIndex, seg, dir, c); 80 | 81 | // DEBUG 82 | //System.out.println("Could have had accident. " + c.getId() + "," + c.getCurrTime() + "," + wiggle + "," + timeSinceAccidentForThisXway); 83 | // DEBUG END 84 | 85 | if (acc != null) { // Remember, there's no guarantee a suitable second car will be found. 86 | // If an Accident is created/creatable set it in the appropriate TrafficCondition AND XWay. 87 | xway.turnOnAccident(acc); 88 | 89 | // DEBUG 90 | System.out.println("Accident created. " + xway.getXWayNumber() + "," + currTime + "," + acc.getC1().getId() + "," + acc.getC2().getId()); 91 | System.out.println("Accident info: " + seg + "," + dir + "," + acc.getC1().getCurrPosition() + "," + acc.getC2().getCurrPosition()); 92 | // DEBUG END 93 | 94 | // Stop at one Accident for each expressway 95 | break; 96 | } 97 | } 98 | }*/ 99 | } 100 | } 101 | 102 | // MT 103 | private static void checkToCreateOrClearAccidentMT(int x, List cars, int currTime) { 104 | XWay xway = Environment.xways.get(x); 105 | if (xway.hasAccident()) { 106 | // Remember, the Accident reference in the TrafficCondition is only for adjusting traffic patterns. 107 | //if (currAccDuration > Environment.minToSec(Environment.ACCIDENT_CLEAR_WAIT_TIME) + (Math.random() * Environment.minToSec(10))) { 108 | if (currTime > xway.getClearAccidentTime()) { 109 | Accident acc = xway.getAccident(); 110 | 111 | // DEBUG 112 | System.out.println("Accident cleared. " + xway.getXWayNumber() + "," + currTime + "," + acc.getC1().getId() + "," + acc.getC2().getId()); 113 | // DEBUG END 114 | 115 | clearAccident(xway.getXWayNumber(), currTime); 116 | // Loop through all xways to see if there is an Accident. 117 | } 118 | } 119 | 120 | // See if it's been long enough since the last Accident to potentially add an accident. 121 | if (currTime > xway.getNextAccidentTime() && !xway.hasAccident()) { 122 | 123 | // Loop through the cars in the simulation that is in this XWay 124 | // to see if one fits the bill to create an Accident. 125 | for (Iterator iter = cars.iterator(); iter.hasNext(); ) { 126 | Car c = iter.next(); 127 | 128 | if (c.hasBeenInAccident()) continue; 129 | 130 | int xwayIndex = c.getCurrXway(); 131 | if (xwayIndex != xway.getXWayNumber()) continue; 132 | int seg = c.getCurrSegment(); 133 | int dir = c.getCurrDir(); 134 | 135 | // Try creating an Accident. 136 | Accident acc = createAccident(xwayIndex, seg, dir, c); 137 | 138 | // DEBUG 139 | //System.out.println("Could have had accident. " + c.getId() + "," + c.getCurrTime() + "," + wiggle + "," + timeSinceAccidentForThisXway); 140 | // DEBUG END 141 | 142 | if (acc != null) { // Remember, there's no guarantee a suitable second car will be found. 143 | // If an Accident is created/creatable set it in the appropriate TrafficCondition AND XWay. 144 | xway.turnOnAccident(acc); 145 | 146 | // DEBUG 147 | System.out.println("Accident created. " + xway.getXWayNumber() + "," + currTime + "," + acc.getC1().getId() + "," + acc.getC2().getId()); 148 | System.out.println("Accident info: " + seg + "," + dir + "," + acc.getC1().getCurrPosition() + "," + acc.getC2().getCurrPosition()); 149 | // DEBUG END 150 | 151 | // Stop at one Accident for each expressway 152 | break; 153 | } 154 | } 155 | } 156 | } 157 | // MT END 158 | 159 | /** 160 | * Set the current car as an Accident car. (A 'Stopped' Car.) 161 | * Find another car with this xway+seg+dir to stop at the same place the current car is stopped. 162 | * Update the XWay. 163 | * Update the associated TrafficCondition. 164 | *

165 | * Passing in the xway, seg, dir is redundant since the information is all found in a car anyways. 166 | * But, for some obsessive compulsive reason I'm passing them in anyways. 167 | * 168 | * @param xway the current expressway 169 | * @param seg the current segment 170 | * @param dir the current direction 171 | * @param c the Car that will be the "first" car in the Accident 172 | */ 173 | private static Accident createAccident(int xway, int seg, int dir, Car c) { 174 | Accident newAccident = null; 175 | if (!isInTravelLane(c)) { 176 | return null; 177 | } 178 | Car c2 = findCarWithin1000FeetOfCurrCar(xway, seg, dir, c); 179 | if (c2 != null) { // If there are no other cars that fit the bill no Accident can be created. 180 | c.stopCar(); 181 | c2.stopCar(); 182 | // Move c2, magically, to c's position and lane 183 | c2.targetStoppedCar(c); 184 | 185 | // Create a new Accident 186 | newAccident = new Accident(c, c2, c.getCurrTime(), seg, dir); 187 | } 188 | return newAccident; 189 | } 190 | 191 | /** 192 | * Find a car in the same xway+seg+dir, which is the same TrafficCondition. 193 | * Because a TrafficCondition holds an unordered Set of cars, this is inefficient at the moment. 194 | * 195 | * @param c 196 | * @return 197 | */ 198 | /* 199 | public static Car findCarInSegmentBeforeCurrCar(int xway, int seg, int dir, Car c) { 200 | TrafficCondition tc = Environment.trafficConditions.get(xway).get(seg).get(dir); 201 | Iterator cars = tc.getCarsIterator(); 202 | Car tempCar; 203 | while (cars.hasNext()) { 204 | tempCar = cars.next(); 205 | // Just pick the first one that fits the bill 206 | // In a travel lane 207 | // Before the first car in position (based on dir) 208 | if (carIsBeforeCar(c, tempCar) && isInTravelLane(tempCar)) { 209 | return tempCar; 210 | } 211 | } 212 | return null; 213 | }*/ 214 | 215 | /** 216 | * Find a car in the same xway+seg+dir, which is in the same TrafficCondition and within 1000 feet of the first. 217 | * And maybe this is better adjusted to have an argument rather than a set distance. 218 | * Because a TrafficCondition holds an unordered Set of cars, this is inefficient at the moment. 219 | * 220 | * @param c the Car to check 221 | * @return the Car that is behind and within 1000 feet of the first Car 222 | */ 223 | private static Car findCarWithin1000FeetOfCurrCar(int xway, int seg, int dir, Car c) { 224 | TrafficCondition tc = Environment.trafficConditions.get(xway).get(seg).get(dir); 225 | Iterator cars = tc.getCarsIterator(); 226 | Car tempCar; 227 | while (cars.hasNext()) { 228 | // Just pick the first one that fits the bill 229 | tempCar = cars.next(); 230 | if (c == tempCar) continue; 231 | // Is in a travel lane and 232 | // is within 1000 feet of the first car. 233 | if (carIsWithin1000FtOfCar(c, tempCar) && isInTravelLane(tempCar)) { 234 | return tempCar; 235 | } 236 | } 237 | return null; 238 | } 239 | 240 | 241 | /** 242 | * Helper function to check if one car is before another based on direction 243 | * Assumes that the two cars are in the same direction because they are in the same TrafficCondition. 244 | * 245 | * @param car1 246 | * @param car2 247 | * @return 248 | */ 249 | /* 250 | public static boolean carIsBeforeCar(Car car1, Car car2) { 251 | // Pull the direction from either car. 252 | int dir = car1.getCurrDir(); 253 | if (dir == 0) { 254 | if (car2.getCurrPosition() < car1.getCurrPosition()) { 255 | return true; 256 | } 257 | } else { 258 | if (car2.getCurrPosition() > car1.getCurrPosition()) { 259 | return true; 260 | } 261 | } 262 | return false; 263 | }*/ 264 | 265 | /** 266 | * Check to see if Car2 is within 1000 feet of Car1. 267 | * Car1 has to be ahead of the Car2. 268 | * 269 | * @param car1 the first car 270 | * @param car2 the second car 271 | * @return they are within 1000, non-inclusive 272 | */ 273 | private static boolean carIsWithin1000FtOfCar(Car car1, Car car2) { 274 | if (car1.getCurrDir() == 0) { 275 | if (car1.getCurrPosition() - car2.getCurrPosition() < 1000) { 276 | return true; 277 | } 278 | } else { 279 | if (car2.getCurrPosition() - car1.getCurrPosition() < 1000) { 280 | return true; 281 | } 282 | } 283 | return false; 284 | } 285 | 286 | /** 287 | * Ensure the car is not in the entrance or exit lane. 288 | * 289 | * @param c the Car 290 | * @return whether the Car is in a travel lane 291 | */ 292 | private static boolean isInTravelLane(Car c) { 293 | int currentLane = c.getCurrLane(); 294 | if (currentLane > 0 && currentLane < Environment.NUM_LANES - 1) { 295 | return true; 296 | } 297 | return false; 298 | } 299 | 300 | /** 301 | * Clearing an Accident essentially equates to allowing the two accident cars to move again. 302 | * Set the two accident cars so that they can start moving again--set the appropriate flags. 303 | * 304 | * @param xway the xway 305 | * @param currTime the current simulation time 306 | */ 307 | private static void clearAccident(int xway, int currTime) { 308 | Environment.xways.get(xway).clearAccident(currTime); 309 | } 310 | 311 | /** 312 | * A separate function to do possible additional processing. None at the moment. 313 | * 314 | * @param iter 315 | * @param c 316 | */ 317 | private static void removeCarFromActiveCars(Iterator iter, Car c) { 318 | iter.remove(); 319 | } 320 | 321 | /** 322 | * Create the toll file using the maxCarId. 323 | * Use all the type 3's created during data file creation to fix the toll file while it's being created. 324 | * 325 | * @param maxCarId the max car id 326 | * @param type3map a map of type 3 notifications to accurately map xways in the newly created toll file 327 | * @param fileName the name of the data output file that will have ".tolls.dat" appended to it 328 | * @param flagMap the map of command-line args, currently used to write to Hadoop directly if -h flag is present 329 | */ 330 | private static void createTollFile(int maxCarId, String fileName, Map type3map, Map flagMap) { 331 | int carId, day, toll, xway; 332 | Random rand = new Random(); 333 | PrintWriter writer = null; 334 | // Hadoop? 335 | if (flagMap != null && flagMap.containsKey("-h")) { 336 | ////////////////////////////////////////////////////////////////// 337 | // HDFS: comment out if not needed to remove hadoop dependencies 338 | /*String hdfsHost = flagMap.get("-h").substring(0, flagMap.get("-h").indexOf("/", 8)); 339 | System.out.println(hdfsHost); 340 | writer = HdfsWriter.getHdfsWriter(hdfsHost, flagMap.get("-h") + ".tolls.dat");*/ 341 | // HDFS: END 342 | ////////////////////////////////////////////////////////////////// 343 | } else { 344 | try { 345 | writer = new PrintWriter(fileName + ".tolls.dat"); 346 | } catch (FileNotFoundException e) { 347 | System.err.println("There was a problem creating the output file."); 348 | System.err.println(e); 349 | } 350 | } 351 | maxCarId++; // we want to include the max carId in the result set 352 | for (carId = 0; carId < maxCarId; carId++) { 353 | for (day = 1; day < Environment.HISTORICAL_DAY_LAST + 1; day++) { 354 | toll = rand.nextInt(90) + 10; 355 | xway = type3map.getOrDefault(carId + "-" + day, rand.nextInt(Environment.NUM_XWAYS)); 356 | writer.println(carId + "," + day + "," + xway + "," + toll); 357 | } 358 | } 359 | writer.close(); 360 | } 361 | 362 | /** 363 | * Use the default path/name for the data file or use a user-supplied path/name. 364 | * 365 | * @param argMap parsed and Mapped command-line arguments 366 | * @return the filename for the output file 367 | */ 368 | private static String getOutfileName(Map argMap) { 369 | if (argMap != null && argMap.containsKey("-o")) { 370 | return argMap.get("-o"); 371 | } else { 372 | return "car.dat"; 373 | } 374 | } 375 | 376 | /** 377 | * Pass in the parsed and Mapped command-line to get the PrintWriter, writing to the argument to -o or -h. 378 | * 379 | * @param argMap parsed and Mapped command-line arguments 380 | * @return a PrinterWriter pointing to a file based on the argument to -o or -h 381 | */ 382 | private static PrintWriter getWriter(Map argMap) { 383 | String defaultOutfile = "car.dat"; 384 | PrintWriter writer = null; 385 | if (argMap != null) { 386 | if (argMap.containsKey("-o")) { 387 | try { 388 | writer = new PrintWriter(argMap.get("-o")); 389 | } catch (FileNotFoundException e) { 390 | System.err.println("File " + argMap.get("-o") + " not found. Using the default of ./car.dat"); 391 | } 392 | } 393 | ////////////////////////////////////////////////////////////////// 394 | // HDFS: comment out if not needed to remove hadoop dependencies 395 | /*if (argMap.containsKey("-h")) { 396 | // DEBUG 397 | String hdfsHost = argMap.get("-h").substring(0, argMap.get("-h").indexOf("/", 8)); 398 | System.out.println(hdfsHost); 399 | // DEBUG END 400 | writer = HdfsWriter.getHdfsWriter(hdfsHost, argMap.get("-h")); 401 | }*/ 402 | // HDFS: END 403 | ////////////////////////////////////////////////////////////////// 404 | } 405 | // Just create the default out file 'car.dat' if possible and quit if it can't be created. 406 | if (writer == null) { 407 | try { 408 | writer = new PrintWriter(defaultOutfile); 409 | } catch (FileNotFoundException e) { 410 | System.err.println("File car.dat could not be created. Exiting."); 411 | System.exit(1); 412 | } 413 | } 414 | return writer; 415 | } 416 | 417 | /** 418 | * Override the default number of expressways. 419 | * 420 | * @param argMap 421 | */ 422 | private static void setNumberOfExpressways(Map argMap) { 423 | // Override default number of expressways? 424 | if (argMap != null && argMap.containsKey("-x")) { 425 | Environment.NUM_XWAYS = Integer.parseInt(argMap.get("-x")); 426 | // DEBUG 427 | System.out.println("Environment.NUM_XWAYS: " + Environment.NUM_XWAYS); 428 | // DEBUG END 429 | } 430 | } 431 | 432 | // MT 433 | 434 | private static class XWayRunnable implements Runnable { 435 | List cars; 436 | int t; // currTime 437 | int xway; 438 | Map type3map; 439 | PrintWriter writer; 440 | 441 | 442 | public XWayRunnable(int xway, int t, List cars, PrintWriter writer, Map type3map) { 443 | this.xway = xway; 444 | this.cars = cars; 445 | this.t = t; 446 | this.type3map = type3map; 447 | this.writer = writer; 448 | } 449 | 450 | @Override 451 | public void run() { 452 | Iterator iter = cars.iterator(); 453 | Car cr; 454 | while (iter.hasNext()) { 455 | cr = iter.next(); 456 | 457 | if (((cr.getCurrTime() - cr.getStartTime()) % Environment.TIME_INTERVAL == 0)) { 458 | // Emit pre-update to handle new Car creation and exits. 459 | // In other words, emit happens for creation or the previous change. 460 | String notification = cr.emit(); 461 | if (notification.contains("\n3")) { 462 | String[] tokens = notification.trim().split("\n3")[1].trim().split(","); 463 | synchronized (type3map) { 464 | type3map.put(cr.getId() + "-" + tokens[14], cr.getCurrXway()); 465 | } 466 | } 467 | synchronized (writer) { 468 | writer.print(notification); 469 | } 470 | 471 | // Take the time here to remove any cars that should be removed. 472 | if (cr.isRemoveable()) { 473 | removeCarFromActiveCars(iter, cr); 474 | } 475 | } 476 | // Check if you want to create an Accident with this Car in its xway-seg-dir-position 477 | //checkToCreateOrClearAccident(cr); 478 | 479 | cr.moveCar(); 480 | cr.updateTime(Environment.UPDATE_INTERVAL); 481 | } 482 | 483 | // Check to see if you can create an Accident 484 | checkToCreateOrClearAccidentMT(xway, cars, t); 485 | } 486 | } 487 | // MT END 488 | 489 | /** 490 | * Run the simulation to build the cars file. 491 | * 492 | * @param args 493 | */ 494 | public static void main(String[] args) { 495 | // 496 | long startTime = System.nanoTime(); 497 | 498 | // MT 499 | boolean MT = false; 500 | 501 | // Parse command-line args, if any. 502 | // Allow for command-line arguments for an outfile file but also to override some default settings 503 | // Only bother with non-default args if the number of command-line args are >= 2. 504 | Map argMap = CLParser.parseCommandLine(args); 505 | if (argMap.get("-o") != null) { 506 | MT = true; 507 | } 508 | 509 | // Process command-line args if any. 510 | String outfileName = getOutfileName(argMap); 511 | PrintWriter writer = getWriter(argMap); 512 | 513 | setNumberOfExpressways(argMap); 514 | 515 | // Initialize static Environment variables. 516 | Environment.initializeTrafficConditions(); 517 | Environment.initializeXWays(); 518 | Environment.initializeReentrants(); 519 | 520 | 521 | // The Map to hold type 3's. Key: carId-day ; Value: xway. Necessary to have accurate xways in the historical 522 | // toll file. 523 | Map type3map = new HashMap<>(); 524 | 525 | // Random generator to use throughout the main simulation. 526 | Random rand = new Random(); 527 | 528 | // We do removals from arbitrary positions, so we use a LinkedList, not an ArrayList. 529 | List cars = new LinkedList<>(); 530 | Route r; 531 | Car c; 532 | 533 | // For MT 534 | // We can create the data structure, but won't do anything with it if MT is not true. 535 | Map> xwayCars = new HashMap<>(); 536 | for (int i = 0; i < Environment.NUM_XWAYS; i++) { 537 | xwayCars.put(i, new LinkedList<>()); 538 | } 539 | // For MT END 540 | 541 | // Keep track of last assigned car id 542 | int carId = 0; 543 | 544 | // The iterator to iterate through cars in the simulation, once for each 'tick' or second. 545 | Iterator iter; 546 | // --------------------------------------- 547 | // THE MAIN SIMULATION LOOP 548 | // --------------------------------------- 549 | // For each second of the simulation insert BASE_CARS_PER_UPDATE_INTERVAL_PER_XWAY 550 | for (int i = 0; i < Environment.SIM_LENGTH; i++) { 551 | // DEBUG 552 | //System.out.println("carMultiplier: " + getCarMultiplier(i)); 553 | // END - DEBUG 554 | 555 | // Insert a random number of cars up to BASE_CARS_PER_UPDATE_INTERVAL_PER_XWAY. The multiplier follows a SIN curve from 556 | // 0 - 180, with amplitude 1 557 | for (int j = 0; j < getCarMultiplier(i) * Environment.BASE_CARS_PER_UPDATE_INTERVAL_PER_XWAY * Environment.NUM_XWAYS; j++) { 558 | // The range is effectively 2-97 so we don't get cars entering where they have no place to go but exit. 559 | r = Environment.createRoute(); 560 | c = new Car(carId++, r, i); 561 | // MT 562 | if (MT) { 563 | xwayCars.get(r.getXway()).add(c); 564 | } else { 565 | cars.add(c); 566 | } 567 | // MT END 568 | // The car will emit below on creation time because emission happens before update and movement. 569 | } 570 | 571 | // Insert any classifying re-entrant cars 572 | if (Environment.reentrants.containsKey(i)) { 573 | // If there's a key, there's also going to be a List of Cars as the value 574 | List reCars = Environment.reentrants.get(i); 575 | // Reset the cars and put them in new xways, segs, and directions 576 | for (Car rc : reCars) { 577 | // MT 578 | if (MT) { 579 | Car nc = rc.reEnter(i); 580 | xwayCars.get(nc.getCurrXway()).add(nc); 581 | } else { 582 | cars.add(rc.reEnter(i)); 583 | } 584 | // MT END 585 | } 586 | Environment.reentrants.remove(i); 587 | } 588 | 589 | // Insert a random shuffle to add some non-uniformity to the reports. 590 | 591 | // Target for multi-threading 592 | 593 | // Update all cars 594 | // MT 595 | if (MT) { 596 | List threads = new ArrayList<>(); 597 | for (int x : xwayCars.keySet()) { 598 | List listOfCars = xwayCars.get(x); 599 | //Thread t = new Thread(new XWayRunnable(x, i, listOfCars, writerMT, type3mapMT)); 600 | Thread t = new Thread(new XWayRunnable(x, i, listOfCars, writer, type3map)); 601 | t.start(); 602 | threads.add(t); 603 | } 604 | for (Thread t : threads) { 605 | try { 606 | t.join(); 607 | } catch (InterruptedException e) { 608 | System.err.println(e); 609 | System.err.println("Error occurred while thread joining."); 610 | System.exit(1); 611 | } 612 | } // MT END 613 | } else { 614 | iter = cars.iterator(); 615 | Car cr; 616 | while (iter.hasNext()) { 617 | cr = iter.next(); 618 | 619 | if (((cr.getCurrTime() - cr.getStartTime()) % Environment.TIME_INTERVAL == 0)) { 620 | // Emit pre-update to handle new Car creation and exits. 621 | // In other words, emit happens for creation or the previous change. 622 | String notification = cr.emit(); 623 | if (notification.contains("\n3")) { 624 | String[] tokens = notification.trim().split("\n3")[1].trim().split(","); 625 | type3map.put(cr.getId() + "-" + tokens[14], cr.getCurrXway()); 626 | } 627 | writer.print(notification); 628 | 629 | // Take the time here to remove any cars that should be removed. 630 | if (cr.isRemoveable()) { 631 | removeCarFromActiveCars(iter, cr); 632 | } 633 | } 634 | // Check if you want to create an Accident with this Car in its xway-seg-dir-position 635 | //checkToCreateOrClearAccident(cr); 636 | 637 | cr.moveCar(); 638 | cr.updateTime(Environment.UPDATE_INTERVAL); 639 | } 640 | 641 | // Check to see if you can create an Accident 642 | checkToCreateOrClearAccident(cars, i); 643 | } 644 | 645 | 646 | // Update all xway times 647 | //for (int j = 0; j < Environment.xways.size(); j++) { 648 | // Environment.xways.get(j).incrTime(); 649 | //} 650 | // real 10m55.576s 651 | // user 11m7.693s 652 | // sys 0m11.224s 653 | //for (XWay xway : Environment.xways) { 654 | // xway.incrTime(); 655 | //} 656 | // real 10m50.421s (2xway) 657 | // user 11m3.918s 658 | // sys 0m10.840s 659 | // Using forEach and lambda's _can_ be faster, but it isn't always the case. Java >=8. 660 | //Environment.xways.forEach(xway -> xway.incrTime()); 661 | // real 10m59.189s (2xway) 662 | // user 11m8.998s 663 | // sys 0m11.411s 664 | // Using the for each loop, available since Java 1.5 was marginally faster. 665 | 666 | // Update the world sim time 667 | Environment.simTime++; 668 | } 669 | writer.close(); 670 | System.out.println("All notification data created."); 671 | System.out.printf("Time to create %d xways: %f\n", Environment.NUM_XWAYS, (System.nanoTime() - startTime) / 1000000000.0); 672 | 673 | // Use the max carId to create the toll file here. 674 | System.out.println("Creating the toll file ..."); 675 | long tollStartTime = System.nanoTime(); 676 | createTollFile(carId, outfileName, type3map, argMap); 677 | System.out.printf("Time to create toll data: %f\n", (System.nanoTime() - tollStartTime) / 1000000000.0); 678 | 679 | System.out.printf("Total time to create all data: %f\n", (System.nanoTime() - startTime) / 1000000000.0); 680 | 681 | System.out.println("---REPORT---"); 682 | System.out.println("Number of cars created: " + carId); 683 | System.out.println("Number of queries created: " + Car.getQid()); 684 | System.out.println("Number of type 0's created: " + Car.getT0s()); 685 | System.out.println("Number of type 2's created: " + Car.getT2s()); 686 | System.out.println("Number of type 3's created: " + Car.getT3s()); 687 | System.out.println("Total Number of notifications created: " + Car.getTAll()); 688 | System.out.println("---REPORT END---"); 689 | } 690 | } 691 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/Route.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * The route a Car takes. 7 | *

8 | * A car will be assigned a route. 9 | * The route will determine where a car gets on, gets off, and its desired destination. 10 | * More details can be added to take into account a more complex simulation world. 11 | *

12 | * Created by Sung Kim on 6/28/2016. 13 | */ 14 | public class Route { 15 | private int entranceSegIndex; // 0-99 with Environment.NUM_SEGMENTS == 100 16 | private int exitSegIndex; // 0-99 - Same. This will be generated, based on direction and entranceSegIndex. 17 | private int dir; // The direction 18 | private int xway; 19 | 20 | // A true simulator would allow a car to change expressways right away, i.e. 405 to the 5 21 | public Route(int entranceSegIndex, int dir, int xway) { 22 | this.entranceSegIndex = entranceSegIndex; 23 | this.dir = dir; 24 | this.xway = xway; 25 | exitSegIndex = createExitSegIndex(); 26 | } 27 | 28 | /** 29 | * This does the meat of the calculations to determine a desired exit. 30 | * If the center is 'downtown' then you'll have more cars exiting there. Future, 'full-world' simulations can 31 | * take into account time-of-day to have flows heavier FROM downtown to 'suburbs' at appropriate times. 32 | * The segments closer to the xway boundaries/edges, i.e. closer to 0 and 99 if there are 100 segments, are the 33 | * suburbs. 34 | * And then, more cars can enter from the downtown after 5:00 pm and exit in a suburb. 35 | * 36 | * @return the exit segment number for this Route and the Car it will be embedded in 37 | */ 38 | private int createExitSegIndex() { 39 | Random r = new Random(); 40 | int exitSeg = -1; 41 | 42 | if (dir == 0) { 43 | exitSeg = r.nextInt(Environment.NUM_SEGMENTS - entranceSegIndex) + (entranceSegIndex + Environment.ROUTE_BUFFER); 44 | if (exitSeg > Environment.NUM_SEGMENTS - 1) exitSeg = Environment.NUM_SEGMENTS - 1; // No longer necessary. 45 | } else { 46 | exitSeg = r.nextInt(Environment.NUM_SEGMENTS - (Environment.NUM_SEGMENTS - entranceSegIndex) + 1) - Environment.ROUTE_BUFFER; 47 | if (exitSeg < 0) exitSeg = 0; // No longer necessary. 48 | } 49 | 50 | // Greater than 1/3 and less than 2/3 is 'downtown' and increases the odds of a car exiting in an segment that's in 'downtown' 51 | if (!isInDowntown() && isBeforeDowntown()) { 52 | //int chance = r.nextInt(100); // 0-99 53 | //if (chance < 80) { // Exit in downtown , 75% chance, 0-74 54 | if (Math.random() < .8) { 55 | exitSeg = r.nextInt(Environment.DOWNTOWN_END - Environment.DOWNTOWN_START) + Environment.DOWNTOWN_START; 56 | } 57 | } 58 | return exitSeg; 59 | } 60 | 61 | /** 62 | * What constitutes 'downtown' can be whichever segments are desired. At the moment it will be more 63 | * formulaic as the middle 1/3 of the available segments. 64 | * >= (middle third) <= is part of downtown. 65 | * 66 | * @return whether this segment is part of 'downtown' 67 | */ 68 | private boolean isInDowntown() { 69 | if (entranceSegIndex >= Environment.DOWNTOWN_START && 70 | entranceSegIndex <= Environment.DOWNTOWN_END) { 71 | return true; 72 | } 73 | return false; 74 | } 75 | 76 | /** 77 | * We only want to consider increasing the odds of getting off in downtown if we're 'before' downtown per our dir. 78 | * 79 | * @return 80 | */ 81 | private boolean isBeforeDowntown() { 82 | if (dir == 0 && entranceSegIndex < Environment.DOWNTOWN_START) return true; 83 | if (dir == 1 && entranceSegIndex > Environment.DOWNTOWN_END) return true; 84 | return false; 85 | } 86 | 87 | 88 | /** 89 | * @return the direction of the Route 90 | */ 91 | public int getDir() { 92 | return dir; 93 | } 94 | 95 | /** 96 | * @return the xway of the Route 97 | */ 98 | public int getXway() { 99 | return xway; 100 | } 101 | 102 | /** 103 | * @return the entrance segment of the Route 104 | */ 105 | public int getEntranceSegIndex() { 106 | return entranceSegIndex; 107 | } 108 | 109 | /** 110 | * @return the exit segment of the Route 111 | */ 112 | public int getExitSegIndex() { 113 | return exitSegIndex; 114 | } 115 | 116 | 117 | /** 118 | * Test Route 119 | * Created by Sung Kim on 6/28/2016. 120 | */ 121 | public static void main(String[] args) { 122 | // Test Route 123 | Route r = new Route(0, 0, 0); 124 | assert r.isBeforeDowntown() : "Route seg 0, dir 0 isBeforeDowntown failed when before downtown. Dir 0."; 125 | r = new Route(99, 0, 0); 126 | assert !r.isBeforeDowntown() : "Route seg 99, dir 0 !isBeforeDowntown failed when actually after downtown. Dir 0"; 127 | r = new Route(99, 1, 0); 128 | assert r.isBeforeDowntown() : "Route seg 99, dir 1 isBeforeDowntown failed when actually after downtown. Dir 1"; 129 | r = new Route(0, 1, 0); 130 | assert !r.isBeforeDowntown() : "Route seg 0, dir 1 !isBeforeDowntown failed when actually after downtown. Dir 1"; 131 | r = new Route(33, 0, 0); 132 | assert !r.isBeforeDowntown() : "Route seg 33, dir 0 !isBeforeDowntown failed when actually is in downtown. Dir 0"; 133 | assert r.isInDowntown() : "Route seg 33, dir 0 isInDowntown() failed."; 134 | r = new Route(66, 0, 0); 135 | assert !r.isBeforeDowntown() : "Route seg 66, dir 0 !isBeforeDowntown failed when actually is in downtown. Dir 0"; 136 | assert r.isInDowntown() : "Route seg 66, dir 0 isInDowntown() failed."; 137 | r = new Route(33, 1, 0); 138 | assert !r.isBeforeDowntown() : "Route !isBeforeDowntown failed when actually is in downtown. Dir 1"; 139 | assert r.isInDowntown() : "Route seg 66, dir 1 isInDowntown() failed."; 140 | r = new Route(66, 1, 0); 141 | assert !r.isBeforeDowntown() : "Route !isBeforeDowntown failed when actually is in downtown. Dir 1"; 142 | assert r.isInDowntown() : "Route seg 66, dir 1 isInDowntown() failed."; 143 | r = new Route(32, 0, 0); 144 | assert r.isBeforeDowntown() : "Route seg 32, dir 0 isBeforeDowntown failed."; 145 | assert !r.isInDowntown() : "Route seg 32, dir 0 !isInDowntown failed!"; 146 | r = new Route(67, 0, 0); 147 | assert !r.isBeforeDowntown() : "Route seg 67, dir 0 !isBeforeDowntown failed."; 148 | assert !r.isInDowntown() : "Route seg 67, dir 0 !isInDowntown failed!"; 149 | r = new Route(32, 1, 0); 150 | assert !r.isBeforeDowntown() : "Route seg 32, dir 1 isBeforeDowntown failed."; 151 | assert !r.isInDowntown() : "Route seg 32, dir 1 !isInDowntown failed!"; 152 | r = new Route(67, 1, 0); 153 | assert r.isBeforeDowntown() : "Route seg 67, dir 1 isBeforeDowntown failed."; 154 | assert !r.isInDowntown() : "Route seg 67, dir 0 !isInDowntown failed!"; 155 | r = new Route(Environment.NUM_SEGMENTS - 1, 0, 0); 156 | assert r.createExitSegIndex() == Environment.NUM_SEGMENTS - 1 : "Route seg (n_segs-1), dir 0 does not have exit seg index of (n_segs-1)"; 157 | r = new Route(0, 1, 0); 158 | assert r.createExitSegIndex() == 0 : "Route seg 0, dir 1 does not have exit seg index of 0"; 159 | 160 | for (int i = 0; i < Environment.NUM_SEGMENTS; i++) { 161 | int dir = (Math.random() < 0.5 ? 0 : 1); 162 | r = new Route(i, dir, 0); 163 | System.out.println(i + "," + r.isInDowntown() + "," + dir + "," + r.createExitSegIndex()); 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/TrafficCondition.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | import java.util.HashSet; 4 | import java.util.Iterator; 5 | import java.util.Set; 6 | 7 | /** 8 | * Routes will read TrafficCondition to determine how Routes may need to be adjusted. 9 | * TrafficCondition will take Accidents and number of cars into account. 10 | * TrafficCondition's are assigned to a three-dimensional Environment.trafficConditions List>>. 11 | * The levels of embedding for the List are xway -> segment -> dir. 12 | *

13 | * TODO: If there's an accident in a TrafficCondition then: 14 | * cars will slow down in the prior segment and in the accident-segment, 15 | * a random number of cars will exit, 5%, 16 | *

17 | * A TrafficCondition is: 18 | * 1) The number of cars in a segment 19 | * 2) Whether there's an accident 20 | * 21 | * Created by Sung Kim on 6/28/2016. 22 | */ 23 | public class TrafficCondition { 24 | 25 | /** 26 | * Required to facilitate checking of cars moving from one segment to another. 27 | */ 28 | private int id; 29 | 30 | /** 31 | * XWay of this TrafficCondition. 32 | * Simply retrieve from a Car. 33 | */ 34 | //private int xway; 35 | 36 | /** 37 | * Necessary if speeds and car behaviors will be altered by the presence of an Accident 38 | */ 39 | //private Accident accident; 40 | // Look up an accident, do not hold a reference to the Accident. 41 | 42 | /** 43 | * The Cars in this xway-seg-dir. 44 | */ 45 | private Set cars; 46 | 47 | /** 48 | * Create a new TrafficCondition. 49 | * 50 | * @param id 51 | */ 52 | public TrafficCondition(int id) {//}, int xway) { 53 | this.id = id; 54 | //setAccident(null); 55 | cars = new HashSet<>(); 56 | } 57 | 58 | /** 59 | * 60 | * @return return the id of this TrafficCondition 61 | */ 62 | public int getId() { 63 | return id; 64 | } 65 | 66 | /** 67 | * Backed by a Set<>. 68 | * 69 | * @return return the number of Cars in this TrafficCondition 70 | */ 71 | public int getNumCars() { 72 | return cars.size(); 73 | } 74 | 75 | /** 76 | * 77 | * @param c the Car to be potentially added to the Set of Cars in this TrafficCondition 78 | */ 79 | public void addCar(Car c) { 80 | cars.add(c); 81 | } 82 | 83 | /** 84 | * We remove a Car when it leaves the 'seg-dir' connected to this TrafficCondition. 85 | * Again, the seg-dir is represented by the holding List Environment.trafficConditions, which is a 3-d List 86 | * as described above. 87 | * 88 | * @param c the Car to be removed from the Set of Cars in this TrafficCondition 89 | * @return whether the car was found and removed 90 | */ 91 | public boolean removeCar(Car c) { 92 | return cars.remove(c); 93 | } 94 | 95 | /** 96 | * 97 | * @return an Iterator over the Set of Cars 98 | */ 99 | public Iterator getCarsIterator() { 100 | return cars.iterator(); 101 | } 102 | 103 | /** 104 | * We store an Accident to be able to adjust traffic, taking the Accident into account. 105 | * We simply look up an accident in the current Xway 106 | * 107 | * @param accident the Accident instance to assign to this TrafficCondition 108 | */ 109 | //public void setAccident(Accident accident) { 110 | // this.accident = accident; 111 | //} 112 | 113 | /** 114 | * Unless we return the accident related to the XWay we don't return anything 115 | * @return this TrafficConditions Accident, if any 116 | * 117 | */ 118 | //public Accident getAccident() { 119 | //return accident; 120 | //} 121 | 122 | /** 123 | * Clear an accident. 124 | * 125 | * TODO?: Don't we want to remove cars from the simulation? if they were in an accident? 126 | * No, it could have simply been a fender bender... 127 | */ 128 | //public void clearAccident() { 129 | // Allow the two involved Cars to move again. 130 | // cars.forEach(c -> { 131 | // if (c.equals(accident.getC1()) || c.equals(accident.getC2())) { 132 | // c.startCar(); 133 | // } 134 | //}); 135 | //setAccident(null); 136 | //} 137 | 138 | /** 139 | * Remember, a TrafficCondition maps to a seg-dir in the simulation. 140 | * 141 | * @return whether there is an active Accident in this TrafficCondition 142 | */ 143 | public boolean hasAccident() { 144 | for(Car c : cars) { 145 | return Environment.xways.get(c.getCurrXway()).hasAccident(); 146 | } 147 | return false; 148 | } 149 | 150 | 151 | /** 152 | * 153 | * @return a String representation of this TrafficCondition. Simply the number of cars and whether there's an 154 | * Accident 155 | */ 156 | @Override 157 | public String toString() { 158 | return "numCars:" + getNumCars() + ", hasAccident: " + hasAccident(); 159 | } 160 | 161 | /** 162 | * Set equality of TrafficConditions based on its id. 163 | * 164 | * @param o the other TrafficCondition 165 | * @return whether the cars are the same object or have the same id 166 | */ 167 | @Override 168 | public boolean equals(Object o) { 169 | if (this == o) { 170 | return true; 171 | } 172 | if (o instanceof TrafficCondition) { 173 | TrafficCondition that = (TrafficCondition) o; 174 | if (that.id == id) { 175 | return true; 176 | } 177 | } 178 | return false; 179 | } 180 | 181 | /** 182 | * The hash code will simply the TrafficCondition's id. 183 | * 184 | * @return the TrafficConditions's id 185 | */ 186 | @Override 187 | public int hashCode() { 188 | return id; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/com/walmart/linearroad/generator/XWay.java: -------------------------------------------------------------------------------- 1 | package com.walmart.linearroad.generator; 2 | 3 | /** 4 | * The XWay 5 | * This ends up as a Convenience container, primarily for tracking Accidents. 6 | * 7 | * Created by Sung Kim on 6/28/2016. 8 | */ 9 | public class XWay { 10 | 11 | /** 12 | * The XWay number. 13 | */ 14 | private int id; 15 | 16 | /** 17 | * The time since the last Accident. Necessary to track how often/when to make this XWay eligible for another 18 | * Accident. 19 | */ 20 | private int timeSinceLastAccident; 21 | 22 | /** 23 | * How long this XWay has had its current Accident. Necessary to track when to clear an Accident. 24 | */ 25 | private int timeWithAccident; 26 | 27 | /** 28 | * Whether the XWay has an Accident or not. 29 | * There will only be one accident per XWay at any given time. 30 | */ 31 | //private boolean hasAccident; 32 | 33 | /** 34 | * The Accident associated with this XWay. 35 | */ 36 | private Accident accident; 37 | 38 | /** 39 | * The time of the next Accident, with some wiggle room. 40 | */ 41 | private int nextAccidentTime; 42 | 43 | /** 44 | * The time to clear the accident, with some wiggle room. 45 | */ 46 | private int clearAccidentTime; 47 | 48 | /** 49 | * 50 | * @param xway number externally assigned. There are no checks for previous existence 51 | */ 52 | public XWay(int xwayNumber) { 53 | id = xwayNumber; 54 | //clearAccident(0); 55 | accident = null; 56 | nextAccidentTime = createNextAccidentTime(Environment.ACCIDENT_INTERVAL); 57 | } 58 | 59 | /** 60 | * 61 | * @param base the number of minutes to wait before being a candidate for an Accident 62 | * @return the time around which to create the Accident 63 | */ 64 | private int createNextAccidentTime(int base) { 65 | return (int)((Environment.minToSec(base) + (Math.random() < 0.5 ? (Math.random() * Environment.minToSec(5)) : -(Math.random() * Environment.minToSec(5))))); 66 | } 67 | 68 | /** 69 | * 70 | * @param base the number of minutes to wait before clearing an Accident 71 | * @return the time around which to clear the Accident 72 | */ 73 | private int createAccidentClearTime(int base) { 74 | return (int)((Environment.minToSec(base) + (Math.random() < 0.5 ? (Math.random() * Environment.minToSec(5)) : -(Math.random() * Environment.minToSec(5))))); 75 | } 76 | 77 | /** 78 | * @return the time set for the next Accident 79 | */ 80 | public int getNextAccidentTime() { 81 | return nextAccidentTime; 82 | } 83 | 84 | public int getClearAccidentTime() { 85 | return clearAccidentTime; 86 | } 87 | 88 | /** 89 | * 90 | * @return return the XWay number 91 | */ 92 | public int getXWayNumber() { 93 | return id; 94 | } 95 | 96 | /** 97 | * 98 | * @return the Accident, if it exists, otherwise null 99 | */ 100 | public Accident getAccident() { 101 | return accident; 102 | } 103 | /** 104 | * Set this XWay as having an Accident. 105 | */ 106 | public void turnOnAccident(Accident acc) { 107 | //hasAccident = true; 108 | accident = acc; 109 | clearAccidentTime = acc.getTime() + createAccidentClearTime(Environment.ACCIDENT_CLEAR_WAIT_TIME); 110 | // DEBUG 111 | System.out.println("Accident to-be-cleared time at " + clearAccidentTime); 112 | // DEBUG END 113 | //timeWithAccident = 0; 114 | } 115 | 116 | /** 117 | * Increment the time of this XWay with or without an Accident. 118 | */ 119 | public void incrTime() { 120 | //incrTimeSinceLastAccident(); 121 | //incrTimeWithAccident(); 122 | } 123 | 124 | /** 125 | * Increment the time with an Accident. 126 | */ 127 | /*private void incrTimeWithAccident() { 128 | if (hasAccident()) { 129 | timeWithAccident++; 130 | } 131 | }*/ 132 | 133 | /** 134 | * Increment the time without an Accident. 135 | */ 136 | /*private void incrTimeSinceLastAccident() { 137 | if (!hasAccident()) { 138 | timeSinceLastAccident++; 139 | } 140 | }*/ 141 | 142 | /** 143 | * @return Return the amount of time since this XWay has had an Accident. 0 if no Accident exists. 144 | */ 145 | public int getTimeWithAccident() { 146 | return timeWithAccident; 147 | } 148 | 149 | /** 150 | * 151 | * @return Return the amount of time since the last Accident was cleared. 152 | */ 153 | public int getTimeSinceLastAccident() { 154 | return timeSinceLastAccident; 155 | } 156 | 157 | /** 158 | * 159 | * @return returns whether this Car has an Accident 160 | */ 161 | public boolean hasAccident() { 162 | return accident != null; 163 | } 164 | 165 | /** 166 | * Clear the Accident in this XWay. 167 | * Clearing Accidents must be done in both the XWay and the TrafficCondition. 168 | * 169 | * @param currTime the current time, which is needed to add a random time for the _next_ accident 170 | */ 171 | public void clearAccident(int currTime) { 172 | accident.getC1().startCar(); 173 | accident.getC2().startCar(); 174 | 175 | //resetTimeSinceLastAccident(); 176 | // We don't base the next Accident on the sim time but instead the time since the accident was last cleared. 177 | nextAccidentTime = currTime + createNextAccidentTime(Environment.ACCIDENT_INTERVAL); 178 | accident = null; 179 | 180 | // DEBUG 181 | System.out.println("xway: " + id + ", nextAccidentTime at " + nextAccidentTime); 182 | // DEBUG END 183 | } 184 | 185 | /** 186 | * Reset how long it's been since the last Accident. 187 | */ 188 | public void resetTimeSinceLastAccident() { 189 | //timeSinceLastAccident = 0; 190 | } 191 | 192 | /** 193 | * Set equality of XWay based on its id. 194 | * 195 | * @param o the other XWay 196 | * @return whether the cars are the same object or have the same id 197 | */ 198 | @Override 199 | public boolean equals(Object o) { 200 | if (this == o) { 201 | return true; 202 | } 203 | if (o instanceof XWay) { 204 | XWay that = (XWay) o; 205 | if (that.id == id) { 206 | return true; 207 | } 208 | } 209 | return false; 210 | } 211 | 212 | /** 213 | * The hash code will simply the TrafficCondition's id. 214 | * 215 | * @return the XWay's id 216 | */ 217 | @Override 218 | public int hashCode() { 219 | return id; 220 | } 221 | } 222 | --------------------------------------------------------------------------------