|
| 1 | +# [Problem 2069: Walking Robot Simulation II](https://leetcode.com/problems/walking-robot-simulation-ii/description/?envType=daily-question) |
| 2 | + |
| 3 | +## Initial thoughts (stream-of-consciousness) |
| 4 | +The robot moves along the boundary of a width x height grid. Every time it would step out of bounds it turns 90° counterclockwise and keeps trying until it completes that step. Observing the motion, the robot always stays on the perimeter (border) cells and traverses them in a fixed cyclical order: along the bottom edge from left to right, then up the right edge, then along the top edge right-to-left, then down the left edge, and repeat. |
| 5 | + |
| 6 | +So we can precompute the list of perimeter coordinates in that traversal order and treat the robot as moving along indices of that list. A step(num) is simply advancing the current index by num (mod perimeter length). For getDir(), direction depends on which segment of the perimeter the current index lies on; there's a special-case: when the robot is at the starting position (index 0) and it has completed at least one step in the past (i.e., total steps taken > 0), it should be facing "South" (this matches the turn pattern after a full loop). |
| 7 | + |
| 8 | +This yields O(width+height) setup and O(1) per operation. |
| 9 | + |
| 10 | +## Refining the problem, round 2 thoughts |
| 11 | +Edge cases: |
| 12 | +- width, height >= 2 so perimeter length is at least 4. |
| 13 | +- step(num) can be large, so use modulo arithmetic to update index. |
| 14 | +- Need to know whether the robot has ever moved a positive number of steps to decide the special direction at index 0. Keep a total_steps counter (unbounded integer in Python is fine). |
| 15 | +- Compute the boundary list carefully to avoid duplicating corner cells. |
| 16 | + |
| 17 | +Time complexity: |
| 18 | +- Initialization: O(width + height) |
| 19 | +- step / getPos / getDir: O(1) |
| 20 | + |
| 21 | +Space complexity: |
| 22 | +- O(perimeter) = O(width + height) |
| 23 | + |
| 24 | +## Attempted solution(s) |
| 25 | +```python |
| 26 | +class Robot: |
| 27 | + def __init__(self, width: int, height: int): |
| 28 | + # Build the perimeter path in the order the robot will traverse: |
| 29 | + # bottom row left->right, right column bottom->top, |
| 30 | + # top row right->left, left column top->bottom (excluding corners duplicates) |
| 31 | + self.w = width |
| 32 | + self.h = height |
| 33 | + path = [] |
| 34 | + # bottom edge (y = 0), x = 0..w-1 |
| 35 | + for x in range(0, width): |
| 36 | + path.append((x, 0)) |
| 37 | + # right edge (x = w-1), y = 1..h-1 |
| 38 | + for y in range(1, height): |
| 39 | + path.append((width - 1, y)) |
| 40 | + # top edge (y = h-1), x = w-2..0 |
| 41 | + for x in range(width - 2, -1, -1): |
| 42 | + path.append((x, height - 1)) |
| 43 | + # left edge (x = 0), y = h-2..1 |
| 44 | + for y in range(height - 2, 0, -1): |
| 45 | + path.append((0, y)) |
| 46 | + |
| 47 | + self.path = path |
| 48 | + self.L = len(path) # perimeter length, equals 2*(w + h) - 4 |
| 49 | + self.idx = 0 # current index in path |
| 50 | + self.total_steps = 0 # total steps ever taken (to detect full-loop special case) |
| 51 | + |
| 52 | + def step(self, num: int) -> None: |
| 53 | + if self.L == 0: |
| 54 | + return |
| 55 | + self.total_steps += num |
| 56 | + self.idx = (self.idx + num) % self.L |
| 57 | + |
| 58 | + def getPos(self) -> [int, int]: |
| 59 | + x, y = self.path[self.idx] |
| 60 | + return [x, y] |
| 61 | + |
| 62 | + def getDir(self) -> str: |
| 63 | + # Special case: if at starting cell after having moved >0 steps, robot faces South |
| 64 | + if self.idx == 0 and self.total_steps > 0: |
| 65 | + return "South" |
| 66 | + |
| 67 | + # Determine which edge segment the current index is on |
| 68 | + # indices: |
| 69 | + # 0 .. (w-1) - 1 -> bottom edge -> East |
| 70 | + # (w-1) .. (w-1 + h-1) - 1 -> right edge -> North |
| 71 | + # ... next (w-1) -> top edge -> West |
| 72 | + # remaining -> left edge -> South |
| 73 | + seg1 = self.w - 1 |
| 74 | + seg2 = seg1 + (self.h - 1) |
| 75 | + seg3 = seg2 + (self.w - 1) |
| 76 | + # seg4 is implicit remainder |
| 77 | + |
| 78 | + if self.idx < seg1: |
| 79 | + return "East" |
| 80 | + elif self.idx < seg2: |
| 81 | + return "North" |
| 82 | + elif self.idx < seg3: |
| 83 | + return "West" |
| 84 | + else: |
| 85 | + return "South" |
| 86 | +``` |
| 87 | +- Notes: |
| 88 | + - We precompute the perimeter path in traversal order; moving is an index shift modulo path length. |
| 89 | + - We maintain total_steps to distinguish the initial facing ("East" at start index 0) from the facing after a full loop (index 0 but should be "South"). |
| 90 | + - Time: O(width + height) init, O(1) per method call. |
| 91 | + - Space: O(width + height) for the path list. |
0 commit comments