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}