elevate_lib/
elevator.rs

1//Import standard/imported modules
2use rand::Rng;
3
4//Import source modules
5use crate::person::Person;
6use crate::people::People;
7
8/// # Elevator struct
9///
10/// An `Elevator` is aggregated by buildings, and transports people between floors.
11/// The `Elevator` struct generally should not be directly instantiated; instead it
12/// should be managed via the `Building` type and `ElevatorController` implementations.
13#[derive(Clone)]
14pub struct Elevator {
15    pub floor_on: usize,
16    pub moving_up: bool,
17    pub stopped: bool,
18    pub people: Vec<Person>,
19    pub energy_up: f64,
20    pub energy_down: f64,
21    pub energy_coef: f64
22}
23
24/// # Elevator type implementation
25///
26/// The following functions are used by `Building` and `Controller` types as well as
27/// `Elevators` implementations to update and control the behavior of an `Elevator`.
28impl Elevator {
29    /// Initialize a new elevator given the elevator's energy spent moving up, energy
30    /// spent moving down, and energy coefficient (additional energy spent per person
31    /// transported).  The elevator is initialized stopped on the first floor with no
32    /// people.
33    ///
34    /// ### Example
35    ///
36    /// ```
37    /// let energy_up: f64 = 5.0_f64;
38    /// let energy_down: f64 = 2.5_f64;
39    /// let energy_coef: f64 = 0.5_f64;
40    /// let my_elev: Elevator = Elevator::from(energy_up, energy_down, energy_coef);
41    /// ```
42    pub fn from(energy_up: f64, energy_down: f64, energy_coef: f64) -> Elevator {
43        Elevator {
44            floor_on: 0_usize,
45            moving_up: false,
46            stopped: true,
47            people: Vec::new(),
48            energy_up: energy_up,
49            energy_down: energy_down,
50            energy_coef: energy_coef
51        }
52    }
53    
54    /// Calculate the total energy spent (as an `f64`) while the elevator is moving.
55    /// If the elevator is not moving then return `0.0_f64`.
56    pub fn get_energy_spent(&mut self) -> f64 {
57        let energy_spent = if self.stopped {
58                0.0_f64
59            } else if self.moving_up {
60                self.energy_up + (self.energy_coef * (self.people.len() as f64))
61            } else {
62                self.energy_down + (self.energy_coef * (self.people.len() as f64))
63            };
64        energy_spent
65    }
66
67    /// Update the `stopped` and `moving_up` properties of the elevator given a
68    /// destination floor for the elevator.  The properties will be set such that
69    /// the elevator moves in the direction of the provided floor with respect to
70    /// its current floor when updated.
71    pub fn update_direction(&mut self, floor_to: usize) {
72        //If the elevator is not on its destination floor, then move toward it
73        if floor_to > self.floor_on {
74            self.stopped = false;
75            self.moving_up = true;
76        } else if floor_to < self.floor_on {
77            self.stopped = false;
78            self.moving_up = false;
79        //If the elevator is on its destination floor, then stop
80        } else {
81            self.stopped = true;
82        }
83    } 
84
85    /// Use the `stopped` and `moving_up` properties of the elevator to update the
86    /// elevator's floor index.  If stopped, then no change.  If moving up then
87    /// increment the `floor_on` by `1_usize`.  If moving down then decrement the
88    /// `floor_on` by `1_usize`.
89    pub fn update_floor(&mut self) -> usize {
90        //If the elevator is stopped, then return early
91        if self.stopped {
92            return self.floor_on;
93        }
94
95        //If the elevator is moving then update the floor the elevator is on
96        self.floor_on = if self.moving_up {
97            self.floor_on + 1_usize
98        } else {
99            self.floor_on - 1_usize
100        };
101
102        //Loop through the elevator's people and update their floor accordingly
103        for pers in self.people.iter_mut() {
104            pers.floor_on = self.floor_on;
105        }
106
107        //Return the floor the elevator is on
108        self.floor_on
109    }
110    
111    /// If there are people on the elevator, this returns the nearest destination
112    /// floor among those people represented as a length-2 tuple of `usize`s.  The
113    /// first element is the destination floor, and the second is the distance to
114    /// the floor.  If there are no people on the floor, it returns `(0_usize, 0_usize)`.
115    pub fn get_nearest_dest_floor(&self) -> (usize, usize) {
116        //Get the current floor the elevator is on
117        let floor_index: usize = self.floor_on;
118
119        //Get the destination floors from the elevator, if none then return
120        let dest_floors: Vec<usize> = self.get_dest_floors();
121        if dest_floors.len() == 0_usize {
122            return (0_usize, 0_usize);
123        }
124
125        //Initialize variables to track the nearest destination floor
126        //and the min distance between here and a destination floor
127        let mut nearest_dest_floor: usize = 0_usize;
128        let mut min_dest_floor_dist: usize = 0_usize;
129
130        //Calculate the distance between each dest floor and the current floor
131        for dest_floor_index in dest_floors.iter() {
132            let dest_floor_dist: usize = if floor_index > *dest_floor_index {
133                floor_index - dest_floor_index
134            } else {
135                dest_floor_index - floor_index
136            };
137
138            //Check whether this is less than the current minimum, or if no
139            //minimum has been assigned yet (in which case it is 0_usize)
140            if min_dest_floor_dist == 0_usize || dest_floor_dist < min_dest_floor_dist {
141                min_dest_floor_dist = dest_floor_dist;
142                nearest_dest_floor = *dest_floor_index;
143            }
144        }
145
146        //Return the nearest destination floor
147        (nearest_dest_floor, min_dest_floor_dist)
148    }
149
150    /// If the elevator is stopped, this function returns a `Vec<Person>` containing
151    /// the people on the elevator whose destination floor is the current floor.  If
152    /// the elevator is not stopped, this function returns an empty vector.
153    pub fn flush_people_leaving_elevator(&mut self) -> Vec<Person> {
154        //Initialize a vector of people for the people leaving
155        let mut people_leaving: Vec<Person> = Vec::new();
156
157        //If the elevator is not stopped then return the empty vector
158        if !self.stopped {
159            return people_leaving;
160        }
161
162        //Loop through the people on the elevator and add to the vec
163        let mut removals = 0_usize;
164        for i in 0..self.people.len() {
165            //If the person is not on their destination floor, then skip
166            if self.people[i-removals].floor_on != self.people[i-removals].floor_to {
167                continue;
168            }
169
170            //If the person is on their destination floor, then remove them from
171            //the elevator and add them to the leaving vec, incrementing the removals
172            let person_leaving: Person = self.people.remove(i - removals);
173            people_leaving.push(person_leaving);
174            removals += 1_usize;
175        }
176
177        //Return the vector of people leaving
178        people_leaving
179    }
180}
181
182//Implement the extend trait for the elevator struct
183impl Extend<Person> for Elevator {
184    fn extend<T: IntoIterator<Item=Person>>(&mut self, iter: T) {
185        for pers in iter {
186            self.people.push(pers);
187        }
188    }
189}
190
191//Implement the people trait for the elevator struct
192impl People for Elevator {
193    /// Generates the number of people among the collection of people who will tip.
194    fn gen_num_tips(&self, rng: &mut impl Rng) -> usize {
195        self.people.gen_num_tips(rng)
196    }
197
198    /// Determines the destination floors for all people and returns it as a vector.
199    fn get_dest_floors(&self) -> Vec<usize> {
200        self.people.get_dest_floors()
201    }
202
203    /// Determines the total number of people and returns it as a usize.
204    fn get_num_people(&self) -> usize {
205        self.people.get_num_people()
206    }
207
208    /// Determines the number of people waiting, that is, not at their desired floor.
209    fn get_num_people_waiting(&self) -> usize {
210        self.people.get_num_people_waiting()
211    }
212
213    /// Reads the wait times from people waiting/not at their desired floor and aggregates
214    /// the total into a usize.
215    fn get_aggregate_wait_time(&self) -> usize {
216        self.people.get_aggregate_wait_time()
217    }
218
219    /// Determines whether anyone in the collection of people are going to a given floor,
220    /// and returns a bool which is true if so, and false if not.
221    fn are_people_waiting(&self) -> bool {
222        self.people.are_people_waiting()
223    }
224
225    /// Determines whether anyone in the collection of people is waiting/not at their
226    /// desired floor, and returns a bool which is true if so, and false if not.
227    fn are_people_going_to_floor(&self, floor_index: usize) -> bool {
228        self.people.are_people_going_to_floor(floor_index)
229    }
230
231    /// Increments the wait times (by `1_usize`) among all people waiting/not at
232    /// their desired floor.
233    fn increment_wait_times(&mut self) {
234        self.people.increment_wait_times()
235    }
236
237    /// Resets the wait times (to `0_usize`) among all people who have a nonzero
238    /// wait time and are on their desired floor.
239    fn reset_wait_times(&mut self) {
240        self.people.reset_wait_times()
241    }
242}