elevator_core/dispatch/
nearest_car.rs1use smallvec::SmallVec;
4
5use crate::entity::EntityId;
6use crate::world::World;
7
8use super::{DispatchDecision, DispatchManifest, DispatchStrategy, ElevatorGroup};
9
10pub struct NearestCarDispatch;
16
17impl NearestCarDispatch {
18 #[must_use]
20 pub const fn new() -> Self {
21 Self
22 }
23}
24
25impl Default for NearestCarDispatch {
26 fn default() -> Self {
27 Self::new()
28 }
29}
30
31impl DispatchStrategy for NearestCarDispatch {
32 fn decide(
33 &mut self,
34 _elevator: EntityId,
35 _elevator_position: f64,
36 _group: &ElevatorGroup,
37 _manifest: &DispatchManifest,
38 _world: &World,
39 ) -> DispatchDecision {
40 DispatchDecision::Idle
42 }
43
44 fn decide_all(
45 &mut self,
46 elevators: &[(EntityId, f64)],
47 group: &ElevatorGroup,
48 manifest: &DispatchManifest,
49 world: &World,
50 ) -> Vec<(EntityId, DispatchDecision)> {
51 let mut pending_stops: SmallVec<[(EntityId, f64); 16]> = SmallVec::new();
53 for &stop_eid in group.stop_entities() {
54 if manifest.has_demand(stop_eid)
55 && let Some(pos) = world.stop_position(stop_eid)
56 {
57 pending_stops.push((stop_eid, pos));
58 }
59 }
60
61 if pending_stops.is_empty() {
62 return elevators
63 .iter()
64 .map(|(eid, _)| (*eid, DispatchDecision::Idle))
65 .collect();
66 }
67
68 let mut results: Vec<(EntityId, DispatchDecision)> = Vec::new();
69 let mut assigned_stops: SmallVec<[EntityId; 16]> = SmallVec::new();
70 let mut assigned_elevators: SmallVec<[EntityId; 16]> = SmallVec::new();
71
72 pending_stops.sort_by(|a, b| {
75 let demand_a = manifest.waiting_count_at(a.0);
76 let demand_b = manifest.waiting_count_at(b.0);
77 demand_b.cmp(&demand_a)
78 });
79
80 for (stop_eid, stop_pos) in &pending_stops {
81 if assigned_stops.contains(stop_eid) {
82 continue;
83 }
84
85 let nearest = elevators
87 .iter()
88 .filter(|(eid, _)| !assigned_elevators.contains(eid))
89 .min_by(|a, b| {
90 let dist_a = (a.1 - stop_pos).abs();
91 let dist_b = (b.1 - stop_pos).abs();
92 dist_a.total_cmp(&dist_b)
93 });
94
95 if let Some((elev_eid, _)) = nearest {
96 results.push((*elev_eid, DispatchDecision::GoToStop(*stop_eid)));
97 assigned_elevators.push(*elev_eid);
98 assigned_stops.push(*stop_eid);
99 }
100 }
101
102 for (eid, _) in elevators {
104 if !assigned_elevators.contains(eid) {
105 results.push((*eid, DispatchDecision::Idle));
106 }
107 }
108
109 results
110 }
111}