diff --git a/problems/2069-walking-robot-simulation-ii/analysis.md b/problems/2069-walking-robot-simulation-ii/analysis.md new file mode 100644 index 0000000..48e0836 --- /dev/null +++ b/problems/2069-walking-robot-simulation-ii/analysis.md @@ -0,0 +1,55 @@ +# 2069. Walking Robot Simulation II + +[LeetCode Link](https://leetcode.com/problems/walking-robot-simulation-ii/) + +Difficulty: Medium +Topics: Design, Simulation +Acceptance Rate: 30.4% + +## Hints + +### Hint 1 + +The robot only ever walks along the perimeter of the grid. Think about what shape the robot's path traces and how you can represent its position more compactly than (x, y, direction). + +### Hint 2 + +The perimeter of a W x H grid has exactly `2*(W-1) + 2*(H-1)` cells. You can map the robot's position to a single integer index along this perimeter and use modular arithmetic to avoid simulating each individual step. + +### Hint 3 + +The tricky edge case is at position (0, 0). Initially the robot faces East, but if it returns to (0, 0) after walking, it arrived via the left edge traveling South. You need to track whether the robot has ever moved to distinguish these two cases. + +## Approach + +Model the robot's position as a single index along the perimeter of the grid. The perimeter has `P = 2*(W-1) + 2*(H-1)` positions, and the robot walks in a cycle: + +1. **Bottom edge (East):** index 0 to W-1, cells (i, 0) +2. **Right edge (North):** index W to W+H-2, cells (W-1, i-W+1) +3. **Top edge (West):** index W+H-1 to 2W+H-3, cells (2W+H-3-i+W+H-1... simplified: W-2-offset, H-1) +4. **Left edge (South):** index 2W+H-2 to P-1, cells (0, H-2-offset) + +When `step(num)` is called, simply do `pos = (pos + num) % P`. This is O(1) per call instead of O(num). + +For `getPos()`, convert the perimeter index back to (x, y) coordinates by determining which edge the index falls on. + +For `getDir()`, the direction is determined by which edge the index belongs to: +- Bottom edge -> "East" +- Right edge -> "North" +- Top edge -> "West" +- Left edge -> "South" + +**Special case:** Position 0 is (0, 0). Initially the robot faces "East", but after any movement that lands back on index 0, the robot arrived from the South direction. Track a `moved` boolean to handle this. + +## Complexity Analysis + +Time Complexity: O(1) per `step`, `getPos`, and `getDir` call. +Space Complexity: O(1) - only stores width, height, position index, and a boolean flag. + +## Edge Cases + +- **Full perimeter walk:** After exactly P steps, the robot returns to (0, 0) but faces South, not East. +- **Large step counts:** `num` can be up to 10^5 with up to 10^4 calls, so total steps can reach 10^9. Modular arithmetic is essential; naive simulation would TLE. +- **Minimum grid (2x2):** Perimeter is only 4 cells. Each edge has exactly 1 step. Ensure the formulas work at these boundaries. +- **Multiple full loops:** `num` much larger than P should reduce correctly via modulo. +- **Corner positions:** Corners belong to the edge the robot was traveling along when it arrived (e.g., (W-1, 0) is East, not North). diff --git a/problems/2069-walking-robot-simulation-ii/problem.md b/problems/2069-walking-robot-simulation-ii/problem.md new file mode 100644 index 0000000..ecda594 --- /dev/null +++ b/problems/2069-walking-robot-simulation-ii/problem.md @@ -0,0 +1,76 @@ +--- +number: "2069" +frontend_id: "2069" +title: "Walking Robot Simulation II" +slug: "walking-robot-simulation-ii" +difficulty: "Medium" +topics: + - "Design" + - "Simulation" +acceptance_rate: 3038.2 +is_premium: false +created_at: "2026-04-07T03:36:34.001142+00:00" +fetched_at: "2026-04-07T03:36:34.001142+00:00" +link: "https://leetcode.com/problems/walking-robot-simulation-ii/" +date: "2026-04-07" +--- + +# 2069. Walking Robot Simulation II + +A `width x height` grid is on an XY-plane with the **bottom-left** cell at `(0, 0)` and the **top-right** cell at `(width - 1, height - 1)`. The grid is aligned with the four cardinal directions (`"North"`, `"East"`, `"South"`, and `"West"`). A robot is **initially** at cell `(0, 0)` facing direction `"East"`. + +The robot can be instructed to move for a specific number of **steps**. For each step, it does the following. + + 1. Attempts to move **forward one** cell in the direction it is facing. + 2. If the cell the robot is **moving to** is **out of bounds** , the robot instead **turns** 90 degrees **counterclockwise** and retries the step. + + + +After the robot finishes moving the number of steps required, it stops and awaits the next instruction. + +Implement the `Robot` class: + + * `Robot(int width, int height)` Initializes the `width x height` grid with the robot at `(0, 0)` facing `"East"`. + * `void step(int num)` Instructs the robot to move forward `num` steps. + * `int[] getPos()` Returns the current cell the robot is at, as an array of length 2, `[x, y]`. + * `String getDir()` Returns the current direction of the robot, `"North"`, `"East"`, `"South"`, or `"West"`. + + + + + +**Example 1:** + +![example-1](https://assets.leetcode.com/uploads/2021/10/09/example-1.png) + + + **Input** + ["Robot", "step", "step", "getPos", "getDir", "step", "step", "step", "getPos", "getDir"] + [[6, 3], [2], [2], [], [], [2], [1], [4], [], []] + **Output** + [null, null, null, [4, 0], "East", null, null, null, [1, 2], "West"] + + **Explanation** + Robot robot = new Robot(6, 3); // Initialize the grid and the robot at (0, 0) facing East. + robot.step(2); // It moves two steps East to (2, 0), and faces East. + robot.step(2); // It moves two steps East to (4, 0), and faces East. + robot.getPos(); // return [4, 0] + robot.getDir(); // return "East" + robot.step(2); // It moves one step East to (5, 0), and faces East. + // Moving the next step East would be out of bounds, so it turns and faces North. + // Then, it moves one step North to (5, 1), and faces North. + robot.step(1); // It moves one step North to (5, 2), and faces **North** (not West). + robot.step(4); // Moving the next step North would be out of bounds, so it turns and faces West. + // Then, it moves four steps West to (1, 2), and faces West. + robot.getPos(); // return [1, 2] + robot.getDir(); // return "West" + + + + + +**Constraints:** + + * `2 <= width, height <= 100` + * `1 <= num <= 105` + * At most `104` calls **in total** will be made to `step`, `getPos`, and `getDir`. diff --git a/problems/2069-walking-robot-simulation-ii/solution_daily_20260407.go b/problems/2069-walking-robot-simulation-ii/solution_daily_20260407.go new file mode 100644 index 0000000..dba2f62 --- /dev/null +++ b/problems/2069-walking-robot-simulation-ii/solution_daily_20260407.go @@ -0,0 +1,66 @@ +package main + +// Walking Robot Simulation II +// Model the robot's position as an index along the grid perimeter. +// Use modular arithmetic in Step() for O(1) per call. +// Convert perimeter index to (x, y) and direction on demand. + +type Robot struct { + w, h int + pos int + moved bool +} + +func Constructor(width int, height int) Robot { + return Robot{w: width, h: height} +} + +func (r *Robot) Step(num int) { + p := 2*(r.w-1) + 2*(r.h-1) + r.pos = (r.pos + num) % p + r.moved = true +} + +func (r *Robot) GetPos() []int { + x, y := r.getXY() + return []int{x, y} +} + +func (r *Robot) GetDir() string { + pos := r.pos + w, h := r.w, r.h + if pos == 0 { + if r.moved { + return "South" + } + return "East" + } + if pos < w { + return "East" + } + if pos < w+h-1 { + return "North" + } + if pos < 2*w+h-2 { + return "West" + } + return "South" +} + +func (r *Robot) getXY() (int, int) { + pos := r.pos + w, h := r.w, r.h + if pos < w { + return pos, 0 + } + pos -= w + if pos < h-1 { + return w - 1, pos + 1 + } + pos -= h - 1 + if pos < w-1 { + return w - 2 - pos, h - 1 + } + pos -= w - 1 + return 0, h - 2 - pos +} diff --git a/problems/2069-walking-robot-simulation-ii/solution_daily_20260407_test.go b/problems/2069-walking-robot-simulation-ii/solution_daily_20260407_test.go new file mode 100644 index 0000000..33af8c7 --- /dev/null +++ b/problems/2069-walking-robot-simulation-ii/solution_daily_20260407_test.go @@ -0,0 +1,135 @@ +package main + +import "testing" + +func TestRobot(t *testing.T) { + tests := []struct { + name string + width int + height int + operations []string + stepArgs []int + wantPos [][]int // nil entries for non-getPos operations + wantDir []*string // nil entries for non-getDir operations + }{ + { + name: "example 1: 6x3 grid with mixed operations", + width: 6, + height: 3, + operations: []string{"step", "step", "getPos", "getDir", "step", "step", "step", "getPos", "getDir"}, + stepArgs: []int{2, 2, 0, 0, 2, 1, 4, 0, 0}, + wantPos: [][]int{nil, nil, {4, 0}, nil, nil, nil, nil, {1, 2}, nil}, + wantDir: []*string{nil, nil, nil, strPtr("East"), nil, nil, nil, nil, strPtr("West")}, + }, + { + name: "edge case: no steps, initial position", + width: 3, + height: 3, + operations: []string{"getPos", "getDir"}, + stepArgs: []int{0, 0}, + wantPos: [][]int{{0, 0}, nil}, + wantDir: []*string{nil, strPtr("East")}, + }, + { + name: "edge case: full perimeter returns to origin facing South", + width: 4, + height: 3, + operations: []string{"step", "getPos", "getDir"}, + stepArgs: []int{10, 0, 0}, // P = 2*3 + 2*2 = 10 + wantPos: [][]int{nil, {0, 0}, nil}, + wantDir: []*string{nil, nil, strPtr("South")}, + }, + { + name: "edge case: minimum 2x2 grid", + width: 2, + height: 2, + operations: []string{"step", "getPos", "getDir", "step", "getPos", "getDir", "step", "getPos", "getDir", "step", "getPos", "getDir"}, + stepArgs: []int{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0}, + wantPos: [][]int{nil, {1, 0}, nil, nil, {1, 1}, nil, nil, {0, 1}, nil, nil, {0, 0}, nil}, + wantDir: []*string{nil, nil, strPtr("East"), nil, nil, strPtr("North"), nil, nil, strPtr("West"), nil, nil, strPtr("South")}, + }, + { + name: "edge case: large steps with modulo", + width: 6, + height: 3, + operations: []string{"step", "getPos", "getDir"}, + stepArgs: []int{100000, 0, 0}, + // P = 2*5 + 2*2 = 14, 100000 % 14 = 12 -> (0, 2), West (top-left corner) + wantPos: [][]int{nil, {0, 2}, nil}, + wantDir: []*string{nil, nil, strPtr("West")}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + robot := Constructor(tt.width, tt.height) + for i, op := range tt.operations { + switch op { + case "step": + robot.Step(tt.stepArgs[i]) + case "getPos": + got := robot.GetPos() + want := tt.wantPos[i] + if want != nil && (got[0] != want[0] || got[1] != want[1]) { + t.Errorf("operation %d getPos() = %v, want %v", i, got, want) + } + case "getDir": + got := robot.GetDir() + want := tt.wantDir[i] + if want != nil && got != *want { + t.Errorf("operation %d getDir() = %q, want %q", i, got, *want) + } + } + } + }) + } +} + +func TestRobotCornerDirections(t *testing.T) { + // Verify direction at each corner of a 3x3 grid (P=8) + robot := Constructor(3, 3) + + // Walk to each corner and check direction + corners := []struct { + steps int + wantPos []int + wantDir string + }{ + {2, []int{2, 0}, "East"}, // bottom-right corner + {2, []int{2, 2}, "North"}, // top-right corner + {2, []int{0, 2}, "West"}, // top-left corner + {2, []int{0, 0}, "South"}, // back to origin + } + + for i, c := range corners { + robot.Step(c.steps) + pos := robot.GetPos() + dir := robot.GetDir() + if pos[0] != c.wantPos[0] || pos[1] != c.wantPos[1] { + t.Errorf("corner %d: getPos() = %v, want %v", i, pos, c.wantPos) + } + if dir != c.wantDir { + t.Errorf("corner %d: getDir() = %q, want %q", i, dir, c.wantDir) + } + } +} + +func TestRobotMultipleLoops(t *testing.T) { + // 5x4 grid, P = 2*4 + 2*3 = 14 + robot := Constructor(5, 4) + // 3 full loops + 5 extra steps + robot.Step(3*14 + 5) + pos := robot.GetPos() + dir := robot.GetDir() + // pos 5 is on right edge: (4, 1), North + if pos[0] != 4 || pos[1] != 1 { + t.Errorf("getPos() = %v, want [4 1]", pos) + } + if dir != "North" { + t.Errorf("getDir() = %q, want %q", dir, "North") + } +} + +func strPtr(s string) *string { + return &s +}