LeetCode 874: Walking Robot Simulation Explained

Problem summary

A robot starts at the origin (0, 0) and faces north.

You are given:

  • commands
  • obstacles

The robot processes each command one by one:

  • -2 โ†’ turn left 90ยฐ
  • -1 โ†’ turn right 90ยฐ
  • positive integer x โ†’ move forward x steps

If the next cell contains an obstacle, the robot stops moving for that command and continues with the next command.

We need to return:

the maximum squared Euclidean distance from the origin that the robot reaches at any time.

That means if robot is at (x, y), distance is:x2+y2x^2 + y^2x2+y2


Key observation

The main challenge is obstacle checking.

If we store obstacles in a normal list and do:

if [nx, ny] in obstacles:

then Python checks the list one by one, which is slow.

Since the robot may move many steps, repeated list lookup causes TLE.

So the best optimization is:

  • convert obstacles into a set of tuples
  • then obstacle lookup becomes fast

Example:

obs = {(2, 4), (1, 2), (3, 5)}

Now checking:

(nx, ny) in obs

is much faster.


Direction handling

Instead of storing direction as angles like 0, 90, 180, 270, it is cleaner to store direction as an index.

We use:

dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]

These represent:

  • 0 โ†’ north
  • 1 โ†’ east
  • 2 โ†’ south
  • 3 โ†’ west

So:

  • turn right โ†’ d = (d + 1) % 4
  • turn left โ†’ d = (d - 1) % 4

Approach

1. Convert obstacles to a set

We do:

obs = set((x, y) for x, y in obstacles)

This allows quick obstacle checks.


2. Track robot position and direction

Start with:

  • x = 0
  • y = 0
  • d = 0 meaning north

3. Process each command

If command is -1

Turn right.

If command is -2

Turn left.

If command is positive

Move step by step.

We move one step at a time because an obstacle may appear before the full command finishes.

For each step:

  • compute next position (nx, ny)
  • if obstacle exists, stop
  • otherwise move to (nx, ny)

After each valid movement, update maximum squared distance.


Python solution

from typing import List

class Solution:
def robotSim(self, commands: List[int], obstacles: List[List[int]]) -> int:
obs = set((x, y) for x, y in obstacles)

# north, east, south, west
dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]
d = 0 # start facing north

x, y = 0, 0
ans = 0

for cmd in commands:
if cmd == -1:
d = (d + 1) % 4
elif cmd == -2:
d = (d - 1) % 4
else:
dx, dy = dirs[d]
for _ in range(cmd):
nx, ny = x + dx, y + dy
if (nx, ny) in obs:
break
x, y = nx, ny
ans = max(ans, x * x + y * y)

return ans

Dry run

Let:

commands = [4, -1, 3]
obstacles = []

Robot starts at (0, 0) facing north.

Command = 4

Move north 4 steps:

  • (0,1) โ†’ distance = 1
  • (0,2) โ†’ distance = 4
  • (0,3) โ†’ distance = 9
  • (0,4) โ†’ distance = 16

Maximum so far = 16

Command = -1

Turn right.

Now facing east.

Command = 3

Move east 3 steps:

  • (1,4) โ†’ distance = 17
  • (2,4) โ†’ distance = 20
  • (3,4) โ†’ distance = 25

Final answer = 25


Example with obstacle

commands = [4, -1, 4, -2, 4]
obstacles = [[2, 4]]

Obstacle is at (2,4).

Step-by-step

Start at (0,0), facing north.

Command = 4

Move to:

  • (0,1)
  • (0,2)
  • (0,3)
  • (0,4)

Command = -1

Turn right โ†’ face east

Command = 4

Try to move east:

  • (1,4) โ†’ allowed
  • (2,4) โ†’ obstacle found, stop immediately

So robot remains at (1,4).

Command = -2

Turn left โ†’ face north

Command = 4

Move north:

  • (1,5)
  • (1,6)
  • (1,7)
  • (1,8)

Maximum squared distance becomes:12+82=651^2 + 8^2 = 6512+82=65

Answer = 65


Why step-by-step movement is necessary

Suppose command is:

5

and robot is moving north.

You cannot directly jump 5 cells ahead, because an obstacle may appear after 1 or 2 steps.

That is why we simulate one step at a time.


Why set is faster than list

If obstacles are stored in a list:

[[2,4], [1,2], [3,5], ...]

then checking whether (x, y) is an obstacle requires scanning many elements.

That is slow.

If obstacles are stored in a set:

{(2,4), (1,2), (3,5), ...}

then Python uses hashing, so lookup is much faster on average.

This is the main reason the optimized solution passes.


Time complexity

Let:

  • n = len(commands)
  • total movement steps across all positive commands be S

Then:

  • each command is processed once
  • each step is simulated once
  • each obstacle lookup is O(1) on average

So time complexity is:O(S)O(S)O(S)

If we include obstacle set creation:O(S+m)O(S + m)O(S+m)

where m is number of obstacles.


Space complexity

We store all obstacles in a set.

So space complexity is:O(m)O(m)O(m)


Common mistakes

1. Using list lookup for obstacles

Wrong:

if [nx, ny] in obstacles:

This causes TLE.


2. Using if / if / else incorrectly

Wrong:

if cmd == -1:
...
if cmd == -2:
...
else:
...

The else belongs only to the second if.

Correct:

if cmd == -1:
...
elif cmd == -2:
...
else:
...

3. Trying to move entire command at once

Obstacle checking must happen after every single step.


C++ solution

class Solution {
public:
int robotSim(vector<int>& commands, vector<vector<int>>& obstacles) {
set<pair<int, int>> obs;
for (auto& o : obstacles) {
obs.insert({o[0], o[1]});
} vector<pair<int, int>> dirs = {{0,1}, {1,0}, {0,-1}, {-1,0}};
int d = 0;
int x = 0, y = 0;
int ans = 0; for (int cmd : commands) {
if (cmd == -1) {
d = (d + 1) % 4;
} else if (cmd == -2) {
d = (d + 3) % 4;
} else {
int dx = dirs[d].first;
int dy = dirs[d].second; for (int step = 0; step < cmd; step++) {
int nx = x + dx;
int ny = y + dy; if (obs.count({nx, ny})) break; x = nx;
y = ny;
ans = max(ans, x * x + y * y);
}
}
} return ans;
}
};

Final takeaway

This problem is mostly about simulation plus efficient obstacle lookup.

The robot logic is simple:

  • turn left/right when command is negative
  • move step by step when command is positive
  • stop if obstacle is ahead
  • track the maximum squared distance

The most important optimization is:

convert obstacles into a set

Without that, list membership checking becomes too slow and may cause TLE.

Leave a Comment