diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..82cda0b --- /dev/null +++ b/__init__.py @@ -0,0 +1,2 @@ +import os +import sys diff --git a/src/part/particles.json b/particles.json similarity index 99% rename from src/part/particles.json rename to particles.json index b4a2daa..454da41 100644 --- a/src/part/particles.json +++ b/particles.json @@ -17,7 +17,7 @@ "friction": 0.5, "viscosity": 0, "pressure": 0, - "melt": "molten-glass", + "melt": "molten-Glass", "melt_temperature": 1700, "conductive": false, "liquid": false, diff --git a/src/rendering/rendering.py b/rendering.py similarity index 99% rename from src/rendering/rendering.py rename to rendering.py index 6b5d9eb..80c9872 100644 --- a/src/rendering/rendering.py +++ b/rendering.py @@ -22,9 +22,8 @@ The `clear_screen` function is used to reset the simulation grid and clear the d """ -from config.settings import pygame, random, particle_properties, engine_settings -from typing import List, Dict -import colorsys +from settings import pygame, random, particle_properties, engine_settings + class Rendering: diff --git a/src/sandpypi.py b/sandpypi.py similarity index 97% rename from src/sandpypi.py rename to sandpypi.py index 698ab84..664ffdc 100644 --- a/src/sandpypi.py +++ b/sandpypi.py @@ -11,22 +11,19 @@ The main function to run the Sandpypi program. This function initializes the Pygame environment, creates the simulation and rendering objects, and enters the main event loop. It handles user input events such as mouse clicks, mouse wheel scrolling, and keyboard presses. It also updates the simulation, draws the particles, buttons, and other UI elements, and manages the settings menu. -The main loop runs at a target frame rate of 60 FPS (this fps varies on my mood and the testing), with the actual frame rate displayed in the debug overlay. +The main loop runs at a target frame rate of 60 FPS, with the actual frame rate displayed in the debug overlay. """ - -# Import Require files for the Engine. -from config.settings import pygame, cProfile, pstats, engine_settings - -from rendering.rendering import Rendering -from physics.sim import Simulation - +import cProfile +import pstats +from settings import pygame, engine_settings +from rendering import Rendering """ This is for the future physics engine until i figure out a better method used for testing right now. #import os #import sys #sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) """ - +from sim import Simulation def update_simulation(sim, dt, engine_settings): """Update simulation state""" @@ -181,7 +178,7 @@ def main(): screen.fill((0, 0, 0)) fps = clock.get_fps() - dt = clock.tick(60) / 1000 + dt = clock.tick(1000) / 1000 keys = pygame.key.get_pressed() mouse_pos = pygame.mouse.get_pos() zoom_active = keys[pygame.K_z] diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..50ce540 --- /dev/null +++ b/settings.py @@ -0,0 +1,47 @@ +""" +#File Name: settings.py + +Global settings and imports for the project. + +This module defines various settings for the game engine, such as enabling or disabling the cursor, glow effect, gas effect, debug mode, and FPS display. It also provides a function to load particle properties from a JSON file. + +The `engine_settings` dictionary contains the configurable settings for the game engine. These settings can be used to customize the behavior of the game. + +The `load_particle_properties()` function attempts to load particle properties from a 'particles.json' file. If the file is not found or the JSON data is invalid, it returns an empty dictionary. + +The `particle_properties` variable is initialized by calling `load_particle_properties()` when the module is imported. +""" + + +import pygame +import json +import random +import time +import numpy as np + +engine_settings = { + 'pause_sim': True, + 'enable_cursor': True, + 'enable_glow': False, + 'enable_gas_effect': True, + 'enable_debug': False, + 'enable_fps': True, + 'enable_WVisuals': False, + 'enable_PVisuals': False, + 'enable_TempVisuals': False, + 'outerwall': True, + # 'settings': True/False +} + +# Load particle properties from JSON file +def load_particle_properties(): + try: + with open('particles.json') as f: + return json.load(f) + except (FileNotFoundError, json.JSONDecodeError): + print("Error loading particles.json") + return {} + +# Load particle properties once when module is imported +particle_properties = load_particle_properties() + diff --git a/src/physics/sim.py b/sim.py similarity index 96% rename from src/physics/sim.py rename to sim.py index 77493c8..e400744 100644 --- a/src/physics/sim.py +++ b/sim.py @@ -8,21 +8,18 @@ This module implements a 2D particle simulation with physics, interactions, and Key Components: -------------- 1. Particle Class - - Handles individual particle properties and behaviors - - Supports multiple particle types (solid, liquid, gas) + - Handles individual particle properties and behaviors + - Supports multiple particle types (solid, liquid, gas) + - Manages temperature and state transitions 2. Simulation Class - - Core simulation engine - - Manages particle creation, movement and interactions - - Handles physics calculations and spatial partitioning - - Manages temperature and state transitions + - Core simulation engine + - Manages particle creation, movement and interactions + - Handles physics calculations and spatial partitioning """ -#Load the imports. -from config.settings import random, time, particle_properties - -from dataclasses import dataclass -import math +#Load the imports. Pygame is what makes this even work and so simple may consider other engines for performance depends on learning curve. +from settings import random, time, particle_properties # Load particle properties from json so we know what particles we got and how they should be simulated. class Particle: @@ -100,7 +97,6 @@ class Simulation: self.wind_zones = [] self.wind = [0.0, 0.0] # Global wind vector (x, y) - def reset_particle_count(self): self.particle_count = 0 @@ -125,21 +121,7 @@ class Simulation: if cell_key in self.spatial_grid: self.spatial_grid[cell_key].discard((x, y)) return cell_key - - def _get_neighbors_from_grid(self, x, y): - """Get neighbors using spatial grid""" - cell_key = self.get_cell_key(x, y) - neighbors = [] - - # Check current and adjacent cells - for dx in [-1, 0, 1]: - for dy in [-1, 0, 1]: - check_key = (cell_key[0] + dx, cell_key[1] + dy) - if check_key in self.spatial_grid: - neighbors.extend(self.spatial_grid[check_key]) - - return neighbors def update_spatial_grid(self): # this is where we update the spatial grid. """Update spatial grid for optimized collision detection""" @@ -154,8 +136,7 @@ class Simulation: cell_lists[cell_key].append((x, y)) self.spatial_grid = {k: set(v) for k, v in cell_lists.items()} - - + def _check_dormant_state(self, x, y, particle): key = (x, y) if particle.particle_type == 'wall': @@ -177,8 +158,7 @@ class Simulation: self.particle_movement_counter[key] = 0 self.dormant_particles.discard(key) return False - - + def handle_phase_transitions(self, particle, x, y): # this is where we handle all the phase transitions. """Handle all phase transitions for a particle""" # Check evaporation @@ -207,7 +187,6 @@ class Simulation: if new_type in self.particle_properties: self.transform_particle(x, y, new_type) - def handle_particle_interactions(self, dt): # this is where we handle all the particle interactions. """Handle interactions between different particle types""" for x, y in list(self.active_particles): @@ -281,7 +260,6 @@ class Simulation: self.active_particles.add((new_x, new_y)) self.active_particles.discard((x, y)) - def add_wind_zone(self, x, y): # Instead of creating particles, store wind zone data wind_zone = { @@ -321,18 +299,15 @@ class Simulation: fy += drag * particle.velocity[1] # Check neighboring particles - neighbors = self._get_neighbors_from_grid(x, y) + neighbors = self._get_quick_neighbors(x, y) for nx, ny in neighbors: - if(nx, ny) != (x, y): - if 0 <= nx < self.width and 0 <= ny < self.height: - neighbor = self.particles[nx][ny] - if neighbor: - self._apply_neighbor_forces(particle, neighbor, fx, fy) + if 0 <= nx < self.width and 0 <= ny < self.height: + neighbor = self.particles[nx][ny] + if neighbor: + self._apply_neighbor_forces(particle, neighbor, fx, fy) return fx, fy - - def _process_particle_batch(self, batch, dt): updates = [] new_active = set() @@ -381,19 +356,16 @@ class Simulation: return new_active - def _get_quick_neighbors(self, x, y): """Quick neighbor lookup without full spatial grid""" return [(x+dx, y+dy) for dx, dy in [(-1,0), (1,0), (0,-1), (0,1)]] - def _apply_neighbor_forces(self, particle, neighbor, fx, fy): """Optimized neighbor force calculation""" if hasattr(neighbor, 'temperature') and hasattr(particle, 'temperature'): temp_diff = neighbor.temperature - particle.temperature fy += temp_diff * 0.05 - def ignite_particle(self, particle): # this is where we ignite the particle. """Handle ignition and burning of flammable particles.""" if hasattr(particle, 'flamability') and particle.flamability > 0.5: @@ -533,7 +505,6 @@ class Simulation: return particle.particle_type return None - def apply_gravity(self, dt): # this is where we apply gravity. """Handle only gravity and basic particle movement""" self.spatial_grid.clear() @@ -773,8 +744,7 @@ class Simulation: if key in self.dormant_particles: self.dormant_particles.discard(key) self.particle_movement_counter[key] = 0 - - + def track_tps(self): """Track Ticks Per Second for simulation performance monitoring""" if not hasattr(self, '_tps_counter'): @@ -796,17 +766,19 @@ class Simulation: def simulate_step(self, dt, engine_settings): - """Run simulation step with spatial grid updates""" + """Run a single step of the simulation""" - self.update_spatial_grid() - active_list = list(self.active_particles) - batch_size = 1024 + batch_size = 1000 for i in range(0, len(active_list), batch_size): batch = active_list[i:i + batch_size] self._process_particle_batch(batch, dt) + # Update spatial grid only when needed + if len(self.active_particles) > 100: + self.update_spatial_grid() + # Update particle positions and physics self.apply_gravity(dt) self.apply_physics(dt, engine_settings) diff --git a/src/__init__.py b/src/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/config/__init__.py b/src/config/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/config/settings.py b/src/config/settings.py deleted file mode 100644 index 0b0b589..0000000 --- a/src/config/settings.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -#File Name: settings.py - -Global settings and imports for the project. - -This module defines various settings for the game engine, such as enabling or disabling the cursor, glow effect, gas effect, debug mode, and FPS display. It also provides a function to load particle properties from a JSON file. - -The `engine_settings` dictionary contains the configurable settings for the game engine. These settings can be used to customize the behavior of the game. - -The `load_particle_properties()` function attempts to load particle properties from a 'particles.json' file. If the file is not found or the JSON data is invalid, it returns an empty dictionary. - -The `particle_properties` variable is initialized by calling `load_particle_properties()` when the module is imported. -""" -import cProfile -import pstats -import sys -import os -import pygame -import json -import random -import time -import numpy as np - -engine_settings = { - 'pause_sim': True, - 'enable_cursor': True, - 'enable_glow': False, - 'enable_gas_effect': True, - 'enable_debug': True, - 'enable_fps': True, - 'enable_WVisuals': False, - 'enable_PVisuals': False, - 'enable_TempVisuals': False, - 'outerwall': False, - # 'settings': True/False -} -# For particles.json loading in settings.py: -particles_path = os.path.join(os.path.dirname(__file__), '..', 'part', 'particles.json') - - -# Load particle properties from JSON file -def load_particle_properties(): - # Load core particles - particle_data = {} - print(f"Loading particles from {particles_path}") - # Core particles - try: - with open(particles_path, 'r') as f: - particle_data.update(json.load(f)) - print(f"Loaded {len(particle_data)} core particles") - except (FileNotFoundError, json.JSONDecodeError) as e: - print(f"Error loading core particles: {e}") - - # Load mods from mods directory - mods_path = os.path.join(os.path.dirname(__file__), '..', 'part', 'mods') - if os.path.exists(mods_path): - for filename in os.listdir(mods_path): - if filename.endswith('.json'): - mod_file = os.path.join(mods_path, filename) - try: - with open(mod_file, 'r') as f: - mod_data = json.load(f) - print(f"Loading mod: {filename}") - particle_data.update(mod_data) - print(f"Loaded {len(mod_data)} particles from {filename}") - except (FileNotFoundError, json.JSONDecodeError) as e: - print(f"Error loading mod {filename}: {e}") - - return particle_data - -# Load particle properties once when module is imported -particle_properties = load_particle_properties() -__all__ = ['pygame', 'np', 'random', 'time', 'engine_settings', 'particle_properties', 'cProfile', 'pstats', 'sys', 'os'] - diff --git a/src/part/__init__.py b/src/part/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/part/mods/test.json b/src/part/mods/test.json deleted file mode 100644 index 32d906a..0000000 --- a/src/part/mods/test.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "wsand": { - "name": "Wet Sand", - "size": 1, - "hardness": 0.5, - "color": [200, 200, 25, 255], - "velocity": 0.5, - "mass": 0.5, - "conductivity": 0, - "heat_capacity": 1, - "flamability": 0.8, - "temperature": 20, - "explosive": false, - "explosion_radius": 0, - "explosion_color": [0, 0, 0], - "friction": 0.5, - "viscosity": 0.3, - "pressure": 0, - "melt": "sand", - "melt_temperature": 100, - "conductive": false, - "liquid": false, - "solid": true, - "is_gas": false - }, - "ultratanium": { - "name": "Ultra Tanium", - "size": 1, - "hardness": 1000, - "velocity": 0.1, - "conductivity": 1, - "heat_capacity": 1, - "color": [255, 200, 255, 255], - "pressure": 100, - "explosive": true, - "explosion_radius": 15, - "explosion_color": [ - 255, - 0, - 255 - ], - "temperature": 10000, - "conductive": true, - "liquid": true, - "solid": true, - "is_gas": true, - "special": true - } -} \ No newline at end of file diff --git a/src/physics/__init__.py b/src/physics/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/rendering/__init__.py b/src/rendering/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/ui/__init__.py b/src/ui/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/template_particles.json b/template_particles.json similarity index 100% rename from src/template_particles.json rename to template_particles.json