Congratulations on the initiative!
So let's go there:
The most "easy to start and difficult to continue" way of doing this kind of thing is to use sequences within sequences.
In any case, you are defining your labyrinth as a multiline string - so it is better to turn it into a data structure as a list-within-a-list so you can address each point automatically. For this, we can do the following steps:
break the string with the maze all where there is a line break (character \ n). Use each substring thus obtained "as is", since a string is a string. (The% more% you put in the maze soon avoids an empty line at the beginning of the maze and is important):
maze = maze.split("\n")
Ready. Now \n
will access the sixth character in the second line. The "split" method breaks a string in a list of strings in the indicated tab (and omits the tab), and is enough for that. Both lists and strings are sequences - then maze[1][5]
will retrieve you a line from the maze.
Now, labyrinths of this sort are pretty cool things to begin to understand Object Orientation in Python. All you have to do is implement the maze[n]
method in a class, and Python can retrieve elements there with the __getitem__
syntax as if it were a native string - and if our [ ]
supports indexes separated by __getitem__
which are passed as tuples, we can access elements in the labyrinth as ,
with just a few lines of code.
To allow the maze to be alterable, this is: you can insert new elements into it after it's created, I'll use lists within lists instead of strings within lists:
class Maze:
def __init__(self, text_maze):
self.maze = [[char for char in line] for line in text_maze.split() if line.strip()]
def __getitem__(self, index):
# getitem simples, vai retornar um erro do próprio Python
# se for acessado um elemento com índice errado
x, y = index
return self.maze[y][x]
def __setitem__(self, index, value):
x, y = index
self.maze[y][x] = value
def __len__(self):
return len(self.maze)
def __repr__(self):
return "\n".join("".join(char for char in line) for line in self.maze)
Now, you're still going to have to plot elements inside the labyrinth (like a character walking, or even the algorithm showing the paths already traveled) in text mode, to use a
true graphic mode and see the maze as colorful little blocks on the screen.
The terminal is full of pranks - while graphical functions are made for it.
Look, by taking advantage of what I had done to OOP, I inherited the labyrinth class, and put the code for the labrinto if I draw it using tkinter.
See what you can do with 40 more lines (well spaced) and using tkinter - (the reference for the tkinter canvas is here: link )
maze_data = """\
###########################
# # ## ##
# ##### ### # # ######## ##
# # ### # # #
# ### # ##### # # ### ### #
# # # # # #
### #### #### ######### # #
### # ## ## # # #
# ### ## ## #### ## # #
# ### ## #### ## # ## ### #
# # ## ## # ## # #
### ### ######## # ## ## ##
# ## X#
###########################
"""
import tkinter
class Maze:
def __init__(self, text_maze):
self.maze = [[char for char in line] for line in text_maze.split("\n") if line.strip()]
print(self)
def __getitem__(self, index):
# getitem simples, vai retornar um erro do próprio Python
# se for acessado um elemento com índice errado
x, y = index
return self.maze[y][x]
def __setitem__(self, index, value):
x, y = index
self.maze[y][x] = value
def __len__(self):
return len(self.maze)
def __repr__(self):
return "\n".join("".join(char for char in line) for line in self.maze)
width = property(lambda self: len(self.maze[0]))
height = property(lambda self: len(self.maze))
BLOCK_SIZE = 24
COLORS = {"#": "#000", " ": "#fff", "X":"#f00"}
class TkinterMaze(Maze):
def __init__(self, root, text_maze):
super().__init__(text_maze)
self.root = root
self.canvas = tkinter.Canvas(root, width=self.width * BLOCK_SIZE, height=self.height * BLOCK_SIZE)
self.canvas.pack()
self.rectangles = {}
self.update()
def clear(self):
for coordinates, rect_id in self.rectangles.items():
self.canvas.delete(rect_id)
def update(self):
self.clear()
for (x, y), char in self:
color = COLORS[char]
rect_id = self.canvas.create_rectangle(
x * BLOCK_SIZE,
y * BLOCK_SIZE,
(x + 1) * BLOCK_SIZE,
(y + 1) * BLOCK_SIZE,
fill = color,
width = 0
)
self.rectangles[x, y] = rect_id
def __iter__(self):
for y, line in enumerate(self.maze):
for x, char in enumerate(line):
yield ((x,y), char)
def main():
window = tkinter.Tk()
maze = TkinterMaze(window, maze_data)
window.bind("destroy", window.destroy)
tkinter.mainloop()
main()
Looking a little closer to the tkinter documentation, you can use the bind event to receive broken keys and move the character through the maze. (A better implementation will be with the personga in a separate class with a reference to the labyrinth - there using "getitem" he can know where he can go where he can not go.He can share the ".canvas" of the labyrinth to draw in it screen, or the labrinto can incorporate another "layer" to draw objects that are not wall)