Ich sende dir hier eine ganze reihe von codesnippets welche alle durch die zeile in der !!!BREAK!!! steht getrennt sind. Alle codesnippets stammen aus einem gesprächsverlauf indem eine anwendung schritt für schritt entwickelt wurde. Nun soll aus diesen codesnippet verlauf (gesprächsverlauf) als resultat das vollständige skript der erstellten anwendung mit dem gesamten erstellten codes aus den snippets zusammengefügt werden um ein einziges skript als resultat ihrer entwicklung während des gesprächs zu erhalten. Erstelle also dieses skript anhand aller codesnippets, beachte dabei das die anwendung auch unter verwendung aller snippets noch nicht vollständig fertig entwickelt ist. Erstelle trotzdem das soweit die anwendung bisher entwickelt wurde, das vollständige skript der gesamten anwendung unter berücksichtung aller snippets. achte darauf keine inhalte zu vergessen.

Snippets:

---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# Environment parameters
ENV_SIZE = (100, 100, 50) # Increase environment size
VOXEL_SIZE = 0.2 # smaller voxel size: 0.3 * ANT_SIZE ≈ 0.2 (ANT_SIZE = 0.5)
INITIAL_ANTS = 20
FOOD_SPAWN_RATE = 0.05
PHEROMONE_EVAPORATION_RATE = 0.01
FOOD_PHEROMONE_THRESHOLD = 0.5 # Threshold for ants to follow food pheromone

# Voxel types
AIR = 0
SOIL = 1
FOOD = 2
NEST_MATERIAL = 3

# Ant parameters
ANT_SIZE = 0.5 # Size of an ant (adjust as needed)
LEG_LENGTH = ANT_SIZE / 3
ANTENNA_LENGTH = ANT_SIZE / 2
NUM_LEGS = 6
NUM_ANTENNA_SEGMENTS = 3 # This parameter is not directly used anymore, but let's keep it for potential use.
NUM_BODY_SEGMENTS = 3
MAX_LEG_STRETCH = LEG_LENGTH * 1.2 # Maximum leg stretch

# Detail culling distance
CULL_DISTANCE = 50

# Gravity (not used directly, but kept for potential future use)
GRAVITY = np.array([0, 0, -0.1])


class Voxel:
def __init__(self, voxel_type):
self.type = voxel_type
self.food_pheromone = 0.0 # Initialize pheromone level to 0

def evaporate_pheromone(self):
self.food_pheromone = max(0, self.food_pheromone - PHEROMONE_EVAPORATION_RATE)

class Ant:
def __init__(self, position, colony):
# ... (other attributes)
self.body_segments = [np.array(position) + np.array([i * ANT_SIZE / 3, 0, 0]) for i in range(NUM_BODY_SEGMENTS)]
self.body_rotations = [0.0] * (NUM_BODY_SEGMENTS - 1) # Rotation angles between body segments
self.leg_angles = [0.0] * NUM_LEGS # For leg animation
self.antenna_angles = [0.0] * NUM_ANTENNA_SEGMENTS * 2 # for antenna animation (2 antennae)
self.leg_targets = [np.array(position) for _ in range(NUM_LEGS)] # initialize leg targets
self.antenna_targets = [np.zeros(3) for _ in range(2)]
self.leg_positions = [[]] * NUM_LEGS # Clear leg positions to be recalculated dynamically in the ant's move method
self.antenna_positions = [np.zeros(3) for _ in range(2)] # set antenna positions before movement logic is applied

self.has_food = False
self.direction = np.random.rand(3) - 0.5 # Random initial direction

def move(self, environment):

# Pheromone influence on movement
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])
pheromone_direction = np.zeros(3)

for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0,1]: # consider 3D Pheromone
nx, ny, nz = x + dx, y + dy, z + dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
pheromone_strength = environment[nx, ny, nz].food_pheromone
if pheromone_strength > FOOD_PHEROMONE_THRESHOLD and not self.has_food: # Ants with food don't follow food pheromones.
pheromone_direction += np.array([dx, dy, dz]) * pheromone_strength

# Combine random walk with pheromone direction
self.direction += np.random.rand(3) * 0.2 - 0.1 + pheromone_direction * 0.5 # Adjust pheromone influence as needed

self.direction = self.direction / np.linalg.norm(self.direction) # Normalize

new_pos = self.position + self.direction

if 0 <= new_pos[0] < ENV_SIZE[0] and 0 <= new_pos[1] < ENV_SIZE[1] and 0 <= new_pos[2] < ENV_SIZE[2]:
if environment[int(new_pos[0]), int(new_pos[1]), int(new_pos[2])].type != SOIL:
self.position = new_pos

# Update body segment positions based on movement
for i in range(1, NUM_BODY_SEGMENTS):
self.body_segments[i] = self.body_segments[0] + np.array([i * ANT_SIZE / 3 * math.cos(self.direction[0]), i * ANT_SIZE/3 * math.sin(self.direction[1]), 0]) # Simple segment rotation for now

# Inverse Kinematics for legs with 3D collision detection
for i in range(NUM_LEGS):
# ... (calculate start_pos)

# Check if current target is still reachable
if np.linalg.norm(start_pos - self.leg_targets[i]) > MAX_LEG_STRETCH:
# Find new target with 3D collision detection


# Define search directions around the leg (more directions mean better coverage but worse performance):
search_directions = np.array([
[0, 1, 0], [0, -1, 0], [1, 0, 0], [-1, 0, 0], [0, 0, -1], # basic directions
[1,1,0], [1,-1,0], [-1,1,0], [-1,-1,0],
[1, 0, -1], [-1, 0, -1], [0, 1, -1], [0, -1, -1],
[1,1,-1], [1,-1,-1], [-1,1,-1], [-1,-1,-1]
])



closest_surface = None
closest_distance = float('inf')

for direction in search_directions:
x,y,z = int(start_pos[0]), int(start_pos[1]), int(start_pos[2])


for j in range(int(MAX_LEG_STRETCH)): # loop through voxels in direction
nx,ny,nz = x + int(j * direction[0]), y + int(j * direction[1]), z + int(j * direction[2])


if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx,ny,nz].type == SOIL:

dist = np.linalg.norm(start_pos - np.array([nx,ny,nz]))
if dist < closest_distance:
closest_distance = dist
closest_surface = np.array([nx, ny, nz])

break

if closest_surface is not None:
self.leg_targets[i] = closest_surface
else:
self.leg_targets[i] = start_pos + self.direction * LEG_LENGTH # move target forward if no ground is found


# Simplified IK calculation for two leg segments:
target = self.leg_targets[i]
dist_to_target = np.linalg.norm(start_pos-target)
if dist_to_target > 0:
mid_pos = start_pos + (target - start_pos) / 2 + np.array([0,0,LEG_LENGTH/2 * (1-(dist_to_target / MAX_LEG_STRETCH)) ])
# Adjust mid point height dynamically based on leg stretch

self.leg_positions[i] = [start_pos, mid_pos, target]

# 3D body rotation (simplified):
for i in range(NUM_BODY_SEGMENTS - 1):
segment_direction = self.body_segments[i+1] - self.body_segments[i]
angle = math.atan2(segment_direction[1], segment_direction[0]) # Calculate angle in x-y plane
self.body_rotations[i] = angle # apply angle



# Advanced antenna animation (exploration behavior):
for i in range(2): # 2 antennae
# Simplified exploration by random target around the ant:
self.antenna_targets[i] = self.body_segments[-1] + np.random.rand(3) * ANT_SIZE - ANT_SIZE/2 # Target somewhere around the ant's head.

# simple "dampened" movement of antennae toward target point
antenna_base = self.body_segments[-1]
self.antenna_positions[i] = 0.9 * self.antenna_positions[i] + 0.1 * (self.antenna_targets[i]- antenna_base)

def forage(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])

if environment[x, y, z].type == FOOD:
self.has_food = True
environment[x, y, z].type = AIR # consume food

elif self.has_food and (x, y, z) == self.colony.nest_entrance:
self.has_food = False
self.colony.food_count += 1

# Deposit pheromone at the nest entrance:
environment[x,y,z].food_pheromone = 1.0


# Pheromone Deposit for food trail:
if self.has_food:
environment[x, y, z].food_pheromone = 1.0


