mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +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
|
||||
|
||||
- model cars parking
|
||||
- populate a bunch of parked cars initially
|
||||
- maybe render numbers on the cars to distinguish them
|
||||
- need to update the tests to seed, then start
|
||||
- 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
|
||||
- try to simplify straw_model step (less phases?)
|
||||
@ -18,6 +20,7 @@
|
||||
- draw cars in slightly different colors, to distinguish them better
|
||||
|
||||
- start implementing a second AORTAish driving model
|
||||
- then make cars park/unpark at the correct position
|
||||
|
||||
- reversible sim
|
||||
|
||||
|
@ -414,9 +414,16 @@ impl gui::GUI for UI {
|
||||
self.geom_validator = Validator::start(&self.draw_map);
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
}
|
||||
if input.unimportant_key_pressed(Key::S, "Spawn 1000 cars in random places") {
|
||||
self.sim_ctrl.sim.spawn_many_on_empty_roads(&self.map, 1000);
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
if self.sim_ctrl.sim.total_cars() == 0 {
|
||||
if input.unimportant_key_pressed(Key::S, "Seed the map with 50% parked cars") {
|
||||
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 {
|
||||
@ -435,10 +442,8 @@ impl gui::GUI for UI {
|
||||
}
|
||||
|
||||
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 !self.sim_ctrl.sim.spawn_one_on_road(&self.map, id) {
|
||||
println!("No room, sorry");
|
||||
}
|
||||
if input.key_pressed(Key::A, "Press A to start a parked car on this road") {
|
||||
self.sim_ctrl.sim.start_parked_car(&self.map, id);
|
||||
return gui::EventLoopMode::InputOnly;
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,9 @@ use dimensioned::si;
|
||||
use draw_car::DrawCar;
|
||||
use geom::{Angle, Pt2D};
|
||||
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 std;
|
||||
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;
|
||||
pub use sim::{Benchmark, Sim};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
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 {
|
||||
value_unsafe: 0.1,
|
||||
_marker: std::marker::PhantomData,
|
||||
|
@ -20,7 +20,7 @@ impl ParkingSimState {
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
let mut total_capacity = 0;
|
||||
@ -33,7 +33,8 @@ impl ParkingSimState {
|
||||
for spot in &mut r.spots {
|
||||
if !spot.is_some() && rng.gen_bool(percent_capacity_to_fill) {
|
||||
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
|
||||
);
|
||||
}
|
||||
|
||||
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)]
|
||||
@ -64,4 +73,14 @@ impl ParkingLane {
|
||||
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 dimensioned::si;
|
||||
use draw_car::DrawCar;
|
||||
use driving::{Car, DrivingSimState, On};
|
||||
use map_model;
|
||||
use driving::DrivingSimState;
|
||||
use map_model::{LaneType, Map, RoadID, TurnID};
|
||||
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
||||
use std::collections::VecDeque;
|
||||
use parking::ParkingSimState;
|
||||
use std::f64;
|
||||
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
|
||||
// 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.
|
||||
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 total_cars(&self) -> usize {
|
||||
self.id_counter
|
||||
}
|
||||
|
||||
pub fn spawn_many_on_empty_roads(&mut self, map: &Map, num_cars: usize) {
|
||||
let mut roads: Vec<RoadID> = self.driving_state
|
||||
.roads
|
||||
.iter()
|
||||
pub fn seed_parked_cars(&mut self, percent: f64) {
|
||||
self.parking_state.seed_random_cars(&mut self.rng, percent, &mut self.id_counter)
|
||||
}
|
||||
|
||||
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| {
|
||||
if map.get_r(r.id.as_road()).lane_type == LaneType::Driving && r.is_empty() {
|
||||
Some(r.id.as_road())
|
||||
if r.lane_type == LaneType::Driving && self.driving_state.roads[r.id.0].is_empty() {
|
||||
Some(r.id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// Don't ruin determinism for silly reasons. :)
|
||||
if !roads.is_empty() {
|
||||
self.rng.shuffle(&mut roads);
|
||||
if !driving_lanes.is_empty() {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
self.time.increment();
|
||||
|
||||
// TODO Vanish action should become Park
|
||||
self.driving_state.step(self.time, map, control_map);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user