def test_two_by_two_graph(self): g = Graph() # The graph looks like this: # o - o # | # o - o node_00 = "(0, 0)" node_01 = "(0, 1)" node_10 = "(1, 0)" node_11 = "(1, 1)" g.add_vertice(node_00) g.add_vertice(node_01) g.add_vertice(node_10) g.add_vertice(node_11) g.add_edge(node_00, node_01, 1) g.add_edge(node_10, node_11, 2) g.add_edge(node_01, node_11, 3) g.add_edge(node_00, node_11, 4) kruskal_edges = kruskal(g) self.assertEqual(3, len(kruskal_edges)) self.assertTrue(self.contains_edge(node_00, node_01, kruskal_edges)) self.assertTrue(self.contains_edge(node_10, node_11, kruskal_edges)) self.assertTrue(self.contains_edge(node_01, node_11, kruskal_edges))
def test_non_existent_node_neighbor(self): g = Graph() try: g.neighbors("lol") self.fail("Expected fail if no lol exists") except ValueError as e: self.assertEqual("Vertice not in graph", str(e))
def test_BFS_negative_depth(self): g = Graph() g.add_vertice("s") try: nodes_at_level(g, "s", -1) self.fail("Should not be able to use negative depth") except ValueError as e: self.assertEqual("Given invalid depth", str(e))
def test_shortest_path_not_dest_node(self): g = Graph() g.add_vertice("from") try: shortest_path(g, "from", "to") self.fail("Should not be able to find a path in non-existent node") except ValueError as e: self.assertEqual("Vertice not found in graph", str(e))
def test_simple_connection_BFS_depth(self): g = Graph() g.add_vertice("one") g.add_vertice("two") g.add_vertice("three") g.add_edge("one", "two") g.add_edge("two", "three") self.assertEqual(["one"], nodes_at_level(g, "one", 0)) self.assertEqual(["two"], nodes_at_level(g, "one", 1)) self.assertEqual(["three"], nodes_at_level(g, "one", 2))
def test_BFS_invalid_root(self): g = Graph() try: nodes_at_level(g, "dog", 2) self.fail("Should not be able to use non-existent vertice") except ValueError as e: self.assertEqual("Root not found in graph", str(e))
def test_forked_BFS(self): g = Graph() v00 = "(0, 0)" v01 = "(0, 1)" v10 = "(1, 0)" v11 = "(1, 1)" g.add_vertice(v00) g.add_vertice(v01) g.add_vertice(v10) g.add_vertice(v11) g.add_edge(v00, v01) g.add_edge(v00, v10) g.add_edge(v10, v11) self.assertCountEqual([v00], nodes_at_level(g, v00, 0)) self.assertCountEqual([v01, v10], nodes_at_level(g, v00, 1)) self.assertCountEqual([v11], nodes_at_level(g, v00, 2))
def test_adding_edge_forces_connection(self): g = Graph() g.add_vertice("one") g.add_vertice("two") self.assertFalse(g.contains_edge("one", "two")) g.add_edge("one", "two") self.assertTrue(g.contains_edge("two", "one")) self.assertTrue(g.contains_edge("one", "two")) vertices = g.vertices() vertice_one = self.find_vertice("one", vertices) vertice_two = self.find_vertice("two", vertices) self.assertTrue(vertice_one.is_neighbor(vertice_two)) self.assertTrue(vertice_two.is_neighbor(vertice_one))
def test_add_self_reference_edge(self): g = Graph() g.add_vertice("single") try: g.add_edge("single", "single") except ValueError as e: self.assertEqual("Vertice cannot become it's own neighbor", str(e)) self.assertFalse(g.contains_edge("single", "single"))
def test_shortest_path_forked_path(self): g = Graph() v00 = "(0, 0)" v01 = "(0, 1)" v10 = "(1, 0)" v11 = "(1, 1)" g.add_vertice(v00) g.add_vertice(v01) g.add_vertice(v10) g.add_vertice(v11) g.add_edge(v00, v01) g.add_edge(v00, v10) g.add_edge(v10, v11) self.assertCountEqual([v00, v10, v11], shortest_path(g, v00, v11)) self.assertCountEqual([], shortest_path(g, v00, v00)) self.assertCountEqual([v01, v00, v10, v11], shortest_path(g, v01, v11))
def test_shortest_path_one_path(self): g = Graph() g.add_vertice("one") g.add_vertice("two") g.add_edge("one", "two") self.assertCountEqual(["one", "two"], shortest_path(g, "one", "two"))
def test_adding_existent_edge(self): g = Graph() g.add_vertice("one") try: g.add_vertice("one") self.fail("Should not be able to add another vertice") except ValueError as e: self.assertEqual("Vertice already in the graph", str(e)) self.assertTrue(g.contains_vertice("one"))
def test_both_non_existent_edge(self): g = Graph() g.add_vertice("other") try: g.add_edge("nil", "nope") self.fail("Should not be able to add edge if both vertices exist") except ValueError as e: self.assertEqual("Vertice contained in edge not in graph", str(e))
def test_one_direction_vertical_connection(self): g = Graph() node_00 = "(0, 0)" node_10 = "(1, 0)" node_20 = "(2, 0)" g.add_vertice(node_00) g.add_vertice(node_10) g.add_vertice(node_20) g.add_edge(node_00, node_10, 1) g.add_edge(node_10, node_20, 2) kruskal_edges = kruskal(g) self.assertEqual(2, len(kruskal_edges)) self.assertTrue(self.contains_edge(node_00, node_10, kruskal_edges)) self.assertTrue(self.contains_edge(node_10, node_20, kruskal_edges))
def test_one_direction_horizontal_connection(self): g = Graph() # The graph looks like this: # o - o - o node_00 = "(0, 0)" node_01 = "(0, 1)" node_02 = "(0, 2)" g.add_vertice(node_00) g.add_vertice(node_01) g.add_vertice(node_02) g.add_edge(node_00, node_01, 1) g.add_edge(node_01, node_02, 2) kruskal_edges = kruskal(g) self.assertEqual(2, len(kruskal_edges)) self.assertTrue(self.contains_edge(node_00, node_01, kruskal_edges)) self.assertTrue(self.contains_edge(node_01, node_02, kruskal_edges))
def test_removing_edges_with_no_added_edge(self): """Expected behavior is to interact with the graph by using edges. By mutating the vertices and add neighbors manually, the edges will not be correctly connected""" g = Graph() g.add_vertice("one") g.add_vertice("two") vertices = g.vertices() vertice_one = self.find_vertice("one", vertices) vertice_two = self.find_vertice("two", vertices) vertice_one.add_neighbor(vertice_two) self.assertTrue(vertice_one.is_neighbor(vertice_two)) self.assertFalse(vertice_two.is_neighbor(vertice_one)) g.remove_edge("one", "two") # There was initially no edge and thus nothing was changed self.assertTrue(vertice_one.is_neighbor(vertice_two)) self.assertFalse(vertice_two.is_neighbor(vertice_one))
def test_three_by_three_graph(self): g = Graph() # The graph looks like this: # o - o - o # | # o - o - o # | # o - o - o node_00 = "(0, 0)" node_01 = "(0, 1)" node_02 = "(0, 2)" node_10 = "(1, 0)" node_11 = "(1, 1)" node_12 = "(1, 2)" node_20 = "(2, 0)" node_21 = "(2, 1)" node_22 = "(2, 2)" g.add_vertice(node_00) g.add_vertice(node_01) g.add_vertice(node_02) g.add_vertice(node_10) g.add_vertice(node_11) g.add_vertice(node_12) g.add_vertice(node_20) g.add_vertice(node_21) g.add_vertice(node_22) g.add_edge(node_00, node_01, 0) g.add_edge(node_01, node_02, 1) g.add_edge(node_00, node_10, 10) g.add_edge(node_01, node_11, 2) g.add_edge(node_02, node_12, 11) g.add_edge(node_10, node_11, 3) g.add_edge(node_11, node_12, 4) g.add_edge(node_10, node_20, 5) g.add_edge(node_11, node_21, 12) g.add_edge(node_12, node_22, 13) g.add_edge(node_20, node_21, 6) g.add_edge(node_21, node_22, 7) kruskal_edges = kruskal(g) self.assertEqual(8, len(kruskal_edges)) self.assertTrue(self.contains_edge(node_00, node_01, kruskal_edges)) self.assertTrue(self.contains_edge(node_01, node_02, kruskal_edges)) self.assertTrue(self.contains_edge(node_01, node_11, kruskal_edges)) self.assertTrue(self.contains_edge(node_10, node_11, kruskal_edges)) self.assertTrue(self.contains_edge(node_11, node_12, kruskal_edges)) self.assertTrue(self.contains_edge(node_10, node_20, kruskal_edges)) self.assertTrue(self.contains_edge(node_20, node_21, kruskal_edges)) self.assertTrue(self.contains_edge(node_21, node_22, kruskal_edges))
def test_adding_vertice_basic_add(self): g = Graph() self.assertFalse(g.contains_vertice("one")) g.add_vertice("one") self.assertTrue(g.contains_vertice("one"))
def test_shortest_path_empty_case(self): g = Graph() g.add_vertice("one") g.add_vertice("two") self.assertEqual([], shortest_path(g, "one", "two"))
def test_simple_case_BFS_depth(self): g = Graph() g.add_vertice("one") self.assertEqual(["one"], nodes_at_level(g, "one", 0))
def test_no_neighbors(self): g = Graph() g.add_vertice("hi") self.assertEqual([], g.neighbors("hi"))
def test_two_by_three_graph(self): g = Graph() # The graph should look like this: # o - o - o # | | # o - o o node_00 = "(0, 0)" node_01 = "(0, 1)" node_02 = "(0, 2)" node_10 = "(1, 0)" node_11 = "(1, 1)" node_12 = "(1, 2)" g.add_vertice(node_00) g.add_vertice(node_01) g.add_vertice(node_02) g.add_vertice(node_10) g.add_vertice(node_11) g.add_vertice(node_12) g.add_edge(node_00, node_01, 0) g.add_edge(node_01, node_02, 1) g.add_edge(node_10, node_11, 2) g.add_edge(node_11, node_12, 9) g.add_edge(node_00, node_10, 3) g.add_edge(node_01, node_11, 10) g.add_edge(node_02, node_12, 4) kruskal_edges = kruskal(g) self.assertEqual(5, len(kruskal_edges)) self.assertTrue(self.contains_edge(node_00, node_01, kruskal_edges)) self.assertTrue(self.contains_edge(node_01, node_02, kruskal_edges)) self.assertTrue(self.contains_edge(node_00, node_10, kruskal_edges)) self.assertTrue(self.contains_edge(node_02, node_12, kruskal_edges)) self.assertTrue(self.contains_edge(node_10, node_11, kruskal_edges))
def __construct_maze(self, width, height, length): """Given a length on which to construct the length of the solution to the maze, constructs a maze with hints towards the size of the maze. By the constraints of our maze, the bare minimum dimensions of this maze must necessarily be (width + height) >= length - 1 to find a path such that a path of the appropriate length can be found. Args: width(int): the width of the maze. height(int): the height of the maze. length(int): the length of the maze. Returns: Graph: a resulting graph """ weight_lower_bound = 0 weight_upper_bound = 10000 graph = Graph() def add_vertices(): for row in range(height): for col in range(width): graph.add_vertice(cell_format.format(row, col)) def add_edges(): # the edges are added in sequence of each row connected first, # then all rows are joined. # e. g. o - o - o # then o o o # | | | # o o o for row in range(height): for col in range(width - 1): graph.add_edge( cell_format.format(row, col), cell_format.format(row, col + 1), random.randint(weight_lower_bound, weight_upper_bound)) for row in range(height - 1): for col in range(width): graph.add_edge( cell_format.format(row, col), cell_format.format(row + 1, col), random.randint(weight_lower_bound, weight_upper_bound)) def remove_non_mst_edges(): kruskal_edges = set(kruskal(graph)) for edge in graph.edges(): if edge not in kruskal_edges: graph.remove_edge(edge.from_vertice().name(), edge.to_vertice().name()) def set_starting_position(): # arbitrarily picks the first starting point # length - 1 because 0 denotes the starting position potential_starting_points = nodes_at_level( graph, cell_format.format(self.__end_pos[0], self.__end_pos[1]), length - 1) # always works given that the width/height invariant is satisfied initial_starting_point_str = potential_starting_points[0] # Since the string is of the form (x, y) self.__starting_pos = tuple( [int(x) for x in initial_starting_point_str[1:-1].split(',')]) add_vertices() add_edges() remove_non_mst_edges() set_starting_position() return graph
def test_remove_edges_with_no_existing_vertices(self): g = Graph() g.remove_edge("dog", "fad")
def test_complex_graph_shortest_path(self): g = Graph() v00 = "(0, 0)" v01 = "(0, 1)" v02 = "(0, 2)" v10 = "(1, 0)" v11 = "(1, 1)" v12 = "(1, 2)" v20 = "(2, 0)" v21 = "(2, 1)" v22 = "(2, 2)" g.add_vertice(v00) g.add_vertice(v01) g.add_vertice(v02) g.add_vertice(v10) g.add_vertice(v11) g.add_vertice(v12) g.add_vertice(v20) g.add_vertice(v21) g.add_vertice(v22) # o - o - o # | # o o o # | | | # o - o - o g.add_edge(v00, v01) g.add_edge(v01, v02) g.add_edge(v01, v11) g.add_edge(v10, v20) g.add_edge(v11, v21) g.add_edge(v12, v22) g.add_edge(v20, v21) g.add_edge(v21, v22) self.assertCountEqual([v00, v01, v11, v21, v22, v12], shortest_path(g, v00, v12)) self.assertCountEqual([v00, v01, v02], shortest_path(g, v00, v02)) self.assertCountEqual([v10, v20, v21, v11, v01, v00], shortest_path(g, v10, v00)) self.assertCountEqual([v21, v11, v01, v02], shortest_path(g, v21, v02)) self.assertCountEqual([v12, v22, v21, v20, v10], shortest_path(g, v12, v10))
def test_multiple_neighbors(self): g = Graph() g.add_vertice("one") g.add_vertice("two") g.add_vertice("three") g.add_vertice("four") g.add_edge("one", "two") g.add_edge("one", "three") g.add_edge("two", "three") g.add_edge("two", "four") self.assertCountEqual(["two", "three"], g.neighbors("one")) self.assertCountEqual(["one", "three", "four"], g.neighbors("two")) self.assertCountEqual(["one", "two"], g.neighbors("three")) self.assertCountEqual(["two"], g.neighbors("four"))
def test_complex_graph_BFS(self): g = Graph() v00 = "(0, 0)" v01 = "(0, 1)" v02 = "(0, 2)" v10 = "(1, 0)" v11 = "(1, 1)" v12 = "(1, 2)" v20 = "(2, 0)" v21 = "(2, 1)" v22 = "(2, 2)" g.add_vertice(v00) g.add_vertice(v01) g.add_vertice(v02) g.add_vertice(v10) g.add_vertice(v11) g.add_vertice(v12) g.add_vertice(v20) g.add_vertice(v21) g.add_vertice(v22) # o - o - o # | # o o o` # | | | # o - o - o g.add_edge(v00, v01) g.add_edge(v01, v02) g.add_edge(v01, v11) g.add_edge(v10, v20) g.add_edge(v11, v21) g.add_edge(v12, v22) g.add_edge(v20, v21) g.add_edge(v21, v22) self.assertCountEqual([v00], nodes_at_level(g, v00, 0)) self.assertCountEqual([v01], nodes_at_level(g, v00, 1)) self.assertCountEqual([v02, v11], nodes_at_level(g, v00, 2)) self.assertCountEqual([v21], nodes_at_level(g, v00, 3)) self.assertCountEqual([v20, v22], nodes_at_level(g, v00, 4)) self.assertCountEqual([v10, v12], nodes_at_level(g, v00, 5)) self.assertCountEqual([v21], nodes_at_level(g, v21, 0)) self.assertCountEqual([v20, v22, v11], nodes_at_level(g, v21, 1)) self.assertCountEqual([v10, v12, v01], nodes_at_level(g, v21, 2)) self.assertCountEqual([v00, v02], nodes_at_level(g, v21, 3))