Merge pull request 'Redesigned project architecture to be a little bit more modular' (#12) from sandbox into main
Reviewed-on: #12
@ -1,61 +0,0 @@
|
|||||||
import random
|
|
||||||
import sys
|
|
||||||
import pygame
|
|
||||||
|
|
||||||
|
|
||||||
class Colors:
|
|
||||||
BLACK = 0, 0, 0
|
|
||||||
WHITE = 255, 255, 255
|
|
||||||
RED = 255, 0, 0
|
|
||||||
GREEN = 0, 255, 0
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_COLOR = Colors.WHITE
|
|
||||||
|
|
||||||
|
|
||||||
def default_color(func):
|
|
||||||
def wrap(*args, **kwargs):
|
|
||||||
if "color" not in kwargs:
|
|
||||||
kwargs["color"] = DEFAULT_COLOR
|
|
||||||
result = func(*args, **kwargs)
|
|
||||||
return result
|
|
||||||
|
|
||||||
return wrap
|
|
||||||
|
|
||||||
|
|
||||||
class GridDraw:
|
|
||||||
def __init__(self, width=None, height=None, background=None):
|
|
||||||
self.width = width if width != None else 100
|
|
||||||
self.height = height if height != None else 100
|
|
||||||
self.background = background if background != None else Colors.BLACK
|
|
||||||
|
|
||||||
pygame.init()
|
|
||||||
self.screen = pygame.display.set_mode((self.width, self.height))
|
|
||||||
|
|
||||||
def start_draw(self):
|
|
||||||
self.screen.fill(Colors.BLACK)
|
|
||||||
|
|
||||||
def end_draw(self):
|
|
||||||
pygame.display.flip()
|
|
||||||
|
|
||||||
@default_color
|
|
||||||
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))
|
|
||||||
|
|
||||||
@default_color
|
|
||||||
def board(self, tiles_x, tiles_y, color=None):
|
|
||||||
tiles_width = self.width / tiles_x
|
|
||||||
tiles_height = self.height / tiles_y
|
|
||||||
|
|
||||||
for i in range(1, tiles_x):
|
|
||||||
self.line(tiles_width * i, 0, tiles_width * i, self.height, color=color)
|
|
||||||
|
|
||||||
for i in range(1, tiles_y):
|
|
||||||
self.line(0, tiles_height * i, self.width, tiles_height * i, color=color)
|
|
||||||
|
|
||||||
@default_color
|
|
||||||
def circle(self, x, y, radius, color=None):
|
|
||||||
pygame.draw.circle(self.screen, color, (x, y), radius)
|
|
||||||
|
|
||||||
def image(self, x, y, image):
|
|
||||||
self.screen.blit(image, (x, y))
|
|
@ -1,25 +0,0 @@
|
|||||||
import pygame
|
|
||||||
import sys
|
|
||||||
|
|
||||||
|
|
||||||
def movement_key_press(board_size, x, y):
|
|
||||||
for event in pygame.event.get():
|
|
||||||
if event.type == pygame.QUIT:
|
|
||||||
sys.exit()
|
|
||||||
if event.type == pygame.KEYDOWN:
|
|
||||||
# go left
|
|
||||||
if event.key == pygame.K_LEFT and x > 0:
|
|
||||||
x -= 1
|
|
||||||
|
|
||||||
# go right
|
|
||||||
if event.key == pygame.K_RIGHT and x < board_size - 1:
|
|
||||||
x += 1
|
|
||||||
|
|
||||||
# go up
|
|
||||||
if event.key == pygame.K_UP and y > 0:
|
|
||||||
y -= 1
|
|
||||||
|
|
||||||
# go down
|
|
||||||
if event.key == pygame.K_DOWN and y < board_size - 1:
|
|
||||||
y += 1
|
|
||||||
return (x, y)
|
|
@ -1,266 +0,0 @@
|
|||||||
from enum import Enum
|
|
||||||
import random
|
|
||||||
from typing import List
|
|
||||||
from Interface.grid_draw import GridDraw, Colors
|
|
||||||
import sys
|
|
||||||
import pygame
|
|
||||||
from Interface.movement import movement_key_press
|
|
||||||
|
|
||||||
|
|
||||||
# window_dimensions says how many pixels window have
|
|
||||||
# board_size says how many lines board have in one row
|
|
||||||
def initial_draw(window_dimensions, board_size):
|
|
||||||
# window name
|
|
||||||
pygame.display.set_caption("AI Vacuum Cleaner")
|
|
||||||
|
|
||||||
# define additional variables
|
|
||||||
tile_size = window_dimensions / board_size
|
|
||||||
|
|
||||||
# initialize board array
|
|
||||||
newGrid = Grid(board_size, window_dimensions=window_dimensions, board_size=board_size)
|
|
||||||
newGrid.add(objectOnTile(1, 1, acceptedType.PLAYER))
|
|
||||||
newGrid.add(objectOnTile(7, 8, acceptedType.ANIMAL))
|
|
||||||
newGrid.add(objectOnTile(2, 8, acceptedType.PLANT1))
|
|
||||||
newGrid.add(objectOnTile(4, 1, acceptedType.PLANT1))
|
|
||||||
newGrid.add(objectOnTile(3, 4, acceptedType.PLANT2))
|
|
||||||
newGrid.add(objectOnTile(8, 8, acceptedType.PLANT2))
|
|
||||||
newGrid.add(objectOnTile(9, 3, acceptedType.PLANT3))
|
|
||||||
|
|
||||||
|
|
||||||
player = newGrid.findFirst(acceptedType.PLAYER)
|
|
||||||
newGrid.move(1, 1, 1, 2)
|
|
||||||
newGrid.move(1, 2, 1, 1)
|
|
||||||
|
|
||||||
# set window dimension
|
|
||||||
window_width = window_dimensions
|
|
||||||
window_height = window_dimensions
|
|
||||||
|
|
||||||
# initialize drawer
|
|
||||||
drawer = GridDraw(window_width, window_height)
|
|
||||||
|
|
||||||
# rendering loop
|
|
||||||
while True:
|
|
||||||
drawer.start_draw()
|
|
||||||
drawer.board(board_size, board_size)
|
|
||||||
|
|
||||||
player = newGrid.findFirst(acceptedType.PLAYER)
|
|
||||||
|
|
||||||
(x, y) = movement_key_press(board_size, player.position_x, player.position_y)
|
|
||||||
|
|
||||||
newGrid.move(player.position_x, player.position_y, x, y)
|
|
||||||
|
|
||||||
newGrid.render(drawer, newGrid=newGrid)
|
|
||||||
drawer.end_draw()
|
|
||||||
pygame.time.delay(30)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO wrap it all to another file that handles array rendering
|
|
||||||
class acceptedType(Enum):
|
|
||||||
EMPTY = "empty"
|
|
||||||
PLAYER = "player"
|
|
||||||
RUBBISH = "rubbish"
|
|
||||||
PLANT1 = "plant1"
|
|
||||||
PLANT2 = "plant2"
|
|
||||||
PLANT3 = "plant3"
|
|
||||||
ANIMAL = "animal"
|
|
||||||
|
|
||||||
|
|
||||||
class objectOnTile:
|
|
||||||
def __init__(
|
|
||||||
self, position_x: int, position_y: int, type: acceptedType = acceptedType.EMPTY
|
|
||||||
):
|
|
||||||
self.position_x = position_x
|
|
||||||
self.position_y = position_y
|
|
||||||
self.type = type
|
|
||||||
|
|
||||||
|
|
||||||
# calculate position from array position to window position eg.: array_position = 0 => window_position = 50 (px)
|
|
||||||
def _translate_array_to_window_position(array_position, tile_size_window) -> int:
|
|
||||||
return array_position * tile_size_window + tile_size_window / 2
|
|
||||||
|
|
||||||
|
|
||||||
class Grid:
|
|
||||||
|
|
||||||
def __init__(self, size_array, window_dimensions, board_size):
|
|
||||||
self.array = [
|
|
||||||
[objectOnTile(i, j) for j in range(size_array)] for i in range(size_array)
|
|
||||||
]
|
|
||||||
self.list: List[objectOnTile] = []
|
|
||||||
|
|
||||||
self.tile_size = window_dimensions / board_size
|
|
||||||
self.board_size = board_size
|
|
||||||
|
|
||||||
self.cat_last_tick = pygame.time.get_ticks()
|
|
||||||
self.cat_cooldown = 1000
|
|
||||||
self.cat_velocity = 1
|
|
||||||
self.cat_busy = False
|
|
||||||
|
|
||||||
#region images
|
|
||||||
self.cat_front_image = pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_front.png"), (self.tile_size, self.tile_size))
|
|
||||||
self.cat_back_image = pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_back.png"), (self.tile_size, self.tile_size))
|
|
||||||
self.cat_left_image = pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_left.png"), (self.tile_size, self.tile_size))
|
|
||||||
self.cat_right_image = pygame.transform.scale(pygame.image.load("Interface/images/cat/standing_right.png"), (self.tile_size, self.tile_size))
|
|
||||||
self.cat_current_image = self.cat_front_image
|
|
||||||
self.plant1 = pygame.transform.scale(pygame.image.load("Interface/images/plants/plant1.png"), (self.tile_size + self.tile_size/4, self.tile_size + self.tile_size/4))
|
|
||||||
self.plant2 = pygame.transform.scale(pygame.image.load("Interface/images/plants/plant2.png"), (self.tile_size + self.tile_size/4, self.tile_size + self.tile_size/4))
|
|
||||||
self.plant3 = pygame.transform.scale(pygame.image.load("Interface/images/plants/plant3.png"), (self.tile_size + self.tile_size/4, self.tile_size + self.tile_size/4))
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
# render the array
|
|
||||||
def render(self, drawer: GridDraw, newGrid):
|
|
||||||
#tile_size = window_dimensions / board_size
|
|
||||||
|
|
||||||
# render object with respect to type
|
|
||||||
for item in self.list:
|
|
||||||
if item.type == acceptedType.PLAYER:
|
|
||||||
# constants for player
|
|
||||||
PLAYER_RADIUS_RATIO = 3
|
|
||||||
PLAYER_COLOR = Colors.RED
|
|
||||||
|
|
||||||
# position on screen
|
|
||||||
render_x = _translate_array_to_window_position(
|
|
||||||
item.position_x, self.tile_size
|
|
||||||
)
|
|
||||||
render_y = _translate_array_to_window_position(
|
|
||||||
item.position_y, self.tile_size
|
|
||||||
)
|
|
||||||
|
|
||||||
# image rendering function
|
|
||||||
drawer.circle(
|
|
||||||
render_x,
|
|
||||||
render_y,
|
|
||||||
self.tile_size / PLAYER_RADIUS_RATIO,
|
|
||||||
color=PLAYER_COLOR,
|
|
||||||
)
|
|
||||||
if item.type == acceptedType.ANIMAL:
|
|
||||||
now = pygame.time.get_ticks()
|
|
||||||
#region cat random movement
|
|
||||||
if now - self.cat_last_tick >= self.cat_cooldown:
|
|
||||||
|
|
||||||
if self.cat_busy == False:
|
|
||||||
while True:
|
|
||||||
self.cat_direction = random.randint(0,3)
|
|
||||||
if not((self.cat_direction == 0 and item.position_y == 0) or (self.cat_direction == 1 and item.position_x == self.board_size - 1) or (self.cat_direction == 2 and item.position_y == self.board_size - 1) or (self.cat_direction == 3 and item.position_x == 0)):
|
|
||||||
break
|
|
||||||
|
|
||||||
if self.cat_direction == 0: #up
|
|
||||||
if self.cat_current_image == self.cat_back_image:
|
|
||||||
newGrid.move(item.position_x, item.position_y, item.position_x, item.position_y - 1)
|
|
||||||
self.cat_busy = False
|
|
||||||
else:
|
|
||||||
self.cat_busy = True
|
|
||||||
self.cat_current_image = self.cat_back_image
|
|
||||||
if self.cat_direction == 1: #right
|
|
||||||
if self.cat_current_image == self.cat_right_image:
|
|
||||||
newGrid.move(item.position_x, item.position_y, item.position_x + 1, item.position_y)
|
|
||||||
self.cat_busy = False
|
|
||||||
else:
|
|
||||||
self.cat_busy = True
|
|
||||||
self.cat_current_image = self.cat_right_image
|
|
||||||
if self.cat_direction == 2: #down
|
|
||||||
if self.cat_current_image == self.cat_front_image:
|
|
||||||
newGrid.move(item.position_x, item.position_y, item.position_x, item.position_y + 1)
|
|
||||||
self.cat_busy = False
|
|
||||||
else:
|
|
||||||
self.cat_busy = True
|
|
||||||
self.cat_current_image = self.cat_front_image
|
|
||||||
if self.cat_direction == 3: #left
|
|
||||||
if self.cat_current_image == self.cat_left_image:
|
|
||||||
newGrid.move(item.position_x, item.position_y, item.position_x - 1, item.position_y)
|
|
||||||
self.cat_busy = False
|
|
||||||
else:
|
|
||||||
self.cat_busy = True
|
|
||||||
self.cat_current_image = self.cat_left_image
|
|
||||||
self.cat_last_tick = pygame.time.get_ticks()
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
render_x = item.position_x * self.tile_size
|
|
||||||
render_y = item.position_y * self.tile_size
|
|
||||||
|
|
||||||
drawer.image(render_x, render_y, self.cat_current_image)
|
|
||||||
|
|
||||||
if item.type == acceptedType.PLANT1:
|
|
||||||
drawer.image((item.position_x - 0.1) * self.tile_size, (item.position_y - 0.25) * self.tile_size, self.plant1)
|
|
||||||
if item.type == acceptedType.PLANT2:
|
|
||||||
drawer.image((item.position_x - 0.1) * self.tile_size, (item.position_y - 0.25) * self.tile_size, self.plant2)
|
|
||||||
if item.type == acceptedType.PLANT3:
|
|
||||||
drawer.image((item.position_x - 0.1) * self.tile_size, (item.position_y - 0.25) * self.tile_size, self.plant3)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# TODO act accordingly to other options(rubbish)
|
|
||||||
|
|
||||||
# add new object on grid
|
|
||||||
def add(self, newObject: objectOnTile):
|
|
||||||
if (
|
|
||||||
self.array[newObject.position_x][newObject.position_y].type
|
|
||||||
!= acceptedType.EMPTY
|
|
||||||
):
|
|
||||||
print(
|
|
||||||
f"Cannot add object at ({newObject.position_x}, {newObject.position_y}): position already occupied"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.array[newObject.position_x][newObject.position_y] = newObject
|
|
||||||
self.list.append(newObject)
|
|
||||||
|
|
||||||
# deletes object from game
|
|
||||||
# untested, potentially not working
|
|
||||||
def delete(self, position_x: int, position_y: int):
|
|
||||||
# Find the object with the given position in the list
|
|
||||||
for obj in self.list:
|
|
||||||
if obj.position_x == position_x and obj.position_y == position_y:
|
|
||||||
break
|
|
||||||
|
|
||||||
else: # No object found with the given position
|
|
||||||
print(f"No object found at ({position_x}, {position_y})")
|
|
||||||
return
|
|
||||||
|
|
||||||
# Remove the object from both the array and the list
|
|
||||||
self.array[position_x][position_y] = objectOnTile(position_x, position_y)
|
|
||||||
self.list.remove(obj)
|
|
||||||
|
|
||||||
# move: update position from (start_x, start_y) to (end_x, end_y)
|
|
||||||
def move(self, start_x: int, start_y: int, end_x: int, end_y: int):
|
|
||||||
# no change
|
|
||||||
if start_x == end_x and start_y == end_y:
|
|
||||||
return
|
|
||||||
|
|
||||||
#check if object moves beyond border
|
|
||||||
if end_x > self.board_size - 1 or end_y > self.board_size - 1 or end_x < 0 or end_y < 0:
|
|
||||||
print(
|
|
||||||
f"Cannot move object beyond board"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
# check if obj exist at starting position
|
|
||||||
if self.array[start_x][start_y].type == acceptedType.EMPTY:
|
|
||||||
print(
|
|
||||||
f"Cannot move object at ({start_x}, {start_y}): no object on position"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
# check if destination is empty
|
|
||||||
if self.array[end_x][end_y].type != acceptedType.EMPTY:
|
|
||||||
print(
|
|
||||||
f"Cannot move object to ({end_x}, {end_y}): position already occupied"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# all OK
|
|
||||||
# change position attribute in array
|
|
||||||
self.array[start_x][start_y].position_x = end_x
|
|
||||||
self.array[start_x][start_y].position_y = end_y
|
|
||||||
|
|
||||||
# change position in array
|
|
||||||
self.array[end_x][end_y] = self.array[start_x][start_y]
|
|
||||||
self.array[start_x][start_y] = objectOnTile(start_x, start_y)
|
|
||||||
|
|
||||||
def findFirst(self, find_type: acceptedType) -> objectOnTile:
|
|
||||||
for item in self.list:
|
|
||||||
if item.type == find_type:
|
|
||||||
return item
|
|
||||||
else:
|
|
||||||
print(f"Cannot find object of type: ({find_type})!")
|
|
3
domain/commands/command.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
class Command:
|
||||||
|
def run(self):
|
||||||
|
raise NotImplementedError()
|
70
domain/commands/random_cat_move_command.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
from random import randint
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
|
||||||
|
from domain.commands.command import Command
|
||||||
|
from domain.entities.cat import Cat
|
||||||
|
from domain.world import World
|
||||||
|
|
||||||
|
|
||||||
|
class RandomCatMoveCommand(Command):
|
||||||
|
def __init__(self, world: World, cat: Cat) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.world = world
|
||||||
|
self.cat = cat
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
move_vector = (0, 0)
|
||||||
|
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:
|
||||||
|
move_vector = (0, -1)
|
||||||
|
cat.busy = not cat.busy
|
||||||
|
if cat.direction == 1: # right
|
||||||
|
if cat.busy:
|
||||||
|
move_vector = (1, 0)
|
||||||
|
cat.busy = not cat.busy
|
||||||
|
if cat.direction == 2: # down
|
||||||
|
if cat.busy:
|
||||||
|
move_vector = (0, 1)
|
||||||
|
cat.busy = not cat.busy
|
||||||
|
if cat.direction == 3: # left
|
||||||
|
if cat.busy:
|
||||||
|
move_vector = (-1, 0)
|
||||||
|
cat.busy = not cat.busy
|
||||||
|
cat.last_tick = pygame.time.get_ticks()
|
||||||
|
|
||||||
|
if move_vector == (0, 0):
|
||||||
|
return
|
||||||
|
|
||||||
|
end_x = cat.x + move_vector[0]
|
||||||
|
end_y = cat.y + move_vector[1]
|
||||||
|
|
||||||
|
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[cat.x][cat.y].remove(cat)
|
||||||
|
cat.x = end_x
|
||||||
|
cat.y = end_y
|
||||||
|
self.world.obstacles[end_x][end_y].append(cat)
|
||||||
|
# endregion cat random movement
|
34
domain/commands/vacuum_move_command.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from typing import Tuple
|
||||||
|
|
||||||
|
from domain.commands.command import Command
|
||||||
|
from domain.entities.vacuum import Vacuum
|
||||||
|
from domain.world import World
|
||||||
|
|
||||||
|
|
||||||
|
class VacuumMoveCommand(Command):
|
||||||
|
def __init__(
|
||||||
|
self, world: World, vacuum: Vacuum, move_vector: Tuple[int, int]
|
||||||
|
) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.world = world
|
||||||
|
self.vacuum = vacuum
|
||||||
|
self.dx = move_vector[0]
|
||||||
|
self.dy = move_vector[1]
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
end_x = self.vacuum.x + self.dx
|
||||||
|
end_y = self.vacuum.y + self.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.vacuum.x = end_x
|
||||||
|
self.vacuum.y = end_y
|
14
domain/entities/cat.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import pygame
|
||||||
|
|
||||||
|
from domain.entities.entity import Entity
|
||||||
|
from domain.world import World
|
||||||
|
|
||||||
|
|
||||||
|
class Cat(Entity):
|
||||||
|
def __init__(self, x: int, y: int):
|
||||||
|
super().__init__(x, y, "CAT")
|
||||||
|
self.last_tick = pygame.time.get_ticks()
|
||||||
|
self.cooldown = 1000
|
||||||
|
self.velocity = 1
|
||||||
|
self.busy = False
|
||||||
|
self.direction = 0
|
5
domain/entities/entity.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class Entity:
|
||||||
|
def __init__(self, x: int, y: int, type: str):
|
||||||
|
self.x = x
|
||||||
|
self.y = y
|
||||||
|
self.type = type
|
9
domain/entities/vacuum.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
from domain.entities.entity import Entity
|
||||||
|
from domain.world import World
|
||||||
|
|
||||||
|
|
||||||
|
class Vacuum(Entity):
|
||||||
|
def __init__(self, x: int, y: int):
|
||||||
|
super().__init__(x, y, "VACUUM")
|
||||||
|
self.battery = 100
|
||||||
|
# TODO add more properties
|
26
domain/world.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from domain.entities.entity import Entity
|
||||||
|
|
||||||
|
|
||||||
|
class World:
|
||||||
|
def __init__(self, width: int, height: int):
|
||||||
|
self.width = width
|
||||||
|
self.height = height
|
||||||
|
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.vacuum = None
|
||||||
|
self.cat = None
|
||||||
|
|
||||||
|
def add_entity(self, entity: Entity):
|
||||||
|
if entity.type == "PEEL":
|
||||||
|
self.dust[entity.x][entity.y].append(entity)
|
||||||
|
elif entity.type == "VACUUM":
|
||||||
|
self.vacuum = entity
|
||||||
|
elif entity.type == "CAT":
|
||||||
|
self.cat = entity
|
||||||
|
self.obstacles[entity.x][entity.y].append(entity)
|
||||||
|
else:
|
||||||
|
self.obstacles[entity.x][entity.y].append(entity)
|
||||||
|
|
||||||
|
def is_obstacle_at(self, x: int, y: int) -> bool:
|
||||||
|
return bool(self.obstacles[x][y])
|
91
main.py
@ -1,3 +1,90 @@
|
|||||||
from Interface.vacuum_render import initial_draw
|
from random import randint
|
||||||
|
|
||||||
initial_draw(500, 10)
|
import pygame
|
||||||
|
|
||||||
|
from domain.commands.random_cat_move_command import RandomCatMoveCommand
|
||||||
|
from domain.commands.vacuum_move_command import VacuumMoveCommand
|
||||||
|
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 = generate_world(tiles_x, tiles_y)
|
||||||
|
|
||||||
|
self.commands = []
|
||||||
|
|
||||||
|
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.commands.append(
|
||||||
|
VacuumMoveCommand(self.world, self.world.vacuum, (-1, 0))
|
||||||
|
)
|
||||||
|
if event.key == pygame.K_RIGHT:
|
||||||
|
self.commands.append(
|
||||||
|
VacuumMoveCommand(self.world, self.world.vacuum, (1, 0))
|
||||||
|
)
|
||||||
|
if event.key == pygame.K_UP:
|
||||||
|
self.commands.append(
|
||||||
|
VacuumMoveCommand(self.world, self.world.vacuum, (0, -1))
|
||||||
|
)
|
||||||
|
if event.key == pygame.K_DOWN:
|
||||||
|
self.commands.append(
|
||||||
|
VacuumMoveCommand(self.world, self.world.vacuum, (0, 1))
|
||||||
|
)
|
||||||
|
|
||||||
|
def update(self):
|
||||||
|
self.commands.append(RandomCatMoveCommand(self.world, self.world.cat))
|
||||||
|
|
||||||
|
for command in self.commands:
|
||||||
|
command.run()
|
||||||
|
self.commands.clear()
|
||||||
|
|
||||||
|
|
||||||
|
def generate_world(tiles_x: int, tiles_y: int) -> World:
|
||||||
|
world = World(tiles_x, tiles_y)
|
||||||
|
for _ in range(10):
|
||||||
|
temp_x = randint(0, tiles_x - 1)
|
||||||
|
temp_y = randint(0, tiles_y - 1)
|
||||||
|
world.add_entity(Entity(temp_x, temp_y, "PEEL"))
|
||||||
|
world.vacuum = Vacuum(1, 1)
|
||||||
|
world.cat = Cat(7, 8)
|
||||||
|
world.add_entity(world.cat)
|
||||||
|
world.add_entity(Entity(2, 8, "PLANT1"))
|
||||||
|
world.add_entity(Entity(4, 1, "PLANT1"))
|
||||||
|
world.add_entity(Entity(3, 4, "PLANT2"))
|
||||||
|
world.add_entity(Entity(8, 8, "PLANT2"))
|
||||||
|
world.add_entity(Entity(9, 3, "PLANT3"))
|
||||||
|
return world
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app = Main()
|
||||||
|
app.run()
|
||||||
|
Before Width: | Height: | Size: 621 B After Width: | Height: | Size: 621 B |
Before Width: | Height: | Size: 765 B After Width: | Height: | Size: 765 B |
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 411 B |
Before Width: | Height: | Size: 584 B After Width: | Height: | Size: 584 B |
BIN
media/sprites/peel.webp
Normal file
After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
BIN
media/sprites/tile.jpeg
Normal file
After Width: | Height: | Size: 87 KiB |
BIN
media/sprites/tile_cropped.jpeg
Normal file
After Width: | Height: | Size: 112 KiB |
BIN
media/sprites/vacuum.png
Normal file
After Width: | Height: | Size: 122 KiB |
BIN
media/sprites/wall.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
113
view/renderer.py
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
import random
|
||||||
|
from random import randint
|
||||||
|
|
||||||
|
import pygame
|
||||||
|
from pygame import Color
|
||||||
|
|
||||||
|
from domain.entities.cat import Cat
|
||||||
|
from domain.entities.entity import Entity
|
||||||
|
from domain.world import World
|
||||||
|
|
||||||
|
|
||||||
|
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("media/sprites/cat/standing_front.png"),
|
||||||
|
(self.tile_width, self.tile_height)),
|
||||||
|
"CAT_BACK": pygame.transform.scale(pygame.image.load("media/sprites/cat/standing_back.png"),
|
||||||
|
(self.tile_width, self.tile_height)),
|
||||||
|
"CAT_LEFT": pygame.transform.scale(pygame.image.load("media/sprites/cat/standing_left.png"),
|
||||||
|
(self.tile_width, self.tile_height)),
|
||||||
|
"CAT_RIGHT": pygame.transform.scale(pygame.image.load("media/sprites/cat/standing_right.png"),
|
||||||
|
(self.tile_width, self.tile_height)),
|
||||||
|
"PLANT1": pygame.transform.scale(pygame.image.load("media/sprites/plants/plant1.png"),
|
||||||
|
(self.tile_width + self.tile_width / 4, self.tile_height + self.tile_height / 4)),
|
||||||
|
"PLANT2": pygame.transform.scale(pygame.image.load("media/sprites/plants/plant2.png"),
|
||||||
|
(self.tile_width + self.tile_width / 4, self.tile_height + self.tile_height / 4)),
|
||||||
|
"PLANT3": pygame.transform.scale(pygame.image.load("media/sprites/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")
|