def draw(self, screen):
# Detail culling
distance_to_camera = np.linalg.norm(self.body_segments[0] - camera_pos)
if distance_to_camera > CULL_DISTANCE:
# Draw simplified representation (e.g., only body)
screen_x, screen_y = project_to_screen(self.body_segments[0])
pygame.draw.circle(screen, (255, 0, 0), (screen_x, screen_y), ANT_SIZE / zoom)
return

# Draw rotated body segments

for i in range(NUM_BODY_SEGMENTS):
screen_x, screen_y = project_to_screen(self.body_segments[i])

# Draw segment
segment_size = ANT_SIZE / 2 / zoom * (1-(i/NUM_BODY_SEGMENTS)*0.5)
pygame.draw.circle(screen, (255, 0, 0), (screen_x, screen_y), segment_size)



if i > 0: # Connect segments with rotated lines

prev_screen_x, prev_screen_y = project_to_screen(self.body_segments[i-1])
pygame.draw.line(screen, (255, 0, 0), (prev_screen_x, prev_screen_y), (screen_x, screen_y), int(ANT_SIZE / 3 / zoom))

# Draw legs (IK-based)
for i in range(NUM_LEGS):
if len(self.leg_positions[i]) > 0:
start_pos, mid_pos, target = self.leg_positions[i]


screen_x1, screen_y1 = project_to_screen(start_pos)
screen_x2, screen_y2 = project_to_screen(mid_pos)
screen_x3, screen_y3 = project_to_screen(target)
pygame.draw.line(screen, (0, 0, 0), (screen_x1, screen_y1), (screen_x2, screen_y2), int(ANT_SIZE / 6 / zoom))
pygame.draw.line(screen, (0, 0, 0), (screen_x2, screen_y2), (screen_x3, screen_y3), int(ANT_SIZE / 6 / zoom))

# Draw antennae:
for i in range(2):
screen_x1, screen_y1 = project_to_screen(self.body_segments[-1])
screen_x2, screen_y2 = project_to_screen(self.body_segments[-1] + self.antenna_positions[i])

pygame.draw.line(screen, (0, 0, 0), (screen_x1, screen_y1), (screen_x2, screen_y2), int(ANT_SIZE / 6 / zoom))

class Colony:
def __init__(self, nest_entrance):
self.nest_entrance = nest_entrance # Tuple (x, y, z)
self.ants = []
self.food_count = 0

# ... (functions: project_to_screen, etc.)


def generate_environment(grid, voxel_types):

# basic flat surface
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
environment[x,y,0] = Voxel(SOIL)



# Simplified object generation (example: stick)

stick_start = np.array([random.randint(0, ENV_SIZE[0]-1), random.randint(0, ENV_SIZE[1]-1),1]) # put it a little bit above the ground
stick_end = stick_start + np.array([random.randint(-5,5),random.randint(-5,5),random.randint(5,10)]) # create a stick that can be placed in different positions


# place stick voxels
stick_line = np.linspace(stick_start, stick_end, 10).astype(int)

for x, y, z in stick_line:

if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
voxel_types[x,y,z] = SOIL



# ... (generate other objects: stones, leaves, etc.)

grid.point_data["voxel_types"] = voxel_types.flatten() # Update pyvista with new data

return grid, voxel_types





def draw_environment(grid):


plotter.update()
plotter.screenshot("screenshot.png")


screenshot = pygame.image.load("screenshot.png").convert() # load screenshot

# Get the current image on the plotter and display it in Pygame
screen.blit(screenshot, (0,0))



# Add camera controls
def camera_controls(plotter, event):
global zoom, camera_pos, camera_target
if event.type == pygame.MOUSEWHEEL:
if event.y > 0: # Scrolling up (zoom in)
zoom *= 1.2
else:
zoom /= 1.2
zoom = max(0.1, zoom)

# Panning with right mouse button:
elif event.type == pygame.MOUSEMOTION:
if event.buttons[2]: # Right mouse button
dx, dy = event.rel
camera_pos[0] += dx / zoom * 0.1
camera_pos[1] += dy / zoom * 0.1
camera_target[0] += dx / zoom * 0.1
camera_target[1] += dy / zoom * 0.1



def project_to_screen(position):
# Very basic projection - needs improvement for perspective and rotation
relative_pos = position - camera_target
rotated_x = relative_pos[0] * math.cos(camera_rotation[1]) + relative_pos[2] * math.sin(camera_rotation[1])
rotated_z = -relative_pos[0] * math.sin(camera_rotation[1]) + relative_pos[2] * math.cos(camera_rotation[1])

screen_x = width/2 + rotated_x * zoom * 10
screen_y = height/2 + (relative_pos[1] * zoom * 10 - rotated_z * zoom * 5)
return screen_x, screen_y


def main():


global grid, voxel_types

grid, voxel_types = generate_environment(grid, voxel_types)


# ... (colony initialization)
running = True

while running:

# Pygame event handling (for user input)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# add camera mouse controls here later

# ... (ant logic)

# Camera zoom and panning:
keys = pygame.key.get_pressed()

if keys[pygame.K_EQUALS]: # zoom in with +
zoom += 0.1

if keys[pygame.K_MINUS]: # zoom out with -
zoom -= 0.1
zoom = max(0.1, zoom) # Prevent zooming too far out



# Clear screen
screen.fill((0, 0, 0))

draw_environment(grid) # draw the environment
for ant in colony.ants:
ant.forage(environment)
ant.leg_positions = [[]] * NUM_LEGS # Clear leg positions to be recalculated dynamically in the ant's move method

ant.move(environment)
ant.draw(screen) # Draw the ant

pygame.display.flip()
clock.tick(30) # Limit framerate

pygame.quit()


if __name__ == "__main__":
main()


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# Environment parameters
ENV_SIZE = (100, 100, 50) # Increase environment size
VOXEL_SIZE = 0.2 # smaller voxel size: 0.3 * ANT_SIZE ≈ 0.2 (ANT_SIZE = 0.5)
INITIAL_ANTS = 20
FOOD_SPAWN_RATE = 0.05
PHEROMONE_EVAPORATION_RATE = 0.01
FOOD_PHEROMONE_THRESHOLD = 0.5 # Threshold for ants to follow food pheromone

# Voxel types
AIR = 0
SOIL = 1
FOOD = 2
NEST_MATERIAL = 3

# Ant parameters
ANT_SIZE = 0.5 # Size of an ant (adjust as needed)
LEG_LENGTH = ANT_SIZE / 3
ANTENNA_LENGTH = ANT_SIZE / 2
NUM_LEGS = 6
NUM_ANTENNA_SEGMENTS = 3 # This parameter is not directly used anymore, but let's keep it for potential use.
NUM_BODY_SEGMENTS = 3
MAX_LEG_STRETCH = LEG_LENGTH * 1.2 # Maximum leg stretch

# Detail culling distance
CULL_DISTANCE = 50

# Gravity (not used directly, but kept for potential future use)
GRAVITY = np.array([0, 0, -0.1])


class Voxel:
def __init__(self, voxel_type):
self.type = voxel_type
self.food_pheromone = 0.0 # Initialize pheromone level to 0

def evaporate_pheromone(self):
self.food_pheromone = max(0, self.food_pheromone - PHEROMONE_EVAPORATION_RATE)


class Ant:
def __init__(self, position, colony):
# ... (other attributes)
self.body_segments = [np.array(position) + np.array([i * ANT_SIZE / 3, 0, 0]) for i in
range(NUM_BODY_SEGMENTS)]
self.body_rotations = [0.0] * (NUM_BODY_SEGMENTS - 1) # Rotation angles between body segments
self.leg_angles = [0.0] * NUM_LEGS # For leg animation
self.antenna_angles = [0.0] * NUM_ANTENNA_SEGMENTS * 2 # for antenna animation (2 antennae)
self.leg_targets = [np.array(position) for _ in range(NUM_LEGS)] # initialize leg targets
self.antenna_targets = [np.zeros(3) for _ in range(2)]
self.leg_positions = [[]] * NUM_LEGS # Clear leg positions to be recalculated dynamically in the ant's move method
self.antenna_positions = [np.zeros(3) for _ in range(2)] # set antenna positions before movement logic is applied

