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.)