diff --git a/map_model/src/pathfind.rs b/map_model/src/pathfind.rs index 18a74275e4..88a65d4a65 100644 --- a/map_model/src/pathfind.rs +++ b/map_model/src/pathfind.rs @@ -331,8 +331,7 @@ impl Pathfinder { .pathfind(map, start, goal)?; for s in internal_steps.into_iter() { if let InternalPathStep::RideBus(stop1, stop2, route) = s { - //return Some((stop1, stop2, route)); - return None; + return Some((stop1, stop2, route)); } } None diff --git a/sim/src/make/spawner.rs b/sim/src/make/spawner.rs index 2daeb90565..676881dc6e 100644 --- a/sim/src/make/spawner.rs +++ b/sim/src/make/spawner.rs @@ -217,7 +217,7 @@ impl TripSpawner { )); } DrivingGoal::Border(_, _) => {} - } + }; let trip = trips.new_trip(start_time, legs); scheduler.enqueue_command(Command::SpawnPed( @@ -231,8 +231,27 @@ impl TripSpawner { }, )); } - TripSpec::UsingTransit(_, _, _, _, _) => { - panic!("implement"); + TripSpec::UsingTransit(start, route, stop1, stop2, goal) => { + let walk_to = SidewalkSpot::bus_stop(stop1, map); + let trip = trips.new_trip( + start_time, + vec![ + TripLeg::Walk(ped_id.unwrap(), walk_to.clone()), + TripLeg::RideBus(ped_id.unwrap(), route, stop2), + TripLeg::Walk(ped_id.unwrap(), goal), + ], + ); + + scheduler.enqueue_command(Command::SpawnPed( + start_time, + CreatePedestrian { + id: ped_id.unwrap(), + start, + goal: walk_to, + path, + trip, + }, + )); } } } @@ -272,9 +291,12 @@ impl TripSpec { can_use_bike_lanes: false, can_use_bus_lanes: false, }, - TripSpec::UsingTransit(_, _, _, _, _) => { - panic!("implement"); - } + TripSpec::UsingTransit(start, _, stop1, _, _) => PathRequest { + start: start.sidewalk_pos, + end: SidewalkSpot::bus_stop(*stop1, map).sidewalk_pos, + can_use_bike_lanes: false, + can_use_bus_lanes: false, + }, } } } diff --git a/sim/src/mechanics/driving.rs b/sim/src/mechanics/driving.rs index f079be9bef..bf6547d26a 100644 --- a/sim/src/mechanics/driving.rs +++ b/sim/src/mechanics/driving.rs @@ -2,8 +2,8 @@ use crate::mechanics::car::{Car, CarState}; use crate::mechanics::queue::Queue; use crate::{ ActionAtEnd, AgentID, CarID, CreateCar, DrawCarInput, IntersectionSimState, ParkedCar, - ParkingSimState, Scheduler, TimeInterval, TransitSimState, TripManager, BUS_LENGTH, - FOLLOWING_DISTANCE, + ParkingSimState, Scheduler, TimeInterval, TransitSimState, TripManager, WalkingSimState, + BUS_LENGTH, FOLLOWING_DISTANCE, }; use abstutil::{deserialize_btreemap, serialize_btreemap}; use ezgui::{Color, GfxCtx}; @@ -186,6 +186,7 @@ impl DrivingSimState { trips: &mut TripManager, scheduler: &mut Scheduler, transit: &mut TransitSimState, + walking: &mut WalkingSimState, ) { // Promote Crossing to Queued and Unparking to Crossing. for queue in self.queues.values_mut() { @@ -268,7 +269,7 @@ impl DrivingSimState { ); } Some(ActionAtEnd::BusAtStop) => { - transit.bus_arrived_at_stop(car.vehicle.id); + transit.bus_arrived_at_stop(car.vehicle.id, walking); car.state = CarState::Idling( dist, TimeInterval::new(time, time + TIME_TO_WAIT_AT_STOP), diff --git a/sim/src/mechanics/walking.rs b/sim/src/mechanics/walking.rs index c9c4c26ecb..f768d1f5d2 100644 --- a/sim/src/mechanics/walking.rs +++ b/sim/src/mechanics/walking.rs @@ -1,6 +1,7 @@ use crate::{ AgentID, CreatePedestrian, DistanceInterval, DrawPedestrianInput, IntersectionSimState, - ParkingSimState, PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot, TimeInterval, TripManager, + ParkingSimState, PedestrianID, Scheduler, SidewalkPOI, SidewalkSpot, TimeInterval, + TransitSimState, TripManager, }; use abstutil::{deserialize_multimap, serialize_multimap, MultiMap}; use geom::{Distance, Duration, Line, Speed}; @@ -102,6 +103,7 @@ impl WalkingSimState { parking: &ParkingSimState, scheduler: &mut Scheduler, trips: &mut TripManager, + transit: &mut TransitSimState, ) { let mut delete = Vec::new(); for ped in self.peds.values_mut() { @@ -128,7 +130,15 @@ impl WalkingSimState { ); } SidewalkPOI::BusStop(stop) => { - panic!("implement"); + if trips.ped_reached_bus_stop(ped.id, stop, map, transit) { + delete.push(ped.id); + self.peds_per_traversable.remove( + ped.path.current_step().as_traversable(), + ped.id, + ); + } else { + ped.state = PedState::WaitingForBus; + } } SidewalkPOI::Border(i) => { delete.push(ped.id); @@ -209,6 +219,7 @@ impl WalkingSimState { ped.state = ped.crossing_state(spot.sidewalk_pos.dist_along(), time, map); } } + PedState::WaitingForBus => {} }; } for id in delete { @@ -216,6 +227,13 @@ impl WalkingSimState { } } + pub fn ped_boarded_bus(&mut self, id: PedestrianID) { + match self.peds.remove(&id).unwrap().state { + PedState::WaitingForBus => {} + _ => unreachable!(), + }; + } + pub fn debug_ped(&self, id: PedestrianID) { if let Some(ped) = self.peds.get(&id) { println!("{}", abstutil::to_json(ped)); @@ -289,6 +307,7 @@ impl Pedestrian { PedState::EnteringBuilding(b, _) => map.get_b(b).front_path.sidewalk.dist_along(), PedState::StartingToBike(ref spot, _, _) => spot.sidewalk_pos.dist_along(), PedState::FinishingBiking(ref spot, _, _) => spot.sidewalk_pos.dist_along(), + PedState::WaitingForBus => self.goal.sidewalk_pos.dist_along(), } } @@ -322,6 +341,7 @@ impl Pedestrian { PedState::FinishingBiking(_, ref line, ref time_int) => { line.percent_along(time_int.percent(time)) } + PedState::WaitingForBus => self.goal.sidewalk_pos.pt(map), }; DrawPedestrianInput { @@ -349,4 +369,5 @@ enum PedState { EnteringBuilding(BuildingID, TimeInterval), StartingToBike(SidewalkSpot, Line, TimeInterval), FinishingBiking(SidewalkSpot, Line, TimeInterval), + WaitingForBus, } diff --git a/sim/src/sim.rs b/sim/src/sim.rs index b66b32e56b..65d38cd354 100644 --- a/sim/src/sim.rs +++ b/sim/src/sim.rs @@ -275,6 +275,7 @@ impl Sim { &mut self.trips, &mut self.scheduler, &mut self.transit, + &mut self.walking, ); self.walking.step_if_needed( self.time, @@ -283,6 +284,7 @@ impl Sim { &self.parking, &mut self.scheduler, &mut self.trips, + &mut self.transit, ); // Spawn stuff at the end, so we can see the correct state of everything else at this time. diff --git a/sim/src/transit.rs b/sim/src/transit.rs index 7f100e6736..b3041de23b 100644 --- a/sim/src/transit.rs +++ b/sim/src/transit.rs @@ -1,7 +1,7 @@ -use crate::{CarID, Event, PedestrianID, Router}; +use crate::{CarID, Event, PedestrianID, Router, WalkingSimState}; use abstutil::{deserialize_btreemap, serialize_btreemap}; use geom::Distance; -use map_model::{BusRoute, BusRouteID, BusStop, Map, Path, PathRequest, Pathfinder}; +use map_model::{BusRoute, BusRouteID, BusStop, BusStopID, Map, Path, PathRequest, Pathfinder}; use serde_derive::{Deserialize, Serialize}; use std::collections::BTreeMap; @@ -33,7 +33,8 @@ impl Route { struct Bus { car: CarID, route: BusRouteID, - passengers: Vec, + // Where does each passenger want to deboard? + passengers: Vec<(PedestrianID, BusStopID)>, state: BusState, } @@ -56,6 +57,8 @@ pub struct TransitSimState { deserialize_with = "deserialize_btreemap" )] routes: BTreeMap, + // Can organize this more to make querying cheaper + peds_waiting: Vec<(PedestrianID, BusStopID, BusRouteID, BusStopID)>, events: Vec, } @@ -65,6 +68,7 @@ impl TransitSimState { TransitSimState { buses: BTreeMap::new(), routes: BTreeMap::new(), + peds_waiting: Vec::new(), events: Vec::new(), } } @@ -130,15 +134,27 @@ impl TransitSimState { ); } - pub fn bus_arrived_at_stop(&mut self, id: CarID) { + pub fn bus_arrived_at_stop(&mut self, id: CarID, walking: &mut WalkingSimState) { let mut bus = self.buses.get_mut(&id).unwrap(); match bus.state { BusState::DrivingToStop(stop_idx) => { bus.state = BusState::AtStop(stop_idx); - self.events.push(Event::BusArrivedAtStop( - id, - self.routes[&bus.route].stops[stop_idx].id, - )); + let stop = self.routes[&bus.route].stops[stop_idx].id; + self.events.push(Event::BusArrivedAtStop(id, stop)); + + // Board new passengers. + let mut still_waiting = Vec::new(); + for (ped, stop1, route, stop2) in self.peds_waiting.drain(..) { + if stop == stop1 && bus.route == route { + bus.passengers.push((ped, stop2)); + self.events.push(Event::PedEntersBus(ped, id)); + walking.ped_boarded_bus(ped); + // TODO shift trips + } else { + still_waiting.push((ped, stop1, route, stop2)); + } + } + self.peds_waiting = still_waiting; } BusState::AtStop(_) => unreachable!(), }; @@ -174,6 +190,35 @@ impl TransitSimState { } } + // If true, the pedestrian boarded a bus immediately. + pub fn ped_waiting_for_bus( + &mut self, + ped: PedestrianID, + stop1: BusStopID, + route_id: BusRouteID, + stop2: BusStopID, + ) -> bool { + assert!(stop1 != stop2); + let route = &self.routes[&route_id]; + for bus in &route.buses { + if let BusState::AtStop(idx) = self.buses[bus].state { + if route.stops[idx].id == stop1 { + self.buses + .get_mut(bus) + .unwrap() + .passengers + .push((ped, stop2)); + // TODO shift trips + self.events.push(Event::PedEntersBus(ped, *bus)); + return true; + } + } + } + + self.peds_waiting.push((ped, stop1, route_id, stop2)); + false + } + pub fn collect_events(&mut self) -> Vec { self.events.drain(..).collect() } diff --git a/sim/src/trips.rs b/sim/src/trips.rs index 7b0efb99b4..5d6d081ffb 100644 --- a/sim/src/trips.rs +++ b/sim/src/trips.rs @@ -1,6 +1,7 @@ use crate::{ AgentID, CarID, Command, CreateCar, CreatePedestrian, DrivingGoal, Event, ParkingSimState, - ParkingSpot, PedestrianID, Router, Scheduler, SidewalkPOI, SidewalkSpot, TripID, Vehicle, + ParkingSpot, PedestrianID, Router, Scheduler, SidewalkPOI, SidewalkSpot, TransitSimState, + TripID, Vehicle, }; use abstutil::{deserialize_btreemap, serialize_btreemap}; use geom::Duration; @@ -284,6 +285,28 @@ impl TripManager { trip.finished_at = Some(time); } + // If true, the pedestrian boarded a bus immediately. + pub fn ped_reached_bus_stop( + &mut self, + ped: PedestrianID, + stop: BusStopID, + map: &Map, + transit: &mut TransitSimState, + ) -> bool { + self.events.push(Event::PedReachedBusStop(ped, stop)); + let trip = &self.trips[self.active_trip_mode[&AgentID::Pedestrian(ped)].0]; + assert_eq!( + trip.legs[0], + TripLeg::Walk(ped, SidewalkSpot::bus_stop(stop, map)) + ); + match trip.legs[1] { + TripLeg::RideBus(_, route, stop2) => { + transit.ped_waiting_for_bus(ped, stop, route, stop2) + } + _ => unreachable!(), + } + } + pub fn ped_reached_border( &mut self, time: Duration,