From a0fe606c238ba1b3ffe6eeb9a895c8132baf4a66 Mon Sep 17 00:00:00 2001 From: Eikthyrnir Date: Wed, 29 Mar 2023 07:13:08 +0200 Subject: [PATCH] added cat and plants --- domain/__init__.py | 0 domain/entities/cat.py | 28 ++++++++ domain/{ => entities}/entity.py | 0 domain/entities/vacuum.py | 23 +++++++ domain/vacuum.py | 8 --- domain/world.py | 41 +++++++++--- main.py | 99 +++++++++++++++++++++++++++- view/renderer.py | 112 ++++++++++++++++++++++++++++++++ view/user_interface.py | 112 -------------------------------- 9 files changed, 293 insertions(+), 130 deletions(-) delete mode 100644 domain/__init__.py create mode 100644 domain/entities/cat.py rename domain/{ => entities}/entity.py (100%) create mode 100644 domain/entities/vacuum.py delete mode 100644 domain/vacuum.py create mode 100644 view/renderer.py delete mode 100644 view/user_interface.py diff --git a/domain/__init__.py b/domain/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/domain/entities/cat.py b/domain/entities/cat.py new file mode 100644 index 0000000..f2f082c --- /dev/null +++ b/domain/entities/cat.py @@ -0,0 +1,28 @@ +import pygame + +from domain.entities.entity import Entity +from domain.world import World + + +class Cat(Entity): + def __init__(self, x: int, y: int, world: World): + super().__init__(x, y, "CAT") + self.world = world + self.last_tick = pygame.time.get_ticks() + self.cooldown = 1000 + self.velocity = 1 + self.busy = False + self.direction = 0 + + def move(self, dx: int, dy: int): + end_x = self.x + dx + end_y = self.y + dy + + if end_x > self.world.width - 1 or end_y > self.world.height - 1 or end_x < 0 or end_y < 0: + return + + self.world.obstacles[self.x][self.y].remove(self) + self.x = end_x + self.y = end_y + self.world.obstacles[end_x][end_y].append(self) + diff --git a/domain/entity.py b/domain/entities/entity.py similarity index 100% rename from domain/entity.py rename to domain/entities/entity.py diff --git a/domain/entities/vacuum.py b/domain/entities/vacuum.py new file mode 100644 index 0000000..1f8c9c8 --- /dev/null +++ b/domain/entities/vacuum.py @@ -0,0 +1,23 @@ +from domain.entities.entity import Entity +from domain.world import World + + +class Vacuum(Entity): + def __init__(self, x: int, y: int, world: World): + super().__init__(x, y, 'VACUUM') + self.world = world + self.battery = 100 + # TODO add more properties + + def move(self, dx, dy): + end_x = self.x + dx + end_y = self.y + dy + + if end_x > self.world.width - 1 or end_y > self.world.height - 1 or end_x < 0 or end_y < 0: + return + + if self.world.is_obstacle_at(end_x, end_y): + return + + self.x = end_x + self.y = end_y diff --git a/domain/vacuum.py b/domain/vacuum.py deleted file mode 100644 index 338f154..0000000 --- a/domain/vacuum.py +++ /dev/null @@ -1,8 +0,0 @@ -from domain.entity import Entity - - -class Vacuum(Entity): - def __init__(self, x: int, y: int): - super().__init__(x, y, 'VACUUM') - self.battery = 100 - # TODO add more properties diff --git a/domain/world.py b/domain/world.py index 22f64f7..35722b3 100644 --- a/domain/world.py +++ b/domain/world.py @@ -1,19 +1,44 @@ -from domain.entity import Entity -from domain.vacuum import Vacuum +from domain.entities.entity import Entity class World: def __init__(self, width: int, height: int): self.width = width self.height = height - self.grid = [ + self.dust = [ + [[] for j in range(height)] for i in range(width) + ] + self.obstacles = [ [[] for j in range(height)] for i in range(width) ] self.entities = [] - self.cleaner = Vacuum(0, 0) - self.add(self.cleaner) + self.vacuum = None + self.cat = None - def add(self, entity: Entity): - self.entities.append(entity) - self.grid[entity.x][entity.y].append(entity) + # move: update position from (start_x, start_y) to (end_x, end_y) + # def move(self, entity: Entity, end_x: int, end_y: int): + # # no change + # if entity.x == end_x and entity.y == end_y: + # return + # + # # check if object moves beyond border + # if end_x > self.width - 1 or end_y > self.height - 1 or end_x < 0 or end_y < 0: + # print("Cannot move object beyond board") + # return + # + # # check if destination is empty + # # if self.is_obstacle_at(end_x, end_y): + # # print( + # # f"Cannot move object to ({end_x}, {end_y}): position already occupied" + # # ) + # # return + # + # # change position in array + # self.grid[entity.x][entity.y].remove(entity) + # self.grid[end_x][end_y].append(entity) + # entity.x = end_x + # entity.y = end_y + + def is_obstacle_at(self, x: int, y: int) -> bool: + return bool(self.obstacles[x][y]) diff --git a/main.py b/main.py index a9f53f8..f692a8d 100644 --- a/main.py +++ b/main.py @@ -1,3 +1,98 @@ -from Interface.vacuum_render import initial_draw +from random import randint -initial_draw(500, 10) +import pygame + +from Interface.vacuum_render import initial_draw +from domain.entities.cat import Cat +from domain.entities.entity import Entity +from domain.entities.vacuum import Vacuum +from domain.world import World +from view.renderer import Renderer + + +# initial_draw(500, 10) + +class Main: + def __init__(self): + tiles_x = 10 + tiles_y = 10 + + self.renderer = Renderer(800, 800, tiles_x, tiles_y) + + self.world = World(tiles_x, tiles_y) + for _ in range(10): + temp_x = randint(0, tiles_x - 1) + temp_y = randint(0, tiles_y - 1) + self.world.dust[temp_x][temp_y].append(Entity(temp_x, temp_y, "PEEL")) + self.world.vacuum = Vacuum(1, 1, self.world) + self.world.cat = Cat(7, 8, self.world) + self.world.obstacles[7][8].append(self.world.cat) + self.world.obstacles[2][8].append(Entity(2, 8, "PLANT1")) + self.world.obstacles[4][1].append(Entity(4, 1, "PLANT1")) + self.world.obstacles[3][4].append(Entity(3, 4, "PLANT2")) + self.world.obstacles[8][8].append(Entity(8, 8, "PLANT2")) + self.world.obstacles[9][3].append(Entity(9, 3, "PLANT3")) + + self.clock = pygame.time.Clock() + self.running = True + self.fps = 60 + + def run(self): + while self.running: + self.process_input() + self.update() + self.renderer.render(self.world) + self.clock.tick(self.fps) + + pygame.quit() + + def process_input(self): + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.running = False + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_LEFT: + self.world.vacuum.move(-1, 0) + if event.key == pygame.K_RIGHT: + self.world.vacuum.move(1, 0) + if event.key == pygame.K_UP: + self.world.vacuum.move(0, -1) + if event.key == pygame.K_DOWN: + self.world.vacuum.move(0, 1) + + def update(self): + now = pygame.time.get_ticks() + # region cat random movement + cat = self.world.cat + if now - cat.last_tick >= cat.cooldown: + if not cat.busy: + while True: + cat.direction = randint(0, 3) + if not ((cat.direction == 0 and cat.y == 0) + or (cat.direction == 1 and cat.x == self.world.width - 1) + or (cat.direction == 2 and cat.y == self.world.height - 1) + or (cat.direction == 3 and cat.x == 0)): + break + + if cat.direction == 0: # up + if cat.busy: + cat.move(0, - 1) + cat.busy = not cat.busy + if cat.direction == 1: # right + if cat.busy: + cat.move(1, 0) + cat.busy = not cat.busy + if cat.direction == 2: # down + if cat.busy: + cat.move(0, 1) + cat.busy = not cat.busy + if cat.direction == 3: # left + if cat.busy: + cat.move(-1, 0) + cat.busy = not cat.busy + cat.last_tick = pygame.time.get_ticks() + + +if __name__ == "__main__": + app = Main() + app.run() diff --git a/view/renderer.py b/view/renderer.py new file mode 100644 index 0000000..d3c4c15 --- /dev/null +++ b/view/renderer.py @@ -0,0 +1,112 @@ +import random +from random import randint + +import pygame +from pygame import Color + +from domain.entities.cat import Cat +from domain.world import World, Entity + + +class Renderer: + + def __init__( + self, + width=800, + height=800, + tiles_x=10, + tiles_y=10, + ): + self.width = width + self.height = height + + self.tiles_x = tiles_x + self.tiles_y = tiles_y + + self.tile_width = self.width / self.tiles_x + self.tile_height = self.height / self.tiles_y + + pygame.init() + + pygame.display.set_caption("AI Vacuum Cleaner") + self.screen = pygame.display.set_mode((self.width, self.height)) + + self.sprites = { + "VACUUM": pygame.transform.scale(pygame.image.load("media/sprites/vacuum.png"), + (self.tile_width, self.tile_height)), + "WALL": pygame.transform.scale(pygame.image.load("media/sprites/wall.png"), + (self.tile_width, self.tile_height)), + "TILE": pygame.transform.scale(pygame.image.load("media/sprites/tile_cropped.jpeg"), + (self.tile_width, self.tile_height)), + "PEEL": pygame.transform.scale(pygame.image.load("media/sprites/peel.webp"), + (self.tile_width, self.tile_height)), + "CAT_FRONT": pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_front.png"), + (self.tile_width, self.tile_height)), + "CAT_BACK": pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_back.png"), + (self.tile_width, self.tile_height)), + "CAT_LEFT": pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_left.png"), + (self.tile_width, self.tile_height)), + "CAT_RIGHT": pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_right.png"), + (self.tile_width, self.tile_height)), + "PLANT1": pygame.transform.scale(pygame.image.load("Interface/images/plants/plant1.png"), + (self.tile_width + self.tile_width / 4, self.tile_height + self.tile_height / 4)), + "PLANT2": pygame.transform.scale(pygame.image.load("Interface/images/plants/plant2.png"), + (self.tile_width + self.tile_width / 4, self.tile_height + self.tile_height / 4)), + "PLANT3": pygame.transform.scale(pygame.image.load("Interface/images/plants/plant3.png"), + (self.tile_width + self.tile_width / 4, self.tile_height + self.tile_height / 4)), + } + + self.cat_direction_sprite = { + 0: self.sprites["CAT_BACK"], + 1: self.sprites["CAT_RIGHT"], + 2: self.sprites["CAT_FRONT"], + 3: self.sprites["CAT_LEFT"], + } + + def render(self, world: World): + self.render_floor() + self.render_board() + for x in range(world.width): + for y in range(world.height): + for entity in world.dust[x][y]: + self.draw_entity(entity) + for x in range(world.width): + for y in range(world.height): + for entity in world.obstacles[x][y]: + self.draw_entity(entity) + self.draw_entity(world.vacuum) + self.draw_entity(world.cat) + pygame.display.update() + + def line(self, x_1, y_1, x_2, y_2, color=None): + pygame.draw.line(self.screen, color, (x_1, y_1), (x_2, y_2)) + + def render_board(self, color=Color("black")): + for i in range(1, self.tiles_x): + self.line(self.tile_width * i, 0, self.tile_width * i, self.height, color=color) + + for i in range(1, self.tiles_y): + self.line(0, self.tile_height * i, self.width, self.tile_height * i, color=color) + + def draw_entity(self, entity: Entity): + sprite = self.sprites.get(entity.type, None) + draw_pos = (entity.x * self.tile_width, entity.y * self.tile_height) + if "PLANT" in entity.type: + draw_pos = ((entity.x - 0.1) * self.tile_width, (entity.y - 0.25) * self.tile_height) + if "CAT" in entity.type and isinstance(entity, Cat): + sprite = self.cat_direction_sprite[entity.direction] + self.screen.blit(sprite, draw_pos) + + def draw_sprite(self, x: int, y: int, sprite_name: str): + self.screen.blit( + self.sprites[sprite_name], + (x * self.tile_width, y * self.tile_height) + ) + + def fill_grid_with_sprite(self, sprite): + for tile_x in range(self.tiles_x): + for tile_y in range(self.tiles_y): + self.draw_sprite(tile_x, tile_y, sprite) + + def render_floor(self): + self.fill_grid_with_sprite("TILE") diff --git a/view/user_interface.py b/view/user_interface.py deleted file mode 100644 index f6a2cc7..0000000 --- a/view/user_interface.py +++ /dev/null @@ -1,112 +0,0 @@ -from random import randint - -import pygame -from pygame import Color - -from domain.world import World, Entity - - -class UserInterface: - - def __init__( - self, - width=800, - height=800, - tiles_x=10, - tiles_y=10, - ): - self.width = width - self.height = height - - self.tiles_x = tiles_x - self.tiles_y = tiles_y - - self.tile_width = self.width / self.tiles_x - self.tile_height = self.height / self.tiles_y - - self.world = World(tiles_x, tiles_y) - for _ in range(10): - temp_x = randint(0, tiles_x - 1) - temp_y = randint(0, tiles_y - 1) - self.world.add(Entity(temp_x, temp_y, 'PEEL')) - - pygame.init() - - pygame.display.set_caption('AI Vacuum Cleaner') - self.screen = pygame.display.set_mode((self.width, self.height)) - - self.sprites = { - 'VACUUM': pygame.transform.scale(pygame.image.load('../media/sprites/vacuum.png'), - (self.tile_width, self.tile_height)), - 'WALL': pygame.transform.scale(pygame.image.load('../media/sprites/wall.png'), - (self.tile_width, self.tile_height)), - 'TILE': pygame.transform.scale(pygame.image.load('../media/sprites/tile_cropped.jpeg'), - (self.tile_width, self.tile_height)), - 'PEEL': pygame.transform.scale(pygame.image.load('../media/sprites/peel.webp'), - (self.tile_width, self.tile_height)), - } - - self.clock = pygame.time.Clock() - self.running = True - self.fps = 60 - - def process_input(self): - for event in pygame.event.get(): - if event.type == pygame.QUIT: - self.running = False - if event.type == pygame.KEYDOWN: - if event.key == pygame.K_LEFT and self.world.cleaner.x > 0: - self.world.cleaner.x -= 1 - if event.key == pygame.K_RIGHT and self.world.cleaner.x < self.tiles_x - 1: - self.world.cleaner.x += 1 - if event.key == pygame.K_UP and self.world.cleaner.y > 0: - self.world.cleaner.y -= 1 - if event.key == pygame.K_DOWN and self.world.cleaner.y < self.tiles_y - 1: - self.world.cleaner.y += 1 - - def update(self): - pass - - def render(self): - self.render_floor() - self.render_board() - for entity in self.world.entities: - self.draw_sprite(entity.x, entity.y, entity.type) - self.draw_sprite(self.world.cleaner.x, self.world.cleaner.y, self.world.cleaner.type) - pygame.display.update() - - def run(self): - while self.running: - self.process_input() - self.update() - self.render() - self.clock.tick(self.fps) - - pygame.quit() - - def line(self, x_1, y_1, x_2, y_2, color=None): - pygame.draw.line(self.screen, color, (x_1, y_1), (x_2, y_2)) - - def render_board(self, color=Color('black')): - for i in range(1, self.tiles_x): - self.line(self.tile_width * i, 0, self.tile_width * i, self.height, color=color) - - for i in range(1, self.tiles_y): - self.line(0, self.tile_height * i, self.width, self.tile_height * i, color=color) - - def draw_sprite(self, x, y, sprite): - self.screen.blit( - self.sprites[sprite], - (x * self.tile_width, y * self.tile_height) - ) - - def fill_grid_with_sprite(self, sprite): - for tile_x in range(self.tiles_x): - for tile_y in range(self.tiles_y): - self.draw_sprite(tile_x, tile_y, sprite) - - def render_floor(self): - self.fill_grid_with_sprite('TILE') - - -UserInterface(800, 800, 10, 10).run()