4 Commits

Author SHA1 Message Date
da7e 2fc67683d8 add key handling without color management (not implemented) 2026-03-27 20:58:28 +01:00
da7e cb19cf1413 ADD(mlx path animation) 2026-03-27 19:47:21 +01:00
da7e 6ec617848f Merge branch 'main' of github.com:maoakeEnterprise/amazing into mlx 2026-03-27 18:29:39 +01:00
da7e 349e58ce41 ifpjefp 2026-03-27 18:29:09 +01:00
5 changed files with 182 additions and 85 deletions
+120 -13
View File
@@ -1,9 +1,10 @@
from typing import Any from typing import Any, Generator
from src.AMazeIng import AMazeIng from src.AMazeIng import AMazeIng
from src.parsing import Parsing from src.parsing import Parsing
from mlx.mlx import Mlx from mlx import Mlx
import numpy as np import numpy as np
import math import math
import time
class MazeMLX: class MazeMLX:
@@ -12,18 +13,33 @@ class MazeMLX:
self.height = height self.height = height
self.width = width self.width = width
self.mlx_ptr = self.mlx.mlx_init() self.mlx_ptr = self.mlx.mlx_init()
self.generator = None
self.win_ptr = self.mlx.mlx_new_window( self.win_ptr = self.mlx.mlx_new_window(
self.mlx_ptr, width, height, "amazing" self.mlx_ptr, width, height + 200, "A-Maze-Ing"
) )
self.img_ptr = self.mlx.mlx_new_image(self.mlx_ptr, width, height) self.img_ptr = self.mlx.mlx_new_image(self.mlx_ptr, width, height)
self.buf, self.bpp, self.size_line, self.format = ( self.buf, self.bpp, self.size_line, self.format = (
self.mlx.mlx_get_data_addr(self.img_ptr) self.mlx.mlx_get_data_addr(self.img_ptr)
) )
self.path_printer = None
self.generator = None
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 redraw_image(self) -> None:
self.mlx.mlx_clear_window(self.mlx_ptr, self.win_ptr)
self.mlx.mlx_put_image_to_window(
self.mlx_ptr, self.win_ptr, self.img_ptr, 0, 0
)
self.mlx.mlx_string_put(
self.mlx_ptr,
self.win_ptr,
self.width // 3,
self.height + 100,
0xFFFFFF,
"1: regen; 2: path; 3: color; 4: quit;",
)
def put_pixel(self, x, y) -> None: def put_pixel(self, x, y) -> None:
offset = y * self.size_line + x * (self.bpp // 8) offset = y * self.size_line + x * (self.bpp // 8)
@@ -35,7 +51,6 @@ class MazeMLX:
def clear_image(self) -> None: def clear_image(self) -> None:
self.buf[:] = b"\x00" * len(self.buf) self.buf[:] = b"\x00" * len(self.buf)
self.mlx.mlx_clear_window(self.mlx_ptr, self.win_ptr)
def put_line(self, start: tuple[int, int], end: tuple[int, int]) -> None: def put_line(self, start: tuple[int, int], end: tuple[int, int]) -> None:
sx, sy = start sx, sy = start
@@ -75,28 +90,120 @@ 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.mlx.mlx_put_image_to_window( self.redraw_image()
self.mlx_ptr, self.win_ptr, self.img_ptr, 0, 0)
def put_block(self, ul: tuple[int, int], dr: tuple[int, int]) -> 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))
def put_path(self, amazing: AMazeIng):
path = amazing.solve_path()
print(path)
actual = amazing.entry
actual = (actual[0] - 1, actual[1] - 1)
maze = amazing.maze.get_maze()
if maze is None:
return
margin = math.trunc(
math.sqrt(self.width if self.width > self.height else self.height)
// 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)):
ul = (
(actual[0]) * cell_size + margin + 12,
(actual[1]) * cell_size + 12 + margin,
)
dr = (
(actual[0]) * cell_size + cell_size + margin - 12,
(actual[1]) * cell_size + cell_size - 12 + margin,
)
self.put_block(ul, dr)
self.redraw_image()
x0 = actual[0] * cell_size + margin + 12
y0 = actual[1] * cell_size + margin + 12
x1 = actual[0] * cell_size + cell_size + margin - 12
y1 = actual[1] * cell_size + cell_size + margin - 12
yield
match path[i]:
case "N":
self.put_block((x0, y0), (x1, y0 - 24))
actual = (actual[0], actual[1] - 1)
case "E":
self.put_block((x1, y0), (x1 + 24, y1))
actual = (actual[0] + 1, actual[1])
case "S":
self.put_block((x0, y1), (x1, y1 + 24))
actual = (actual[0], actual[1] + 1)
case "W":
self.put_block((x0, y0), (x0 - 24, y1))
actual = (actual[0] - 1, actual[1])
ul = (
(actual[0]) * cell_size + margin + 12,
(actual[1]) * cell_size + 12 + margin,
)
dr = (
(actual[0]) * cell_size + cell_size + margin - 12,
(actual[1]) * cell_size + cell_size - 12 + margin,
)
self.put_block(ul, dr)
self.redraw_image()
return
def close_loop(self, _: Any): def close_loop(self, _: Any):
self.mlx.mlx_loop_exit(self.mlx_ptr) self.mlx.mlx_loop_exit(self.mlx_ptr)
def gen_maze(self, amazing: AMazeIng) -> None: def handle_key_press(self, keycode: int, amazing: AMazeIng) -> None:
self.generator = amazing.generate() 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: def start(self, amazing: AMazeIng) -> None:
self.gen_maze(amazing) self.restart_maze(amazing)
self.mlx.mlx_loop_hook(self.mlx_ptr, self.render, amazing) self.mlx.mlx_loop_hook(self.mlx_ptr, self.render_maze, amazing)
self.mlx.mlx_hook(self.win_ptr, 33, 0, self.close_loop, None) 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) self.mlx.mlx_loop(self.mlx_ptr)
def render(self, amazing: AMazeIng): def restart_maze(self, amazing: AMazeIng) -> None:
self.generator = amazing.generate()
def restart_path(self, amazing: AMazeIng) -> None:
self.path_printer = self.put_path(amazing)
def render_path(self):
try:
next(self.path_printer)
time.sleep(0.03)
except StopIteration:
pass
def render_maze(self, amazing: AMazeIng):
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) # time.sleep(0.01)
except StopIteration: except StopIteration:
pass if self.path_printer is not None:
try:
next(self.path_printer)
time.sleep(0.03)
except StopIteration:
pass
def main() -> None: def main() -> None:
+5 -5
View File
@@ -1,8 +1,8 @@
WIDTH=15 WIDTH=11
HEIGHT=15 HEIGHT=11
ENTRY=1,1 ENTRY=1,1
EXIT=2,2 EXIT=11,11
OUTPUT_FILE=maze.txt OUTPUT_FILE=maze.txt
PERFECT=False PERFECT=True
GENERATOR=DFS GENERATOR=Kruskal
SOLVER=AStar SOLVER=AStar
+2 -2
View File
@@ -20,9 +20,9 @@ class AMazeIng(BaseModel):
@model_validator(mode="after") @model_validator(mode="after")
def check_entry_exit(self) -> Self: def check_entry_exit(self) -> Self:
if self.entry[0] >= self.width or self.entry[1] >= self.height: if self.entry[0] > self.width or self.entry[1] > self.height:
raise ValueError("Entry coordinates exceed the maze size") raise ValueError("Entry coordinates exceed the maze size")
if self.exit[0] >= self.width or self.exit[1] >= self.height: if self.exit[0] > self.width or self.exit[1] > self.height:
raise ValueError("Exit coordinates exceed the maze size") raise ValueError("Exit coordinates exceed the maze size")
return self return self
+55 -46
View File
@@ -9,30 +9,44 @@ class MazeSolver(ABC):
self.end = (end[1] - 1, end[0] - 1) self.end = (end[1] - 1, end[0] - 1)
@abstractmethod @abstractmethod
def solve( def solve(self, maze: Maze, height: int = None,
self, maze: Maze, height: int = None, width: int = None width: int = None) -> str: ...
) -> str: ...
class AStar(MazeSolver): class AStar(MazeSolver):
def __init__(self, start: tuple[int, int], end: tuple[int, int]) -> None: def __init__(self, start: tuple[int, int], end: tuple[int, int]) -> None:
super().__init__(start, end) super().__init__(start, end)
self.path = []
def f(self, n): def f(self, n):
def g() -> int: def g(n: tuple[int, int]) -> int:
return len(self.path) + 1 res = 0
if n[0] < self.start[0]:
res += self.start[0] - n[0]
else:
res += n[0] - self.start[0]
if n[1] < self.start[1]:
res += self.start[1] - n[1]
else:
res += n[1] - self.start[1]
return res
def h(n: tuple[int, int]) -> int: def h(n: tuple[int, int]) -> int:
return ( res = 0
max(n[0], self.end[0]) if n[0] < self.end[0]:
- min(n[0], self.end[0]) res += self.end[0] - n[0]
+ max(n[1], self.end[1]) else:
- min(n[1], self.end[1]) res += n[0] - self.end[0]
) if n[1] < self.end[1]:
res += self.end[1] - n[1]
else:
res += n[1] - self.end[1]
return res
return g() + h(n) try:
return g(n) + h(n)
except Exception:
return 1000
def best_path( def best_path(
self, self,
@@ -99,46 +113,47 @@ class AStar(MazeSolver):
return actual return actual
def get_path(self, maze: np.ndarray) -> str | None: def get_path(self, maze: np.ndarray) -> str | None:
self.path = [(self.start, self.best_path(maze, self.start, None))] path = [(self.start, self.best_path(maze, self.start, None))]
visited = [self.start] visited = [self.start]
while len(self.path) > 0 and self.path[-1][0] != self.end: while len(path) > 0 and path[-1][0] != self.end:
if len(self.path[-1][1]) == 0: if len(path[-1][1]) == 0:
self.path.pop(-1) path.pop(-1)
if len(self.path) == 0: if len(path) == 0:
break break
k = next(iter(self.path[-1][1])) k = next(iter(path[-1][1]))
self.path[-1][1].pop(k) path[-1][1].pop(k)
continue continue
while len(self.path[-1][1]) > 0: while len(path[-1][1]) > 0:
next_pos = self.get_next_pos( next_pos = self.get_next_pos(
list(self.path[-1][1].keys())[0], self.path[-1][0] list(path[-1][1].keys())[0], path[-1][0]
) )
if next_pos in visited: if next_pos in visited:
k = next(iter(self.path[-1][1])) k = next(iter(path[-1][1]))
self.path[-1][1].pop(k) path[-1][1].pop(k)
else: else:
break break
if len(self.path[-1][1]) == 0: if len(path[-1][1]) == 0:
self.path.pop(-1) path.pop(-1)
continue continue
pre = self.get_opposit(list(self.path[-1][1].keys())[0]) pre = self.get_opposit(list(path[-1][1].keys())[0])
self.path.append( path.append(
( (
next_pos, next_pos,
self.best_path(maze, next_pos, pre), self.best_path(maze, next_pos, pre),
) )
) )
visited += [next_pos] visited += [next_pos]
if len(self.path) == 0: if len(path) == 0:
return None return None
self.path[-1] = (self.end, {}) path[-1] = (self.end, {})
return "".join( return "".join(
str(list(c[1].keys())[0]) for c in self.path if len(c[1]) > 0 str(list(c[1].keys())[0]) for c in path if len(c[1]) > 0
) )
def solve(self, maze: Maze, height: int = None, width: int = None) -> str: def solve(self, maze: Maze, height: int = None,
width: int = None) -> str:
res = self.get_path(maze.get_maze()) res = self.get_path(maze.get_maze())
if res is None: if res is None:
raise Exception("Path not found") raise Exception("Path not found")
@@ -149,7 +164,8 @@ 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,
width: int = None) -> str:
path_str = "" path_str = ""
visited = np.zeros((height, width), dtype=bool) visited = np.zeros((height, width), dtype=bool)
path = list() path = list()
@@ -163,9 +179,8 @@ class DepthFirstSearchSolver(MazeSolver):
rand_p = self.random_path(visited, coord, maze_s, h_w) rand_p = self.random_path(visited, coord, maze_s, h_w)
if not rand_p: if not rand_p:
path, move = self.back_on_step( path, move = self.back_on_step(path, visited, maze_s, h_w,
path, visited, maze_s, h_w, move move)
)
if not path: if not path:
break break
coord = path[-1] coord = path[-1]
@@ -180,9 +195,8 @@ class DepthFirstSearchSolver(MazeSolver):
return path_str return path_str
@staticmethod @staticmethod
def random_path( def random_path(visited: np.ndarray, coord: tuple,
visited: np.ndarray, coord: tuple, maze: np.ndarray, h_w: tuple maze: np.ndarray, h_w: tuple) -> list:
) -> list:
random_p = [] random_p = []
h, w = h_w h, w = h_w
y, x = coord y, x = coord
@@ -205,13 +219,8 @@ class DepthFirstSearchSolver(MazeSolver):
return np.random.choice(rand_path) return np.random.choice(rand_path)
@staticmethod @staticmethod
def back_on_step( def back_on_step(path: list, visited: np.ndarray,
path: list, maze: np.ndarray, h_w: tuple, move: list) -> list:
visited: np.ndarray,
maze: np.ndarray,
h_w: tuple,
move: list,
) -> list:
while path: while path:
last = path[-1] last = path[-1]
if DepthFirstSearchSolver.random_path(visited, last, maze, h_w): if DepthFirstSearchSolver.random_path(visited, last, maze, h_w):
-19
View File
@@ -1,19 +0,0 @@
D53939553D13B93
93AAC697A92C6AA
AC6A95056AE956A
A916856D1454152
A843C5394555696
A87AF96AFFFB96B
AE96FC5457F8412
C52BFFFBFFFC102
B94453FAFD516EA
86953AFAFFFA956
81692C54153A853
AC3A83950546C3A
ABA86C2D69517C2
C2AC55293ABC53A
D6C55546C4457C6
1,1
2,2
EESSWN