floodfill and pathfind to a free parking spot, instead of randomly picking one turn at a time

This commit is contained in:
Dustin Carlino 2019-08-20 16:08:31 -07:00
parent e1c4e6f39e
commit f012660da7

View File

@ -1,10 +1,10 @@
use crate::{ParkingSimState, ParkingSpot, SidewalkSpot, Vehicle};
use geom::Distance;
use map_model::{
BuildingID, IntersectionID, Map, Path, PathStep, Position, Traversable, Turn, TurnID,
BuildingID, IntersectionID, LaneID, Map, Path, PathStep, Position, Traversable, TurnID,
};
use serde_derive::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::collections::{HashMap, VecDeque};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
pub struct Router {
@ -25,11 +25,10 @@ pub enum ActionAtEnd {
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
enum Goal {
// Spot and cached distance along the last driving lane
// TODO Right now, the building is ignored.
// TODO Right now, the building is ignored when choosing the best spot.
ParkNearBuilding {
target: BuildingID,
spot: Option<(ParkingSpot, Distance)>,
turns_attempted_while_roaming: BTreeSet<TurnID>,
},
EndAtBorder {
end_dist: Distance,
@ -57,7 +56,6 @@ impl Router {
goal: Goal::ParkNearBuilding {
target: bldg,
spot: None,
turns_attempted_while_roaming: BTreeSet::new(),
},
}
}
@ -149,14 +147,34 @@ impl Router {
None => true,
};
if need_new_spot {
let current_lane = self.path.current_step().as_lane();
if let Some((new_spot, new_pos)) = parking.get_first_free_spot(
Position::new(self.path.current_step().as_lane(), front),
Position::new(current_lane, front),
vehicle,
map,
) {
*spot = Some((new_spot, new_pos.dist_along()));
} else {
self.roam_around_for_parking(vehicle, map);
if let Some((new_path_steps, new_spot, new_pos)) = path_to_free_parking_spot(
self.path.current_step().as_lane(),
vehicle,
map,
parking,
) {
*spot = Some((new_spot, new_pos.dist_along()));
for step in new_path_steps {
self.path.add(step, map);
}
} else {
// TODO Fix by avoiding parking blackholes in the first place.
println!("{} can't find parking on {}, and also it's a dead-end, so they'll be stuck there forever. Vanishing.", vehicle.id, current_lane);
// TODO Hmm, actually, this might not be a border -- it just inevitably
// leads only to a border.
self.goal = Goal::EndAtBorder {
end_dist: map.get_l(current_lane).length(),
i: map.get_l(current_lane).dst_i,
};
}
return Some(ActionAtEnd::GotoLaneEnd);
}
}
@ -192,43 +210,50 @@ impl Router {
}
}
}
fn roam_around_for_parking(&mut self, vehicle: &Vehicle, map: &Map) {
let turns_attempted_while_roaming = match self.goal {
Goal::ParkNearBuilding {
ref mut turns_attempted_while_roaming,
..
} => turns_attempted_while_roaming,
_ => unreachable!(),
};
let current_lane = self.path.current_step().as_lane();
let all_choices = map.get_turns_from_lane(current_lane);
let new_choices: Vec<&&Turn> = all_choices
.iter()
.filter(|t| !turns_attempted_while_roaming.contains(&t.id))
.collect();
if all_choices.is_empty() {
// TODO Fix properly by picking and pathfinding fully to a nearby parking lane.
println!("{} can't find parking on {}, and also it's a dead-end, so they'll be stuck there forever. Vanishing.", vehicle.id, current_lane);
self.goal = Goal::EndAtBorder {
end_dist: map.get_l(current_lane).length(),
i: map.get_l(current_lane).dst_i,
};
return;
}
// TODO Better strategies than this: look for lanes with free spots (if it'd be feasible to
// physically see the spots), stay close to the original goal building, avoid lanes we've
// visited, prefer easier turns...
let turn = if !new_choices.is_empty() {
new_choices[0]
} else {
all_choices[0]
};
turns_attempted_while_roaming.insert(turn.id);
self.path.add(PathStep::Turn(turn.id), map);
self.path.add(PathStep::Lane(turn.id.dst), map);
}
}
// Unrealistically assumes the driver has knowledge of currently free parking spots, even if
// they're far away. Since they don't reserve the spot in advance, somebody else can still beat
// them there, producing some nice, realistic churn if there's too much contention.
// The first PathStep is the turn after start, NOT PathStep::Lane(start).
fn path_to_free_parking_spot(
start: LaneID,
vehicle: &Vehicle,
map: &Map,
parking: &ParkingSimState,
) -> Option<(Vec<PathStep>, ParkingSpot, Position)> {
let mut backrefs: HashMap<LaneID, TurnID> = HashMap::new();
// BFS, so we wind up vaguely closer to the start
let mut queue: VecDeque<LaneID> = VecDeque::new();
queue.push_back(start);
while !queue.is_empty() {
let current = queue.pop_front().unwrap();
if let Some((spot, pos)) =
parking.get_first_free_spot(Position::new(current, Distance::ZERO), vehicle, map)
{
let mut steps = vec![PathStep::Lane(current)];
let mut current = current;
loop {
if current == start {
// Don't include PathStep::Lane(start)
steps.pop();
steps.reverse();
return Some((steps, spot, pos));
}
let turn = backrefs[&current];
steps.push(PathStep::Turn(turn));
steps.push(PathStep::Lane(turn.src));
current = turn.src;
}
}
for turn in map.get_turns_from_lane(current) {
if map.is_turn_allowed(turn.id) && !backrefs.contains_key(&turn.id.dst) {
backrefs.insert(turn.id.dst, turn.id);
queue.push_back(turn.id.dst);
}
}
}
None
}