def test_add_edge_contains():
    '''
    Adding an edge between non-existant edges should cause contains to report they
    exist.
    '''
    g = DirectedGraph()
    assert g.add_edge('a', 'b', 42)
    assert g.contains('a')
    assert g.contains('b')
def test_add_edge_nodes():
    '''
    Adding an edge between non-existant edges should cause those nodes to be
    returned by .nodes().
    '''
    g = DirectedGraph()
    assert g.add_edge('a', 'b', 42)
    assert len(list(g.nodes())) == 2
    assert 'a' in g.nodes()
    assert 'b' in g.nodes()
def test_add_edge_neighbors():
    '''
    add_edge should connect existing nodes in the graph, causing neighbors to
    return the connected edges.

    add_edge should not overwrite existing edges and return False if the user tries.

    add_edge should create nodes if they do not already exist.
    '''
    g = DirectedGraph()

    # Edges can connect existing nodes
    assert g.add_node('a')
    assert g.add_node('b')
    assert g.add_edge('a', 'b')  # Default weight is 1
    assert ('b', 1) in g.neighbors('a')

    # Adding an edge that already exists should be rejected, and return False
    assert g.add_edge('a', 'b', 2) == False
    assert ('b', 1) in g.neighbors('a')

    # Adding an edge with a non-existing node works just fine
    assert g.add_edge('b', 'c', 2)
    assert ('b', 1) in g.neighbors('a')
    assert ('c', 2) in g.neighbors('b')
예제 #4
0
def test_single_path_directed():
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('b', 'd')
    g.add_edge('d', 'e')
    g.add_edge('e', 'f')

    assert depth_first_search_by_criteria(g, 'a', criterion) == ['a', 'b', 'd', 'e']
예제 #5
0
def test_single_path_directed():
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('b', 'd')
    g.add_edge('d', 'e')
    g.add_edge('e', 'f')

    assert breadth_first_search(g, 'a', 'e') == ['a', 'b', 'd', 'e']
예제 #6
0
def test_multiple_paths_directed():
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('b', 'e')
    g.add_edge('b', 'd')
    g.add_edge('d', 'e')
    g.add_edge('e', 'f')

    found_path = depth_first_search_by_criteria(g, 'a', criterion)
    assert found_path == ['a', 'b', 'e'] or found_path == ['a', 'b', 'd', 'e']
예제 #7
0
def test_no_path_directed_cycles():
    '''
    Test BFS on a simple directed graph that contains cycles, but no path to the goal.
    '''
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('c', 'a')
    g.add_edge('b', 'd')
    g.add_edge('e', 'f')

    assert breadth_first_search_by_criteria(g, 'a', criterion) is None
예제 #8
0
def test_start_is_stop():
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('b', 'd')
    g.add_edge('e', 'f')

    assert breadth_first_search_by_criteria(g, 'e', criterion) == ['e']
예제 #9
0
def test_start_is_stop():
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('b', 'd')
    g.add_edge('e', 'f')

    assert breadth_first_search(g, 'a', 'a') == ['a']
def test_add_contains():
    '''
    Adding a node should cause contains to return true, adding a duplicate
    should return False, and contains should still report True for that node
    '''
    g = DirectedGraph()
    assert g.add_node('a')
    assert g.contains('a')
    assert g.add_node('a') == False
    assert g.contains('a')
예제 #11
0
def test_no_path_directed():
    '''
    Test BFS on a simple directed graph that contains a no cycles, and no path to the goal.
    '''
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('b', 'd')
    g.add_edge('e', 'f')

    assert breadth_first_search(g, 'a', 'e') is None
예제 #12
0
def test_single_path_with_cycles_directed():
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('c', 'h')
    g.add_edge('h', 'i')
    g.add_edge('i', 'j')
    g.add_edge('j', 'e')
    g.add_edge('b', 'd')
    g.add_edge('d', 'k')
    g.add_edge('k', 'f')
    g.add_edge('f', 'b')

    assert breadth_first_search_by_criteria(
        g, 'a', criterion) == ['a', 'b', 'c', 'h', 'i', 'j', 'e']
def test_add_nodes():
    '''
    Adding a node should cause that node to appear in the return value of .nodes().
    '''
    g = DirectedGraph()
    assert g.add_node('a')
    assert 'a' in g.nodes()
    assert len(list(g.nodes())) == 1

    assert g.add_node('a') == False
    assert 'a' in g.nodes()
    assert len(list(g.nodes())) == 1

    assert g.add_node('b')
    assert 'a' in g.nodes()
    assert 'b' in g.nodes()
    assert len(list(g.nodes())) == 2
