Have you ever found yourself in a situation where you stumbled upon an opportunity completely by chance? That's exactly what happened to me last week when I unknowingly entered a room filled with software engineers, ready to take on a coding challenge in a workshop organised by Everest Academy in Melbourne. The purpose of this workshop was to practice Hardcore Test-Driven Development (TDD) by incorporating peer programming. I learned the importance of writing comprehensive test cases, the art of refactoring to improve code quality, and the immense value of working alongside a programming partner. Yet, with every passing moment, my anticipation grew, and I couldn't wait to dive into the actual coding task itself.
Challenge: Develop an API that translates the commands sent from Earth to instructions that are understood by a Mars Rover.
Well, I was quite excited to complete this coding task and am eager to share my code in this blog post.
Note: At each step, the highlighted text represents the new added code that's relevant to the step.
The coding task had the following requirements:
You are given the initial starting point (x,y) of a rover and the direction (N.S, E,W) it is facing.
The rover receives a character array of commands.
Implement commands that move the rover forward/backward (f, b).
Implement commands that turn the rover left/right (I,r).
Implement wrapping from one edge of the grid to another.
Implement obstacle detection before each move to a new square. If a given sequence of commands encounters an obstacle, the rover moves up to the last possible point, aborts the sequence and reports the obstacle.
STEP 1: You are given the initial starting point (x,y) of a rover and the direction (N.S, E,W) it is facing.
Input:
from typing import List
class Rover:
def __init__(self):
self.x = 0
self.y = 0
self.direction = 'N'
self.directions = ['N', 'W', 'S', 'E']
print("We are currently at coordinate ", (self.x, self.y), " and facing direction", self.direction, ".\n")
mars_rover = Rover()
Output:
We are currently at coordinate (0, 0) and facing direction N .
STEP 2: Implement commands that turn the rover left/right (I,r).
Input:
from typing import List
class Rover:
def __init__(self):
self.x = 0
self.y = 0
self.direction = 'N'
self.directions = ['N', 'W', 'S', 'E']
print("We are currently at coordinate ", (self.x, self.y), " and facing direction", self.direction, ".\n"
"Instructions:\n"
" 1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.\n "
"Example: instance.rotate(['l', 'r', 'l', 'l'])\n")
def rotate(self, inputs: List):
output = ""
# Validate the inputs
while not all(char == 'l' or char == 'r' for char in inputs):
error = "Error: Invalid input. Only 'l' and 'r' are allowed."
return error
direction_mapping = {'N': 0, 'W': 1, 'S': 2, 'E': 3}
anticlockwise_rotations = inputs.count('l')
clockwise_rotations = inputs.count('r')
net_rotations = direction_mapping[self.direction] + anticlockwise_rotations - clockwise_rotations
if net_rotations < 0:
net_rotations = net_rotations + 4
self.direction = self.directions[net_rotations % 4]
output += f"We are now at position {(self.x, self.y)} and facing direction {self.direction}."
return output
mars_rover = Rover()
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
Output:
We are currently at coordinate (0, 0) and facing direction N .
Instructions:
1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.
Example: instance.rotate(['l', 'r', 'l', 'l'])
We are now at position (0, 0) and facing direction W.
We are now at position (0, 0) and facing direction S.
We are now at position (0, 0) and facing direction E.
STEP 3: Implement commands that move the rover forward/backward (f, b).
Input:
from typing import List
class Rover:
def __init__(self):
self.x = 0
self.y = 0
self.direction = 'N'
self.directions = ['N', 'W', 'S', 'E']
print("We are currently at coordinate ", (self.x, self.y), " and facing direction", self.direction, ".\n"
"Instructions:\n"
" 1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.\n "
"Example: instance.rotate(['l', 'r', 'l', 'l'])\n"
" 2. Please enter a list of 'f's and 'b's separated by comma's to move forward or backward
respectively.\n"
" Example: instance.steps(['f', 'f', 'b', 'b'])\n")
def rotate(self, inputs: List):
output = ""
# Validate the inputs
while not all(char == 'l' or char == 'r' for char in inputs):
error = "Error: Invalid input. Only 'l' and 'r' are allowed."
return error
direction_mapping = {'N': 0, 'W': 1, 'S': 2, 'E': 3}
anticlockwise_rotations = inputs.count('l')
clockwise_rotations = inputs.count('r')
net_rotations = direction_mapping[self.direction] + anticlockwise_rotations - clockwise_rotations
if net_rotations < 0:
net_rotations = net_rotations + 4
self.direction = self.directions[net_rotations % 4]
output += f"We are now at position {(self.x, self.y)} and facing direction {self.direction}."
return output
def steps(self, inputs: List):
output = ""
while not all(char == 'f' or char == 'b' for char in inputs):
error = "Error: Invalid input. Only 'f' and 'b' are allowed."
return error
forward_steps = inputs.count('f')
backward_steps = inputs.count('b')
steps = forward_steps - backward_steps
direction_steps = {'N': (self.x, self.y + steps),
'W': (self.x - steps, self.y),
'S': (self.x, self.y - steps),
'E': (self.x + steps, self.y)}
self.x = direction_steps[self.direction][0]
self.y = direction_steps[self.direction][1]
output += f"We are now at position {(self.x, self.y)} and facing direction {self.direction}."
return output
mars_rover = Rover()
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.steps(['f', 'f', 'f', 'f', 'f', 'f', 'f']))
print(mars_rover.rotate(['l']))
Output:
We are currently at coordinate (0, 0) and facing direction N .
Instructions:
1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.
Example: instance.rotate(['l', 'r', 'l', 'l'])
2. Please enter a list of 'f's and 'b's separated by comma's to move forward or backward respectively.
Example: instance.steps(['f', 'f', 'b', 'b'])
We are now at position (0, 0) and facing direction W.
We are now at position (0, 0) and facing direction S.
We are now at position (0, 0) and facing direction E.
We are now at position (7, 0) and facing direction E.
We are now at position (7, 0) and facing direction N.
STEP 4: Implement wrapping from one edge of the grid to another. (Choose 5 x 5 grid)
Input:
from typing import List
class Rover:
def __init__(self):
self.x = 0
self.y = 0
self.direction = 'N'
self.directions = ['N', 'W', 'S', 'E']
print("We are currently at coordinate ", (self.x, self.y), " and facing direction", self.direction, ".\n"
"Instructions:\n"
" 1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.\n "
"Example: instance.rotate(['l', 'r', 'l', 'l'])\n"
" 2. Please enter a list of 'f's and 'b's separated by comma's to move forward or backward
respectively.\n"
" Example: instance.steps(['f', 'f', 'b', 'b'])\n"
" 3. We are wrapped around in a 5 x 5 grid centered around origin (0,0).\n")
def rotate(self, inputs: List):
output = ""
# Validate the inputs
while not all(char == 'l' or char == 'r' for char in inputs):
error = "Error: Invalid input. Only 'l' and 'r' are allowed."
return error
direction_mapping = {'N': 0, 'W': 1, 'S': 2, 'E': 3}
anticlockwise_rotations = inputs.count('l')
clockwise_rotations = inputs.count('r')
net_rotations = direction_mapping[self.direction] + anticlockwise_rotations - clockwise_rotations
if net_rotations < 0:
net_rotations = net_rotations + 4
self.direction = self.directions[net_rotations % 4]
output += f"We are now at position {(self.x % 5, self.y % 5)} and facing direction {self.direction}."
return output
def steps(self, inputs: List):
output = ""
while not all(char == 'f' or char == 'b' for char in inputs):
error = "Error: Invalid input. Only 'f' and 'b' are allowed."
return error
forward_steps = inputs.count('f')
backward_steps = inputs.count('b')
steps = forward_steps - backward_steps
direction_steps = {'N': (self.x, self.y + steps),
'W': (self.x - steps, self.y),
'S': (self.x, self.y - steps),
'E': (self.x + steps, self.y)}
self.x = direction_steps[self.direction][0]
self.y = direction_steps[self.direction][1]
output += f"We are now at position {(self.x % 5, self.y % 5)} and facing direction {self.direction}."
return output
mars_rover = Rover()
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.steps(['f', 'f', 'f', 'f', 'f', 'f', 'f']))
print(mars_rover.rotate(['l']))
Output:
We are currently at coordinate (0, 0) and facing direction N .
Instructions:
1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.
Example: instance.rotate(['l', 'r', 'l', 'l'])
2. Please enter a list of 'f's and 'b's separated by comma's to move forward or backward respectively.
3. We are wrapped around in a 5 x 5 grid centered around origin (0,0).
We are now at position (0, 0) and facing direction W.
We are now at position (0, 0) and facing direction S.
We are now at position (0, 0) and facing direction E.
We are now at position (2, 0) and facing direction E.
We are now at position (2, 0) and facing direction N.
STEP 5: Implement obstacle detection before each move to a new square. If a given sequence of commands encounters an obstacle, the rover moves up to the last possible point, aborts the sequence and reports the obstacle. (Choose coordinates (2, 3), (4, 1) as obstacles)
Input:
from typing import List
class Rover:
def __init__(self):
self.x = 0
self.y = 0
self.direction = 'N'
self.directions = ['N', 'W', 'S', 'E']
self.obstacles = [(2, 3), (4, 1)]
print("We are currently at coordinate ", (self.x, self.y), " and facing direction", self.direction, ".\n"
"Instructions:\n"
" 1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.\n "
"Example: instance.rotate(['l', 'r', 'l', 'l'])\n"
" 2. Please enter a list of 'f's and 'b's separated by comma's to move forward or backward
respectively.\n"
" Example: instance.steps(['f', 'f', 'b', 'b'])\n"
" 3. We are wrapped around in a 5 x 5 grid centered around origin (0,0).")
def rotate(self, inputs: List):
output = ""
# Validate the inputs
while not all(char == 'l' or char == 'r' for char in inputs):
error = "Error: Invalid input. Only 'l' and 'r' are allowed."
return error
direction_mapping = {'N': 0, 'W': 1, 'S': 2, 'E': 3}
anticlockwise_rotations = inputs.count('l')
clockwise_rotations = inputs.count('r')
net_rotations = direction_mapping[self.direction] + anticlockwise_rotations - clockwise_rotations
if net_rotations < 0:
net_rotations = net_rotations + 4
self.direction = self.directions[net_rotations % 4]
output += f"We are now at position {(self.x % 5, self.y % 5)} and facing direction {self.direction}."
return output
def steps(self, inputs: List):
output = ""
while not all(char == 'f' or char == 'b' for char in inputs):
error = "Error: Invalid input. Only 'f' and 'b' are allowed."
return error
forward_steps = inputs.count('f')
backward_steps = inputs.count('b')
steps = forward_steps - backward_steps
direction_steps = {'N': (self.x, self.y + steps),
'W': (self.x - steps, self.y),
'S': (self.x, self.y - steps),
'E': (self.x + steps, self.y)}
self.x = direction_steps[self.direction][0]
self.y = direction_steps[self.direction][1]
output += f"We are now at position {(self.x % 5, self.y % 5)} and facing direction {self.direction}."
return output
def steps_with_obstacle_detection(self, inputs: List):
output = " "
while not all(char == 'f' or char == 'b' for char in inputs):
error = "Error: Invalid input. Only 'f' and 'b' are allowed."
return error
forward_steps = inputs.count('f')
backward_steps = inputs.count('b')
steps = forward_steps - backward_steps
for _ in range(0, abs(steps)):
direction_steps = {'N': (self.x, self.y + 1),
'W': (self.x - 1, self.y),
'S': (self.x, self.y - 1),
'E': (self.x + 1, self.y)}
x = direction_steps[self.direction][0] % 5
y = direction_steps[self.direction][1] % 5
obstacle_detected = False
for i in self.obstacles:
if i == (x, y):
obstacle_detected = True
break
if obstacle_detected:
print(f"Warning: Obstacle detected at {(x, y)}. Move aborted.")
break
else:
self.x = x
self.y = y
output += f"We are now at position {(self.x, self.y)} and facing direction {self.direction}."
return output
mars_rover = Rover()
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.rotate(['l', 'l', 'r']))
print(mars_rover.steps(['f', 'f', 'f', 'f', 'f', 'f', 'f']))
print(mars_rover.rotate(['l']))
print(mars_rover.steps_with_obstacle_detection(['f', 'f', 'f', 'f']))
Output:
We are currently at coordinate (0, 0) and facing direction N .
Instructions:
1. Please enter a list of 'l's and 'r's separated by comma's to rotate left or right respectively.
Example: instance.rotate(['l', 'r', 'l', 'l'])
2. Please enter a list of 'f's and 'b's separated by comma's to move forward or backward respectively.
Example: instance.steps(['f', 'f', 'b', 'b'])
3. We are wrapped around in a 5 x 5 grid centered around origin (0,0).
We are now at position (0, 0) and facing direction W.
We are now at position (0, 0) and facing direction S.
We are now at position (0, 0) and facing direction E.
We are now at position (2, 0) and facing direction E.
We are now at position (2, 0) and facing direction N.
Warning: Obstacle detected at (2, 3). Move aborted.
We are now at position (2, 2) and facing direction N.
That's done! So, we now have an API that follows my instructions on Mars (at least hypothetically). If you want more explanations and have any further suggestions, leave them in the comments. I am excited to continue embracing these challenges in my future coding endeavours and am confident in the transformative power these workshops hold.
Author:
Sadiah Z.
Comments