|
|
@@ -1,5 +1,6 @@
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
+import copy
|
|
|
from enum import Enum
|
|
|
|
|
|
class Elevator:
|
|
|
@@ -7,9 +8,11 @@ class Elevator:
|
|
|
self.id = e_id
|
|
|
self.floor = floor
|
|
|
self.goal = goal
|
|
|
+ self.pickup_direction = None # the direction we're handling pickup requests for
|
|
|
self.dropoffs = []
|
|
|
|
|
|
def direction(self):
|
|
|
+ ' the direction the elevator is currently moving in '
|
|
|
if self.goal is None:
|
|
|
return None
|
|
|
if self.goal > self.floor:
|
|
|
@@ -22,12 +25,14 @@ class Elevator:
|
|
|
assert self.goal is not None
|
|
|
return
|
|
|
assert floor != self.floor
|
|
|
+ assert self.pickup_direction is None
|
|
|
|
|
|
if self.goal is None:
|
|
|
self.goal = floor
|
|
|
self.dropoffs.append(floor)
|
|
|
else:
|
|
|
direction = self.direction()
|
|
|
+ # TODO: handle opposite direction dropoffs
|
|
|
if direction == Direction.UP:
|
|
|
if floor > self.goal:
|
|
|
self.goal = floor
|
|
|
@@ -39,6 +44,42 @@ class Elevator:
|
|
|
self.dropoffs.append(floor)
|
|
|
self.dropoffs.sort(reverse=True)
|
|
|
|
|
|
+ def estimate_pickup_time(self, floor, direction):
|
|
|
+ # while dropping off, can handle requests in same elevator dir in between current and goal
|
|
|
+ # while picking up, can handle requests in same dir as request dir
|
|
|
+ # when requested floor is in dir of elevator
|
|
|
+ our_dir = self.direction()
|
|
|
+ if our_dir is None:
|
|
|
+ return abs(floor - self.floor)
|
|
|
+
|
|
|
+ if self.dropoffs: # we are moving and have dropoffs
|
|
|
+ if our_dir == Direction.UP:
|
|
|
+ if self.floor <= floor and direction == our_dir:
|
|
|
+ return floor - self.floor
|
|
|
+ else:
|
|
|
+ to_goal = self.goal - self.floor
|
|
|
+ to_request = abs(self.goal - floor)
|
|
|
+ return to_goal + to_request
|
|
|
+ else:
|
|
|
+ if self.floor >= floor and direction == our_dir:
|
|
|
+ return self.floor - floor
|
|
|
+ else:
|
|
|
+ to_goal = self.floor - self.goal
|
|
|
+ to_request = abs(self.goal - floor)
|
|
|
+ return to_goal + to_request
|
|
|
+ else: # we are moving to pick up a request
|
|
|
+ assert self.pickup_direction is not None
|
|
|
+ if our_dir == Direction.UP:
|
|
|
+ if self.floor <= floor <= self.goal and direction == self.pickup_direction:
|
|
|
+ return floor - self.floor
|
|
|
+ else:
|
|
|
+ return float('inf')
|
|
|
+ else:
|
|
|
+ if self.floor >= floor >= self.goal and direction == self.pickup_direction:
|
|
|
+ return self.floor - floor
|
|
|
+ else:
|
|
|
+ return float('inf')
|
|
|
+
|
|
|
def __repr__(self):
|
|
|
attrs = ', '.join('%s=%s' % t for t in self.__dict__.items())
|
|
|
return 'Elevator(%s)' % attrs
|
|
|
@@ -60,11 +101,25 @@ class ElevatorControlSystem:
|
|
|
|
|
|
def step(self):
|
|
|
# serve pickup requests
|
|
|
- free_elevators = list(filter(lambda e: e.goal is None, self.elevators.values()))
|
|
|
- while free_elevators and self.pickup_requests:
|
|
|
- floor, direction = self.pickup_requests.pop(0)
|
|
|
- elevator = min(free_elevators, key=lambda e: abs(e.floor - floor))
|
|
|
- elevator.goal = floor
|
|
|
+ for floor, direction in copy.copy(self.pickup_requests):
|
|
|
+ min_time = float('inf')
|
|
|
+ min_elevator = None
|
|
|
+ for elevator in self.elevators.values():
|
|
|
+ time = elevator.estimate_pickup_time(floor, direction)
|
|
|
+ if time < min_time:
|
|
|
+ min_time = time
|
|
|
+ min_elevator = elevator
|
|
|
+ if min_elevator:
|
|
|
+ # only handle the request now if the min_elevator is right here or doing nothing. if
|
|
|
+ # it's moving, min_time should improve by at least 1 per step. handle this request
|
|
|
+ # later (in case some other elevator finishes a dropoff and can pickup sooner)
|
|
|
+ if min_time == 0:
|
|
|
+ assert min_elevator.floor == floor
|
|
|
+ self.pickup_requests.remove((floor, direction))
|
|
|
+ elif min_elevator.goal is None:
|
|
|
+ min_elevator.goal = floor
|
|
|
+ min_elevator.pickup_direction = direction
|
|
|
+ self.pickup_requests.remove((floor, direction))
|
|
|
|
|
|
# move elevators to requested floors
|
|
|
for elevator in self.elevators.values():
|
|
|
@@ -77,7 +132,7 @@ class ElevatorControlSystem:
|
|
|
if elevator.dropoffs and elevator.dropoffs[0] == elevator.floor:
|
|
|
elevator.dropoffs = elevator.dropoffs[1:]
|
|
|
if elevator.goal == elevator.floor:
|
|
|
- elevator.goal = None
|
|
|
+ elevator.goal = elevator.pickup_direction = None
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
ecs = ElevatorControlSystem(2)
|
|
|
@@ -94,12 +149,27 @@ if __name__ == '__main__':
|
|
|
ecs.step()
|
|
|
print(' '*4, ecs.elevators)
|
|
|
|
|
|
- print('requesting dropoff on floor 3')
|
|
|
+ print('requesting dropoff on floor 5 and 3')
|
|
|
+ ecs.dropoff(1, 5)
|
|
|
ecs.dropoff(1, 3)
|
|
|
ecs.step()
|
|
|
print(' '*4, ecs.elevators)
|
|
|
|
|
|
- print('requesting pickup on floor 2 up')
|
|
|
- ecs.pickup(2, Direction.UP)
|
|
|
+ print('requesting pickup on floor 3 up')
|
|
|
+ ecs.pickup(3, Direction.UP)
|
|
|
+ ecs.step()
|
|
|
+ print(' '*4, ecs.elevators)
|
|
|
+
|
|
|
+ print('requesting pickup on floor 5 down')
|
|
|
+ ecs.pickup(5, Direction.DOWN)
|
|
|
+ ecs.step()
|
|
|
+ print(' '*8, ecs.pickup_requests)
|
|
|
+ print(' '*4, ecs.elevators)
|
|
|
+
|
|
|
+ ecs.step()
|
|
|
+ print(' '*8, ecs.pickup_requests)
|
|
|
+ print(' '*4, ecs.elevators)
|
|
|
+
|
|
|
ecs.step()
|
|
|
+ print(' '*8, ecs.pickup_requests)
|
|
|
print(' '*4, ecs.elevators)
|