self.has_food = False
self.direction = np.random.rand(3) - 0.5 # Random initial direction

def move(self, environment):

# Pheromone influence on movement
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])
pheromone_direction = np.zeros(3)

for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0, 1]: # consider 3D Pheromone
nx, ny, nz = x + dx, y + dy, z + dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
pheromone_strength = environment[nx, ny, nz].food_pheromone
if pheromone_strength > FOOD_PHEROMONE_THRESHOLD and not self.has_food: # Ants with food don't follow food pheromones.
pheromone_direction += np.array([dx, dy, dz]) * pheromone_strength

# Combine random walk with pheromone direction
self.direction += np.random.rand(3) * 0.2 - 0.1 + pheromone_direction * 0.5 # Adjust pheromone influence as needed

self.direction = self.direction / np.linalg.norm(self.direction) # Normalize

new_pos = self.position + self.direction

if 0 <= new_pos[0] < ENV_SIZE[0] and 0 <= new_pos[1] < ENV_SIZE[1] and 0 <= new_pos[2] < ENV_SIZE[2]:
if environment[int(new_pos[0]), int(new_pos[1]), int(new_pos[2])].type != SOIL:
self.position = new_pos

# Update body segment positions based on movement
for i in range(1, NUM_BODY_SEGMENTS):
self.body_segments[i] = self.body_segments[0] + np.array(
[i * ANT_SIZE / 3 * math.cos(self.direction[0]), i * ANT_SIZE / 3 * math.sin(self.direction[1]), 0]) # Simple segment rotation for now

# Inverse Kinematics for legs with 3D collision detection
for i in range(NUM_LEGS):
start_pos = self.body_segments[i // (NUM_LEGS // NUM_BODY_SEGMENTS)]

if np.linalg.norm(start_pos - self.leg_targets[i]) > MAX_LEG_STRETCH:
# Find new target with 3D collision detection

x, y, z = int(start_pos[0]), int(start_pos[1]), int(start_pos[2])

ground_z = -1

for dz in range(int(start_pos[2]), -1, -1):
nx, ny, nz = x, y, dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx, ny, nz].type == SOIL:
ground_z = nz

self.leg_targets[i] = np.array([nx, ny, ground_z]) # Place leg on colliding surface
break # stop loop to prevent placing the target position below a colliding voxel

if ground_z == -1:
self.leg_targets[i] = start_pos + self.direction * LEG_LENGTH

# Simplified IK calculation for two leg segments:
target = self.leg_targets[i]
dist_to_target = np.linalg.norm(start_pos - target)
if dist_to_target > 0:
mid_pos = start_pos + (target - start_pos) / 2 + np.array(
[0, 0, LEG_LENGTH / 2 * (1 - (dist_to_target / MAX_LEG_STRETCH))])
# Adjust mid point height dynamically based on leg stretch

self.leg_positions[i] = [start_pos, mid_pos, target]

# 3D body rotation (simplified):
for i in range(NUM_BODY_SEGMENTS - 1):
segment_direction = self.body_segments[i + 1] - self.body_segments[i]
angle = math.atan2(segment_direction[1], segment_direction[0]) # Calculate angle in x-y plane
self.body_rotations[i] = angle # apply angle

# Advanced antenna animation (exploration behavior):
for i in range(2): # 2 antennae
# Simplified exploration by random target around the ant:
self.antenna_targets[i] = self.body_segments[-1] + np.random.rand(3) * ANT_SIZE - ANT_SIZE / 2 # Target somewhere around the ant's head.

# simple "dampened" movement of antennae toward target point
antenna_base = self.body_segments[-1]
self.antenna_positions[i] = 0.9 * self.antenna_positions[i] + 0.1 * (self.antenna_targets[i] - antenna_base)

def forage(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])

if environment[x, y, z].type == FOOD:
self.has_food = True
environment[x, y, z].type = AIR # consume food

elif self.has_food and (x, y, z) == self.colony.nest_entrance:
self.has_food = False
self.colony.food_count += 1

# Deposit pheromone at the nest entrance:
environment[x, y, z].food_pheromone = 1.0

# Pheromone Deposit for food trail:
if self.has_food:
environment[x, y, z].food_pheromone = 1.0

def draw(self, screen):
# Detail culling
distance_to_camera = np.linalg.norm(self.body_segments[0] - camera_pos)
if distance_to_camera > CULL_DISTANCE:
# Draw simplified representation (e.g., only body)
screen_x, screen_y = project_to_screen(self.body_segments[0])
pygame.draw.circle(screen, (255, 0, 0), (screen_x, screen_y), ANT_SIZE / zoom)
return

# Draw rotated body segments

for i in range(NUM_BODY_SEGMENTS):
screen_x, screen_y = project_to_screen(self.body_segments[i])

# Draw segment
segment_size = ANT_SIZE / 2 / zoom * (1 - (i / NUM_BODY_SEGMENTS) * 0.5)
pygame.draw.circle(screen, (255, 0, 0), (screen_x, screen_y), segment_size)

if i > 0: # Connect segments with rotated lines
prev_screen_x, prev_screen_y = project_to_screen(self.body_segments[i - 1])
pygame.draw.line(screen, (255, 0, 0), (prev_screen_x, prev_screen_y), (screen_x, screen_y),
int(ANT_SIZE / 3 / zoom))

# Draw legs (IK-based)
for i in range(NUM_LEGS):
if len(self.leg_positions[i]) > 0:
start_pos, mid_pos, target = self.leg_positions[i]

screen_x1, screen_y1 = project_to_screen(start_pos)
screen_x2, screen_y2 = project_to_screen(mid_pos)
screen_x3, screen_y3 = project_to_screen(target)
pygame.draw.line(screen, (0, 0, 0), (screen_x1, screen_y1), (screen_x2, screen_y2),
int(ANT_SIZE / 6 / zoom))
pygame.draw.line(screen, (0, 0, 0), (screen_x2, screen_y2), (screen_x3, screen_y3),
int(ANT_SIZE / 6 / zoom))

# Draw antennae:
for i in range(2):
screen_x1, screen_y1 = project_to_screen(self.body_segments[-1])
screen_x2, screen_y2 = project_to_screen(self.body_segments[-1] + self.antenna_positions[i])

pygame.draw.line(screen, (0, 0, 0), (screen_x1, screen_y1), (screen_x2, screen_y2),
int(ANT_SIZE / 6 / zoom))


class Colony:
def __init__(self, nest_entrance):
self.nest_entrance = nest_entrance # Tuple (x, y, z)
self.ants = []
self.food_count = 0


# ... (functions: project_to_screen, etc.)


def generate_environment(grid, voxel_types):

# Use OpenSimplex for more varied terrain
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
height = int(opensimplex.noise2(x / 10, y / 10) * 5 + ENV_SIZE[2] / 4)
for z in range(height):
voxel_types[x, y, z] = SOIL

# Generate objects (example: stick, stone, leaf)
def generate_object(start_pos, end_pos, material):
object_line = np.linspace(start_pos, end_pos, random.randint(5, 15)).astype(
int) # Vary object size

for x, y, z in object_line:

if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:

voxel_types[x, y, z] = material

# Generate various objects at random locations and angles.

for _ in range(10): # Number of sticks
start_pos = np.array(
[random.randint(0, ENV_SIZE[0] - 1), random.randint(0, ENV_SIZE[1] - 1), random.randint(1, ENV_SIZE[2] - 1)])
end_pos = start_pos + np.array(
[random.randint(-5, 5), random.randint(-5, 5), random.randint(-10, 10)]) # Allow sticks at any angle
generate_object(start_pos, end_pos, SOIL) # Use SOIL material for stick

for _ in range(5): # Number of stones
center = np.array(
[random.randint(0, ENV_SIZE[0] - 1), random.randint(0, ENV_SIZE[1] - 1), random.randint(1, ENV_SIZE[2] - 1)])

radius = random.randint(1, 3)
for x in range(center[0] - radius, center[0] + radius + 1):
for y in range(center[1] - radius, center[1] + radius + 1):
for z in range(center[2] - radius, center[2] + radius + 1): # make stones roughly spherical
dist_to_center = np.linalg.norm(np.array([x, y, z]) - center)
if dist_to_center <= radius:
if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
voxel_types[x, y, z] = SOIL

