Skip to content

Instantly share code, notes, and snippets.

@ludolara
Created November 3, 2023 23:54
Show Gist options
  • Save ludolara/66ad645eed4c2c34e479459154f6faf7 to your computer and use it in GitHub Desktop.
Save ludolara/66ad645eed4c2c34e479459154f6faf7 to your computer and use it in GitHub Desktop.
import unittest
class MarsRover():
def __init__(self, facing, x, y, planet_map) -> None:
self.facing: str = facing
self.x: int = x
self.y: int = y
self.planet_map: Planet = planet_map
self.compass: list[str] = ["N", "E", "S", "W"]
self.obstacle_detected: bool = False
def process_commands(self, commands) -> bool:
if not isinstance(commands, str):
return False
for command in commands:
if command not in ("f", "b", "r", "l"):
return False
if command in ("f", "b") and not self.move(command):
return False
if command in ("r", "l"):
self.turn(command)
return True
def move(self, step: str) -> bool:
delta_x, delta_y = self._calculate_movement_delta(step)
new_x, new_y = self._calculate_wrapped_position(delta_x, delta_y)
if self._detects_obstacle(new_x, new_y):
self.obstacle_detected = True
return False
self.x, self.y = new_x, new_y
return True
def _calculate_movement_delta(self, step: str) -> tuple[int, int]:
delta = 1 if step == "f" else -1
return {
"N": (0, delta),
"E": (delta, 0),
"S": (0, -delta),
"W": (-delta, 0)
}[self.facing]
def _calculate_wrapped_position(self, delta_x: int, delta_y: int) -> tuple[int, int]:
new_x = (self.x + delta_x) % self.planet_map.width
new_y = (self.y + delta_y) % self.planet_map.height
return new_x, new_y
def _detects_obstacle(self, x: int, y: int) -> bool:
return self.planet_map.planet_map[y][x] == "*"
def turn(self, direction) -> None:
int_facing = self.compass.index(self.facing)
if direction == "r":
new_facing = (int_facing+1) % len(self.compass)
elif direction == "l":
new_facing = (int_facing-1) % len(self.compass)
self.facing = self.compass[new_facing]
class Planet():
def __init__(self, width, height) -> None:
self.width = width
self.height = height
self.planet_map = [["-" for _x in range(width)] for _y in range(height)]
def add_obstacle(self, x, y):
self.planet_map[y][x] = "*"
class TestMarsRoverInitialState(unittest.TestCase):
def setUp(self):
self.mars_map = Planet(3, 3)
self.rover = MarsRover("N", 1, 2, self.mars_map)
def test_rover_facing_specify_initial_direction(self):
self.assertEqual(self.rover.facing, "N")
def test_rover_coordinate_x_specified_initial_x(self):
self.assertEqual(self.rover.x, 1)
def test_rover_coordinate_y_specified_initial_y(self):
self.assertEqual(self.rover.y, 2)
def test_rover_planet_map_specified_initial_planet_map(self):
self.assertEqual(self.rover.planet_map.width, 3)
self.assertEqual(self.rover.planet_map.height, 3)
def test_rover_compass_initial_default(self):
self.assertEqual(self.rover.compass, ["N", "E", "S", "W"])
class TestMarsRover(unittest.TestCase):
def setUp(self):
self.mars_map = Planet(3, 3)
self.mars_map.add_obstacle(0,2)
self.rover = MarsRover("N", 0, 0, self.mars_map)
def test_valid_str_commands(self):
is_valid = self.rover.process_commands("lfrb")
self.assertTrue(is_valid)
def test_invalid_int_commands(self):
is_valid = self.rover.process_commands(1)
self.assertFalse(is_valid)
def test_invalid_str_command(self):
self.assertFalse(self.rover.process_commands("x"))
def test_command_sequence_without_obstacle(self):
self.assertTrue(self.rover.process_commands("lffrbb"))
def test_command_sequence_with_obstacle(self):
self.assertFalse(self.rover.process_commands("ff"))
class TestMarsRoverMove(unittest.TestCase):
def setUp(self):
self.mars_map = Planet(3, 3)
self.rover = MarsRover("N", 0, 0, self.mars_map)
def test_move_forward_from_north_increments_y(self):
self.rover.move("f")
self.assertEqual(self.rover.y, 1)
def test_move_backward_from_north_wraps_y_to_other_edge(self):
self.rover.move("b")
self.assertEqual(self.rover.y, 2)
def test_move_forward_from_east_increments_x(self):
self.rover.facing = "E"
self.rover.move("f")
self.assertEqual(self.rover.x, 1)
def test_move_backward_from_east_wraps_x_to_other_edge(self):
self.rover.facing = "E"
self.rover.move("b")
self.assertEqual(self.rover.x, 2)
def test_move_forward_from_south_wraps_y_to_other_edge(self):
self.rover.facing = "S"
self.rover.move("f")
self.assertEqual(self.rover.y, 2)
def test_move_backward_from_south_increments_y(self):
self.rover.facing = "S"
self.rover.move("b")
self.assertEqual(self.rover.y, 1)
def test_move_forward_from_west_wraps_x_to_other_edge(self):
self.rover.facing = "W"
self.rover.move("f")
self.assertEqual(self.rover.x, 2)
def test_move_backward_from_west_increments_x(self):
self.rover.facing = "W"
self.rover.move("b")
self.assertEqual(self.rover.x, 1)
class TestMarsRoverTurn(unittest.TestCase):
def setUp(self):
self.mars_map = Planet(3, 3)
self.rover = MarsRover("N", 0, 0, self.mars_map)
def test_turn_left_from_north_to_west(self):
self.rover.turn("l")
self.assertEqual(self.rover.facing, "W")
def test_turn_right_from_north_to_east(self):
self.rover.turn("r")
self.assertEqual(self.rover.facing, "E")
def test_turn_left_from_west_to_south(self):
self.rover.facing = "W"
self.rover.turn("l")
self.assertEqual(self.rover.facing, "S")
def test_turn_right_from_west_to_north(self):
self.rover.facing = "W"
self.rover.turn("r")
self.assertEqual(self.rover.facing, "N")
class TestPlanet(unittest.TestCase):
def setUp(self):
self.planet = Planet(3, 3)
def test_planet_creation(self):
expected_map = [
["-", "-", "-"],
["-", "-", "-"],
["-", "-", "-"]
]
self.assertEqual(self.planet.planet_map, expected_map)
def test_add_obstacle(self):
self.planet.add_obstacle(1, 1)
expected_map_with_obstacle = [
["-", "-", "-"],
["-", "*", "-"],
["-", "-", "-"]
]
self.assertEqual(self.planet.planet_map, expected_map_with_obstacle)
# 2 |*|-|-|
# 1 |-|-|-|
# 0 |^|-|-|
# 0 1 2
if __name__ == '__main__':
unittest.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment