class TestCommandLine(unittest.TestCase): def setUp(self): time_step = 0.05 self.agents = [Agent(0, 0, time_step)] self.arena = Arena(10, 20) self.biased_grid = BiasedGrid(self.arena.get_dimensions()) self.algorithm = AStar(self.arena, self.agents, self.biased_grid) self.cli = CommandLine(self.arena, self.agents) def test_help(self): command = 'help' retval = self.cli.parse_command(command) self.assertTrue(retval) def test_help_specific(self): command = 'help move_agent' retval = self.cli.parse_command(command) self.assertTrue(retval) def test_agent_move(self): command = 'move_agent 0 X 4' retval = self.cli.parse_command(command) self.assertTrue(retval) def test_blockage(self): command = 'blockage set 4 4' retval = self.cli.parse_command(command) self.assertTrue(retval) self.assertEqual(TileState.BLOCKED, self.arena.get_tile_state(4, 4))
def __init__(self, arena: Arena, agents: list, routing_manager: MultiAgentAlgorithm, biased_grid: BiasedGrid, timestep: float, dpi_scaling: int = 40) -> None: """ Create a new canvas for rendering the simulation. The base canvas is based on an Arena object, which controls the domain of the simulation :param: arena: the arena object :param: agents: list of agents :param: routing_manager: routing manager object :param: biased_grid: biased grid for routing only in certain directions :param: timestep: the animation timestep in seconds :param: dpi_scaling: the dpi scaling to use for one grid square :return: """ # initialize pygame as a renderer pygame.init() pygame.font.init() # create the rendering canvas self.x_size, self.y_size = arena.get_dimensions() self.dpi = dpi_scaling self._display_x = self.x_size * self.dpi self._display_y = self.y_size * self.dpi self.screen = pygame.display.set_mode( (self._display_x, self._display_y)) self.clock = pygame.time.Clock() self.arena = arena self.agents = agents self.routing_manager = routing_manager self.biased_grid = biased_grid self.agent_selected = None self._render_directions = True self._event_tiles = None # dict of color keys self.colors_dict = { 'tile_free': (180, 180, 180), 'tile_blocked': (0, 0, 0), 'tile_reserved': (60, 60, 60), 'tile_target': (200, 135, 135), 'grid_lines': (255, 255, 255), 'agent': COLORS, 'agent_selected': (245, 100, 90), 'agent_border': (0, 0, 0), 'soft_bias': (225, 225, 225), 'hard_bias': (200, 80, 80), 'event_tile': (255, 255, 200) } self.total_elements = len(self.colors_dict)
class TestAStar(unittest.TestCase): def setUp(self): time_step = 0.005 self.arena = Arena(10, 10) self.biased_grid = BiasedGrid(self.arena.get_dimensions()) self.agents = [Agent(0, 0, time_step), Agent(1, 1, time_step)] self.a_star = AStar(self.arena, self.agents, self.biased_grid) def test_a_star_init(self): self.assertEqual(self.arena, self.a_star.arena) self.assertEqual(self.agents, self.a_star.agents) self.assertEqual([], self.a_star.path) def test_check_blocked_square_fails(self): self.arena.set_blockage([0], [1]) status = self.a_star.check_target_location(0, 1) self.assertEqual(RoutingStatus.TARGET_BLOCKED, status) def test_check_reserved_square_fails(self): self.arena.set_reserved([0], [1]) status = self.a_star.check_target_location(0, 1) self.assertEqual(RoutingStatus.TARGET_RESERVED, status) def test_route_to_blocked_square_fails(self): self.arena.set_blockage([0], [1]) status = self.a_star.route(self.agents[0], (0, 1)) self.assertEqual(RoutingStatus.TARGET_BLOCKED, status) def test_route_to_reserved_square_fails(self): self.arena.set_reserved([0], [1]) status = self.a_star.route(self.agents[0], (0, 1)) self.assertEqual(RoutingStatus.TARGET_RESERVED, status) def test_initialize_nodes(self): start = (0, 0) target = (0, 4) start_node = AStarNode(start) end_node = AStarNode(target) self.a_star.initialize_nodes(self.agents[0], target) start_eq = start_node == self.a_star.start end_eq = end_node == self.a_star.target self.assertTrue(start_eq) self.assertTrue(end_eq) def test_get_new_nodes_in_empty_area(self): # initialize the search, and then get neighbouring tiles start = (1, 1) expected_neighbours = [(2, 1), (1, 2), (0, 1), (1, 0)] parent_node = AStarNode(start) new_nodes = self.a_star.generate_new_nodes(parent_node) self.assertEqual(len(expected_neighbours), len(new_nodes)) # make sure all of the correct nodes are returned for node_location in expected_neighbours: test_node = AStarNode(node_location) node_exists = test_node in new_nodes self.assertTrue(node_exists) def test_get_new_nodes_in_corner(self): # initialize the search in a corner, and then get neighbouring tiles start = (0, 0) expected_neighbours = [(0, 1), (1, 0)] parent_node = AStarNode(start) new_nodes = self.a_star.generate_new_nodes(parent_node) self.assertEqual(len(expected_neighbours), len(new_nodes)) # make sure all of the correct nodes are returned for node_location in expected_neighbours: test_node = AStarNode(node_location) node_exists = test_node in new_nodes self.assertTrue(node_exists) def test_get_new_nodes_with_blockage(self): # initialize the search, set some blockages, and then make sure only the free tiles are returned start = (1, 1) self.arena.set_blockage([2], [0, 1, 2, 3]) expected_neighbours = [(1, 2), (0, 1), (1, 0)] parent_node = AStarNode(start) new_nodes = self.a_star.generate_new_nodes(parent_node) self.assertEqual(len(expected_neighbours), len(new_nodes)) # make sure all of the correct nodes are returned for node_location in expected_neighbours: test_node = AStarNode(node_location) node_exists = test_node in new_nodes self.assertTrue(node_exists) def test_calculate_heuristic(self): start = (1, 1) target = (4, 4) start_node = AStarNode(start) target_node = AStarNode(target) heuristic = self.a_star.calculate_heuristic_cost( start_node, target_node) self.assertEqual(6, heuristic) @unittest.skip('debug') def test_routing_simple_path(self): target = (4, 4) status = self.a_star.route(self.agents[0], target) self.assertEqual(RoutingStatus.SUCCESS, status) # make sure the final nodes are correct self.assertTupleEqual((4, 4), self.a_star.node_path[-1].location) def test_route_to_tile_with_other_agent_fails(self): status = self.a_star.check_target_location(1, 1) self.assertEqual(RoutingStatus.TARGET_RESERVED, status) def test_calculate_turn_cost_no_factor(self): start_node = AStarNode((0, 0)) node_1 = AStarNode((1, 0), parent=start_node) node_2 = AStarNode((2, 0), parent=node_1) node_3 = AStarNode((2, 1), parent=node_2) node_4 = AStarNode((2, 2), parent=node_3) node_5 = AStarNode((3, 2), parent=node_4) turn_cost = self.a_star.calculate_turn_cost(node_5) self.assertEqual(0, turn_cost) def test_calculate_turn_cost_with_weight(self): start_node = AStarNode((0, 0)) self.a_star.turn_factor = 1 node_1 = AStarNode((1, 0), parent=start_node) node_2 = AStarNode((2, 0), parent=node_1) node_3 = AStarNode((2, 1), parent=node_2) node_4 = AStarNode((2, 2), parent=node_3) node_5 = AStarNode((3, 2), parent=node_4) turn_cost = self.a_star.calculate_turn_cost(node_5) self.assertEqual(2, turn_cost) def test_calculate_inline_cost(self): self.a_star.inline_factor = 10 self.arena.set_agent_target(3, 3) node = AStarNode((1, 3)) score = self.a_star.calculate_inline_cost(node) self.assertEqual(10, score) def test_is_direction_valid(self): parents = [(1, 1), (1, 1), (1, 1), (1, 1)] children = [(2, 1), (0, 1), (1, 2), (1, 0)] main_biases = [ BiasedDirection.ONLY_X_POSITIVE, BiasedDirection.ONLY_X_NEGATIVE, BiasedDirection.ONLY_Y_POSITIVE, BiasedDirection.ONLY_Y_NEGATIVE ] secondary_biases = [ BiasedDirection.ONLY_Y_POSITIVE, BiasedDirection.ONLY_Y_NEGATIVE, BiasedDirection.ONLY_X_POSITIVE, BiasedDirection.ONLY_X_NEGATIVE ] for idx in range(len(parents)): parent = parents[idx] child = children[idx] bias = main_biases[idx] self.biased_grid[child] = bias self.biased_grid[parent] = bias self.assertTrue(self.a_star.is_direction_valid(parent, child)) self.assertFalse(self.a_star.is_direction_valid(child, parent)) bias = secondary_biases[idx] self.biased_grid[child] = bias self.biased_grid[parent] = bias self.assertFalse(self.a_star.is_direction_valid(parent, child))
class TestMultiAgentAlgorithm(unittest.TestCase): def setUp(self): time_step = 0.005 self.arena = Arena(10, 10) self.biased_grid = BiasedGrid(self.arena.get_dimensions()) self.agents = [Agent(0, 0, time_step), Agent(5, 5, time_step)] self.algorithm = SingleAgentAlgorithmMock(self.arena, self.agents, self.biased_grid) self.routing_manager = MultiAgentAlgorithmMock(self.arena, self.agents, self.algorithm) def tearDown(self): pass def test_is_simulation_completed_passes_if_final_goal_reached(self): self.routing_manager.add_agent_goal(0, (0, 0)) self.routing_manager.add_agent_goal(1, (5, 5)) self.assertTrue(self.routing_manager.is_simulation_complete()) def test_simulation_not_completed_if_final_goal_not_reached(self): # add the current location as a segment, but also add a second location for each agent self.routing_manager.add_agent_goal(0, (0, 0)) self.routing_manager.add_agent_goal(1, (5, 5)) self.routing_manager.add_agent_goal(0, (1, 1)) self.routing_manager.add_agent_goal(1, (4, 4)) self.assertFalse(self.routing_manager.is_simulation_complete()) def test_update_agent_goal(self): self.routing_manager.add_agent_goal(0, (0, 0)) self.routing_manager.add_agent_goal(0, (1, 1)) self.assertTrue(self.routing_manager.is_agent_at_goal(0)) self.routing_manager.update_agent_goal(0) self.assertTupleEqual((1, 1), self.routing_manager.agent_goals[0][0]) self.assertFalse(self.routing_manager.is_agent_at_goal(0)) self.assertFalse(self.routing_manager.is_simulation_complete()) def test_routing_blockages(self): move_x_task = AgentTask(AgentTasks.MOVE, [AgentCoordinates.X, 3]) move_y_task = AgentTask(AgentTasks.MOVE, [AgentCoordinates.Y, 3]) self.routing_manager.add_agent_task(0, move_x_task) for i in range(1, 3): self.assertEqual(TileState.RESERVED, self.arena.get_tile_state(i, 0)) self.assertEqual(TileState.AGENT_TARGET, self.arena.get_tile_state(3, 0)) self.routing_manager.add_agent_task(0, move_y_task) for i in range(1, 3): self.assertEqual(TileState.RESERVED, self.arena.get_tile_state(3, i)) self.assertEqual(TileState.AGENT_TARGET, self.arena.get_tile_state(3, 3)) def test_adding_and_completing_task_clears_blockage(self): move_x_task = AgentTask(AgentTasks.MOVE, [AgentCoordinates.X, 3]) self.routing_manager.add_agent_task(0, move_x_task) for i in range(1, 3): self.assertEqual(TileState.RESERVED, self.arena.get_tile_state(i, 0)) self.assertEqual(TileState.AGENT_TARGET, self.arena.get_tile_state(3, 0)) self.routing_manager.signal_agent_event(0, AgentEvent.TASK_COMPLETED) for i in range(1, 4): self.assertEqual(TileState.FREE, self.arena.get_tile_state(i, 0)) def test_completing_task_clears_agent_active(self): move_x_task = AgentTask(AgentTasks.MOVE, [AgentCoordinates.X, 3]) self.routing_manager.add_agent_task(0, move_x_task) self.routing_manager.signal_agent_event(0, AgentEvent.TASK_COMPLETED) self.assertFalse(self.routing_manager.active_agents[0]) def test_initialize_algorithm(self): self.routing_manager.initialize() self.assertTrue(self.routing_manager.initialized) def test_is_agent_at_goal(self): self.routing_manager.add_agent_goal(0, (0, 0)) self.assertTrue(self.routing_manager.is_agent_at_goal(0)) def test_start_new_task(self): move_x_task = AgentTask(AgentTasks.MOVE, [AgentCoordinates.X, 3]) self.routing_manager.add_agent_task(0, move_x_task) self.routing_manager.start_new_task(0) self.assertTrue(self.routing_manager.active_agents[0])
class TestSingleAgentAlgorithm(unittest.TestCase): def setUp(self): time_step = 0.005 self.arena = Arena(10, 10) self.biased_grid = BiasedGrid(self.arena.get_dimensions()) self.agents = [Agent(0, 0, time_step), Agent(1, 1, time_step)] self.algorithm = SingleAgentAlgorithmMock(self.arena, self.agents, self.biased_grid) def tearDown(self): pass def test_reconstruct_empty_path_is_invalid(self): self.algorithm.node_path = list() status = self.algorithm.create_path() self.assertEqual(RoutingStatus.INVALID_PATH, status) def test_diagonal_path_is_invalid(self): self.algorithm.node_path = [NodeMock((0, 0)), NodeMock((1, 1))] status = self.algorithm.create_path() self.assertEqual(RoutingStatus.INVALID_PATH, status) def test_reconstruct_path(self): # force a list of nodes to be the node path self.algorithm.node_path = [ NodeMock((0, 0)), NodeMock((0, 1)), NodeMock((0, 2)), NodeMock((1, 2)), NodeMock((2, 2)) ] status = self.algorithm.create_path() self.assertEqual(RoutingStatus.SUCCESS, status) # The order of the tasks should be: Move X 2, Move Y 2 task_direction = [AgentCoordinates.Y, AgentCoordinates.X] task_distance = [2, 2] for idx, task in enumerate(self.algorithm.path): self.assertEqual(task_direction[idx], task.args[0]) self.assertEqual(task_distance[idx], task.args[1]) def test_reconstruct_path_zig_zag(self): # force a list of nodes to be the node path self.algorithm.node_path = [ NodeMock((0, 0)), NodeMock((1, 0)), NodeMock((1, 1)), NodeMock((2, 1)), NodeMock((2, 2)), NodeMock((3, 2)), NodeMock((3, 3)), NodeMock((4, 3)), NodeMock((4, 4)) ] status = self.algorithm.create_path() self.assertEqual(RoutingStatus.SUCCESS, status) # this path should contain 8 tasks self.assertEqual(8, len(self.algorithm.path)) # The order of the tasks should be: Move X 1, Move Y 1, Move X 1, Move Y 1 task_direction = [ AgentCoordinates.X, AgentCoordinates.Y, AgentCoordinates.X, AgentCoordinates.Y, AgentCoordinates.X, AgentCoordinates.Y, AgentCoordinates.X, AgentCoordinates.Y ] task_distance = [1, 1, 1, 1, 1, 1, 1, 1] for idx, task in enumerate(self.algorithm.path): self.assertEqual(task_direction[idx], task.args[0]) self.assertEqual(task_distance[idx], task.args[1])
class TestArena(unittest.TestCase): def setUp(self): # note: this means coordinates go from x = [0-4], y = [0-9] self.arena = Arena(5, 10) def test_generate_grid(self): self.assertEqual(TileState.FREE, self.arena.get_tile_state(4, 9)) def test_set_blockage(self): x_values = list(range(3)) y_values = [3] self.arena.set_blockage(x_values, y_values) for x in x_values: for y in y_values: self.assertEqual(TileState.BLOCKED, self.arena.get_tile_state(x, y)) def test_set_reserved(self): x_values = list(range(3)) y_values = [3] self.arena.set_reserved(x_values, y_values) for x in x_values: for y in y_values: self.assertEqual(TileState.RESERVED, self.arena.get_tile_state(x, y)) def test_get_dimensions(self): dimensions = self.arena.get_dimensions() self.assertTupleEqual((5, 10), dimensions) def test_clear_blockage(self): x_values = list(range(3)) y_values = [3] self.arena.set_blockage(x_values, y_values) self.arena.clear_blockage(x_values, y_values) for x in x_values: for y in y_values: self.assertEqual(TileState.FREE, self.arena.get_tile_state(x, y)) def test_boundary_checking(self): x_range = list(range(5)) y_range = list(range(10)) # test the upper and lower rows for x in x_range: self.assertTrue(self.arena.is_boundary_tile(x, 0)) self.assertTrue(self.arena.is_boundary_tile(x, 9)) # test the sides for y in y_range: self.assertTrue(self.arena.is_boundary_tile(0, y)) self.assertTrue(self.arena.is_boundary_tile(4, y)) def test_neighbours_at_corners(self): corners = [(0, 0), (0, 9), (4, 9), (4, 0)] test_neighbours = [[(1, 0), (0, 1)], [(0, 8), (1, 9)], [(3, 9), (4, 8)], [(3, 0), (4, 1)]] for idx, corner in enumerate(corners): neighbours = self.arena.get_neighbours(corner[0], corner[1]) # each corner should only have two neighbours self.assertEqual(2, len(neighbours)) for check in test_neighbours[idx]: self.assertTrue((check in neighbours)) def test_set_agent_target(self): self.arena.set_agent_target(4, 4) state = self.arena.get_tile_state(4, 4) self.assertEqual(TileState.AGENT_TARGET, state)