Enhance simulation performance and add sprint mode functionality
Some checks failed
Build Simulation and Test / Run All Tests (push) Failing after 30s

This commit is contained in:
Sam 2025-06-16 16:57:03 -05:00
parent 8a53d4ac37
commit deee83edb3
5 changed files with 95 additions and 7 deletions

View File

@ -25,9 +25,9 @@ CELL_SIZE = 20
RENDER_BUFFER = 50
# Performance settings
DEFAULT_TPS = 20
DEFAULT_TPS = 40
MAX_FPS = 180
TURBO_MULTIPLIER = 4
TURBO_MULTIPLIER = 8
# Camera settings
DEFAULT_CAMERA_SPEED = 700
@ -44,6 +44,7 @@ SELECTION_THRESHOLD = 3 # pixels
# Simulation settings
FOOD_SPAWNING = True
FOOD_OBJECTS_COUNT = 100
RANDOM_SEED = 0
# Vector visualization settings
@ -58,7 +59,7 @@ DIRECTION_TIP_SIZE = 3
MAX_ACCELERATION = 0.1
MAX_ANGULAR_ACCELERATION = 0.25
MAX_VELOCITY = 0.5
MAX_ROTATIONAL_VELOCITY = 6
MAX_ROTATIONAL_VELOCITY = 3
KEYMAP_LEGEND = [
("WASD", "Move camera"),

View File

@ -25,6 +25,7 @@ class InputHandler:
# Speed control
self.tps = DEFAULT_TPS
self.default_tps = DEFAULT_TPS
self.sprint_mode = False
def handle_events(self, events):
"""Process all pygame events and return game state."""
@ -74,6 +75,8 @@ class InputHandler:
self.tps = self.default_tps * TURBO_MULTIPLIER
elif event.key == pygame.K_r:
self.camera.reset_position()
elif event.key == pygame.K_RSHIFT:
self.sprint_mode = True # Enter sprint mode
return running
@ -81,6 +84,8 @@ class InputHandler:
"""Handle keyup events."""
if event.key == pygame.K_LSHIFT:
self.tps = self.default_tps
if event.key == pygame.K_RSHIFT:
self.sprint_mode = False # Exit sprint mode
def _handle_mouse_down(self, event):
"""Handle mouse button down events."""

View File

@ -39,9 +39,12 @@ class SimulationEngine:
random.seed(RANDOM_SEED)
if FOOD_SPAWNING:
world.add_object(FoodObject(Position(x=random.randint(-100, 100), y=random.randint(-100, 100))))
for _ in range(FOOD_OBJECTS_COUNT):
x = random.randint(-100, 100)
y = random.randint(-100, 100)
world.add_object(FoodObject(Position(x=x, y=y)))
for _ in range(1):
for _ in range(20):
world.add_object(DefaultCell(Position(x=random.randint(-100, 100), y=random.randint(-100, 100)), Rotation(angle=0)))
return world
@ -60,6 +63,30 @@ class SimulationEngine:
# Handle events
self.running = self.input_handler.handle_events(pygame.event.get())
if self.input_handler.sprint_mode:
# Sprint mode: run as many ticks as possible, skip rendering
current_time = time.perf_counter()
while True:
self.input_handler.update_selected_objects()
self.world.tick_all()
self.tick_counter += 1
self.total_ticks += 1
# Optionally break after some time to allow event processing
if time.perf_counter() - current_time > 0.05: # ~50ms per batch
break
# Update TPS every second
if time.perf_counter() - self.last_tps_time >= 1.0:
self.actual_tps = self.tick_counter
self.tick_counter = 0
self.last_tps_time = time.perf_counter()
# No rendering or camera update
self.renderer.clear_screen()
self.hud.render_sprint_debug(self.screen, self.actual_tps, self.total_ticks)
pygame.display.flip()
self.clock.tick(MAX_FPS)
return
if not self.input_handler.is_paused:
current_time = time.perf_counter()
while current_time - self.last_tick_time >= tick_interval:

View File

@ -462,3 +462,18 @@ class HUD:
for surf in tooltip_surfs:
screen.blit(surf, (tooltip_rect.left + TOOLTIP_PADDING_X, y))
y += surf.get_height() + TOOLTIP_LINE_SPACING
def render_sprint_debug(self, screen, actual_tps, total_ticks):
"""Render sprint debug info: header, TPS, and tick count."""
header = self.font.render("Sprinting...", True, (255, 200, 0))
tps_text = self.font.render(f"TPS: {actual_tps}", True, (255, 255, 255))
ticks_text = self.font.render(f"Ticks: {total_ticks}", True, (255, 255, 255))
y = SCREEN_HEIGHT // 2 - 40
header_rect = header.get_rect(center=(SCREEN_WIDTH // 2, y))
tps_rect = tps_text.get_rect(center=(SCREEN_WIDTH // 2, y + 40))
ticks_rect = ticks_text.get_rect(center=(SCREEN_WIDTH // 2, y + 80))
screen.blit(header, header_rect)
screen.blit(tps_text, tps_rect)
screen.blit(ticks_text, ticks_rect)

View File

@ -250,6 +250,8 @@ class DefaultCell(BaseEntity):
self.rotational_velocity: int = 0
self.angular_acceleration: int = 0
self.energy: int = 1000
self.behavioral_model: CellBrain = CellBrain()
self.max_visual_width: int = 10
@ -274,6 +276,17 @@ class DefaultCell(BaseEntity):
:return: Self.
"""
if self.energy == 0:
# too hungry lmao
self.flag_for_death()
return self
self.energy -= 1
if self.tick_count > 2000:
# too old lmao
self.flag_for_death()
if interactable is None:
interactable = []
@ -287,14 +300,40 @@ class DefaultCell(BaseEntity):
food_object = FoodObject(self.position)
angle_between_food = self.calculate_angle_between_food(self.position.get_position(), self.rotation.get_rotation(), food_object.position.get_position())
distance_to_food = get_distance_between_objects(self, food_object)
if distance_to_food < self.max_visual_width and food_objects:
self.energy += 100
food_object.flag_for_death()
return self
if self.energy >= 1500:
# too much energy, split
duplicate_x, duplicate_y = self.position.get_position()
duplicate_x += random.randint(-self.interaction_radius, self.interaction_radius)
duplicate_y += random.randint(-self.interaction_radius, self.interaction_radius)
duplicate_x_2, duplicate_y_2 = self.position.get_position()
duplicate_x_2 += random.randint(-self.interaction_radius, self.interaction_radius)
duplicate_y_2 += random.randint(-self.interaction_radius, self.interaction_radius)
new_cell = DefaultCell(Position(x=int(duplicate_x), y=int(duplicate_y)), Rotation(angle=random.randint(0, 359)))
new_cell.set_brain(self.behavioral_model.mutate(1))
new_cell_2 = DefaultCell(Position(x=int(duplicate_x_2), y=int(duplicate_y_2)), Rotation(angle=random.randint(0, 359)))
new_cell_2.set_brain(self.behavioral_model.mutate(1))
return [new_cell, new_cell_2]
input_data = {
"distance": get_distance_between_objects(self, food_object),
"distance": distance_to_food,
"angle": angle_between_food,
}
output_data = self.behavioral_model.tick(input_data)
# everything below this point is physics simulation and needs to be extracted to a separate class
# clamp accelerations
output_data["linear_acceleration"] = max(-MAX_ACCELERATION, min(MAX_ACCELERATION, output_data["linear_acceleration"]))
output_data["angular_acceleration"] = max(-MAX_ANGULAR_ACCELERATION, min(MAX_ANGULAR_ACCELERATION, output_data["angular_acceleration"]))
@ -408,4 +447,5 @@ class DefaultCell(BaseEntity):
position = f"({round(self.position.x, 1)}, {round(self.position.y, 1)})"
velocity = tuple(round(value, 1) for value in self.velocity)
acceleration = tuple(round(value, 1) for value in self.acceleration)
return f"DefaultCell(position={position}, velocity={velocity}, acceleration={acceleration}"
rotation = round(self.rotation.get_rotation(), 1)
return f"DefaultCell(position={position}, velocity={velocity}, acceleration={acceleration}, rotation={rotation}, energy={self.energy}, age={self.tick_count})"