mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-27 00:12:55 +03:00
lots of the logic to start a parked car
This commit is contained in:
parent
dbc6cd1e67
commit
d3bcbe1da2
@ -3,8 +3,10 @@
|
|||||||
## cars
|
## cars
|
||||||
|
|
||||||
- model cars parking
|
- model cars parking
|
||||||
- populate a bunch of parked cars initially
|
- need to update the tests to seed, then start
|
||||||
- maybe render numbers on the cars to distinguish them
|
- render parked cars (different color)
|
||||||
|
- make vanished cars just park again
|
||||||
|
- how to handle when parking is full or no parking at goal road?
|
||||||
|
|
||||||
- code cleanup
|
- code cleanup
|
||||||
- try to simplify straw_model step (less phases?)
|
- try to simplify straw_model step (less phases?)
|
||||||
@ -18,6 +20,7 @@
|
|||||||
- draw cars in slightly different colors, to distinguish them better
|
- draw cars in slightly different colors, to distinguish them better
|
||||||
|
|
||||||
- start implementing a second AORTAish driving model
|
- start implementing a second AORTAish driving model
|
||||||
|
- then make cars park/unpark at the correct position
|
||||||
|
|
||||||
- reversible sim
|
- reversible sim
|
||||||
|
|
||||||
|
@ -414,9 +414,16 @@ impl gui::GUI for UI {
|
|||||||
self.geom_validator = Validator::start(&self.draw_map);
|
self.geom_validator = Validator::start(&self.draw_map);
|
||||||
return gui::EventLoopMode::InputOnly;
|
return gui::EventLoopMode::InputOnly;
|
||||||
}
|
}
|
||||||
if input.unimportant_key_pressed(Key::S, "Spawn 1000 cars in random places") {
|
if self.sim_ctrl.sim.total_cars() == 0 {
|
||||||
self.sim_ctrl.sim.spawn_many_on_empty_roads(&self.map, 1000);
|
if input.unimportant_key_pressed(Key::S, "Seed the map with 50% parked cars") {
|
||||||
return gui::EventLoopMode::InputOnly;
|
self.sim_ctrl.sim.seed_parked_cars(0.5);
|
||||||
|
return gui::EventLoopMode::InputOnly;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if input.unimportant_key_pressed(Key::S, "Make 1000 parked cars start driving") {
|
||||||
|
self.sim_ctrl.sim.start_many_parked_cars(&self.map, 1000);
|
||||||
|
return gui::EventLoopMode::InputOnly;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.current_selection_state {
|
match self.current_selection_state {
|
||||||
@ -435,10 +442,8 @@ 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 start a parked car on this road") {
|
||||||
if !self.sim_ctrl.sim.spawn_one_on_road(&self.map, id) {
|
self.sim_ctrl.sim.start_parked_car(&self.map, id);
|
||||||
println!("No room, sorry");
|
|
||||||
}
|
|
||||||
return gui::EventLoopMode::InputOnly;
|
return gui::EventLoopMode::InputOnly;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,9 @@ use dimensioned::si;
|
|||||||
use draw_car::DrawCar;
|
use draw_car::DrawCar;
|
||||||
use geom::{Angle, Pt2D};
|
use geom::{Angle, Pt2D};
|
||||||
use intersections::{IntersectionPolicy, StopSign, TrafficSignal};
|
use intersections::{IntersectionPolicy, StopSign, TrafficSignal};
|
||||||
use map_model::{Map, RoadID, TurnID};
|
use map_model;
|
||||||
|
use rand::Rng;
|
||||||
|
use map_model::{Map, RoadID, TurnID, LaneType};
|
||||||
use multimap::MultiMap;
|
use multimap::MultiMap;
|
||||||
use std;
|
use std;
|
||||||
use std::collections::{BTreeMap, HashSet, VecDeque};
|
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||||
@ -393,4 +395,44 @@ impl DrivingSimState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// road (from a building), so just ignore this problem for now.
|
||||||
|
// True if we spawned one
|
||||||
|
pub fn start_car_on_road<R: Rng + ?Sized>(&mut self, time: Tick, start: RoadID, car: CarID, map: &Map, rng: &mut R) -> bool {
|
||||||
|
if !self.roads[start.0].room_at_end(time, &self.cars) {
|
||||||
|
// TODO car should enter Unparking state and wait for room
|
||||||
|
println!("No room for {} to start driving on {}", car, start);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let goal = 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(
|
||||||
|
car,
|
||||||
|
Car {
|
||||||
|
id: car,
|
||||||
|
path,
|
||||||
|
started_at: time,
|
||||||
|
on: On::Road(start),
|
||||||
|
waiting_for: None,
|
||||||
|
debug: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
self.roads[start.0].cars_queue.push(car);
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,17 @@ mod sim;
|
|||||||
|
|
||||||
use dimensioned::si;
|
use dimensioned::si;
|
||||||
pub use sim::{Benchmark, Sim};
|
pub use sim::{Benchmark, Sim};
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||||
pub struct CarID(pub usize);
|
pub struct CarID(pub usize);
|
||||||
|
|
||||||
|
impl fmt::Display for CarID {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "CarID({0})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub const TIMESTEP: si::Second<f64> = si::Second {
|
pub const TIMESTEP: si::Second<f64> = si::Second {
|
||||||
value_unsafe: 0.1,
|
value_unsafe: 0.1,
|
||||||
_marker: std::marker::PhantomData,
|
_marker: std::marker::PhantomData,
|
||||||
|
@ -20,7 +20,7 @@ impl ParkingSimState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Kind of vague whether this should handle existing spots or not
|
// Kind of vague whether this should handle existing spots or not
|
||||||
pub(crate) fn seed_random_cars<R: Rng + ?Sized>(&mut self, rng: &mut R, percent_capacity_to_fill: f64) {
|
pub(crate) fn seed_random_cars<R: Rng + ?Sized>(&mut self, rng: &mut R, percent_capacity_to_fill: f64, id_counter: &mut usize) {
|
||||||
assert!(percent_capacity_to_fill >= 0.0 && percent_capacity_to_fill <= 1.0);
|
assert!(percent_capacity_to_fill >= 0.0 && percent_capacity_to_fill <= 1.0);
|
||||||
|
|
||||||
let mut total_capacity = 0;
|
let mut total_capacity = 0;
|
||||||
@ -33,7 +33,8 @@ impl ParkingSimState {
|
|||||||
for spot in &mut r.spots {
|
for spot in &mut r.spots {
|
||||||
if !spot.is_some() && rng.gen_bool(percent_capacity_to_fill) {
|
if !spot.is_some() && rng.gen_bool(percent_capacity_to_fill) {
|
||||||
new_cars += 1;
|
new_cars += 1;
|
||||||
*spot = Some(CarID(42)); // TODO create a new car, right?
|
*spot = Some(CarID(*id_counter));
|
||||||
|
*id_counter += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,6 +43,14 @@ impl ParkingSimState {
|
|||||||
new_cars, total_capacity
|
new_cars, total_capacity
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_last_parked_car(&self, id: RoadID) -> Option<CarID> {
|
||||||
|
self.roads[id.0].get_last_parked_car()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn remove_last_parked_car(&self, id: RoadID, car: CarID) {
|
||||||
|
self.roads[id.0].remove_last_parked_car(car)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||||
@ -64,4 +73,14 @@ impl ParkingLane {
|
|||||||
spots: iter::repeat(None).take(r.number_parking_spots()).collect(),
|
spots: iter::repeat(None).take(r.number_parking_spots()).collect(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_last_parked_car(&self) -> Option<CarID> {
|
||||||
|
self.spots.iter().rfind(|&&x| x.is_some()).map(|r| r.unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_last_parked_car(&mut self, car: CarID) {
|
||||||
|
let idx = self.spots.iter().rposition(|&x| x.is_some()).expect("No parked cars at all now");
|
||||||
|
assert_eq!(self.spots[idx], Some(car));
|
||||||
|
self.spots[idx] = None;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
103
sim/src/sim.rs
103
sim/src/sim.rs
@ -3,11 +3,9 @@
|
|||||||
use control::ControlMap;
|
use control::ControlMap;
|
||||||
use dimensioned::si;
|
use dimensioned::si;
|
||||||
use draw_car::DrawCar;
|
use draw_car::DrawCar;
|
||||||
use driving::{Car, DrivingSimState, On};
|
use driving::DrivingSimState;
|
||||||
use map_model;
|
|
||||||
use map_model::{LaneType, Map, RoadID, TurnID};
|
use map_model::{LaneType, Map, RoadID, TurnID};
|
||||||
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
||||||
use std::collections::VecDeque;
|
|
||||||
use parking::ParkingSimState;
|
use parking::ParkingSimState;
|
||||||
use std::f64;
|
use std::f64;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
@ -45,75 +43,78 @@ impl Sim {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO cars basically start in the intersection, with their front bumper right at the
|
pub fn total_cars(&self) -> usize {
|
||||||
// beginning of the road. later, we want cars starting at arbitrary points in the middle of the
|
self.id_counter
|
||||||
// road (from a building), so just ignore this problem for now.
|
|
||||||
pub fn spawn_one_on_road(&mut self, map: &Map, start: RoadID) -> bool {
|
|
||||||
if !self.driving_state.roads[start.0].room_at_end(self.time, &self.driving_state.cars) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let id = CarID(self.id_counter);
|
|
||||||
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.driving_state.cars.insert(
|
|
||||||
id,
|
|
||||||
Car {
|
|
||||||
id,
|
|
||||||
path,
|
|
||||||
started_at: self.time,
|
|
||||||
on: On::Road(start),
|
|
||||||
waiting_for: None,
|
|
||||||
debug: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
self.driving_state.roads[start.0].cars_queue.push(id);
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn_many_on_empty_roads(&mut self, map: &Map, num_cars: usize) {
|
pub fn seed_parked_cars(&mut self, percent: f64) {
|
||||||
let mut roads: Vec<RoadID> = self.driving_state
|
self.parking_state.seed_random_cars(&mut self.rng, percent, &mut self.id_counter)
|
||||||
.roads
|
}
|
||||||
.iter()
|
|
||||||
|
pub fn start_many_parked_cars(&mut self, map: &Map, num_cars: usize) {
|
||||||
|
let mut driving_lanes: Vec<RoadID> = map.all_roads().iter()
|
||||||
.filter_map(|r| {
|
.filter_map(|r| {
|
||||||
if map.get_r(r.id.as_road()).lane_type == LaneType::Driving && r.is_empty() {
|
if r.lane_type == LaneType::Driving && self.driving_state.roads[r.id.0].is_empty() {
|
||||||
Some(r.id.as_road())
|
Some(r.id)
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
// Don't ruin determinism for silly reasons. :)
|
// Don't ruin determinism for silly reasons. :)
|
||||||
if !roads.is_empty() {
|
if !driving_lanes.is_empty() {
|
||||||
self.rng.shuffle(&mut roads);
|
self.rng.shuffle(&mut driving_lanes);
|
||||||
}
|
}
|
||||||
|
|
||||||
let n = num_cars.min(roads.len());
|
let n = num_cars.min(driving_lanes.len());
|
||||||
let mut actual = 0;
|
let mut actual = 0;
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
if self.spawn_one_on_road(map, roads[i]) {
|
if self.start_parked_car(map, driving_lanes[i]) {
|
||||||
actual += 1;
|
actual += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("Spawned {} of {}", actual, n);
|
println!("Started {} parked cars of requested {}", actual, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_parked_car(&mut self, map: &Map, id: RoadID) -> bool {
|
||||||
|
let (driving_lane, parking_lane) = match map.get_r(id).lane_type {
|
||||||
|
LaneType::Sidewalk => {
|
||||||
|
println!("{} is a sidewalk, can't start a parked car here", id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LaneType::Driving => {
|
||||||
|
if let Some(parking) = map.find_parking_lane(id) {
|
||||||
|
(id, parking)
|
||||||
|
} else {
|
||||||
|
println!("{} has no parking lane", id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LaneType::Parking => {
|
||||||
|
if let Some(driving) = map.find_driving_lane(id) {
|
||||||
|
(driving, id)
|
||||||
|
} else {
|
||||||
|
println!("{} has no driving lane", id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(car) = self.parking_state.get_last_parked_car(parking_lane) {
|
||||||
|
if self.driving_state.start_car_on_road(self.time, driving_lane, car, map, &mut self.rng) {
|
||||||
|
self.parking_state.remove_last_parked_car(parking_lane, car);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
println!("No parked cars on {}", parking_lane);
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
|
// TODO Vanish action should become Park
|
||||||
self.driving_state.step(self.time, map, control_map);
|
self.driving_state.step(self.time, map, control_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user