diff --git a/config.py b/config.py index 03e8b3e..c5bb251 100644 --- a/config.py +++ b/config.py @@ -30,7 +30,7 @@ SMILEY = 1 NO_CREATURES = 0 -SQUARES_ONE_SPECIES_WON = 1 +ONE_SMILEY_SPECIES_WON = 1 ALL_ZOMBIES = 2 ONLY_ZOMBIE_BOSS = 3 diff --git a/display_results.py b/display_results.py index 6ac5f9d..b43a5b8 100644 --- a/display_results.py +++ b/display_results.py @@ -7,14 +7,14 @@ import config @handle -def D_find_average_from_smileys(): +def D_find_average_from_smilies(): smilies_amount = len(smilies) write_text_eventual = '' averaged = { 'generation_n': round(fmean(smiley.generation_n for smiley in smilies)), - 'plant_preference': round(fmean(1 if smiley.food_preference == PLANTS else 0 for smiley in smilies)), # Percent of smileys preferring plants + 'plant_preference': round(fmean(1 if smiley.food_preference == PLANTS else 0 for smiley in smilies)), # Percent of smilies preferring plants 'vision_distance': round(fmean(smiley.vision_distance for smiley in smilies)), 'average_smiley_speed': round(fmean(smiley.speed for smiley in smilies)*SPEED_RATIO), 'procreation_threshold': round(fmean(smiley.procreation_threshold for smiley in smilies)), @@ -25,7 +25,7 @@ def D_find_average_from_smileys(): averaged_analysis = {} one_species_survived_line = 'One of the species has survived' - average_generation_number = f'The average generation number of the smileys of the survived species: {averaged["generation_n"]}' + average_generation_number = f'The average generation number of the smilies of the survived species: {averaged["generation_n"]}' def ending() -> str: # Handling the 's' at the end of the word 'descendant' return f"{smilies_amount} descendant{'s' if smilies_amount > 1 else ''} left" @@ -39,7 +39,7 @@ def ending() -> str: # Handling the 's' at the end of the word 'descendant' 'energy': INITIAL_ENERGY } - plant_preference_chance_text_turtle = round(sum(1 if smiley.food_preference == PLANTS else 0 for smiley in smilies)/smilies_amount*100) # Percent of smileys preferring plants + plant_preference_chance_text_turtle = round(sum(1 if smiley.food_preference == PLANTS else 0 for smiley in smilies)/smilies_amount*100) # Percent of smilies preferring plants for property in averaged: if property not in ('generation_n', 'descendants left', 'average_smiley_speed'): if property == 'plant_preference': @@ -66,9 +66,9 @@ def ending() -> str: # Handling the 's' at the end of the word 'descendant' def D_display_results(): match evolution_status.result: case config.NO_CREATURES: - write_text_eventual = 'Neither smileys nor zombies have survived' - case config.SQUARES_ONE_SPECIES_WON: - write_text_eventual = D_find_average_from_smileys() + write_text_eventual = 'Neither smilies nor zombies have survived' + case config.ONE_SMILEY_SPECIES_WON: + write_text_eventual = D_find_average_from_smilies() case config.ONLY_ZOMBIE_BOSS: write_text_eventual = 'Only the zombie boss has survived. This happens tremendously infrequently :)' case _: diff --git a/draw_creatures.py b/draw_creatures.py index cfc5b2b..7897406 100644 --- a/draw_creatures.py +++ b/draw_creatures.py @@ -1,10 +1,11 @@ +from ast import Return from tkinter import ARC, PhotoImage from tkinter.font import nametofont from time import time from config import * from zombies_images import * -from global_items import evolution_status, smilies, zombies +from global_items import window_commands, evolution_status, smilies, zombies, boss_shape_size, zombie_shape_size import global_items, config @@ -12,21 +13,21 @@ def create_boss_image(): ZOMBIE_BOSS_SIZE_RATIO = 27 # Higher => smaller # Equalizing the size of a smiley and the size of a zombie boss - global zombie_boss_shape, zombie_boss_width + global zombie_boss_shape zombie_boss_shape = PhotoImage(data=ZOMBIE_BOSS).subsample(ZOMBIE_BOSS_SIZE_RATIO, ZOMBIE_BOSS_SIZE_RATIO) - global_items.zombie_boss_half_height = zombie_boss_shape.height()/2 - zombie_boss_width = zombie_boss_shape.width() + + boss_shape_size['half_width'] = zombie_boss_shape.width()/2 + boss_shape_size['half_height'] = zombie_boss_shape.height()/2 def draw_zombie_boss(): global zombie_boss_shape - return global_items.canvas.create_image( - evolution_status.zombie_boss.x, evolution_status.zombie_boss.y, - image=zombie_boss_shape, tags='boss') + global_items.canvas.create_image( + evolution_status.zombie_boss.x, evolution_status.zombie_boss.y, + image=zombie_boss_shape, tags='boss') def update_zombie_boss_image(): - zombie_boss = evolution_status.zombie_boss global_items.canvas.delete('boss') - zombie_boss.image_reference = draw_zombie_boss() + draw_zombie_boss() # Demonstrating the fact that the zombie boss is sleeping/has wakened def draw_z(x, y): @@ -34,11 +35,10 @@ def draw_z(x, y): def draw_z_z_z(): '''Demonstrating the fact that the zombie boss is currently sleeping''' - global zombie_boss_width zombie_boss = evolution_status.zombie_boss if zombie_boss is None: return - x_base = zombie_boss.x+zombie_boss_width/2 + x_base = zombie_boss.x+boss_shape_size['half_width'] draw_z(x_base+2, zombie_boss.y-7) draw_z(x_base+7, zombie_boss.y-13) draw_z(x_base+12, zombie_boss.y-21) @@ -57,21 +57,23 @@ def create_zombies_image(): # Equalizing the size of a smiley and the size of a zombie global zombie_shape zombie_shape = PhotoImage(data=ZOMBIE).subsample(ZOMBIE_SIZE_RATIO, ZOMBIE_SIZE_RATIO) - global_items.zombie_half_height = zombie_shape.height()/2 + + zombie_shape_size['half_height'] = zombie_shape.height()/2 + zombie_shape_size['half_width'] = zombie_shape.width()/2 def draw_zombie(zombie): global zombie_shape - return global_items.canvas.create_image(zombie.x, zombie.y, image=zombie_shape, tags='zombie') + global_items.canvas.create_image(zombie.x, zombie.y, image=zombie_shape, tags='zombie') def update_zombie_images(): '''Erasing all of the zombies that have already been drawn and drawing new ones.''' global_items.canvas.delete('zombie') for zombie in zombies: - zombie.image_reference = draw_zombie(zombie) + draw_zombie(zombie) # Drawing the smilies def update_smiley_images(): - '''Erasing all of the smileys that have already been drawn and drawing new ones.''' + '''Erasing all of the smilies that have already been drawn and drawing new ones.''' global_items.canvas.delete('smiley') for smiley in smilies: smiley.image_reference = draw_smiley(smiley) @@ -191,11 +193,16 @@ def draw_smiley(smiley): smiley.stimulus_start = now # Storing the time when the stimulus was resolved to be displayed # The stimulus might be displayed in a succeeding tact + if time() > smiley.stimulus_start + DISPLAY_PERIOD\ + or window_commands['to-show-selected-property'] != 'Nothing'\ or smiley.stimulus_start == now: # The stimulus is not displayed if there was one-tact-long sleep between active states (not sleeping) + draw_sleeping_smiley(smiley_to_draw) else: draw_aggressive_smiley(smiley_to_draw) + + # The stimulus by smilies x0, y0 = smiley_to_draw.x+0.5*SMILEY_SIZE, smiley_to_draw.y-2.3*SMILEY_SIZE x1, y1 = x0+46, y0+28, # 46 and 28 have been chosen manually tags = ('stimulus', smiley_to_draw.smiley_tag, 'smiley') diff --git a/draw_non_creatures.py b/draw_non_creatures.py index 8b665ad..a7d4967 100644 --- a/draw_non_creatures.py +++ b/draw_non_creatures.py @@ -1,10 +1,10 @@ from time import time -from tkinter import LAST, S, N +from tkinter import LAST, S, N, E from config import * from zombies_images import * from crosses import crosses_list -from global_items import zombies, distance_between_objects, handle, smilies, plants, window_commands, evolution_status +from global_items import zombies, distance_between_objects, handle, smilies, plants, window_commands, evolution_status, boss_shape_size, zombie_shape_size import global_items # Plants @@ -101,8 +101,22 @@ def display_property(creature: object, text: str, exceeding_y=0): anchor=S ) +def display_collision_result(creature: object, decrease_x: int | float): + '''Displaying stuff related to the collision.''' + number = round(creature.collision.result) + sign = '+' if number >= 0 else '–' + text = f'{sign}{abs(number)}' + global_items.canvas.create_text( + creature.x-decrease_x-2, creature.y, + text=text, + tags='property', + anchor=E, + fill='blue' if sign == '+' else 'red' + ) + NONE = 'None' NEWLY_BORN_PERIOD = 4 # In seconds +COLLISION_RESULTS_DISPLAY_PERIOD = 1 # How long the stuff related to the collision shall be displaed @handle def D_handle_properties(): @@ -110,14 +124,18 @@ def D_handle_properties(): global_items.canvas.delete('property') global_items.canvas.delete('circle') + now = time() + for smiley in smilies: match window_commands['to-show-selected-property']: case 'Energy/health': display_property(creature=smiley, text=round(smiley.energy), exceeding_y=HALF_SMILEY_SIZE) + if now < smiley.collision.moment + COLLISION_RESULTS_DISPLAY_PERIOD: + display_collision_result(smiley, HALF_SMILEY_SIZE) case 'Speed': display_property(creature=smiley, text=round(smiley.speed*SPEED_RATIO), exceeding_y=HALF_SMILEY_SIZE) case '"Newly born" if newly born': - display_property(creature=smiley, text='Newly born' if time() <= smiley.birth_time + NEWLY_BORN_PERIOD and smiley.generation_n != 0 else '', exceeding_y=HALF_SMILEY_SIZE) + display_property(creature=smiley, text='Newly born' if now <= smiley.birth_time + NEWLY_BORN_PERIOD and smiley.generation_n != 0 else '', exceeding_y=HALF_SMILEY_SIZE) case 'Procreation threshold': display_property(creature=smiley, text=round(smiley.procreation_threshold), exceeding_y=HALF_SMILEY_SIZE) case 'Food preference': @@ -142,11 +160,13 @@ def D_handle_properties(): if not global_items.mask_exists: draw_one_vision_distance_circle(creature=zombie_boss) case 'Energy/health': - display_property(creature=zombie_boss, text=round(zombie_boss.health), exceeding_y=global_items.zombie_boss_half_height) + display_property(creature=zombie_boss, text=round(zombie_boss.health), exceeding_y=boss_shape_size['half_height']) + if now < zombie_boss.collision.moment + COLLISION_RESULTS_DISPLAY_PERIOD: + display_collision_result(zombie_boss, boss_shape_size['half_width']) case 'Speed': - display_property(creature=zombie_boss, text=round(zombie_boss.speed*SPEED_RATIO), exceeding_y=global_items.zombie_boss_half_height) + display_property(creature=zombie_boss, text=round(zombie_boss.speed*SPEED_RATIO), exceeding_y=boss_shape_size['half_height']) case _: - display_property(creature=zombie_boss, text=NONE, exceeding_y=global_items.zombie_boss_half_height) + display_property(creature=zombie_boss, text=NONE, exceeding_y=boss_shape_size['half_height']) # Writing for zombies for zombie in zombies: @@ -154,16 +174,18 @@ def D_handle_properties(): case 'Nothing': continue case 'Energy/health': - display_property(creature=zombie, text=round(zombie.health), exceeding_y=global_items.zombie_half_height) + display_property(creature=zombie, text=round(zombie.health), exceeding_y=zombie_shape_size['half_height']) + if now < zombie.collision.moment + COLLISION_RESULTS_DISPLAY_PERIOD: + display_collision_result(zombie, zombie_shape_size['half_width']) case 'Speed': - display_property(creature=zombie, text=round(zombie.speed*SPEED_RATIO), exceeding_y=global_items.zombie_half_height) + display_property(creature=zombie, text=round(zombie.speed*SPEED_RATIO), exceeding_y=zombie_shape_size['half_height']) case 'Vision distance': draw_one_vision_distance_circle(creature=zombie) case _: - display_property(creature=zombie, text=NONE, exceeding_y=global_items.zombie_half_height) + display_property(creature=zombie, text=NONE, exceeding_y=zombie_shape_size['half_height']) # Stimulus -STIMULUS_DELAY: int = 25 # Displaying the stimulus toward the zombie boss in STIMULUS_DELAY seconds of inaction +STIMULUS_DELAY: int = 15 # Displaying the stimulus toward the zombie boss in STIMULUS_DELAY seconds of inaction def set_stimulus_start_time(): # The stimulus is only displayed above the smilies which recently started sleeping @@ -179,7 +201,7 @@ def display_stimulus(): # This function is called every second zombie_boss = evolution_status.zombie_boss if zombie_boss is not None: global_items.canvas.create_text( - zombie_boss.x, zombie_boss.y+global_items.zombie_boss_half_height, + zombie_boss.x, zombie_boss.y+boss_shape_size['half_height'], text='Lazy lounger!', tags='stimulus', anchor=N) diff --git a/evolution.py b/evolution.py index 6a04303..4e8ae2c 100644 --- a/evolution.py +++ b/evolution.py @@ -1,6 +1,6 @@ from tkinter import DISABLED from config import * -from smileys_functions import create_zero_generation, D_delete_all_smileys +from smilies_functions import create_zero_generation, D_delete_all_smilies from mask import D_delete_mask, D_handle_mask from plants import create_initial_plants, create_plant_image, D_delete_all_plants from draw_non_creatures import update_plant_images @@ -35,7 +35,7 @@ def evolution(): evolution_status.description = DELETE_ERASE_EVERYTHING global_items.stimulus_on = False D_delete_mask() - D_delete_all_smileys() + D_delete_all_smilies() D_delete_all_zombies() D_delete_all_plants() D_delete_all_crosses() @@ -46,7 +46,7 @@ def evolution(): # Evolution prep evolution_status.description = EVOLUTION_PREPARATION - global_items.canvas.create_rectangle(2, 2, evolution_field['width']+1, evolution_field['height']+1) # Evolution field frame + global_items.canvas.create_rectangle(2, 2, evolution_field['width']+1, evolution_field['height']+1, tags='frame') # Evolution field frame create_zero_generation() create_initial_plants() update_smiley_images() diff --git a/evolution_functions.py b/evolution_functions.py index 1b4f108..cd85647 100644 --- a/evolution_functions.py +++ b/evolution_functions.py @@ -25,16 +25,16 @@ def one_evolution(): D_one_evolution_step() fps_clock.tick(FPS) - smileys_empty, zombies_empty = smilies == [], zombies == [] - if smileys_empty and not zombies_empty: + smilies_empty, zombies_empty = smilies == [], zombies == [] + if smilies_empty and not zombies_empty: evolution_status.result = ALL_ZOMBIES - elif smileys_empty and zombies_empty: + elif smilies_empty and zombies_empty: if evolution_status.zombie_boss is None: evolution_status.result = NO_CREATURES else: evolution_status.result = ONLY_ZOMBIE_BOSS elif zombies_empty and evolution_status.zombie_boss is None and len(set(smiley.species for smiley in smilies)) == 1: - evolution_status.result = SQUARES_ONE_SPECIES_WON + evolution_status.result = ONE_SMILEY_SPECIES_WON if evolution_status.result is not None: return @@ -62,6 +62,7 @@ def D_one_evolution_step(): delete_old_cross() update_cross_images() D_handle_properties() + global_items.canvas.tag_raise('frame', 'all') global_items.canvas.update() if window_commands['run/pause'] == PAUSE: evolution_status.description = PAUSED diff --git a/global_items.py b/global_items.py index 612cd44..aa9530d 100644 --- a/global_items.py +++ b/global_items.py @@ -20,6 +20,12 @@ def __init__(self): self.zombie_boss = None self.result = None +class Collision: + '''Data about a collision with a smiley or a plant''' + def __init__(self): + self.result = None # The amount of received/spent energy/health + self.moment = float('-inf') # The time when the collision took place + class Creature: # For the smilies, normal zombies and the zombie boss def __init__( self, @@ -31,6 +37,7 @@ def __init__( self.vision_distance = vision_distance self.speed = speed self.x, self.y = x, y + self.collision = Collision() class CreatureStatus: def __init__(self): @@ -65,6 +72,8 @@ def __init__( evolution_field = {} smilies_data ={} scales = {} +boss_shape_size = {} +zombie_shape_size = {} # Functions def delete_help_window(event=None): diff --git a/images.py b/images.py index c370f91..afba834 100644 --- a/images.py +++ b/images.py @@ -6,4 +6,4 @@ CROSS_IMAGE = 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA99JREFUWEe9l1GIVUUYx///e92HICgIpALBhwWh1plZlyDwIeghdiPYDYNEaS1RM7Q0M8tIMrK2LG2tJMuoNBSFoqJI6UEICgS57sy4Kwg+CEGBVCAFPaz3fjF3z13PmXvuvWfz1vd2Z+b7/j/m+8+dOcT/GKLUGnr/UVqS6R+i9bsgK7T2025ziVKjIA8B2E3nnmvUnwUQrd8C8EwysZ7OfdAtCNH6YQDHZuuJjNP7p8PvOoAY8ypEXsgIimyi9+9cL4QYMwKRL3PqjNK5z2YAtH4MwMdNi8httPbNfwshixcPoVT6rilfZG3DC9daYMwKiBzJEdtB53bNFUKUuhfkSQA9mVxyI63d3+SB+k4otQzk5zliu+jcjqIQYsxSiATxG6OcLXTu7ZanIGnHAwC+BlCKkjPubQUjxtwF4AREbonWbKdzr8d5mWPYmBSt7wPwFYAbou3bR2s3txTv61Mol08AuD0y9E56/3JeXi5AfSf6++9BrRZ24qYo8QCdeyIuJkotSnq+MIIeo7XZE5Za0BIg8cTdIAPE/EjwEzq3enbHjFmY9HxRtG4vnWv8t+RuXFuAxBP9iScWRNt6lN6vlIGB2zA9fRKkihT207mNnYzbESBpxx1JO3qjgt8AuBVAMN61EDlI79d1Eg/zhQDqEEuW9KJaDca8s21h8jCtXVVEfE4AdYi+vgUol4MnQlvy4jidW15UfM4AiTHng7wA4OZI6DfMm9fLSuXKfwugdbhYRlqInIbIML2/XBSisAeSExGu1HC1tosJVKvDnJz8uQhEYQAx5hBERqOiP4L8EyJD0fgUyuURnj17sRNEIQBR6kOQa6NiZ3D16iCnpv4QrcMFtiyav4hSaZgTE+fbQXQEEK3fA7AhU0TEo6dnkJXKr41xUeoIyBWRWGjDMJ2baAXR/q9Y6z0AtkTJF0AO0tpLcVHROjxqwuMmHZcTY57Og2h9GRnzGkS2R0mXIDJI78MxzA3R+n0A66PJK0k7foiT8q9jpV4CuTNa/Auq1SFOTvpOxhJjxiGyKVr3dzi+dO779HgTgGj9PICxTDL5O4AhWnumk/isJ7R+A8C2aH0t8cS3jfH4uyA8lfdGSX8lPf+pqHgK4hUALzbliTxE778I4+lH6QaIBMenYzrp+am5iqcgAkAAyQa5ktYenXmWK7UG5MGmRbXa/Tx3LjyxrivEmGchsjunyGqK1o8AOJxD+CCtDddvV0KUegrkvshbY40dGAeZdu1yOne8K8qpIqL14wAOJEN76NzW9LfhjGtFVtH75h3pEo0Y8yhEBujckxkTNrwQfz53SbdlmX8AoU5k0z9z4mcAAAAASUVORK5CYII=' -LOGO = b'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' \ No newline at end of file +LOGO = b'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' diff --git a/main.py b/main.py index a9bcf75..9e72c8b 100644 --- a/main.py +++ b/main.py @@ -6,7 +6,7 @@ from images import LOGO from help_maintenance import show_help from evolution import evolution -from smileys_functions import calculate_data_for_smilies +from smilies_functions import calculate_data_for_smilies from zombie_boss import recreating_zombie_boss, calculate_data_for_zombie_boss from tips import tips_for_evolution, stop_tips_timer from global_items import smilies_data, scales, delete_help_window, window_commands, window, evolution_field, evolution_status @@ -15,7 +15,6 @@ import global_items def window_handling(): # Creating and handling the window - '''Creating the window''' window.title(TITLE) window.iconphoto(True, PhotoImage(data=LOGO)) @@ -129,7 +128,7 @@ def change_zombie_boss_vision_distance(value: str): labelanchor='n' )).pack(side=RIGHT, padx=5) - # Creating an OptionMenu for selecting which properties have to be shown over the smileys + # Creating an OptionMenu for selecting which properties have to be shown over the smilies def handle_selection(choice: str): window_commands['to-show-selected-property'] = choice @@ -142,7 +141,7 @@ def handle_selection(choice: str): 'Procreation threshold', 'Food preference', 'Generation number', - 'Amount of smileys with this species', + 'Amount of smilies with this species', 'ID of the species' ) diff --git a/plants.py b/plants.py index 99e1920..6baf44c 100644 --- a/plants.py +++ b/plants.py @@ -22,7 +22,7 @@ def create_plant_image(): TIMES_ATTEMPTED = 200 # Limit of times for trying to place a plant on the window -class Plant: # Food for the smileys which is not able to move +class Plant: # Food for the smilies which is not able to move def __init__(self): self.image_reference = None # Situating plant_pattern to a random place so that it does not overlap other plants or other boies. If the spot is not found in TIMES_ATTEMPTED times, then stop placing the plants diff --git a/related/icon.png b/related/icon.png new file mode 100644 index 0000000..0376a3d Binary files /dev/null and b/related/icon.png differ diff --git a/smileys_class.py b/smilies_class.py similarity index 96% rename from smileys_class.py rename to smilies_class.py index 148f9b3..7d8c06b 100644 --- a/smileys_class.py +++ b/smilies_class.py @@ -44,7 +44,7 @@ def __init__( self.birth_time = time() self.energy = energy self.stimulus_start = float('-inf') - self.stimulus = choice(('💀!', '😑', '😐')) + self.stimulus = choice(('💀!', '😠', '😐', '👎')) self.previous_x, self.previous_y = x, y @handle @@ -58,7 +58,7 @@ def D_one_action(self): self.energy -= self.speed*smilies_data['energy_for_moving'] # Checking whether it is time for the smiley to die or not - if self.energy <= 0: + if round(self.energy) <= 0: add_cross(self.x, self.y) smilies.remove(self) return @@ -84,8 +84,8 @@ def D_one_action(self): dangerous_pursuers = [] # All of the objects of visible_pursuers that can get to the place where self current is not dying on the way towards this for pursuer in visible_pursuers: distance = distance_between_objects(self, pursuer) - time = distance/pursuer.speed - final_energy = pursuer.energy - time*total_energy_loss(pursuer) + time_ = distance/pursuer.speed + final_energy = pursuer.energy - time_*total_energy_loss(pursuer) if final_energy > 0: dangerous_pursuers.append(pursuer) if dangerous_pursuers != []: # Escaping the closest pursuers @@ -158,6 +158,11 @@ def D_one_action(self): if distance_between_objects(self, prey) <= self.speed: if self.energy > prey.energy: self.energy += prey.energy + + # Storing the data about the collision with a smiley + self.collision.result = prey.energy + self.collision.moment = time() + smilies.remove(prey) self.procreate() else: @@ -169,6 +174,11 @@ def D_one_action(self): self.one_step_to(plant_prey) if distance_between_objects(self, plant_prey) <= self.speed: self.energy += PLANT_ENERGY + + # Storing the data about the collision with a plant + self.collision.result = PLANT_ENERGY + self.collision.moment = time() + plants.remove(plant_prey) self.procreate() @@ -242,11 +252,11 @@ def find_plant(self) -> tuple[str, object] | None: profitable_plants = [] # The plants that are worth being eaten because the energy which is received from them can make up the energy that was spent to catch them for plant in far_enough_from_zombie_boss: distance = distance_between_objects(self, plant) - time = distance/self.speed - self_final_energy = self.energy - time*total_energy_loss(self) + time_ = distance/self.speed + self_final_energy = self.energy - time_*total_energy_loss(self) if self_final_energy <= 0: continue # Self will die before the plant is caught because of a low level of energy - if self_final_energy + PLANT_ENERGY <= self.energy - time*self.vision_distance*smilies_data['energy_for_vision']: + if self_final_energy + PLANT_ENERGY <= self.energy - time_*self.vision_distance*smilies_data['energy_for_vision']: continue # It is not worth it to attempt to catch the plant in terms of the energy profitable_plants.append(plant) diff --git a/smileys_functions.py b/smilies_functions.py similarity index 98% rename from smileys_functions.py rename to smilies_functions.py index 0f4d03f..a955698 100644 --- a/smileys_functions.py +++ b/smilies_functions.py @@ -2,7 +2,7 @@ from random import randrange, random from config import * -from smileys_class import Smiley +from smilies_class import Smiley from global_items import smilies_data, distance_between_objects, handle, random_attribute, random_place, handle, smilies, evolution_field OLD_VISION_DISTANCE = 75 # A satisfying value for the size of the evolution field being {'width': 950, 'height': 500} @@ -70,5 +70,5 @@ def D_create_one_smiley(id_: int): smilies.append(smiley) @handle -def D_delete_all_smileys(): +def D_delete_all_smilies(): smilies.clear() \ No newline at end of file diff --git a/tips.py b/tips.py index ba67d22..fd3cd6e 100644 --- a/tips.py +++ b/tips.py @@ -1,4 +1,4 @@ -from global_items import smilies, evolution_status, handle +from global_items import smilies, evolution_status, handle, boss_shape_size from math import dist from config import * @@ -22,7 +22,7 @@ def prepare_tips_handle(): # Preparing for the future calls of D_tips_handle() after_id = None DELAY = 300 # This amount of time has to pass (in ms) for the program to consider the user implies to see the tip for the smiley/zombie boss -BASE_TIP_TEXT = 'If you place your mouse cursor on a smiley, then you will see the further tips.\nClick the right mouse button to commence the evolution.' +BASE_TIP_TEXT = 'If you place your mouse cursor on a smiley, then you will see the further tips.\nClick the right mouse button to commence the evolution ' @handle def D_tips_handle(): # This function restlessly works while the zombie boss selecting stage is active @@ -45,7 +45,7 @@ def D_tips_handle(): # This function restlessly works while the zombie boss sele if hovered_over is None: if evolution_status.zombie_boss is not None: if dist((canvas_mouse_x, canvas_mouse_y), - (evolution_status.zombie_boss.x, evolution_status.zombie_boss.y)) <= global_items.zombie_boss_half_height*EXTENSION: + (evolution_status.zombie_boss.x, evolution_status.zombie_boss.y)) <= boss_shape_size['half_height']*EXTENSION: hovered_over = evolution_status.zombie_boss # Displaying the tip or starting the timer diff --git a/window_functions.py b/window_functions.py index bd7b743..c307875 100644 --- a/window_functions.py +++ b/window_functions.py @@ -6,7 +6,7 @@ from draw_non_creatures import D_handle_properties from mask import D_create_mask from tips import stop_tips_timer, D_tips_handle, force_tip_for_creature, prepare_tips_handle -from global_items import handle, window_commands, smilies, evolution_status +from global_items import handle, window_commands, smilies, evolution_status, boss_shape_size from zombie_boss import D_create_new_boss from special_window_functions import D_change_user_control_widgets_state, set_scales_colours import global_items @@ -66,7 +66,7 @@ def selecting_creature(event): if evolution_status.zombie_boss is not None: if dist((event.x, event.y), (evolution_status.zombie_boss.x, evolution_status.zombie_boss.y)) <=\ - global_items.zombie_boss_half_height*EXTENSION: # Clicked the zombie boss; it is assumed that the height is greater than the width + boss_shape_size['half_height']*EXTENSION: # Clicked the zombie boss; it is assumed that the height is greater than the width force_tip_for_creature(SMILEY) # Displaying a tip forthwith replace_boss_with_smiley() D_change_user_control_widgets_state(DISABLED) diff --git a/zombie_boss.py b/zombie_boss.py index c697335..dd1ebaf 100644 --- a/zombie_boss.py +++ b/zombie_boss.py @@ -1,5 +1,6 @@ from math import dist, sqrt from tkinter import DISABLED, RIGHT, NORMAL +from time import time from config import * from crosses import add_cross @@ -36,11 +37,11 @@ def __init__( health=health, x=x, y=y) - self.sleeping = False + self.sleeping = False @handle def D_create_new_boss(speed, vision_distance, health, x, y): - evolution_status.zombie_boss = Boss(speed=speed, vision_distance=vision_distance, + evolution_status.zombie_boss = Boss(vision_distance=vision_distance, speed=speed, health=health, x=x, y=y) def recreating_zombie_boss(): @@ -56,8 +57,9 @@ def recreating_zombie_boss(): def is_zombie_boss_okay() -> bool: # If the health of the zombie boss is okay, then True is returned; whereas if it is not okay, then it ceases living zombie_boss = evolution_status.zombie_boss - if zombie_boss.health <= 0: # Checking if it is time to remove the zombie boss - global_items.canvas.delete(zombie_boss.image_reference) + + if round(zombie_boss.health) <= 0: # Checking if it is time to remove the zombie boss + global_items.canvas.delete('boss') add_cross(zombie_boss.x, zombie_boss.y) evolution_status.zombie_boss = None D_change_user_control_widgets_state(DISABLED) @@ -111,6 +113,11 @@ def zombie_boss_one_action(): for plant in plants: if distance_between_objects(zombie_boss, plant) <= ZOMBIE_BOSS_PLANT_GAP_TO_REACH: zombie_boss.health -= ZOMBIE_BOSS_PLANT_HEALTH_LOSS + + # Storing the data about the collision with a plant + zombie_boss.collision.result = -ZOMBIE_BOSS_PLANT_HEALTH_LOSS + zombie_boss.collision.moment = time() + to_remove = plant # Evading 'RuntimeError: Set changed size during iteration' break if to_remove is not None: @@ -118,18 +125,24 @@ def zombie_boss_one_action(): is_zombie_boss_okay() return - # Consuming smileys + # Consuming smilies transfer_to_zombies = None for smiley in smilies: if distance_between_objects(zombie_boss, smiley) <= ZOMBIE_BOSS_SMILEY_GAP_TO_REACH: transfer_to_zombies = smiley min_health = min(smiley.energy, NEW_ZOMBIE_HEALTH) - zombie_boss.health += smiley.energy - min_health + + # Storing the data about the collision with a smiley + extra_health = smiley.energy - min_health + zombie_boss.health += extra_health + zombie_boss.collision.result = extra_health + zombie_boss.collision.moment = time() break if transfer_to_zombies is not None: - zombies.append(create_zombie(speed=transfer_to_zombies.speed, - vision_distance=transfer_to_zombies.vision_distance, - health=min_health, - x=transfer_to_zombies.x, - y=transfer_to_zombies.y)) + zombies.append(create_zombie( + speed=transfer_to_zombies.speed, + vision_distance=transfer_to_zombies.vision_distance, + health=min_health, + x=transfer_to_zombies.x, + y=transfer_to_zombies.y)) smilies.remove(transfer_to_zombies) \ No newline at end of file diff --git a/zombies.py b/zombies.py index 3196334..052067c 100644 --- a/zombies.py +++ b/zombies.py @@ -1,3 +1,5 @@ +from time import time + from config import * from crosses import add_cross from global_items import CreatureStatus, Unalive, distance_between_objects, handle, plants, smilies, zombies @@ -18,7 +20,7 @@ def __init__( self.status=CreatureStatus() def create_zombie(speed, vision_distance, health, x, y): - new_zombie = Zombie(speed=speed, vision_distance=vision_distance, + new_zombie = Zombie(vision_distance=vision_distance, speed=speed, health=health, x=x, y=y) new_zombie.status.description = SLEEPING return new_zombie @@ -37,20 +39,25 @@ def zombie_one_action(zombie: object): for plant in plants: if distance_between_objects(zombie, plant) <= global_items.half_plant_size: zombie.health -= ZOMBIE_PLANT_HEALTH_LOSS + + # Storing the data about the collision with a plant + zombie.collision.result = -ZOMBIE_PLANT_HEALTH_LOSS + zombie.collision.moment = time() + to_remove = plant # Evading 'RuntimeError: Set changed size during iteration' break if to_remove is not None: plants.remove(to_remove) - # Checking whether it is time for zombie to die or not - if zombie.health <= 0: + # Checking whether it is time for the zombie to die or not + if round(zombie.health) <= 0: add_cross(zombie.x, zombie.y) zombies.remove(zombie) return # Find a satisfactory smiley to chase for the zombie - visible_smileys = tuple(filter(lambda smiley: zombie.vision_distance >= distance_between_objects(zombie, smiley), smilies)) - closest_smiley = min(visible_smileys, key=lambda smiley: distance_between_objects(zombie, smiley), default=None) + visible_smilies = tuple(filter(lambda smiley: zombie.vision_distance >= distance_between_objects(zombie, smiley), smilies)) + closest_smiley = min(visible_smilies, key=lambda smiley: distance_between_objects(zombie, smiley), default=None) if closest_smiley is None: zombie.status.description = SLEEPING @@ -70,7 +77,13 @@ def zombie_one_action(zombie: object): zombie.x, zombie.y = zombie.x + coeff*dx, zombie.y + coeff*dy # Moving the zombie toward the prey if distance_between_objects(zombie, prey) <= zombie.speed: min_health = min(prey.energy, NEW_ZOMBIE_HEALTH) - zombie.health += prey.energy - min_health + + # Storing the data about the collision with a smiley + extra_health = prey.energy - min_health + zombie.health += extra_health + zombie.collision.result = extra_health + zombie.collision.moment = time() + zombies.append(create_zombie( speed=prey.speed, vision_distance=prey.vision_distance,