mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
floodfill and pathfind to a free parking spot, instead of randomly picking one turn at a time
This commit is contained in:
parent
e1c4e6f39e
commit
f012660da7
@ -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[¤t];
|
||||
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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user