17 Commits
3 changed files with 201 additions and 97 deletions
+190 -84
View File
@@ -1,4 +1,4 @@
from typing import Any, Generator from typing import Any
from src.AMazeIng import AMazeIng from src.AMazeIng import AMazeIng
from src.parsing import Parsing from src.parsing import Parsing
from mlx import Mlx from mlx import Mlx
@@ -12,6 +12,8 @@ class MazeMLX:
self.mlx = Mlx() self.mlx = Mlx()
self.height = height self.height = height
self.width = width self.width = width
self.print_path = False
self.color = [0x00, 0x00, 0xFF, 0xFF]
self.mlx_ptr = self.mlx.mlx_init() self.mlx_ptr = self.mlx.mlx_init()
self.win_ptr = self.mlx.mlx_new_window( self.win_ptr = self.mlx.mlx_new_window(
self.mlx_ptr, width, height + 200, "A-Maze-Ing" self.mlx_ptr, width, height + 200, "A-Maze-Ing"
@@ -26,6 +28,12 @@ class MazeMLX:
def close(self) -> None: def close(self) -> None:
self.mlx.mlx_destroy_image(self.mlx_ptr, self.img_ptr) self.mlx.mlx_destroy_image(self.mlx_ptr, self.img_ptr)
def close_loop(self, _: Any):
self.mlx.mlx_loop_exit(self.mlx_ptr)
def clear_image(self) -> None:
self.buf[:] = b"\x00" * len(self.buf)
def redraw_image(self) -> None: def redraw_image(self) -> None:
self.mlx.mlx_clear_window(self.mlx_ptr, self.win_ptr) self.mlx.mlx_clear_window(self.mlx_ptr, self.win_ptr)
self.mlx.mlx_put_image_to_window( self.mlx.mlx_put_image_to_window(
@@ -40,47 +48,99 @@ class MazeMLX:
"1: regen; 2: path; 3: color; 4: quit;", "1: regen; 2: path; 3: color; 4: quit;",
) )
def put_pixel(self, x, y) -> None: def put_pixel(self, x, y, color: list | None = None) -> None:
if x < 0 or y < 0 or x >= self.width or y >= self.height:
return
offset = y * self.size_line + x * (self.bpp // 8) offset = y * self.size_line + x * (self.bpp // 8)
self.buf[offset + 0] = 0xFF if color:
self.buf[offset + 1] = 0xFF self.buf[offset + 0] = color[0]
self.buf[offset + 2] = 0xFF self.buf[offset + 1] = color[1]
if self.bpp >= 32: self.buf[offset + 2] = color[2]
self.buf[offset + 3] = 0xFF if self.bpp >= 32:
self.buf[offset + 3] = color[3]
else:
self.buf[offset + 0] = self.color[0]
self.buf[offset + 1] = self.color[1]
self.buf[offset + 2] = self.color[2]
if self.bpp >= 32:
self.buf[offset + 3] = self.color[3]
def clear_image(self) -> None: def put_line(
self.buf[:] = b"\x00" * len(self.buf) self,
start: tuple[int, int],
def put_line(self, start: tuple[int, int], end: tuple[int, int]) -> None: end: tuple[int, int],
color: list | None = None,
) -> None:
sx, sy = start sx, sy = start
ex, ey = end ex, ey = end
if sy == ey: if sy == ey:
for x in range(min(sx, ex), max(sx, ex) + 1): for x in range(min(sx, ex), max(sx, ex) + 1):
self.put_pixel(x, sy) self.put_pixel(x, sy, color)
if sx == ex: if sx == ex:
for y in range(min(sy, ey), max(sy, ey) + 1): for y in range(min(sy, ey), max(sy, ey) + 1):
self.put_pixel(sx, y) self.put_pixel(sx, y, color)
def put_block(
self,
ul: tuple[int, int],
dr: tuple[int, int],
color: list | None = None,
) -> None:
for y in range(min(ul[1], dr[1]), max(dr[1], ul[1])):
self.put_line(
(min(ul[0], dr[0]), y), (max(ul[0], dr[0]), y), color
)
@staticmethod
def random_color_ft() -> Any:
colors = [
[0xFF, 0xBF, 0x00, 0xFF], # blue
[0xFF, 0x00, 0x80, 0xFF], # purple
[0xFF, 0x00, 0xFF, 0xFF], # rose
]
while True:
for color in colors:
yield color
@staticmethod
def random_color() -> Any:
colors = [
[0x00, 0x00, 0xFF, 0xFF], # red
[0x00, 0xFF, 0xFF, 0xFF], # yellow
[0x00, 0xFF, 0x40, 0xFF], # green
[0xFF, 0xBF, 0x00, 0xFF], # blue
[0xFF, 0x00, 0x80, 0xFF], # purple
[0xFF, 0x00, 0xFF, 0xFF], # pink
]
while True:
for color in colors:
yield color
def get_margin_line_len(self, maze: np.ndarray) -> tuple[int, int, int]:
rows = len(maze)
cols = len(maze[0])
line_len = min(self.width // cols, self.height // rows) - 1
maze_width = cols * line_len
maze_height = rows * line_len
margin_x = ((self.width - maze_width) // 2) + 1
margin_y = ((self.height - maze_height) // 2) + 1
return (line_len, margin_x, margin_y)
def update_maze(self, maze: np.ndarray) -> None: def update_maze(self, maze: np.ndarray) -> None:
self.clear_image() self.clear_image()
margin = math.trunc(
math.sqrt(self.width if self.width > self.height else self.height) line_len, margin_x, margin_y = self.get_margin_line_len(maze)
// 2
)
line_len = math.trunc(
(
(self.height - margin) // len(maze)
if self.height > self.width
else (self.width - margin) // len(maze[0])
)
)
for y in range(len(maze)): for y in range(len(maze)):
for x in range(len(maze[0])): for x in range(len(maze[0])):
x0 = x * line_len + margin x0 = x * line_len + margin_x
y0 = y * line_len + margin y0 = y * line_len + margin_y
x1 = x * line_len + line_len + margin x1 = x * line_len + line_len + margin_x
y1 = y * line_len + line_len + margin y1 = y * line_len + line_len + margin_y
if maze[y][x].get_north(): if maze[y][x].get_north():
self.put_line((x0, y0), (x1, y0)) self.put_line((x0, y0), (x1, y0))
@@ -90,13 +150,8 @@ class MazeMLX:
self.put_line((x0, y1), (x1, y1)) self.put_line((x0, y1), (x1, y1))
if maze[y][x].get_west(): if maze[y][x].get_west():
self.put_line((x0, y0), (x0, y1)) self.put_line((x0, y0), (x0, y1))
self.redraw_image()
def put_block(self, ul: tuple[int, int], dr: tuple[int, int]) -> None: def put_path(self, amazing: AMazeIng) -> Any:
for y in range(min(ul[1], dr[1]), max(dr[1], ul[1])):
self.put_line((min(ul[0], dr[0]), y), (max(ul[0], dr[0]), y))
def put_path(self, amazing: AMazeIng):
path = amazing.solve_path() path = amazing.solve_path()
print(path) print(path)
actual = amazing.entry actual = amazing.entry
@@ -104,33 +159,23 @@ class MazeMLX:
maze = amazing.maze.get_maze() maze = amazing.maze.get_maze()
if maze is None: if maze is None:
return return
margin = math.trunc(
math.sqrt(self.width if self.width > self.height else self.height) line_len, margin_x, margin_y = self.get_margin_line_len(maze)
// 2
)
cell_size = math.trunc(
(
(self.height - margin) // len(maze)
if self.height > self.width
else (self.width - margin) // len(maze[0])
)
)
self.update_maze(maze)
for i in range(len(path)): for i in range(len(path)):
ul = ( ul = (
(actual[0]) * cell_size + margin + 12, (actual[0]) * line_len + margin_x + 12,
(actual[1]) * cell_size + 12 + margin, (actual[1]) * line_len + 12 + margin_y,
) )
dr = ( dr = (
(actual[0]) * cell_size + cell_size + margin - 12, (actual[0]) * line_len + line_len + margin_x - 12,
(actual[1]) * cell_size + cell_size - 12 + margin, (actual[1]) * line_len + line_len - 12 + margin_y,
) )
self.put_block(ul, dr) self.put_block(ul, dr)
self.redraw_image() x0 = actual[0] * line_len + margin_x + 12
x0 = actual[0] * cell_size + margin + 12 y0 = actual[1] * line_len + margin_y + 12
y0 = actual[1] * cell_size + margin + 12 x1 = actual[0] * line_len + line_len + margin_x - 12
x1 = actual[0] * cell_size + cell_size + margin - 12 y1 = actual[1] * line_len + line_len + margin_y - 12
y1 = actual[1] * cell_size + cell_size + margin - 12
yield yield
match path[i]: match path[i]:
case "N": case "N":
@@ -146,38 +191,72 @@ class MazeMLX:
self.put_block((x0, y0), (x0 - 24, y1)) self.put_block((x0, y0), (x0 - 24, y1))
actual = (actual[0] - 1, actual[1]) actual = (actual[0] - 1, actual[1])
ul = ( ul = (
(actual[0]) * cell_size + margin + 12, (actual[0]) * line_len + margin_x + 12,
(actual[1]) * cell_size + 12 + margin, (actual[1]) * line_len + 12 + margin_y,
) )
dr = ( dr = (
(actual[0]) * cell_size + cell_size + margin - 12, (actual[0]) * line_len + line_len + margin_x - 12,
(actual[1]) * cell_size + cell_size - 12 + margin, (actual[1]) * line_len + line_len - 12 + margin_y,
) )
self.put_block(ul, dr) self.put_block(ul, dr)
self.redraw_image()
return return
def close_loop(self, _: Any): def put_start_end(self, amazing: AMazeIng):
self.mlx.mlx_loop_exit(self.mlx_ptr) entry = amazing.entry
exit = amazing.exit
maze = amazing.maze.get_maze()
if maze is None:
return
def handle_key_press(self, keycode: int, amazing: AMazeIng) -> None: line_len, margin_x, margin_y = self.get_margin_line_len(maze)
if keycode == 49:
self.restart_maze(amazing)
if keycode == 50:
self.restart_path(amazing)
if keycode == 51:
pass
if keycode == 52:
self.close_loop(None)
def start(self, amazing: AMazeIng) -> None: ul = (
self.restart_maze(amazing) (entry[0] - 1) * line_len + margin_x + 3,
self.mlx.mlx_loop_hook(self.mlx_ptr, self.render_maze, amazing) (entry[1] - 1) * line_len + 3 + margin_y,
self.mlx.mlx_hook(self.win_ptr, 33, 0, self.close_loop, None)
self.mlx.mlx_hook(
self.win_ptr, 2, 1 << 0, self.handle_key_press, amazing
) )
self.mlx.mlx_loop(self.mlx_ptr) dr = (
(entry[0] - 1) * line_len + line_len + margin_x - 3,
(entry[1] - 1) * line_len + line_len - 3 + margin_y,
)
self.put_block(ul, dr, [0xFF, 0xBF, 0x00, 0x9F])
ul = (
(exit[0] - 1) * line_len + margin_x + 3,
(exit[1] - 1) * line_len + 3 + margin_y,
)
dr = (
(exit[0] - 1) * line_len + line_len + margin_x - 3,
(exit[1] - 1) * line_len + line_len - 3 + margin_y,
)
self.put_block(ul, dr, [0x00, 0xFF, 0x40, 0x9F])
def draw_ft(self, maze: np.ndarray, color: list | None = None):
line_len, margin_x, margin_y = self.get_margin_line_len(maze)
for y in range(len(maze)):
for x in range(len(maze[0])):
if maze[y][x].value == 15:
x0 = x * line_len + margin_x
y0 = y * line_len + margin_y
x1 = x * line_len + line_len + margin_x
y1 = y * line_len + line_len + margin_y
self.put_block((x0, y0), (x1, y1))
def draw_image(self, amazing: AMazeIng) -> None:
if self.render_maze(amazing):
if self.path_printer and self.print_path:
if self.render_path():
color = next(self.color_gen_ft)
color
else:
self.draw_ft(amazing.maze.get_maze())
self.redraw_image()
def shift_color(self):
self.color_gen = self.random_color()
def shift_color_ft(self):
self.color_gen_ft = self.random_color_ft()
def restart_maze(self, amazing: AMazeIng) -> None: def restart_maze(self, amazing: AMazeIng) -> None:
self.generator = amazing.generate() self.generator = amazing.generate()
@@ -185,27 +264,54 @@ class MazeMLX:
def restart_path(self, amazing: AMazeIng) -> None: def restart_path(self, amazing: AMazeIng) -> None:
self.path_printer = self.put_path(amazing) self.path_printer = self.put_path(amazing)
def render_path(self): def render_path(self) -> bool:
try: try:
next(self.path_printer) next(self.path_printer)
time.sleep(0.03) time.sleep(0.03)
return False
except StopIteration: except StopIteration:
pass pass
return True
def render_maze(self, amazing: AMazeIng): def render_maze(self, amazing: AMazeIng) -> bool:
try: try:
next(self.generator) next(self.generator)
self.update_maze(amazing.maze.get_maze()) self.update_maze(amazing.maze.get_maze())
# time.sleep(0.01) self.put_start_end(amazing)
return False
except StopIteration: except StopIteration:
if self.path_printer is not None: pass
self.render_path() return True
def handle_key_press(self, keycode: int, amazing: AMazeIng) -> None:
if keycode == 49:
self.restart_maze(amazing)
self.print_path = False
if keycode == 50:
self.restart_path(amazing)
self.print_path = True if self.print_path is False else False
if keycode == 51:
self.print_path = False
self.color = next(self.color_gen)
if keycode == 52:
self.close_loop(None)
def start(self, amazing: AMazeIng) -> None:
self.restart_maze(amazing)
self.shift_color()
self.shift_color_ft()
self.mlx.mlx_loop_hook(self.mlx_ptr, self.draw_image, amazing)
self.mlx.mlx_hook(self.win_ptr, 33, 0, self.close_loop, None)
self.mlx.mlx_hook(
self.win_ptr, 2, 1 << 0, self.handle_key_press, amazing
)
self.mlx.mlx_loop(self.mlx_ptr)
def main() -> None: def main() -> None:
mlx = None mlx = None
try: try:
mlx = MazeMLX(1000, 1000) mlx = MazeMLX(1800, 1800)
config = Parsing.DataMaze.get_data_maze("config.txt") config = Parsing.DataMaze.get_data_maze("config.txt")
amazing = AMazeIng(**config) amazing = AMazeIng(**config)
mlx.start(amazing) mlx.start(amazing)
+3 -3
View File
@@ -1,7 +1,7 @@
WIDTH=15 WIDTH=30
HEIGHT=15 HEIGHT=30
ENTRY=1,1 ENTRY=1,1
EXIT=15,15 EXIT=5,5
OUTPUT_FILE=maze.txt OUTPUT_FILE=maze.txt
PERFECT=False PERFECT=False
GENERATOR=Kruskal GENERATOR=Kruskal
+8 -10
View File
@@ -11,7 +11,7 @@ class MazeSolver(ABC):
@abstractmethod @abstractmethod
def solve( def solve(
self, maze: Maze, height: int = None, width: int = None self, maze: Maze, height: int | None = None, width: int | None = None
) -> str: ... ) -> str: ...
@@ -38,9 +38,6 @@ class AStar(MazeSolver):
super().__init__(start, end) super().__init__(start, end)
self.path = [] self.path = []
def g(self, n: tuple[int, int]) -> int:
return len(self.path) + 1
def h(self, n: tuple[int, int]) -> int: def h(self, n: tuple[int, int]) -> int:
return ( return (
max(n[0], self.end[0]) max(n[0], self.end[0])
@@ -49,9 +46,6 @@ class AStar(MazeSolver):
- min(n[1], self.end[1]) - min(n[1], self.end[1])
) )
def f(self, n: tuple[int, int]) -> int:
return self.g(n) + self.h(n)
def get_paths( def get_paths(
self, self,
maze: np.ndarray, maze: np.ndarray,
@@ -103,7 +97,7 @@ class AStar(MazeSolver):
self.start, self.start,
0, 0,
self.h(self.start), self.h(self.start),
self.f(self.start), self.h(self.start),
None, None,
) )
) )
@@ -161,7 +155,9 @@ class AStar(MazeSolver):
break break
return res return res
def solve(self, maze: Maze, height: int = None, width: int = None) -> str: def solve(
self, maze: Maze, height: int | None = None, width: int | None = None
) -> str:
path = self.get_path(maze.get_maze()) path = self.get_path(maze.get_maze())
return self.translate(path) return self.translate(path)
@@ -170,7 +166,9 @@ class DepthFirstSearchSolver(MazeSolver):
def __init__(self, start, end): def __init__(self, start, end):
super().__init__(start, end) super().__init__(start, end)
def solve(self, maze: Maze, height: int = None, width: int = None) -> str: def solve(
self, maze: Maze, height: int | None = None, width: int | None = None
) -> str:
path_str = "" path_str = ""
visited = np.zeros((height, width), dtype=bool) visited = np.zeros((height, width), dtype=bool)
path = list() path = list()