grid.point_data["voxel_types"] = voxel_types.flatten()
return grid, voxel_types


def draw_environment(grid):
plotter.update()
plotter.screenshot("screenshot.png")

screenshot = pygame.image.load("screenshot.png").convert() # load screenshot

# Get the current image on the plotter and display it in Pygame
screen.blit(screenshot, (0, 0))


# Add camera controls
def camera_controls(plotter, event):
global zoom, camera_pos, camera_target
if event.type == pygame.MOUSEWHEEL:
if event.y > 0: # Scrolling up (zoom in)
zoom *= 1.2
else:
zoom /= 1.2
zoom = max(0.1, zoom)

# Panning with right mouse button:
elif event.type == pygame.MOUSEMOTION:
if event.buttons[2]: # Right mouse button
dx, dy = event.rel
camera_pos[0] += dx / zoom * 0.1
camera_pos[1] += dy / zoom * 0.1
camera_target[0] += dx / zoom * 0.1
camera_target[1] += dy / zoom * 0.1


def project_to_screen(position):
# Very basic projection - needs improvement for perspective and rotation
relative_pos = position - camera_target
rotated_x = relative_pos[0] * math.cos(camera_rotation[1]) + relative_pos[2] * math.sin(camera_rotation[1])
rotated_z = -relative_pos[0] * math.sin(camera_rotation[1]) + relative_pos[2] * math.cos(camera_rotation[1])

screen_x = width / 2 + rotated_x * zoom * 10
screen_y = height / 2 + (relative_pos[1] * zoom * 10 - rotated_z * zoom * 5)
return screen_x, screen_y


def main():
global grid, voxel_types, camera_pos, camera_target, zoom

# Pygame initialization
pygame.init()
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

# Camera parameters
camera_pos = np.array([ENV_SIZE[0] / 2, ENV_SIZE[1] / 2, ENV_SIZE[2] / 2]) # Start in the middle of the environment.
camera_target = np.array([ENV_SIZE[0] / 2, ENV_SIZE[1] / 2, 0])
zoom = 1.0
camera_rotation = np.array([0.0, 0.0]) # rotation around x and y axis

# PyVista initialization
pv.set_plot_theme("document") # simpler theme
plotter = pv.Plotter(off_screen=True) # for faster rendering. change to False, if you want to see the scene

grid = pv.UniformGrid() # use UniformGrid to store voxels
grid.dimensions = np.array(ENV_SIZE) + 1 # add 1 for correct dimensions
grid.spacing = (VOXEL_SIZE, VOXEL_SIZE, VOXEL_SIZE)
grid.origin = (0, 0, 0)

# Add a scalar array to hold voxel types. Initially all air.
voxel_types = np.zeros(ENV_SIZE, dtype=int)
grid.point_data["voxel_types"] = voxel_types.flatten()

plotter.add_mesh(grid, name="environment") # add grid to plotter

# Initialize environment and colony
grid, voxel_types = generate_environment(grid, voxel_types)
environment = np.zeros(ENV_SIZE, dtype=object)
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
environment[x, y, z] = Voxel(voxel_types[x, y, z]) # Initialize environment voxels
nest_entrance = (ENV_SIZE[0] // 2, ENV_SIZE[1] // 2, 0) # Nest entrance at center ground level
colony = Colony(nest_entrance)

# Create initial ants
for _ in range(INITIAL_ANTS):
x, y = random.randint(0, ENV_SIZE[0] - 1), random.randint(0, ENV_SIZE[1] - 1)
colony.ants.append(Ant((x, y, 0), colony)) # Start ants at ground level

running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
camera_controls(plotter, event) # call camera control function.

# Rotate Camera:
plotter.camera_position = [(camera_pos[0] * VOXEL_SIZE, camera_pos[1] * VOXEL_SIZE, camera_pos[2] * VOXEL_SIZE),
(camera_target[0] * VOXEL_SIZE, camera_target[1] * VOXEL_SIZE,
camera_target[2] * VOXEL_SIZE),
(0, 0, 1)]
for ant in colony.ants:
ant.forage(environment)
ant.leg_positions = [[]] * NUM_LEGS # Clear leg positions to be recalculated dynamically in the ant's move method
ant.move(environment)
ant.draw(screen)

draw_environment(grid)
pygame.display.flip()
clock.tick(30) # Limit framerate

pygame.quit()


if __name__ == "__main__":
main()


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# ... (environment parameters, voxel types, etc.)

# ... (Ant class, Colony class, Voxel class)

# ... (functions: project_to_screen, generate_environment, draw_environment, camera_controls)

def main():
# ... (Pygame initialization, camera parameters, PyVista initialization, environment and colony initialization)

running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
camera_controls(plotter, event)

# Rotate Camera:
plotter.camera_position = [(camera_pos[0] * VOXEL_SIZE, camera_pos[1] * VOXEL_SIZE, camera_pos[2] * VOXEL_SIZE),
(camera_target[0] * VOXEL_SIZE, camera_target[1] * VOXEL_SIZE,
camera_target[2] * VOXEL_SIZE),
(0, 0, 1)]

# Ant movement
for ant in colony.ants:

# 1. Foraging:
ant.forage(environment)

# 2. Movement:
ant.move(environment)

# 3. Leg and antenna animation:
# 3a. Legs
for i in range(NUM_LEGS):
ant.leg_positions[i] = [] # Reset leg positions (important to recalculate for each step)
ant.move(environment) # Recalculate leg positions

# 3b. Antennae
for i in range(2):
# Simplified exploration by random target around the ant:
ant.antenna_targets[i] = ant.body_segments[-1] + np.random.rand(3) * ANT_SIZE - ANT_SIZE / 2
# simple "dampened" movement of antennae toward target point
antenna_base = ant.body_segments[-1]
ant.antenna_positions[i] = 0.9 * ant.antenna_positions[i] + 0.1 * (
ant.antenna_targets[i] - antenna_base)

# 4. Draw ant:
ant.draw(screen)

# Draw environment
draw_environment(grid)

# ... (rest of the main loop: pygame.display.flip(), clock.tick(30))

pygame.quit()


if __name__ == "__main__":
main()


---!!!BREAK!!!---

def build_nest(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])

# Check if close to nest:
if np.linalg.norm(self.position - self.colony.nest_entrance) < 5: # Adjust distance as needed

# Check if carrying food:
if not self.has_food:

# Try to pick up material:
if environment[x, y, z].type == SOIL: # Check for a soil voxel
if random.random() < 0.2: # Probability of picking up material
environment[x, y, z].type = AIR
self.carrying_material = True

# Try to place material:
if self.carrying_material:
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0, 1]: # Look for a nearby voxel
nx, ny, nz = x + dx, y + dy, z + dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx, ny, nz].type == AIR: # Check for an air voxel
if random.random() < 0.5: # Probability of placing material
environment[nx, ny, nz].type = NEST_MATERIAL
self.carrying_material = False
return


---!!!BREAK!!!---


def build_nest(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])

# Check if close to nest:
if np.linalg.norm(self.position - self.colony.nest_entrance) < 5:

# Check if carrying food:
if not self.has_food:

# Try to pick up material:
if environment[x, y, z].type == SOIL:
if random.random() < 0.2:
environment[x, y, z].type = AIR
self.carrying_material = True

# Try to place material:
if self.carrying_material:
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0, 1]:
nx, ny, nz = x + dx, y + dy, z + dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx, ny, nz].type == AIR and (
environment[nx, ny, nz - 1].type == NEST_MATERIAL or random.random() < 0.1):
# Place material on top of existing nest or randomly inside
environment[nx, ny, nz].type = NEST_MATERIAL
self.carrying_material = False
return

# Try to dig a tunnel (less likely):
if random.random() < 0.05:
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0, 1]:
nx, ny, nz = x + dx, y + dy, z + dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx, ny, nz].type == SOIL and (
environment[nx, ny, nz - 1].type == NEST_MATERIAL or random.random() < 0.1):
environment[nx, ny, nz].type = AIR
return


---!!!BREAK!!!---

# ... (Voxel class with alpha value)
class Voxel:
def __init__(self, voxel_type):
self.type = voxel_type
self.food_pheromone = 0.0
self.alpha = 255 # Default: Opaque

