From e5ffc7586bf0879bbecbccd0d117600f5658d72b Mon Sep 17 00:00:00 2001 From: M1thieu Date: Sun, 21 Sep 2025 13:13:37 +0200 Subject: [PATCH 1/8] Restore Scorers clamping behavior Reverts panic-on-invalid-input back to automatic clamping for production safety. Prevents crashes from floating-point drift, config errors, and FixedScore edge cases. I did try a little and faced an issue whereas I did not with this one my bad here --- crates/systems/ai/src/core/scorers.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/systems/ai/src/core/scorers.rs b/crates/systems/ai/src/core/scorers.rs index 3a41fc3..8f39696 100644 --- a/crates/systems/ai/src/core/scorers.rs +++ b/crates/systems/ai/src/core/scorers.rs @@ -24,14 +24,9 @@ impl Score { /// Set the `Score`'s value. /// - /// ### Panics - /// - /// Panics if `value` isn't within `0.0..=1.0`. + /// Values outside `0.0..=1.0` will be automatically clamped to the valid range. pub fn set(&mut self, value: f32) { - if !(0.0..=1.0).contains(&value) { - panic!("Score value must be between 0.0 and 1.0"); - } - self.0 = value; + self.0 = value.clamp(0.0, 1.0); } /// Set the `Score`'s value. Allows values outside the range `0.0..=1.0` From e538bd396011a0234b37e05d5358a7ef0d5c4a4e Mon Sep 17 00:00:00 2001 From: M1thieu Date: Fri, 26 Sep 2025 04:57:17 +0200 Subject: [PATCH 2/8] Refactor Stabilise Systemic Physics Math - Guard wave helpers against zero vectors and apply displacement from rest pose - Fix wave solver chunking, force accumulation, and basic ai if-let - Harden ai need decay, weighted product, and save reflection --- crates/energy/src/waves/oscillation.rs | 13 ++++- crates/energy/src/waves/propagation.rs | 27 +++++++-- crates/energy/src/waves/superposition.rs | 25 ++++++-- crates/energy/src/waves/wave_equation.rs | 58 +++++++++++-------- crates/forces/src/core/newton_laws.rs | 8 +++ crates/systems/ai/src/core/measures.rs | 6 +- crates/systems/ai/src/drives/needs.rs | 18 ++++-- crates/systems/save_system/src/save_system.rs | 56 +++++++++--------- examples/basic_ai.rs | 3 +- 9 files changed, 140 insertions(+), 74 deletions(-) diff --git a/crates/energy/src/waves/oscillation.rs b/crates/energy/src/waves/oscillation.rs index a23414e..e026932 100644 --- a/crates/energy/src/waves/oscillation.rs +++ b/crates/energy/src/waves/oscillation.rs @@ -1,5 +1,14 @@ use bevy::prelude::*; +#[inline] +fn normalize_or(vec: Vec2, fallback: Vec2) -> Vec2 { + if vec.length_squared() > f32::EPSILON { + vec.normalize() + } else { + fallback + } +} + /// Wave parameters for configuring wave behavior #[derive(Component, Debug, Clone, Copy, Reflect)] pub struct WaveParameters { @@ -68,13 +77,13 @@ impl WaveParameters { /// Fluent interface for setting direction pub fn with_direction(mut self, direction: Vec2) -> Self { - self.direction = direction.normalize(); + self.direction = normalize_or(direction, self.direction); self } /// Fluent interface for setting displacement axis pub fn with_displacement_axis(mut self, displacement_axis: Vec2) -> Self { - self.displacement_axis = displacement_axis.normalize(); + self.displacement_axis = normalize_or(displacement_axis, self.displacement_axis); self } diff --git a/crates/energy/src/waves/propagation.rs b/crates/energy/src/waves/propagation.rs index cd9415d..079c614 100644 --- a/crates/energy/src/waves/propagation.rs +++ b/crates/energy/src/waves/propagation.rs @@ -1,6 +1,15 @@ use super::oscillation::{WaveParameters, angular_frequency, wave_number}; use bevy::prelude::*; +#[inline] +fn normalize_or(vec: Vec2, fallback: Vec2) -> Vec2 { + if vec.length_squared() > f32::EPSILON { + vec.normalize() + } else { + fallback + } +} + // Calculate modified angular frequency with dispersion #[inline] pub fn dispersive_angular_frequency(params: &WaveParameters, k: f32) -> f32 { @@ -52,7 +61,8 @@ pub fn solve_wave(params: &WaveParameters, position: Vec2, time: f32) -> f32 { let k = wave_number(params.wavelength); let omega = dispersive_angular_frequency(params, k); - let k_vec = params.direction.normalize() * k; + let direction = normalize_or(params.direction, Vec2::X); + let k_vec = direction * k; let dot_product = k_vec.dot(position); // Calculate the phase argument for the wave function @@ -106,6 +116,8 @@ pub fn update_wave_displacements( let t = time.elapsed_secs(); for (mut transform, params, position, wave_type) in query.iter_mut() { + let base_translation = Vec3::new(position.0.x, position.0.y, transform.translation.z); + let displacement = match wave_type { Some(WaveType::Radial) => { // Find the nearest wave center @@ -128,9 +140,10 @@ pub fn update_wave_displacements( _ => solve_wave(params, position.0, t), }; - let displacement_vec = params.displacement_axis.normalize() * displacement; - transform.translation.x += displacement_vec.x; - transform.translation.y += displacement_vec.y; + let displacement_axis = normalize_or(params.displacement_axis, Vec2::Y); + let displacement_vec = displacement_axis * displacement; + transform.translation = + base_translation + Vec3::new(displacement_vec.x, displacement_vec.y, 0.0); } } @@ -145,13 +158,15 @@ pub fn create_linear_wave( damping: f32, dispersion_factor: f32, ) -> WaveParameters { + let current = WaveParameters::default(); + WaveParameters { amplitude, wavelength, speed, phase, - direction: direction.normalize(), - displacement_axis: displacement_axis.normalize(), + direction: normalize_or(direction, current.direction), + displacement_axis: normalize_or(displacement_axis, current.displacement_axis), damping, dispersion_factor, } diff --git a/crates/energy/src/waves/superposition.rs b/crates/energy/src/waves/superposition.rs index 6757faa..80c2958 100644 --- a/crates/energy/src/waves/superposition.rs +++ b/crates/energy/src/waves/superposition.rs @@ -2,6 +2,15 @@ use super::oscillation::{WaveParameters, angular_frequency, wave_number}; use super::propagation::WavePosition; use bevy::prelude::*; +#[inline] +fn normalize_or(vec: Vec2, fallback: Vec2) -> Vec2 { + if vec.length_squared() > f32::EPSILON { + vec.normalize() + } else { + fallback + } +} + #[inline] pub fn solve_standing_wave( params: &WaveParameters, @@ -12,7 +21,7 @@ pub fn solve_standing_wave( let k = wave_number(params.wavelength); let omega = angular_frequency(params.speed, k); - let direction = params.direction.normalize(); + let direction = normalize_or(params.direction, Vec2::X); let spatial_term = (k * direction.dot(position) + params.phase).sin(); let temporal_term = (omega * time).cos(); @@ -37,10 +46,12 @@ pub fn update_standing_waves( let t = time.elapsed_secs(); for (mut transform, params, position) in query.iter_mut() { + let base_translation = Vec3::new(position.0.x, position.0.y, transform.translation.z); let displacement = solve_standing_wave(params, position.0, t, None:: f32>); - let displacement_vec = params.displacement_axis.normalize() * displacement; - transform.translation.x += displacement_vec.x; - transform.translation.y += displacement_vec.y; + let displacement_axis = normalize_or(params.displacement_axis, Vec2::Y); + let displacement_vec = displacement_axis * displacement; + transform.translation = + base_translation + Vec3::new(displacement_vec.x, displacement_vec.y, 0.0); } } @@ -72,13 +83,15 @@ pub fn create_standing_wave_parameters( damping: f32, dispersion_factor: f32, ) -> WaveParameters { + let defaults = WaveParameters::default(); + WaveParameters { amplitude, wavelength, speed: frequency * wavelength, phase, - direction: direction.normalize(), - displacement_axis: displacement_axis.normalize(), + direction: normalize_or(direction, defaults.direction), + displacement_axis: normalize_or(displacement_axis, defaults.displacement_axis), damping, dispersion_factor, } diff --git a/crates/energy/src/waves/wave_equation.rs b/crates/energy/src/waves/wave_equation.rs index d5c2a97..2e1412c 100644 --- a/crates/energy/src/waves/wave_equation.rs +++ b/crates/energy/src/waves/wave_equation.rs @@ -89,31 +89,39 @@ impl WaveEquation2D { const CHUNK_SIZE: usize = 32; // Optimize memory access patterns by processing in blocks - for j_chunk in 1..((self.ny - 1).div_ceil(CHUNK_SIZE)) { - let j_start = (j_chunk - 1) * CHUNK_SIZE + 1; - let j_end = (j_start + CHUNK_SIZE - 1).min(self.ny - 2); - - for i_chunk in 1..((self.nx - 1).div_ceil(CHUNK_SIZE)) { - let i_start = (i_chunk - 1) * CHUNK_SIZE + 1; - let i_end = (i_start + CHUNK_SIZE - 1).min(self.nx - 2); - - for j in j_start..=j_end { - for i in i_start..=i_end { - // Finite difference formula for 2D wave equation - let laplacian_x = self.get(&self.u_current, i + 1, j) - - 2.0 * self.get(&self.u_current, i, j) - + self.get(&self.u_current, i - 1, j); - - let laplacian_y = self.get(&self.u_current, i, j + 1) - - 2.0 * self.get(&self.u_current, i, j) - + self.get(&self.u_current, i, j - 1); - - let next_value = 2.0 * self.get(&self.u_current, i, j) - - self.get(&self.u_previous, i, j) - + cx * laplacian_x - + cy * laplacian_y; - - self.set(&mut u_next, i, j, next_value); + let inner_height = self.ny.saturating_sub(2); + let inner_width = self.nx.saturating_sub(2); + + if inner_height > 0 && inner_width > 0 { + let j_chunks = (inner_height + CHUNK_SIZE - 1) / CHUNK_SIZE; + let i_chunks = (inner_width + CHUNK_SIZE - 1) / CHUNK_SIZE; + + for j_chunk in 0..j_chunks { + let j_start = 1 + j_chunk * CHUNK_SIZE; + let j_end = (j_start + CHUNK_SIZE - 1).min(self.ny - 2); + + for i_chunk in 0..i_chunks { + let i_start = 1 + i_chunk * CHUNK_SIZE; + let i_end = (i_start + CHUNK_SIZE - 1).min(self.nx - 2); + + for j in j_start..=j_end { + for i in i_start..=i_end { + // Finite difference formula for 2D wave equation + let laplacian_x = self.get(&self.u_current, i + 1, j) + - 2.0 * self.get(&self.u_current, i, j) + + self.get(&self.u_current, i - 1, j); + + let laplacian_y = self.get(&self.u_current, i, j + 1) + - 2.0 * self.get(&self.u_current, i, j) + + self.get(&self.u_current, i, j - 1); + + let next_value = 2.0 * self.get(&self.u_current, i, j) + - self.get(&self.u_previous, i, j) + + cx * laplacian_x + + cy * laplacian_y; + + self.set(&mut u_next, i, j, next_value); + } } } } diff --git a/crates/forces/src/core/newton_laws.rs b/crates/forces/src/core/newton_laws.rs index 4b9b5b7..26a0f5e 100644 --- a/crates/forces/src/core/newton_laws.rs +++ b/crates/forces/src/core/newton_laws.rs @@ -159,6 +159,11 @@ pub fn apply_forces(time: Res