def test_simple_integration():
    '''
    test a simple use case, retesting assumptions at each step.
    '''
    g = DirectedGraph()

    assert g.add_node('a')
    assert g.contains('a')
    assert len(list(g.nodes())) == 1
    assert 'a' in g.nodes()
    assert len(list(g.neighbors('a'))) == 0

    assert g.add_node('b')
    assert g.contains('b')
    assert len(list(g.nodes())) == 2
    assert 'a' in g.nodes()
    assert 'b' in g.nodes()
    assert len(list(g.neighbors('a'))) == 0
    assert len(list(g.neighbors('b'))) == 0

    assert g.add_node('c')
    assert g.contains('c')
    assert len(list(g.nodes())) == 3
    assert 'a' in g.nodes()
    assert 'b' in g.nodes()
    assert 'c' in g.nodes()
    assert len(list(g.neighbors('a'))) == 0
    assert len(list(g.neighbors('b'))) == 0
    assert len(list(g.neighbors('c'))) == 0

    g.add_edge('a', 'b')
    edges = list(g.edges())
    assert len(edges) == 1
    assert ('a', 'b', 1) in edges
    assert g.contains('a')
    assert g.contains('b')
    assert g.contains('c')
    assert len(list(g.nodes())) == 3
    assert 'a' in g.nodes()
    assert 'b' in g.nodes()
    assert 'c' in g.nodes()
    assert len(list(g.neighbors('a'))) == 1
    assert ('b', 1) in g.neighbors('a')
    assert len(list(g.neighbors('b'))) == 0
    assert len(list(g.neighbors('c'))) == 0

    g.add_edge('a', 'c')
    edges = list(g.edges())
    assert len(edges) == 2
    assert ('a', 'b', 1) in edges
    assert ('a', 'c', 1) in edges
    assert g.contains('a')
    assert g.contains('b')
    assert g.contains('c')
    assert len(list(g.nodes())) == 3
    assert 'a' in g.nodes()
    assert 'b' in g.nodes()
    assert 'c' in g.nodes()
    assert len(list(g.neighbors('a'))) == 2
    assert ('b', 1) in g.neighbors('a')
    assert ('c', 1) in g.neighbors('a')
    assert len(list(g.neighbors('b'))) == 0
    assert len(list(g.neighbors('c'))) == 0

    edges = list(g.edges())
    assert ('a', 'b', 1) in edges
    assert ('a', 'c', 1) in edges
예제 #15
0
def test_single_path_with_cycles_directed():
    g = DirectedGraph()
    g.add_edge('a', 'b')
    g.add_edge('b', 'c')
    g.add_edge('c', 'h')
    g.add_edge('h', 'i')
    g.add_edge('i', 'j')
    g.add_edge('j', 'k')
    g.add_edge('b', 'd')
    g.add_edge('d', 'e')
    g.add_edge('e', 'f')
    g.add_edge('f', 'b')

    assert depth_first_search(g, 'a',
                              'k') == ['a', 'b', 'c', 'h', 'i', 'j', 'k']
예제 #16
0
def construct_three_jugs_graph():
    '''
    This function constructs and returns a DirectedGraph representing the
    three jugs problem. Each node in the graph is represented by a 3-tuple
    where the three values represent the amount of water currently in each jug,
    (twelve_liter, eight_liter, five_liter).

    The starting node is (12, 0, 0) -- 12 in the 12 liter, 0 in the 8 liter, and
    0 in the 5 liter. Creating the graph is essentially a depth first search
    algorithm over the space, but instead of stopping when we find a partiucular
    node, we explore until the frontier is empty.
    '''
    g = DirectedGraph()

    frontier = []
    explored = set()

    # The start node
    frontier.append((12, 0, 0))

    while len(frontier) > 0:
        current_state = frontier.pop()

        if current_state in explored:
            continue

        g.add_node(current_state)

        # Discover nodes and edges for the neighbors
        twelve_amount, eight_amount, five_amount = current_state
        if twelve_amount > 0:
            # Pouring 12 into 8
            if eight_amount < 8:
                amount_poured = min((8 - eight_amount), twelve_amount)
                next_state = (twelve_amount - amount_poured,
                              eight_amount + amount_poured, five_amount)
                g.add_node(next_state)
                g.add_edge(current_state, next_state)
                frontier.append(next_state)

            # Pouring 12 into 5
            if five_amount < 5:
                amount_poured = min((5 - five_amount), twelve_amount)
                next_state = (twelve_amount - amount_poured, eight_amount,
                              five_amount + amount_poured)
                g.add_node(next_state)
                g.add_edge(current_state, next_state)
                frontier.append(next_state)

        if eight_amount > 0:
            # Pouring 8 into 12
            if twelve_amount < 12:
                amount_poured = min((12 - twelve_amount), eight_amount)
                next_state = (twelve_amount + amount_poured,
                              eight_amount - amount_poured, five_amount)
                g.add_node(next_state)
                g.add_edge(current_state, next_state)
                frontier.append(next_state)

            # Pouring 8 into 5
            if five_amount < 5:
                amount_poured = min((5 - five_amount), eight_amount)
                next_state = (twelve_amount, eight_amount - amount_poured,
                              five_amount + amount_poured)
                g.add_node(next_state)
                g.add_edge(current_state, next_state)
                frontier.append(next_state)

        if five_amount > 0:
            # Pouring 5 into 12
            if twelve_amount < 12:
                amount_poured = min((12 - twelve_amount), five_amount)
                next_state = (twelve_amount + amount_poured, eight_amount,
                              five_amount - amount_poured)
                g.add_node(next_state)
                g.add_edge(current_state, next_state)
                frontier.append(next_state)

            # Pouring 5 into 8
            if eight_amount < 8:
                amount_poured = min((8 - eight_amount), five_amount)
                next_state = (twelve_amount, eight_amount + amount_poured,
                              five_amount - amount_poured)
                g.add_node(next_state)
                g.add_edge(current_state, next_state)
                frontier.append(next_state)

        explored.add(current_state)

    return g