sim/mechanics/
queue.rs

1use std::collections::{BTreeSet, HashMap, VecDeque};
2
3use serde::{Deserialize, Serialize};
4
5use abstutil::FixedMap;
6use geom::{Distance, Time};
7use map_model::{Map, Position, Traversable};
8
9use crate::mechanics::car::{Car, CarState};
10use crate::{CarID, VehicleType, FOLLOWING_DISTANCE};
11
12/// A Queue of vehicles on a single lane or turn. This is where
13/// https://a-b-street.github.io/docs/tech/trafficsim/discrete_event.html#exact-positions is
14/// implemented.
15///
16/// Some helpful pieces of terminology:
17///
18/// - a "laggy head" is a vehicle whose front is now past the end of this queue, but whose back may
19///   still be partially in the queue. The position of the first car in the queue is still bounded
20///   by the laggy head's back.
21/// - a "static blockage" is due to a vehicle exiting a driveway and immediately cutting across a
22///   few lanes. The "static" part means it occupies a fixed interval of distance in the queue. When
23///   the vehicle is finished exiting the driveway, this blockage is removed.
24/// - a "dynamic blockage" is due to a vehicle changing lanes in the middle of the queue. The exact
25///   position of the blockage in this queue is unknown (it depends on the target queue). The
26///   blockage just occupies the length of the vehicle and keeps following whatever's in front of
27///   it.
28/// - "active cars" are the main members of the queue -- everything except for laggy heads and
29///   blockages.
30#[derive(Serialize, Deserialize, Clone, Debug)]
31pub(crate) struct Queue {
32    pub id: Traversable,
33    members: VecDeque<Queued>,
34    /// This car's back is still partly in this queue.
35    pub laggy_head: Option<CarID>,
36
37    /// How long the lane or turn physically is.
38    pub geom_len: Distance,
39    /// When a car's turn is accepted, reserve the vehicle length + FOLLOWING_DISTANCE for the
40    /// target lane. When the car completely leaves (stops being the laggy_head), free up that
41    /// space. To prevent blocking the box for possibly scary amounts of time, allocate some of
42    /// this length first. This is unused for turns themselves. This value can exceed geom_len
43    /// (for the edge case of ONE long car on a short queue).
44    pub reserved_length: Distance,
45}
46
47/// A member of a `Queue`.
48#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
49pub enum Queued {
50    /// A regular vehicle trying to move forwards
51    Vehicle(CarID),
52    /// Something occupying a fixed interval of distance on the queue
53    StaticBlockage {
54        /// This vehicle is exiting a driveway and cutting across a few lanes
55        cause: CarID,
56        front: Distance,
57        back: Distance,
58    },
59    /// This follows whatever's in front of it
60    DynamicBlockage {
61        /// This vehicle is in the middle of changing lanes
62        cause: CarID,
63        vehicle_len: Distance,
64    },
65}
66
67/// The exact position of something in a `Queue` at some time
68#[derive(Clone, Debug)]
69pub struct QueueEntry {
70    pub member: Queued,
71    pub front: Distance,
72    /// Not including FOLLOWING_DISTANCE
73    pub back: Distance,
74}
75
76impl Queue {
77    pub fn new(id: Traversable, map: &Map) -> Queue {
78        Queue {
79            id,
80            members: VecDeque::new(),
81            laggy_head: None,
82            geom_len: id.get_polyline(map).length(),
83            reserved_length: Distance::ZERO,
84        }
85    }
86
87    /// Get the front of the last car in the queue.
88    pub fn get_last_car_position(
89        &self,
90        now: Time,
91        cars: &FixedMap<CarID, Car>,
92        queues: &HashMap<Traversable, Queue>,
93    ) -> Option<(CarID, Distance)> {
94        self.inner_get_last_car_position(now, cars, queues, &mut BTreeSet::new(), None)
95    }
96
97    /// Return the exact position of each member of the queue. The farthest along (greatest distance) is first.
98    pub fn get_car_positions(
99        &self,
100        now: Time,
101        cars: &FixedMap<CarID, Car>,
102        queues: &HashMap<Traversable, Queue>,
103    ) -> Vec<QueueEntry> {
104        let mut all_cars = vec![];
105        self.inner_get_last_car_position(
106            now,
107            cars,
108            queues,
109            &mut BTreeSet::new(),
110            Some(&mut all_cars),
111        );
112        all_cars
113    }
114
115    /// Returns the front of the last car in the queue, only if the last member is an active car.
116    fn inner_get_last_car_position(
117        &self,
118        now: Time,
119        cars: &FixedMap<CarID, Car>,
120        queues: &HashMap<Traversable, Queue>,
121        recursed_queues: &mut BTreeSet<Traversable>,
122        mut intermediate_results: Option<&mut Vec<QueueEntry>>,
123    ) -> Option<(CarID, Distance)> {
124        if self.members.is_empty() {
125            return None;
126        }
127
128        // TODO Consider simplifying this loop's structure. Calculate the bound here before
129        // starting the loop, handling the laggy head case.
130        let mut previous: Option<QueueEntry> = None;
131        for queued in self.members.iter().cloned() {
132            let bound = match previous {
133                Some(entry) => entry.back - FOLLOWING_DISTANCE,
134                None => match self.laggy_head {
135                    Some(id) => {
136                        // The simple but broken version:
137                        //self.geom_len - cars[&id].vehicle.length - FOLLOWING_DISTANCE
138
139                        // The expensive case. We need to figure out exactly where the laggy head
140                        // is on their queue.
141                        let leader = &cars[&id];
142
143                        // But don't create a cycle!
144                        let recurse_to = leader.router.head();
145                        if recursed_queues.contains(&recurse_to) {
146                            // See the picture in
147                            // https://github.com/a-b-street/abstreet/issues/30. We have two
148                            // extremes to break the cycle.
149                            //
150                            // 1) Hope that the last person in this queue isn't bounded by the
151                            //    agent in front of them yet. geom_len
152                            // 2) Assume the leader has advanced minimally into the next lane.
153                            //    geom_len - laggy head's length - FOLLOWING_DISTANCE.
154                            //
155                            // For now, optimistically assume 1. If we're wrong, consequences could
156                            // be queue spillover (we're too optimistic about the number of
157                            // vehicles that can fit on a lane) or cars jumping positions slightly
158                            // while the cycle occurs.
159                            self.geom_len
160                        } else {
161                            recursed_queues.insert(recurse_to);
162
163                            let (head, head_dist) = queues[&leader.router.head()]
164                                .inner_get_last_car_position(
165                                    now,
166                                    cars,
167                                    queues,
168                                    recursed_queues,
169                                    None,
170                                )
171                                .unwrap();
172                            assert_eq!(head, id);
173
174                            let mut dist_away_from_this_queue = head_dist;
175                            for on in &leader.last_steps {
176                                if *on == self.id {
177                                    break;
178                                }
179                                dist_away_from_this_queue += queues[on].geom_len;
180                            }
181                            // They might actually be out of the way, but laggy_head hasn't been
182                            // updated yet.
183                            if dist_away_from_this_queue
184                                < leader.vehicle.length + FOLLOWING_DISTANCE
185                            {
186                                self.geom_len
187                                    - (cars[&id].vehicle.length - dist_away_from_this_queue)
188                                    - FOLLOWING_DISTANCE
189                            } else {
190                                self.geom_len
191                            }
192                        }
193                    }
194                    None => self.geom_len,
195                },
196            };
197
198            // There's spillover and a car shouldn't have been able to enter yet.
199            if bound < Distance::ZERO {
200                if let Some(intermediate_results) = intermediate_results {
201                    dump_cars(intermediate_results, cars, self.id, now);
202                }
203                panic!(
204                    "Queue has spillover on {} at {} -- can't draw {:?}, bound is {}. Laggy head is \
205                     {:?}. This is usually a geometry bug; check for duplicate roads going \
206                     between the same intersections.",
207                    self.id, now, queued, bound, self.laggy_head
208                );
209            }
210
211            let entry = match queued {
212                Queued::Vehicle(id) => {
213                    let car = &cars[&id];
214                    let front = match car.state {
215                        CarState::Queued { .. } => {
216                            if car.router.last_step() {
217                                car.router.get_end_dist().min(bound)
218                            } else {
219                                bound
220                            }
221                        }
222                        CarState::WaitingToAdvance { .. } => {
223                            if bound != self.geom_len {
224                                if let Some(intermediate_results) = intermediate_results {
225                                    dump_cars(intermediate_results, cars, self.id, now);
226                                }
227                                panic!("{} is waiting to advance on {}, but the current bound is {}, not geom_len {}. How can anything be in front of them?", id, self.id, bound, self.geom_len);
228                            }
229                            self.geom_len
230                        }
231                        CarState::Crossing {
232                            ref time_int,
233                            ref dist_int,
234                            ..
235                        } => {
236                            // TODO Why percent_clamp_end? We process car updates in any order, so we might
237                            // calculate this before moving this car from Crossing to another state.
238                            dist_int.lerp(time_int.percent_clamp_end(now)).min(bound)
239                        }
240                        CarState::ChangingLanes {
241                            ref new_time,
242                            ref new_dist,
243                            ..
244                        } => {
245                            // Same as the Crossing logic
246                            new_dist.lerp(new_time.percent_clamp_end(now)).min(bound)
247                        }
248                        CarState::Unparking { front, .. } => front,
249                        CarState::Parking(front, _, _) => front,
250                        CarState::IdlingAtStop(front, _) => front,
251                    };
252                    QueueEntry {
253                        member: queued,
254                        front,
255                        back: front - car.vehicle.length,
256                    }
257                }
258                Queued::StaticBlockage { front, back, .. } => QueueEntry {
259                    member: queued,
260                    front,
261                    back,
262                },
263                Queued::DynamicBlockage { vehicle_len, .. } => QueueEntry {
264                    member: queued,
265                    // This is a reasonable guess, because a vehicle only starts changing lanes if
266                    // there's something slower in front of it. So we assume that slow vehicle
267                    // continues to exist for the 1 second that lane-changing takes. If for some
268                    // reason that slower leader vanishes, this bound could jump up, which just
269                    // causes anything following the lane-changing vehicle to be able to go a
270                    // little faster.
271                    front: bound,
272                    back: bound - vehicle_len,
273                },
274            };
275
276            if let Some(ref mut intermediate_results) = intermediate_results {
277                intermediate_results.push(entry.clone());
278            }
279            previous = Some(entry);
280        }
281        // Enable to detect possible bugs, but save time otherwise
282        if false {
283            if let Some(intermediate_results) = intermediate_results {
284                validate_positions(intermediate_results, cars, now, self.id)
285            }
286        }
287
288        let previous = previous?;
289        match previous.member {
290            Queued::Vehicle(car) => Some((car, previous.front)),
291            Queued::StaticBlockage { .. } => None,
292            Queued::DynamicBlockage { .. } => None,
293        }
294    }
295
296    /// If the specified car can appear in the queue, return the position in the queue to do so.
297    pub fn get_idx_to_insert_car(
298        &self,
299        start_dist: Distance,
300        vehicle_len: Distance,
301        now: Time,
302        cars: &FixedMap<CarID, Car>,
303        queues: &HashMap<Traversable, Queue>,
304    ) -> Option<usize> {
305        if self.laggy_head.is_none() && self.members.is_empty() {
306            return Some(0);
307        }
308
309        let dists = self.get_car_positions(now, cars, queues);
310        // TODO Binary search
311        let idx = match dists.iter().position(|entry| start_dist >= entry.front) {
312            Some(i) => i,
313            None => dists.len(),
314        };
315
316        // Nope, there's not actually room at the front right now.
317        // (This is overly conservative; we could figure out exactly where the laggy head is and
318        // maybe allow it.)
319        if idx == 0 {
320            if let Some(c) = self.laggy_head {
321                // We don't know exactly where the laggy head is. So assume the worst case; that
322                // they've just barely started the turn, and we have to use the same
323                // too-close-to-leader math.
324                //
325                // TODO We can be more precise! We already call get_car_positions, and that
326                // calculates exactly where the laggy head is. We just need to plumb that bound
327                // back here.
328                if self.geom_len - cars[&c].vehicle.length - FOLLOWING_DISTANCE < start_dist {
329                    return None;
330                }
331            }
332        }
333
334        // Are we too close to the leader?
335        if idx != 0 && dists[idx - 1].back - FOLLOWING_DISTANCE < start_dist {
336            return None;
337        }
338        // Or the follower?
339        if idx != dists.len() && start_dist - vehicle_len - FOLLOWING_DISTANCE < dists[idx].front {
340            return None;
341        }
342
343        Some(idx)
344    }
345
346    /// Record that a car has entered a queue at a position. This must match get_idx_to_insert_car
347    /// -- the same index and immediately after passing that query.
348    pub fn insert_car_at_idx(&mut self, idx: usize, car: &Car) {
349        self.members.insert(idx, Queued::Vehicle(car.vehicle.id));
350        self.reserved_length += car.vehicle.length + FOLLOWING_DISTANCE;
351    }
352
353    /// Record that a car has entered a queue at the end. It's assumed that try_to_reserve_entry
354    /// has already happened.
355    pub fn push_car_onto_end(&mut self, car: CarID) {
356        self.members.push_back(Queued::Vehicle(car));
357    }
358
359    /// Change the first car in the queue to the laggy head, indicating that it's front has left
360    /// the queue, but its back is still there. Return that car.
361    pub fn move_first_car_to_laggy_head(&mut self) -> CarID {
362        assert!(self.laggy_head.is_none());
363        let car = match self.members.pop_front() {
364            Some(Queued::Vehicle(c)) => c,
365            x => {
366                panic!(
367                    "First member of {} is {:?}, not an active vehicle",
368                    self.id, x
369                );
370            }
371        };
372        self.laggy_head = Some(car);
373        car
374    }
375
376    /// If true, there's room and the car must actually start the turn (because the space is
377    /// reserved).
378    pub fn try_to_reserve_entry(&mut self, car: &Car, force_entry: bool) -> bool {
379        // If self.reserved_length >= self.geom_len, then the lane is already full. Normally we
380        // won't allow more cars to start a turn towards it, but if force_entry is true, then we'll
381        // allow it.
382
383        // Sometimes a car + FOLLOWING_DISTANCE might be longer than the geom_len entirely. In that
384        // case, it just means the car won't totally fit on the queue at once, which is fine.
385        // Reserve the normal amount of space; the next car trying to enter will get rejected.
386        // Also allow this don't-block-the-box prevention to be disabled.
387        if self.room_for_car(car) || force_entry {
388            self.reserved_length += car.vehicle.length + FOLLOWING_DISTANCE;
389            return true;
390        }
391        false
392    }
393
394    /// True if the reserved length exceeds the physical length. This means a vehicle is headed
395    /// towards the queue already and is expected to not fit entirely inside.
396    pub fn is_overflowing(&self) -> bool {
397        self.reserved_length >= self.geom_len
398    }
399
400    /// Can a car start a turn for this queue?
401    pub fn room_for_car(&self, car: &Car) -> bool {
402        self.reserved_length == Distance::ZERO
403            || self.reserved_length + car.vehicle.length + FOLLOWING_DISTANCE < self.geom_len
404    }
405
406    /// Once a car has fully exited a queue, free up the space it was reserving.
407    pub fn free_reserved_space(&mut self, car: &Car) {
408        self.reserved_length -= car.vehicle.length + FOLLOWING_DISTANCE;
409        assert!(
410            self.reserved_length >= Distance::ZERO,
411            "invalid reserved length: {:?}, car: {:?}",
412            self.reserved_length,
413            car
414        );
415    }
416
417    /// Return a penalty for entering this queue, as opposed to some adjacent ones. Used for
418    /// lane-changing. (number of vehicles, is there a bike here)
419    pub fn target_lane_penalty(&self) -> (usize, usize) {
420        let mut num_vehicles = self.members.len();
421        if self.laggy_head.is_some() {
422            num_vehicles += 1;
423        }
424
425        let bike_cost = if self
426            .members
427            .iter()
428            .any(|x| matches!(x, Queued::Vehicle(c) if c.vehicle_type == VehicleType::Bike))
429            || self
430                .laggy_head
431                .map(|c| c.vehicle_type == VehicleType::Bike)
432                .unwrap_or(false)
433        {
434            1
435        } else {
436            0
437        };
438
439        (num_vehicles, bike_cost)
440    }
441
442    /// Find the vehicle in front of the specified input. None if the specified vehicle isn't
443    /// ACTIVE (not a blockage) in the queue at all, or they're the front (with or without a laggy
444    /// head).
445    pub fn get_leader(&self, id: CarID) -> Option<CarID> {
446        let mut leader = None;
447        for queued in &self.members {
448            match queued {
449                Queued::Vehicle(car) => {
450                    if *car == id {
451                        return leader;
452                    }
453                    leader = Some(*car);
454                }
455                Queued::StaticBlockage { .. } | Queued::DynamicBlockage { .. } => {
456                    leader = None;
457                }
458            }
459        }
460        None
461    }
462
463    /// Record that a car is blocking a static portion of the queue (from front to back). Must use
464    /// the index from can_block_from_driveway.
465    pub fn add_static_blockage(
466        &mut self,
467        cause: CarID,
468        front: Distance,
469        back: Distance,
470        idx: usize,
471    ) {
472        assert!(front > back);
473        assert!(back >= FOLLOWING_DISTANCE);
474        let vehicle_len = front - back;
475        self.members
476            .insert(idx, Queued::StaticBlockage { cause, front, back });
477        self.reserved_length += vehicle_len + FOLLOWING_DISTANCE;
478    }
479
480    /// Record that a car is no longer blocking a static portion of the queue.
481    pub fn clear_static_blockage(&mut self, caused_by: CarID, idx: usize) {
482        let blockage = self.members.remove(idx).unwrap();
483        match blockage {
484            Queued::StaticBlockage { front, back, cause } => {
485                assert_eq!(caused_by, cause);
486                let vehicle_len = front - back;
487                self.reserved_length -= vehicle_len + FOLLOWING_DISTANCE;
488            }
489            _ => unreachable!(),
490        }
491    }
492
493    /// Record that a car is starting to change lanes away from this queue.
494    pub fn replace_car_with_dynamic_blockage(&mut self, car: &Car, idx: usize) {
495        self.remove_car_from_idx(car.vehicle.id, idx);
496        self.members.insert(
497            idx,
498            Queued::DynamicBlockage {
499                cause: car.vehicle.id,
500                vehicle_len: car.vehicle.length,
501            },
502        );
503        // We don't need to touch reserved_length -- it's still vehicle_len + FOLLOWING_DISTANCE
504    }
505
506    /// Record that a car is no longer blocking a dynamic portion of the queue.
507    pub fn clear_dynamic_blockage(&mut self, caused_by: CarID, idx: usize) {
508        let blockage = self.members.remove(idx).unwrap();
509        match blockage {
510            Queued::DynamicBlockage { cause, vehicle_len } => {
511                assert_eq!(caused_by, cause);
512                self.reserved_length -= vehicle_len + FOLLOWING_DISTANCE;
513            }
514            _ => unreachable!(),
515        }
516    }
517
518    /// True if a static blockage can be inserted into the queue without anything already there
519    /// intersecting it. Returns the index if so. The position represents the front of the
520    /// blockage.
521    pub fn can_block_from_driveway(
522        &self,
523        pos: &Position,
524        vehicle_len: Distance,
525        now: Time,
526        cars: &FixedMap<CarID, Car>,
527        queues: &HashMap<Traversable, Queue>,
528    ) -> Option<usize> {
529        self.get_idx_to_insert_car(pos.dist_along(), vehicle_len, now, cars, queues)
530    }
531
532    /// Get all cars in the queue, not including the laggy head or blockages.
533    ///
534    /// TODO Do NOT use this for calculating indices or getting the leader/follower. Might be safer
535    /// to just hide this and only expose number of active cars, first, and last.
536    pub fn get_active_cars(&self) -> Vec<CarID> {
537        self.members
538            .iter()
539            .filter_map(|x| match x {
540                Queued::Vehicle(c) => Some(*c),
541                Queued::StaticBlockage { .. } => None,
542                Queued::DynamicBlockage { .. } => None,
543            })
544            .collect()
545    }
546
547    /// Remove a car from a position. Need to separately do free_reserved_space.
548    pub fn remove_car_from_idx(&mut self, car: CarID, idx: usize) {
549        assert_eq!(self.members.remove(idx), Some(Queued::Vehicle(car)));
550    }
551
552    /// If a car thinks it's reached the end of the queue, double check. Blockages or laggy heads
553    /// might be in the way.
554    pub fn is_car_at_front(&self, car: CarID) -> bool {
555        self.laggy_head.is_none() && self.members.get(0) == Some(&Queued::Vehicle(car))
556    }
557}
558
559fn validate_positions(
560    dists: &[QueueEntry],
561    cars: &FixedMap<CarID, Car>,
562    now: Time,
563    id: Traversable,
564) {
565    for pair in dists.windows(2) {
566        if pair[0].back - FOLLOWING_DISTANCE < pair[1].front {
567            dump_cars(dists, cars, id, now);
568            panic!(
569                "get_car_positions wound up with bad positioning: {} then {}\n{:?}",
570                pair[0].front, pair[1].front, dists
571            );
572        }
573    }
574}
575
576fn dump_cars(dists: &[QueueEntry], cars: &FixedMap<CarID, Car>, id: Traversable, now: Time) {
577    println!("\nOn {} at {}...", id, now);
578    for entry in dists {
579        println!("- {:?} @ {}..{}", entry.member, entry.front, entry.back);
580        match entry.member {
581            Queued::Vehicle(id) => match cars[&id].state {
582                CarState::Crossing {
583                    ref time_int,
584                    ref dist_int,
585                    ..
586                } => {
587                    println!(
588                        "  Going {} .. {} during {} .. {}",
589                        dist_int.start, dist_int.end, time_int.start, time_int.end
590                    );
591                }
592                CarState::ChangingLanes {
593                    ref new_time,
594                    ref new_dist,
595                    ..
596                } => {
597                    println!(
598                        "  Going {} .. {} during {} .. {}, also in the middle of lane-changing",
599                        new_dist.start, new_dist.end, new_time.start, new_time.end
600                    );
601                }
602                CarState::Queued { .. } => {
603                    println!("  Queued currently");
604                }
605                CarState::WaitingToAdvance { .. } => {
606                    println!("  WaitingToAdvance currently");
607                }
608                CarState::Unparking { ref time_int, .. } => {
609                    println!("  Unparking during {} .. {}", time_int.start, time_int.end);
610                }
611                CarState::Parking(_, _, ref time_int) => {
612                    println!("  Parking during {} .. {}", time_int.start, time_int.end);
613                }
614                CarState::IdlingAtStop(_, ref time_int) => {
615                    println!("  Idling during {} .. {}", time_int.start, time_int.end);
616                }
617            },
618            Queued::StaticBlockage { cause, .. } => {
619                println!("  Static blockage by {}", cause);
620            }
621            Queued::DynamicBlockage { cause, vehicle_len } => {
622                println!("  Dynamic blockage of length {} by {}", vehicle_len, cause);
623            }
624        }
625    }
626    println!();
627}