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