Compare commits
	
		
			No commits in common. "master" and "ui-rework" have entirely different histories.
		
	
	
		
	
		
| @ -19,8 +19,8 @@ SELECTION_GRAY = (128, 128, 128, 80) | |||||||
| SELECTION_BORDER = (80, 80, 90) | SELECTION_BORDER = (80, 80, 90) | ||||||
| 
 | 
 | ||||||
| # Grid settings | # Grid settings | ||||||
| GRID_WIDTH = 50 | GRID_WIDTH = 30 | ||||||
| GRID_HEIGHT = 50 | GRID_HEIGHT = 25 | ||||||
| CELL_SIZE = 20 | CELL_SIZE = 20 | ||||||
| RENDER_BUFFER = 50 | RENDER_BUFFER = 50 | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ import pygame | |||||||
| import math | import math | ||||||
| from config.constants import * | from config.constants import * | ||||||
| from world.base.brain import CellBrain | from world.base.brain import CellBrain | ||||||
| from world.objects import DefaultCell |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Renderer: | class Renderer: | ||||||
| @ -14,8 +13,11 @@ class Renderer: | |||||||
|         self.render_height = render_area.get_height() |         self.render_height = render_area.get_height() | ||||||
|         self.render_width = render_area.get_width() |         self.render_width = render_area.get_width() | ||||||
| 
 | 
 | ||||||
|     def clear_screen(self): |     def clear_screen(self, main_screen=None): | ||||||
|         """Clear the screen with a black background.""" |         """Clear the screen with a black background.""" | ||||||
|  |         if main_screen: | ||||||
|  |             main_screen.fill(BLACK) | ||||||
|  | 
 | ||||||
|         self.render_area.fill(BLACK) |         self.render_area.fill(BLACK) | ||||||
| 
 | 
 | ||||||
|     def draw_grid(self, camera, showing_grid=True): |     def draw_grid(self, camera, showing_grid=True): | ||||||
| @ -99,9 +101,6 @@ class Renderer: | |||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
|         for obj in world.get_objects(): |         for obj in world.get_objects(): | ||||||
|             if not isinstance(obj, DefaultCell): |  | ||||||
|                 continue |  | ||||||
| 
 |  | ||||||
|             obj_x, obj_y = obj.position.get_position() |             obj_x, obj_y = obj.position.get_position() | ||||||
|             radius = obj.interaction_radius |             radius = obj.interaction_radius | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -18,29 +18,21 @@ from ui.hud import HUD | |||||||
| class SimulationEngine: | class SimulationEngine: | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         pygame.init() |         pygame.init() | ||||||
|         self._init_window() |  | ||||||
|         self._init_ui() |  | ||||||
|         self._init_simulation() |  | ||||||
|         self.running = True |  | ||||||
| 
 | 
 | ||||||
