def test_givenEmptyMazeAndRobotRadius_whenAddObstacle_thenRobotRadiusIsTakenIntoAccountWhenAddingObstacle( self, ): expected_maze = Maze( np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) obstacle_position = Position(13, 9) actual_maze = self.maze_factory.create_from_shape((17, 20, 0)) actual_maze.add_obstacle(obstacle_position, self.A_ROBOT_RADIUS, self.AN_OBSTACLE_RADIUS) self.assertEqual(expected_maze, actual_maze)
def create_from_shape(self, shape: Tuple[int, int, int]) -> Maze: width, height, *_ = shape array = np.zeros((width, height)) for i in range(width): for j in range(height): if i == 0 or j == 0 or i == width - 1 or j == height - 1: array[i][j] = 1 return Maze(array)
def test_givenPuckObstacle_whenRemovePuckAsObstacle_thenObstacleIsRemove( self): expected_maze = self.maze_factory.create_from_shape((8, 10, 0)) actual_maze = Maze( np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 1, 1, 1, 0, 0, 0, 1], [1, 0, 0, 1, 1, 1, 0, 0, 0, 1], [1, 0, 0, 1, 1, 1, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) actual_maze.remove_puck_as_obstacle(Position(5, 4), obstacle_radius=2) self.assertEqual(actual_maze, expected_maze)
def test_whenCreateFromShape_thenMazeHasBorderOfOnesAndRightShape(self, ): maze_width = 5 maze_height = 10 shape = (maze_width, maze_height, 0) expected_maze = Maze( np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) maze = self.maze_factory.create_from_shape(shape) self.assertEqual(maze, expected_maze)
def test_givenObstacleCloseToEdge_whenAddObstacle_thenDontRaiseIndexOufOfBoundsException( self, ): expected_maze = Maze( np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) obstacle_position = Position(6, 8) actual_maze = self.maze_factory.create_from_shape((8, 10, 0)) actual_maze.add_obstacle(obstacle_position, self.A_ROBOT_RADIUS, self.AN_OBSTACLE_RADIUS) self.assertEqual(expected_maze, actual_maze)
def _validate_maze(self, maze: Maze) -> Maze: if maze.get_shape() != (0, ) and len(maze.get_shape()) <= 2: return maze raise InvalidMazeException
class TestAStarShortestPathAlgorithm(TestCase): A_BAD_MAZE = Maze(array=np.array([])) A_MAZE = Maze(array=np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) AN_HORIZONTALLY_OUT_OF_BOUND_POSITION = Position(1, 25) A_VERTICALLY_OUT_OF_BOUND_POSITION = Position(12, 1) A_NEGATIVE_HORIZONTAL_POSITION = Position(1, -1) A_NEGATIVE_VERTICAL_POSITION = Position(-1, 1) A_VALID_POSITION = Position(1, 1) ANOTHER_VALID_POSITION = Position(2, 2) AN_OBSTACLE_POSITION = Position(4, 0) def setUp(self) -> None: self.a_start_algorithm = AStarShortestPathAlgorithm() self.a_start_algorithm.set_maze(self.A_MAZE) def test_givenABadMaze_whenInitializingAlgorithm_thenRaiseInvalidMazeException( self, ): algorithm = AStarShortestPathAlgorithm() with self.assertRaises(InvalidMazeException): algorithm.set_maze(self.A_BAD_MAZE) def test_givenNoneMazeSet_whenFindPath_thenRaiseInvalidMazeException(self): algorithm = AStarShortestPathAlgorithm() with self.assertRaises(InvalidMazeException): algorithm.find_shortest_path_with_cartesian_coordinates( self.A_VALID_POSITION, self.ANOTHER_VALID_POSITION) def test_givenStartPositionWithNegativeCoordinate_whenFindPath_thenRaiseInvalidStartPointException( self, ): with self.assertRaises(InvalidStartPointException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.A_NEGATIVE_VERTICAL_POSITION, self.A_VALID_POSITION) def test_givenHorizontallyOutOfBoundsStartPosition_whenFindPath_thenRaiseInvalidStartPointException( self, ): with self.assertRaises(InvalidStartPointException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.AN_HORIZONTALLY_OUT_OF_BOUND_POSITION, self.A_VALID_POSITION) def test_givenVerticallyOutOfBoundsStartPosition_whenFindPath_thenRaiseInvalidStartPointException( self, ): with self.assertRaises(InvalidStartPointException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.A_VERTICALLY_OUT_OF_BOUND_POSITION, self.A_VALID_POSITION) def test_givenEndPositionWithNegativeCoordinate_whenFindPath_thenRaiseInvalidStartPointException( self, ): with self.assertRaises(InvalidStartPointException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.A_NEGATIVE_HORIZONTAL_POSITION, self.A_VALID_POSITION) def test_givenHorizontallyOutOfBoundsEndingPosition_whenFindPath_thenRaiseInvalidDestinationException( self, ): with self.assertRaises(InvalidDestinationException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.A_VALID_POSITION, self.AN_HORIZONTALLY_OUT_OF_BOUND_POSITION) def test_givenVerticallyOutOfBoundsEndingPosition_whenFindPath_thenRaiseInvalidDestinationException( self, ): with self.assertRaises(InvalidDestinationException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.A_VALID_POSITION, self.A_VERTICALLY_OUT_OF_BOUND_POSITION) def test_givenAMaze_whenFindPathWithObstacleAsDestination_thenRaiseInvalidDestinationException( self, ): with self.assertRaises(InvalidDestinationException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.A_VALID_POSITION, self.AN_OBSTACLE_POSITION) def test_givenAMaze_whenFindPathWithObstacleAsStartPoint_thenRaiseInvalidDestinationException( self, ): with self.assertRaises(InvalidStartPointException): self.a_start_algorithm.find_shortest_path_with_cartesian_coordinates( self.AN_OBSTACLE_POSITION, self.A_VALID_POSITION) def test_givenStartAndEndPosition_whenFindPathWithCartesianCoordinates_thenFlipsCoordinatesAndFindsPath( self, ): starting_position = Position(2, 1) ending_position = Position(5, 1) expected_path = Path([ Position(2, 1), Position(3, 1), Position(3, 2), Position(3, 3), Position(3, 4), Position(3, 5), Position(3, 6), Position(4, 6), Position(5, 6), Position(5, 5), Position(5, 4), Position(5, 3), Position(5, 2), Position(5, 1), ]) actual_path = (self.a_start_algorithm. find_shortest_path_with_cartesian_coordinates( starting_position, ending_position)) self.assertEqual(expected_path, actual_path)
class TestMaze(TestCase): A_ROBOT_RADIUS = 2 AN_OBSTACLE_RADIUS = 3 A_MAZE_ARRAY = np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 1, 0, 0, 0, 0, 1], ]) AN_INDEX = 2 def setUp(self) -> None: self.maze_factory = MazeFactory(self.A_ROBOT_RADIUS, self.AN_OBSTACLE_RADIUS) self.maze = Maze(self.A_MAZE_ARRAY) def test_whenGetShape_thenReturnNumpyShape(self) -> None: expected_shape = self.A_MAZE_ARRAY.shape actual_shape = self.maze.get_shape() self.assertEqual(expected_shape, actual_shape) def test_whenGetItem_thenReturnItemInNumpyArray(self) -> None: expected_item = self.A_MAZE_ARRAY[self.AN_INDEX][self.AN_INDEX] actual_item = self.maze[self.AN_INDEX][self.AN_INDEX] self.assertEqual(expected_item, actual_item) def test_givenEmptyMazeAndRobotRadius_whenAddObstacle_thenRobotRadiusIsTakenIntoAccountWhenAddingObstacle( self, ): expected_maze = Maze( np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) obstacle_position = Position(13, 9) actual_maze = self.maze_factory.create_from_shape((17, 20, 0)) actual_maze.add_obstacle(obstacle_position, self.A_ROBOT_RADIUS, self.AN_OBSTACLE_RADIUS) self.assertEqual(expected_maze, actual_maze) def test_givenObstacleCloseToEdge_whenAddObstacle_thenDontRaiseIndexOufOfBoundsException( self, ): expected_maze = Maze( np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) obstacle_position = Position(6, 8) actual_maze = self.maze_factory.create_from_shape((8, 10, 0)) actual_maze.add_obstacle(obstacle_position, self.A_ROBOT_RADIUS, self.AN_OBSTACLE_RADIUS) self.assertEqual(expected_maze, actual_maze) def test_givenPuckObstacle_whenRemovePuckAsObstacle_thenObstacleIsRemove( self): expected_maze = self.maze_factory.create_from_shape((8, 10, 0)) actual_maze = Maze( np.array([ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 0, 0, 0, 0, 0, 0, 1], [1, 0, 0, 1, 1, 1, 0, 0, 0, 1], [1, 0, 0, 1, 1, 1, 0, 0, 0, 1], [1, 0, 0, 1, 1, 1, 0, 0, 0, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], ])) actual_maze.remove_puck_as_obstacle(Position(5, 4), obstacle_radius=2) self.assertEqual(actual_maze, expected_maze)
def setUp(self) -> None: self.maze_factory = MazeFactory(self.A_ROBOT_RADIUS, self.AN_OBSTACLE_RADIUS) self.maze = Maze(self.A_MAZE_ARRAY)