Skip to content
Advertisement

Creating a graph from a string

Let’s consider a “maze” defined by a string, for example

'''
################
#******#########
#*####*********#
##############*#
#*****#*#***##F#
#*###*###*#****#
#S###*****######
################
''' 

The sign “#” denotes a wall and the sign “*” denotes the possible path. Moreover “S” is the starting point and “F” is the destination.

I would like to apply a path searching algorithm to solve this labyrinth for instance Breadth-First Search. I’ve read that that algorithm uses a graph to find the proper path. Here comes my question.

I would like to create a graph out of my path. I think it should look something like this:

graph = {
    0: [],
    ...
    17: [18],
    18: [17, 19],
    ...
}

as the first node is a wall so it is not linked to any path and the seventeenth node is a path and it is linked to his neighbour and so on.

My first thought was to implement several function, different ones for the center of the maze and the sides.

For example I would like to check, for all nodes i inside the graph (by inside I mean nodes which are not on the sides) I would check whether they are linked to i-n_1, i-n_2, i-n_3, i-n_4, because we can only go downstairs, upstairs, left and right. n_j would depend on the size of the maze.

However I find that idea to be completely ineffective and ugly. I would appreciate any other ideas or tips.

Advertisement

Answer

It isn’t too hard to read a string like that into a graph.

The first function gets all the coordinates of non-wall cells, and the second function figures out which ones are neighbors of which.

import collections


def read_maze_string(maze_string):
    start_coord = None
    end_coord = None
    non_walls = set()

    for y, row in enumerate(maze_string.strip().splitlines()):
        for x, c in enumerate(row):
            if c == "#":
                continue
            non_walls.add((x, y))
            if c == "S":
                start_coord = (x, y)
            if c == "F":
                end_coord = (x, y)
    return (non_walls, start_coord, end_coord)


def get_graph(non_walls):
    neighbors = collections.defaultdict(set)
    neighbor_deltas = [(-1, 0), (+1, 0), (0, -1), (0, +1)]  # You could also add the diagonal deltas
    for c in non_walls:
        x, y = c
        for dx, dy in neighbor_deltas:
            nc = (x + dx, y + dy)  # neighbor coordinate
            if nc in non_walls:
                neighbors[c].add(nc)
    return dict(neighbors)


non_walls, start_coord, end_coord = read_maze_string(
    """
################
#******#########
#*####*********#
##############*#
#*****#*#***##F#
#*###*###*#****#
#S###*****######
################
"""
)
graph = get_graph(non_walls)
print(graph)

The output is e.g.

{(3, 4): {(4, 4), (2, 4)}, (14, 4): {(14, 3), (14, 5)}, (3, 1): {(4, 1), (2, 1)}, (5, 4): {(4, 4), (5, 5)}, (5, 1): {(6, 1), (4, 1)}, (9, 2): {(8, 2), (10, 2)}, (9, 5): {(9, 6), (9, 4)}, (11, 2): {(12, 2), (10, 2)}, (11, 5): {(12, 5), (11, 4)}, (8, 6): {(9, 6), (7, 6)}, (13, 2): {(14, 2), (12, 2)}, (1, 6): {(1, 5)}, (13, 5): {(12, 5), (14, 5)}, (6, 2): {(6, 1), (7, 2)}, (14, 3): {(14, 2), (14, 4)}, (5, 6): {(6, 6), (5, 5)}, (8, 2): {(9, 2), (7, 2)}, (11, 4): {(10, 4), (11, 5)}, (10, 2): {(11, 2), (9, 2)}, (9, 4): {(10, 4), (9, 5)}, (2, 4): {(3, 4), (1, 4)}, (1, 2): {(1, 1)}, (2, 1): {(3, 1), (1, 1)}, (1, 5): {(1, 6), (1, 4)}, (6, 1): {(6, 2), (5, 1)}, (7, 6): {(6, 6), (8, 6)}, (12, 2): {(13, 2), (11, 2)}, (12, 5): {(13, 5), (11, 5)}, (14, 2): {(13, 2), (14, 3)}, (4, 1): {(3, 1), (5, 1)}, (14, 5): {(13, 5), (14, 4)}, (4, 4): {(5, 4), (3, 4)}, (5, 5): {(5, 4), (5, 6)}, (10, 4): {(11, 4), (9, 4)}, (1, 1): {(1, 2), (2, 1)}, (9, 6): {(9, 5), (8, 6)}, (1, 4): {(2, 4), (1, 5)}, (7, 2): {(8, 2), (6, 2)}, (6, 6): {(7, 6), (5, 6)}}
User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement