Untitled
π§© Syntax:
import random
import copy
import math
import time
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from IPython.display import display
from collections import deque
# Logging Function
def log_to_file(log_file, message):
with open(log_file, "a") as file:
file.write(message + "\n")
log_file = "simulation_log.txt"
class Resource:
def __init__(self, scarcity, renewable):
self.quantity = random.uniform(0.5, 1.0) # Initial quantity
self.scarcity = scarcity
self.renewable = renewable
class PhysicsElement:
def __init__(self, effect):
self.effect = effect
# Add the Predator class
class Predator:
def __init__(self, x, y, environment):
self.position = (x, y)
self.environment = environment
self.hunger = 0
self.sight_range = 4 # Sight range for predators
self.max_lifetime = 10 # Maximum lifetime for predators
self.lifetime = 0
self.conflicts_won = 0
def beat_unit(self, unit):
log_to_file(log_file, f"Predator at {self.position} beats unit at {unit.position}.")
self.conflicts_won += 1
def move(self):
# Move the predator randomly within the environment
possible_moves = [(1, 0), (-1, 0), (0, 1), (0, -1)]
new_position = (self.position[0] + random.choice(possible_moves)[0], self.position[1] + random.choice(possible_moves)[1])
self.position = (
new_position[0] % self.environment.width,
new_position[1] % self.environment.height
)
def check_prey(self, units):
# Check if there are prey units within sight range
for unit in units:
distance = math.sqrt((self.position[0] - unit.position[0]) ** 2 + (self.position[1] - unit.position[1]) ** 2)
if distance <= self.sight_range:
return True # Predator has detected prey
return False
def consume_prey(self, unit):
# Consume a prey unit
self.hunger -= 3.5
unit.hunger = 1.0 # Prey unit's hunger is reset
unit.position = (random.randint(0, self.environment.width - 1), random.randint(0, self.environment.height - 1)) # Prey unit is respawned
def update(self, units):
# Predator behavior update
self.move()
self.lifetime += 1
if self.lifetime >= self.max_lifetime:
#Predator dies after reaching its maximum lifetime
self.environment.remove_predator(self)
elif self.check_prey(units):
# If prey is detected, consume it
prey_candidates = [unit for unit in units if self.position == unit.position]
if prey_candidates:
prey = random.choice(prey_candidates)
self.consume_prey(prey)
class Terrain:
def __init__(self, movement_cost):
self.movement_cost = movement_cost
class ResourceType:
def __init__(self, quantity, name, scarcity, renewable,position):
self.quantity = quantity
self.name = name
self.scarcity = scarcity
self.renewable = renewable
self.position = position
resource_type1 = ResourceType(quantity=10, name="Resource1", scarcity=0.2, renewable=True, position=(random))
resource_type2 = ResourceType(quantity=15, name="Resource2", scarcity=0.5, renewable=False, position=(random))
# Add more resource types as needed
resource_types = [resource_type1, resource_type2, ...]
class CraftedItem:
def __init__(self):
self.position = None # Position of the crafted item
class Environment:
def __init__(self, width, height, obstacle_spawn_rate, num_resource_types, num_terrain_types, num_physics_elements):
self.width = width
self.height = height
self.obstacles = set()
self.obstacle_spawn_rate = obstacle_spawn_rate
self.resources = [[0.0] * width for _ in range(height)] # Initialize resource matrix
# Initialize an empty list for predators
self.predators = []
# Initialize multiple resource types
# Initialize multiple resource types with positions
self.resource_types = [ResourceType(
quantity=random.uniform(0.5, 1.0),
name=f"Resource{i+1}",
scarcity=random.uniform(0.1, 0.9),
renewable=random.choice([True, False]),
position=(random.randint(0, width - 1), random.randint(0, height - 1)) # Add a random position
) for i in range(num_resource_types)]
# Initialize a template for crafted items
self.crafted_item_template = CraftedItem()
# Initialize multiple terrain types
self.terrain_types = [Terrain(movement_cost=random.uniform(0.5, 1.0)) for _ in range(num_terrain_types)]
# Initialize multiple physics elements
self.physics_elements = [PhysicsElement(effect=random.uniform(-0.5, 0.5)) for _ in range(num_physics_elements)]
# Initialize a list to store crafted items
self.crafted_items = []
for i in range(width):
for j in range(height):
if random.uniform(0, 1) < 0.2: # Adjust probability as needed
# Add obstacle for each resource type
self.obstacles.add((i, j))
self.change_threshold = 0.7 # Threshold for abrupt environmental changes
def remove_predator(self, predator):
# Implement logic to remove the predator from the environment
# For example, you can have a list of predators and remove the specified predator
if predator in self.predators:
self.predators.remove(predator)
def update(self):
# Update resource dynamics, depletion, and replenishment here
for resource_type in self.resource_types:
if resource_type.renewable:
# Implement resource replenishment for renewable resources
resource_type.quantity += 0.1 # You can adjust the replenishment rate
# Ensure that the quantity doesn't exceed a maximum value
resource_type.quantity = min(1.0, resource_type.quantity)
else:
# Implement resource depletion for non-renewable resources
resource_type.quantity -= 0.05 # You can adjust the depletion rate
# Ensure that the quantity doesn't go below a minimum value
resource_type.quantity = max(0.0, resource_type.quantity)
def cooperative_challenge(self, step):
if step == 12:
# Cooperative Challenge: Units need to work together to achieve a common goal
self.obstacles = {(2, 3), (4, 4), (3, 2), (5, 3)}
goal_location = (7, 7) # Cooperative goal location
self.resources[goal_location[1]][goal_location[0]] = 1.0 # Set a high-value resource at the goal
def additional_challenges(self, step):
if step == 15:
# Challenge 4: Units need to navigate through a narrow passage
self.obstacles = {(2, 3), (3, 3), (4, 3)}
elif step == 18:
# Challenge 5: Units face resource scarcity and must find hidden resources
self.resources[7][7] = 0.0 # Remove the high-value resource at the goal
self.resources[3][5] = 1.0 # Place a hidden resource
self.resources[6][2] = 1.0 # Place another hidden resource
def sequential_challenge(self, step):
if step == 3:
# Challenge 1: Units need to navigate through a maze
self.obstacles = {(2, 2), (2, 3), (2, 4), (3, 4), (4, 4), (5, 4), (5, 3), (5, 2), (4, 2)}
elif step == 6:
# Challenge 2: Units need to find resources scattered in a new pattern
self.obstacles = {(2, 3), (4, 4), (3, 2), (5, 3)}
elif step == 9:
# Challenge 3: Units face a combination of obstacles and resources in a larger environment
self.obstacles = {(2, 2), (2, 3), (2, 4), (3, 4), (4, 4), (5, 4), (5, 3), (5, 2), (4, 2), (7, 7), (8, 8), (9, 9)}
def update_resources(self):
for resource_type in self.resource_types:
for i in range(self.width):
for j in range(self.height):
if (i, j) not in self.obstacles:
# Deplete resources over time
resource_type.quantity -= resource_type.scarcity * 0.02
# Randomly replenish resources (if renewable)
if resource_type.renewable and random.uniform(0, 1) < 0.1:
resource_type.quantity += random.uniform(0, 0.2)
# Ensure resource quantity stays within bounds
resource_type.quantity = max(0, min(1, resource_type.quantity))
# Check if it's time to spawn obstacles with food
if random.uniform(0, 1) < self.obstacle_spawn_rate:
# Spawn obstacles with food
food_positions = [(i, j) for i in range(self.width) for j in range(min(self.height, len(self.resource_types))) if self.resource_types[j].quantity > 0.5]
self.obstacles.update(food_positions)
# Spawn obstacles with food based on available resource types
for resource_type in self.resource_types:
food_positions = [(i, j) for i in range(self.width) for j in range(self.height) if
resource_type.quantity > 0.5 and (i, j) not in self.obstacles]
self.obstacles.update(food_positions)
# Simulate abrupt environmental changes
if random.uniform(0, 1) < self.change_threshold:
self.resources = [[random.uniform(0, 1) for _ in range(self.width)] for _ in range(self.height)]
# Check if it's time to spawn obstacles with food
if random.uniform(0, 1) < self.obstacle_spawn_rate:
# Spawn obstacles with food
food_positions = [(i, j) for i in range(self.width) for j in range(self.height) if self.resources[j][i] > 0.5]
self.obstacles.update(food_positions)
# Introduce a chance for dynamic changes in obstacle locations
if random.uniform(0, 1) < 0.05: # Adjust probability as needed
num_obstacles_to_move = random.randint(1, 5) # Adjust range as needed
obstacles_to_move = random.sample(list(self.obstacles), num_obstacles_to_move)
for obstacle in obstacles_to_move:
new_position = (
(obstacle[0] + random.randint(-1, 1)) % self.width,
(obstacle[1] + random.randint(-1, 1)) % self.height
)
self.obstacles.remove(obstacle)
self.obstacles.add(new_position)
# Update resource dynamics
self.update_resource_dynamics()
# Update resource transformation
self.update_resource_transformation()
# Increase obstacle spawn rate over time
self.obstacle_spawn_rate += 0.01
def update_resource_dynamics(self):
# Simulate resource depletion or replenishing based on type
for resource_type in self.resource_types:
for i in range(self.width):
for j in range(self.height):
if (i, j) not in self.obstacles:
resource_type.quantity -= 0.02 # Deplete resources over time
if random.uniform(0, 1) < 0.1:
resource_type.quantity += random.uniform(0, 0.2) # Randomly replenish resources
# Introduce a chance for dynamic changes in obstacle locations
if random.uniform(0, 1) < 0.05: # Adjust probability as needed
num_obstacles_to_move = random.randint(1, 5) # Adjust range as needed
obstacles_to_move = random.sample(list(self.obstacles), num_obstacles_to_move)
for obstacle in obstacles_to_move:
new_position = (
(obstacle[0] + random.randint(-1, 1)) % self.width,
(obstacle[1] + random.randint(-1, 1)) % self.height
)
self.obstacles.remove(obstacle)
self.obstacles.add(new_position)
# Increase obstacle spawn rate over time
self.obstacle_spawn_rate += 0.01
def update_resource_transformation(self):
# Implement a basic resource transformation mechanism
for i in range(self.width):
for j in range(self.height):
if (i, j) not in self.obstacles:
for resource_type in self.resource_types:
if resource_type.quantity > 0.5 and random.uniform(0, 1) < 0.05:
# Transform resource into a crafted item
resource_type.quantity -= 0.5
crafted_item = copy.deepcopy(self.crafted_item_template)
crafted_item.position = (i, j)
self.crafted_items.append(crafted_item)
def move_cost(self, position):
# Return the movement cost of the terrain at the specified position
return self.terrain_types[position[1] % len(self.terrain_types)].movement_cost
def move_unit(self, unit, new_position):
# Move the unit to the new position, considering terrain movement costs
move_cost = self.move_cost(new_position)
unit.position = new_position if random.uniform(0, 1) > move_cost else unit.position
def apply_physics(self, unit):
# Apply physics effects to the unit
for physics_element in self.physics_elements:
unit.hunger += physics_element.effect
def get_resource_type_at_position(self, position):
"""
Get the resource type at the given position.
Parameters:
- position (tuple): The (x, y) position to check.
Returns:
- ResourceType or None: The resource type at the given position, or None if no resource is present.
"""
for resource_type in self.resource_types:
if position in resource_type.position:
return resource_type
return None
# Unit Class
class Unit:
def __init__(self, x, y,predators,resource_types, environment, strength=1, perception=1):
self.position = (x, y)
self.environment = environment # Add this line
self.hunger = 1
self.curiosity = 0
self.memory = set()
self.current_goal = None
self.sight_range = 3 # Sight-like detection range
self.internal_complexity = 1 # Internal complexity of decision-making processes
self.successful_foraging_actions = 0
self.problem_solving_actions = 0 # attribute for problem solving
self.communication_count = 0 # Add this attribute
self.past_positions = set() # Add a set to store past positions
self.max_memory_size = 20 # Adjust the maximum memory size as needed
self.abstract_messages = set() # New attribute for abstract messages
self.self_reflection_threshold = 0.8 # Threshold for self-reflection
self.communication_range = 2
self.communication_timer = 0
self.communicate_drive = 0.5 # You can set the initial value accordingly
self.resource_type = None
self.goal = None
self.strength = strength # Strength attribute representing combat ability
self.perception = perception # Perception attribute representing sensory abilities
self.seek_food_drive = 0.5 # Initial value, adjust as neededΕΎ
self.crafted_items = set()
self.used_resources = set()
# Enhancement: Longer Memory Span
self.past_positions = deque(maxlen=20) # Set a suitable maximum length
self.self_awareness_threshold = 10 # Set a default value, adjust as needed
# Enhancement: Memory Types
self.short_term_memory = deque(maxlen=10)
self.long_term_memory = []
self.predators = predators
self.resource_types = resource_types
def __deepcopy__(self, memo):
# Create a new instance and copy necessary attributes
new_unit = Unit(0, 0, self.predators, self.resource_types, self.environment)
new_unit.hunger = self.hunger
new_unit.curiosity = self.curiosity
new_unit.sight_range = self.sight_range
new_unit.internal_complexity = self.internal_complexity
# Copy other attributes as needed
# Copy the memory set explicitly
new_unit.memory = set(self.memory)
return new_unit
def update_memory(self):
# Store current position in both short-term and long-term memory
self.short_term_memory.append(self.position)
self.past_positions.append(self.position)
# Move short-term memory to long-term memory periodically
if random.uniform(0, 1) < 0.1: # Adjust probability as needed
self.long_term_memory.extend(self.short_term_memory)
self.short_term_memory.clear()
def self_modify(self):
# Your self-modification logic goes here
# For example, you can modify some internal attributes or behaviors
self.internal_complexity += random.uniform(0, 0.1)
print(f"Unit at {self.position} has self-modified. New internal complexity: {self.internal_complexity}")
log_to_file(log_file, f"Unit at {self.position} has self-modified {self.internal_complexity}.")
def sense(self, environment, units):
# Detect resources within a radius around the unit
radius = 1
sensing_range = 2
for i in range(-radius, radius + 1):
for j in range(-radius, radius + 1):
neighbor_x, neighbor_y = self.position[0] + i, self.position[1] + j
if 0 <= neighbor_x < environment.width and 0 <= neighbor_y < environment.height:
for resource_type in environment.resource_types:
if resource_type.quantity > 0.5 and (neighbor_x, neighbor_y) not in self.memory:
self.memorize(("resource", resource_type, (neighbor_x, neighbor_y)))
for i in range(-sensing_range, sensing_range + 1):
for j in range(-sensing_range, sensing_range + 1):
new_x, new_y = self.position[0] + i, self.position[1] + j
if 0 <= new_x < environment.width and 0 <= new_y < environment.height:
# Existing obstacle sensing logic
if (new_x, new_y) in environment.obstacles:
self.obstacle_sensed = True
# Enhancement: Environmental Information
resource_type = environment.get_resource_type_at_position((new_x, new_y))
if resource_type:
self.resource_sensed = True
self.sensed_resource_type = resource_type
# Detect other units within sight range
for other_unit in units:
if other_unit != self:
distance = math.sqrt((self.position[0] - other_unit.position[0]) ** 2 + (self.position[1] - other_unit.position[1]) ** 2)
if distance <= self.sight_range:
self.memorize(("unit", other_unit))
# Detect obstacles within sight range
for obstacle in environment.obstacles:
distance = math.sqrt((self.position[0] - obstacle[0]) ** 2 + (self.position[1] - obstacle[1]) ** 2)
if distance <= self.sight_range:
self.memorize(("obstacle", obstacle))
def use_resource(self, resource):
# ... (existing implementation)
self.used_resources.add(resource)
def craft_item(self, item):
# ... (existing implementation)
self.crafted_items.add(item)
# Add a method to handle encounters with predators
def encounter_predator(self, predator):
# ... (existing implementation)
log_to_file(log_file, f"Unit at {self.position} encounters predator at {predator.position}.")
def update_drives(self, environment, all_units):
# Update drives based on the environment
current_position = self.position
for resource_type in self.environment.resource_types:
resource_quantity = resource_type.quantity
scarcity = resource_type.scarcity
resource_value = resource_quantity * (1 - scarcity)
# Check if there are crafted items at the current position
for item in self.environment.crafted_items:
if item.position == current_position:
# Increase the resource value if a crafted item is present
resource_value += 0.2
# Update seek_food_drive based on resource values
self.seek_food_drive = max(0.1, min(0.9, self.seek_food_drive + 0.1 * resource_value))
# Update communicate_drive based on the presence of other units
for other_unit in all_units:
if other_unit != self and other_unit.position == current_position:
# Increase communicate_drive if another unit is present at the same position
self.communicate_drive = max(0.1, min(0.9, self.communicate_drive + 0.2))
break
else:
# Decrease communicate_drive if no other units are present
self.communicate_drive = max(0.1, min(0.9, self.communicate_drive - 0.1))
def solve_problem(self, environment):
if environment is not None:
print(f"Unit at {self.position} encounters and solves a problem!")
self.problem_solving_actions += 1 # Increment the problem-solving action count
else:
print(f"Unit at {self.position} encounters and solves a problem!")
def move_towards_goal(self, environment):
# Apply physics effects
# Update memory after movement
self.update_memory()
environment.apply_physics(self)
if self.current_goal == "explore":
# Move towards unexplored areas with scarce resources
unexplored_cells = [(i, j) for i in range(environment.width) for j in range(environment.height)
if ("resource", environment.resource_types[0], (i, j)) not in self.memory]
if unexplored_cells:
closest_cell = min(unexplored_cells, key=lambda cell: abs(self.position[0] - cell[0]) + abs(self.position[1] - cell[1]))
self.position = closest_cell if closest_cell[0] < environment.width and closest_cell[1] < environment.height else self.position
# Check for obstacles and trigger problem-solving
if self.position in environment.obstacles:
self.solve_problem(environment)
elif self.current_goal == "gather":
# Move towards high-value resources based on scarcity
high_value_cells = []
for resource_type in environment.resource_types:
high_value_cells += [(i, j) for i in range(environment.width) for j in range(environment.height)
if resource_type.quantity > 0.5 and ("resource", resource_type, (i, j)) in self.memory]
if high_value_cells:
closest_cell = min(high_value_cells, key=lambda cell: abs(self.position[0] - cell[0]) + abs(self.position[1] - cell[1]))
self.position = closest_cell if closest_cell[0] < environment.width and closest_cell[1] < environment.height else self.position
elif self.current_goal == "solve_challenge":
# Check if the current position has an obstacle
if self.position in environment.obstacles:
# If there is an obstacle, solve the problem
self.solve_problem()
else:
# Move towards a predefined challenge-solving location
challenge_location = (environment.width // 2, environment.height // 2)
self.position = challenge_location if challenge_location[0] < environment.width and challenge_location[1] < environment.height else self.position
else:
# Random movement with consideration for terrain types
possible_moves = [(1, 0), (-1, 0), (0, 1), (0, -1)]
new_position = (self.position[0] + random.choice(possible_moves)[0], self.position[1] + random.choice(possible_moves)[1])
environment.move_unit(self, new_position)
def consume(self, environment):
# Reduce the resource in the Environment if the unit is on a cell with resources
for resource_type in environment.resource_types:
if resource_type.quantity > 0.5 and ("resource", resource_type, self.position) in self.memory:
resource_type.quantity -= 0.1 # Decrease the resource
# Potentially decrease the unit's hunger
self.hunger -= 0.1
# Successful foraging action
self.successful_foraging_actions += 1
def communicate(self, other_unit):
self.communication_count += 1 # Increment the communication count
# Share more abstract information or intentions
abstract_message = f"Let's cooperate on goal {self.current_goal}!"
self.abstract_messages.add((other_unit.position, abstract_message))
def memorize(self, event):
# Store the types of resources encountered, or perhaps the unit's past locations
self.memory.add(event)
if len(self.memory) > self.max_memory_size:
# Implement a mechanism to prioritize or forget memories based on relevance and recency
self.memory.pop()
if event[0] == "position":
self.past_positions.add(event[1])
if len(self.past_positions) > self.max_memory_size:
self.past_positions.pop()
def think(self, mutation_rate):
# Mutate the think function
if random.uniform(0, 1) < mutation_rate:
self.current_goal = random.choice(["explore", "gather", None])
# Internal complexity growth over generations
self.internal_complexity += random.uniform(0, 0.1)
# Learn from problem-solving experiences and adapt
if self.problem_solving_actions > 0:
self.internal_complexity += 0.1 # Increase internal complexity with problem-solving experience
def set_goal(self, new_goal):
# Set a new goal for the unit
self.current_goal = new_goal
def react_to_predators(self, environment):
# Check for predators and adjust behavior accordingly
for predator in environment.predators:
distance = math.sqrt((self.position[0] - predator.position[0]) ** 2 + (self.position[1] - predator.position[1]) ** 2)
if distance <= self.sight_range:
# Implement evasion strategy or defensive tactics
self.curiosity -= 0.5 # Decrease curiosity when predators are detected
def move_towards_dynamic_goal(self, environment):
# Move towards dynamically changing goals
if self.current_goal == "dynamic_challenge":
# Move towards the dynamically changing goal location
goal_location = random.sample(environment.obstacles, 1)[0]
self.position = goal_location
# Update memory after movement
self.update_memory()
else:
# Continue with existing goal logic
self.move_towards_goal(environment)
def set_dynamic_goal(self, new_goal):
# Set a new dynamically changing goal for the unit
self.current_goal = new_goal
def reproduce(self, mutation_rate):
# Reproduction: create offspring that can adapt to dynamic challenges
offspring = copy.deepcopy(self)
# Mutations: introduce more radical changes, including in the think function
if random.uniform(0, 1) < mutation_rate:
offspring.hunger += random.uniform(-0.5, 0.5)
if random.uniform(0, 1) < mutation_rate:
offspring.curiosity += random.uniform(-0.5, 0.5)
if random.uniform(0, 1) < mutation_rate:
offspring.sight_range += random.uniform(-1, 1)
if random.uniform(0, 1) < mutation_rate:
offspring.think(mutation_rate)
if random.uniform(0, 1) < mutation_rate:
offspring.set_dynamic_goal("dynamic_challenge")
return offspring
def calculate_fitness(unit):
return 1 / (unit.hunger + 1) + unit.successful_foraging_actions + unit.problem_solving_actions
def self_reflection(self):
# Rudimentary form of self-awareness based on hunger level
if self.hunger > self.self_reflection_threshold:
log_to_file(log_file, f"Unit at {self.position} reflects on its hunger: {self.hunger}.")
# Enhanced self-awareness based on internal complexity
if self.internal_complexity > self.self_awareness_threshold:
log_to_file(log_file, f"Unit at {self.position} is becoming self-aware: Internal Complexity - {self.internal_complexity}.")
# Self-awareness when encountering obstacles
if self.position in self.environment.obstacles:
log_to_file(log_file, f"Unit at {self.position} is aware of the obstacle and adjusts its behavior.")
# Self-awareness when encountering predators
for predator in self.predators:
self.encounter_predator(predator)
distance = math.sqrt((self.position[0] - predator.position[0]) ** 2 + (self.position[1] - predator.position[1]) ** 2)
if distance <= 1: # Adjust the distance threshold for encounters
print(f"Unit at {self.position} is aware of the predator and adjusts its behavior.")
log_to_file(log_file, f"Unit at {self.position} is aware of the predator and adjusts its behavior.")
# Genetic Algorithm Functions
def select_parents(units):
# Selection: Randomly select parents based on fitness (lower hunger is better)
weights = [1.0 / (unit.hunger + 1) for unit in units]
# Add a small offset to avoid a total of weights being zero
total_weights = sum(weights)
if total_weights <= 0:
weights = [1.0 for _ in units] # Use equal weights if total_weights is zero
parents = random.choices(units, weights=weights, k=2)
return parents
def crossover(parents):
# Crossover (Recombination): Simple averaging of attributes
offspring = copy.deepcopy(parents[0])
offspring.hunger = (parents[0].hunger + parents[1].hunger) / 2
offspring.curiosity = (parents[0].curiosity + parents[1].curiosity) / 2
offspring.sight_range = (parents[0].sight_range + parents[1].sight_range) / 2
offspring.internal_complexity = (parents[0].internal_complexity + parents[1].internal_complexity) / 2
return offspring
# Simulation Loop with Visualization
def simulate(environment_size, initial_population, num_steps, mutation_rate, log_interval):
environment = Environment(width=10, height=10, obstacle_spawn_rate=0.1, num_resource_types=3, num_terrain_types=3, num_physics_elements=10)
predators = [] # Declare the predators list outside the loop
units = [Unit(random.randint(0, environment.width - 1), random.randint(0, environment.height - 1), predators, resource_types, environment) for _ in range(initial_population)]
for step in range(num_steps):
for predator in predators:
predator.update(units)
# Log encounters with predators
log_to_file(log_file, f"Step {step}:")
log_to_file(log_file, f"Predator at {predator.position} encountered.")
# Add new predators to the environment
if random.uniform(0, 1) < 0.1:
new_predator = Predator(random.randint(0, environment.width - 1), random.randint(0, environment.height - 1), environment)
predators.append(new_predator)
for unit in units:
unit.sense(environment, units)
unit.update_drives(environment,units)
unit.think(mutation_rate)
unit.move_towards_goal(environment)
unit.consume(environment)
environment.update()
environment.update_resources() # Update resource dynamics
environment.sequential_challenge(step)
# Logging at intervals
if step % log_interval == 0:
log_to_file(log_file, f"Step {step}:")
log_to_file(log_file, "Unit Locations:")
for unit in units:
log_to_file(log_file, f" Unit at {unit.position}")
log_to_file(log_file, f" Internal Complexity: {unit.internal_complexity}")
log_to_file(log_file, f" Hunger: {unit.hunger}")
log_to_file(log_file, f" Curiosity: {unit.curiosity}")
log_to_file(log_file, f" Past Positions: {unit.past_positions}")
# Check and log how the unit feels after finding food
if 0 <= unit.position[1] < len(environment.resource_types):
if environment.resource_types[unit.position[1]].quantity > 0.5:
log_to_file(log_file, " Feeling: Found food! Yummy!")
# Check and log how the unit feels after encountering danger
if unit.position in environment.obstacles:
log_to_file(log_file, " Feeling: Danger! Avoiding obstacle.")
if random.uniform(0, 1) < environment.change_threshold:
log_to_file(log_file, " Abrupt environmental change occurred.")
log_to_file(log_file, f"Average Internal Complexity: {sum(unit.internal_complexity for unit in units) / len(units)}")
log_to_file(log_file, f"Average Hunger: {sum(unit.hunger for unit in units) / len(units)}")
log_to_file(log_file, f"Average Curiosity: {sum(unit.curiosity for unit in units) / len(units)}")
log_to_file(log_file, f"Successful Foraging Actions: {sum(unit.successful_foraging_actions for unit in units)}")
log_to_file(log_file, f"Problem-Solving Actions: {sum(unit.problem_solving_actions for unit in units)}")
log_to_file(log_file, f"Total Communications: {sum(unit.communication_count for unit in units)}")
log_to_file(log_file, "Major Environmental Changes:")
log_to_file(log_file, "Communication Analysis:")
log_to_file(log_file, f" Total Communications: {sum(unit.communication_count for unit in units)}")
log_to_file(log_file, f"Unit at {unit.position} is solving a challenge unexpectedly.")
# Spread of Solutions Analysis
spread_of_solutions = [unit.communication_count for unit in units]
log_to_file(log_file, f"Spread of Solutions: {spread_of_solutions}")
for other_unit in units:
if unit != other_unit and unit.position == other_unit.position:
# Log information about the encounter
log_to_file(log_file, f"Unit at {unit.position} found a solution to beat the predator at {other_unit.position}.")
# Communication Leaders Analysis
communication_leaders = [unit for unit in units if unit.communication_count > 0]
log_to_file(log_file, "Communication Leaders:")
for leader in communication_leaders:
log_to_file(log_file, f" Unit at {leader.position} with Internal Complexity {leader.internal_complexity} shares solutions often.")
# Log finding solutions to beat predators
for other_unit in units:
if isinstance(unit, Predator) and isinstance(other_unit, Predator):
distance = math.sqrt((unit.position[0] - other_unit.position[0]) ** 2 + (unit.position[1] - other_unit.position[1]) ** 2)
if distance <= 1:
log_to_file(log_file, f"Unit at {unit.position} found a solution to beat the predator at {other_unit.position}.")
# Check for communication and problem-solving
for unit1 in units:
for unit2 in units:
if unit1 != unit2:
distance = math.sqrt((unit1.position[0] - unit2.position[0]) ** 2 + (unit1.position[1] - unit2.position[1]) ** 2)
if distance <= 1: # Adjust the distance threshold for communication
unit1.communicate(unit2)
# Introduce self-modification
if random.uniform(0, 1) < 0.03:
unit.self_modify()
# Introduce reproduction
if random.uniform(0, 1) < 0.05:
offspring = unit.reproduce(mutation_rate)
new_units.append(offspring)
for unit in units:
unit.self_reflection() # Add self-reflection for each unit
# Introduce resource usage against obstacles and predators
if random.uniform(0, 1) < 0.1: # Adjust probability as needed
# Check if the unit's position is within valid range
if 0 <= unit.position[1] < len(environment.resource_types):
# Check if the resource type exists at the given index
if environment.resource_types[unit.position[1]].quantity > 0.5:
unit.use_resource(environment.resource_types[unit.position[1]])
log_to_file(log_file, f"Unit at {unit.position} used resource against obstacle or predator.")
# Log finding solutions to beat predators
for other_unit in units:
if isinstance(unit, Predator) and isinstance(other_unit, Predator):
distance = math.sqrt((unit.position[0] - other_unit.position[0]) ** 2 + (unit.position[1] - other_unit.position[1]) ** 2)
if distance <= 1:
log_to_file(log_file, f"Unit at {unit.position} found a solution to beat the predator at {other_unit.position}.")
# Unexpected problem-solving: Units develop ways to overcome environmental challenges
if step == 3:
for unit in units:
unit.set_goal("solve_challenge")
unit.problem_solving_actions += 1 # Increment the problem-solving count
# Log unexpected problem-solving
# Intentionality Analysis
log_to_file(log_file, "Intentionality Analysis:")
for unit in units:
if unit.position in environment.obstacles and unit.current_goal != "solve_challenge":
log_to_file(log_file, f" Unit at {unit.position} is avoiding obstacles unintentionally.")
else:
log_to_file(log_file, f" Unit at {unit.position} is intentionally solving obstacles.")
# Unexpected problem-solving: Units develop ways to overcome environmental challenges
if step == 3:
for unit in units:
unit.set_goal("solve_challenge")
unit.problem_solving_actions += 1 # Increment the problem-solving count
# Adaptation: Units modify behaviors and thrive or fail in response to abrupt environmental changes
if step == 2:
environment.change_threshold = 0.9
# Internal complexity growth over generations
if step % 25 == 0:
for unit in units:
unit.internal_complexity += random.uniform(0, 0.1)
# Reproduction and mutation
new_units = []
while len(new_units) < initial_population:
parents = select_parents(units)
offspring = crossover(parents).reproduce(mutation_rate)
new_units.append(offspring)
units = new_units
# Check for encounters with predators
for unit in units:
for predator in predators:
distance = math.sqrt((unit.position[0] - predator.position[0]) ** 2 + (unit.position[1] - predator.position[1]) ** 2)
if distance <= 1: # Adjust the distance threshold for encounters
unit.encounter_predator(predator)
# Introduce resource usage against obstacles and predators
if random.uniform(0, 1) < 0.1: # Adjust probability as needed
if unit.position[1] < len(environment.resource_types):
if environment.resource_types[unit.position[1]].quantity > 0.5:
unit.use_resource(environment.resource_types[unit.position[1]])
log_to_file(log_file, f"Unit at {unit.position} used resource against obstacle or predator.")
# Log finding solutions to beat predators
for other_unit in units:
if isinstance(unit, Predator) and isinstance(other_unit, Predator):
distance = math.sqrt((unit.position[0] - other_unit.position[0]) ** 2 + (unit.position[1] - other_unit.position[1]) ** 2)
if distance <= 1:
log_to_file(log_file, f"Unit at {unit.position} found a solution to beat the predator at {other_unit.position}.")
# Generalized Problem-solving: Random dynamic obstacle challenges
if random.uniform(0, 1) < 0.1: # Adjust probability as needed
challenge_positions = set()
for _ in range(random.randint(3, 6)): # Adjust the range as needed
challenge_positions.add((random.randint(0, environment.width - 1), random.randint(0, environment.height - 1)))
environment.obstacles.update(challenge_positions)
log_to_file(log_file, f"Random Dynamic Obstacle Challenge at Step {step}: {challenge_positions}")
# Units react to dynamic changes
for unit in units:
if random.uniform(0, 1) < 0.2: # Adjust probability as needed
unit.set_dynamic_goal("dynamic_challenge")
unit.problem_solving_actions += 1 # Increment the problem-solving count
log_to_file(log_file, "")
# Example Usage
environment_size = (10, 10)
initial_population = 20
num_steps = 100
mutation_rate = 3
log_interval = 0.5
start_time = time.time()
simulate(environment_size, initial_population, num_steps, mutation_rate, log_interval)
print(f"Time taken: {time.time() - start_time} seconds")