UNIX> sh /home/jplank/cs302/Labs/Lab8/start_lab.shThen write src/city_map.cpp. The makefile will compile.
Street Avenue X Y Green-Street Green-Avenue |
Street and Avenue are integers. The rest are doubles. The units of X and Y are miles, and the units of Green-Street and Green-Avenue are seconds.
Intersections may be specified in any order.
So, here are some example cities:
city-1.txt
0 0 1.3 1 9 12 0 1 1.0 1 3 4 |
|
Since light [0,0]'s coordinates are (1.3,1) and light [0,1]'s coordinates are (1,1), the distance between the two lights is 0.3 miles. Note that since 0 is a multiple of five, Street zero and Avenue zero are two-way. Moreover, since Avenue one is the highest avenue, it is two-way.
city-2.txt
1 0 1.3 1.4 9 12 1 1 1 1.4 11 7 2 0 1.3 1.8 9 12 1 2 0.7 1.4 3 3 0 1 1 1 3 3 2 1 1 2.2 15 7 2 2 0.7 2.2 6 15 0 2 0.7 1 3 3 0 0 1.3 1 9 12 |
|
Now, Streets 1 and 2 are one-way, as is Avenue 1. The distances between intersections are straightforward, with the exception of [2,0] and [2,1]. That segment has a distance of 0.5, since sqrt(0.32+0.42) equals five.
Note also that the intersections are specified in a more random order here.
city-3.txt
0 0 0.5 0.1 9 12 0 1 0.4 0.1 1 110 0 2 0.3 0.1 110 1 0 3 0.2 0.1 110 1 0 4 0.1 0.1 110 1 1 0 0.5 0.2 1 110 1 1 0.4 0.2 110 1 1 2 0.3 0.2 110 1 1 3 0.2 0.2 1 110 1 4 0.1 0.2 1 110 2 0 0.5 0.3 1 110 2 1 0.4 0.3 110 1 2 2 0.3 0.3 110 1 2 3 0.2 0.3 110 1 2 4 0.1 0.3 1 110 |
|
usage: city_map none|best|worst|avg time|print|jgraph - map on standard input |
In other words, there are two command line arguments. The first has one of four values:
The second command line has one of three values:
UNIX> bin/city_map none print < city-1.txt 0 : Intersection: 0 0 - 1.300000 1.000000 - 9.000000 12.000000 1 : Segment to 0 1 Distance 0.300000 2 : Intersection: 0 1 - 1.000000 1.000000 - 3.000000 4.000000 3 : Segment to 0 0 Distance 0.300000 UNIX> bin/city_map none print < city-2.txt 0 : Intersection: 1 0 - 1.300000 1.400000 - 9.000000 12.000000 1 : Segment to 2 0 Distance 0.400000 1 : Segment to 0 0 Distance 0.400000 2 : Intersection: 1 1 - 1.000000 1.400000 - 11.000000 7.000000 3 : Segment to 1 0 Distance 0.300000 3 : Segment to 0 1 Distance 0.400000 4 : Intersection: 2 0 - 1.300000 1.800000 - 9.000000 12.000000 5 : Segment to 2 1 Distance 0.500000 5 : Segment to 1 0 Distance 0.400000 6 : Intersection: 1 2 - 0.700000 1.400000 - 3.000000 3.000000 7 : Segment to 1 1 Distance 0.300000 7 : Segment to 2 2 Distance 0.800000 7 : Segment to 0 2 Distance 0.400000 8 : Intersection: 0 1 - 1.000000 1.000000 - 3.000000 3.000000 9 : Segment to 0 2 Distance 0.300000 9 : Segment to 0 0 Distance 0.300000 10 : Intersection: 2 1 - 1.000000 2.200000 - 15.000000 7.000000 11 : Segment to 2 2 Distance 0.300000 11 : Segment to 1 1 Distance 0.800000 12 : Intersection: 2 2 - 0.700000 2.200000 - 6.000000 15.000000 13 : Segment to 1 2 Distance 0.800000 14 : Intersection: 0 2 - 0.700000 1.000000 - 3.000000 3.000000 15 : Segment to 0 1 Distance 0.300000 15 : Segment to 1 2 Distance 0.400000 16 : Intersection: 0 0 - 1.300000 1.000000 - 9.000000 12.000000 17 : Segment to 0 1 Distance 0.300000 17 : Segment to 1 0 Distance 0.400000 UNIX>Now, let's try some shortest path calculations:
UNIX> bin/city_map best time < city-1.txt 36 UNIX> bin/city_map worst time < city-1.txt 40 UNIX> bin/city_map avg time < city-1.txt 37.1429 UNIX>The distance between the two lights in city-1.txt is 0.3 miles, which takes 36 seconds at 30 MPH: 0.3 / 30 * 3600 = 36. Therefore the best time is 36 seconds. The worst time adds four seconds because in the worst case, you arrive at the light as it turns green for Avenue 1. You wait four seconds before the light turns green for you again. The average case adds 1.1429 seconds to the best case. This is because 3/7 of the time, the light is green. The other 4/7 of the time, the light is red for an average of 2 seconds. Thus, your expected wait time is 0*(3/7) + 2*(4/7) = 8/7 = 1.1429 seconds. Note, that is also equal to the equation I gave: 42/(2(3+4)) = 8/7.
We print the paths below:
UNIX> bin/city_map best print < city-1.txt 0 : Intersection: 0 0 - 1.300000 1.000000 - 9.000000 12.000000 1 : Segment to 0 1 Distance 0.300000 2 : Intersection: 0 1 - 1.000000 1.000000 - 3.000000 4.000000 3 : Segment to 0 0 Distance 0.300000 4 : PATH: [0000,0000] -> [0000,0001] - Time: 36.000000 UNIX> bin/city_map worst print < city-1.txt | grep PATH 4 : PATH: [0000,0000] -> [0000,0001] - Time: 40.000000 UNIX> bin/city_map avg print < city-1.txt | grep PATH 4 : PATH: [0000,0000] -> [0000,0001] - Time: 37.142857 UNIX>Let's look at the paths in city-2.txt:
UNIX> bin/city_map best print < city-2.txt | grep PATH 18 : PATH: [0000,0000] -> [0001,0000] - Time: 48.000000 19 : PATH: [0001,0000] -> [0002,0000] - Time: 96.000000 20 : PATH: [0002,0000] -> [0002,0001] - Time: 156.000000 21 : PATH: [0002,0001] -> [0002,0002] - Time: 192.000000 UNIX> bin/city_map worst print < city-2.txt | grep PATH 18 : PATH: [0000,0000] -> [0000,0001] - Time: 39.000000 19 : PATH: [0000,0001] -> [0000,0002] - Time: 78.000000 20 : PATH: [0000,0002] -> [0001,0002] - Time: 129.000000 21 : PATH: [0001,0002] -> [0002,0002] - Time: 231.000000 UNIX> bin/city_map avg print < city-2.txt | grep PATH 18 : PATH: [0000,0000] -> [0001,0000] - Time: 49.928571 19 : PATH: [0001,0000] -> [0002,0000] - Time: 99.857143 20 : PATH: [0002,0000] -> [0002,0001] - Time: 160.970779 21 : PATH: [0002,0001] -> [0002,0002] - Time: 202.327922 UNIX>Obviously, when all lights are green, the fastest path is the one going from light [2,0] to [2,1], since that cuts distance. That path goes (0.4 + 0.4 + 0.5 + 0.3) = 1.6 miles (192 seconds at 30MPH), while the path that travels along Street 0 to Avenue 2 goes (0.3 + 0.3 + 0.4 + 0.8) = 1.8 miles (216 seconds).
When we wait the maximum time, we wait (9+9+7+15) = 40 seconds for lights, for a total of 232 seconds on the first path. On the second, we wait (3+3+3+6) = 15 seconds, for a total of 231. For that reason, the worst case path is the one that travels all the way down Street 0 first. The best average case uses the first path.
Finally, let's look at city-3.txt. Clearly, the best case path simply has the shortest number of edges, since all road segments are the same length:
UNIX> bin/city_map best print < city-3.txt | grep PATH 30 : PATH: [0000,0000] -> [0001,0000] - Time: 12.000000 31 : PATH: [0001,0000] -> [0002,0000] - Time: 24.000000 32 : PATH: [0002,0000] -> [0002,0001] - Time: 36.000000 33 : PATH: [0002,0001] -> [0002,0002] - Time: 48.000000 34 : PATH: [0002,0002] -> [0002,0003] - Time: 60.000000 35 : PATH: [0002,0003] -> [0002,0004] - Time: 72.000000 UNIX>However, when we wait the maximum amount for each light, we see that we can get through the graph in a circuitous fashion, waiting a maximum of one second for each light. Even though this makes us travel 1.4 miles (168 seconds) instead of 0.6 (72 seconds), it is worth it because we only wait 14 extra seconds for lights. The original path makes us wait 115 seconds for a total of 187 seconds, making it inferior to the path below:
UNIX> bin/city_map worst print < city-3.txt | grep PATH 30 : PATH: [0000,0000] -> [0001,0000] - Time: 13.000000 31 : PATH: [0001,0000] -> [0002,0000] - Time: 26.000000 32 : PATH: [0002,0000] -> [0002,0001] - Time: 39.000000 33 : PATH: [0002,0001] -> [0002,0002] - Time: 52.000000 34 : PATH: [0002,0002] -> [0002,0003] - Time: 65.000000 35 : PATH: [0002,0003] -> [0001,0003] - Time: 78.000000 36 : PATH: [0001,0003] -> [0001,0002] - Time: 91.000000 37 : PATH: [0001,0002] -> [0001,0001] - Time: 104.000000 38 : PATH: [0001,0001] -> [0000,0001] - Time: 117.000000 39 : PATH: [0000,0001] -> [0000,0002] - Time: 130.000000 40 : PATH: [0000,0002] -> [0000,0003] - Time: 143.000000 41 : PATH: [0000,0003] -> [0000,0004] - Time: 156.000000 42 : PATH: [0000,0004] -> [0001,0004] - Time: 169.000000 43 : PATH: [0001,0004] -> [0002,0004] - Time: 182.000000 UNIX>The jgraph output is nice if you can plot postscript. Below, I've converted the outputs to JPG files. For example:
UNIX> bin/city_map none jgraph < city-2.txt | jgraph -P > city-2-none.ps UNIX> open city-2-none.ps UNIX>Then I convert the postscript to JPG using the Preview program on my Macintosh.
City-2: None |
City-2: Best |
City-2: Worst |
City-2: Avg |
City-3: None |
City-3: Best |
City-3: Worst |
City-3: Avg |
I have some more interesting and fun maps in the other city-x.txt files. You can click on the links for uncompressed jpg images.
City-4: None |
City-4: Best |
City-4: Worst |
City-5: Best |
City-5: Worst |
City-5: Avg |
City-6: Best |
City-6: Worst |
City-6: Avg |
Don't try to view pictures of city-7.txt. It's too big.
UNIX> bin/city_map none print < city-7.txt | tail -n 1 502001 : Segment to 499 500 Distance 0.458654 UNIX> bin/city_map best print < city-7.txt | grep PATH | wc 1000 9000 64000 UNIX> time bin/city_map best print < city-7.txt > /dev/null 6.684u 0.120s 0:06.82 99.7% 0+0k 0+0io 0pf+0w UNIX>
City-8: Best |
City-8: Worst |
City-8: Avg |
City-9: Best |
City-9: Worst |
City-9: Avg |
To grade, I am going to pipe the output of your city_map with print to sort. It must match the output of my city_map piped to sort. All of the grading examples have unique light durations and edge lengths. Therefore, it should not be hard to make the two outputs match when piped to sort. Note that in city-3.txt, there are multiple best and avg paths -- that is because light durations and edge lengths are not unique. You do not have to match my output in this case -- you only have to match the outputs (after piping to sort) of the grading examples.
typedef enum { STREET, AVENUE } Road_Type; class Intersection { public: int street; int avenue; double x; double y; double green[2]; // Light green times for STREET & AVENUE list <class Road_Segment *> adj; double best_time; class Road_Segment *backedge; multimap <double, Intersection *>::iterator bfsq_ptr; }; |
The first five fields of an Intersection are the input values -- street number, avenue number, x coordinate, y coordinate, and green durations, indexed by Road_Type. Next is an adjacency list. For example, in city-2.txt, the adjacency list for [1,1] contains road segments to [1,0] and [0,1]. This is because street 1 and avenue 1 are both one-way. On the other hand, the adjacency list for [1,2] contains road segments to [0,2], [2,2] and [1,1].
I don't care about the order of your adjacency lists. They do not have to match mine. This is why I pipe the output of city_map to sort .
The best_time, backedge and bfsq_ptr fields are for you to use when you implement Dijkstra.
class Road_Segment { public: Road_Type type; int number; double distance; Intersection *from; Intersection *to; }; |
Road_Segment instances represent edges. They should be completely straightforward. For example, in city-2.txt the Road_Segment values for the segment from [2,0] to [2,1] will have:
class City_Map { public: City_Map(); void Print(); void Spit_Jgraph(); double Dijkstra(int avg_best_worst); // 'A' for avg, 'B' for best, 'W' for worst Intersection *first; Intersection *last; list <Intersection *> all; multimap <double, Intersection *> bfsq; list <Road_Segment *> path; }; |
Finally, the City_Map class has four methods -- the constructor and Dijkstra(), which you implement, and Print()/Spit_Jgraph(), which I have implemented.
Additionally, it has five variables. The first three you have to set up with your constructor:
Work incrementally on the constructor. First, write a pass that reads all of the lights, creates their Intersection classes and puts them onto all, without worrying about adjacency lists. You should then be able to run the program and have it print out the graph without edges.
Next, work on adding the edges and again print. This is a pain. I used a temporary two-dimensional vector of Intersections, which made it easy for me to get from one intersection to another, and set up the adjacencly lists. When I was done, I discarded the vector (actually, that was done automatically for me)..
When you're done with this, none and print should work. Then get busy on Dijkstra(). Frankly, I think writing Dijkstra() is easier than getting the adjacency lists set up.