|
1 | 1 | # [Problem 874: Walking Robot Simulation](https://leetcode.com/problems/walking-robot-simulation/description/?envType=daily-question)
|
2 | 2 |
|
3 | 3 | ## Initial thoughts (stream-of-consciousness)
|
| 4 | +- I'm thinking we'll want to keep track of the current position and heading |
| 5 | +- The position could be represented as two numbers `(x, y)` |
| 6 | +- The heading could be represented as two numbers also: |
| 7 | + - North: `(0, 1)` |
| 8 | + - East: `(1, 0)` |
| 9 | + - South: `(0, -1)` |
| 10 | + - West: `(-1, 0)` |
| 11 | +- So the full position would be: `[(x, y), (a, b)]` |
| 12 | +- We'll also need to keep track of the "order" of turning: |
| 13 | + - Clockwise/right: north --> east --> south --> west |
| 14 | + - Counterclockwise/left: north --> west --> south --> east |
| 15 | +- To move forward `i` units (assuming no obstables), we can update the new position to `[(x + i * a, y + i * b), (a, b)]` |
| 16 | +- If there are obstacles, things could get messy-- every time we move (but not when we turn), we'll need to check if we've hit *any* obstacle |
| 17 | + - I'm not sure how to get away from having to do this... |
| 18 | + - We'll need to check if the *next* move will result in a collision (and if so, we don't make that move) |
| 19 | + - This also, I think tells us what to do with the "there can be an obstacle in [0, 0]" note-- I initially thought it might cause the robot to get stuck at `(0, 0)`. But actually, maybe we can just treat that like any other obstacle. Note: we'll need to check this special case when testing, because the instructions are a little ambiguous. |
| 20 | +- Finally, the squared distance is easy to compute-- it's just `x ** 2 + y ** 2`, since we always start at the origin. Any time we move, we'll need to compare the current distance with the current max (intialized to 0). Then at th eend we just return `max_distance`. |
4 | 21 |
|
5 | 22 | ## Refining the problem, round 2 thoughts
|
| 23 | +- We should initialize `position = [(0, 0), (0, 1)]`. Or actually, we could just represent the position as `[x, y, a, b]` instead of using nested lists-- so let's intialize `position = [0, 0, 0, 1]` |
| 24 | +- Turning left (`next_command == -2`) and right (`next_command == -1`): |
| 25 | +```python |
| 26 | +def turn_left(pos): |
| 27 | + x, y, a, b = pos |
| 28 | + if a == 0: # currently either north or south |
| 29 | + if b == 1: # north |
| 30 | + a, b = -1, 0 |
| 31 | + else: # south |
| 32 | + a, b = 1, 0 |
| 33 | + else: # currently either east or west |
| 34 | + if a == 1: # east |
| 35 | + a, b = 0, 1 |
| 36 | + else: # west |
| 37 | + a, b = 0, -1 |
| 38 | + return x, y, a, b |
| 39 | + |
| 40 | +def turn_right(pos): |
| 41 | + x, y, a, b = pos |
| 42 | + if a == 0: # currently either north or south |
| 43 | + if b == 1: # north |
| 44 | + a, b = 1, 0 |
| 45 | + else: # south |
| 46 | + a, b = -1, 0 |
| 47 | + else: # currently either east or west |
| 48 | + if a == 1: # east |
| 49 | + a, b = 0, -1 |
| 50 | + else: # west |
| 51 | + a, b = 0, 1 |
| 52 | + return x, y, a, b |
| 53 | +``` |
| 54 | +- To walk forward, we have to both check for obstacles (one step ahead) and update `max_distance` each time `x` or `y` changes. |
| 55 | +- First, let's see how we'll check for obstacles: |
| 56 | +```python |
| 57 | +def collision(x, y): |
| 58 | + for xo, yo in obstacles: |
| 59 | + if x == xo and y == yo: |
| 60 | + return True |
| 61 | + return False |
| 62 | +``` |
| 63 | +- Then we can move as follows (if `next_command >= 1`): |
| 64 | +```python |
| 65 | +def move(pos, steps, max_distance): |
| 66 | + x, y, a, b = pos |
| 67 | + for _ in range(steps): |
| 68 | + x_next, y_next = x + a, y + b |
| 69 | + if collision(x_next, y_next): |
| 70 | + break |
| 71 | + x, y = x_next, y_next |
| 72 | + max_distance = max(max_distance, x ** 2 + y ** 2) |
| 73 | + return (x, y, a, b), max_distance |
| 74 | +``` |
| 75 | +- I think this is everything...let's put it together! |
6 | 76 |
|
7 | 77 | ## Attempted solution(s)
|
8 | 78 | ```python
|
9 |
| -class Solution: # paste your code here! |
10 |
| - ... |
| 79 | +class Solution: |
| 80 | + def robotSim(self, commands: List[int], obstacles: List[List[int]]) -> int: |
| 81 | + max_distance = 0 |
| 82 | + pos = [0, 0, 0, 1] |
| 83 | + |
| 84 | + def turn_left(pos): |
| 85 | + x, y, a, b = pos |
| 86 | + if a == 0: # currently either north or south |
| 87 | + if b == 1: # north |
| 88 | + a, b = -1, 0 |
| 89 | + else: # south |
| 90 | + a, b = 1, 0 |
| 91 | + else: # currently either east or west |
| 92 | + if a == 1: # east |
| 93 | + a, b = 0, 1 |
| 94 | + else: # west |
| 95 | + a, b = 0, -1 |
| 96 | + return x, y, a, b |
| 97 | + |
| 98 | + def turn_right(pos): |
| 99 | + x, y, a, b = pos |
| 100 | + if a == 0: # currently either north or south |
| 101 | + if b == 1: # north |
| 102 | + a, b = 1, 0 |
| 103 | + else: # south |
| 104 | + a, b = -1, 0 |
| 105 | + else: # currently either east or west |
| 106 | + if a == 1: # east |
| 107 | + a, b = 0, -1 |
| 108 | + else: # west |
| 109 | + a, b = 0, 1 |
| 110 | + return x, y, a, b |
| 111 | + |
| 112 | + def collision(x, y): |
| 113 | + for xo, yo in obstacles: |
| 114 | + if x == xo and y == yo: |
| 115 | + return True |
| 116 | + return False |
| 117 | + |
| 118 | + def move(pos, steps, max_distance): |
| 119 | + x, y, a, b = pos |
| 120 | + for _ in range(steps): |
| 121 | + x_next, y_next = x + a, y + b |
| 122 | + if collision(x_next, y_next): |
| 123 | + break |
| 124 | + x, y = x_next, y_next |
| 125 | + max_distance = max(max_distance, x ** 2 + y ** 2) |
| 126 | + return (x, y, a, b), max_distance |
| 127 | + |
| 128 | + for c in commands: |
| 129 | + if c == -2: |
| 130 | + pos = turn_left(pos) |
| 131 | + elif c == -1: |
| 132 | + pos = turn_right(pos) |
| 133 | + else: |
| 134 | + pos, max_distance = move(pos, c, max_distance) |
| 135 | + |
| 136 | + return max_distance |
11 | 137 | ```
|
| 138 | +- Given test cases pass |
| 139 | +- Let's try a case where there's an obstacle at 0,0: |
| 140 | + - `commands = [5, 6, 7, 2, 7, -1, -2, 5, 6, -2, 3, 5, 7, 9, 1, 1, -1, 2, 3, 4, -2, 5], obstacles = [[0, 0], [5, 2], [-3, -6]]`: pass |
| 141 | +- Ok...so I'm pretty sure the algorithm is right. I'm not totally certain that it's efficient enough (so we might time out)...but let's try submitting... |
| 142 | + |
| 143 | + |
| 144 | + |
| 145 | +Hrmph, I was afraid that would happen 😞... |
| 146 | + |
| 147 | +- One potentially simple fix would be to convert `obstacles` to a `set` instead of a `list`: `obstacles = {(x, y) for x, y in obstacles}` |
| 148 | +- Then lookups will be constant time instead of $O(n)$ time for $n$ obstacles-- so we could just use: |
| 149 | +```python |
| 150 | +class Solution: |
| 151 | + def robotSim(self, commands: List[int], obstacles: List[List[int]]) -> int: |
| 152 | + max_distance = 0 |
| 153 | + obstacles = {(x, y) for x, y in obstacles} |
| 154 | + pos = [0, 0, 0, 1] |
| 155 | + |
| 156 | + def turn_left(pos): |
| 157 | + x, y, a, b = pos |
| 158 | + if a == 0: # currently either north or south |
| 159 | + if b == 1: # north |
| 160 | + a, b = -1, 0 |
| 161 | + else: # south |
| 162 | + a, b = 1, 0 |
| 163 | + else: # currently either east or west |
| 164 | + if a == 1: # east |
| 165 | + a, b = 0, 1 |
| 166 | + else: # west |
| 167 | + a, b = 0, -1 |
| 168 | + return x, y, a, b |
| 169 | + |
| 170 | + def turn_right(pos): |
| 171 | + x, y, a, b = pos |
| 172 | + if a == 0: # currently either north or south |
| 173 | + if b == 1: # north |
| 174 | + a, b = 1, 0 |
| 175 | + else: # south |
| 176 | + a, b = -1, 0 |
| 177 | + else: # currently either east or west |
| 178 | + if a == 1: # east |
| 179 | + a, b = 0, -1 |
| 180 | + else: # west |
| 181 | + a, b = 0, 1 |
| 182 | + return x, y, a, b |
| 183 | + |
| 184 | + def collision(x, y): |
| 185 | + return (x, y) in obstacles |
| 186 | + |
| 187 | + def move(pos, steps, max_distance): |
| 188 | + x, y, a, b = pos |
| 189 | + for _ in range(steps): |
| 190 | + x_next, y_next = x + a, y + b |
| 191 | + if collision(x_next, y_next): |
| 192 | + break |
| 193 | + x, y = x_next, y_next |
| 194 | + max_distance = max(max_distance, x ** 2 + y ** 2) |
| 195 | + return (x, y, a, b), max_distance |
| 196 | + |
| 197 | + for c in commands: |
| 198 | + if c == -2: |
| 199 | + pos = turn_left(pos) |
| 200 | + elif c == -1: |
| 201 | + pos = turn_right(pos) |
| 202 | + else: |
| 203 | + pos, max_distance = move(pos, c, max_distance) |
| 204 | + |
| 205 | + return max_distance |
| 206 | +``` |
| 207 | +- Test cases still pass; let's see if that pushes us far enough over the threshold... |
| 208 | + |
| 209 | + |
| 210 | + |
| 211 | +Great, solved! |
| 212 | + |
0 commit comments