# ... (draw_environment function)
def draw_environment(grid, tunnel_mode):
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
voxel = environment[x, y, z]

# Set alpha for tunnel mode
if tunnel_mode and voxel.type == NEST_MATERIAL:
# ... (Implement raycasting logic to determine visibility)
if voxel is visible_from_inside:
voxel.alpha = 255 # Opaque if inside the tunnel
else:
voxel.alpha = 128 # Transparent if outside the tunnel

# Draw voxel with alpha
draw_voxel(voxel, (x, y, z))

# ... (main function)
def main():
# ... (Initialize variables: tunnel_mode = False)

running = True
while running:
# ... (event handling, camera control)

# "Tunnelblick"-Modus aktivieren/deaktivieren:
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_t:
tunnel_mode = not tunnel_mode

draw_environment(grid, tunnel_mode)
# ... (draw ants)

# ... (rest of the main loop)


---!!!BREAK!!!---


# ... (other code)

def draw_environment(grid, tunnel_mode, camera_pos): # Pass camera position to the function
# ... (other code in draw_environment)

if tunnel_mode and voxel.type == NEST_MATERIAL:
voxel_center = np.array([x, y, z]) + VOXEL_SIZE/2 # Calculate center of voxel

camera_to_voxel = voxel_center - camera_pos

# Very simplified approximation: Check if camera is inside or outside the nest based on distance to center:
distance_to_nest_center = np.linalg.norm(camera_pos - self.colony.nest_entrance) # make sure colony.nest_entrance is defined

if distance_to_nest_center < 5: # Assuming nest radius is 5. Adjust as needed!
voxel.alpha = 255 # opaque inside
else:
voxel.alpha = 128 # transparent outside


# Draw voxel with alpha
draw_voxel(voxel, (x, y, z))


# ... (main function)
def main():

# ...

while running:
# ...
draw_environment(grid, tunnel_mode, camera_pos) # Pass camera position
# ...


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# Environment parameters
ENV_SIZE = (100, 100, 50) # Increase environment size
VOXEL_SIZE = 0.2 # smaller voxel size: 0.3 * ANT_SIZE ≈ 0.2 (ANT_SIZE = 0.5)
INITIAL_ANTS = 20
FOOD_SPAWN_RATE = 0.05
PHEROMONE_EVAPORATION_RATE = 0.01
FOOD_PHEROMONE_THRESHOLD = 0.5 # Threshold for ants to follow food pheromone

# Voxel types
AIR = 0
SOIL = 1
FOOD = 2
NEST_MATERIAL = 3

# Ant parameters
ANT_SIZE = 0.5 # Size of an ant (adjust as needed)
LEG_LENGTH = ANT_SIZE / 3
ANTENNA_LENGTH = ANT_SIZE / 2
NUM_LEGS = 6
NUM_ANTENNA_SEGMENTS = 3 # This parameter is not directly used anymore, but let's keep it for potential use.
NUM_BODY_SEGMENTS = 3
MAX_LEG_STRETCH = LEG_LENGTH * 1.2 # Maximum leg stretch

# Detail culling distance
CULL_DISTANCE = 50

# Gravity (not used directly, but kept for potential future use)
GRAVITY = np.array([0, 0, -0.1])

# Tunnel mode
tunnel_mode = False


class Voxel:
def __init__(self, voxel_type):
self.type = voxel_type
self.food_pheromone = 0.0 # Initialize pheromone level to 0
self.alpha = 255 # Default: Opaque

def evaporate_pheromone(self):
self.food_pheromone = max(0, self.food_pheromone - PHEROMONE_EVAPORATION_RATE)


class Ant:
# ... (same as before)


class Colony:
def __init__(self, nest_entrance):
self.nest_entrance = nest_entrance # Tuple (x, y, z)
self.ants = []
self.food_count = 0


# Pygame initialization
pygame.init()
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
clock = pygame.time.Clock()

# Camera parameters
camera_pos = np.array([ENV_SIZE[0] / 2, ENV_SIZE[1] / 2, ENV_SIZE[2] / 2]) # Start in the middle of the environment.
camera_target = np.array([ENV_SIZE[0] / 2, ENV_SIZE[1] / 2, 0])
zoom = 1.0
camera_rotation = np.array([0.0, 0.0]) # rotation around x and y axis

# PyVista initialization
pv.set_plot_theme("document") # simpler theme
plotter = pv.Plotter(off_screen=True) # for faster rendering. change to False, if you want to see the scene

grid = pv.UniformGrid() # use UniformGrid to store voxels
grid.dimensions = np.array(ENV_SIZE) + 1 # add 1 for correct dimensions
grid.spacing = (VOXEL_SIZE, VOXEL_SIZE, VOXEL_SIZE)
grid.origin = (0, 0, 0)

# Add a scalar array to hold voxel types. Initially all air.
voxel_types = np.zeros(ENV_SIZE, dtype=int)
grid.point_data["voxel_types"] = voxel_types.flatten()

plotter.add_mesh(grid, name="environment") # add grid to plotter


# ... (generate_environment function - same as before)


def draw_environment(grid, tunnel_mode, camera_pos, colony): # Pass camera position and colony to the function
global environment

# Update PyVista grid with current voxel data
grid.point_data["voxel_types"] = [voxel.type for voxel in environment.flatten()]
plotter.update()
plotter.screenshot("screenshot.png")

screenshot = pygame.image.load("screenshot.png").convert() # load screenshot

# Get the current image on the plotter and display it in Pygame
screen.blit(screenshot, (0, 0))


# ... (camera_controls function - same as before)


def project_to_screen(position):
# ... (same as before)


def main():
global grid, voxel_types, camera_pos, camera_target, zoom, environment, tunnel_mode

# ... (Initialize environment and colony - same as before)

running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
camera_controls(plotter, event)

# Toggle tunnel mode with 'T' key
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_t:
tunnel_mode = not tunnel_mode

# Rotate Camera:
plotter.camera_position = [(camera_pos[0] * VOXEL_SIZE, camera_pos[1] * VOXEL_SIZE, camera_pos[2] * VOXEL_SIZE),
(camera_target[0] * VOXEL_SIZE, camera_target[1] * VOXEL_SIZE,
camera_target[2] * VOXEL_SIZE),
(0, 0, 1)]

# Ant logic
for ant in colony.ants:
ant.forage(environment)
ant.leg_positions = [[]] * NUM_LEGS # Reset leg positions
ant.move(environment)
ant.build_nest(environment)
ant.draw(screen)

# Apply tunnel view after ant logic to show updated nest structure
if tunnel_mode:
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
voxel = environment[x, y, z]
if voxel.type == NEST_MATERIAL:
voxel_center = np.array([x, y, z]) + VOXEL_SIZE / 2 # Calculate center of voxel
camera_to_voxel = voxel_center - camera_pos
distance_to_nest_center = np.linalg.norm(
camera_pos - colony.nest_entrance) # Assuming nest is centered at nest entrance
if distance_to_nest_center < 10: # Adjust this value based on nest size
voxel.alpha = 255 # Opaque inside the nest
else:
voxel.alpha = 128 # Transparent outside the nest

draw_environment(grid, tunnel_mode, camera_pos, colony) # Pass tunnel mode, camera position, and colony
pygame.display.flip()
clock.tick(30) # Limit framerate

pygame.quit()


if __name__ == "__main__":
main()


---!!!BREAK!!!---


def build_nest(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])

if self.carrying_material:
# Search for suitable place to deposit material (e.g., next to existing nest material)

best_nx, best_ny, best_nz = -1,-1,-1
best_neighbors = 0



for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0, 1]:
nx, ny, nz = x + dx, y + dy, z + dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:

if environment[nx, ny, nz].type == AIR:
current_neighbors = 0

for nnx in [-1,0,1]:
for nny in [-1,0,1]:
for nnz in [-1,0,1]:
annx, anny, annz = nx + nnx, ny+nny, nz+nnz
if 0 <= annx < ENV_SIZE[0] and 0 <= anny < ENV_SIZE[1] and 0 <= annz < ENV_SIZE[2]:
if environment[annx, anny, annz].type == NEST_MATERIAL:
current_neighbors+=1


if current_neighbors > best_neighbors:
best_neighbors = current_neighbors
best_nx, best_ny, best_nz = nx, ny, nz

if best_nx != -1:
environment[best_nx,best_ny,best_nz].type = NEST_MATERIAL
self.carrying_material = False



# Try to dig tunnel (with small probability):
elif random.random() < 0.02: # Reduced probability
# ... (Digging logic - similar as before)


---!!!BREAK!!!---


def generate_environment(grid, voxel_types):
# ... (terrain generation - similar as before)


def generate_object(material, min_size, max_size): # Function for object generation with more detailed arguments

size = random.randint(min_size, max_size)


# ... (logic for placing the object with varied sizes, shapes and distributions)

generate_object(SOIL, 2, 5) # Generate small stones
generate_object(SOIL, 5, 10) # Generate larger stones
# ... (generate other objects)


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# ... (other code)

def is_voxel_supported(environment, x, y, z):
"""Checks if a voxel is supported by other voxels below or beside it."""
if z == 0: # Ground level is always supported
return True
for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:
for dz in [-1, 0,1]: # check neighbors below and beside.
nx, ny, nz = x + dx, y + dy, z + dz
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx, ny, nz].type != AIR:
return True # Supported if a neighboring voxel exists


return False # Not supported


def build_nest(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])


if self.carrying_material:
# ... (Find best place to deposit material using neighboring voxel count as before)

elif random.random() < 0.02: # tunnel digging
# ... (Digging logic - similar as before)

# physics check: (only check nest material)
if environment[x,y,z].type == NEST_MATERIAL: # Only check nest voxels
if not is_voxel_supported(environment, x, y, z):
environment[x, y, z].type = AIR # Remove unsupported voxel


# In the main loop:
def main():
# ... (initialization)

while running:
# ... (event handling, camera, ant movement, drawing)

for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
environment[x,y,z].evaporate_pheromone() # evaporate pheromones
ant.build_nest(environment) # Let the ant potentially build or modify the nest


# Apply tunnel view... (same as before)
# ...


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# ... (other code)

def apply_physics(environment):
"""Applies simple physics to the environment (falling voxels)."""
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[1]-1, 0, -1): # Iterate from top to bottom
if environment[x, y, z].type != AIR and not is_voxel_supported(environment, x, y, z):
# Find the lowest empty spot below
nz = z - 1

while nz >= 0 and environment[x, y, nz].type != AIR:
nz -=1

if nz >=0:
# move to empty spot
environment[x,y,nz].type = environment[x,y,z].type #copy type
environment[x, y, z].type = AIR # Remove original voxel


def is_voxel_supported(environment, x, y, z):
"""Checks if a voxel is supported by neighbors below or beside."""
if z == 0:
return True


for dx in [-1, 0, 1]:
for dy in [-1, 0, 1]:

nx, ny, nz = x + dx, y + dy, z - 1 # Only check voxels below and beside
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:

if environment[nx, ny, nz].type != AIR:
return True

return False





def build_nest(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])


if self.carrying_material:

# ... (Find best place to deposit material using neighboring voxel count as before)

elif random.random() < 0.02: # tunnel digging
if environment[x,y,z].type == SOIL: # only remove soil (not other nest materials)

if random.random() < 0.2: # reduce possibility of digging for larger chambers
environment[x, y, z].type = AIR



# ... other functions


# In the main loop:
def main():
# ... (initialization)

while running:

# ... (event handling, camera, ant movement, drawing)


# Apply physics:
apply_physics(environment)

for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
environment[x,y,z].evaporate_pheromones() # evaporate pheromones



# Apply tunnel view... (same as before)
# ...


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# ... (other code)


def apply_physics(environment, x, y, z): # Only apply physics around the changed voxel
"""Applies simple physics to the environment (falling voxels) around a specific location."""
for dx in [-2, -1, 0, 1, 2]: # expand range a little bit
for dy in [-2, -1, 0, 1, 2]:
for dz in range(z + 2, max(0,z-3),-1): # only check area above the changed voxel (2 voxels) and area below (3 Voxel) the changed voxel
nx, ny, nz = x + dx, y + dy, dz


if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx, ny, nz].type != AIR and not is_voxel_supported(environment, nx, ny, nz):
# Find the lowest empty spot below
lnz = nz - 1
while lnz >= 0 and environment[nx, ny, lnz].type != AIR:
lnz -= 1

if lnz >= 0:
environment[nx, ny, lnz].type = environment[nx, ny, nz].type # Copy type
environment[nx, ny, nz].type = AIR # Remove original voxel


def is_voxel_supported(environment, x, y, z):
# ... (same as before)



def build_nest(self, environment):
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])


if self.carrying_material:
# Find best place to deposit material using neighboring voxel count
# ... same as before

apply_physics(environment, best_nx, best_ny, best_nz) # apply physics


elif random.random() < 0.02: # tunnel digging
if environment[x,y,z].type == SOIL:

if random.random() < 0.2:
environment[x, y, z].type = AIR
apply_physics(environment, x, y, z) # apply physics after removing material





# ... other functions

# In the main loop:
def main():
# ... (initialization)

while running:

# ... (event handling, camera, ant movement, drawing)

for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
environment[x,y,z].evaporate_pheromones()


# Apply tunnel view... (same as before)
# ...


---!!!BREAK!!!---


def generate_environment(grid, voxel_types):
# Terrain generation with OpenSimplex and varying heights
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
height = int(opensimplex.noise2(x / 20, y / 20) * 10 + ENV_SIZE[2] / 4) # Adjust scale and height as needed
for z in range(height):
voxel_types[x, y, z] = SOIL


# Generate objects with varying sizes and materials
def generate_object(material, min_size, max_size):
size = random.randint(min_size, max_size)
x = random.randint(0, ENV_SIZE[0] - size)
y = random.randint(0, ENV_SIZE[1] - size)
z = int(opensimplex.noise2(x / 20, y / 20) * 10 + ENV_SIZE[2] / 4) # Place objects on the terrain

for dx in range(size):
for dy in range(size):

if 0 <= x + dx < ENV_SIZE[0] and 0 <= y + dy < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
voxel_types[x + dx, y + dy, z] = material

# Example object generation:
for _ in range(5): # Generate a few larger stones
generate_object(SOIL, 5, 15)
for _ in range(20): # Generate more smaller stones
generate_object(SOIL, 2, 5)
# ... generate other objects (leaves, sticks, etc.) with varying parameters

grid.point_data["voxel_types"] = voxel_types.flatten()

return grid, voxel_types


---!!!BREAK!!!---


def generate_environment(grid, voxel_types):

# 1. Terrain and moisture:
height_noise = opensimplex.OpenSimplex(seed=random.randint(0,10000))
moisture_noise = opensimplex.OpenSimplex(seed=random.randint(0,10000)) # Separate noise for moisture

for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
height = int(height_noise.noise2(x / 20, y / 20) * 10 + ENV_SIZE[2] / 4)
moisture = moisture_noise.noise2(x / 40, y / 40) # Lower frequency for larger moisture variations

for z in range(height):
if moisture < -0.5: # Example: Drier areas get different soil
voxel_types[x, y, z] = SOIL # SAND (you'll need to define this type)
else:
voxel_types[x, y, z] = SOIL

# 2. Procedural object generation (example: simple plant with L-system like logic):


if moisture > 0 and random.random() < 0.1: # plant grows at higher moisture and a given chance:
plant_height = random.randint(2, 5)
for pz in range(height, height + plant_height):
if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= pz < ENV_SIZE[2]:
voxel_types[x, y, pz] = FOOD # Or a PLANT type, if you define one


# 3. Generate rocks (example with random shape):
for _ in range(10): # Number of rocks
center_x = random.randint(0, ENV_SIZE[0] - 1)
center_y = random.randint(0, ENV_SIZE[1] - 1)
center_z = int(height_noise.noise2(center_x / 20, center_y / 20) * 10 + ENV_SIZE[2] / 4) # height according to terrain
radius = random.randint(2, 5)
irregularity = random.uniform(0.5, 1.5) # For more natural shape

