mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
make cars pathfind to their destination
This commit is contained in:
parent
f3f3bfd1e9
commit
c033f51da2
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
## cars
|
## cars
|
||||||
|
|
||||||
- make cars pathfind to their destination
|
|
||||||
|
|
||||||
- model cars parking
|
- model cars parking
|
||||||
- maybe render numbers on the cars to distinguish them
|
- maybe render numbers on the cars to distinguish them
|
||||||
- document the FSM (on lane driving, waiting, turning, parking, etc)
|
- document the FSM (on lane driving, waiting, turning, parking, etc)
|
||||||
|
- populate a bunch of parked cars initially
|
||||||
|
|
||||||
- try to simplify straw_model step (less phases?)
|
- try to simplify straw_model step (less phases?)
|
||||||
|
|
||||||
|
@ -436,7 +436,7 @@ impl gui::GUI for UI {
|
|||||||
|
|
||||||
if self.map.get_r(id).lane_type == map_model::LaneType::Driving {
|
if self.map.get_r(id).lane_type == map_model::LaneType::Driving {
|
||||||
if input.key_pressed(Key::A, "Press A to add a car starting from this road") {
|
if input.key_pressed(Key::A, "Press A to add a car starting from this road") {
|
||||||
if !self.sim_ctrl.sim.spawn_one_on_road(id) {
|
if !self.sim_ctrl.sim.spawn_one_on_road(&self.map, id) {
|
||||||
println!("No room, sorry");
|
println!("No room, sorry");
|
||||||
}
|
}
|
||||||
return gui::EventLoopMode::InputOnly;
|
return gui::EventLoopMode::InputOnly;
|
||||||
|
@ -24,15 +24,13 @@ pub fn pathfind(map: &Map, start: RoadID, end: RoadID) -> Option<Vec<RoadID>> {
|
|||||||
let mut lookup = current;
|
let mut lookup = current;
|
||||||
loop {
|
loop {
|
||||||
path.push(lookup);
|
path.push(lookup);
|
||||||
if let Some(next) = backrefs.get(&lookup) {
|
if lookup == start {
|
||||||
lookup = *next;
|
|
||||||
} else {
|
|
||||||
assert!(lookup == start);
|
|
||||||
path.reverse();
|
path.reverse();
|
||||||
assert_eq!(path[0], start);
|
assert_eq!(path[0], start);
|
||||||
assert_eq!(*path.last().unwrap(), end);
|
assert_eq!(*path.last().unwrap(), end);
|
||||||
return Some(path);
|
return Some(path);
|
||||||
}
|
}
|
||||||
|
lookup = backrefs[&lookup];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,11 +6,12 @@ use ezgui::GfxCtx;
|
|||||||
use geom::{Angle, Pt2D};
|
use geom::{Angle, Pt2D};
|
||||||
use graphics;
|
use graphics;
|
||||||
use graphics::math::Vec2d;
|
use graphics::math::Vec2d;
|
||||||
|
use map_model;
|
||||||
use map_model::geometry;
|
use map_model::geometry;
|
||||||
use map_model::{LaneType, Map, RoadID, TurnID};
|
use map_model::{LaneType, Map, RoadID, TurnID};
|
||||||
use multimap::MultiMap;
|
use multimap::MultiMap;
|
||||||
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||||
use std::f64;
|
use std::f64;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use straw_intersections::{IntersectionPolicy, StopSign, TrafficSignal};
|
use straw_intersections::{IntersectionPolicy, StopSign, TrafficSignal};
|
||||||
@ -122,6 +123,8 @@ struct Car {
|
|||||||
// TODO ideally, something else would remember Goto was requested and not even call step()
|
// TODO ideally, something else would remember Goto was requested and not even call step()
|
||||||
waiting_for: Option<On>,
|
waiting_for: Option<On>,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
|
// Head is the next road
|
||||||
|
path: VecDeque<RoadID>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Action {
|
enum Action {
|
||||||
@ -137,10 +140,11 @@ impl Car {
|
|||||||
format!("Car {:?}", self.id),
|
format!("Car {:?}", self.id),
|
||||||
format!("On {:?}, started at {:?}", self.on, self.started_at),
|
format!("On {:?}, started at {:?}", self.on, self.started_at),
|
||||||
format!("Committed to waiting for {:?}", self.waiting_for),
|
format!("Committed to waiting for {:?}", self.waiting_for),
|
||||||
|
format!("{} roads left in path", self.path.len()),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&self, map: &Map, time: Tick, rng: &mut XorShiftRng) -> Action {
|
fn step(&self, map: &Map, time: Tick) -> Action {
|
||||||
if let Some(on) = self.waiting_for {
|
if let Some(on) = self.waiting_for {
|
||||||
return Action::Goto(on);
|
return Action::Goto(on);
|
||||||
}
|
}
|
||||||
@ -150,21 +154,29 @@ impl Car {
|
|||||||
return Action::Continue;
|
return Action::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Done!
|
||||||
|
if self.path.is_empty() {
|
||||||
|
return Action::Vanish;
|
||||||
|
}
|
||||||
|
|
||||||
match self.on {
|
match self.on {
|
||||||
// For now, just kill off cars that're stuck on disconnected bits of the map
|
|
||||||
On::Road(id) if map.get_turns_from_road(id).is_empty() => Action::Vanish,
|
|
||||||
// TODO cant try to go to next road unless we're the front car
|
// TODO cant try to go to next road unless we're the front car
|
||||||
// if we dont do this here, we wont be able to see what turns people are waiting for
|
// if we dont do this here, we wont be able to see what turns people are waiting for
|
||||||
// even if we wait till we're the front car, we might unravel the line of queued cars
|
// even if we wait till we're the front car, we might unravel the line of queued cars
|
||||||
// too quickly
|
// too quickly
|
||||||
On::Road(id) => Action::Goto(On::Turn(self.choose_turn(id, map, rng))),
|
On::Road(id) => Action::Goto(On::Turn(self.choose_turn(id, map))),
|
||||||
On::Turn(id) => Action::Goto(On::Road(map.get_t(id).dst)),
|
On::Turn(id) => Action::Goto(On::Road(map.get_t(id).dst)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn choose_turn(&self, from: RoadID, map: &Map, rng: &mut XorShiftRng) -> TurnID {
|
fn choose_turn(&self, from: RoadID, map: &Map) -> TurnID {
|
||||||
assert!(self.waiting_for.is_none());
|
assert!(self.waiting_for.is_none());
|
||||||
rng.choose(&map.get_turns_from_road(from)).unwrap().id
|
for t in map.get_turns_from_road(from) {
|
||||||
|
if t.dst == self.path[0] {
|
||||||
|
return t.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("No turn from {} to {}", from, self.path[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the angle and the dist along the road/turn too
|
// Returns the angle and the dist along the road/turn too
|
||||||
@ -312,6 +324,7 @@ impl Sim {
|
|||||||
intersections,
|
intersections,
|
||||||
|
|
||||||
cars: BTreeMap::new(),
|
cars: BTreeMap::new(),
|
||||||
|
// TODO only driving ones
|
||||||
roads: map.all_roads()
|
roads: map.all_roads()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|r| SimQueue::new(On::Road(r.id), map))
|
.map(|r| SimQueue::new(On::Road(r.id), map))
|
||||||
@ -329,23 +342,39 @@ impl Sim {
|
|||||||
// TODO cars basically start in the intersection, with their front bumper right at the
|
// TODO cars basically start in the intersection, with their front bumper right at the
|
||||||
// beginning of the road. later, we want cars starting at arbitrary points in the middle of the
|
// beginning of the road. later, we want cars starting at arbitrary points in the middle of the
|
||||||
// road (from a building), so just ignore this problem for now.
|
// road (from a building), so just ignore this problem for now.
|
||||||
pub fn spawn_one_on_road(&mut self, road: RoadID) -> bool {
|
pub fn spawn_one_on_road(&mut self, map: &Map, start: RoadID) -> bool {
|
||||||
if !self.roads[road.0].room_at_end(self.time, &self.cars) {
|
if !self.roads[start.0].room_at_end(self.time, &self.cars) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let id = CarID(self.id_counter);
|
let id = CarID(self.id_counter);
|
||||||
self.id_counter += 1;
|
self.id_counter += 1;
|
||||||
|
|
||||||
|
let goal = self.rng.choose(map.all_roads()).unwrap();
|
||||||
|
if goal.lane_type != LaneType::Driving || goal.id == start {
|
||||||
|
println!("Chose bad goal {}", goal.id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut path = if let Some(steps) = map_model::pathfind(map, start, goal.id) {
|
||||||
|
VecDeque::from(steps)
|
||||||
|
} else {
|
||||||
|
println!("No path from {} to {}", start, goal.id);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
// path includes the start, but that's not the invariant Car enforces
|
||||||
|
path.pop_front();
|
||||||
|
|
||||||
self.cars.insert(
|
self.cars.insert(
|
||||||
id,
|
id,
|
||||||
Car {
|
Car {
|
||||||
id,
|
id,
|
||||||
|
path,
|
||||||
started_at: self.time,
|
started_at: self.time,
|
||||||
on: On::Road(road),
|
on: On::Road(start),
|
||||||
waiting_for: None,
|
waiting_for: None,
|
||||||
debug: false,
|
debug: false,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.roads[road.0].cars_queue.push(id);
|
self.roads[start.0].cars_queue.push(id);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -366,19 +395,22 @@ impl Sim {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let n = num_cars.min(roads.len());
|
let n = num_cars.min(roads.len());
|
||||||
|
let mut actual = 0;
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
assert!(self.spawn_one_on_road(roads[i]));
|
if self.spawn_one_on_road(map, roads[i]) {
|
||||||
|
actual += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
println!("Spawned {}", n);
|
println!("Spawned {} of {}", actual, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step(&mut self, map: &Map, control_map: &ControlMap) {
|
pub fn step(&mut self, map: &Map, control_map: &ControlMap) {
|
||||||
self.time.increment();
|
self.time.increment();
|
||||||
|
|
||||||
// Could be concurrent. Ask all cars for their move, reinterpreting Goto to see if there's
|
// Could be concurrent, since this is deterministic. Note no RNG. Ask all cars for their
|
||||||
// room now. It's important to query has_room_now here using the previous, fixed state of
|
// move, reinterpreting Goto to see if there's room now. It's important to query
|
||||||
// the world. If we did it in the next loop, then order of updates would matter for more
|
// has_room_now here using the previous, fixed state of the world. If we did it in the next
|
||||||
// than just conflict resolution.
|
// loop, then order of updates would matter for more than just conflict resolution.
|
||||||
//
|
//
|
||||||
// Note that since this uses RNG right now, it's only deterministic if iteration order is!
|
// Note that since this uses RNG right now, it's only deterministic if iteration order is!
|
||||||
// So can't be concurrent and use RNG. Could have a RNG per car or something later if we
|
// So can't be concurrent and use RNG. Could have a RNG per car or something later if we
|
||||||
@ -387,7 +419,7 @@ impl Sim {
|
|||||||
for c in self.cars.values() {
|
for c in self.cars.values() {
|
||||||
requested_moves.push((
|
requested_moves.push((
|
||||||
c.id,
|
c.id,
|
||||||
match c.step(map, self.time, &mut self.rng) {
|
match c.step(map, self.time) {
|
||||||
Action::Goto(on) => {
|
Action::Goto(on) => {
|
||||||
// This is a monotonic property in conjunction with
|
// This is a monotonic property in conjunction with
|
||||||
// new_car_entered_this_step. The last car won't go backwards.
|
// new_car_entered_this_step. The last car won't go backwards.
|
||||||
@ -444,6 +476,8 @@ impl Sim {
|
|||||||
let c = self.cars.get_mut(&id).unwrap();
|
let c = self.cars.get_mut(&id).unwrap();
|
||||||
if let On::Turn(t) = c.on {
|
if let On::Turn(t) = c.on {
|
||||||
self.intersections[map.get_t(t).parent.0].on_exit(c.id);
|
self.intersections[map.get_t(t).parent.0].on_exit(c.id);
|
||||||
|
assert_eq!(c.path[0], map.get_t(t).dst);
|
||||||
|
c.path.pop_front();
|
||||||
}
|
}
|
||||||
c.waiting_for = None;
|
c.waiting_for = None;
|
||||||
c.on = on;
|
c.on = on;
|
||||||
|
Loading…
Reference in New Issue
Block a user