#![allow(
clippy::unwrap_used,
clippy::missing_docs_in_private_items,
clippy::missing_const_for_fn
)]
use std::collections::HashMap;
use elevator_core::components::CallDirection;
use elevator_core::dispatch::{BuiltinStrategy, DispatchManifest, DispatchStrategy, ElevatorGroup};
use elevator_core::entity::EntityId;
use elevator_core::ids::GroupId;
use elevator_core::prelude::*;
use elevator_core::stop::StopConfig;
use elevator_core::world::World;
#[derive(Default)]
struct IdlePenaltyDispatch {
last_served_tick: HashMap<EntityId, u64>,
idle_for: HashMap<EntityId, f64>,
tick: u64,
}
impl DispatchStrategy for IdlePenaltyDispatch {
fn pre_dispatch(
&mut self,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &mut World,
) {
self.tick = self.tick.saturating_add(1);
}
fn prepare_car(
&mut self,
car: EntityId,
_car_position: f64,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &World,
) {
let last = self.last_served_tick.get(&car).copied().unwrap_or(0);
let idle = self.tick.saturating_sub(last) as f64;
self.idle_for.insert(car, idle);
self.last_served_tick.insert(car, self.tick);
}
fn rank(
&mut self,
car: EntityId,
car_position: f64,
_stop: EntityId,
stop_position: f64,
_group: &ElevatorGroup,
_manifest: &DispatchManifest,
_world: &World,
) -> Option<f64> {
let distance = (car_position - stop_position).abs();
let idle_for = self.idle_for.get(&car).copied().unwrap_or(0.0);
Some(0.01f64.mul_add(-idle_for, distance).max(0.0))
}
fn notify_removed(&mut self, elevator: EntityId) {
self.last_served_tick.remove(&elevator);
self.idle_for.remove(&elevator);
}
}
fn main() {
let mut sim = SimulationBuilder::demo()
.stops(vec![
StopConfig {
id: StopId(0),
name: "Lobby".into(),
position: 0.0,
},
StopConfig {
id: StopId(1),
name: "Mezzanine".into(),
position: 4.0,
},
StopConfig {
id: StopId(2),
name: "Roof".into(),
position: 8.0,
},
])
.build()
.unwrap();
sim.set_dispatch(
GroupId(0),
Box::new(IdlePenaltyDispatch::default()),
BuiltinStrategy::Custom("idle_penalty".into()),
);
sim.spawn_rider(StopId(0), StopId(2), 70.0).unwrap();
sim.spawn_rider(StopId(1), StopId(0), 72.0).unwrap();
sim.spawn_rider(StopId(2), StopId(1), 80.0).unwrap();
let mezzanine = sim.stop_entity(StopId(1)).unwrap();
let lobby_car = sim.world().elevator_ids()[0];
sim.press_hall_button(mezzanine, CallDirection::Up).unwrap();
sim.pin_assignment(lobby_car, mezzanine, CallDirection::Up)
.unwrap();
println!(
"Pinned car {:?} to mezzanine up-call. Active hall calls now: {}",
lobby_car,
sim.hall_calls().count(),
);
for _ in 0..5000 {
sim.step();
}
let m = sim.metrics();
println!("Delivered: {}", m.total_delivered());
println!("Avg wait: {:.1} ticks", m.avg_wait_time());
println!("Avg ride: {:.1} ticks", m.avg_ride_time());
println!("Total dist: {:.1} units", m.total_distance());
match sim.strategy_id(GroupId(0)) {
Some(BuiltinStrategy::Custom(name)) => {
println!("Strategy name: {name} (will round-trip through snapshots)");
}
other => println!("Strategy name: {other:?}"),
}
}