/* * TRAFFIC JAM SIMULATION v1.0 * * This simple model is just a quick experiment to check the * model and behaviour suggested by Mitchel Resnick (MIT Media Lab) * in the paper titled 'Learning About Life'. * * The emergent behaviour of this decentralized system of cars is to * form 'traffic jams' which move *backward* considering the direction * of the cars itselves. * * This quick experimental simulation produces the expected behavior which * is best seen with the NumOfLanes (number of lanes on the road) parameter is * set to one. * * Written by Gulya, 30/07/1998 * (c) 1998. CEU Systems Laboratory */ /* * * Possible Further Directions: (D R E A M S) * * 1) (Technical) To set the initial distribution through * some introductionary cycles so that every car starts from * the start line but not at the same time (thus letting the * 'older ones' to move away. * * 2) To introduce the capability of changing lanes. * * 3) To introduce real roads and behaviour to follow them. * * 4) After 3) to introduce the capability of turning. * * 5) To introduce the notion of 'goal' as a place to get, so that * cars would direct themself somewhere instead of just wandering * around. * * 6) And so this whole system could be grounded in real world urban data. */ $define MAXSPEED : "5"; /////////////////////////////////////////////////////////////////////////// @model Traffic_Jam { @var: int NumOfCars; // Number of cars @var: int NumOfLanes; // Number of lanes @var: int RouteLength; // The length of the route /////////////////////////////////////////////////////////////////////////// @agent Car { @var public: int lane; // The lane the car is in @var public: int distance; // Distance from the start // Note that this will be mapped into // the physical route's range by the model @var public: int speed; // Speed (between 0 and MAXSPEED) // To be performed at every timestep @sub: (void) forward { int i, ok; // Checks whether it must immediately stop // (That is if there anybody in the distance it will // cover in this step /depending on its speed/) ok = 0; i = distance+1; while ((ok==0) && (i<=distance+speed)) { ok = [model isCarAtLane: lane Distance: i]; i++; } // If there is an 'obstacle' ahead of it, it will // just stop behind it if (ok > 0) speed = i - distance - 2; // Then it moves [model removeCarFromLane: lane Distance: distance]; distance += speed; [model putCarToLane: lane Distance: distance]; // After moving it looks forward and decides to speed up or to slow // down, according to the situation if (ok > 0) // If it had had to stop speed = 0; // then sets speed to 0 else { ok = 0; // Otherwise checks wether the speed // can be increased by one // (It uses the same 'obstacle checking' // algorithm as above.) i = distance+1; while ((ok==0) && (i<=distance+speed+1)) { ok = [model isCarAtLane: lane Distance: i]; i++; } // It there is no 'obstacle', increases the speed by 1 if (ok==0) speed++; else { // Otherwise, if there is an 'obstacle' which would cause // a hard stop, it decreases the speed by 1 if (i<=distance+speed+1) speed--; } } // It keeps itself within the range if (speed < 0) speed = 0; if (speed > MAXSPEED) speed = MAXSPEED; } } /////////////////////////////////////////////////////////////////////////// @var: [] [] int route; // The array of the 'physical' route // These subroutines administrate the positions of the cars on the // 'physical' road map. Note, that the positions are mapped in to // the 'physical range' of the road. @sub: (void) removeCarFromLane: (int) l Distance: (int) d { l = l % NumOfLanes; d = d % RouteLength; route[l][d]--; } @sub: (void) putCarToLane: (int) l Distance: (int) d { l = l % NumOfLanes; d = d % RouteLength; route[l][d]++; } @sub: (int) isCarAtLane: (int) l Distance: (int) d { l = l % NumOfLanes; d = d % RouteLength; return route[l][d]; } /////////////////////////////////////////////////////////////////////////// @schedule cyclic (1) { 0: @forEach groupOfCar forward; } /////////////////////////////////////////////////////////////////////////// @init: // Create and initialize the 'physical' road @create [NumOfLanes:i][RouteLength:j] int route { route[i][j] = 0; } // Create and initialize the cars { int i, l, d; Car *dummy; for (i=0; ispeed = [uniformIntRand getIntegerWithMin: 1 withMax: MAXSPEED]; do { // Random initial location // (These ugly branches are needed since the RNG does not // accept min to be equal to max.) if (NumOfLanes == 1) l = 0; else l = [uniformIntRand getIntegerWithMin: 0 withMax: NumOfLanes-1]; if (RouteLength == 1) d = 0; else d = [uniformIntRand getIntegerWithMin: 0 withMax: RouteLength-1]; // If the selected location is already occupied, then try // it again. // NOTE THAT IT LEADS TO AN INFINITE LOOP IF THERE ARE MORE CARS // THAN POSSIBLE LOCATIONS!!!! } while ([self isCarAtLane: l Distance: d] > 0); // Set the location of the car, and record it on the // 'physical' map too. dummy->lane = l; dummy->distance = d; [self putCarToLane: l Distance: d]; } } } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// @observe Traffic_Jam { @uses ; // Variables to be probed @probe: var "NumOfLanes", var "RouteLength", var "NumOfCars", var "zoomFactor"; /////////////////////////////////////////////////////////////////////////// @var: int zoomFactor; // Drives the size of the Display @var: id colormap; // Color scheme @var: id routeRaster; // The display @var: int displayOffset; // Offset to center the road on the // display which must be square. // Updates the displat (draws the cars) @sub: (void) updateRoute { int i, j; for (i=0; i NumOfLanes) [routeRaster setWidth: RouteLength Height: RouteLength]; else [routeRaster setWidth: NumOfLanes Height: NumOfLanes]; [routeRaster setWindowTitle: "The Road..."]; [routeRaster pack]; // Calculates the offset to center the road on the display displayOffset = (RouteLength > NumOfLanes) ? (RouteLength - NumOfLanes) /2 : 0; [routeRaster erase]; // Displays the road at time 0... [self updateRoute]; [routeRaster drawSelf]; [controlPanel setStateStopped]; // and stops the simulation for the // display to be visible. // NOTE THAT THIS RESULTS IN A STOP // AFTER THE FIRST HIT OF 'GO' OR // 'TIMESTEP' -- SO 'GO' SHOULD BE // HIT TWICE AT THE BEGINNING }