elevate_lib/controller.rs
1//Import source modules
2use crate::building::Building;
3use crate::floors::Floors;
4use crate::people::People;
5
6//Implement standard/imported modules
7use rand::{Rng, SeedableRng};
8use rand::rngs::StdRng;
9use rand::distributions::{Distribution, Uniform, Bernoulli};
10
11/// # `ElevatorController` trait
12///
13/// An `ElevatorController` implementation controls the elevators of a building.
14pub trait ElevatorController {
15 fn get_building(&mut self) -> &Building;
16
17 fn get_building_mut(&mut self) -> &mut Building;
18
19 fn clone_building(&mut self) -> Building;
20
21 fn can_be_upgraded(&self) -> bool;
22
23 fn upgrade(&mut self, incrementation: f64);
24
25 fn update_elevators(&mut self);
26}
27
28/// # `RandomController` struct
29///
30/// A `RandomController` implements the `ElevatorController` trait. It randomly
31/// generates destination floors for each of a building's elevators once the elevator
32/// reaches its destination floor.
33 pub struct RandomController {
34 pub building: Building,
35 num_floors: usize,
36 floors_to: Vec<Option<usize>>,
37 dst_to: Uniform<usize>,
38 p_rational: f64,
39 dst_rational: Bernoulli,
40 upgradable: bool,
41 rng: StdRng
42}
43
44//Implement the RandomController interface
45impl RandomController {
46 /// Initialize a new RandomController given a `Building`, an `StdRng` (from
47 /// the rand library), and an `f64` representing the probability that the
48 /// RandomController behaves rationally.
49 ///
50 /// ## Example
51 ///
52 /// ```
53 /// let my_rng = rand::thread_rng();
54 /// let my_building: Building = Building::from(
55 /// 4_usize,
56 /// 2_usize,
57 /// 0.5_f64,
58 /// 5.0_f64,
59 /// 2.5_f64,
60 /// 0.5_f64
61 /// );
62 /// let my_controller: RandomController = RandomController::from(
63 /// my_building,
64 /// my_rng,
65 /// 0.5_f64
66 /// );
67 /// ```
68 pub fn from(building: Building, rng: StdRng, p_rational: f64) -> RandomController {
69 //Get the number of floors and elevators in the building
70 let num_floors: usize = building.floors.len();
71 let num_elevators: usize = building.elevators.len();
72
73 //Initialize the destination floors for the elevators
74 let floors_to: Vec<Option<usize>> = {
75 let mut tmp_floors_to: Vec<Option<usize>> = Vec::new();
76 for _ in 0..num_elevators {
77 tmp_floors_to.push(None);
78 }
79 tmp_floors_to
80 };
81
82 //Initialize the distribution for randomizing dest floors
83 let dst_to: Uniform<usize> = Uniform::new(0_usize, num_floors);
84
85 //Initialize the controller
86 RandomController {
87 building: building,
88 num_floors: num_floors,
89 floors_to: floors_to,
90 dst_to: dst_to,
91 p_rational: p_rational,
92 dst_rational: Bernoulli::new(p_rational).unwrap(),
93 upgradable: true,
94 rng: rng
95 }
96 }
97
98 /// Initialize a new RandomController from just a building. The rng is
99 /// created on the fly, and the rational probability is defaulted to 0.
100 ///
101 /// ## Example
102 ///
103 /// ```
104 /// let my_building: Building = Building::from(
105 /// 4_usize,
106 /// 2_usize,
107 /// 0.5_f64,
108 /// 5.0_f64,
109 /// 2.5_f64,
110 /// 0.5_f64
111 /// );
112 /// let my_controller: RandomController = RandomController::from(my_building);
113 /// ```
114 pub fn from_building(building: Building) -> RandomController {
115 //Initialize default values for the additional properties for this controller
116 let rng = StdRng::from_seed(rand::thread_rng().gen());
117 let p_rational = 0.0_f64;
118
119 //Initialize and return the RandomController
120 RandomController::from(building, rng, p_rational)
121 }
122
123 /// Set the destination floors of the elevators randomly according to
124 /// random or rational logic, depending on the p_rational
125 pub fn update_floors_to(&mut self) {
126 //If the number of elevators in the building is greater than the number
127 //of destination floors in the controller, then add new destination
128 //floors
129 while self.building.elevators.len() > self.floors_to.len() {
130 self.floors_to.push(None);
131 }
132
133 //If the numer of floors in the building is greater than the number of
134 //floors tracked by the controller, then update the number of floors
135 //tracked by the controller and re-instantiate the dest distribution
136 if self.building.floors.len() != self.num_floors {
137 self.num_floors = self.building.floors.len();
138 self.dst_to = Uniform::new(0, self.num_floors);
139 }
140
141 //Loop through the elevators in the building
142 for (i, elevator) in self.building.elevators.iter().enumerate() {
143 //If the destination floor for the elevator is None, then update it
144 match self.floors_to[i] {
145 Some(_) => {},
146 None => {
147 if self.dst_rational.sample(&mut self.rng) {
148 if elevator.stopped {
149 //Find the nearest destination floor among people on the elevator
150 let (nearest_dest_floor, min_dest_floor_dist): (usize, usize) = elevator.get_nearest_dest_floor();
151
152 //If the nearest dest floor is identified, then set as the dest floor
153 if min_dest_floor_dist != 0_usize {
154 self.floors_to[i] = Some(nearest_dest_floor);
155 continue;
156 }
157
158 //Find the nearest waiting floor among people throughout the building
159 let (nearest_wait_floor, min_wait_floor_dist): (usize, usize) = self.building.get_nearest_wait_floor(elevator.floor_on);
160
161 //If the nearest wait floor is identified, then set as the dest floor
162 if min_wait_floor_dist != 0_usize {
163 self.floors_to[i] = Some(nearest_wait_floor);
164 continue;
165 }
166 }
167 } else {
168 self.floors_to[i] = Some(self.dst_to.sample(&mut self.rng));
169 continue;
170 }
171 self.floors_to[i] = Some(elevator.floor_on);
172 }
173 }
174 }
175 }
176
177 /// If any elevators are at their destination floor, then set that floor
178 /// to None so that it can be re-randomized next time step.
179 pub fn clear_floors_to(&mut self) {
180 //Loop through the elevators in the building
181 for (i, elevator) in self.building.elevators.iter().enumerate() {
182 let dest_floor = self.floors_to[i].unwrap();
183 if dest_floor == elevator.floor_on {
184 self.floors_to[i] = None;
185 }
186 }
187 }
188}
189
190//Implement the ElevatorController trait for the RandomController
191impl ElevatorController for RandomController {
192 /// Immutably borrow the building belonging to the controller
193 fn get_building(&mut self) -> &Building {
194 &self.building
195 }
196
197 /// Mutably borrow the building belonging to the controller
198 fn get_building_mut(&mut self) -> &mut Building {
199 &mut self.building
200 }
201
202 /// Clone the building belonging to the controller. Generally used when
203 /// swapping controllers.
204 fn clone_building(&mut self) -> Building {
205 self.building.clone()
206 }
207
208 /// Return a boolean signifying whether the controller can be upgraded or
209 /// not.
210 fn can_be_upgraded(&self) -> bool {
211 //If the controller is 100% rational, then no further upgrades are
212 //possible
213 if self.p_rational >= 1.0_f64 {
214 return false;
215 }
216
217 //Otherwise, the elevator controller can be upgraded
218 self.upgradable
219 }
220
221 /// Upgrade the controller given an incrementation float
222 fn upgrade(&mut self, incrementation: f64) {
223 //Add the current rationality probability to the incrementation and
224 //check to see if it exceeds 1, if so then ceiling it at 1.0
225 let mut new_p_rational: f64 = self.p_rational + incrementation;
226 if new_p_rational > 1.0_f64 {
227 new_p_rational = 1.0_f64;
228 }
229
230 //Update the rationality probability and distribution of the controller
231 self.p_rational = new_p_rational;
232 self.dst_rational = Bernoulli::new(self.p_rational).unwrap();
233 }
234
235 /// If the destination floor is None, then randomize a new destination floor.
236 /// If the elevator is not on its destination floor then move toward it. If the
237 /// elevator is on its destination floor then stop it and set its destination
238 /// floor to None for randomization during the next step.
239 fn update_elevators(&mut self) {
240 //Update the destination floors
241 self.update_floors_to();
242
243 //Loop through the dest floors and update the building's elevators accordingly
244 for (i, floor_to) in self.floors_to.iter().enumerate() {
245 //Unwrap the destination floor
246 let dest_floor: usize = floor_to.unwrap();
247
248 //Update the elevator's direction based on its destination floor
249 self.building.elevators[i].update_direction(dest_floor);
250
251 //Update the elevator
252 let _new_floor_index = self.building.elevators[i].update_floor();
253 }
254
255 //Clear the destination floors if any elevators arrived at their destinations
256 self.clear_floors_to();
257 }
258}
259
260/// # `NearestController` struct
261///
262/// A `NearestController` implements the `ElevatorController` trait. It decides each
263/// elevator's direction based on the nearest destination floor among people on the
264/// elevator, then the nearest floor with people waiting.
265pub struct NearestController {
266 pub building: Building,
267 upgradable: bool
268}
269
270//Implement the NearestController interface
271impl NearestController {
272 /// Initialize a new NearestController given a `Building`.
273 ///
274 /// ## Example
275 ///
276 /// ```
277 /// let my_building: Building = Building::from(
278 /// 4_usize,
279 /// 2_usize,
280 /// 0.5_f64,
281 /// 5.0_f64,
282 /// 2.5_f64,
283 /// 0.5_f64
284 /// );
285 /// let my_controller: NearestController = NearestController::from(my_building);
286 /// ```
287 pub fn from(building: Building) -> NearestController {
288 //Initialize the controller
289 NearestController {
290 building: building,
291 upgradable: false
292 }
293 }
294
295 /// Initialize a new NearestController from just a building
296 ///
297 /// ## Example
298 ///
299 /// ```
300 /// let my_building: Building = Building::from(
301 /// 4_usize,
302 /// 2_usize,
303 /// 0.5_f64,
304 /// 5.0_f64,
305 /// 2.5_f64,
306 /// 0.5_f64
307 /// );
308 /// let my_controller: NearestController = NearestController::from(my_building);
309 /// ```
310 pub fn from_building(building: Building) -> NearestController {
311 //Initialize the controller
312 NearestController {
313 building: building,
314 upgradable: false
315 }
316 }
317}
318
319//Implement the ElevatorController trait for the NearestController
320impl ElevatorController for NearestController {
321 /// Get the building belonging to the controller
322 fn get_building(&mut self) -> &Building {
323 &self.building
324 }
325
326 /// Mutably borrow the building belonging to the controller
327 fn get_building_mut(&mut self) -> &mut Building {
328 &mut self.building
329 }
330
331 /// Clone the building belonging to the controller. Generally used when
332 /// swapping controllers.
333 fn clone_building(&mut self) -> Building {
334 self.building.clone()
335 }
336
337 /// Return a boolean signifying whether the controller can be upgraded or
338 /// not. Always returns false, since the NearestController cannot be
339 /// upgraded.
340 fn can_be_upgraded(&self) -> bool {
341 self.upgradable
342 }
343
344 /// Upgrade the controller given an incrementation float. Does nothing for
345 /// the NearestController since it cannot be upgraded.
346 fn upgrade(&mut self, _incrementation: f64) {}
347
348 /// Decide each elevator's direction based on the nearest destination floor among
349 /// people on the elevator, then the nearest floor with people waiting.
350 fn update_elevators(&mut self) {
351 //Initialize a vector of decisions for the elevators
352 let mut elevator_decisions: Vec<usize> = Vec::new();
353
354 //Loop through the elevators in the building
355 for elevator in self.building.elevators.iter() {
356 //If stopped, check where to go next
357 if elevator.stopped {
358 //Find the nearest destination floor among people on the elevator
359 let (nearest_dest_floor, min_dest_floor_dist): (usize, usize) = elevator.get_nearest_dest_floor();
360
361 //If the nearest dest floor is identified, then update the elevator
362 if min_dest_floor_dist != 0_usize {
363 elevator_decisions.push(nearest_dest_floor);
364 continue;
365 }
366
367 //Find the nearest waiting floor among people throughout the building
368 let (nearest_wait_floor, min_wait_floor_dist): (usize, usize) = self.building.get_nearest_wait_floor(elevator.floor_on);
369
370 //If the nearest wait floor is identified, then update the elevator
371 if min_wait_floor_dist != 0_usize {
372 elevator_decisions.push(nearest_wait_floor);
373 continue;
374 }
375 } else {
376 //If moving down and on the bottom floor, then stop
377 if !elevator.moving_up && elevator.floor_on == 0_usize {
378 elevator_decisions.push(elevator.floor_on);
379 continue;
380 }
381
382 //If moving up and on the top floor, then stop
383 if elevator.moving_up && elevator.floor_on == (self.building.floors.len() - 1_usize) {
384 elevator_decisions.push(elevator.floor_on);
385 continue;
386 }
387
388 //If there are people waiting on the elevator for the current floor, then stop
389 if elevator.are_people_going_to_floor(elevator.floor_on) {
390 elevator_decisions.push(elevator.floor_on);
391 continue;
392 }
393
394 //If there are people waiting on the current floor, then stop
395 if self.building.are_people_waiting_on_floor(elevator.floor_on) {
396 elevator_decisions.push(elevator.floor_on);
397 continue;
398 }
399 }
400
401 //If we make it this far without returning, then return the current state
402 elevator_decisions.push(elevator.floor_on);
403 }
404
405 //Loop through the elevator decisions and update the elevators
406 for (i, decision) in elevator_decisions.iter().enumerate() {
407 //Update the elevator direction
408 self.building.elevators[i].update_direction(*decision);
409
410 //Update the elevator
411 let _new_floor_index = self.building.elevators[i].update_floor();
412 }
413 }
414}