Skip to main content

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}