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')
def test_add_edge_edges():
    '''
    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')
    assert len(list(g.edges())) == 1
    assert ('a', 'b', 1) in g.edges()

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

    # Adding an edge with a non-existing node works just fine
    assert g.add_edge('b', 'c', 2)
    assert len(list(g.edges())) == 2
    assert ('a', 'b', 1) in g.edges()
    assert ('b', 'c', 2) in g.edges()
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
예제 #5
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