for x in range(center_x - radius, center_x + radius + 1):
for y in range(center_y - radius, center_y + radius + 1):
for z in range(center_z, center_z + radius + 1):
distance = np.linalg.norm(np.array([x, y, z]) - np.array([center_x, center_y, center_z]))
if distance <= radius * irregularity * random.random(): # Add irregularity
if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= z < ENV_SIZE[2]:
voxel_types[x,y,z] = SOIL

grid.point_data["voxel_types"] = voxel_types.flatten()
return grid, voxel_types


---!!!BREAK!!!---


# Smaller grass blade
small_grass = np.array([
[[0, 0, 1]],
[[0, 0, 1]],
[[0, 0, 1]]
]) # 3 voxels high

# Larger grass blade
large_grass = np.array([
[[0, 0, 1]],
[[0, 0, 1]],
[[0, 0, 1]],
[[0, 0, 1]],
[[0, 0, 1]]

]) # 5 voxels high


# Dandelion (simplified)
dandelion = np.array([
[[0, 1, 0]], # Leaf
[[0, 1, 0]], # Leaf
[[0, 0, 2]], # Yellow flower
[[0,0,2]]
]) # Slightly higher flower


---!!!BREAK!!!---


def generate_environment(grid, voxel_types):
# ... (terrain and moisture generation - same as before)

def place_object(obj, x, y, z, scale=1, rotation=0): # Add parameters for scale and rotation
# Implement rotation and scaling here (using matrix transformations if needed) and then place voxels
# ...

# Place grass blades (denser for small grass)
for _ in range(200): # Number of small grass blades
x = random.randint(0, ENV_SIZE[0] - 1)
y = random.randint(0, ENV_SIZE[1] - 1)
z = # Height based on the terrain (same as before)
place_object(small_grass, x, y, z, scale=random.uniform(0.8, 1.2), rotation=random.randint(0, 360))

for _ in range(50): # Number of larger grass blades (less dense)
# ... (similar placement logic)

# Place dandelions (less frequent)
for _ in range(20): # number of dandelions
# ... (place dandelion with variations in height using scaling).


# ... (generate rocks, etc. - similar as before)


---!!!BREAK!!!---


class Ant:
# ... (other methods)

def move(self, environment):

# ... (pheromone influence)

new_pos = self.position + self.direction


# Height adjustment (using IK)

target_height = 0

for dz in range(int(new_pos[2])+1, int(new_pos[2])-5, -1): # Search for a voxel to stand on
x, y = int(new_pos[0]), int(new_pos[1])

if 0 <= x < ENV_SIZE[0] and 0 <= y < ENV_SIZE[1] and 0 <= dz < ENV_SIZE[2]:
if environment[x, y, dz].type != AIR: # Use type != AIR to find suitable surface
target_height = dz # set Height

if environment[x,y,dz].type == FOOD: # climb food (simplified)

new_pos[2] += 0.1 # move upwards

break # voxel to stand on found, so stop searching

new_pos[2] = target_height




# ... (check environment bounds and set position as before).


# Inverse Kinematics for legs with 3D collision detection
# ... (same as before)


---!!!BREAK!!!---


class Ant:

# ... other methods

def move(self, environment):

# 1. Local exploration:
x, y, z = int(self.position[0]), int(self.position[1]), int(self.position[2])

# Check for pheromones in the vicinity
pheromone_direction = np.zeros(3)

search_radius = 3 # Adjust search radius

for dx in range(-search_radius, search_radius+1):
for dy in range(-search_radius, search_radius+1):

nx, ny = x + dx, y + dy

if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1]:
pheromone_strength = environment[nx, ny, z].food_pheromone
if pheromone_strength > FOOD_PHEROMONE_THRESHOLD: # adjust threshold if needed
pheromone_direction += (np.array([nx,ny,z]) - self.position) * pheromone_strength # move towards the source




# 2. Random movement with direction changes
if np.linalg.norm(pheromone_direction) > 0 : # follow pheromone
self.direction = pheromone_direction / np.linalg.norm(pheromone_direction)

elif random.random() < 0.2: # Adjust probability for direction changes
self.direction = np.random.rand(3) - 0.5 # random direction change
self.direction[2] = 0 # Prevent vertical movement for now

self.direction = self.direction / np.linalg.norm(self.direction)

new_pos = self.position + self.direction * self.speed

# 3. Collision detection and climbing
nx, ny, nz = int(new_pos[0]), int(new_pos[1]), int(new_pos[2])


if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:
if environment[nx, ny, nz].type == AIR:

# 4. Height adjustment and gravity (simplified):

target_height = 0
for dz in range(nz+1, nz-5, -1):
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= dz < ENV_SIZE[2]:
if environment[nx,ny,dz].type != AIR:
target_height = dz
break


new_pos[2] = target_height
self.position = new_pos

elif environment[nx,ny,nz].type == FOOD: # climb if possible

new_pos[2]+=0.1 # climbing logic (adjust speed as needed)
self.position = new_pos



# ... (rest of the movement logic)



# ... (rest of the ant class - same as before)


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# ... (other code - same as before)


class Ant:

# ... (other methods)
def move(self, environment):
# ... (pheromone influence - same as before)

# 1. Movement with collision detection and climbing
new_pos = self.position + self.direction * self.speed

nx, ny, nz = int(new_pos[0]), int(new_pos[1]), int(new_pos[2])

if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= nz < ENV_SIZE[2]:

if environment[nx, ny, nz].type == AIR: # Check if the new position is empty
# 2. Height adjustment (maintain contact with the surface)
target_height = -1 # initialize it
for dz in range(nz + 1, nz - 5, -1): # Search downwards for voxel
if 0 <= nx < ENV_SIZE[0] and 0 <= ny < ENV_SIZE[1] and 0 <= dz < ENV_SIZE[2]:
if environment[nx, ny, dz].type != AIR:
target_height = dz
break

if target_height != -1: # If ground is found
new_pos[2] = target_height
self.position = new_pos


elif environment[nx, ny, nz].type == FOOD: # climb
new_pos[2] += 0.1 # Climbing logic (adjust speed as needed)
self.position = new_pos

else: # Obstacle: investigate with antennae before deciding next action
# Antennae investigation (simplified)
for i in range(2):
self.antenna_targets[i] = np.array([nx, ny, nz]) + np.random.rand(3) * ANT_SIZE - ANT_SIZE / 2
antenna_base = self.body_segments[-1]
self.antenna_positions[i] = 0.9 * self.antenna_positions[i] + 0.1 * (self.antenna_targets[i] - antenna_base)

if random.random() < 0.5: # 50% chance to climb, 50% chance to turn
new_pos[2] += 0.1 # Climbing
else:
self.direction = np.random.rand(3) - 0.5 # Turn randomly
self.direction[2] = 0



# Sun-based orientation (simplified)
if np.linalg.norm(pheromone_direction) == 0 and random.random() < 0.1: # Low probability if no pheromones and random chance
sun_direction = np.array([1, 0, 0]) # Replace with actual sun direction if available
self.direction += sun_direction * 0.1 # Move slightly towards the sun



# ... (Inverse Kinematics for legs - same as before)

# ... (rest of ant class)


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# ... (Environment parameters, voxel types, etc.)

# ... (Ant class, Colony class, Voxel class)

# Day/Night cycle parameters
DAY_LENGTH = 1000 # Number of simulation steps for a full day/night cycle
current_time = 0


# Pheromone visualization
PHEROMONE_VISUALIZATION_SCALE = 20

# ... (Other functions: project_to_screen, camera_controls)



def generate_environment(grid, voxel_types):
# ... (same as before, using OpenSimplex, microhabitats, object placement with variations)




def draw_environment(grid, tunnel_mode, camera_pos, colony, show_pheromones): # Add show_pheromones parameter
global environment
# ... (same as before, rendering terrain and objects)

if show_pheromones:

for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
pheromone_strength = environment[x, y, z].food_pheromone
if pheromone_strength > 0:
pheromone_color = (0, 255, 0, int(pheromone_strength* 255)) # green, adjust alpha based on strength.
# Draw pheromone visualization (e.g., a small square or circle at the voxel position).

screen_x, screen_y = project_to_screen(np.array([x, y, z]))
pygame.draw.circle(screen, pheromone_color, (screen_x, screen_y), int(PHEROMONE_VISUALIZATION_SCALE * pheromone_strength / zoom))






def main():

# ... (initialization of pygame, pyvista, environment, colony)

