Skip to content

Commit beb46a4

Browse files
My solution to 874
1 parent a697b91 commit beb46a4

File tree

1 file changed

+203
-2
lines changed

1 file changed

+203
-2
lines changed

problems/874/jeremymanning.md

+203-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,212 @@
11
# [Problem 874: Walking Robot Simulation](https://leetcode.com/problems/walking-robot-simulation/description/?envType=daily-question)
22

33
## 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`.
421

522
## 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!
676

777
## Attempted solution(s)
878
```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
11137
```
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+
![Screenshot 2024-09-03 at 11 29 06 PM](https://github.com/user-attachments/assets/0165408c-0186-479e-96b3-39204daeb8d3)
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+
![Screenshot 2024-09-03 at 11 34 34 PM](https://github.com/user-attachments/assets/55255ae3-6087-4a35-b9e7-186cb2ebbb74)
210+
211+
Great, solved!
212+

0 commit comments

Comments
 (0)