diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..72f2b55 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.analysis.extraPaths": [ + "./Animals" + ] +} \ No newline at end of file diff --git a/animal.py b/Animals/animal.py similarity index 97% rename from animal.py rename to Animals/animal.py index 32dbac8..a4a40bc 100644 --- a/animal.py +++ b/Animals/animal.py @@ -1,5 +1,5 @@ import pygame -from abc import ABC, abstractmethod +from abc import abstractmethod class Animal: def __init__(self, x, y,name, image, food_image, food, environment, adult=False,): diff --git a/animals.py b/Animals/animals.py similarity index 77% rename from animals.py rename to Animals/animals.py index ccb5e54..b228a52 100644 --- a/animals.py +++ b/Animals/animals.py @@ -36,4 +36,12 @@ def create_animals(): penguin1, penguin2, penguin3, penguin4, parrot1, parrot2, parrot3, parrot4, parrot5] - return Animals \ No newline at end of file + return Animals + +def draw_Animals(Animals, const): + for Animal in Animals: + Animal.draw(const.screen, const.GRID_SIZE) + if Animal.feed() == 'True': + Animal.draw_exclamation(const.screen, const.GRID_SIZE, Animal.x, Animal.y) + else: + Animal.draw_food(const.screen,const.GRID_SIZE,Animal.x,Animal.y) \ No newline at end of file diff --git a/bear.py b/Animals/bear.py similarity index 95% rename from bear.py rename to Animals/bear.py index e0aafe3..3eb5c65 100644 --- a/bear.py +++ b/Animals/bear.py @@ -2,8 +2,6 @@ from animal import Animal import pygame from datetime import datetime - - class Bear(Animal): def __init__(self, x, y, adult=False): Bear_image = pygame.image.load('images/bear.png') diff --git a/elephant.py b/Animals/elephant.py similarity index 100% rename from elephant.py rename to Animals/elephant.py diff --git a/giraffe.py b/Animals/giraffe.py similarity index 100% rename from giraffe.py rename to Animals/giraffe.py diff --git a/parrot.py b/Animals/parrot.py similarity index 100% rename from parrot.py rename to Animals/parrot.py diff --git a/penguin.py b/Animals/penguin.py similarity index 100% rename from penguin.py rename to Animals/penguin.py diff --git a/agent.py b/agent.py index 0e0359a..19734b3 100644 --- a/agent.py +++ b/agent.py @@ -31,8 +31,6 @@ class Agent: elif event.key == pygame.K_RIGHT: self.move('Turn Right', max_x, max_y, obstacles, animals) - # feed_animal(self, animals) - def move(self, action, max_x, max_y, obstacles, animals, goal): if action == 'Go Forward': new_x, new_y = self.x, self.y diff --git a/constants.py b/constants.py new file mode 100644 index 0000000..2796685 --- /dev/null +++ b/constants.py @@ -0,0 +1,15 @@ +import pygame + +class Constants: + def __init__(self): + self.BLACK = (0, 0, 0) + self.RED = (255, 0, 0) + self.GRID_SIZE = 50 + self.GRID_WIDTH = 30 + self.GRID_HEIGHT = 15 + self.WINDOW_SIZE = (self.GRID_WIDTH * self.GRID_SIZE, self.GRID_HEIGHT * self.GRID_SIZE) + self.background_image = pygame.transform.scale(pygame.image.load('images/tło.jpg'), self.WINDOW_SIZE) + +def init_pygame(const): + pygame.init() + const.screen = pygame.display.set_mode(const.WINDOW_SIZE) \ No newline at end of file diff --git a/draw.py b/draw.py new file mode 100644 index 0000000..f032281 --- /dev/null +++ b/draw.py @@ -0,0 +1,14 @@ +import pygame + +def draw_goal(const, goal): + x, y = goal + rect = (x * const.GRID_SIZE, y * const.GRID_SIZE, const.GRID_SIZE, const.GRID_SIZE) + pygame.draw.rect(const.screen, const.RED, rect) + pygame.display.flip() + pygame.time.delay(2000) + +def draw_grid(const): + for y in range(0, const.GRID_HEIGHT * const.GRID_SIZE, const.GRID_SIZE): + for x in range(0, const.GRID_WIDTH * const.GRID_SIZE, const.GRID_SIZE): + rect = pygame.Rect(x, y, const.GRID_SIZE, const.GRID_SIZE) + pygame.draw.rect(const.screen, const.BLACK, rect, 1) \ No newline at end of file diff --git a/enclosure.py b/enclosure.py index 5036766..723e243 100644 --- a/enclosure.py +++ b/enclosure.py @@ -50,7 +50,6 @@ class Enclosure: if (self.x2, j) != (gate_x1, gate_y1) and (self.x2, j) != (gate_x2, gate_y2): screen.blit(self.imageV, (self.x2 * grid_size, j * grid_size)) - def create_enclosures(): fenceH = pygame.image.load('images/fenceHor.png') fenceV = pygame.image.load('images/fenceVer.png') @@ -65,3 +64,11 @@ def create_enclosures(): Enclosures = [en1, en2, en3, en4, en5] return Enclosures + +def draw_enclosures(Enclosures, const): + for enclosure in Enclosures: + enclosure.draw(const.screen, const.GRID_SIZE) + +def draw_gates(Enclosures, const): + for enclosure in Enclosures: + enclosure.gatebuild(const.screen, const.GRID_SIZE) \ No newline at end of file diff --git a/main.py b/main.py index 531701c..8246fde 100644 --- a/main.py +++ b/main.py @@ -1,97 +1,40 @@ -from enum import Enum import random import pygame import sys -from animals import create_animals +import sys +sys.path.append('./Animals') +from animals import create_animals, draw_Animals from agent import Agent -from enclosure import Enclosure, create_enclosures +from enclosure import create_enclosures, draw_enclosures, draw_gates from spawner import Spawner -from state_space_search import graphsearch -from terrain_obstacle import Terrain_Obstacle +from state_space_search import graphsearch, generate_cost_map +from terrain_obstacle import create_obstacles, draw_Terrain_Obstacles +from constants import Constants, init_pygame +from draw import draw_goal, draw_grid -BLACK = (0, 0, 0) -RED = (255, 0, 0) - -GRID_SIZE = 50 -GRID_WIDTH = 30 -GRID_HEIGHT = 15 - -pygame.init() - -WINDOW_SIZE = (GRID_WIDTH * GRID_SIZE, GRID_HEIGHT * GRID_SIZE) -screen = pygame.display.set_mode(WINDOW_SIZE) +const = Constants() +init_pygame(const) pygame.display.set_caption("Mini Zoo") - - -background_image = pygame.image.load('images/tło.jpg') -background_image = pygame.transform.scale(background_image, WINDOW_SIZE) -fenceH = pygame.image.load('images/fenceHor.png') -fenceV = pygame.image.load('images/fenceVer.png') -gate = pygame.image.load('images/gate.png') -puddle_image = pygame.image.load('images/puddle.png') -bush_image = pygame.image.load('images/bush.png') obstacles = set() animals_position = set() terrain_obstacles_position = set() Animals = create_animals() Enclosures = create_enclosures() - -puddle1 = Terrain_Obstacle(0,0,'puddle', puddle_image) -puddle2 = Terrain_Obstacle(0,0,'puddle', puddle_image) -puddle3 = Terrain_Obstacle(0,0,'puddle', puddle_image) -puddle4 = Terrain_Obstacle(0,0,'puddle', puddle_image) -puddle5 = Terrain_Obstacle(0,0,'puddle', puddle_image) -puddle6 = Terrain_Obstacle(0,0,'puddle', puddle_image) -puddle7 = Terrain_Obstacle(0,0,'puddle', puddle_image) -bush1 = Terrain_Obstacle(0,0,'bush', bush_image) -bush2 = Terrain_Obstacle(0,0,'bush', bush_image) -bush3 = Terrain_Obstacle(0,0,'bush', bush_image) -bush4 = Terrain_Obstacle(0,0,'bush', bush_image) -bush5 = Terrain_Obstacle(0,0,'bush', bush_image) -Terrain_Obstacles = [puddle1, puddle2, puddle3, puddle4, puddle5, puddle6, puddle7, bush1, bush2, bush3, bush4, bush5] - -def draw_grid(): - for y in range(0, GRID_HEIGHT * GRID_SIZE, GRID_SIZE): - for x in range(0, GRID_WIDTH * GRID_SIZE, GRID_SIZE): - rect = pygame.Rect(x, y, GRID_SIZE, GRID_SIZE) - pygame.draw.rect(screen, BLACK, rect, 1) - -def draw_enclosures(): - for enclosure in Enclosures: - enclosure.draw(screen, GRID_SIZE) - -def draw_gates(): - for enclosure in Enclosures: - enclosure.gatebuild(screen, GRID_SIZE) - -def draw_Animals(): - for Animal in Animals: - Animal.draw(screen, GRID_SIZE) - if Animal.feed() == 'True': - Animal.draw_exclamation(screen, GRID_SIZE, Animal.x, Animal.y) - else: - Animal.draw_food(screen,GRID_SIZE,Animal.x,Animal.y) +Terrain_Obstacles = create_obstacles() def spawn_all_animals(): for Animal in Animals: spawner1 = Spawner(Animal) spawner1.spawn_animal(obstacles, animals_position, Enclosures) -def draw_Terrain_Obstacles(): - for terrain_obstacle in Terrain_Obstacles: - terrain_obstacle.draw(screen, GRID_SIZE) - def spawn_obstacles(): for terrain_obstacle in Terrain_Obstacles: - spawner2= Spawner(terrain_obstacle) - spawner2.spawn_terrain_obstacles(obstacles,animals_position, terrain_obstacles_position, GRID_WIDTH, GRID_HEIGHT) + spawner2 = Spawner(terrain_obstacle) + spawner2.spawn_terrain_obstacles(obstacles, animals_position, terrain_obstacles_position, const.GRID_WIDTH, const.GRID_HEIGHT) - -obstacles = set() def generate_obstacles(): - for en in Enclosures: # Pobierz współrzędne bramy gate_x, gate_y = en.gate1 @@ -124,196 +67,50 @@ def generate_obstacles(): return obstacles -cost_map = {} -def generate_cost_map(): - adult_animal_cost = 10 - baby_animal_cost = 5 - puddle_cost = 50 - bush_cost = 20 - wall_cost = 1000 - for animal in Animals: - if animal.adult: - cost_map[(animal.x + 1, animal.y + 1)] = baby_animal_cost - cost_map[(animal.x + 1, animal.y)] = baby_animal_cost - cost_map[(animal.x, animal.y + 1)] = baby_animal_cost - cost_map[(animal.x, animal.y)] = adult_animal_cost - else: - cost_map[(animal.x, animal.y)] = baby_animal_cost - - for terrain_obstacle in Terrain_Obstacles: - if terrain_obstacle.type == 'puddle': - cost_map[(terrain_obstacle.x , terrain_obstacle.y )] = puddle_cost - else: - cost_map[(terrain_obstacle.x , terrain_obstacle.y )] = bush_cost - - for wall in Walls: - cost_map[wall[0], wall[1]] = wall_cost - # Inne pola z różnym kosztem - # cost_map[(x, y)] = cost_value - -# region Fields Tests -available_fields_small = set() -available_fields_large = set() - -WHITE = (255,255,255) -GREEN = (0, 255, 0) -YELLOW = (255, 255, 0) -BLACK = (0, 0, 0) - -def generate_available_fields(): - for enclosure in Enclosures: - for x in range(enclosure.x1 + 1, enclosure.x2): - for y in range(enclosure.y1 + 1, enclosure.y2): - field = (x, y) - if field not in obstacles: - available_fields_small.add(field) - if x < enclosure.x2 - 1 and y < enclosure.y2 - 1: - available_fields_large.add(field) - -def draw_fields(fields, color): - for field in fields: - x, y = field - pygame.draw.rect(screen, color, (x * GRID_SIZE, y * GRID_SIZE, GRID_SIZE, GRID_SIZE)) - -def available_fields_tests(): - obstacles = generate_obstacles() - - while True: - screen.fill(WHITE) - - draw_grid() - draw_fields(obstacles, BLACK) - - generate_available_fields() - - draw_fields(available_fields_small, GREEN) - draw_fields(available_fields_large, YELLOW) - - # Odświeżenie ekranu - pygame.display.flip() -# endregion - -# region Main Code def main(): - initial_state = (0,0,'S') - agent = Agent(initial_state, 'images/agent1.png', GRID_SIZE) + initial_state = (0, 0, 'S') + agent = Agent(initial_state, 'images/agent1.png', const.GRID_SIZE) obstacles = generate_obstacles() actions = [] clock = pygame.time.Clock() - spawned = False + while True: - # Manualne poruszanie agentem for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() - agent.handle_event(event, GRID_WIDTH, GRID_HEIGHT, Animals, obstacles) + agent.handle_event(event, const.GRID_WIDTH, const.GRID_HEIGHT, Animals, obstacles) + + const.screen.blit(const.background_image, (0, 0)) + draw_grid(const) + draw_enclosures(Enclosures, const) + draw_gates(Enclosures, const) - screen.blit(background_image,(0,0)) - draw_grid() - draw_enclosures() - draw_gates() if not spawned: spawn_all_animals() spawn_obstacles() - generate_cost_map() + cost_map = generate_cost_map(Animals, Terrain_Obstacles) for animal in Animals: - animal._feed = 2 # Ustawienie aby zwierzę było głodne + animal._feed = 2 # Ustawienie, aby zwierzę było głodne spawned = True - draw_Animals() - draw_Terrain_Obstacles() - agent.draw(screen, GRID_SIZE) + + draw_Animals(Animals, const) + draw_Terrain_Obstacles(Terrain_Obstacles, const) + agent.draw(const.screen, const.GRID_SIZE) pygame.display.flip() clock.tick(10) if actions: action = actions.pop(0) - agent.move(action, GRID_WIDTH, GRID_HEIGHT, obstacles, Animals, goal) + agent.move(action, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, Animals, goal) pygame.time.wait(200) else: animal = random.choice(Animals) goal = (animal.x, animal.y) - - # --- Zaznaczenie celu --- - pygame.draw.rect(screen, RED, (animal.x * GRID_SIZE, animal.y * GRID_SIZE, GRID_SIZE, GRID_SIZE)) - pygame.display.flip() - pygame.time.delay(2000) - # ------------------------ - - actions = graphsearch(agent.istate, goal, GRID_WIDTH, GRID_HEIGHT, obstacles, cost_map) -# endregion - -Walls = [] -# region A* Test -from elephant import Elephant -puddle1 = Terrain_Obstacle(15,8,'puddle', puddle_image) -bush1 = Terrain_Obstacle(15,6,'bush', bush_image) -animal = Elephant(15, 10) -animal1 = Elephant(14, 10) -animal2 = Elephant(13, 10) -animal3 = Elephant(12, 10) -animal4 = Elephant(11, 10) -#Animals = [animal, animal1, animal2, animal3, animal4] *Uncomment to test A* -#Terrain_Obstacles = [puddle1, bush1] *Uncomment to test A* - -empty_rows = [5, 7, 9] - -def generate_test_walls(): - for x in range(4,26): - for y in range(0, 15): - if y not in empty_rows: - Walls.append((x, y)) - return Walls - -def draw_test_walls(): - for wall in generate_test_walls(): - pygame.draw.rect(screen, BLACK, (wall[0] * GRID_SIZE, wall[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE)) - -def a_star_testing(): - initial_state = (0, 7, 'E') - agent = Agent(initial_state, 'images/agent1.png', GRID_SIZE) - goal = (29, 7) - - actions = [] - clock = pygame.time.Clock() - - generated = False - while True: - screen.fill(WHITE) - draw_grid() - draw_test_walls() - draw_Terrain_Obstacles() - draw_Animals() - if not generated: - generate_cost_map() - - agent.draw(screen, GRID_SIZE) - pygame.draw.rect(screen, RED, (goal[0] * GRID_SIZE, goal[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE)) - - pygame.display.flip() - clock.tick(10) - - if actions: - action = actions.pop(0) - agent.move(action, GRID_WIDTH, GRID_HEIGHT, obstacles, Animals, goal) - pygame.time.wait(100) - else: - actions = graphsearch(agent.istate, goal, GRID_WIDTH, GRID_HEIGHT, obstacles, cost_map) -# endregion - -class DebugMode(Enum): - MAIN = 1 - AVAILABLE_FIELDS = 2 - A_STAR_TESTING = 3 + draw_goal(const, goal) + actions = graphsearch(agent.istate, goal, const.GRID_WIDTH, const.GRID_HEIGHT, obstacles, cost_map) if __name__ == "__main__": - debug_mode = DebugMode.MAIN - - if debug_mode == DebugMode.MAIN: - main() - elif debug_mode == DebugMode.AVAILABLE_FIELDS: - available_fields_tests() - elif debug_mode == DebugMode.A_STAR_TESTING: - a_star_testing() \ No newline at end of file + main() \ No newline at end of file diff --git a/spawner.py b/spawner.py index bcc2fa4..29f89eb 100644 --- a/spawner.py +++ b/spawner.py @@ -4,8 +4,6 @@ class Spawner: def __init__(self, entity): self.entity = entity - - def spawn_animal(self, blocked, taken, enclosures): self.enclosures = [enclosure for enclosure in enclosures if enclosure.type == self.entity.environment] # Wyrażenie listowe filtrujące tylko te wybiegi, które pasują do środowiska zwierzęcia @@ -19,9 +17,11 @@ class Spawner: self.entity.x = random.randint(enclosure.x1+1, enclosure.x2) self.entity.y = random.randint(enclosure.y1+1, enclosure.y2) - if self.check(blocked, taken): + if self.check(blocked | {(8,5),(3,10),(15,2),(26,2),(11,4),(15,7),(22,4),(24,10),(11,12),(19,12)}, taken): break + def spawn_terrain_obstacles(self, blocked1, blocked2, taken, grid_width, grid_height): + blocked1 = blocked1 | {(8,5),(3,10),(15,2),(26,2),(11,4),(15,7),(22,4),(24,10),(11,12),(19,12)} while True: self.entity.x = random.randint(0, grid_width - 1) self.entity.y = random.randint(0, grid_height - 1) @@ -50,5 +50,4 @@ class Spawner: else: taken.add((x,y)) - return True - + return True \ No newline at end of file diff --git a/state_space_search.py b/state_space_search.py index dea6e6f..7aae41e 100644 --- a/state_space_search.py +++ b/state_space_search.py @@ -89,15 +89,39 @@ def current_cost(node, cost_map): _, parent, action = node # Dodaj koszt pola z mapy kosztów tylko jeśli akcja to "Forward" if action == 'Go Forward': - #if True: state, _, _ = node cost += cost_map.get(state[:2], DEFAULT_COST_VALUE) # Pobiera koszt przejścia przez dane pole, a jeśli koszt nie jest zdefiniowany to bierze wartość domyślną + if action == 'Turn Right' or action == 'Turn Left': - cost += 1 + cost += DEFAULT_COST_VALUE + node = parent # Przejdź do rodzica return cost def heuristic(state, goal): x, y, _ = state goal_x, goal_y = goal - return abs(x - goal_x) + abs(y - goal_y) # Odległość Manhattana do celu \ No newline at end of file + return abs(x - goal_x) + abs(y - goal_y) # Odległość Manhattana do celu + +def generate_cost_map(Animals, Terrain_Obstacles, cost_map={}): + adult_animal_cost = 15 # Default : 15 + baby_animal_cost = 10 # Default : 10 + puddle_cost = 50 # Default : 50 + bush_cost = 20 # Default : 20 + + for animal in Animals: + if animal.adult: + cost_map[(animal.x + 1, animal.y + 1)] = adult_animal_cost + cost_map[(animal.x + 1, animal.y)] = adult_animal_cost + cost_map[(animal.x, animal.y + 1)] = adult_animal_cost + cost_map[(animal.x, animal.y)] = adult_animal_cost + else: + cost_map[(animal.x, animal.y)] = baby_animal_cost + + for terrain_obstacle in Terrain_Obstacles: + if terrain_obstacle.type == 'puddle': + cost_map[(terrain_obstacle.x , terrain_obstacle.y )] = puddle_cost + else: + cost_map[(terrain_obstacle.x , terrain_obstacle.y )] = bush_cost + + return cost_map \ No newline at end of file diff --git a/terrain_obstacle.py b/terrain_obstacle.py index 28bf83c..9e00d61 100644 --- a/terrain_obstacle.py +++ b/terrain_obstacle.py @@ -1,5 +1,4 @@ import pygame -from abc import ABC, abstractmethod class Terrain_Obstacle: def __init__(self, x, y, type , image): @@ -10,4 +9,29 @@ class Terrain_Obstacle: def draw(self, screen, grid_size): scaled_image = pygame.transform.scale(self.image, (grid_size, grid_size)) - screen.blit(scaled_image, (self.x * grid_size, self.y * grid_size)) \ No newline at end of file + screen.blit(scaled_image, (self.x * grid_size, self.y * grid_size)) + +def create_obstacles(): + puddle_image = pygame.image.load('images/puddle.png') + bush_image = pygame.image.load('images/bush.png') + + puddle1 = Terrain_Obstacle(0,0,'puddle', puddle_image) + puddle2 = Terrain_Obstacle(0,0,'puddle', puddle_image) + puddle3 = Terrain_Obstacle(0,0,'puddle', puddle_image) + puddle4 = Terrain_Obstacle(0,0,'puddle', puddle_image) + puddle5 = Terrain_Obstacle(0,0,'puddle', puddle_image) + puddle6 = Terrain_Obstacle(0,0,'puddle', puddle_image) + puddle7 = Terrain_Obstacle(0,0,'puddle', puddle_image) + bush1 = Terrain_Obstacle(0,0,'bush', bush_image) + bush2 = Terrain_Obstacle(0,0,'bush', bush_image) + bush3 = Terrain_Obstacle(0,0,'bush', bush_image) + bush4 = Terrain_Obstacle(0,0,'bush', bush_image) + bush5 = Terrain_Obstacle(0,0,'bush', bush_image) + + Terrain_Obstacles = [puddle1, puddle2, puddle3, puddle4, puddle5, puddle6, puddle7, bush1, bush2, bush3, bush4, bush5] + + return Terrain_Obstacles + +def draw_Terrain_Obstacles(Terrain_Obstacles, const): + for terrain_obstacle in Terrain_Obstacles: + terrain_obstacle.draw(const.screen, const.GRID_SIZE) \ No newline at end of file