|     def _init_window(self): |  | ||||||
|         info = pygame.display.Info() |         info = pygame.display.Info() | ||||||
|         self.window_width = int(info.current_w // 1.5) |         self.window_width, self.window_height = info.current_w // 2, info.current_h // 2 | ||||||
|         self.window_height = int(info.current_h // 1.5) |         self.screen = pygame.display.set_mode((self.window_width, self.window_height), | ||||||
|         self.screen = pygame.display.set_mode( |                                               pygame.RESIZABLE, vsync=1) | ||||||
|             (self.window_width, self.window_height), | 
 | ||||||
|             pygame.RESIZABLE, vsync=1 |         self.ui_manager = UIManager((self.window_width, self.window_height)) | ||||||
|         ) | 
 | ||||||
|  |         self.camera = Camera(SCREEN_WIDTH, SCREEN_HEIGHT, RENDER_BUFFER) | ||||||
|  |         self._update_simulation_view() | ||||||
|  | 
 | ||||||
|  |         # self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), vsync=1) | ||||||
|         pygame.display.set_caption("Dynamic Abstraction System Testing") |         pygame.display.set_caption("Dynamic Abstraction System Testing") | ||||||
|         self.clock = pygame.time.Clock() |         self.clock = pygame.time.Clock() | ||||||
| 
 | 
 | ||||||
|     def _init_ui(self): |  | ||||||
|         self.ui_manager = UIManager((self.window_width, self.window_height)) |  | ||||||
|         self.hud = HUD(self.ui_manager, self.window_width, self.window_height) |  | ||||||
|         self.hud.update_layout(self.window_width, self.window_height) |  | ||||||
|         self._update_simulation_view() |  | ||||||
| 
 |  | ||||||
|     def _init_simulation(self): |  | ||||||
|         self.last_tick_time = time.perf_counter() |         self.last_tick_time = time.perf_counter() | ||||||
|         self.last_tps_time = time.perf_counter() |         self.last_tps_time = time.perf_counter() | ||||||
|         self.tick_counter = 0 |         self.tick_counter = 0 | ||||||
| @ -50,17 +42,21 @@ class SimulationEngine: | |||||||
|         self.world = self._setup_world() |         self.world = self._setup_world() | ||||||
|         self.input_handler = InputHandler(self.camera, self.world, self.sim_view_rect) |         self.input_handler = InputHandler(self.camera, self.world, self.sim_view_rect) | ||||||
|         self.renderer = Renderer(self.sim_view) |         self.renderer = Renderer(self.sim_view) | ||||||
|  |         self.hud = HUD(self.ui_manager, self.window_width, self.window_height) | ||||||
|  |         self.hud.update_layout(self.window_width, self.window_height) | ||||||
|  | 
 | ||||||
|  |         self.running = True | ||||||
| 
 | 
 | ||||||
|     def _update_simulation_view(self): |     def _update_simulation_view(self): | ||||||
|         viewport_rect = self.hud.get_viewport_rect() |         self.sim_view_width = int(self.window_width * 0.75) | ||||||
|         self.sim_view_width = viewport_rect.width |         self.sim_view_height = int(self.window_height * 0.75) | ||||||
|         self.sim_view_height = viewport_rect.height |  | ||||||
|         self.sim_view = pygame.Surface((self.sim_view_width, self.sim_view_height)) |         self.sim_view = pygame.Surface((self.sim_view_width, self.sim_view_height)) | ||||||
|         self.sim_view_rect = self.sim_view.get_rect(topleft=(viewport_rect.left, viewport_rect.top)) |         self.sim_view_rect = self.sim_view.get_rect(center=(self.window_width // 2, self.window_height // 2)) | ||||||
| 
 | 
 | ||||||
|         self.ui_manager.set_window_resolution((self.window_width, self.window_height)) |         self.ui_manager.set_window_resolution((self.window_width, self.window_height)) | ||||||
|         self.renderer = Renderer(self.sim_view) |         self.renderer = Renderer(self.sim_view) | ||||||
| 
 | 
 | ||||||
|  |         # Update camera to match new sim_view size | ||||||
|         if hasattr(self, 'camera'): |         if hasattr(self, 'camera'): | ||||||
|             self.camera.screen_width = self.sim_view_width |             self.camera.screen_width = self.sim_view_width | ||||||
|             self.camera.screen_height = self.sim_view_height |             self.camera.screen_height = self.sim_view_height | ||||||
| @ -68,8 +64,6 @@ class SimulationEngine: | |||||||
|         if hasattr(self, 'input_handler'): |         if hasattr(self, 'input_handler'): | ||||||
|             self.input_handler.update_sim_view_rect(self.sim_view_rect) |             self.input_handler.update_sim_view_rect(self.sim_view_rect) | ||||||
| 
 | 
 | ||||||
|         if not hasattr(self, 'camera'): |  | ||||||
|             self.camera = Camera(self.sim_view_width, self.sim_view_height, RENDER_BUFFER) |  | ||||||
| 
 | 
 | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _setup_world(): |     def _setup_world(): | ||||||
| @ -81,32 +75,23 @@ class SimulationEngine: | |||||||
| 
 | 
 | ||||||
|         if FOOD_SPAWNING: |         if FOOD_SPAWNING: | ||||||
|             for _ in range(FOOD_OBJECTS_COUNT): |             for _ in range(FOOD_OBJECTS_COUNT): | ||||||
|                 x = random.randint(-half_width // 2, half_width // 2) |                 x = random.randint(-half_width, half_width) | ||||||
|                 y = random.randint(-half_height // 2, half_height // 2) |                 y = random.randint(-half_height, half_height) | ||||||
|                 world.add_object(FoodObject(Position(x=x, y=y))) |                 world.add_object(FoodObject(Position(x=x, y=y))) | ||||||
| 
 | 
 | ||||||
|         for _ in range(350): |         for _ in range(300): | ||||||
|             new_cell = DefaultCell( |             new_cell = DefaultCell(Position(x=random.randint(-half_width, half_width), y=random.randint(-half_height, half_height)), Rotation(angle=0)) | ||||||
|                 Position(x=random.randint(-half_width // 2, half_width // 2), y=random.randint(-half_height // 2, half_height // 2)), | 
 | ||||||
|                 Rotation(angle=0) |  | ||||||
|             ) |  | ||||||
|             new_cell.behavioral_model = new_cell.behavioral_model.mutate(3) |             new_cell.behavioral_model = new_cell.behavioral_model.mutate(3) | ||||||
|  | 
 | ||||||
|             world.add_object(new_cell) |             world.add_object(new_cell) | ||||||
| 
 | 
 | ||||||
|         return world |         return world | ||||||
| 
 | 
 | ||||||
|     def _count_cells(self): |  | ||||||
|         count = 0 |  | ||||||
|         for entity in self.world.get_objects(): |  | ||||||
|             if isinstance(entity, DefaultCell): |  | ||||||
|                 count += 1 |  | ||||||
|         return count |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def run(self): |     def run(self): | ||||||
|         print(self.world.current_buffer) |  | ||||||
|         while self.running: |         while self.running: | ||||||
|             self._handle_frame() |             self._handle_frame() | ||||||
|  | 
 | ||||||
|         pygame.quit() |         pygame.quit() | ||||||
|         sys.exit() |         sys.exit() | ||||||
| 
 | 
 | ||||||
| @ -114,24 +99,53 @@ class SimulationEngine: | |||||||
|         deltatime = self.clock.get_time() / 1000.0 |         deltatime = self.clock.get_time() / 1000.0 | ||||||
|         tick_interval = 1.0 / self.input_handler.tps |         tick_interval = 1.0 / self.input_handler.tps | ||||||
| 
 | 
 | ||||||
|  |         # Handle events | ||||||
|         events = pygame.event.get() |         events = pygame.event.get() | ||||||
|         self.running = self.input_handler.handle_events(events, self.hud.manager) |         self.running = self.input_handler.handle_events(events, self.hud.manager) | ||||||
|         self._handle_window_events(events) | 
 | ||||||
|  |         for event in events: | ||||||
|  |             if event.type == pygame.VIDEORESIZE: | ||||||
|  |                 self.window_width, self.window_height = event.w, event.h | ||||||
|  |                 self.screen = pygame.display.set_mode((self.window_width, self.window_height), | ||||||
|  |                                                       pygame.RESIZABLE) | ||||||
|  |                 self._update_simulation_view() | ||||||
|  |                 self.hud.update_layout(self.window_width, self.window_height) | ||||||
| 
 | 
 | ||||||
|         if self.input_handler.sprint_mode: |         if self.input_handler.sprint_mode: | ||||||
|             self._handle_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 |             return | ||||||
| 
 | 
 | ||||||
|         # Only process one tick per frame if enough time has passed |  | ||||||
|         if not self.input_handler.is_paused: |         if not self.input_handler.is_paused: | ||||||
|             current_time = time.perf_counter() |             current_time = time.perf_counter() | ||||||
|             if current_time - self.last_tick_time >= tick_interval: |             while current_time - self.last_tick_time >= tick_interval: | ||||||
|                 self.last_tick_time += tick_interval |                 self.last_tick_time += tick_interval | ||||||
|                 self.tick_counter += 1 |                 self.tick_counter += 1 | ||||||
|                 self.total_ticks += 1 |                 self.total_ticks += 1 | ||||||
|  | 
 | ||||||
|                 self.input_handler.update_selected_objects() |                 self.input_handler.update_selected_objects() | ||||||
|                 self.world.tick_all() |                 self.world.tick_all() | ||||||
|                 self.hud.manager.update(deltatime) |                 self.hud.manager.update(deltatime) | ||||||
|  | 
 | ||||||
|             if current_time - self.last_tps_time >= 1.0: |             if current_time - self.last_tps_time >= 1.0: | ||||||
|                 self.actual_tps = self.tick_counter |                 self.actual_tps = self.tick_counter | ||||||
|                 self.tick_counter = 0 |                 self.tick_counter = 0 | ||||||
| @ -144,85 +158,34 @@ class SimulationEngine: | |||||||
|         self._update(deltatime) |         self._update(deltatime) | ||||||
|         self._render() |         self._render() | ||||||
| 
 | 
 | ||||||
|     def _handle_window_events(self, events): |  | ||||||
|         for event in events: |  | ||||||
|             self.hud.process_event(event) |  | ||||||
|             if event.type == pygame.VIDEORESIZE: |  | ||||||
|                 self.window_width, self.window_height = event.w, event.h |  | ||||||
|                 self.screen = pygame.display.set_mode( |  | ||||||
|                     (self.window_width, self.window_height), |  | ||||||
|                     pygame.RESIZABLE |  | ||||||
|                 ) |  | ||||||
|                 self._update_simulation_view() |  | ||||||
|                 self.hud.update_layout(self.window_width, self.window_height) |  | ||||||
| 
 |  | ||||||
|         self.hud.update_layout(self.window_width, self.window_height) |  | ||||||
|         self._update_simulation_view() |  | ||||||
| 
 |  | ||||||
|     def _handle_sprint_mode(self): |  | ||||||
|         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 |  | ||||||
|             pygame.event.pump()  # Prevent event queue overflow |  | ||||||
|             if time.perf_counter() - current_time > 0.05: |  | ||||||
|                 break |  | ||||||
|         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() |  | ||||||
|         self.screen.fill(BLACK) |  | ||||||
|         self.renderer.clear_screen() |  | ||||||
|         cell_count = self._count_cells() |  | ||||||
|         self.hud.render_sprint_debug(self.screen, self.actual_tps, self.total_ticks, cell_count) |  | ||||||
|         pygame.display.flip() |  | ||||||
|         self.clock.tick(MAX_FPS) |  | ||||||
|         self.last_tick_time = time.perf_counter() |  | ||||||
| 
 |  | ||||||
|     def _handle_simulation_ticks(self, tick_interval, deltatime): |  | ||||||
|         current_time = time.perf_counter() |  | ||||||
|         while current_time - self.last_tick_time >= tick_interval: |  | ||||||
|             self.last_tick_time += tick_interval |  | ||||||
|             self.tick_counter += 1 |  | ||||||
|             self.total_ticks += 1 |  | ||||||
|             self.input_handler.update_selected_objects() |  | ||||||
|             self.world.tick_all() |  | ||||||
|             self.hud.manager.update(deltatime) |  | ||||||
|         if current_time - self.last_tps_time >= 1.0: |  | ||||||
|             self.actual_tps = self.tick_counter |  | ||||||
|             self.tick_counter = 0 |  | ||||||
|             self.last_tps_time += 1.0 |  | ||||||
| 
 |  | ||||||
|     def _update(self, deltatime): |     def _update(self, deltatime): | ||||||
|         keys = pygame.key.get_pressed() |         keys = pygame.key.get_pressed() | ||||||
|         self.input_handler.update_camera(keys, deltatime) |         self.input_handler.update_camera(keys, deltatime) | ||||||
| 
 | 
 | ||||||
|     def _render(self): |     def _render(self): | ||||||
|         self.screen.fill(BLACK) |         self.renderer.clear_screen(self.screen) | ||||||
|         self.renderer.clear_screen() |         self.renderer.draw_grid(self.camera, self.input_handler.show_grid) | ||||||
|  |         self.renderer.render_world(self.world, self.camera) | ||||||
|  |         self.renderer.render_interaction_radius(self.world, self.camera, self.input_handler.selected_objects, self.input_handler.show_interaction_radius) | ||||||
|  |         self.renderer.render_selection_rectangle(self.input_handler.get_selection_rect(), self.sim_view_rect) | ||||||
|  |         self.renderer.render_selected_objects_outline(self.input_handler.selected_objects, self.camera) | ||||||
| 
 | 
 | ||||||
|         if not self.hud.dragging_splitter: |         # In core/simulation_engine.py, in _render(): | ||||||
|             self.renderer.draw_grid(self.camera, self.input_handler.show_grid) |         self.screen.blit(self.sim_view, (self.sim_view_rect.left, self.sim_view_rect.top)) | ||||||
|             self.renderer.render_world(self.world, self.camera) |  | ||||||
|             self.renderer.render_interaction_radius( |  | ||||||
|                 self.world, self.camera, self.input_handler.selected_objects, self.input_handler.show_interaction_radius |  | ||||||
|             ) |  | ||||||
|             self.renderer.render_selection_rectangle(self.input_handler.get_selection_rect(), self.sim_view_rect) |  | ||||||
|             self.renderer.render_selected_objects_outline(self.input_handler.selected_objects, self.camera) |  | ||||||
|             self.screen.blit(self.sim_view, (self.sim_view_rect.left, self.sim_view_rect.top)) |  | ||||||
| 
 | 
 | ||||||
|         self.hud.manager.draw_ui(self.screen) |         # Draw border around sim_view | ||||||
|         self.hud.draw_splitters(self.screen) |         border_color = (255, 255, 255)  # White | ||||||
|  |         border_width = 3 | ||||||
|  |         pygame.draw.rect(self.screen, border_color, self.sim_view_rect, border_width) | ||||||
| 
 | 
 | ||||||
|         # self.hud.render_mouse_position(self.screen, self.camera, self.sim_view_rect) |         self.hud.render_mouse_position(self.screen, self.camera, self.sim_view_rect) | ||||||
|         self.hud.render_fps(self.screen, self.clock) |         self.hud.render_fps(self.screen, self.clock) | ||||||
|         self.hud.render_tps(self.screen, self.actual_tps) |         self.hud.render_tps(self.screen, self.actual_tps) | ||||||
|         # self.hud.render_tick_count(self.screen, self.total_ticks) |         self.hud.render_tick_count(self.screen, self.total_ticks) | ||||||
|         # self.hud.render_selected_objects_info(self.screen, self.input_handler.selected_objects) |         self.hud.render_selected_objects_info(self.screen, self.input_handler.selected_objects) | ||||||
|         self.hud.render_legend(self.screen, self.input_handler.show_legend) |         self.hud.render_legend(self.screen, self.input_handler.show_legend) | ||||||
|         self.hud.render_pause_indicator(self.screen, self.input_handler.is_paused) |         self.hud.render_pause_indicator(self.screen, self.input_handler.is_paused) | ||||||
|  | 
 | ||||||
|         if self.input_handler.selected_objects: |         if self.input_handler.selected_objects: | ||||||
|             self.hud.render_neural_network_visualization(self.screen, self.input_handler.selected_objects[0]) |             self.hud.render_neural_network_visualization(self.screen, self.input_handler.selected_objects[0]) | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								main.py
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								main.py
									
									
									
									
									
								
							| @ -1,8 +1,5 @@ | |||||||
| from core.simulation_engine import SimulationEngine | from core.simulation_engine import SimulationEngine | ||||||
| 
 | 
 | ||||||
| def main(): | if __name__ == "__main__": | ||||||
|     engine = SimulationEngine() |     engine = SimulationEngine() | ||||||
|     engine.run() |     engine.run() | ||||||
| 
 |  | ||||||
| if __name__ == "__main__": |  | ||||||
|     main() |  | ||||||
| @ -14,6 +14,5 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [dependency-groups] | [dependency-groups] | ||||||
| dev = [ | dev = [ | ||||||
|     "psutil>=7.0.0", |  | ||||||
|     "ruff>=0.11.12", |     "ruff>=0.11.12", | ||||||
| ] | ] | ||||||
|  | |||||||
| @ -1,93 +0,0 @@ | |||||||
| import time |  | ||||||
| import random |  | ||||||
| import statistics |  | ||||||
| import hashlib |  | ||||||
| import pickle |  | ||||||
| 
 |  | ||||||
| class HeadlessSimulationBenchmark: |  | ||||||
|     def __init__(self, setup_world, random_seed=42): |  | ||||||
|         """ |  | ||||||
|         :param setup_world: Callable that returns a World instance. |  | ||||||
|         :param random_seed: Seed for random number generation. |  | ||||||
|         """ |  | ||||||
|         self.setup_world = setup_world |  | ||||||
|         self.random_seed = random_seed |  | ||||||
|         self.world = None |  | ||||||
|         self.tps_history = [] |  | ||||||
|         self._running = False |  | ||||||
|         self.ticks_elapsed_time = None  # Track time for designated ticks |  | ||||||
| 
 |  | ||||||
|     def set_random_seed(self, seed): |  | ||||||
|         self.random_seed = seed |  | ||||||
|         random.seed(seed) |  | ||||||
| 
 |  | ||||||
|     def start(self, ticks=100, max_seconds=None): |  | ||||||
|         self.set_random_seed(self.random_seed) |  | ||||||
|         self.world = self.setup_world(self.random_seed) |  | ||||||
|         self.tps_history.clear() |  | ||||||
|         self._running = True |  | ||||||
| 
 |  | ||||||
|         tick_count = 0 |  | ||||||
|         start_time = time.perf_counter() |  | ||||||
|         last_time = start_time |  | ||||||
| 
 |  | ||||||
|         # For precise tick timing |  | ||||||
|         tick_timing_start = None |  | ||||||
| 
 |  | ||||||
|         if ticks is not None: |  | ||||||
|             tick_timing_start = time.perf_counter() |  | ||||||
| 
 |  | ||||||
|         while self._running and (ticks is None or tick_count < ticks): |  | ||||||
|             self.world.tick_all() |  | ||||||
|             tick_count += 1 |  | ||||||
|             now = time.perf_counter() |  | ||||||
|             elapsed = now - last_time |  | ||||||
|             if elapsed > 0: |  | ||||||
|                 self.tps_history.append(1.0 / elapsed) |  | ||||||
|             last_time = now |  | ||||||
|             if max_seconds and (now - start_time) > max_seconds: |  | ||||||
|                 break |  | ||||||
| 
 |  | ||||||
|         if ticks is not None: |  | ||||||
|             tick_timing_end = time.perf_counter() |  | ||||||
|             self.ticks_elapsed_time = tick_timing_end - tick_timing_start |  | ||||||
|         else: |  | ||||||
|             self.ticks_elapsed_time = None |  | ||||||
| 
 |  | ||||||
|         self._running = False |  | ||||||
| 
 |  | ||||||
|     def stop(self): |  | ||||||
|         self._running = False |  | ||||||
| 
 |  | ||||||
|     def get_tps_history(self): |  | ||||||
|         return self.tps_history |  | ||||||
| 
 |  | ||||||
|     def get_tps_average(self): |  | ||||||
|         return statistics.mean(self.tps_history) if self.tps_history else 0.0 |  | ||||||
| 
 |  | ||||||
|     def get_tps_stddev(self): |  | ||||||
|         return statistics.stdev(self.tps_history) if len(self.tps_history) > 1 else 0.0 |  | ||||||
| 
 |  | ||||||
|     def get_simulation_hash(self): |  | ||||||
|         # Serialize the world state and hash it for determinism checks |  | ||||||
|         state = [] |  | ||||||
|         for obj in self.world.get_objects(): |  | ||||||
|             state.append(( |  | ||||||
|                 type(obj).__name__, |  | ||||||
|                 getattr(obj, "position", None), |  | ||||||
|                 getattr(obj, "rotation", None), |  | ||||||
|                 getattr(obj, "flags", None), |  | ||||||
|                 getattr(obj, "interaction_radius", None), |  | ||||||
|                 getattr(obj, "max_visual_width", None), |  | ||||||
|             )) |  | ||||||
|         state_bytes = pickle.dumps(state) |  | ||||||
|         return hashlib.sha256(state_bytes).hexdigest() |  | ||||||
| 
 |  | ||||||
|     def get_summary(self): |  | ||||||
|         return { |  | ||||||
|             "tps_avg": self.get_tps_average(), |  | ||||||
|             "tps_stddev": self.get_tps_stddev(), |  | ||||||
|             "ticks": len(self.tps_history), |  | ||||||
|             "simulation_hash": self.get_simulation_hash(), |  | ||||||
|             "ticks_elapsed_time": self.ticks_elapsed_time, |  | ||||||
|         } |  | ||||||
| @ -1,57 +0,0 @@ | |||||||
| import pytest |  | ||||||
| import random |  | ||||||
| 
 |  | ||||||
| from world.world import World, Position, Rotation |  | ||||||
| from world.objects import FoodObject, DefaultCell |  | ||||||
| from tests.benchmarking import HeadlessSimulationBenchmark |  | ||||||
| 
 |  | ||||||
| # Hardcoded simulation parameters (copied from config/constants.py) |  | ||||||
| CELL_SIZE = 20 |  | ||||||
| GRID_WIDTH = 30 |  | ||||||
| GRID_HEIGHT = 25 |  | ||||||
| FOOD_OBJECTS_COUNT = 500 |  | ||||||
| RANDOM_SEED = 12345 |  | ||||||
| 
 |  | ||||||
| def _setup_world(seed=RANDOM_SEED): |  | ||||||
|     world = World(CELL_SIZE, (CELL_SIZE * GRID_WIDTH, CELL_SIZE * GRID_HEIGHT)) |  | ||||||
|     random.seed(seed) |  | ||||||
| 
 |  | ||||||
|     half_width = GRID_WIDTH * CELL_SIZE // 2 |  | ||||||
|     half_height = GRID_HEIGHT * CELL_SIZE // 2 |  | ||||||
| 
 |  | ||||||
|     for _ in range(FOOD_OBJECTS_COUNT): |  | ||||||
|         x = random.randint(-half_width, half_width) |  | ||||||
|         y = random.randint(-half_height, half_height) |  | ||||||
|         world.add_object(FoodObject(Position(x=x, y=y))) |  | ||||||
| 
 |  | ||||||
|     for _ in range(300): |  | ||||||
|         new_cell = DefaultCell( |  | ||||||
|             Position(x=random.randint(-half_width, half_width), y=random.randint(-half_height, half_height)), |  | ||||||
|             Rotation(angle=0) |  | ||||||
|         ) |  | ||||||
|         new_cell.behavioral_model = new_cell.behavioral_model.mutate(3) |  | ||||||
|         world.add_object(new_cell) |  | ||||||
| 
 |  | ||||||
|     return world |  | ||||||
| 
 |  | ||||||
| def test_simulation_determinism(): |  | ||||||
|     bench1 = HeadlessSimulationBenchmark(lambda seed: _setup_world(seed), random_seed=RANDOM_SEED) |  | ||||||
|     bench2 = HeadlessSimulationBenchmark(lambda seed: _setup_world(seed), random_seed=RANDOM_SEED) |  | ||||||
| 
 |  | ||||||
|     bench1.start(ticks=100) |  | ||||||
|     bench2.start(ticks=100) |  | ||||||
| 
 |  | ||||||
|     hash1 = bench1.get_simulation_hash() |  | ||||||
|     hash2 = bench2.get_simulation_hash() |  | ||||||
| 
 |  | ||||||
|     assert hash1 == hash2, f"Simulation hashes differ: {hash1} != {hash2}" |  | ||||||
| 
 |  | ||||||
| def test_simulation_benchmark(): |  | ||||||
|     bench = HeadlessSimulationBenchmark(lambda seed: _setup_world(seed), random_seed=RANDOM_SEED+1) |  | ||||||
|     tick_count = 100 |  | ||||||
|     bench.start(ticks=tick_count) |  | ||||||
|     summary = bench.get_summary() |  | ||||||
|     print(f"{tick_count} ticks took {summary.get('ticks_elapsed_time', 0):.4f} seconds, TPS avg: {summary['tps_avg']:.2f}, stddev: {summary['tps_stddev']:.2f}") |  | ||||||
| 
 |  | ||||||
|     assert summary['tps_avg'] > 0, "Average TPS should be greater than zero" |  | ||||||
|     assert summary['ticks_elapsed_time'] > 0, "Elapsed time should be greater than zero" |  | ||||||
| @ -1,12 +1,10 @@ | |||||||
| import pytest | import pytest | ||||||
| from world.world import World, Position, BaseEntity, Rotation | from world.world import World, Position, BaseEntity | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class DummyEntity(BaseEntity): | class DummyEntity(BaseEntity): | ||||||
|     def __init__(self, position, rotation=None): |     def __init__(self, position): | ||||||
|         if rotation is None: |         super().__init__(position) | ||||||
|             rotation = Rotation(angle=0) |  | ||||||
|         super().__init__(position, rotation) |  | ||||||
|         self.ticked = False |         self.ticked = False | ||||||
|         self.rendered = False |         self.rendered = False | ||||||
| 
 | 
 | ||||||
| @ -85,6 +83,9 @@ def test_tick_all_calls_tick(world): | |||||||
| 
 | 
 | ||||||
| def test_add_object_out_of_bounds(world): | def test_add_object_out_of_bounds(world): | ||||||
|     entity = DummyEntity(Position(x=1000, y=1000)) |     entity = DummyEntity(Position(x=1000, y=1000)) | ||||||
|  | 
 | ||||||
|     world.add_object(entity) |     world.add_object(entity) | ||||||
|  | 
 | ||||||
|     entity = world.get_objects()[0] |     entity = world.get_objects()[0] | ||||||
|  | 
 | ||||||
|     assert entity.position.x == 49 and entity.position.y == 49 |     assert entity.position.x == 49 and entity.position.y == 49 | ||||||
							
								
								
									
										217
									
								
								ui/hud.py
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								ui/hud.py
									
									
									
									
									
								
							| @ -6,188 +6,18 @@ import pygame_gui | |||||||
| from config.constants import * | from config.constants import * | ||||||
| from world.base.brain import CellBrain, FlexibleNeuralNetwork | from world.base.brain import CellBrain, FlexibleNeuralNetwork | ||||||
| from world.objects import DefaultCell | from world.objects import DefaultCell | ||||||
| from pygame_gui.elements import UIPanel |  | ||||||
| import math | import math | ||||||
| 
 | 
 | ||||||
| DARK_GRAY = (40, 40, 40) |  | ||||||
| DARKER_GRAY = (25, 25, 25) |  | ||||||
| 
 | 
 | ||||||
| class HUD: | class HUD: | ||||||
|     def __init__(self, ui_manager, screen_width=SCREEN_WIDTH, screen_height=SCREEN_HEIGHT): |     def __init__(self, ui_manager, screen_width=SCREEN_WIDTH, screen_height=SCREEN_HEIGHT): | ||||||
|         self.font = pygame.font.Font("freesansbold.ttf", FONT_SIZE) |         self.font = pygame.font.Font("freesansbold.ttf", FONT_SIZE) | ||||||
|         self.legend_font = pygame.font.Font("freesansbold.ttf", LEGEND_FONT_SIZE) |         self.legend_font = pygame.font.Font("freesansbold.ttf", LEGEND_FONT_SIZE) | ||||||
| 
 | 
 | ||||||
|         self.manager = ui_manager |  | ||||||
|         self.screen_width = screen_width |         self.screen_width = screen_width | ||||||
|         self.screen_height = screen_height |         self.screen_height = screen_height | ||||||
| 
 | 
 | ||||||
|         # Panel size defaults |         self.manager = ui_manager | ||||||
|         self.control_bar_height = 48 |  | ||||||
|         self.inspector_width = 260 |  | ||||||
|         self.properties_width = 320 |  | ||||||
|         self.console_height = 120 |  | ||||||
|         self.splitter_thickness = 6 |  | ||||||
| 
 |  | ||||||
|         self.dragging_splitter = None |  | ||||||
|         self._create_panels() |  | ||||||
| 
 |  | ||||||
|     def _create_panels(self): |  | ||||||
|         # Top control bar |  | ||||||
|         self.control_bar = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect(0, 0, self.screen_width, self.control_bar_height), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#control_bar", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Left inspector |  | ||||||
|         self.inspector_panel = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect( |  | ||||||
|                 0, self.control_bar_height, |  | ||||||
|                 self.inspector_width, |  | ||||||
|                 self.screen_height - self.control_bar_height |  | ||||||
|             ), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#inspector_panel", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Right properties |  | ||||||
|         self.properties_panel = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect( |  | ||||||
|                 self.screen_width - self.properties_width, |  | ||||||
|                 self.control_bar_height, |  | ||||||
|                 self.properties_width, |  | ||||||
|                 self.screen_height - self.control_bar_height |  | ||||||
|             ), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#properties_panel", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         # Bottom console |  | ||||||
|         self.console_panel = UIPanel( |  | ||||||
|             relative_rect=pygame.Rect( |  | ||||||
|                 self.inspector_width, |  | ||||||
|                 self.screen_height - self.console_height, |  | ||||||
|                 self.screen_width - self.inspector_width - self.properties_width, |  | ||||||
|                 self.console_height |  | ||||||
|             ), |  | ||||||
|             manager=self.manager, |  | ||||||
|             object_id="#console_panel", |  | ||||||
|         ) |  | ||||||
| 
 |  | ||||||
|         self.panels = [ |  | ||||||
|             self.control_bar, |  | ||||||
|             self.inspector_panel, |  | ||||||
|             self.properties_panel, |  | ||||||
|             self.console_panel |  | ||||||
|         ] |  | ||||||
|         self.dragging_splitter = None |  | ||||||
| 
 |  | ||||||
|     def get_viewport_rect(self): |  | ||||||
|         # Returns the rect for the simulation viewport |  | ||||||
|         x = self.inspector_width |  | ||||||
|         y = self.control_bar_height |  | ||||||
|         w = self.screen_width - self.inspector_width - self.properties_width |  | ||||||
|         h = self.screen_height - self.control_bar_height - self.console_height |  | ||||||
|         return pygame.Rect(x, y, w, h) |  | ||||||
| 
 |  | ||||||
|     def update_layout(self, window_width, window_height): |  | ||||||
|         self.screen_width = window_width |  | ||||||
|         self.screen_height = window_height |  | ||||||
| 
 |  | ||||||
|         # Control bar (top) |  | ||||||
|         self.control_bar.set_relative_position((0, 0)) |  | ||||||
|         self.control_bar.set_dimensions((self.screen_width, self.control_bar_height)) |  | ||||||
| 
 |  | ||||||
|         # Inspector panel (left) - goes all the way to the bottom |  | ||||||
|         self.inspector_panel.set_relative_position((0, self.control_bar_height)) |  | ||||||
|         self.inspector_panel.set_dimensions((self.inspector_width, self.screen_height - self.control_bar_height)) |  | ||||||
| 
 |  | ||||||
|         # Properties panel (right) - goes all the way to the bottom |  | ||||||
|         self.properties_panel.set_relative_position( |  | ||||||
|             (self.screen_width - self.properties_width, self.control_bar_height)) |  | ||||||
|         self.properties_panel.set_dimensions((self.properties_width, self.screen_height - self.control_bar_height)) |  | ||||||
| 
 |  | ||||||
|         # Console panel (bottom, spans between inspector and properties) |  | ||||||
|         self.console_panel.set_relative_position((self.inspector_width, self.screen_height - self.console_height)) |  | ||||||
|         self.console_panel.set_dimensions( |  | ||||||
|             (self.screen_width - self.inspector_width - self.properties_width, self.console_height)) |  | ||||||
| 
 |  | ||||||
|     def process_event(self, event): |  | ||||||
|         # Handle splitter dragging for resizing panels |  | ||||||
|         if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: |  | ||||||
|             mx, my = event.pos |  | ||||||
|             # Check if mouse is on a splitter (left/right/bottom) |  | ||||||
|             if abs(mx - self.inspector_width) < self.splitter_thickness and self.control_bar_height < my < self.screen_height - self.console_height: |  | ||||||
|                 self.dragging_splitter = "inspector" |  | ||||||
|             elif abs(mx - (self.screen_width - self.properties_width)) < self.splitter_thickness and self.control_bar_height < my < self.screen_height - self.console_height: |  | ||||||
|                 self.dragging_splitter = "properties" |  | ||||||
|             elif abs(my - (self.screen_height - self.console_height)) < self.splitter_thickness and self.inspector_width < mx < self.screen_width - self.properties_width: |  | ||||||
|                 self.dragging_splitter = "console" |  | ||||||
|             self.update_layout(self.screen_width, self.screen_height) |  | ||||||
|         elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: |  | ||||||
|             self.dragging_splitter = None |  | ||||||
|         elif event.type == pygame.MOUSEMOTION and self.dragging_splitter: |  | ||||||
|             mx, my = event.pos |  | ||||||
|             if self.dragging_splitter == "inspector": |  | ||||||
|                 self.inspector_width = max(100, min(mx, self.screen_width - self.properties_width - 100)) |  | ||||||
|             elif self.dragging_splitter == "properties": |  | ||||||
|                 self.properties_width = max(100, min(self.screen_width - mx, self.screen_width - self.inspector_width - 100)) |  | ||||||
|             elif self.dragging_splitter == "console": |  | ||||||
|                 self.console_height = max(60, min(self.screen_height - my, self.screen_height - self.control_bar_height - 60)) |  | ||||||
|             self.update_layout(self.screen_width, self.screen_height) |  | ||||||
| 
 |  | ||||||
|     def draw_splitters(self, screen): |  | ||||||
|         # Draw draggable splitters for visual feedback |  | ||||||
|         indicator_color = (220, 220, 220) |  | ||||||
|         indicator_size = 6  # Length of indicator line |  | ||||||
|         indicator_gap = 4    # Gap between indicator lines |  | ||||||
|         indicator_count = 3  # Number of indicator lines |  | ||||||
| 
 |  | ||||||
|         # Vertical splitter (inspector/properties) |  | ||||||
|         # Inspector/properties only if wide enough |  | ||||||
|         if self.inspector_width > 0: |  | ||||||
|             x = self.inspector_width - 2 |  | ||||||
|             y1 = self.control_bar_height |  | ||||||
|             y2 = self.screen_height - self.console_height |  | ||||||
|             # Draw indicator (horizontal lines) in the middle |  | ||||||
|             mid_y = (y1 + y2) // 2 |  | ||||||
|             for i in range(indicator_count): |  | ||||||
|                 offset = (i - 1) * (indicator_gap + 1) |  | ||||||
|                 pygame.draw.line( |  | ||||||
|                     screen, indicator_color, |  | ||||||
|                     (x - indicator_size // 2, mid_y + offset), |  | ||||||
|                     (x + indicator_size // 2, mid_y + offset), |  | ||||||
|                     2 |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|         if self.properties_width > 0: |  | ||||||
|             x = self.screen_width - self.properties_width + 2 |  | ||||||
|             y1 = self.control_bar_height |  | ||||||
|             y2 = self.screen_height - self.console_height |  | ||||||
|             mid_y = (y1 + y2) // 2 |  | ||||||
|             for i in range(indicator_count): |  | ||||||
|                 offset = (i - 1) * (indicator_gap + 1) |  | ||||||
|                 pygame.draw.line( |  | ||||||
|                     screen, indicator_color, |  | ||||||
|                     (x - indicator_size // 2, mid_y + offset), |  | ||||||
|                     (x + indicator_size // 2, mid_y + offset), |  | ||||||
|                     2 |  | ||||||
|                 ) |  | ||||||
| 
 |  | ||||||
|         # Horizontal splitter (console) |  | ||||||
|         if self.console_height > 0: |  | ||||||
|             y = self.screen_height - self.console_height + 2 |  | ||||||
|             x1 = self.inspector_width |  | ||||||
|             x2 = self.screen_width - self.properties_width |  | ||||||
|             mid_x = (x1 + x2) // 2 |  | ||||||
|             for i in range(indicator_count): |  | ||||||
|                 offset = (i - 1) * (indicator_gap + 1) |  | ||||||
|                 pygame.draw.line( |  | ||||||
|                     screen, indicator_color, |  | ||||||
|                     (mid_x + offset, y - indicator_size // 2), |  | ||||||
|                     (mid_x + offset, y + indicator_size // 2), |  | ||||||
|                     2 |  | ||||||
|                 ) |  | ||||||
| 
 | 
 | ||||||
|     def render_mouse_position(self, screen, camera, sim_view_rect): |     def render_mouse_position(self, screen, camera, sim_view_rect): | ||||||
|         """Render mouse position in top left.""" |         """Render mouse position in top left.""" | ||||||
| @ -315,7 +145,6 @@ class HUD: | |||||||
|         VIZ_WIDTH = 280  # Width of the neural network visualization area |         VIZ_WIDTH = 280  # Width of the neural network visualization area | ||||||
|         VIZ_HEIGHT = 300  # Height of the neural network visualization area |         VIZ_HEIGHT = 300  # Height of the neural network visualization area | ||||||
|         VIZ_RIGHT_MARGIN = VIZ_WIDTH + 50  # Distance from right edge of screen to visualization |         VIZ_RIGHT_MARGIN = VIZ_WIDTH + 50  # Distance from right edge of screen to visualization | ||||||
|         VIZ_BOTTOM_MARGIN = 50  # Distance from the bottom of the screen |  | ||||||
| 
 | 
 | ||||||
|         # Background styling constants |         # Background styling constants | ||||||
|         BACKGROUND_PADDING = 30  # Padding around the visualization background |         BACKGROUND_PADDING = 30  # Padding around the visualization background | ||||||
| @ -367,9 +196,6 @@ class HUD: | |||||||
|         TOOLTIP_MARGIN = 10 |         TOOLTIP_MARGIN = 10 | ||||||
|         TOOLTIP_LINE_SPACING = 0  # No extra spacing between lines |         TOOLTIP_LINE_SPACING = 0  # No extra spacing between lines | ||||||
| 
 | 
 | ||||||
|         if self.properties_width < VIZ_RIGHT_MARGIN + 50: |  | ||||||
|             self.properties_width = VIZ_RIGHT_MARGIN + 50 # Ensure properties panel is wide enough for tooltip |  | ||||||
| 
 |  | ||||||
|         if not hasattr(cell, 'behavioral_model'): |         if not hasattr(cell, 'behavioral_model'): | ||||||
|             return |             return | ||||||
| 
 | 
 | ||||||
| @ -380,9 +206,9 @@ class HUD: | |||||||
| 
 | 
 | ||||||
|         network: FlexibleNeuralNetwork = cell_brain.neural_network |         network: FlexibleNeuralNetwork = cell_brain.neural_network | ||||||
| 
 | 
 | ||||||
|         # Calculate visualization position (bottom right) |         # Calculate visualization position | ||||||
|         viz_x = self.screen_width - VIZ_RIGHT_MARGIN  # Right side of screen |         viz_x = self.screen_width - VIZ_RIGHT_MARGIN  # Right side of screen | ||||||
|         viz_y = self.screen_height - VIZ_HEIGHT - VIZ_BOTTOM_MARGIN  # Above the bottom margin |         viz_y = (self.screen_height // 2) - (VIZ_HEIGHT // 2)  # Centered vertically | ||||||
| 
 | 
 | ||||||
|         layer_spacing = VIZ_WIDTH // max(1, len(network.layers) - 1) if len(network.layers) > 1 else VIZ_WIDTH |         layer_spacing = VIZ_WIDTH // max(1, len(network.layers) - 1) if len(network.layers) > 1 else VIZ_WIDTH | ||||||
| 
 | 
 | ||||||
| @ -392,8 +218,6 @@ class HUD: | |||||||
|         pygame.draw.rect(screen, BACKGROUND_COLOR, background_rect) |         pygame.draw.rect(screen, BACKGROUND_COLOR, background_rect) | ||||||
|         pygame.draw.rect(screen, WHITE, background_rect, BACKGROUND_BORDER_WIDTH) |         pygame.draw.rect(screen, WHITE, background_rect, BACKGROUND_BORDER_WIDTH) | ||||||
| 
 | 
 | ||||||
|         info = network.get_structure_info() |  | ||||||
| 
 |  | ||||||
|         # Title |         # Title | ||||||
|         title_text = self.font.render("Neural Network", True, WHITE) |         title_text = self.font.render("Neural Network", True, WHITE) | ||||||
|         title_rect = title_text.get_rect() |         title_rect = title_text.get_rect() | ||||||
| @ -401,13 +225,6 @@ class HUD: | |||||||
|         title_rect.top = viz_y - TITLE_TOP_MARGIN |         title_rect.top = viz_y - TITLE_TOP_MARGIN | ||||||
|         screen.blit(title_text, title_rect) |         screen.blit(title_text, title_rect) | ||||||
| 
 | 
 | ||||||
|         # Render network cost under the title |  | ||||||
|         cost_text = self.font.render(f"Cost: {info['network_cost']}", True, WHITE) |  | ||||||
|         cost_rect = cost_text.get_rect() |  | ||||||
|         cost_rect.centerx = title_rect.centerx |  | ||||||
|         cost_rect.top = title_rect.bottom + 4  # Small gap below the title |  | ||||||
|         screen.blit(cost_text, cost_rect) |  | ||||||
| 
 |  | ||||||
|         # Get current activations by running a forward pass with current inputs |         # Get current activations by running a forward pass with current inputs | ||||||
|         input_values = [cell_brain.inputs[key] for key in cell_brain.input_keys] |         input_values = [cell_brain.inputs[key] for key in cell_brain.input_keys] | ||||||
| 
 | 
 | ||||||
| @ -566,6 +383,22 @@ class HUD: | |||||||
|                 label_rect.bottom = viz_y + VIZ_HEIGHT + LAYER_LABEL_BOTTOM_MARGIN |                 label_rect.bottom = viz_y + VIZ_HEIGHT + LAYER_LABEL_BOTTOM_MARGIN | ||||||
|                 screen.blit(label_text, label_rect) |                 screen.blit(label_text, label_rect) | ||||||
| 
 | 
 | ||||||
|  |         # Draw network info | ||||||
|  |         info = network.get_structure_info() | ||||||
|  |         info_lines = [ | ||||||
|  |             f"Layers: {info['total_layers']}", | ||||||
|  |             f"Neurons: {info['total_neurons']}", | ||||||
|  |             f"Connections: {info['total_connections']}", | ||||||
|  |             f"Network Cost: {info['network_cost']}", | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         for i, line in enumerate(info_lines): | ||||||
|  |             info_text = self.legend_font.render(line, True, WHITE) | ||||||
|  |             info_rect = info_text.get_rect() | ||||||
|  |             info_rect.left = viz_x | ||||||
|  |             info_rect.top = viz_y + VIZ_HEIGHT + INFO_TEXT_TOP_MARGIN + i * INFO_TEXT_LINE_SPACING | ||||||
|  |             screen.blit(info_text, info_rect) | ||||||
|  | 
 | ||||||
|         # --- Tooltip logic for neuron hover --- |         # --- Tooltip logic for neuron hover --- | ||||||
|         mouse_x, mouse_y = pygame.mouse.get_pos() |         mouse_x, mouse_y = pygame.mouse.get_pos() | ||||||
|         tooltip_text = None |         tooltip_text = None | ||||||
| @ -641,20 +474,22 @@ class HUD: | |||||||
|                 screen.blit(surf, (tooltip_rect.left + TOOLTIP_PADDING_X, y)) |                 screen.blit(surf, (tooltip_rect.left + TOOLTIP_PADDING_X, y)) | ||||||
|                 y += surf.get_height() + TOOLTIP_LINE_SPACING |                 y += surf.get_height() + TOOLTIP_LINE_SPACING | ||||||
| 
 | 
 | ||||||
|     def render_sprint_debug(self, screen, actual_tps, total_ticks, cell_count=None): |     def render_sprint_debug(self, screen, actual_tps, total_ticks): | ||||||
|         """Render sprint debug info: header, TPS, and tick count.""" |         """Render sprint debug info: header, TPS, and tick count.""" | ||||||
|         header = self.font.render("Sprinting...", True, (255, 200, 0)) |         header = self.font.render("Sprinting...", True, (255, 200, 0)) | ||||||
|         tps_text = self.font.render(f"TPS: {actual_tps}", True, (255, 255, 255)) |         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)) |         ticks_text = self.font.render(f"Ticks: {total_ticks}", True, (255, 255, 255)) | ||||||
|         cell_text = self.font.render(f"Cells: {cell_count}" if cell_count is not None else "Cells: N/A", True, (255, 255, 255)) |  | ||||||
| 
 | 
 | ||||||
|         y = self.screen_height // 2 - 80 |         y = self.screen_height // 2 - 40 | ||||||
|         header_rect = header.get_rect(center=(self.screen_width // 2, y)) |         header_rect = header.get_rect(center=(self.screen_width // 2, y)) | ||||||
|         tps_rect = tps_text.get_rect(center=(self.screen_width // 2, y + 40)) |         tps_rect = tps_text.get_rect(center=(self.screen_width // 2, y + 40)) | ||||||
|         ticks_rect = ticks_text.get_rect(center=(self.screen_width // 2, y + 80)) |         ticks_rect = ticks_text.get_rect(center=(self.screen_width // 2, y + 80)) | ||||||
|         cell_rect = cell_text.get_rect(center=(self.screen_width // 2, y + 120)) |  | ||||||
| 
 | 
 | ||||||
|         screen.blit(header, header_rect) |         screen.blit(header, header_rect) | ||||||
|         screen.blit(tps_text, tps_rect) |         screen.blit(tps_text, tps_rect) | ||||||
|         screen.blit(ticks_text, ticks_rect) |         screen.blit(ticks_text, ticks_rect) | ||||||
|         screen.blit(cell_text, cell_rect) | 
 | ||||||
|  |     def update_layout(self, window_width, window_height): | ||||||
|  |         """Update HUD layout on window resize.""" | ||||||
|  |         self.screen_width = window_width | ||||||
|  |         self.screen_height = window_height | ||||||
|  | |||||||
							
								
								
									
										21
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										21
									
								
								uv.lock
									
									
									
										generated
									
									
									
								
							| @ -53,7 +53,6 @@ dependencies = [ | |||||||
| 
 | 
 | ||||||
| [package.dev-dependencies] | [package.dev-dependencies] | ||||||
| dev = [ | dev = [ | ||||||
|     { name = "psutil" }, |  | ||||||
|     { name = "ruff" }, |     { name = "ruff" }, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| @ -68,10 +67,7 @@ requires-dist = [ | |||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [package.metadata.requires-dev] | [package.metadata.requires-dev] | ||||||
| dev = [ | dev = [{ name = "ruff", specifier = ">=0.11.12" }] | ||||||
|     { name = "psutil", specifier = ">=7.0.0" }, |  | ||||||
|     { name = "ruff", specifier = ">=0.11.12" }, |  | ||||||
| ] |  | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "filelock" | name = "filelock" | ||||||
| @ -210,21 +206,6 @@ wheels = [ | |||||||
|     { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, |     { url = "https://files.pythonhosted.org/packages/88/74/a88bf1b1efeae488a0c0b7bdf71429c313722d1fc0f377537fbe554e6180/pre_commit-4.2.0-py2.py3-none-any.whl", hash = "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd", size = 220707, upload-time = "2025-03-18T21:35:19.343Z" }, | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| [[package]] |  | ||||||
| name = "psutil" |  | ||||||
| version = "7.0.0" |  | ||||||
| source = { registry = "https://pypi.org/simple" } |  | ||||||
| sdist = { url = "https://files.pythonhosted.org/packages/2a/80/336820c1ad9286a4ded7e845b2eccfcb27851ab8ac6abece774a6ff4d3de/psutil-7.0.0.tar.gz", hash = "sha256:7be9c3eba38beccb6495ea33afd982a44074b78f28c434a1f51cc07fd315c456", size = 497003, upload-time = "2025-02-13T21:54:07.946Z" } |  | ||||||
| wheels = [ |  | ||||||
|     { url = "https://files.pythonhosted.org/packages/ed/e6/2d26234410f8b8abdbf891c9da62bee396583f713fb9f3325a4760875d22/psutil-7.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:101d71dc322e3cffd7cea0650b09b3d08b8e7c4109dd6809fe452dfd00e58b25", size = 238051, upload-time = "2025-02-13T21:54:12.36Z" }, |  | ||||||
|     { url = "https://files.pythonhosted.org/packages/04/8b/30f930733afe425e3cbfc0e1468a30a18942350c1a8816acfade80c005c4/psutil-7.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:39db632f6bb862eeccf56660871433e111b6ea58f2caea825571951d4b6aa3da", size = 239535, upload-time = "2025-02-13T21:54:16.07Z" }, |  | ||||||
|     { url = "https://files.pythonhosted.org/packages/2a/ed/d362e84620dd22876b55389248e522338ed1bf134a5edd3b8231d7207f6d/psutil-7.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fcee592b4c6f146991ca55919ea3d1f8926497a713ed7faaf8225e174581e91", size = 275004, upload-time = "2025-02-13T21:54:18.662Z" }, |  | ||||||
|     { url = "https://files.pythonhosted.org/packages/bf/b9/b0eb3f3cbcb734d930fdf839431606844a825b23eaf9a6ab371edac8162c/psutil-7.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b1388a4f6875d7e2aff5c4ca1cc16c545ed41dd8bb596cefea80111db353a34", size = 277986, upload-time = "2025-02-13T21:54:21.811Z" }, |  | ||||||
|     { url = "https://files.pythonhosted.org/packages/eb/a2/709e0fe2f093556c17fbafda93ac032257242cabcc7ff3369e2cb76a97aa/psutil-7.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f098451abc2828f7dc6b58d44b532b22f2088f4999a937557b603ce72b1993", size = 279544, upload-time = "2025-02-13T21:54:24.68Z" }, |  | ||||||
|     { url = "https://files.pythonhosted.org/packages/50/e6/eecf58810b9d12e6427369784efe814a1eec0f492084ce8eb8f4d89d6d61/psutil-7.0.0-cp37-abi3-win32.whl", hash = "sha256:ba3fcef7523064a6c9da440fc4d6bd07da93ac726b5733c29027d7dc95b39d99", size = 241053, upload-time = "2025-02-13T21:54:34.31Z" }, |  | ||||||
|     { url = "https://files.pythonhosted.org/packages/50/1b/6921afe68c74868b4c9fa424dad3be35b095e16687989ebbb50ce4fceb7c/psutil-7.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:4cf3d4eb1aa9b348dec30105c55cd9b7d4629285735a102beb4441e38db90553", size = 244885, upload-time = "2025-02-13T21:54:37.486Z" }, |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "pydantic" | name = "pydantic" | ||||||
| version = "2.11.5" | version = "2.11.5" | ||||||
|  | |||||||
| @ -9,7 +9,6 @@ import pygame | |||||||
| from typing import Optional, List, Any, Union | from typing import Optional, List, Any, Union | ||||||
| 
 | 
 | ||||||
| from world.utils import get_distance_between_objects | from world.utils import get_distance_between_objects | ||||||
| from world.physics import Physics |  | ||||||
| 
 | 
 | ||||||
| from math import atan2, degrees | from math import atan2, degrees | ||||||
| 
 | 
 | ||||||
| @ -264,8 +263,6 @@ class DefaultCell(BaseEntity): | |||||||
| 
 | 
 | ||||||
|         self.tick_count = 0 |         self.tick_count = 0 | ||||||
| 
 | 
 | ||||||
|         self.physics = Physics(0.02, 0.05) |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|     def set_brain(self, behavioral_model: CellBrain) -> None: |     def set_brain(self, behavioral_model: CellBrain) -> None: | ||||||
|         self.behavioral_model = behavioral_model |         self.behavioral_model = behavioral_model | ||||||
| @ -300,7 +297,7 @@ class DefaultCell(BaseEntity): | |||||||
|         distance_to_food = get_distance_between_objects(self, food_object) |         distance_to_food = get_distance_between_objects(self, food_object) | ||||||
| 
 | 
 | ||||||
|         if distance_to_food < self.max_visual_width and food_objects: |         if distance_to_food < self.max_visual_width and food_objects: | ||||||
|             self.energy += 130 |             self.energy += 110 | ||||||
|             food_object.flag_for_death() |             food_object.flag_for_death() | ||||||
|             return self |             return self | ||||||
| 
 | 
 | ||||||
| @ -315,10 +312,10 @@ class DefaultCell(BaseEntity): | |||||||
|             duplicate_y_2 += random.randint(-self.max_visual_width, self.max_visual_width) |             duplicate_y_2 += random.randint(-self.max_visual_width, self.max_visual_width) | ||||||
| 
 | 
 | ||||||
|             new_cell = DefaultCell(Position(x=int(duplicate_x), y=int(duplicate_y)), Rotation(angle=random.randint(0, 359))) |             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(0.05)) |             new_cell.set_brain(self.behavioral_model.mutate(0.4)) | ||||||
| 
 | 
 | ||||||
|             new_cell_2 = DefaultCell(Position(x=int(duplicate_x_2), y=int(duplicate_y_2)), Rotation(angle=random.randint(0, 359))) |             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(0.05)) |             new_cell_2.set_brain(self.behavioral_model.mutate(0.4)) | ||||||
| 
 | 
 | ||||||
|             return [new_cell, new_cell_2] |             return [new_cell, new_cell_2] | ||||||
| 
 | 
 | ||||||
| @ -331,12 +328,44 @@ class DefaultCell(BaseEntity): | |||||||
| 
 | 
 | ||||||
|         output_data = self.behavioral_model.tick(input_data) |         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 |         # clamp accelerations | ||||||
|         output_data["linear_acceleration"] = max(-MAX_ACCELERATION, min(MAX_ACCELERATION, output_data["linear_acceleration"])) |         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"])) |         output_data["angular_acceleration"] = max(-MAX_ANGULAR_ACCELERATION, min(MAX_ANGULAR_ACCELERATION, output_data["angular_acceleration"])) | ||||||
| 
 | 
 | ||||||
|         # request physics data from Physics class |         # 2. Apply drag force | ||||||
|         self.velocity, self.acceleration, self.rotational_velocity, self.angular_acceleration = self.physics.move(output_data["linear_acceleration"], output_data["angular_acceleration"], self.rotation.get_rotation()) |         drag_coefficient = 0.02 | ||||||
|  |         drag_x = -self.velocity[0] * drag_coefficient | ||||||
|  |         drag_y = -self.velocity[1] * drag_coefficient | ||||||
|  | 
 | ||||||
|  |         # 3. Combine all forces | ||||||
|  |         total_linear_accel = output_data["linear_acceleration"] | ||||||
|  |         total_linear_accel = max(-0.1, min(0.1, total_linear_accel)) | ||||||
|  | 
 | ||||||
|  |         # 4. Convert to world coordinates | ||||||
|  |         x_component = total_linear_accel * math.cos(math.radians(self.rotation.get_rotation())) | ||||||
|  |         y_component = total_linear_accel * math.sin(math.radians(self.rotation.get_rotation())) | ||||||
|  | 
 | ||||||
|  |         # 5. Add drag to total acceleration | ||||||
|  |         total_accel_x = x_component + drag_x | ||||||
|  |         total_accel_y = y_component + drag_y | ||||||
|  | 
 | ||||||
|  |         self.acceleration = (total_accel_x, total_accel_y) | ||||||
|  | 
 | ||||||
|  |         rotational_drag = 0.05 | ||||||
|  |         self.angular_acceleration = output_data["angular_acceleration"] - self.rotational_velocity * rotational_drag | ||||||
|  | 
 | ||||||
|  |         # tick acceleration | ||||||
|  |         velocity_x = self.velocity[0] + self.acceleration[0] | ||||||
|  |         velocity_y = self.velocity[1] + self.acceleration[1] | ||||||
|  |         self.velocity = (velocity_x, velocity_y) | ||||||
|  | 
 | ||||||
|  |         # # clamp velocity | ||||||
|  |         speed = math.sqrt(self.velocity[0] ** 2 + self.velocity[1] ** 2) | ||||||
|  |         if speed > MAX_VELOCITY: | ||||||
|  |             scale = MAX_VELOCITY / speed | ||||||
|  |             self.velocity = (self.velocity[0] * scale, self.velocity[1] * scale) | ||||||
| 
 | 
 | ||||||
|         # tick velocity |         # tick velocity | ||||||
|         x, y = self.position.get_position() |         x, y = self.position.get_position() | ||||||
| @ -345,12 +374,19 @@ class DefaultCell(BaseEntity): | |||||||
| 
 | 
 | ||||||
|         self.position.set_position(x, y) |         self.position.set_position(x, y) | ||||||
| 
 | 
 | ||||||
|  |         # tick rotational acceleration | ||||||
|  |         self.angular_acceleration = output_data["angular_acceleration"] | ||||||
|  |         self.rotational_velocity += self.angular_acceleration | ||||||
|  | 
 | ||||||
|  |         # clamp rotational velocity | ||||||
|  |         self.rotational_velocity = max(-MAX_ROTATIONAL_VELOCITY, min(MAX_ROTATIONAL_VELOCITY, self.rotational_velocity)) | ||||||
|  | 
 | ||||||
|         # tick rotational velocity |         # tick rotational velocity | ||||||
|         self.rotation.set_rotation(self.rotation.get_rotation() + self.rotational_velocity) |         self.rotation.set_rotation(self.rotation.get_rotation() + self.rotational_velocity) | ||||||
| 
 | 
 | ||||||
|         movement_cost = abs(output_data["angular_acceleration"]) + abs(output_data["linear_acceleration"]) |         movement_cost = abs(output_data["angular_acceleration"]) + abs(output_data["linear_acceleration"]) | ||||||
| 
 | 
 | ||||||
|         self.energy -= (self.behavioral_model.neural_network.network_cost * 0.1) + 1.2 + (0.15 * movement_cost) |         self.energy -= (self.behavioral_model.neural_network.network_cost * 0.01) + 1 + (0.5 * movement_cost) | ||||||
| 
 | 
 | ||||||
|         return self |         return self | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,82 +0,0 @@ | |||||||
| import math |  | ||||||
| 
 |  | ||||||
| from config.constants import MAX_VELOCITY, MAX_ROTATIONAL_VELOCITY |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class Physics: |  | ||||||
|     """ |  | ||||||
|     Simulates basic 2D physics for an object, including linear and rotational motion |  | ||||||
|     with drag effects. |  | ||||||
|     """ |  | ||||||
|     def __init__(self, drag_coefficient: float, rotational_drag: float): |  | ||||||
|         """ |  | ||||||
|         Initialize the Physics object. |  | ||||||
| 
 |  | ||||||
|         Args: |  | ||||||
|             drag_coefficient (float): Linear drag coefficient. |  | ||||||
|             rotational_drag (float): Rotational drag coefficient. |  | ||||||
|         """ |  | ||||||
| 
 |  | ||||||
|         self.drag_coefficient: float = drag_coefficient |  | ||||||
|         self.rotational_drag: float = rotational_drag |  | ||||||
| 
 |  | ||||||
|         self.velocity: tuple[int, int] = (0, 0) |  | ||||||
|         self.acceleration: tuple[int, int] = (0, 0) |  | ||||||
| 
 |  | ||||||
|         self.rotational_velocity: int = 0 |  | ||||||
|         self.angular_acceleration: int = 0 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|     def move(self, linear_acceleration: float, angular_acceleration: int, rotational_position): |  | ||||||
|         """ |  | ||||||
|         Update the object's velocity and acceleration based on input forces and drag. |  | ||||||
| 
 |  | ||||||
|         Args: |  | ||||||
|             linear_acceleration (float): The applied linear acceleration. |  | ||||||
|             angular_acceleration (int): The applied angular acceleration. |  | ||||||
|             rotational_position: The current rotational position in degrees. |  | ||||||
| 
 |  | ||||||
|         Returns: |  | ||||||
|             tuple: Updated (velocity, acceleration, rotational_velocity, angular_acceleration). |  | ||||||
|         """ |  | ||||||
|         # Apply drag force |  | ||||||
|         drag_coefficient = self.drag_coefficient |  | ||||||
|         drag_x = -self.velocity[0] * drag_coefficient |  | ||||||
|         drag_y = -self.velocity[1] * drag_coefficient |  | ||||||
| 
 |  | ||||||
|         # Combine all forces |  | ||||||
|         total_linear_accel = linear_acceleration |  | ||||||
|         total_linear_accel = max(-0.1, min(0.1, total_linear_accel)) |  | ||||||
| 
 |  | ||||||
|         # Convert to world coordinates |  | ||||||
|         x_component = total_linear_accel * math.cos(math.radians(rotational_position)) |  | ||||||
|         y_component = total_linear_accel * math.sin(math.radians(rotational_position)) |  | ||||||
| 
 |  | ||||||
|         # Add drag to total acceleration |  | ||||||
|         total_accel_x = x_component + drag_x |  | ||||||
|         total_accel_y = y_component + drag_y |  | ||||||
| 
 |  | ||||||
|         self.acceleration = (total_accel_x, total_accel_y) |  | ||||||
| 
 |  | ||||||
|         # Apply drag force to angular acceleration |  | ||||||
|         rotational_drag = self.rotational_drag |  | ||||||
|         self.angular_acceleration = angular_acceleration - self.rotational_velocity * rotational_drag |  | ||||||
| 
 |  | ||||||
|         # tick acceleration |  | ||||||
|         velocity_x = self.velocity[0] + self.acceleration[0] |  | ||||||
|         velocity_y = self.velocity[1] + self.acceleration[1] |  | ||||||
|         self.velocity = (velocity_x, velocity_y) |  | ||||||
| 
 |  | ||||||
|         # clamp velocity |  | ||||||
|         speed = math.sqrt(self.velocity[0] ** 2 + self.velocity[1] ** 2) |  | ||||||
|         if speed > MAX_VELOCITY: |  | ||||||
|             scale = MAX_VELOCITY / speed |  | ||||||
|             self.velocity = (self.velocity[0] * scale, self.velocity[1] * scale) |  | ||||||
| 
 |  | ||||||
|         self.angular_acceleration = angular_acceleration |  | ||||||
|         self.rotational_velocity += self.angular_acceleration |  | ||||||
| 
 |  | ||||||
|         # clamp rotational velocity |  | ||||||
|         self.rotational_velocity = max(-MAX_ROTATIONAL_VELOCITY, min(MAX_ROTATIONAL_VELOCITY, self.rotational_velocity)) |  | ||||||
| 
 |  | ||||||
|         return self.velocity, self.acceleration, self.rotational_velocity, self.angular_acceleration |  | ||||||
| @ -153,8 +153,6 @@ class World: | |||||||
| 
 | 
 | ||||||
|         :param camera: The camera object for coordinate transformation. |         :param camera: The camera object for coordinate transformation. | ||||||
|         :param screen: The Pygame screen surface. |         :param screen: The Pygame screen surface. | ||||||
| 
 |  | ||||||
|         Time complexity: O(n), where n is the number of objects in the current buffer. |  | ||||||
|         """ |         """ | ||||||
|         for obj_list in self.buffers[self.current_buffer].values(): |         for obj_list in self.buffers[self.current_buffer].values(): | ||||||
|             for obj in obj_list: |             for obj in obj_list: | ||||||
| @ -163,9 +161,6 @@ class World: | |||||||
|     def tick_all(self) -> None: |     def tick_all(self) -> None: | ||||||
|         """ |         """ | ||||||
|         Advances all objects in the world by one tick, updating their state and handling interactions. |         Advances all objects in the world by one tick, updating their state and handling interactions. | ||||||
| 
 |  | ||||||
|         Time complexity: O(N + K) / O(N*M), where N is the number of objects in the current buffer, |  | ||||||
|         K is the number of objects that can interact with each object, and M is number of objects in checked cells where C is the number of cells checked within the interaction radius. |  | ||||||
|         """ |         """ | ||||||
|         next_buffer: int = 1 - self.current_buffer |         next_buffer: int = 1 - self.current_buffer | ||||||
|         self.buffers[next_buffer].clear() |         self.buffers[next_buffer].clear() | ||||||
| @ -213,8 +208,6 @@ class World: | |||||||
|         :param y: Y coordinate of the center. |         :param y: Y coordinate of the center. | ||||||
|         :param radius: Search radius. |         :param radius: Search radius. | ||||||
|         :return: List of objects within the radius. |         :return: List of objects within the radius. | ||||||
| 
 |  | ||||||
|         Time complexity: O(C * M) / O(N), where C is the number of cells checked within the radius and M is the number of objects in those cells. |  | ||||||
|         """ |         """ | ||||||
|         result: List[BaseEntity] = [] |         result: List[BaseEntity] = [] | ||||||
|         cell_x, cell_y = int(x // self.partition_size), int(y // self.partition_size) |         cell_x, cell_y = int(x // self.partition_size), int(y // self.partition_size) | ||||||
| @ -241,8 +234,6 @@ class World: | |||||||
|         :param x2: Maximum X coordinate. |         :param x2: Maximum X coordinate. | ||||||
|         :param y2: Maximum Y coordinate. |         :param y2: Maximum Y coordinate. | ||||||
|         :return: List of objects within the rectangle. |         :return: List of objects within the rectangle. | ||||||
| 
 |  | ||||||
|         Time complexity: O(C * M) / O(N), where C is the number of cells checked within the rectangle and M is the number of objects in those cells. |  | ||||||
|         """ |         """ | ||||||
|         result: List[BaseEntity] = [] |         result: List[BaseEntity] = [] | ||||||
|         cell_x1, cell_y1 = ( |         cell_x1, cell_y1 = ( | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user