Browse Source

send the best elevator for the job

raylu 9 years ago
parent
commit
e6bb1edfc2
1 changed files with 79 additions and 9 deletions
  1. 79 9
      elevator.py

+ 79 - 9
elevator.py

@@ -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)