show_pheromones = False # Initially hide pheromones



running = True
while running:
for event in pygame.event.get():
# ... (event handling, camera control, quit event)

if event.type == pygame.KEYDOWN:

if event.key == pygame.K_p: # Toggle pheromone visualization with 'P' key
show_pheromones = not show_pheromones



# Day/Night Cycle and Sun/Moon position:

current_time = (current_time + 1) % DAY_LENGTH
sun_angle = (current_time / DAY_LENGTH) * 2 * math.pi # angle from 0 to 2pi

sun_x = math.sin(sun_angle)
sun_y = math.cos(sun_angle)



sun_direction = np.array([sun_x, sun_y, 0.5]) # Simulate sun direction (adjust z as needed)
sun_direction = sun_direction / np.linalg.norm(sun_direction)


if sun_x < 0: # if sun is "below" horizon
sun_direction *= 0.1 # moon light, low intensity


# ... (Rotate Camera - same as before)


# Ant logic
for ant in colony.ants:
ant.forage(environment)


# Sun-based orientation (using the calculated sun_direction):
if np.linalg.norm(ant.pheromone_direction) == 0 and random.random() < 0.1:
ant.direction += sun_direction * 0.1 # Adjust influence as needed




ant.leg_positions = [[]] * NUM_LEGS # Reset leg positions
ant.move(environment)
ant.build_nest(environment)
ant.draw(screen)


# ... (Apply tunnel view if enabled - same as before)


draw_environment(grid, tunnel_mode, camera_pos, colony, show_pheromones) # Pass show_pheromones parameter
pygame.display.flip()
clock.tick(30) # Limit framerate

pygame.quit()



if __name__ == "__main__":
main()


---!!!BREAK!!!---


import pygame
import random
import numpy as np
import opensimplex
import math
import pyvista as pv

# ... (Environment parameters, voxel types, etc.)

# ... (Ant class, Colony class, Voxel class - Voxel class needs pheromone types)

# Day/Night cycle parameters
DAY_LENGTH = 1000 # Number of simulation steps for a full day/night cycle
current_time = 0

# Pheromone types
FOOD_PHEROMONE = 0
HOME_PHEROMONE = 1 # New pheromone type

# Pheromone visualization
PHEROMONE_VISUALIZATION_SCALE = 20

# UI elements
button_font = pygame.font.SysFont(None, 30)
show_pheromones_button = pygame.Rect(width - 150, 10, 140, 40) # Button rect
show_pheromones = False
# ... (Other functions: project_to_screen, camera_controls)

class Voxel: # Add pheromone types to voxel
def __init__(self, voxel_type):
# ... other attributes
self.pheromones = [0.0] * 2 # food and home pheromones

def evaporate_pheromones(self):
self.pheromones = [max(0, p - PHEROMONE_EVAPORATION_RATE) for p in self.pheromones]




def generate_environment(grid, voxel_types):
# ... (same as before, using OpenSimplex, microhabitats, object placement with variations)



def draw_environment(grid, tunnel_mode, camera_pos, colony, show_pheromones):
# ... (same as before, rendering terrain and objects)

if show_pheromones:
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
for pheromone_type in [FOOD_PHEROMONE, HOME_PHEROMONE]:
pheromone_strength = environment[x, y, z].pheromones[pheromone_type]
if pheromone_strength > 0:
pheromone_color = (0, 255, 0) if pheromone_type == FOOD_PHEROMONE else (0, 0, 255) # green for food, blue for home

screen_x, screen_y = project_to_screen(np.array([x, y, z]))

pygame.draw.circle(screen, pheromone_color, (screen_x, screen_y),
int(PHEROMONE_VISUALIZATION_SCALE * pheromone_strength / zoom))




def main():


# ... (initialization of pygame, pyvista, environment, colony)



running = True

while running:


# ... (event handling)

# Mouse click handling for pheromone button
if event.type == pygame.MOUSEBUTTONDOWN:
if show_pheromones_button.collidepoint(event.pos):
show_pheromones = not show_pheromones



# Day/Night Cycle and Sun/Moon position:
# ... (same as before)



# Render the button
pygame.draw.rect(screen, (100, 100, 100), show_pheromones_button)
text = button_font.render("Show Pheromones", True, (255, 255, 255))
screen.blit(text, (show_pheromones_button.x + 10, show_pheromones_button.y + 10))


# Ant logic

for ant in colony.ants:

# ... (foraging - same as before)

# Pheromone handling (dynamic and by type):

x,y,z = int(ant.position[0]), int(ant.position[1]), int(ant.position[2])


# Home pheromone: drop near nest entrance
if np.linalg.norm(ant.position - colony.nest_entrance) < 5:

environment[x,y,z].pheromones[HOME_PHEROMONE] = 1.0



# Food pheromone: drop when carrying food
if ant.has_food:


environment[x, y, z].pheromones[FOOD_PHEROMONE] = max(environment[x,y,z].pheromones[FOOD_PHEROMONE], 1 - (np.linalg.norm(ant.position - colony.nest_entrance) / 50)) # Pheromone strength decreases with distance from nest





# Sun-based orientation (only during daytime):

if sun_direction[2] > 0: # is sun above the horizon?



if np.linalg.norm(ant.pheromone_direction) == 0 and random.random() < 0.1: # Use sun for orientation only if no pheromones detected.
ant.direction += sun_direction * 0.1 # slightly turn towards sun




# ... (rest of ant.move(), 3d climbing - same logic as before or as modified, integrate the climbing logic for all objects if needed)


ant.leg_positions = [[]] * NUM_LEGS # Reset leg positions

ant.move(environment)




ant.build_nest(environment)

ant.draw(screen)



# ... (Apply tunnel view if enabled - same as before)



for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
environment[x, y, z].evaporate_pheromones()



draw_environment(grid, tunnel_mode, camera_pos, colony, show_pheromones)
pygame.display.flip()
clock.tick(30)

pygame.quit()

if __name__ == "__main__":
main()


---!!!BREAK!!!---


# ... (other code)

def main():
# ... (initialization)

while running:
# ... (event handling, camera, etc.)

# Ant logic
for ant in colony.ants:
ant.forage(environment)

# Pheromone handling (dynamic and by type):
x, y, z = int(ant.position[0]), int(ant.position[1]), int(ant.position[2])

# Home pheromone: drop near nest entrance
if np.linalg.norm(ant.position - colony.nest_entrance) < 5:
environment[x, y, z].pheromones[HOME_PHEROMONE] = 1.0

# Food pheromone: drop when carrying food
if ant.has_food:
environment[x, y, z].pheromones[FOOD_PHEROMONE] = 1.0 # Constant strength

# ... (rest of ant logic, movement, drawing)

# Pheromone evaporation (after all ants have moved)
for x in range(ENV_SIZE[0]):
for y in range(ENV_SIZE[1]):
for z in range(ENV_SIZE[2]):
environment[x, y, z].evaporate_pheromones()

# ... (draw environment, etc.)


---!!!BREAK!!!---


# ... (other code)

class Voxel:
def __init__(self, voxel_type):
# ... (other attributes)
self.pheromones = [0.0] * 2 # food and home pheromones

def evaporate_pheromones(self):
self.pheromones = [max(0, p - (PHEROMONE_EVAPORATION_RATE / (1+p))) for p in self.pheromones] # slower evaporation for stronger trails

# ... (other code)


def main():
# ... (initialization)

while running:
# ... (event handling, camera, etc.)

for ant in colony.ants:
# ... (foraging, etc.)

# Pheromone handling (dynamic and accumulating):
x, y, z = int(ant.position[0]), int(ant.position[1]), int(ant.position[2])


if np.linalg.norm(ant.position - colony.nest_entrance) < 5:
environment[x, y, z].pheromones[HOME_PHEROMONE] = min(environment[x,y,z].pheromones[HOME_PHEROMONE] +1, 10) # accumulate up to a value (e.g., 10)



if ant.has_food:


environment[x, y, z].pheromones[FOOD_PHEROMONE] = min(environment[x,y,z].pheromones[FOOD_PHEROMONE]+1, 10) # accumulate up to a value (e.g., 10)



# ... (rest of ant logic, movement, drawing)


# ... (draw environment, apply tunnel view, etc.)