Walking Robot Simulation II
If you are looking for a clean LeetCode 2069 Walking Robot Simulation II solution, this is the easiest way to think about it:
- the robot never needs a full step-by-step simulation across the whole board
- it only moves along the outer perimeter
- that perimeter movement is a fixed cycle
So instead of simulating every query from scratch, we preprocess the full perimeter once, store the robot's position and direction for every state in that cycle, and answer step, getPos, and getDir in O(1).
This approach is perfect for the design requirement and works cleanly in Java, C++, JavaScript, TypeScript, C, C#, Go, and Rust.
Problem Summary
We need to design a Robot class for a grid of size width x height.
The robot:
- starts at
(0, 0) - starts facing
"East" - moves one unit at a time
- turns
90degrees counterclockwise whenever the next move would leave the grid
We have to support three operations:
step(num)updates the robot afternummovesgetPos()returns the current positiongetDir()returns the current direction
Key Insight
The robot always travels around the boundary of the rectangle.
It never enters the inner cells.
That means the robot's entire journey is just one repeating loop of perimeter cells:
- move right on the bottom row
- move up on the right column
- move left on the top row
- move down on the left column
So the real state space is not width * height.
It is only the perimeter length:
2 * (width + height) - 4
Approach: Preprocess The Full Cycle
We build two arrays:
positions[i]= the robot position at cycle indexidirections[i]= the robot direction at cycle indexi
We store the perimeter in traversal order:
- origin
(0, 0)with direction"South" - bottom row from left to right with direction
"East" - right column from bottom to top with direction
"North" - top row from right to left with direction
"West" - left column from top to bottom with direction
"South"
Then we keep:
index= current location inside the cyclemoved= whether the robot has ever actually moved
For step(num):
- mark
moved = true - advance
indexbynum % cycleLength
For getPos():
- return
positions[index]
For getDir():
- if the robot never moved, return
"East" - otherwise return
directions[index]
Important Edge Case: The Origin Direction
This is the only tricky part in the whole problem.
At the very beginning:
- position =
(0, 0) - direction =
"East"
But after the robot completes a full loop and comes back to (0, 0), its direction is:
"South"
So the same position has two possible directions depending on whether the robot has moved yet.
That is why we:
- store the origin in the cycle with direction
"South" - use
movedto return"East"only for the untouched initial state
Algorithm
- Compute the perimeter cycle length
2 * (width + height) - 4 - Preprocess every perimeter state into
positionsanddirections - Store the current cycle index
- On
step(num), moveindex = (index + num % cycleLength) % cycleLength - On
getPos(), return the stored position - On
getDir(), return"East"before any movement, otherwise the stored direction
Code Solution
Switch between languages
class Robot {
private final int[][] positions;
private final String[] directions;
private final int cycleLength;
private int index;
private boolean moved;
public Robot(int width, int height) {
cycleLength = 2 * (width + height) - 4;
positions = new int[cycleLength][2];
directions = new String[cycleLength];
int current = 0;
positions[current][0] = 0;
positions[current][1] = 0;
directions[current++] = "South";
for (int x = 1; x < width; x++) {
positions[current][0] = x;
positions[current][1] = 0;
directions[current++] = "East";
}
for (int y = 1; y < height; y++) {
positions[current][0] = width - 1;
positions[current][1] = y;
directions[current++] = "North";
}
for (int x = width - 2; x >= 0; x--) {
positions[current][0] = x;
positions[current][1] = height - 1;
directions[current++] = "West";
}
for (int y = height - 2; y >= 1; y--) {
positions[current][0] = 0;
positions[current][1] = y;
directions[current++] = "South";
}
}
public void step(int num) {
if (num == 0) {
return;
}
moved = true;
index = (index + num % cycleLength) % cycleLength;
}
public int[] getPos() {
return new int[] {positions[index][0], positions[index][1]};
}
public String getDir() {
return moved ? directions[index] : "East";
}
}Dry Run
Let width = 6 and height = 3.
The perimeter cycle is:
(0,0) S
(1,0) E
(2,0) E
(3,0) E
(4,0) E
(5,0) E
(5,1) N
(5,2) N
(4,2) W
(3,2) W
(2,2) W
(1,2) W
(0,2) W
(0,1) S
Initially:
index = 0getPos() = [0, 0]getDir() = "East"becausemoved = false
Now call step(2):
- new
index = 2 - position becomes
(2, 0) - direction becomes
"East"
Now call step(12):
- cycle length is
14 index = (2 + 12) % 14 = 0- position becomes
(0, 0) - direction becomes
"South"because the robot has completed a loop
That matches the problem's special origin behavior.
Why This Works
The robot's future behavior depends only on its current perimeter state.
Because the path repeats forever:
- every move sequence is just movement on a cycle
- every state has one fixed position
- every state also has one fixed direction
So advancing by num steps is exactly the same as moving forward by num % cycleLength positions in that cycle.
The only exception is the untouched starting state, and that is handled by the moved flag.
Complexity Analysis
Time Complexity
- preprocessing:
O(width + height) step(num):O(1)getPos():O(1)getDir():O(1)
Space Complexity
O(width + height)
We only store the perimeter states, not the whole grid.
Common Mistakes
1. Simulating every single step inside step(num)
That works, but it is unnecessary.
The whole point of this problem is to notice that the robot repeats the same perimeter cycle forever.
2. Forgetting the origin edge case
If you store (0, 0) with direction "East" for the cycle, then a full loop will return the wrong answer.
The cycle-state direction at origin must be "South".
3. Using the entire grid instead of only the perimeter
The robot never visits inner cells, so storing width * height states is wasteful and misses the main observation.
4. Forgetting modulo reduction
step(num) can be large, but only num % cycleLength matters.
Interview Takeaway
This is a classic example of turning a simulation problem into a cycle preprocessing problem.
When you see:
- repeated movement
- deterministic turning
- and a path that eventually loops
it is often better to preprocess the loop once and answer future operations in constant time.
For Walking Robot Simulation II, that single insight makes the design simple, fast, and interview-friendly.
SEO Quick Summary
For LeetCode Walking Robot Simulation II, the optimal design idea is:
- preprocess the robot's perimeter cycle
- store position and direction for each cycle state
- move with modulo arithmetic
- handle the starting origin direction separately
That gives O(width + height) preprocessing and O(1) per query, which is exactly what you want for this robot simulation design problem.