mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 17:34:58 +03:00
removing parametric driving model for now. too much work to keep it updated as aorta driving model improves, and no benefit anticipated.
This commit is contained in:
parent
7f29e5d285
commit
1eecc48dbe
@ -58,10 +58,6 @@ struct Flags {
|
||||
#[structopt(long = "experimental")]
|
||||
experimental_gui: bool,
|
||||
|
||||
/// Use the old parametric sim
|
||||
#[structopt(long = "parametric_sim")]
|
||||
parametric_sim: bool,
|
||||
|
||||
/// Extra KML to display
|
||||
#[structopt(long = "kml")]
|
||||
kml: Option<String>,
|
||||
@ -112,7 +108,6 @@ fn main() {
|
||||
&flags.abst_input,
|
||||
window_size,
|
||||
flags.rng_seed,
|
||||
flags.parametric_sim,
|
||||
flags.kml,
|
||||
flags.load_from,
|
||||
),
|
||||
|
@ -21,9 +21,9 @@ pub struct SimController {
|
||||
}
|
||||
|
||||
impl SimController {
|
||||
pub fn new(map: &Map, rng_seed: Option<u8>, parametric_sim: bool) -> SimController {
|
||||
pub fn new(map: &Map, rng_seed: Option<u8>) -> SimController {
|
||||
SimController {
|
||||
sim: Sim::new(map, rng_seed, parametric_sim),
|
||||
sim: Sim::new(map, rng_seed),
|
||||
desired_speed: 1.0,
|
||||
last_step: None,
|
||||
benchmark: None,
|
||||
|
@ -82,7 +82,6 @@ impl UI {
|
||||
abst_path: &str,
|
||||
window_size: Size,
|
||||
rng_seed: Option<u8>,
|
||||
parametric_sim: bool,
|
||||
kml: Option<String>,
|
||||
load_sim_from: Option<String>,
|
||||
) -> UI {
|
||||
@ -102,7 +101,7 @@ impl UI {
|
||||
|
||||
let steepness_viz = SteepnessVisualizer::new(&map);
|
||||
let turn_colors = TurnColors::new(&control_map);
|
||||
let mut sim_ctrl = SimController::new(&map, rng_seed, parametric_sim);
|
||||
let mut sim_ctrl = SimController::new(&map, rng_seed);
|
||||
if let Some(path) = load_sim_from {
|
||||
sim_ctrl.sim = abstutil::read_json(&path).expect("loading sim state failed");
|
||||
println!("Loaded {}", path);
|
||||
|
@ -20,10 +20,6 @@ struct Flags {
|
||||
#[structopt(long = "rng_seed")]
|
||||
rng_seed: Option<u8>,
|
||||
|
||||
/// Use the old parametric sim
|
||||
#[structopt(long = "parametric_sim")]
|
||||
parametric_sim: bool,
|
||||
|
||||
/// Optional time to savestate
|
||||
#[structopt(long = "save_at")]
|
||||
save_at: Option<u32>,
|
||||
@ -41,7 +37,7 @@ fn main() {
|
||||
.expect("Couldn't load map");
|
||||
// TODO could load savestate
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
let mut sim = sim::Sim::new(&map, flags.rng_seed, flags.parametric_sim);
|
||||
let mut sim = sim::Sim::new(&map, flags.rng_seed);
|
||||
|
||||
if let Some(path) = flags.load_from {
|
||||
sim = abstutil::read_json(&path).expect("loading sim state failed");
|
||||
|
@ -27,7 +27,6 @@ mod driving;
|
||||
mod intersections;
|
||||
mod kinematics;
|
||||
mod models;
|
||||
mod parametric_driving;
|
||||
mod parking;
|
||||
mod sim;
|
||||
mod spawn;
|
||||
|
@ -1,542 +0,0 @@
|
||||
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
// This implements a simple driving model. Might adapt it into something nicer later, but for now,
|
||||
// it exists to kind of enforce that driving models can be subbed out easily.
|
||||
|
||||
use abstutil;
|
||||
use abstutil::{deserialize_btreemap, serialize_btreemap};
|
||||
use dimensioned::si;
|
||||
use draw_car::DrawCar;
|
||||
use geom::{Angle, Pt2D};
|
||||
use intersections::{AgentInfo, IntersectionSimState, Request};
|
||||
use kinematics::Vehicle;
|
||||
use map_model::{LaneID, Map, TurnID};
|
||||
use models::{choose_turn, Action, FOLLOWING_DISTANCE};
|
||||
use multimap::MultiMap;
|
||||
use parking::ParkingSimState;
|
||||
use rand::Rng;
|
||||
use sim::CarParking;
|
||||
use std::collections::{BTreeMap, HashSet, VecDeque};
|
||||
use {AgentID, CarID, CarState, Distance, InvariantViolated, On, Tick};
|
||||
|
||||
// This represents an actively driving car, not a parked one
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
struct Car {
|
||||
id: CarID,
|
||||
on: On,
|
||||
// When did the car start the current On?
|
||||
started_at: Tick,
|
||||
waiting_for: Option<On>,
|
||||
debug: bool,
|
||||
// Head is the next lane
|
||||
path: VecDeque<LaneID>,
|
||||
}
|
||||
|
||||
// TODO this is used for verifying sim state determinism, so it should actually check everything.
|
||||
// the f64 prevents this from being derived.
|
||||
impl PartialEq for Car {
|
||||
fn eq(&self, other: &Car) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
impl Eq for Car {}
|
||||
|
||||
impl Car {
|
||||
// Note this doesn't change the car's state, and it observes a fixed view of the world!
|
||||
fn react(
|
||||
&self,
|
||||
map: &Map,
|
||||
time: Tick,
|
||||
sim: &DrivingSimState,
|
||||
intersections: &IntersectionSimState,
|
||||
) -> Action {
|
||||
let desired_on: On = {
|
||||
if let Some(on) = self.waiting_for {
|
||||
on
|
||||
} else {
|
||||
let dist = self.on.speed_limit(map) * (time - self.started_at).as_time();
|
||||
if dist < self.on.length(map) {
|
||||
return Action::Continue;
|
||||
}
|
||||
|
||||
// Done!
|
||||
if self.path.is_empty() {
|
||||
return Action::Vanish;
|
||||
}
|
||||
|
||||
match self.on {
|
||||
On::Lane(id) => On::Turn(choose_turn(&self.path, &self.waiting_for, id, map)),
|
||||
On::Turn(id) => On::Lane(map.get_t(id).dst),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Can we actually go there right now?
|
||||
// In a more detailed driving model, this would do things like lookahead.
|
||||
let has_room_now = match desired_on {
|
||||
On::Lane(id) => sim.lanes[id.0].room_at_end(time, &sim.cars, map),
|
||||
On::Turn(id) => sim.turns[&id].room_at_end(time, &sim.cars, map),
|
||||
};
|
||||
let is_lead_vehicle = match self.on {
|
||||
On::Lane(id) => sim.lanes[id.0].cars_queue[0] == self.id,
|
||||
On::Turn(id) => sim.turns[&id].cars_queue[0] == self.id,
|
||||
};
|
||||
let intersection_req_granted = match desired_on {
|
||||
// Already doing a turn, finish it!
|
||||
On::Lane(_) => true,
|
||||
On::Turn(id) => intersections.request_granted(Request::for_car(self.id, id)),
|
||||
};
|
||||
if has_room_now && is_lead_vehicle && intersection_req_granted {
|
||||
Action::Goto(desired_on)
|
||||
} else {
|
||||
Action::WaitFor(desired_on)
|
||||
}
|
||||
}
|
||||
|
||||
fn step_goto(
|
||||
&mut self,
|
||||
on: On,
|
||||
time: Tick,
|
||||
map: &Map,
|
||||
intersections: &mut IntersectionSimState,
|
||||
) -> Result<(), InvariantViolated> {
|
||||
if let On::Turn(t) = self.on {
|
||||
intersections.on_exit(Request::for_car(self.id, t));
|
||||
assert_eq!(self.path[0], map.get_t(t).dst);
|
||||
self.path.pop_front();
|
||||
}
|
||||
self.waiting_for = None;
|
||||
self.on = on;
|
||||
if let On::Turn(t) = self.on {
|
||||
intersections.on_enter(Request::for_car(self.id, t))?;
|
||||
}
|
||||
// TODO could calculate leftover (and deal with large timesteps, small
|
||||
// lanes)
|
||||
self.started_at = time;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Returns the angle and the dist along the lane/turn too
|
||||
fn get_best_case_pos(&self, time: Tick, map: &Map) -> (Pt2D, Angle, Distance) {
|
||||
let mut dist = self.on.speed_limit(map) * (time - self.started_at).as_time();
|
||||
if self.waiting_for.is_some() {
|
||||
dist = self.on.length(map);
|
||||
}
|
||||
let (pt, angle) = self.on.dist_along(dist, map);
|
||||
(pt, angle, dist)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Eq)]
|
||||
struct SimQueue {
|
||||
id: On,
|
||||
cars_queue: Vec<CarID>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl SimQueue {
|
||||
fn new(id: On, map: &Map) -> SimQueue {
|
||||
SimQueue {
|
||||
id,
|
||||
cars_queue: Vec::new(),
|
||||
capacity: ((id.length(map) / FOLLOWING_DISTANCE).ceil() as usize).max(1),
|
||||
}
|
||||
}
|
||||
|
||||
// TODO it'd be cool to contribute tooltips (like number of cars currently here, capacity) to
|
||||
// tooltip
|
||||
|
||||
fn room_at_end(&self, time: Tick, cars: &BTreeMap<CarID, Car>, map: &Map) -> bool {
|
||||
if self.cars_queue.is_empty() {
|
||||
return true;
|
||||
}
|
||||
if self.cars_queue.len() == self.capacity {
|
||||
return false;
|
||||
}
|
||||
// Has the last car crossed at least FOLLOWING_DISTANCE? If so and the capacity
|
||||
// isn't filled, then we know for sure that there's room, because in this model, we assume
|
||||
// none of the cars just arbitrarily slow down or stop without reason.
|
||||
(time - cars[self.cars_queue.last().unwrap()].started_at).as_time()
|
||||
>= FOLLOWING_DISTANCE / self.id.speed_limit(map)
|
||||
}
|
||||
|
||||
fn reset(
|
||||
&mut self,
|
||||
ids: &Vec<CarID>,
|
||||
cars: &BTreeMap<CarID, Car>,
|
||||
map: &Map,
|
||||
) -> Result<(), InvariantViolated> {
|
||||
let old_queue = self.cars_queue.clone();
|
||||
|
||||
assert!(ids.len() <= self.capacity);
|
||||
self.cars_queue.clear();
|
||||
self.cars_queue.extend(ids);
|
||||
self.cars_queue.sort_by_key(|id| cars[id].started_at);
|
||||
|
||||
// assert here we're not squished together too much
|
||||
let min_dt = FOLLOWING_DISTANCE / self.id.speed_limit(map);
|
||||
for slice in self.cars_queue.windows(2) {
|
||||
let (c1, c2) = (slice[0], slice[1]);
|
||||
let (t1, t2) = (
|
||||
cars[&c1].started_at.as_time(),
|
||||
cars[&c2].started_at.as_time(),
|
||||
);
|
||||
if t2 - t1 < min_dt {
|
||||
return Err(InvariantViolated(format!("uh oh! on {:?}, reset to {:?} broke. min dt is {}, but we have {} at {} and {} at {}. dt is just {}. prev queue was {:?}", self.id, self.cars_queue, min_dt, c1, t1, c2, t2, t2 - t1, old_queue)));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.cars_queue.is_empty()
|
||||
}
|
||||
|
||||
// TODO this starts cars with their front aligned with the end of the lane, sticking their back
|
||||
// into the intersection. :(
|
||||
fn get_draw_cars(
|
||||
&self,
|
||||
time: Tick,
|
||||
sim: &DrivingSimState,
|
||||
map: &Map,
|
||||
properties: &BTreeMap<CarID, Vehicle>,
|
||||
) -> Vec<DrawCar> {
|
||||
if self.cars_queue.is_empty() {
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// TODO base this on actual speed and each vehicle ;)
|
||||
let vehicle = &properties[&self.cars_queue[0]];
|
||||
let stopping_dist = vehicle.stopping_distance(self.id.speed_limit(map));
|
||||
|
||||
let mut results = Vec::new();
|
||||
let (pos1, angle1, dist_along1) =
|
||||
sim.cars[&self.cars_queue[0]].get_best_case_pos(time, map);
|
||||
results.push(DrawCar::new(
|
||||
self.cars_queue[0],
|
||||
vehicle,
|
||||
sim.cars[&self.cars_queue[0]]
|
||||
.waiting_for
|
||||
.and_then(|on| on.maybe_turn()),
|
||||
map,
|
||||
pos1,
|
||||
angle1,
|
||||
stopping_dist,
|
||||
));
|
||||
let mut dist_along_bound = dist_along1;
|
||||
|
||||
for id in self.cars_queue.iter().skip(1) {
|
||||
let (pos, angle, dist_along) = sim.cars[id].get_best_case_pos(time, map);
|
||||
if dist_along_bound - FOLLOWING_DISTANCE > dist_along {
|
||||
results.push(DrawCar::new(
|
||||
*id,
|
||||
vehicle,
|
||||
sim.cars[id].waiting_for.and_then(|on| on.maybe_turn()),
|
||||
map,
|
||||
pos,
|
||||
angle,
|
||||
stopping_dist,
|
||||
));
|
||||
dist_along_bound = dist_along;
|
||||
} else {
|
||||
dist_along_bound -= FOLLOWING_DISTANCE;
|
||||
// If not, we violated room_at_end() and reset() didn't catch it
|
||||
assert!(dist_along_bound >= 0.0 * si::M, "dist_along_bound went negative ({}) for {:?} (length {}) with queue {:?}. first car at {}", dist_along_bound, self.id, self.id.length(map), self.cars_queue, dist_along1);
|
||||
let (pt, angle) = self.id.dist_along(dist_along_bound, map);
|
||||
results.push(DrawCar::new(
|
||||
*id,
|
||||
vehicle,
|
||||
sim.cars[id].waiting_for.and_then(|on| on.maybe_turn()),
|
||||
map,
|
||||
pt,
|
||||
angle,
|
||||
stopping_dist,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
// This manages only actively driving cars
|
||||
#[derive(Serialize, Deserialize, Derivative, PartialEq, Eq)]
|
||||
pub struct DrivingSimState {
|
||||
// Using BTreeMap instead of HashMap so iteration is deterministic.
|
||||
cars: BTreeMap<CarID, Car>,
|
||||
lanes: Vec<SimQueue>,
|
||||
#[serde(serialize_with = "serialize_btreemap")]
|
||||
#[serde(deserialize_with = "deserialize_btreemap")]
|
||||
turns: BTreeMap<TurnID, SimQueue>,
|
||||
debug: Option<CarID>,
|
||||
}
|
||||
|
||||
impl DrivingSimState {
|
||||
pub fn new(map: &Map) -> DrivingSimState {
|
||||
let mut s = DrivingSimState {
|
||||
cars: BTreeMap::new(),
|
||||
// TODO only driving ones
|
||||
lanes: map.all_lanes()
|
||||
.iter()
|
||||
.map(|l| SimQueue::new(On::Lane(l.id), map))
|
||||
.collect(),
|
||||
turns: BTreeMap::new(),
|
||||
debug: None,
|
||||
};
|
||||
for t in map.all_turns().values() {
|
||||
if !t.between_sidewalks {
|
||||
s.turns.insert(t.id, SimQueue::new(On::Turn(t.id), map));
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn populate_info_for_intersections(&self, info: &mut AgentInfo, map: &Map) {
|
||||
for c in self.cars.values() {
|
||||
let id = AgentID::Car(c.id);
|
||||
info.speeds.insert(
|
||||
id,
|
||||
if c.waiting_for.is_some() {
|
||||
0.0 * si::MPS
|
||||
} else {
|
||||
c.on.speed_limit(map)
|
||||
},
|
||||
);
|
||||
info.leaders.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_car_state(&self, c: CarID) -> CarState {
|
||||
if let Some(driving) = self.cars.get(&c) {
|
||||
if driving.waiting_for.is_none() {
|
||||
CarState::Moving
|
||||
} else {
|
||||
CarState::Stuck
|
||||
}
|
||||
} else {
|
||||
// Assume the caller isn't asking about a nonexistent car
|
||||
CarState::Parked
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_active_and_waiting_count(&self) -> (usize, usize) {
|
||||
let waiting = self.cars
|
||||
.values()
|
||||
.filter(|c| c.waiting_for.is_some())
|
||||
.count();
|
||||
(waiting, self.cars.len())
|
||||
}
|
||||
|
||||
pub fn tooltip_lines(&self, id: CarID) -> Option<Vec<String>> {
|
||||
if let Some(c) = self.cars.get(&id) {
|
||||
Some(vec![
|
||||
format!("Car {:?}", id),
|
||||
format!("On {:?}, started at {:?}", c.on, c.started_at),
|
||||
format!("Committed to waiting for {:?}", c.waiting_for),
|
||||
format!("{} lanes left in path", c.path.len()),
|
||||
])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toggle_debug(&mut self, id: CarID) {
|
||||
if let Some(c) = self.debug {
|
||||
if c != id {
|
||||
self.cars.get_mut(&c).unwrap().debug = false;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(car) = self.cars.get_mut(&id) {
|
||||
println!("{}", abstutil::to_json(car));
|
||||
car.debug = !car.debug;
|
||||
self.debug = Some(id);
|
||||
} else {
|
||||
println!("{} is parked somewhere", id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edit_remove_lane(&mut self, id: LaneID) {
|
||||
assert!(self.lanes[id.0].is_empty());
|
||||
}
|
||||
|
||||
pub fn edit_add_lane(&mut self, id: LaneID) {
|
||||
assert!(self.lanes[id.0].is_empty());
|
||||
}
|
||||
|
||||
pub fn edit_remove_turn(&mut self, id: TurnID) {
|
||||
if let Some(queue) = self.turns.get(&id) {
|
||||
assert!(queue.is_empty());
|
||||
}
|
||||
self.turns.remove(&id);
|
||||
}
|
||||
|
||||
pub fn edit_add_turn(&mut self, id: TurnID, map: &Map) {
|
||||
self.turns.insert(id, SimQueue::new(On::Turn(id), map));
|
||||
}
|
||||
|
||||
pub fn step<R: Rng + ?Sized>(
|
||||
&mut self,
|
||||
time: Tick,
|
||||
map: &Map,
|
||||
_parking_sim: &ParkingSimState,
|
||||
intersections: &mut IntersectionSimState,
|
||||
_rng: &mut R,
|
||||
_properties: &BTreeMap<CarID, Vehicle>,
|
||||
) -> Result<Vec<CarParking>, InvariantViolated> {
|
||||
// Could be concurrent, since this is deterministic.
|
||||
let mut requested_moves: Vec<(CarID, Action)> = Vec::new();
|
||||
for c in self.cars.values() {
|
||||
requested_moves.push((c.id, c.react(map, time, &self, intersections)));
|
||||
}
|
||||
|
||||
// In AORTA, there was a split here -- react vs step phase. We're still following the same
|
||||
// thing, but it might be slightly more clear to express it differently?
|
||||
|
||||
// Apply moves, resolving conflicts. This has to happen serially.
|
||||
// It might make more sense to push the conflict resolution down to SimQueue?
|
||||
// TODO should shuffle deterministically here, to be more fair
|
||||
let mut new_car_entered_this_step = HashSet::new();
|
||||
for (id, act) in &requested_moves {
|
||||
match *act {
|
||||
Action::Vanish => {
|
||||
self.cars.remove(&id);
|
||||
}
|
||||
Action::Continue => {}
|
||||
Action::Goto(on) => {
|
||||
// Order matters due to new_car_entered_this_step.
|
||||
// Why is this needed?
|
||||
// - could two cars enter the same lane from the same turn? proper lookahead
|
||||
// behavior WILL fix this
|
||||
// - could two cars enter the same lane from different turns? no, then
|
||||
// conflicting turns are happening simultaneously!
|
||||
// - could two cars enter the same turn? proper lookahead
|
||||
// behavior and not submitting a request until being the leader vehice should
|
||||
// fix
|
||||
if new_car_entered_this_step.contains(&on) {
|
||||
// The car thought they could go, but have to abort last-minute. We may
|
||||
// need to set waiting_for, since the car didn't necessarily return WaitFor
|
||||
// previously.
|
||||
self.cars.get_mut(&id).unwrap().waiting_for = Some(on);
|
||||
} else {
|
||||
new_car_entered_this_step.insert(on);
|
||||
let c = self.cars.get_mut(&id).unwrap();
|
||||
c.step_goto(on, time, map, intersections)?;
|
||||
}
|
||||
}
|
||||
Action::WaitFor(on) => {
|
||||
self.cars.get_mut(&id).unwrap().waiting_for = Some(on);
|
||||
if let On::Turn(t) = on {
|
||||
// Note this is idempotent and does NOT grant the request.
|
||||
intersections.submit_request(Request::for_car(*id, t))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO could simplify this by only adjusting the SimQueues we need above
|
||||
|
||||
// Group cars by lane and turn
|
||||
// TODO ideally, just hash On
|
||||
let mut cars_per_lane = MultiMap::new();
|
||||
let mut cars_per_turn = MultiMap::new();
|
||||
for c in self.cars.values() {
|
||||
match c.on {
|
||||
On::Lane(id) => cars_per_lane.insert(id, c.id),
|
||||
On::Turn(id) => cars_per_turn.insert(id, c.id),
|
||||
};
|
||||
}
|
||||
|
||||
// Reset all queues
|
||||
for l in &mut self.lanes {
|
||||
if let Some(v) = cars_per_lane.get_vec(&l.id.as_lane()) {
|
||||
l.reset(v, &self.cars, map)?;
|
||||
} else {
|
||||
l.reset(&Vec::new(), &self.cars, map)?;
|
||||
}
|
||||
//l.reset(cars_per_lane.get_vec(&l.id).unwrap_or_else(|| &Vec::new()), &self.cars);
|
||||
}
|
||||
for t in self.turns.values_mut() {
|
||||
if let Some(v) = cars_per_turn.get_vec(&t.id.as_turn()) {
|
||||
t.reset(v, &self.cars, map)?;
|
||||
} else {
|
||||
t.reset(&Vec::new(), &self.cars, map)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
// TODO cars basically start in the intersection, with their front bumper right at the
|
||||
// beginning of the lane. later, we want cars starting at arbitrary points in the middle of the
|
||||
// lane (from a building), so just ignore this problem for now.
|
||||
// True if we spawned one
|
||||
pub fn start_car_on_lane(
|
||||
&mut self,
|
||||
time: Tick,
|
||||
car: CarID,
|
||||
_parking: CarParking,
|
||||
mut path: VecDeque<LaneID>,
|
||||
map: &Map,
|
||||
_properties: &BTreeMap<CarID, Vehicle>,
|
||||
) -> bool {
|
||||
let start = path.pop_front().unwrap();
|
||||
|
||||
if !self.lanes[start.0].room_at_end(time, &self.cars, map) {
|
||||
// TODO car should enter Unparking state and wait for room
|
||||
println!("No room for {} to start driving on {}", car, start);
|
||||
return false;
|
||||
}
|
||||
|
||||
self.cars.insert(
|
||||
car,
|
||||
Car {
|
||||
id: car,
|
||||
path,
|
||||
started_at: time,
|
||||
on: On::Lane(start),
|
||||
waiting_for: None,
|
||||
debug: false,
|
||||
},
|
||||
);
|
||||
self.lanes[start.0].cars_queue.push(car);
|
||||
true
|
||||
}
|
||||
|
||||
pub fn get_draw_car(
|
||||
&self,
|
||||
id: CarID,
|
||||
time: Tick,
|
||||
map: &Map,
|
||||
properties: &BTreeMap<CarID, Vehicle>,
|
||||
) -> Option<DrawCar> {
|
||||
let all = match self.cars.get(&id)?.on {
|
||||
On::Lane(l) => self.get_draw_cars_on_lane(l, time, map, properties),
|
||||
On::Turn(t) => self.get_draw_cars_on_turn(t, time, map, properties),
|
||||
};
|
||||
all.into_iter().find(|c| c.id == id)
|
||||
}
|
||||
|
||||
pub fn get_draw_cars_on_lane(
|
||||
&self,
|
||||
lane: LaneID,
|
||||
time: Tick,
|
||||
map: &Map,
|
||||
properties: &BTreeMap<CarID, Vehicle>,
|
||||
) -> Vec<DrawCar> {
|
||||
self.lanes[lane.0].get_draw_cars(time, self, map, properties)
|
||||
}
|
||||
|
||||
pub fn get_draw_cars_on_turn(
|
||||
&self,
|
||||
turn: TurnID,
|
||||
time: Tick,
|
||||
map: &Map,
|
||||
properties: &BTreeMap<CarID, Vehicle>,
|
||||
) -> Vec<DrawCar> {
|
||||
if let Some(queue) = self.turns.get(&turn) {
|
||||
return queue.get_draw_cars(time, self, map, properties);
|
||||
}
|
||||
return Vec::new();
|
||||
}
|
||||
}
|
125
sim/src/sim.rs
125
sim/src/sim.rs
@ -4,123 +4,18 @@ use control::ControlMap;
|
||||
use dimensioned::si;
|
||||
use draw_car::DrawCar;
|
||||
use draw_ped::DrawPedestrian;
|
||||
use driving;
|
||||
use driving::DrivingSimState;
|
||||
use intersections::{AgentInfo, IntersectionSimState};
|
||||
use kinematics::Vehicle;
|
||||
use map_model::{IntersectionID, LaneID, LaneType, Map, Turn, TurnID};
|
||||
use parametric_driving;
|
||||
use parking::{ParkingSimState, ParkingSpot};
|
||||
use rand::{FromEntropy, Rng, SeedableRng, XorShiftRng};
|
||||
use rand::{FromEntropy, SeedableRng, XorShiftRng};
|
||||
use spawn::Spawner;
|
||||
use std::collections::{BTreeMap, HashMap, HashSet, VecDeque};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::f64;
|
||||
use std::time::{Duration, Instant};
|
||||
use walking::WalkingSimState;
|
||||
use {CarID, CarState, InvariantViolated, PedestrianID, Tick, TIMESTEP};
|
||||
|
||||
#[derive(Serialize, Deserialize, Derivative, PartialEq, Eq)]
|
||||
pub enum DrivingModel {
|
||||
V1(driving::DrivingSimState),
|
||||
V2(parametric_driving::DrivingSimState),
|
||||
}
|
||||
|
||||
macro_rules! delegate {
|
||||
// Immutable, no arguments, return type
|
||||
(fn $fxn_name:ident(&self) -> $ret:ty) => {
|
||||
fn $fxn_name(&self) -> $ret {
|
||||
match self {
|
||||
DrivingModel::V1(s) => s.$fxn_name(),
|
||||
DrivingModel::V2(s) => s.$fxn_name(),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Immutable, arguments, return type
|
||||
(fn $fxn_name:ident(&self, $($value:ident: $type:ty),* ) -> $ret:ty) => {
|
||||
fn $fxn_name(&self, $( $value: $type ),*) -> $ret {
|
||||
match self {
|
||||
DrivingModel::V1(s) => s.$fxn_name($( $value ),*),
|
||||
DrivingModel::V2(s) => s.$fxn_name($( $value ),*),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Immutable, arguments, no return type
|
||||
(fn $fxn_name:ident(&self, $($value:ident: $type:ty),* )) => {
|
||||
fn $fxn_name(&self, $( $value: $type ),*) {
|
||||
match self {
|
||||
DrivingModel::V1(s) => s.$fxn_name($( $value ),*),
|
||||
DrivingModel::V2(s) => s.$fxn_name($( $value ),*),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Mutable, arguments, return type
|
||||
(fn $fxn_name:ident(&mut self, $($value:ident: $type:ty),* ) -> $ret:ty) => {
|
||||
fn $fxn_name(&mut self, $( $value: $type ),*) -> $ret {
|
||||
match self {
|
||||
DrivingModel::V1(s) => s.$fxn_name($( $value ),*),
|
||||
DrivingModel::V2(s) => s.$fxn_name($( $value ),*),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Public, mutable, arguments, return type
|
||||
(pub fn $fxn_name:ident(&mut self, $($value:ident: $type:ty),* ) -> $ret:ty) => {
|
||||
pub fn $fxn_name(&mut self, $( $value: $type ),*) -> $ret {
|
||||
match self {
|
||||
DrivingModel::V1(s) => s.$fxn_name($( $value ),*),
|
||||
DrivingModel::V2(s) => s.$fxn_name($( $value ),*),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO hack, hardcoding the generic type bounds, because I can't figure it out :(
|
||||
// Mutable, arguments, return type
|
||||
(fn $fxn_name:ident<R: Rng + ?Sized>(&mut self, $($value:ident: $type:ty),* ) -> $ret:ty) => {
|
||||
fn $fxn_name<R: Rng + ?Sized>(&mut self, $( $value: $type ),*) -> $ret {
|
||||
match self {
|
||||
DrivingModel::V1(s) => s.$fxn_name($( $value ),*),
|
||||
DrivingModel::V2(s) => s.$fxn_name($( $value ),*),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Mutable, arguments, no return type
|
||||
(fn $fxn_name:ident(&mut self, $($value:ident: $type:ty),* )) => {
|
||||
fn $fxn_name(&mut self, $( $value: $type ),*) {
|
||||
match self {
|
||||
DrivingModel::V1(s) => s.$fxn_name($( $value ),*),
|
||||
DrivingModel::V2(s) => s.$fxn_name($( $value ),*),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl DrivingModel {
|
||||
delegate!(fn populate_info_for_intersections(&self, info: &mut AgentInfo, map: &Map));
|
||||
delegate!(fn get_car_state(&self, c: CarID) -> CarState);
|
||||
delegate!(fn get_active_and_waiting_count(&self) -> (usize, usize));
|
||||
delegate!(fn tooltip_lines(&self, id: CarID) -> Option<Vec<String>>);
|
||||
delegate!(fn toggle_debug(&mut self, id: CarID));
|
||||
delegate!(fn edit_remove_lane(&mut self, id: LaneID));
|
||||
delegate!(fn edit_add_lane(&mut self, id: LaneID));
|
||||
delegate!(fn edit_remove_turn(&mut self, id: TurnID));
|
||||
delegate!(fn edit_add_turn(&mut self, id: TurnID, map: &Map));
|
||||
delegate!(fn step<R: Rng + ?Sized>(&mut self, time: Tick, map: &Map, parking: &ParkingSimState, intersections: &mut IntersectionSimState, rng: &mut R, properties: &BTreeMap<CarID, Vehicle>) -> Result<Vec<CarParking>, InvariantViolated>);
|
||||
delegate!(pub fn start_car_on_lane(
|
||||
&mut self,
|
||||
time: Tick,
|
||||
car: CarID,
|
||||
parking: CarParking,
|
||||
path: VecDeque<LaneID>,
|
||||
map: &Map,
|
||||
properties: &BTreeMap<CarID, Vehicle>
|
||||
) -> bool);
|
||||
delegate!(fn get_draw_car(&self, id: CarID, time: Tick, map: &Map, properties: &BTreeMap<CarID, Vehicle>) -> Option<DrawCar>);
|
||||
delegate!(fn get_draw_cars_on_lane(&self, lane: LaneID, time: Tick, map: &Map, properties: &BTreeMap<CarID, Vehicle>) -> Vec<DrawCar>);
|
||||
delegate!(fn get_draw_cars_on_turn(&self, turn: TurnID, time: Tick, map: &Map, properties: &BTreeMap<CarID, Vehicle>) -> Vec<DrawCar>);
|
||||
}
|
||||
use {CarID, CarState, PedestrianID, Tick, TIMESTEP};
|
||||
|
||||
#[derive(Serialize, Deserialize, Derivative)]
|
||||
#[derivative(PartialEq, Eq)]
|
||||
@ -133,7 +28,7 @@ pub struct Sim {
|
||||
|
||||
spawner: Spawner,
|
||||
intersection_state: IntersectionSimState,
|
||||
driving_state: DrivingModel,
|
||||
driving_state: DrivingSimState,
|
||||
parking_state: ParkingSimState,
|
||||
walking_state: WalkingSimState,
|
||||
|
||||
@ -141,21 +36,15 @@ pub struct Sim {
|
||||
}
|
||||
|
||||
impl Sim {
|
||||
pub fn new(map: &Map, rng_seed: Option<u8>, parametric_sim: bool) -> Sim {
|
||||
pub fn new(map: &Map, rng_seed: Option<u8>) -> Sim {
|
||||
let mut rng = XorShiftRng::from_entropy();
|
||||
if let Some(seed) = rng_seed {
|
||||
rng = XorShiftRng::from_seed([seed; 16]);
|
||||
}
|
||||
|
||||
let driving_state = if parametric_sim {
|
||||
DrivingModel::V2(parametric_driving::DrivingSimState::new(map))
|
||||
} else {
|
||||
DrivingModel::V1(driving::DrivingSimState::new(map))
|
||||
};
|
||||
|
||||
Sim {
|
||||
rng,
|
||||
driving_state,
|
||||
driving_state: DrivingSimState::new(map),
|
||||
spawner: Spawner::empty(),
|
||||
intersection_state: IntersectionSimState::new(map),
|
||||
parking_state: ParkingSimState::new(map),
|
||||
|
@ -1,9 +1,10 @@
|
||||
use driving::DrivingSimState;
|
||||
use kinematics::Vehicle;
|
||||
use map_model;
|
||||
use map_model::{LaneID, Map};
|
||||
use parking::ParkingSimState;
|
||||
use rand::Rng;
|
||||
use sim::{CarParking, DrivingModel};
|
||||
use sim::CarParking;
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::time::Instant;
|
||||
use walking::WalkingSimState;
|
||||
@ -47,7 +48,7 @@ impl Spawner {
|
||||
map: &Map,
|
||||
parking_sim: &mut ParkingSimState,
|
||||
walking_sim: &mut WalkingSimState,
|
||||
driving_sim: &mut DrivingModel,
|
||||
driving_sim: &mut DrivingSimState,
|
||||
properties: &BTreeMap<CarID, Vehicle>,
|
||||
) {
|
||||
for p in self.spawn_parked_cars.drain(0..) {
|
||||
|
@ -13,7 +13,7 @@ fn aorta_model_completes() {
|
||||
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
|
||||
let mut sim = sim::Sim::new(&map, Some(rng_seed), false);
|
||||
let mut sim = sim::Sim::new(&map, Some(rng_seed));
|
||||
sim.seed_pedestrians(&map, spawn_count);
|
||||
sim.seed_parked_cars(0.5);
|
||||
sim.start_many_parked_cars(&map, spawn_count);
|
||||
|
@ -3,95 +3,50 @@ extern crate control;
|
||||
extern crate map_model;
|
||||
extern crate sim;
|
||||
|
||||
// TODO better parametric tests with separate names
|
||||
|
||||
#[test]
|
||||
fn serialization() {
|
||||
for parametric_sim in vec![true, false] {
|
||||
// This assumes this map has been built
|
||||
let input = "../data/small.abst";
|
||||
let rng_seed = 42;
|
||||
let spawn_count = 10;
|
||||
// This assumes this map has been built
|
||||
let input = "../data/small.abst";
|
||||
let rng_seed = 42;
|
||||
let spawn_count = 10;
|
||||
|
||||
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
|
||||
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
|
||||
|
||||
let mut sim = sim::Sim::new(&map, Some(rng_seed), parametric_sim);
|
||||
sim.seed_pedestrians(&map, spawn_count);
|
||||
sim.seed_parked_cars(0.5);
|
||||
sim.start_many_parked_cars(&map, spawn_count);
|
||||
let mut sim = sim::Sim::new(&map, Some(rng_seed));
|
||||
sim.seed_pedestrians(&map, spawn_count);
|
||||
sim.seed_parked_cars(0.5);
|
||||
sim.start_many_parked_cars(&map, spawn_count);
|
||||
|
||||
// Does savestating produce the same string?
|
||||
let save1 = abstutil::to_json(&sim);
|
||||
let save2 = abstutil::to_json(&sim);
|
||||
assert_eq!(save1, save2);
|
||||
}
|
||||
// Does savestating produce the same string?
|
||||
let save1 = abstutil::to_json(&sim);
|
||||
let save2 = abstutil::to_json(&sim);
|
||||
assert_eq!(save1, save2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_scratch() {
|
||||
for parametric_sim in vec![true, false] {
|
||||
// This assumes this map has been built
|
||||
let input = "../data/small.abst";
|
||||
let rng_seed = 42;
|
||||
let spawn_count = 100;
|
||||
// This assumes this map has been built
|
||||
let input = "../data/small.abst";
|
||||
let rng_seed = 42;
|
||||
let spawn_count = 100;
|
||||
|
||||
println!("Creating two simulations");
|
||||
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
println!("Creating two simulations");
|
||||
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
|
||||
let mut sim1 = sim::Sim::new(&map, Some(rng_seed), parametric_sim);
|
||||
let mut sim2 = sim::Sim::new(&map, Some(rng_seed), parametric_sim);
|
||||
sim1.seed_pedestrians(&map, spawn_count);
|
||||
sim1.seed_parked_cars(0.5);
|
||||
sim1.start_many_parked_cars(&map, spawn_count);
|
||||
sim2.seed_pedestrians(&map, spawn_count);
|
||||
sim2.seed_parked_cars(0.5);
|
||||
sim2.start_many_parked_cars(&map, spawn_count);
|
||||
|
||||
for _ in 1..600 {
|
||||
if sim1 != sim2 {
|
||||
// TODO write to temporary files somewhere
|
||||
// TODO need to sort dicts in json output to compare
|
||||
abstutil::write_json("sim1_state.json", &sim1).unwrap();
|
||||
abstutil::write_json("sim2_state.json", &sim2).unwrap();
|
||||
panic!(
|
||||
"sim state differs at {}. compare sim1_state.json and sim2_state.json",
|
||||
sim1.time
|
||||
);
|
||||
}
|
||||
sim1.step(&map, &control_map);
|
||||
sim2.step(&map, &control_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_savestating() {
|
||||
for parametric_sim in vec![true, false] {
|
||||
// This assumes this map has been built
|
||||
let input = "../data/small.abst";
|
||||
let rng_seed = 42;
|
||||
let spawn_count = 100;
|
||||
|
||||
println!("Creating two simulations");
|
||||
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
|
||||
let mut sim1 = sim::Sim::new(&map, Some(rng_seed), parametric_sim);
|
||||
let mut sim2 = sim::Sim::new(&map, Some(rng_seed), parametric_sim);
|
||||
sim1.seed_pedestrians(&map, spawn_count);
|
||||
sim1.seed_parked_cars(0.5);
|
||||
sim1.start_many_parked_cars(&map, spawn_count);
|
||||
sim2.seed_pedestrians(&map, spawn_count);
|
||||
sim2.seed_parked_cars(0.5);
|
||||
sim2.start_many_parked_cars(&map, spawn_count);
|
||||
|
||||
for _ in 1..600 {
|
||||
sim1.step(&map, &control_map);
|
||||
sim2.step(&map, &control_map);
|
||||
}
|
||||
let mut sim1 = sim::Sim::new(&map, Some(rng_seed));
|
||||
let mut sim2 = sim::Sim::new(&map, Some(rng_seed));
|
||||
sim1.seed_pedestrians(&map, spawn_count);
|
||||
sim1.seed_parked_cars(0.5);
|
||||
sim1.start_many_parked_cars(&map, spawn_count);
|
||||
sim2.seed_pedestrians(&map, spawn_count);
|
||||
sim2.seed_parked_cars(0.5);
|
||||
sim2.start_many_parked_cars(&map, spawn_count);
|
||||
|
||||
for _ in 1..600 {
|
||||
if sim1 != sim2 {
|
||||
// TODO write to temporary files somewhere
|
||||
// TODO need to sort dicts in json output to compare
|
||||
abstutil::write_json("sim1_state.json", &sim1).unwrap();
|
||||
abstutil::write_json("sim2_state.json", &sim2).unwrap();
|
||||
panic!(
|
||||
@ -99,29 +54,69 @@ fn with_savestating() {
|
||||
sim1.time
|
||||
);
|
||||
}
|
||||
|
||||
abstutil::write_json("sim1_savestate.json", &sim1).unwrap();
|
||||
|
||||
for _ in 1..60 {
|
||||
sim1.step(&map, &control_map);
|
||||
}
|
||||
|
||||
if sim1 == sim2 {
|
||||
abstutil::write_json("sim1_state.json", &sim1).unwrap();
|
||||
abstutil::write_json("sim2_state.json", &sim2).unwrap();
|
||||
panic!("sim state unexpectedly the same at {}. compare sim1_state.json and sim2_state.json", sim1.time);
|
||||
}
|
||||
|
||||
let sim3: sim::Sim = abstutil::read_json("sim1_savestate.json").unwrap();
|
||||
if sim3 != sim2 {
|
||||
abstutil::write_json("sim3_state.json", &sim3).unwrap();
|
||||
abstutil::write_json("sim2_state.json", &sim2).unwrap();
|
||||
panic!(
|
||||
"sim state differs at {}. compare sim3_state.json and sim2_state.json",
|
||||
sim1.time
|
||||
);
|
||||
}
|
||||
|
||||
std::fs::remove_file("sim1_savestate.json").unwrap();
|
||||
sim1.step(&map, &control_map);
|
||||
sim2.step(&map, &control_map);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_savestating() {
|
||||
// This assumes this map has been built
|
||||
let input = "../data/small.abst";
|
||||
let rng_seed = 42;
|
||||
let spawn_count = 100;
|
||||
|
||||
println!("Creating two simulations");
|
||||
let map = map_model::Map::new(input, &map_model::Edits::new()).expect("Couldn't load map");
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
|
||||
let mut sim1 = sim::Sim::new(&map, Some(rng_seed));
|
||||
let mut sim2 = sim::Sim::new(&map, Some(rng_seed));
|
||||
sim1.seed_pedestrians(&map, spawn_count);
|
||||
sim1.seed_parked_cars(0.5);
|
||||
sim1.start_many_parked_cars(&map, spawn_count);
|
||||
sim2.seed_pedestrians(&map, spawn_count);
|
||||
sim2.seed_parked_cars(0.5);
|
||||
sim2.start_many_parked_cars(&map, spawn_count);
|
||||
|
||||
for _ in 1..600 {
|
||||
sim1.step(&map, &control_map);
|
||||
sim2.step(&map, &control_map);
|
||||
}
|
||||
|
||||
if sim1 != sim2 {
|
||||
abstutil::write_json("sim1_state.json", &sim1).unwrap();
|
||||
abstutil::write_json("sim2_state.json", &sim2).unwrap();
|
||||
panic!(
|
||||
"sim state differs at {}. compare sim1_state.json and sim2_state.json",
|
||||
sim1.time
|
||||
);
|
||||
}
|
||||
|
||||
abstutil::write_json("sim1_savestate.json", &sim1).unwrap();
|
||||
|
||||
for _ in 1..60 {
|
||||
sim1.step(&map, &control_map);
|
||||
}
|
||||
|
||||
if sim1 == sim2 {
|
||||
abstutil::write_json("sim1_state.json", &sim1).unwrap();
|
||||
abstutil::write_json("sim2_state.json", &sim2).unwrap();
|
||||
panic!(
|
||||
"sim state unexpectedly the same at {}. compare sim1_state.json and sim2_state.json",
|
||||
sim1.time
|
||||
);
|
||||
}
|
||||
|
||||
let sim3: sim::Sim = abstutil::read_json("sim1_savestate.json").unwrap();
|
||||
if sim3 != sim2 {
|
||||
abstutil::write_json("sim3_state.json", &sim3).unwrap();
|
||||
abstutil::write_json("sim2_state.json", &sim2).unwrap();
|
||||
panic!(
|
||||
"sim state differs at {}. compare sim3_state.json and sim2_state.json",
|
||||
sim1.time
|
||||
);
|
||||
}
|
||||
|
||||
std::fs::remove_file("sim1_savestate.json").unwrap();
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ fn wander_around_for_parking() {
|
||||
fn setup(map: map_model::Map) -> (map_model::Map, control::ControlMap, sim::Sim) {
|
||||
let rng_seed = 123;
|
||||
let control_map = control::ControlMap::new(&map);
|
||||
let sim = sim::Sim::new(&map, Some(rng_seed), false);
|
||||
let sim = sim::Sim::new(&map, Some(rng_seed));
|
||||
(map, control_map, sim)
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user