├── .DS_Store ├── assets ├── .DS_Store ├── fox.png ├── tree.png ├── wolf.png ├── rabbit.png └── sources.txt ├── __pycache__ ├── gui.cpython-312.pyc ├── settings.cpython-312.pyc ├── simulation.cpython-312.pyc └── visualisation.cpython-312.pyc ├── main.py ├── settings.py ├── gui.py ├── visualisation.py ├── README.md └── simulation.py /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/.DS_Store -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/assets/.DS_Store -------------------------------------------------------------------------------- /assets/fox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/assets/fox.png -------------------------------------------------------------------------------- /assets/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/assets/tree.png -------------------------------------------------------------------------------- /assets/wolf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/assets/wolf.png -------------------------------------------------------------------------------- /assets/rabbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/assets/rabbit.png -------------------------------------------------------------------------------- /__pycache__/gui.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/__pycache__/gui.cpython-312.pyc -------------------------------------------------------------------------------- /__pycache__/settings.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/__pycache__/settings.cpython-312.pyc -------------------------------------------------------------------------------- /__pycache__/simulation.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/__pycache__/simulation.cpython-312.pyc -------------------------------------------------------------------------------- /__pycache__/visualisation.cpython-312.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YPAndrew0907/Animal-Simulation-game/HEAD/__pycache__/visualisation.cpython-312.pyc -------------------------------------------------------------------------------- /assets/sources.txt: -------------------------------------------------------------------------------- 1 | Fox : 2 | Fox icons created by Freepik - Flaticon 3 | 4 | Wolf : 5 | Wolf icons created by Freepik - Flaticon 6 | 7 | Rabbit : 8 | Rabbit icons created by Freepik - Flaticon 9 | 10 | Tree : 11 | Tree icons created by Freepik - Flaticon -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import simulation 3 | from settings import * 4 | from simulation import Simulation 5 | from visualisation import Visualisation 6 | 7 | pygame.init() 8 | 9 | simulation = Simulation() 10 | visualisation = Visualisation() 11 | 12 | if __name__ == "__main__": 13 | print("Press to run one step, press to run continuously") 14 | while True: 15 | visualisation.draw(simulation) 16 | keys_just_pressed = visualisation.events() 17 | keys_pressed = pygame.key.get_pressed() 18 | if keys_pressed[pygame.K_UP]: 19 | simulation.run_simulation_one_step() 20 | if pygame.K_SPACE in keys_just_pressed: 21 | simulation.run_simulation_one_step() 22 | visualisation.update() 23 | -------------------------------------------------------------------------------- /settings.py: -------------------------------------------------------------------------------- 1 | from pygame import font 2 | # Screen 3 | WINDOW_NAME = "Nature simulation v1" 4 | VISUALISATION_WIDTH = 888 5 | VISUALISATION_HEIGHT = 888 6 | VISUALISATION_PANEL_WIDTH = 240 7 | 8 | # Constants for different entities 9 | TREE = '🌳' 10 | RABBIT = '🐇' 11 | FOX = '🦊' 12 | WOLF = '🐺' 13 | EMPTY = ' ' 14 | 15 | # Grid dimensions 16 | WIDTH = 60 17 | HEIGHT = 60 18 | 19 | # Colors 20 | WHITE = (255, 255, 255) 21 | LIGHT_WHITE = (222, 222, 222) 22 | BLACK = (0, 0, 0) 23 | GREY = (83, 92, 104) 24 | ORANGE = (240, 147, 43) 25 | YELLOW = (249, 202, 36) 26 | GREEN = (106, 176, 76) 27 | LIGHT_GREEN = (186, 220, 88) 28 | LIGHT_BLUE = (126, 214, 223) 29 | DARK_BLUE = (48, 51, 107) 30 | 31 | # Fonts 32 | font.init() 33 | SMALL_FONT = font.SysFont("coopbl", 34) 34 | MEDIUM_FONT = font.SysFont("coopbl", 42) 35 | BIG_FONT = font.SysFont("coopbl", 52) 36 | 37 | -------------------------------------------------------------------------------- /gui.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | from settings import * 3 | 4 | def draw_text(surface, text, x, y, font=MEDIUM_FONT, color=WHITE, shadow=BLACK): 5 | text_obj = font.render(text, 1, color) 6 | if shadow: 7 | shadow_text_obj = font.render(text, 1, shadow) 8 | shadow_text_rect = shadow_text_obj.get_rect() 9 | shadow_text_rect.center = (x+2, y+2) 10 | surface.blit(shadow_text_obj, shadow_text_rect) 11 | text_rect = text_obj.get_rect() 12 | text_rect.center = (x, y) 13 | surface.blit(text_obj, text_rect) 14 | 15 | is_mouse_just_clicked = False 16 | def button(surface, text, x, y, font=BIG_FONT, width=160, height=60, color=LIGHT_WHITE, 17 | highlight_color=WHITE, text_color=BLACK, text_shadow=False, elevation=6, button_shadow=BLACK): 18 | global is_mouse_just_clicked 19 | rect = pygame.Rect(x-width//2, y-height//2, width, height) 20 | mouse_pos = pygame.mouse.get_pos() 21 | mouse_clicked = pygame.mouse.get_pressed()[0] 22 | if mouse_clicked == False: 23 | is_mouse_just_clicked = False 24 | draw_shadow = True 25 | if rect.collidepoint(mouse_pos): 26 | color = highlight_color 27 | if mouse_clicked and is_mouse_just_clicked == False: 28 | is_mouse_just_clicked = True 29 | return True 30 | if mouse_clicked: 31 | rect.x -= elevation 32 | rect.y -= elevation 33 | x -= elevation 34 | y -= elevation 35 | draw_shadow = False 36 | if draw_shadow: 37 | pygame.draw.rect(surface, button_shadow, (rect.x-elevation, rect.y-elevation, rect.w, rect.h), border_radius=16) 38 | 39 | pygame.draw.rect(surface, color, rect, border_radius=16) 40 | draw_text(surface, text, x, y, font, text_color, text_shadow) 41 | -------------------------------------------------------------------------------- /visualisation.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import sys 3 | from settings import * 4 | import gui 5 | 6 | def load_image(path, width, height): 7 | image = pygame.image.load(path).convert_alpha() 8 | image = pygame.transform.smoothscale(image, (width, height)) 9 | return image 10 | 11 | class Visualisation: 12 | def __init__(self): 13 | self.mainClock = pygame.time.Clock() 14 | self.SCREEN = pygame.display.set_mode((VISUALISATION_WIDTH + VISUALISATION_PANEL_WIDTH, VISUALISATION_HEIGHT), 0, 32) 15 | pygame.display.set_caption(WINDOW_NAME) 16 | 17 | self.cell_width = VISUALISATION_WIDTH // WIDTH 18 | self.cell_height = VISUALISATION_HEIGHT // HEIGHT 19 | 20 | # images 21 | self.tree_image = load_image("assets/tree.png", self.cell_width, self.cell_height) 22 | self.rabbit_image = load_image("assets/rabbit.png", self.cell_width//1.2, self.cell_height//1.2) 23 | self.fox_image = load_image("assets/fox.png", self.cell_width, self.cell_height) 24 | self.wolf_image = load_image("assets/wolf.png", self.cell_width, self.cell_height) 25 | 26 | self.transition_speed = 1000 # ms 27 | self.start_transition_time = 0 28 | 29 | 30 | 31 | def draw_panel(self, simulation): 32 | # draw the background 33 | pygame.draw.rect(self.SCREEN, DARK_BLUE, (VISUALISATION_WIDTH, 0, VISUALISATION_PANEL_WIDTH, VISUALISATION_HEIGHT)) 34 | text_x_pos = VISUALISATION_WIDTH + VISUALISATION_PANEL_WIDTH // 2 35 | # draw the title 36 | gui.draw_text(self.SCREEN, WINDOW_NAME, text_x_pos, 50, font=SMALL_FONT, color=YELLOW) 37 | # draw the stats 38 | gui.draw_text(self.SCREEN, f"Step: {simulation.step}", text_x_pos, 150) 39 | pygame.draw.line(self.SCREEN, GREY, (VISUALISATION_WIDTH, 190), (VISUALISATION_WIDTH + VISUALISATION_PANEL_WIDTH, 190), 2) 40 | gui.draw_text(self.SCREEN, f"Trees: {len(simulation.trees)}", text_x_pos, 225, color=GREEN) 41 | gui.draw_text(self.SCREEN, f"Rabbits: {len(simulation.rabbits)}", text_x_pos, 260, color=WHITE) 42 | gui.draw_text(self.SCREEN, f"Foxes: {len(simulation.foxes)}", text_x_pos, 295, color=ORANGE) 43 | gui.draw_text(self.SCREEN, f"Wolves: {len(simulation.wolves)}", text_x_pos, 330, color=GREY) 44 | pygame.draw.line(self.SCREEN, GREY, (VISUALISATION_WIDTH, 370), (VISUALISATION_WIDTH + VISUALISATION_PANEL_WIDTH, 370), 2) 45 | 46 | # draw the buttons 47 | if gui.button(self.SCREEN, "Next", text_x_pos, 640): 48 | simulation.run_simulation_one_step() 49 | self.start_transition_time = pygame.time.get_ticks() 50 | 51 | 52 | 53 | def draw_simulation(self, simulation): 54 | pygame.draw.rect(self.SCREEN, LIGHT_GREEN, (0, 0, VISUALISATION_WIDTH, VISUALISATION_HEIGHT)) 55 | for y in range(HEIGHT): 56 | for x in range(WIDTH): 57 | cell = simulation.grid[y][x] 58 | rect = pygame.rect.Rect(x * self.cell_width, y * self.cell_height, self.cell_width, self.cell_height) 59 | center_rect = rect.center 60 | if cell == TREE: 61 | # pygame.draw.rect(self.SCREEN, GREEN, rect) 62 | self.SCREEN.blit(self.tree_image, self.tree_image.get_rect(center=center_rect)) 63 | pass 64 | elif cell == RABBIT: 65 | # pygame.draw.rect(self.SCREEN, WHITE, rect) 66 | self.SCREEN.blit(self.rabbit_image, self.rabbit_image.get_rect(center=center_rect)) 67 | pass 68 | elif cell == FOX: 69 | # pygame.draw.rect(self.SCREEN, ORANGE, rect) 70 | self.SCREEN.blit(self.fox_image, self.fox_image.get_rect(center=center_rect)) 71 | pass 72 | elif cell == WOLF: 73 | # pygame.draw.rect(self.SCREEN, GREY, rect) 74 | self.SCREEN.blit(self.wolf_image, self.wolf_image.get_rect(center=center_rect)) 75 | 76 | def draw(self, simulation): 77 | self.draw_simulation(simulation) 78 | self.draw_panel(simulation) 79 | 80 | 81 | def events(self): 82 | keys_pressed = [] 83 | for event in pygame.event.get(): 84 | if event.type == pygame.QUIT: 85 | pygame.quit() 86 | sys.exit() 87 | if event.type == pygame.KEYDOWN: 88 | if event.key == pygame.K_ESCAPE: 89 | pygame.quit() 90 | sys.exit() 91 | keys_pressed.append(event.key) 92 | return keys_pressed 93 | 94 | def update(self): 95 | pygame.display.update() 96 | self.mainClock.tick(90) 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Animal Simulation Game - Enhanced Operation Instructions 2 | 3 | ## Introduction 4 | 5 | Welcome to **Animal Simulation Game**, where you can explore a dynamic ecosystem of interacting animals and enjoy learning about nature's complexity! This guide aims to provide you with detailed instructions on how to play the game, understand its controls, and enhance your simulation experience. 6 | 7 | --- 8 | 9 | ## Table of Contents 10 | 1. [Getting Started](#getting-started) 11 | 2. [Game Controls](#game-controls) 12 | - [Step-by-Step Simulation](#step-by-step-simulation) 13 | - [Continuous Simulation](#continuous-simulation) 14 | 3. [What Happens in the Simulation](#what-happens-in-the-simulation) 15 | 4. [In-Game Tooltips](#in-game-tooltips) 16 | 5. [Additional Resources](#additional-resources) 17 | 18 | --- 19 | 20 | ## Getting Started 21 | 22 | 1. **Installation**: 23 | - Ensure you have Python installed on your system. 24 | - Install the required dependencies by running: 25 | ```bash 26 | pip install -r requirements.txt 27 | ``` 28 | - Start the game by running: 29 | ```bash 30 | python main.py 31 | ``` 32 | 33 | 2. **Objective**: 34 | - Observe and interact with a simulation of animals such as rabbits, foxes, and wolves in a dynamically evolving ecosystem. 35 | 36 | --- 37 | 38 | ## Game Controls 39 | 40 | Here’s a detailed breakdown of how to control the simulation: 41 | 42 | ### Step-by-Step Simulation 43 | - **Key**: Press `SPACE` 44 | - **What It Does**: Advances the simulation by **one step**, during which: 45 | - Animals **move** to a new position based on their behavior and environment. 46 | - Predators like foxes and wolves may **hunt** nearby prey (e.g., rabbits). 47 | - Animals consume resources (e.g., food) and their **health** updates accordingly. 48 | - Reproductive events or population changes may occur depending on environmental factors. 49 | 50 | ### Continuous Simulation 51 | - **Key**: Press `ENTER` 52 | - **What It Does**: Runs the simulation **continuously** in real-time, where: 53 | - The ecosystem evolves without user intervention. 54 | - You can observe interactions like hunting, movement, and population dynamics seamlessly. 55 | - Animals will continue to move, eat, and reproduce until stopped. 56 | - **Stop Continuous Simulation**: Press `ENTER` again to pause the simulation. 57 | 58 | ### Other Controls 59 | - **Reset Simulation**: Press `R` to reset the game to its initial state. 60 | - **Exit Game**: Press `ESC` to close the game. 61 | 62 | --- 63 | 64 | ## What Happens in the Simulation 65 | 66 | Here’s what you can expect during gameplay: 67 | 68 | - **Animal Movement**: 69 | - Rabbits move randomly to find food and avoid predators. 70 | - Foxes and wolves hunt prey, using strategic movements. 71 | - **Hunting & Survival**: 72 | - Predators hunt nearby prey based on proximity and energy levels. 73 | - Prey animals may escape based on speed and surroundings. 74 | - **Reproduction**: 75 | - Animals reproduce when conditions are favorable (e.g., sufficient food and health). 76 | - **Population Dynamics**: 77 | - Watch as populations rise and fall based on the balance of predators and prey. 78 | 79 | --- 80 | 81 | ## In-Game Tooltips 82 | 83 | To assist new players, we’ve added **in-game tooltips**: 84 | - When you start the game, a popup explains the controls. 85 | - Tooltips display instructions like: 86 | - **"Press SPACE to advance the simulation step-by-step."** 87 | - **"Press ENTER to run the simulation continuously."** 88 | 89 | --- 90 | 91 | ## Additional Resources 92 | 93 | - **Video/GIF Demonstration**: Check out our [demo video](#) to see the simulation in action! 94 | - **FAQs**: 95 | - What is a simulation step? 96 | - A single moment in time where animals act based on their programmed behaviors. 97 | - How do I stop a continuous simulation? 98 | - Press `ENTER` again to pause. 99 | 100 | --- 101 | 102 | We hope these detailed instructions make the game more enjoyable and accessible for all players. If you have any questions or ideas, feel free to open an issue or contribute to the project! 103 | 104 | Happy Simulating! 😊 105 | -------------------------------------------------------------------------------- /simulation.py: -------------------------------------------------------------------------------- 1 | import random 2 | import time 3 | import os 4 | import copy 5 | from collections import deque 6 | from settings import * 7 | 8 | class Simulation: 9 | def __init__(self): 10 | 11 | 12 | # Entity states 13 | self.rabbits = {} # Key: position (i, j), Value: hunger level 14 | self.foxes = {} # Key: position (i, j), Value: hunger level 15 | self.wolves = {} # Key: position (i, j), Value: hunger level 16 | 17 | self.trees = {} # Key: position (i, j), Value: regrowth timer 18 | 19 | self.grid = self.create_empty_grid(WIDTH, HEIGHT) 20 | self.populate_grid() 21 | 22 | self.step = 0 23 | 24 | def create_empty_grid(self, width, height): 25 | return [[EMPTY for _ in range(width)] for _ in range(height)] 26 | 27 | # Function to randomly populate the grid 28 | def populate_grid(self): 29 | # Place specific numbers of rabbits first 30 | placed_rabbits = 0 31 | 32 | while placed_rabbits < 1968: 33 | i, j = random.randint(0, HEIGHT - 1), random.randint(0, WIDTH - 1) 34 | if self.grid[i][j] == EMPTY: 35 | self.grid[i][j] = RABBIT 36 | self.rabbits[(i, j)] = 0 # Initial hunger level for rabbits 37 | placed_rabbits += 1 38 | 39 | # Now populate the rest of the grid randomly, including foxes and wolves 40 | # Make sure not to overwrite the rabbits you've already placed 41 | for i in range(HEIGHT): 42 | for j in range(WIDTH): 43 | if self.grid[i][j] == EMPTY: 44 | rand_num = random.random() 45 | if rand_num < 0.1: 46 | self.grid[i][j] = TREE 47 | elif rand_num < 0.12: 48 | self.grid[i][j] = FOX 49 | self.foxes[(i, j)] = 0 # Initial hunger level for foxes 50 | elif rand_num < 0.14: 51 | self.grid[i][j] = WOLF 52 | self.wolves[(i, j)] = 0 # Initial hunger level for wolves 53 | 54 | 55 | # Function to find nearest entity of a specific type 56 | def find_nearest(self, start_pos, entity_type): 57 | visited = set() 58 | queue = deque([start_pos]) 59 | 60 | while queue: 61 | current_pos = queue.popleft() 62 | i, j = current_pos 63 | 64 | # Check if the current position is the target entity 65 | if self.grid[i][j] == entity_type: 66 | return current_pos 67 | 68 | # Add adjacent positions to the queue 69 | for x, y in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]: 70 | if 0 <= x < HEIGHT and 0 <= y < WIDTH and (x, y) not in visited: 71 | visited.add((x, y)) 72 | queue.append((x, y)) 73 | 74 | # Return None if no entity found 75 | return None 76 | 77 | def reproduce_rabbits(self, position): 78 | # Check if the rabbit at the given position is not hungry 79 | if self.rabbits[position] == 0: 80 | # Find an adjacent empty cell for the new rabbit 81 | i, j = position 82 | adjacent_positions = [(i-1, j), (i+1, j), (i, j-1), (i, j+1)] 83 | random.shuffle(adjacent_positions) 84 | for new_i, new_j in adjacent_positions: 85 | if 0 <= new_i < HEIGHT and 0 <= new_j < WIDTH and self.grid[new_i][new_j] == EMPTY: 86 | self.grid[new_i][new_j] = RABBIT 87 | self.rabbits[(new_i, new_j)] = 0 # New rabbit is not hungry 88 | break 89 | 90 | 91 | 92 | def move_rabbits(self): 93 | new_rabbits = {} 94 | 95 | for position in list(self.rabbits.keys()): 96 | i, j = position 97 | 98 | # Check if the rabbit still exists at this position 99 | if self.grid[i][j] != RABBIT: 100 | continue 101 | 102 | # Determine the movement of the rabbit 103 | new_positions = [(i-1, j), (i+1, j), (i, j-1), (i, j+1)] 104 | random.shuffle(new_positions) # Randomize potential new positions 105 | 106 | moved = False 107 | for new_i, new_j in new_positions: 108 | if 0 <= new_i < HEIGHT and 0 <= new_j < WIDTH: 109 | if self.grid[new_i][new_j] == TREE: 110 | # Rabbit eats the tree and resets hunger 111 | self.rabbits[position] = 0 112 | self.grid[new_i][new_j] = RABBIT 113 | self.grid[i][j] = EMPTY 114 | new_rabbits[(new_i, new_j)] = self.rabbits[position] 115 | self.trees[(new_i, new_j)] = 3 # Set the regrowth timer for the tree 116 | moved = True 117 | break 118 | elif self.grid[new_i][new_j] == EMPTY: 119 | # Increase hunger if not eating 120 | self.rabbits[position] += 1 121 | self.grid[new_i][new_j] = RABBIT 122 | self.grid[i][j] = EMPTY 123 | new_rabbits[(new_i, new_j)] = self.rabbits[position] 124 | moved = True 125 | break 126 | 127 | # Reproduce if the rabbit has not moved and is not hungry 128 | if not moved: 129 | self.reproduce_rabbits(position) 130 | 131 | self.rabbits = new_rabbits 132 | 133 | def reproduce_rabbits(self, position): 134 | reproduction_hunger_threshold = 0 # Rabbits can reproduce when not hungry 135 | 136 | if self.rabbits.get(position, 0) <= reproduction_hunger_threshold: 137 | i, j = position 138 | adjacent_positions = [(i-1, j), (i+1, j), (i, j-1), (i, j+1)] 139 | 140 | for new_i, new_j in adjacent_positions: 141 | if 0 <= new_i < HEIGHT and 0 <= new_j < WIDTH and self.grid[new_i][new_j] == EMPTY: 142 | self.grid[new_i][new_j] = RABBIT 143 | self.rabbits[(new_i, new_j)] = 0 # New rabbit is not hungry 144 | 145 | 146 | def is_closer(self, pos1, pos2, reference): 147 | """ Check if pos1 is closer to the reference point than pos2. """ 148 | return self.distance(pos1, reference) < self.distance(pos2, reference) 149 | 150 | def distance(self, pos1, pos2): 151 | """ Calculate exact distance between two points. """ 152 | return ((pos1[0] - pos2[0])**2 + (pos1[1] - pos2[1])**2)**0.5 153 | 154 | 155 | 156 | def regrow_trees(self): 157 | trees_to_regrow = [] 158 | for position, timer in self.trees.items(): 159 | if timer > 0: 160 | self.trees[position] -= 1 161 | elif self.trees[position] == 0: 162 | # only regrow if the cell is empty 163 | if self.grid[position[0]][position[1]] == EMPTY: 164 | trees_to_regrow.append(position) 165 | 166 | for position in trees_to_regrow: 167 | i, j = position 168 | self.grid[i][j] = TREE 169 | del self.trees[position] # Remove the tree from the regrowth tracking 170 | 171 | 172 | def move_foxes(self): 173 | new_foxes = {} 174 | for position in list(self.foxes.keys()): 175 | i, j = position 176 | 177 | # Check if the fox still exists at this position 178 | if self.grid[i][j] != FOX: 179 | continue 180 | 181 | # Implement smarter hunting behavior 182 | nearest_rabbit = self.find_nearest(position, RABBIT) 183 | 184 | # Determine the best move for hunting 185 | if nearest_rabbit: 186 | best_move = None 187 | min_distance = float('inf') 188 | for x, y in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]: 189 | if 0 <= x < HEIGHT and 0 <= y < WIDTH and self.grid[x][y] in [EMPTY, RABBIT]: 190 | dist = self.distance((x, y), nearest_rabbit) 191 | if dist < min_distance: 192 | min_distance = dist 193 | best_move = (x, y) 194 | 195 | if best_move: 196 | new_i, new_j = best_move 197 | if self.grid[new_i][new_j] == RABBIT: 198 | self.foxes[position] = 0 # Reset hunger 199 | # Remove the eaten rabbit 200 | self.rabbits.pop(nearest_rabbit, None) 201 | else: 202 | self.foxes[position] += 1 # Increase hunger 203 | 204 | self.grid[i][j] = EMPTY 205 | self.grid[new_i][new_j] = FOX 206 | new_foxes[best_move] = self.foxes[position] 207 | else: 208 | # Move randomly if no rabbit is within range 209 | random_move = random.choice([(i-1, j), (i+1, j), (i, j-1), (i, j+1)]) 210 | if 0 <= random_move[0] < HEIGHT and 0 <= random_move[1] < WIDTH and self.grid[random_move[0]][random_move[1]] == EMPTY: 211 | self.grid[i][j] = EMPTY 212 | self.grid[random_move[0]][random_move[1]] = FOX 213 | new_foxes[random_move] = self.foxes[position] 214 | else: 215 | # Move randomly if no rabbit is found 216 | random_move = random.choice([(i-1, j), (i+1, j), (i, j-1), (i, j+1)]) 217 | if 0 <= random_move[0] < HEIGHT and 0 <= random_move[1] < WIDTH and self.grid[random_move[0]][random_move[1]] == EMPTY: 218 | self.grid[i][j] = EMPTY 219 | self.grid[random_move[0]][random_move[1]] = FOX 220 | new_foxes[random_move] = self.foxes[position] 221 | 222 | self.foxes = new_foxes 223 | 224 | def move_wolves(self): 225 | new_wolves = {} 226 | for position in list(self.wolves.keys()): 227 | i, j = position 228 | 229 | # Check if the wolf still exists at this position 230 | if self.grid[i][j] != WOLF: 231 | continue 232 | # Find nearest rabbit and fox 233 | nearest_rabbit = self.find_nearest(position, RABBIT) 234 | nearest_fox = self.find_nearest(position, FOX) 235 | # Determine the closest prey 236 | nearest_prey = None 237 | if nearest_rabbit and nearest_fox: 238 | if self.is_closer(nearest_rabbit, nearest_fox, position): 239 | nearest_prey = nearest_rabbit 240 | else: 241 | nearest_prey = nearest_fox 242 | elif nearest_rabbit: 243 | nearest_prey = nearest_rabbit 244 | elif nearest_fox: 245 | nearest_prey = nearest_fox 246 | 247 | # Move towards the nearest prey 248 | new_positions = [] 249 | if nearest_prey: 250 | for x, y in [(i-1, j), (i+1, j), (i, j-1), (i, j+1)]: 251 | if 0 <= x < HEIGHT and 0 <= y < WIDTH and self.grid[x][y] in [EMPTY, RABBIT, FOX]: 252 | if self.is_closer((x, y), position, nearest_prey): 253 | new_positions.append((x, y)) 254 | # Move wolf if possible 255 | if new_positions: 256 | new_pos = random.choice(new_positions) 257 | new_i, new_j = new_pos 258 | 259 | # Eat prey if present 260 | if self.grid[new_i][new_j] in [RABBIT, FOX]: 261 | self.wolves[position] = 0 # Reset hunger 262 | else: 263 | self.wolves[position] += 1 # Increase hunger 264 | 265 | self.grid[i][j] = EMPTY 266 | self.grid[new_i][new_j] = WOLF 267 | new_wolves[new_pos] = self.wolves[position] 268 | else: # the wolf can't move 269 | new_wolves[position] = self.wolves[position] 270 | self.wolves = new_wolves 271 | 272 | def display_grid(self): 273 | os.system('cls' if os.name == 'nt' else 'clear') # Clear the console for each new display 274 | for row in self.grid: 275 | print(' '.join(row)) 276 | print("\nSimulation Step Completed. Press Ctrl+C to stop.") 277 | 278 | def run_simulation_one_step(self): 279 | 280 | self.move_rabbits() 281 | self.move_foxes() 282 | self.move_wolves() 283 | self.regrow_trees() 284 | # self.display_grid() 285 | self.step += 1 286 | 287 | 288 | # if __name__ == "__main__": 289 | # run_simulation() 290 | --------------------------------------------------------------------------------