|
| 1 | +Python Elevator Challenge |
| 2 | +========================= |
| 3 | + |
| 4 | +So You Think You Can Program An Elevator? |
| 5 | +----------------------------------------- |
| 6 | + |
| 7 | +Many of us ride elevators every day. We intuitively feel like we have a good grasp on them. We understand the how elevators decide where to go. But say you wanted to express this algorithm in code. How would you go about it? |
| 8 | + |
| 9 | +The Test Harness |
| 10 | +---------------- |
| 11 | + |
| 12 | +Lets consider a simplified model of an elevator. Like all elevators, it can go up or down. We define constants for these. This elevator also happens to be in a building with six floors. |
| 13 | + |
| 14 | + >>> UP = 1 |
| 15 | + >>> DOWN = 2 |
| 16 | + >>> FLOOR_COUNT = 6 |
| 17 | + |
| 18 | +We will make an `Elevator` class that simulates an elevator. This will delegate to another class which contains the elevator business logic, i.e. deciding what the elevator should do. Your challenge is to implement the business logic. |
| 19 | + |
| 20 | +A user can interact with the elevator in two ways. She can call the elevator by pressing the up or down button on any floor, and she can select a destination floor by pressing the button for that floor on the panel in the elevator. Both of these actions get passed straight through to the logic delegate. |
| 21 | + |
| 22 | + >>> class Elevator(object): |
| 23 | + ... def call(self, floor, direction): |
| 24 | + ... self._logic_delegate.on_called(floor, direction) |
| 25 | + ... |
| 26 | + ... def select_floor(self, floor): |
| 27 | + ... self._logic_delegate.on_floor_selected(floor) |
| 28 | + |
| 29 | +The logic delegate can respond by setting the elevator to move up, move down, or stop. It can also read the current floor and movement direction of the elevator. These actions are accessed through `Callbacks`, a mediator provided by the `Elevator` class to the logic delegate. |
| 30 | + |
| 31 | + >>> class Elevator(Elevator): |
| 32 | + ... def __init__(self, logic_delegate): |
| 33 | + ... self._current_floor = 1 |
| 34 | + ... print "1...", |
| 35 | + ... self._motor_direction = None |
| 36 | + ... self._logic_delegate = logic_delegate |
| 37 | + ... self._logic_delegate.callbacks = self.Callbacks(self) |
| 38 | + ... |
| 39 | + ... class Callbacks(object): |
| 40 | + ... def __init__(self, outer): |
| 41 | + ... self._outer = outer |
| 42 | + ... |
| 43 | + ... @property |
| 44 | + ... def current_floor(self): |
| 45 | + ... return self._outer._current_floor |
| 46 | + ... |
| 47 | + ... @property |
| 48 | + ... def motor_direction(self): |
| 49 | + ... return self._outer._motor_direction |
| 50 | + ... |
| 51 | + ... @motor_direction.setter |
| 52 | + ... def motor_direction(self, direction): |
| 53 | + ... self._outer._motor_direction = direction |
| 54 | + |
| 55 | +The simulation runs in steps. Every time step consists of either a change of floor, or a pause at a floor. Either way, the business logic delegate gets notified. Along the way, we print out the movements of the elevator so that we can keep track of it. We also define a few helper methods. |
| 56 | + |
| 57 | + >>> class Elevator(Elevator): |
| 58 | + ... def step(self): |
| 59 | + ... delta = 0 |
| 60 | + ... if self._motor_direction == UP: delta = 1 |
| 61 | + ... elif self._motor_direction == DOWN: delta = -1 |
| 62 | + ... |
| 63 | + ... if delta: |
| 64 | + ... self._current_floor = self._current_floor + delta |
| 65 | + ... print "%s..." % self._current_floor, |
| 66 | + ... self._logic_delegate.on_floor_changed() |
| 67 | + ... else: |
| 68 | + ... self._logic_delegate.on_ready() |
| 69 | + ... |
| 70 | + ... assert self._current_floor >= 1 |
| 71 | + ... assert self._current_floor <= FLOOR_COUNT |
| 72 | + ... |
| 73 | + ... def run_until_stopped(self): |
| 74 | + ... self.step() |
| 75 | + ... while self._motor_direction is not None: self.step() |
| 76 | + ... |
| 77 | + ... def run_until_floor(self, floor): |
| 78 | + ... self.step() |
| 79 | + ... for i in range(100): |
| 80 | + ... if self._current_floor == floor: break |
| 81 | + ... self.step() |
| 82 | + ... else: assert False |
| 83 | + |
| 84 | +That's it for the framework. |
| 85 | + |
| 86 | +The Business Logic |
| 87 | +------------------ |
| 88 | + |
| 89 | +As for the business logic, an example implementation is provided in the `elevator.py` file in this project. |
| 90 | + |
| 91 | + >>> from elevator_solution import ElevatorLogic |
| 92 | + |
| 93 | +As provided, it doesn't pass the tests in this document. Your challenge is to fix it so that it does. To run the tests, run this in your shell: |
| 94 | + |
| 95 | + python -m doctest -v README.md |
| 96 | + |
| 97 | +With the correct business logic, here's how the elevator should behave: |
| 98 | + |
| 99 | +### Basic usage |
| 100 | + |
| 101 | +Make an elevator. It starts at the first floor. |
| 102 | + |
| 103 | + >>> elevator = Elevator(ElevatorLogic()) |
| 104 | + 1... |
| 105 | + |
| 106 | +Somebody on the fifth floor wants to go down. |
| 107 | + |
| 108 | + >>> elevator.call(5, DOWN) |
| 109 | + >>> elevator.run_until_stopped() |
| 110 | + 2... 3... 4... 5... |
| 111 | + |
| 112 | +The elevator went up to the fifth floor. Now, the passenger gets on and presses the button to select the first floor. |
| 113 | + |
| 114 | + >>> elevator.select_floor(1) |
| 115 | + >>> elevator.run_until_stopped() |
| 116 | + 4... 3... 2... 1... |
| 117 | + |
| 118 | +More Tests |
| 119 | +---------- |
| 120 | + |
| 121 | + >>> elevator = Elevator(ElevatorLogic()) |
| 122 | + 1... |
| 123 | + >>> elevator.call(3, UP) |
| 124 | + >>> elevator.call(5, UP) |
| 125 | + >>> elevator.run_until_stopped() |
| 126 | + 2... 3... |
| 127 | + >>> elevator.run_until_stopped() |
| 128 | + 4... 5... |
| 129 | + |
| 130 | + >>> elevator = Elevator(ElevatorLogic()) |
| 131 | + 1... |
| 132 | + >>> elevator.call(5, UP) |
| 133 | + >>> elevator.call(3, UP) |
| 134 | + >>> elevator.run_until_stopped() |
| 135 | + 2... 3... |
| 136 | + >>> elevator.run_until_stopped() |
| 137 | + 4... 5... |
| 138 | + |
| 139 | + |
0 commit comments