add maze
This commit is contained in:
parent
efa19e3398
commit
5a4bc5f225
@ -14,6 +14,7 @@ import tempfile
|
|||||||
import urllib.request
|
import urllib.request
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
from random import choice, randrange
|
||||||
from typing import NamedTuple, Optional
|
from typing import NamedTuple, Optional
|
||||||
|
|
||||||
import PIL
|
import PIL
|
||||||
@ -148,6 +149,75 @@ def choose_plural(amount, declensions):
|
|||||||
return f"{amount} {declensions[i]}"
|
return f"{amount} {declensions[i]}"
|
||||||
|
|
||||||
|
|
||||||
|
def maze_gen(row=4, col=4):
|
||||||
|
"""генератор карты
|
||||||
|
взял с коментария
|
||||||
|
https://stepik.org/lesson/502494/step/3?discussion=6527620&unit=494196
|
||||||
|
"""
|
||||||
|
row = max(2 * row + 1, 3)
|
||||||
|
col = max(2 * col + 1, 3)
|
||||||
|
maze = [[2] * col]
|
||||||
|
maze.extend([[2] + [1] * (col - 2) + [2] for _ in range(row - 2)])
|
||||||
|
maze.append(maze[0])
|
||||||
|
|
||||||
|
curr = (randrange(1, len(maze) - 1, 2), randrange(1, len(maze[0]) - 1, 2))
|
||||||
|
path = [curr]
|
||||||
|
maze[curr[0]][curr[1]] = 0
|
||||||
|
|
||||||
|
while path:
|
||||||
|
nexts = [
|
||||||
|
(r1, c1, r2, c2)
|
||||||
|
for r, c in zip((1, 0, -1, 0), (0, 1, 0, -1))
|
||||||
|
if (
|
||||||
|
(r1 := curr[0] + r) is None
|
||||||
|
or (c1 := curr[1] + c) is None
|
||||||
|
or (r2 := curr[0] + 2 * r) is None
|
||||||
|
or (c2 := curr[1] + 2 * c) is None
|
||||||
|
or 1 == maze[r1][c1] == maze[r2][c2]
|
||||||
|
)
|
||||||
|
]
|
||||||
|
if len(nexts):
|
||||||
|
r1, c1, r2, c2 = choice(nexts)
|
||||||
|
maze[r1][c1] = maze[r2][c2] = 0
|
||||||
|
path.append((r2, c2))
|
||||||
|
else:
|
||||||
|
curr = path.pop()
|
||||||
|
|
||||||
|
upd = {
|
||||||
|
("22", "20"): (None, "00"),
|
||||||
|
("02", "22"): ("00", None),
|
||||||
|
("11101", "00101", "11111", "10100", "10111"): (
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
"10001",
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
("10111", "10100", "11111", "00101", "11101"): (
|
||||||
|
None,
|
||||||
|
"10001",
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
for key in upd:
|
||||||
|
for i in range(len(maze) - len(key) + 1):
|
||||||
|
for j in range(len(maze[0]) - len(key[0]) + 1):
|
||||||
|
if all(
|
||||||
|
maze[i + k][j : j + len(v)] == list(map(int, v))
|
||||||
|
for k, v in enumerate(key)
|
||||||
|
):
|
||||||
|
for k, v in filter(lambda x: x[1], enumerate(upd[key])):
|
||||||
|
maze[i + k][j : j + len(v)] = list(map(int, v))
|
||||||
|
|
||||||
|
return maze
|
||||||
|
|
||||||
|
|
||||||
|
def get_maze_sz(maze: list[list[int]]) -> Coords:
|
||||||
|
return Coords(len(maze[0]), len(maze))
|
||||||
|
|
||||||
|
|
||||||
class GameObject(ABC):
|
class GameObject(ABC):
|
||||||
"""обобщение игрового элемента"""
|
"""обобщение игрового элемента"""
|
||||||
|
|
||||||
@ -162,7 +232,7 @@ class GameObject(ABC):
|
|||||||
assets: dict | None = None,
|
assets: dict | None = None,
|
||||||
):
|
):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self._coords = coords
|
self.rect = pygame.Rect(coords, coords)
|
||||||
self.assets = assets or (parent.assets if parent else None)
|
self.assets = assets or (parent.assets if parent else None)
|
||||||
self._surface = None
|
self._surface = None
|
||||||
|
|
||||||
@ -170,20 +240,24 @@ class GameObject(ABC):
|
|||||||
def surface(self) -> pygame.Surface | None:
|
def surface(self) -> pygame.Surface | None:
|
||||||
return self._surface or (self.parent.surface if self.parent else None)
|
return self._surface or (self.parent.surface if self.parent else None)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def scene(self):
|
||||||
|
return self.parent.scene if self.parent else self
|
||||||
|
|
||||||
def get_coords(self) -> Coords:
|
def get_coords(self) -> Coords:
|
||||||
return self._coords
|
return Coords(*self.rect.topleft)
|
||||||
|
|
||||||
def set_coords(self, coords: Coords):
|
def set_coords(self, coords: Coords):
|
||||||
if self.parent:
|
if self.parent:
|
||||||
if self.parent.surface:
|
if self.parent.surface:
|
||||||
if not (
|
if not (
|
||||||
coords.x >= 0
|
coords.x >= 0
|
||||||
and coords.x < self.parent.surface.get_width()
|
and coords.x + self.rect.width <= self.parent.surface.get_width()
|
||||||
and coords.y >= 0
|
and coords.y >= 0
|
||||||
and coords.y < self.parent.surface.get_height()
|
and coords.y + self.rect.height < self.parent.surface.get_height()
|
||||||
):
|
):
|
||||||
return
|
return
|
||||||
self._coords = coords
|
self.rect.topleft = coords
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def draw(self):
|
def draw(self):
|
||||||
@ -204,23 +278,46 @@ class Hero(GameObject):
|
|||||||
assets: dict | None = None,
|
assets: dict | None = None,
|
||||||
):
|
):
|
||||||
super().__init__(coords, parent, assets)
|
super().__init__(coords, parent, assets)
|
||||||
screen_sz = Coords(*parent.surface.get_size())
|
|
||||||
ghost_sz = screen_sz // 10
|
|
||||||
resize_img(self.assets, "ghost.png", ghost_sz)
|
|
||||||
|
|
||||||
self._surface = pygame.image.load(self.assets["ghost.png"])
|
self._surface = pygame.image.load(self.assets["ghost.png"])
|
||||||
self.rect = self.surface.get_rect()
|
self.rect = self.surface.get_rect()
|
||||||
self.coords = coords
|
self.rect.topleft = coords
|
||||||
|
|
||||||
def set_coords(self, coords: Coords):
|
|
||||||
super().set_coords(coords)
|
|
||||||
self.rect.topleft = self.coords
|
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
self.parent.surface.blit(self.surface, self.rect)
|
self.parent.surface.blit(self.surface, self.rect)
|
||||||
|
|
||||||
|
def _check_collision(self, coords):
|
||||||
|
"""Проверка пересечения со стенами"""
|
||||||
|
new_rect = self.rect.copy()
|
||||||
|
new_rect.topleft = coords
|
||||||
|
return self.scene.walls.check_collision(new_rect)
|
||||||
|
|
||||||
|
def _reduce_step(self, coords):
|
||||||
|
"""Уменьшение шага движения, с целью подойти вплотную к стене"""
|
||||||
|
delta = coords - self.coords
|
||||||
|
dx, dy = 0, 0
|
||||||
|
if abs(delta.x) > 1:
|
||||||
|
dx = 1 * (delta.x < 0 or -1)
|
||||||
|
if abs(delta.y) > 1:
|
||||||
|
dy = 1 * (delta.y < 0 or -1)
|
||||||
|
return coords + Coords(dx, dy)
|
||||||
|
|
||||||
|
def set_coords(self, coords: Coords):
|
||||||
|
# проверка колизии
|
||||||
|
has_collision = self._check_collision(coords)
|
||||||
|
if not has_collision:
|
||||||
|
return super().set_coords(coords)
|
||||||
|
|
||||||
|
# уменьшение шага
|
||||||
|
while has_collision and coords != self.coords:
|
||||||
|
coords_new = self._reduce_step(coords)
|
||||||
|
if coords_new == coords:
|
||||||
|
return # не могу уменьшить шаг
|
||||||
|
coords = coords_new
|
||||||
|
has_collision = self._check_collision(coords)
|
||||||
|
super().set_coords(coords)
|
||||||
|
|
||||||
def handle_event(self, event: pygame.event.Event):
|
def handle_event(self, event: pygame.event.Event):
|
||||||
delta = 10
|
delta = 30
|
||||||
if event.type == pygame.KEYDOWN:
|
if event.type == pygame.KEYDOWN:
|
||||||
match event.key:
|
match event.key:
|
||||||
case pygame.K_UP | pygame.K_w:
|
case pygame.K_UP | pygame.K_w:
|
||||||
@ -233,25 +330,106 @@ class Hero(GameObject):
|
|||||||
self.coords += Coords(1, 0) * delta
|
self.coords += Coords(1, 0) * delta
|
||||||
|
|
||||||
|
|
||||||
|
class WallBlock(GameObject):
|
||||||
|
"""объект елемента стены"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
coords: Coords,
|
||||||
|
parent: GameObject,
|
||||||
|
assets: dict | None = None,
|
||||||
|
):
|
||||||
|
super().__init__(coords, parent, assets)
|
||||||
|
self._surface = pygame.image.load(self.assets["brick.png"])
|
||||||
|
self.rect = self.surface.get_rect()
|
||||||
|
self.rect.topleft = coords
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
self.parent.surface.blit(self.surface, self.rect)
|
||||||
|
|
||||||
|
def handle_event(self, event: pygame.event.Event):
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
class Walls(GameObject):
|
||||||
|
"""объект стен"""
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
parent: GameObject,
|
||||||
|
maze: list[list[int]],
|
||||||
|
box_sz: Coords,
|
||||||
|
assets: dict | None = None,
|
||||||
|
):
|
||||||
|
super().__init__(Coords.zero(), parent, assets)
|
||||||
|
self.box_sz = box_sz
|
||||||
|
self.blocks = [
|
||||||
|
WallBlock(Coords(j, i).transform(box_sz), self, self.assets)
|
||||||
|
for i, row in enumerate(maze)
|
||||||
|
for j, item in enumerate(row)
|
||||||
|
if item > 0
|
||||||
|
]
|
||||||
|
|
||||||
|
def draw(self):
|
||||||
|
for block in self.blocks:
|
||||||
|
block.draw()
|
||||||
|
|
||||||
|
def check_collision(self, rect: pygame.Rect) -> bool:
|
||||||
|
for block in self.blocks:
|
||||||
|
if block.rect.colliderect(rect):
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def handle_event(self, event: pygame.event.Event):
|
||||||
|
for block in self.blocks:
|
||||||
|
block.handle_event(event)
|
||||||
|
|
||||||
|
|
||||||
class Scene(GameObject):
|
class Scene(GameObject):
|
||||||
"""основной игровой объект"""
|
"""основной игровой объект"""
|
||||||
|
|
||||||
# кнопки для выхода из игры
|
# кнопки для выхода из игры
|
||||||
exit_keys = (pygame.K_ESCAPE, pygame.K_q)
|
exit_keys = (pygame.K_ESCAPE, pygame.K_q)
|
||||||
|
|
||||||
def __init__(self, assets: dict, sz: Coords):
|
def __init__(self, assets: dict, screen_sz: Coords, maze_sz: Coords):
|
||||||
super().__init__(Coords.zero(), None, assets)
|
super().__init__(Coords.zero(), None, assets)
|
||||||
self._surface = pygame.display.set_mode(sz)
|
self.maze = maze_gen(*maze_sz)
|
||||||
self.hero = Hero(Coords(100, 100), self)
|
maze_sz = get_maze_sz(self.maze)
|
||||||
resize_img(assets, "bg1k.png", sz)
|
|
||||||
|
box_sz = screen_sz // get_maze_sz(self.maze)
|
||||||
|
self.box_sz = box_sz
|
||||||
|
resize_img(self.assets, "brick.png", box_sz)
|
||||||
|
|
||||||
|
hero_sz = Coords(*map(int, box_sz * 0.8))
|
||||||
|
resize_img(self.assets, "ghost.png", hero_sz)
|
||||||
|
|
||||||
|
hero_y_offset = (box_sz.y - hero_sz.y) // 2 + box_sz.y
|
||||||
|
|
||||||
|
self._surface = pygame.display.set_mode(screen_sz)
|
||||||
|
self.rect = self._surface.get_rect()
|
||||||
|
self.hero = Hero(Coords(0, hero_y_offset), self)
|
||||||
|
resize_img(assets, "bg1k.png", screen_sz)
|
||||||
self.background = pygame.image.load(self.assets["bg1k.png"])
|
self.background = pygame.image.load(self.assets["bg1k.png"])
|
||||||
self.done = False
|
self.done = False
|
||||||
|
self.maze = maze_gen(6, 6)
|
||||||
|
self.walls = Walls(self, self.maze, box_sz)
|
||||||
|
print(self.get_exit_rect())
|
||||||
|
print(self.rect)
|
||||||
|
|
||||||
|
def get_exit_rect(self) -> pygame.Rect:
|
||||||
|
maze_sz = get_maze_sz(self.maze)
|
||||||
|
coords = (maze_sz - Coords(1, 2)) * self.box_sz
|
||||||
|
rect = pygame.Rect(coords, coords)
|
||||||
|
rect.width, rect.height = self.box_sz
|
||||||
|
return rect
|
||||||
|
|
||||||
def draw(self):
|
def draw(self):
|
||||||
if self.done:
|
if self.done:
|
||||||
return
|
return
|
||||||
self.surface.blit(self.background, self.coords)
|
self.surface.blit(self.background, self.coords)
|
||||||
|
pygame.draw.rect(self._surface, pygame.Color("#42c53d25"), self.get_exit_rect())
|
||||||
self.hero.draw()
|
self.hero.draw()
|
||||||
|
self.walls.draw()
|
||||||
|
|
||||||
def handle_event(self, event: pygame.event.Event):
|
def handle_event(self, event: pygame.event.Event):
|
||||||
if self.done:
|
if self.done:
|
||||||
@ -276,9 +454,10 @@ class Scene(GameObject):
|
|||||||
def game(assets):
|
def game(assets):
|
||||||
pygame.init()
|
pygame.init()
|
||||||
screen_sz = Coords(1000, 1000)
|
screen_sz = Coords(1000, 1000)
|
||||||
|
maze_sz = Coords(6, 6)
|
||||||
pygame.display.set_caption("Движение рисунка на Pygame")
|
pygame.display.set_caption("Движение рисунка на Pygame")
|
||||||
|
|
||||||
scene = Scene(assets, screen_sz)
|
scene = Scene(assets, screen_sz, maze_sz)
|
||||||
scene.event_loop()
|
scene.event_loop()
|
||||||
|
|
||||||
pygame.quit()
|
pygame.quit()
|
||||||
@ -288,6 +467,7 @@ def main():
|
|||||||
assets = [
|
assets = [
|
||||||
"bg1k.png",
|
"bg1k.png",
|
||||||
"ghost.png",
|
"ghost.png",
|
||||||
|
"brick.png",
|
||||||
]
|
]
|
||||||
with get_assets(assets) as assets:
|
with get_assets(assets) as assets:
|
||||||
game(assets)
|
game(assets)
|
||||||
|
Loading…
Reference in New Issue
Block a user