|
| 1 | +""" |
| 2 | +Given a robot cleaner in a room modeled as a grid. Each cell in the grid can be empty or blocked. |
| 3 | +The robot cleaner with 4 given APIs can move forward, turn left or turn right. Each turn it made |
| 4 | +is 90 degrees. When it tries to move into a blocked cell, its bumper sensor detects the obstacle |
| 5 | +and it stays on the current cell. Design an algorithm to clean the entire room using only the 4 |
| 6 | +given APIs shown below. |
| 7 | +
|
| 8 | +interface Robot { |
| 9 | + // returns true if next cell is open and robot moves into the cell. |
| 10 | + // returns false if next cell is obstacle and robot stays on the current cell. |
| 11 | + boolean move(); |
| 12 | +
|
| 13 | + // Robot will stay on the same cell after calling turnLeft/turnRight. |
| 14 | + // Each turn will be 90 degrees. |
| 15 | + void turnLeft(); |
| 16 | + void turnRight(); |
| 17 | +
|
| 18 | + // Clean the current cell. |
| 19 | + void clean(); |
| 20 | +} |
| 21 | +
|
| 22 | +Example: |
| 23 | +
|
| 24 | +Input: |
| 25 | +room = [ |
| 26 | + [1,1,1,1,1,0,1,1], |
| 27 | + [1,1,1,1,1,0,1,1], |
| 28 | + [1,0,1,1,1,1,1,1], |
| 29 | + [0,0,0,1,0,0,0,0], |
| 30 | + [1,1,1,1,1,1,1,1] |
| 31 | +], |
| 32 | +row = 1, |
| 33 | +col = 3 |
| 34 | +
|
| 35 | +Explanation: |
| 36 | +All grids in the room are marked by either 0 or 1. |
| 37 | +0 means the cell is blocked, while 1 means the cell is accessible. |
| 38 | +The robot initially starts at the position of row=1, col=3. |
| 39 | +From the top left corner, its position is one row below and three columns right. |
| 40 | +
|
| 41 | +Notes: |
| 42 | +- The input is only given to initialize the room and the robot's position internally. |
| 43 | + You must solve this problem "blindfolded". In other words, you must control the robot |
| 44 | + using only the mentioned 4 APIs, without knowing the room layout and the initial robot's position. |
| 45 | +- The robot's initial position will always be in an accessible cell. |
| 46 | +- The initial direction of the robot will be facing up. |
| 47 | +- All accessible cells are connected, which means the all cells marked as 1 will be |
| 48 | + accessible by the robot. |
| 49 | +- Assume all four edges of the grid are all surrounded by wall. |
| 50 | +--------------------------------------------------------------- |
| 51 | +- In: Grid, starting position |
| 52 | +- Out: set of behaviors such that all cells are visited |
| 53 | +- Cases: |
| 54 | + - Fully explorable room |
| 55 | + - Completely unexplorable room |
| 56 | + - Variants |
| 57 | +
|
| 58 | +- clean() is a red herring. We just need to visit every cell. |
| 59 | +- A BFS or DFS will ensure we visit every cell; however, for a BFS, |
| 60 | +we can wind up in an invalid state - we cannot explore a cell we are not |
| 61 | +next to currently, so if we try to visit the neighbor of a cell we were |
| 62 | +in previously, we could have trouble. DFS is probably a better option. |
| 63 | +- We do not actually know what cell we are in when we start; however |
| 64 | +we can just assume we're in 0,0 and go from there; we won't worry about |
| 65 | +array index errors since move() will return false. |
| 66 | +- We don't have access to the grid, just the robot; as such, we need to |
| 67 | +program our DFS from turning and moving |
| 68 | +- Perhaps we should implement Solution methods that match |
| 69 | +the robot's methods and in each call update the internal representation |
| 70 | +as well as call the robot API? |
| 71 | +- Our overall algorithm should be something like: |
| 72 | + visit(cell, parent): |
| 73 | + clean() |
| 74 | + for each neighbor: |
| 75 | + face(neighbor) # turns robot correctly |
| 76 | + if move(): # moves robot forward |
| 77 | + visit_current_cell(neighbor, current) |
| 78 | + face(parent) |
| 79 | + move() # final move will return false, which is OK. |
| 80 | +- The best thing to do would be if we could completely abstract away direction |
| 81 | +from our high level algorithm; maybe if we implement a moveTo() that handles |
| 82 | +correctly facing? |
| 83 | +- Our overall algorithm should be something like: |
| 84 | + visit(cell, parent): |
| 85 | + clean() |
| 86 | + for each neighbor: |
| 87 | + if moveTo(): # moves robot forward |
| 88 | + visit_current_cell(neighbor, current) |
| 89 | + moveTo(parent) # final move will return false, which is OK. |
| 90 | +
|
| 91 | + moveTo(src, dst): |
| 92 | + # turn to face correct direction; |
| 93 | + # if move() succeeds, update position and return true |
| 94 | + # if move() fails, don't update position and return false |
| 95 | +
|
| 96 | + face(src, dest): |
| 97 | + # determine which direction in the list should be faced |
| 98 | + # call robot.turnLeft() or turnRight() until facing that direction |
| 99 | +""" |
| 100 | + |
| 101 | +UP, RIGHT, DOWN, LEFT = (-1, 0), (0, 1), (1, 0), (0, 1) |
| 102 | +DIRECTIONS = [UP, RIGHT, DOWN, LEFT] |
| 103 | + |
| 104 | + |
| 105 | +class Solution(object): |
| 106 | + def __init__(self): |
| 107 | + self.visited = set() |
| 108 | + self.position = (0, 0) # assumed |
| 109 | + self.directions_i = 0 |
| 110 | + |
| 111 | + def cleanRoom(self, robot): |
| 112 | + self.robot = robot |
| 113 | + self.visit(robot, self.position) # what should the parent of the first cell be? |
| 114 | + |
| 115 | + def visit(self, cell, parent): |
| 116 | + """ |
| 117 | + clean() |
| 118 | + for each neighbor: |
| 119 | + if moveTo(): # moves robot forward |
| 120 | + visit_current_cell(neighbor, current) |
| 121 | + moveTo(parent) # final move will return false, which is OK. |
| 122 | + """ |
| 123 | + self.visited.add((self.row, self.col)) |
| 124 | + self.robot.clean() |
| 125 | + return |
| 126 | + |
| 127 | + # returns true if next cell is open and robot moves into the cell. |
| 128 | + # returns false if next cell is obstacle and robot stays on the current cell. |
| 129 | + def moveTo(self, cell): |
| 130 | + self.face(cell) |
| 131 | + if self.robot.move(): |
| 132 | + self.position[0] += DIRECTIONS[self.directions_i][0] |
| 133 | + self.position[1] += DIRECTIONS[self.directions_i][1] |
| 134 | + return True |
| 135 | + return False |
| 136 | + |
| 137 | + # Ensure the robot is facing in the correct direction to move to |
| 138 | + # this cell; turn right until so. |
| 139 | + def face(self, cell): |
| 140 | + correct_direction = (cell[0] - self.position[0], cell[1] - self.position[1]) |
| 141 | + if correct_direction not in DIRECTIONS: # diagonal or other invalid direction |
| 142 | + return |
| 143 | + while DIRECTIONS[self.directions_i] != correct_direction: |
| 144 | + self.robot.turnRight() |
| 145 | + self.directions_i = (self.directions_i + 1) % 4 |
| 146 | + |
| 147 | + |
| 148 | +if __name__ == '__main__': |
| 149 | + s = Solution() |
0 commit comments