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}