From dd1a3bcc6de4e92782867dab92e63b7fdc1695fb Mon Sep 17 00:00:00 2001 From: M1thieu Date: Thu, 7 Aug 2025 02:34:15 +0200 Subject: [PATCH 1/8] Migrate to Rust 2024 Edition & Happy Clippy - Migrate all 11 crates to Rust 2024 edition - Fix clippy warnings (unused imports, needless borrows, missing Default) - Cargo formatting as well along minor typos fixed --- Cargo.toml | 2 +- crates/energy/Cargo.toml | 2 +- crates/energy/src/conservation.rs | 11 ++++++++--- crates/energy/src/lib.rs | 8 ++++---- crates/energy/src/thermodynamics/mod.rs | 12 ++++++------ crates/energy/src/waves/mod.rs | 11 ++++++----- crates/energy/src/waves/propagation.rs | 2 +- crates/energy/src/waves/superposition.rs | 2 +- crates/forces/Cargo.toml | 2 +- crates/forces/src/core/mod.rs | 10 +++++----- crates/information/Cargo.toml | 2 +- crates/information/src/fractals/core.rs | 6 ++++++ crates/information/src/fractals/interpreter.rs | 1 + crates/information/src/fractals/mod.rs | 4 ++-- crates/information/src/fractals/renderer.rs | 1 + crates/information/src/measures/mod.rs | 2 -- .../information/src/measures/mutual/calculation.rs | 4 ++-- crates/information/src/measures/mutual/mod.rs | 4 ++-- crates/matter/Cargo.toml | 2 +- crates/systems/Cargo.toml | 2 +- crates/systems/acoustics/Cargo.toml | 2 +- crates/systems/ai/Cargo.toml | 2 +- crates/systems/ai/src/core/actions.rs | 13 ++++++++++--- crates/systems/ai/src/core/evaluators.rs | 6 +----- crates/systems/ai/src/core/thinkers.rs | 10 +++++----- crates/systems/ai/src/drives/mod.rs | 2 +- crates/systems/ai/src/lib.rs | 4 ++-- crates/systems/ai/src/personality/mod.rs | 2 +- crates/systems/ai/src/relationships/mod.rs | 6 +++--- crates/systems/ai/src/trackers/mod.rs | 2 +- crates/systems/pbmpm/Cargo.toml | 2 +- crates/systems/save_system/Cargo.toml | 2 +- crates/systems/save_system/src/lib.rs | 7 ++++--- 33 files changed, 83 insertions(+), 67 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c27b1f..2b62610 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "LP" version = "0.1.0" -edition = "2021" +edition = "2024" description = "A systemic 2D platformer in a dynamic living ecosystem.." license = "Apache-2.0" diff --git a/crates/energy/Cargo.toml b/crates/energy/Cargo.toml index 76a29cd..30f4947 100644 --- a/crates/energy/Cargo.toml +++ b/crates/energy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "energy" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Core systems governing how energy manifests, transforms and flows." [dependencies] diff --git a/crates/energy/src/conservation.rs b/crates/energy/src/conservation.rs index c701525..d63677a 100644 --- a/crates/energy/src/conservation.rs +++ b/crates/energy/src/conservation.rs @@ -138,10 +138,15 @@ impl EnergyAccountingLedger { return 0.0; } - let current_time = self.transactions.first().map(|t| t.timestamp).unwrap_or(0.0); + let current_time = self + .transactions + .first() + .map(|t| t.timestamp) + .unwrap_or(0.0); let cutoff_time = current_time - time_window; - let recent_transactions: Vec<&EnergyTransaction> = self.transactions + let recent_transactions: Vec<&EnergyTransaction> = self + .transactions .iter() .take_while(|t| t.timestamp >= cutoff_time) .collect(); @@ -157,7 +162,7 @@ impl EnergyAccountingLedger { /// Get current energy flux (sum of all active transfer rates) pub fn current_flux(&self, current_time: f32, active_duration: f32) -> f32 { let cutoff_time = current_time - active_duration; - + self.transactions .iter() .filter(|t| t.timestamp >= cutoff_time && t.duration > 0.0) diff --git a/crates/energy/src/lib.rs b/crates/energy/src/lib.rs index d2fec95..c886617 100644 --- a/crates/energy/src/lib.rs +++ b/crates/energy/src/lib.rs @@ -66,7 +66,7 @@ pub trait EnergySystem { destination, timestamp: 0.0, // Current time should be passed in a real implementation transfer_rate: 0.0, // Default to instantaneous transfer - duration: 0.0, // Default to instantaneous transfer + duration: 0.0, // Default to instantaneous transfer } } @@ -113,9 +113,9 @@ pub mod prelude { pub use super::{EnergySystem, EnergyTransferError}; pub use crate::conservation::{ - conversion_efficiency, verify_conservation, EnergyAccountingLedger, - EnergyConservationPlugin, EnergyConservationTracker, EnergyQuantity, EnergyTransaction, - EnergyTransferEvent, EnergyType, TransactionType, + EnergyAccountingLedger, EnergyConservationPlugin, EnergyConservationTracker, + EnergyQuantity, EnergyTransaction, EnergyTransferEvent, EnergyType, TransactionType, + conversion_efficiency, verify_conservation, }; pub use crate::electromagnetism::prelude::*; diff --git a/crates/energy/src/thermodynamics/mod.rs b/crates/energy/src/thermodynamics/mod.rs index f907adc..71f3d79 100644 --- a/crates/energy/src/thermodynamics/mod.rs +++ b/crates/energy/src/thermodynamics/mod.rs @@ -22,15 +22,15 @@ impl Plugin for ThermodynamicsPlugin { pub mod prelude { pub use super::entropy::{ - entropy_change_heat_transfer, entropy_change_irreversible, is_valid_process, - total_entropy_change, Entropy, Reversibility, + Entropy, Reversibility, entropy_change_heat_transfer, entropy_change_irreversible, + is_valid_process, total_entropy_change, }; pub use super::equilibrium::{ - apply_equilibrium_transitivity, equilibrium_time_estimate, find_equilibrium_group, - is_in_equilibrium, validate_equilibrium_group_consistency, PhaseState, ThermalEquilibrium, - ThermalProperties, + PhaseState, ThermalEquilibrium, ThermalProperties, apply_equilibrium_transitivity, + equilibrium_time_estimate, find_equilibrium_group, is_in_equilibrium, + validate_equilibrium_group_consistency, }; pub use super::thermal::{ - thermal_utils::heat_conduction, Temperature, ThermalConductivity, ThermalDiffusivity, + Temperature, ThermalConductivity, ThermalDiffusivity, thermal_utils::heat_conduction, }; } diff --git a/crates/energy/src/waves/mod.rs b/crates/energy/src/waves/mod.rs index 78d602c..c3f011c 100644 --- a/crates/energy/src/waves/mod.rs +++ b/crates/energy/src/waves/mod.rs @@ -28,16 +28,17 @@ impl Plugin for WavesPlugin { /// This includes the most common types for wave systems. pub mod prelude { pub use crate::waves::oscillation::{ - angular_frequency, damping_from_half_life, wave_number, WaveParameters, + WaveParameters, angular_frequency, damping_from_half_life, wave_number, }; pub use crate::waves::propagation::{ - create_linear_wave, solve_radial_wave, solve_wave, update_wave_displacements, - WaveCenterMarker, WavePosition, WaveType, + WaveCenterMarker, WavePosition, WaveType, create_linear_wave, solve_radial_wave, + solve_wave, update_wave_displacements, }; pub use crate::waves::superposition::{ - create_standing_wave_parameters, solve_standing_wave, update_standing_waves, StandingWaveMarker, + StandingWaveMarker, create_standing_wave_parameters, solve_standing_wave, + update_standing_waves, }; pub use crate::waves::wave_equation::{ - update_wave_equation, WaveEquation2D, WaveEquationComponent, + WaveEquation2D, WaveEquationComponent, update_wave_equation, }; } diff --git a/crates/energy/src/waves/propagation.rs b/crates/energy/src/waves/propagation.rs index b7e979b..cd9415d 100644 --- a/crates/energy/src/waves/propagation.rs +++ b/crates/energy/src/waves/propagation.rs @@ -1,4 +1,4 @@ -use super::oscillation::{angular_frequency, wave_number, WaveParameters}; +use super::oscillation::{WaveParameters, angular_frequency, wave_number}; use bevy::prelude::*; // Calculate modified angular frequency with dispersion diff --git a/crates/energy/src/waves/superposition.rs b/crates/energy/src/waves/superposition.rs index bad8256..6757faa 100644 --- a/crates/energy/src/waves/superposition.rs +++ b/crates/energy/src/waves/superposition.rs @@ -1,4 +1,4 @@ -use super::oscillation::{angular_frequency, wave_number, WaveParameters}; +use super::oscillation::{WaveParameters, angular_frequency, wave_number}; use super::propagation::WavePosition; use bevy::prelude::*; diff --git a/crates/forces/Cargo.toml b/crates/forces/Cargo.toml index 9990301..67c46b2 100644 --- a/crates/forces/Cargo.toml +++ b/crates/forces/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "forces" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Fundamental interaction mechanisms that create relationships between entities and drive dynamic behaviors across scales." [dependencies] diff --git a/crates/forces/src/core/mod.rs b/crates/forces/src/core/mod.rs index e6d55dc..3c9daa9 100644 --- a/crates/forces/src/core/mod.rs +++ b/crates/forces/src/core/mod.rs @@ -7,15 +7,15 @@ pub mod newton_laws; pub mod prelude { // Re-export from gravity module pub use crate::core::gravity::{ - calculate_elliptical_orbit_velocity, calculate_escape_velocity, - calculate_gravitational_attraction, calculate_orbital_velocity, GravityAffected, - GravityParams, GravitySource, MassiveBody, UniformGravity, GRAVITATIONAL_CONSTANT, + GRAVITATIONAL_CONSTANT, GravityAffected, GravityParams, GravitySource, MassiveBody, + UniformGravity, calculate_elliptical_orbit_velocity, calculate_escape_velocity, + calculate_gravitational_attraction, calculate_orbital_velocity, }; // Re-export from newton_laws module pub use crate::core::newton_laws::{ - apply_forces, calculate_kinetic_energy, calculate_momentum, integrate_positions, AppliedForce, Distance, ForceImpulse, Mass, NewtonLawsPlugin, Norm, PairedForce, - PairedForceInteraction, Velocity, + PairedForceInteraction, Velocity, apply_forces, calculate_kinetic_energy, + calculate_momentum, integrate_positions, }; } diff --git a/crates/information/Cargo.toml b/crates/information/Cargo.toml index 6a464d9..417a54f 100644 --- a/crates/information/Cargo.toml +++ b/crates/information/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "information" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Systems handling pattern emergence, complexity formation, and the organization principles that enable order from chaos." [dependencies] diff --git a/crates/information/src/fractals/core.rs b/crates/information/src/fractals/core.rs index 2aaa7a0..422f084 100644 --- a/crates/information/src/fractals/core.rs +++ b/crates/information/src/fractals/core.rs @@ -6,6 +6,12 @@ pub struct RuleManager<'a> { rules: HashMap, } +impl Default for RuleManager<'_> { + fn default() -> Self { + Self::new() + } +} + impl<'a> RuleManager<'a> { /// Create a new RuleManager. pub fn new() -> Self { diff --git a/crates/information/src/fractals/interpreter.rs b/crates/information/src/fractals/interpreter.rs index 3790b13..2d24412 100644 --- a/crates/information/src/fractals/interpreter.rs +++ b/crates/information/src/fractals/interpreter.rs @@ -18,6 +18,7 @@ pub struct InterpreterOutput { } /// Interprets L-System symbols and computes positions and directions for rendering +#[allow(clippy::too_many_arguments)] pub fn interpret( symbols: &str, rotation_angle: f32, diff --git a/crates/information/src/fractals/mod.rs b/crates/information/src/fractals/mod.rs index be19a4b..d371aed 100644 --- a/crates/information/src/fractals/mod.rs +++ b/crates/information/src/fractals/mod.rs @@ -15,9 +15,9 @@ pub mod prelude { pub use super::grammar::apply_rules; // Interpreter and renderer - pub use super::interpreter::{interpret, InterpreterOutput, SymbolType}; + pub use super::interpreter::{InterpreterOutput, SymbolType, interpret}; pub use super::renderer::run_renderer; // Data loading - pub use super::data_loader::{load_template, Parameters, Template}; + pub use super::data_loader::{Parameters, Template, load_template}; } diff --git a/crates/information/src/fractals/renderer.rs b/crates/information/src/fractals/renderer.rs index b73e707..a0fb52c 100644 --- a/crates/information/src/fractals/renderer.rs +++ b/crates/information/src/fractals/renderer.rs @@ -151,6 +151,7 @@ fn draw_lsystem( } /// Bevy app to render the L-System +#[allow(clippy::too_many_arguments)] pub fn run_renderer( output: &str, angle: f32, diff --git a/crates/information/src/measures/mod.rs b/crates/information/src/measures/mod.rs index 5b07ad9..9c271f3 100644 --- a/crates/information/src/measures/mod.rs +++ b/crates/information/src/measures/mod.rs @@ -2,8 +2,6 @@ pub mod divergence; pub mod mutual; pub mod shannon; -use bevy::prelude::*; - // Re-export main plugin pub use mutual::MutualInformationPlugin; diff --git a/crates/information/src/measures/mutual/calculation.rs b/crates/information/src/measures/mutual/calculation.rs index bc1eadc..5ec5388 100644 --- a/crates/information/src/measures/mutual/calculation.rs +++ b/crates/information/src/measures/mutual/calculation.rs @@ -36,8 +36,8 @@ impl MutualInfo { let mut mi = 0.0; for ((x, y), &joint_count) in &joint_counts { let p_xy = joint_count as f64 / n; - let p_x = *x_counts.get(&x).unwrap() as f64 / n; - let p_y = *y_counts.get(&y).unwrap() as f64 / n; + let p_x = *x_counts.get(x).unwrap() as f64 / n; + let p_y = *y_counts.get(y).unwrap() as f64 / n; // Only add to MI if all probabilities are positive if p_xy > 0.0 { diff --git a/crates/information/src/measures/mutual/mod.rs b/crates/information/src/measures/mutual/mod.rs index 0de10eb..3bbdf02 100644 --- a/crates/information/src/measures/mutual/mod.rs +++ b/crates/information/src/measures/mutual/mod.rs @@ -8,13 +8,13 @@ pub use calculation::*; pub struct MutualInformationPlugin; impl Plugin for MutualInformationPlugin { - fn build(&self, app: &mut App) { + fn build(&self, _app: &mut App) { // For now just register the plugin - future Bevy integration here // Could add systems for real-time MI calculation between entities } } pub mod prelude { - pub use super::calculation::*; pub use super::MutualInformationPlugin; + pub use super::calculation::*; } diff --git a/crates/matter/Cargo.toml b/crates/matter/Cargo.toml index d49d0d6..889fd53 100644 --- a/crates/matter/Cargo.toml +++ b/crates/matter/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "matter" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Physical substance modeling from fundamental particles to complex materials, defining how tangible elements behave and interact." [dependencies] diff --git a/crates/systems/Cargo.toml b/crates/systems/Cargo.toml index affa3ae..8067981 100644 --- a/crates/systems/Cargo.toml +++ b/crates/systems/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "systems" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Core simulation infrastructure components that manage and support the virtual universe rather than represent parts of it." [dependencies] diff --git a/crates/systems/acoustics/Cargo.toml b/crates/systems/acoustics/Cargo.toml index f7a32e3..c28c8c5 100644 --- a/crates/systems/acoustics/Cargo.toml +++ b/crates/systems/acoustics/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "acoustics" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Sound generation, playback, and spatial processing system for ambient and interactive audio experiences." [dependencies] diff --git a/crates/systems/ai/Cargo.toml b/crates/systems/ai/Cargo.toml index faee565..a3085a5 100644 --- a/crates/systems/ai/Cargo.toml +++ b/crates/systems/ai/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ai" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Utility-based AI framework for life form simulation with modular drives, memory, personality and relationships" [lib] diff --git a/crates/systems/ai/src/core/actions.rs b/crates/systems/ai/src/core/actions.rs index 96d6e61..63e84e9 100644 --- a/crates/systems/ai/src/core/actions.rs +++ b/crates/systems/ai/src/core/actions.rs @@ -218,7 +218,10 @@ pub fn steps_system( } Cancelled => { #[cfg(feature = "trace")] - trace!("StepsAction has been cancelled. Cancelling current step {:?} before finalizing.", active_ent); + trace!( + "StepsAction has been cancelled. Cancelling current step {:?} before finalizing.", + active_ent + ); let mut step_state = states.get_mut(active_ent).expect("oops"); if matches!(*step_state, Requested | Executing | Init) { *step_state = Cancelled; @@ -349,7 +352,9 @@ pub fn concurrent_system( failed_idx = Some(idx); all_success = false; #[cfg(feature = "trace")] - trace!("Join action has failed. Cancelling all other actions that haven't completed yet."); + trace!( + "Join action has failed. Cancelling all other actions that haven't completed yet." + ); } Success => {} _ => { @@ -385,7 +390,9 @@ pub fn concurrent_system( succeed_idx = Some(idx); all_failure = false; #[cfg(feature = "trace")] - trace!("Race action has succeeded. Cancelling all other actions that haven't completed yet."); + trace!( + "Race action has succeeded. Cancelling all other actions that haven't completed yet." + ); } _ => { all_failure = false; diff --git a/crates/systems/ai/src/core/evaluators.rs b/crates/systems/ai/src/core/evaluators.rs index c6ad1b7..06e9eac 100644 --- a/crates/systems/ai/src/core/evaluators.rs +++ b/crates/systems/ai/src/core/evaluators.rs @@ -162,9 +162,5 @@ impl Default for SigmoidEvaluator { pub(crate) fn clamp(val: T, min: T, max: T) -> T { let val = if val > max { max } else { val }; - if val < min { - min - } else { - val - } + if val < min { min } else { val } } diff --git a/crates/systems/ai/src/core/thinkers.rs b/crates/systems/ai/src/core/thinkers.rs index b87dce2..e4aa5f2 100644 --- a/crates/systems/ai/src/core/thinkers.rs +++ b/crates/systems/ai/src/core/thinkers.rs @@ -9,8 +9,8 @@ use std::{ use bevy::{ log::{ - tracing::{field, span, Span}, Level, + tracing::{Span, field, span}, }, prelude::*, }; @@ -306,16 +306,16 @@ pub fn thinker_system( ActionState::Cancelled => { debug!("Cleaning up."); if let Some(current) = &mut thinker.current_action { - let action_span = action_spans.get(current.0 .0).expect("Where is it?"); + let action_span = action_spans.get(current.0.0).expect("Where is it?"); debug!("Cancelling current action."); let state = action_states - .get_mut(current.0 .0) + .get_mut(current.0.0) .expect("Missing current action") .clone(); match state { ActionState::Success | ActionState::Failure => { debug!("Action already wrapped up."); - if let Ok(mut ent) = cmd.get_entity(current.0 .0) { + if let Ok(mut ent) = cmd.get_entity(current.0.0) { ent.despawn(); } thinker.current_action = None; @@ -325,7 +325,7 @@ pub fn thinker_system( } _ => { let mut state = - action_states.get_mut(current.0 .0).expect("Missing action"); + action_states.get_mut(current.0.0).expect("Missing action"); debug!("Action still executing. Cancelling it."); action_span.span.in_scope(|| { debug!("Cancelling action."); diff --git a/crates/systems/ai/src/drives/mod.rs b/crates/systems/ai/src/drives/mod.rs index 9acf492..546d445 100644 --- a/crates/systems/ai/src/drives/mod.rs +++ b/crates/systems/ai/src/drives/mod.rs @@ -19,6 +19,6 @@ impl Plugin for DrivesPlugin { /// /// This includes core need types and drive components. pub mod prelude { - pub use crate::drives::needs::{get_most_urgent_need, update_needs, Need, NeedType}; pub use crate::drives::DrivesPlugin; + pub use crate::drives::needs::{Need, NeedType, get_most_urgent_need, update_needs}; } diff --git a/crates/systems/ai/src/lib.rs b/crates/systems/ai/src/lib.rs index 22c355a..202ed1b 100644 --- a/crates/systems/ai/src/lib.rs +++ b/crates/systems/ai/src/lib.rs @@ -13,11 +13,11 @@ pub use crate::core::LPAIPlugin; /// This includes the most common types in this crate, re-exported for your convenience. pub mod prelude { // Main plugins for easy access + pub use crate::LPAIPlugin; pub use crate::drives::DrivesPlugin; pub use crate::personality::PersonalityPlugin; pub use crate::relationships::SocialPlugin; pub use crate::trackers::TrackerPlugin; - pub use crate::LPAIPlugin; // Core interfaces - now directly from crate root pub use crate::{AIModule, ActionExecutor}; @@ -32,7 +32,7 @@ pub mod prelude { // Context-aware personality system pub use crate::personality::traits::{ - update_collective_influence, update_context_aware_utilities, ContextAwareUtilities, + ContextAwareUtilities, update_collective_influence, update_context_aware_utilities, }; } diff --git a/crates/systems/ai/src/personality/mod.rs b/crates/systems/ai/src/personality/mod.rs index a57c2de..ecb32a3 100644 --- a/crates/systems/ai/src/personality/mod.rs +++ b/crates/systems/ai/src/personality/mod.rs @@ -17,6 +17,6 @@ impl Plugin for PersonalityPlugin { /// /// This includes personality traits and related components. pub mod prelude { - pub use crate::personality::traits::{Altruistic, Personality}; pub use crate::personality::PersonalityPlugin; + pub use crate::personality::traits::{Altruistic, Personality}; } diff --git a/crates/systems/ai/src/relationships/mod.rs b/crates/systems/ai/src/relationships/mod.rs index c7bfbaf..cd14cc3 100644 --- a/crates/systems/ai/src/relationships/mod.rs +++ b/crates/systems/ai/src/relationships/mod.rs @@ -20,9 +20,9 @@ impl Plugin for SocialPlugin { /// /// This includes social relationships and network components. pub mod prelude { + pub use crate::relationships::SocialPlugin; pub use crate::relationships::social::{ - get_relationship_strength, EntityRelationship, RelationshipStrength, RelationshipType, - SocialNetwork, SocialRelation, + EntityRelationship, RelationshipStrength, RelationshipType, SocialNetwork, SocialRelation, + get_relationship_strength, }; - pub use crate::relationships::SocialPlugin; } diff --git a/crates/systems/ai/src/trackers/mod.rs b/crates/systems/ai/src/trackers/mod.rs index aa1e13b..1782e58 100644 --- a/crates/systems/ai/src/trackers/mod.rs +++ b/crates/systems/ai/src/trackers/mod.rs @@ -21,8 +21,8 @@ impl Plugin for TrackerPlugin { /// /// This includes the most common tracking components and systems. pub mod prelude { + pub use crate::trackers::TrackerPlugin; pub use crate::trackers::base_tracker::{EntityTracker, TrackedEntity, TrackingRelation}; pub use crate::trackers::needs_tracker::NeedsTracker; pub use crate::trackers::perception_tracker::Perception; - pub use crate::trackers::TrackerPlugin; } diff --git a/crates/systems/pbmpm/Cargo.toml b/crates/systems/pbmpm/Cargo.toml index ea1f5fa..7955a3e 100644 --- a/crates/systems/pbmpm/Cargo.toml +++ b/crates/systems/pbmpm/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "pbmpm" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Material Point Method implementation for simulating continuous materials" [dependencies] diff --git a/crates/systems/save_system/Cargo.toml b/crates/systems/save_system/Cargo.toml index 10e3d7a..5fcfb1e 100644 --- a/crates/systems/save_system/Cargo.toml +++ b/crates/systems/save_system/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "save_system" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Framework for preserving and restoring simulation states across sessions." [dependencies] diff --git a/crates/systems/save_system/src/lib.rs b/crates/systems/save_system/src/lib.rs index 9c55579..b41419c 100644 --- a/crates/systems/save_system/src/lib.rs +++ b/crates/systems/save_system/src/lib.rs @@ -24,8 +24,9 @@ impl Default for SaveSystemPlugin { pub mod prelude { pub use super::SaveSystemPlugin; pub use crate::save_system::{ - get_save_directory, get_save_path, load, load_game_data, save, save_game_data, GameEvent, - GameSaveData, GameSnapshot, GameState, GameTracker, SaveMetadata, Saveable, WorldSaveExt, + GameEvent, GameSaveData, GameSnapshot, GameState, GameTracker, SaveMetadata, Saveable, + WorldSaveExt, get_save_directory, get_save_path, load, load_game_data, save, + save_game_data, }; - pub use crate::versioning::{is_save_up_to_date, upgrade_save, SAVE_VERSION}; + pub use crate::versioning::{SAVE_VERSION, is_save_up_to_date, upgrade_save}; } From 7bb6e7927aef655a16313a3fa34a4577a2e5d30c Mon Sep 17 00:00:00 2001 From: M1thieu Date: Thu, 7 Aug 2025 21:05:55 +0200 Subject: [PATCH 2/8] CI update --- .github/workflows/ci.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 2b16cfe..ef17743 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -27,6 +27,8 @@ jobs: - name: Cache dependencies uses: Swatinem/rust-cache@v2 + with: + shared-key: ci - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -36,6 +38,12 @@ jobs: - name: Run tests run: cargo test --workspace + + - name: Check documentation + run: cargo doc --workspace --no-deps + + - name: Check lockfile consistency + run: cargo update --workspace --locked clippy: name: Clippy @@ -50,6 +58,8 @@ jobs: - name: Cache dependencies uses: Swatinem/rust-cache@v2 + with: + shared-key: ci - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -60,7 +70,7 @@ jobs: run: sudo apt-get update && sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev clang lld - name: Run clippy - run: cargo clippy --workspace -- -A clippy::upper-case-acronyms -A clippy::new-without-default -A clippy::manual-flatten -A clippy::excessive-precision -A clippy::too-many-arguments + run: cargo clippy --workspace -- -A clippy::upper-case-acronyms -A clippy::manual-flatten -A clippy::excessive-precision -A clippy::too-many-arguments format: name: Format From 683fe7647a113ef4eed6002470f8fb63935b5b02 Mon Sep 17 00:00:00 2001 From: M1thieu Date: Fri, 8 Aug 2025 04:02:32 +0200 Subject: [PATCH 3/8] Small Refactor - Unique CI cache keys to prevent conflicts - Should solve future conflicts as well --- .github/workflows/ci.yaml | 9 +++++++-- crates/forces/src/core/gravity.rs | 10 ++++++++-- crates/systems/ai/src/memory/types.rs | 2 +- crates/systems/ai/src/trackers/needs_tracker.rs | 7 ++----- crates/systems/save_system/src/save_system.rs | 6 ++++-- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ef17743..4ef6456 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,7 +28,7 @@ jobs: - name: Cache dependencies uses: Swatinem/rust-cache@v2 with: - shared-key: ci + shared-key: test-suite - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -59,7 +59,7 @@ jobs: - name: Cache dependencies uses: Swatinem/rust-cache@v2 with: - shared-key: ci + shared-key: clippy-lint - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable @@ -80,6 +80,11 @@ jobs: - name: Checkout sources uses: actions/checkout@v4 + - name: Cache dependencies + uses: Swatinem/rust-cache@v2 + with: + shared-key: format-check + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@stable with: diff --git a/crates/forces/src/core/gravity.rs b/crates/forces/src/core/gravity.rs index 79113df..b612f6f 100644 --- a/crates/forces/src/core/gravity.rs +++ b/crates/forces/src/core/gravity.rs @@ -95,13 +95,19 @@ mod spatial { pub center_of_mass: Vec3, } - impl MassProperties { - pub fn new() -> Self { + impl Default for MassProperties { + fn default() -> Self { Self { total_mass: 0.0, center_of_mass: Vec3::ZERO, } } + } + + impl MassProperties { + pub fn new() -> Self { + Self::default() + } pub fn add_body(&mut self, position: Vec3, mass: f32) { let new_total_mass = self.total_mass + mass; diff --git a/crates/systems/ai/src/memory/types.rs b/crates/systems/ai/src/memory/types.rs index db7e150..56d1464 100644 --- a/crates/systems/ai/src/memory/types.rs +++ b/crates/systems/ai/src/memory/types.rs @@ -85,6 +85,6 @@ impl ShortTermMemory { .iter() .filter(|(e, _, _)| *e == entity) .map(|(_, _, strength)| *strength) - .last() // Most recent interaction + .next_back() // Most recent interaction (more efficient for DoubleEndedIterator) } } diff --git a/crates/systems/ai/src/trackers/needs_tracker.rs b/crates/systems/ai/src/trackers/needs_tracker.rs index 0d5ee5e..a5ac4da 100644 --- a/crates/systems/ai/src/trackers/needs_tracker.rs +++ b/crates/systems/ai/src/trackers/needs_tracker.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use bevy::prelude::*; /// Tracks and manages needs for an entity -#[derive(Component)] +#[derive(Component, Default)] pub struct NeedsTracker { needs: Vec, most_urgent_need: Option<(NeedType, UtilityScore)>, @@ -10,10 +10,7 @@ pub struct NeedsTracker { impl NeedsTracker { pub fn new() -> Self { - Self { - needs: Vec::new(), - most_urgent_need: None, - } + Self::default() } pub fn add_need(&mut self, need: Need) { diff --git a/crates/systems/save_system/src/save_system.rs b/crates/systems/save_system/src/save_system.rs index a14c762..cf75d74 100644 --- a/crates/systems/save_system/src/save_system.rs +++ b/crates/systems/save_system/src/save_system.rs @@ -341,8 +341,10 @@ pub fn save_game_data( entities.insert(entity_id, entity_data); } - let mut metadata = SaveMetadata::default(); - metadata.playtime_seconds = game_time; + let metadata = SaveMetadata { + playtime_seconds: game_time, + ..Default::default() + }; let save_data = GameSaveData { version: crate::versioning::SAVE_VERSION.to_string(), From d35d40772ae538f1f757e0f70dd8ead016293a24 Mon Sep 17 00:00:00 2001 From: M1thieu Date: Fri, 29 Aug 2025 23:06:30 +0200 Subject: [PATCH 4/8] Improve Bevy code organization and dev experience - Add dynamic linking for faster compile times - Add SystemSets to ThermodynamicsPlugin for better system ordering - Add Default impl for AppliedForce component - Improve system organization following Bevy best practices --- Cargo.toml | 2 +- crates/energy/src/thermodynamics/mod.rs | 17 ++++++++++++++++- crates/forces/src/core/newton_laws.rs | 6 ++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2b62610..5c3899b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,7 @@ forces = { path = "crates/forces" } information = { path = "crates/information" } matter = { path = "crates/matter" } -bevy = "0.16" +bevy = { version = "0.16", features = ["dynamic_linking"] } glam = "0.29.2" diff --git a/crates/energy/src/thermodynamics/mod.rs b/crates/energy/src/thermodynamics/mod.rs index 71f3d79..ff445bf 100644 --- a/crates/energy/src/thermodynamics/mod.rs +++ b/crates/energy/src/thermodynamics/mod.rs @@ -4,6 +4,14 @@ pub mod thermal; use bevy::prelude::*; +#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] +pub enum ThermodynamicsSet { + /// Calculate thermal transfers and conduction + ThermalTransfer, + /// Update entropy and equilibrium states + Equilibrium, +} + pub struct ThermodynamicsPlugin; impl Plugin for ThermodynamicsPlugin { @@ -16,7 +24,14 @@ impl Plugin for ThermodynamicsPlugin { .register_type::() .register_type::() .add_event::() - .add_systems(Update, thermal::calculate_thermal_transfer); + .configure_sets( + Update, + (ThermodynamicsSet::ThermalTransfer, ThermodynamicsSet::Equilibrium).chain(), + ) + .add_systems( + Update, + thermal::calculate_thermal_transfer.in_set(ThermodynamicsSet::ThermalTransfer), + ); } } diff --git a/crates/forces/src/core/newton_laws.rs b/crates/forces/src/core/newton_laws.rs index de166b3..97d798b 100644 --- a/crates/forces/src/core/newton_laws.rs +++ b/crates/forces/src/core/newton_laws.rs @@ -98,6 +98,12 @@ pub struct AppliedForce { pub elapsed: f32, } +impl Default for AppliedForce { + fn default() -> Self { + Self::new(Vec3::ZERO) + } +} + impl AppliedForce { pub fn new(force: Vec3) -> Self { Self { From 25846dd3fbfe00552c6f3434afef83a190d94a5d Mon Sep 17 00:00:00 2001 From: M1thieu Date: Sat, 20 Sep 2025 19:39:15 +0200 Subject: [PATCH 5/8] Modernize Rust patterns and improve codebase safety MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Replace panic-prone Score::set() with robust clamping behavior • Enhance AI error messages from generic to descriptive debugging info • Optimize memory allocations via direct iteration over collect() chains • Modernize pattern matching with unwrap_or_default() and if-let expressions • Bundle Bevy system registration for improved scheduling performance • Clean up repetitive documentation patterns --- .cargo/config.toml | 6 +- crates/energy/src/conservation.rs | 19 +-- crates/energy/src/thermodynamics/mod.rs | 6 +- crates/energy/src/waves/mod.rs | 13 +- crates/forces/src/core/gravity.rs | 7 +- crates/systems/ai/src/core/actions.rs | 157 +++++++++++++----------- crates/systems/ai/src/core/choices.rs | 2 +- crates/systems/ai/src/core/scorers.rs | 73 ++++++----- crates/systems/ai/src/core/thinkers.rs | 38 ++++-- examples/basic_ai.rs | 7 +- examples/basic_forces.rs | 12 +- examples/basic_save.rs | 5 +- 12 files changed, 198 insertions(+), 147 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 4699790..1b2dc76 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -22,12 +22,10 @@ debug = false incremental = true [target.x86_64-unknown-linux-gnu] -linker = "clang" -rustflags = ["-Clink-arg=-fuse-ld=lld", "-Ctarget-cpu=native"] +rustflags = ["-Ctarget-cpu=native"] [target.aarch64-unknown-linux-gnu] -linker = "clang" -rustflags = ["-Clink-arg=-fuse-ld=lld", "-Ctarget-cpu=native"] +rustflags = ["-Ctarget-cpu=native"] [target.x86_64-pc-windows-msvc] linker = "rust-lld.exe" diff --git a/crates/energy/src/conservation.rs b/crates/energy/src/conservation.rs index d63677a..6b4ed79 100644 --- a/crates/energy/src/conservation.rs +++ b/crates/energy/src/conservation.rs @@ -145,18 +145,23 @@ impl EnergyAccountingLedger { .unwrap_or(0.0); let cutoff_time = current_time - time_window; - let recent_transactions: Vec<&EnergyTransaction> = self + let mut total_rate = 0.0; + let mut count = 0; + + for transaction in self .transactions .iter() .take_while(|t| t.timestamp >= cutoff_time) - .collect(); - - if recent_transactions.is_empty() { - return 0.0; + { + total_rate += transaction.transfer_rate; + count += 1; } - let total_rate: f32 = recent_transactions.iter().map(|t| t.transfer_rate).sum(); - total_rate / recent_transactions.len() as f32 + if count == 0 { + 0.0 + } else { + total_rate / count as f32 + } } /// Get current energy flux (sum of all active transfer rates) diff --git a/crates/energy/src/thermodynamics/mod.rs b/crates/energy/src/thermodynamics/mod.rs index ff445bf..f6215e9 100644 --- a/crates/energy/src/thermodynamics/mod.rs +++ b/crates/energy/src/thermodynamics/mod.rs @@ -26,7 +26,11 @@ impl Plugin for ThermodynamicsPlugin { .add_event::() .configure_sets( Update, - (ThermodynamicsSet::ThermalTransfer, ThermodynamicsSet::Equilibrium).chain(), + ( + ThermodynamicsSet::ThermalTransfer, + ThermodynamicsSet::Equilibrium, + ) + .chain(), ) .add_systems( Update, diff --git a/crates/energy/src/waves/mod.rs b/crates/energy/src/waves/mod.rs index c3f011c..d535ec8 100644 --- a/crates/energy/src/waves/mod.rs +++ b/crates/energy/src/waves/mod.rs @@ -17,15 +17,18 @@ impl Plugin for WavesPlugin { .register_type::() .register_type::() .add_event::() - .add_systems(Update, propagation::update_wave_displacements) - .add_systems(Update, superposition::update_standing_waves) - .add_systems(Update, wave_equation::update_wave_equation); + .add_systems( + Update, + ( + propagation::update_wave_displacements, + superposition::update_standing_waves, + wave_equation::update_wave_equation, + ), + ); } } /// The waves prelude. -/// -/// This includes the most common types for wave systems. pub mod prelude { pub use crate::waves::oscillation::{ WaveParameters, angular_frequency, damping_from_half_life, wave_number, diff --git a/crates/forces/src/core/gravity.rs b/crates/forces/src/core/gravity.rs index b612f6f..93cccc6 100644 --- a/crates/forces/src/core/gravity.rs +++ b/crates/forces/src/core/gravity.rs @@ -371,11 +371,8 @@ pub fn calculate_barnes_hut_force( } let mut total_force = Vec3::ZERO; - for child in &node.children { - if let Some(child_node) = child { - total_force += - calculate_barnes_hut_force(affected_position, child_node, theta, softening); - } + for child_node in node.children.iter().flatten() { + total_force += calculate_barnes_hut_force(affected_position, child_node, theta, softening); } total_force diff --git a/crates/systems/ai/src/core/actions.rs b/crates/systems/ai/src/core/actions.rs index 63e84e9..a035616 100644 --- a/crates/systems/ai/src/core/actions.rs +++ b/crates/systems/ai/src/core/actions.rs @@ -184,7 +184,9 @@ pub fn steps_system( #[cfg(feature = "trace")] trace!("Step {:?} failed. Failing entire StepsAction.", active_ent); let step_state = step_state.clone(); - let mut seq_state = states.get_mut(seq_ent).expect("idk"); + let mut seq_state = states + .get_mut(seq_ent) + .expect("Failed to find action state for sequence entity"); *seq_state = step_state; if let Ok(mut ent) = cmd.get_entity(steps_action.active_ent.entity()) { ent.despawn(); @@ -194,7 +196,9 @@ pub fn steps_system( #[cfg(feature = "trace")] trace!("StepsAction completed all steps successfully."); let step_state = step_state.clone(); - let mut seq_state = states.get_mut(seq_ent).expect("idk"); + let mut seq_state = states + .get_mut(seq_ent) + .expect("Failed to find action state for sequence entity"); *seq_state = step_state; if let Ok(mut ent) = cmd.get_entity(steps_action.active_ent.entity()) { ent.despawn(); @@ -222,7 +226,9 @@ pub fn steps_system( "StepsAction has been cancelled. Cancelling current step {:?} before finalizing.", active_ent ); - let mut step_state = states.get_mut(active_ent).expect("oops"); + let mut step_state = states + .get_mut(active_ent) + .expect("Failed to find action state for active step entity"); if matches!(*step_state, Requested | Executing | Init) { *step_state = Cancelled; } else if matches!(*step_state, Failure | Success) { @@ -321,7 +327,10 @@ pub fn concurrent_system( ) { use ActionState::*; for (seq_ent, concurrent_action, _span) in concurrent_q.iter() { - let current_state = states_q.get_mut(seq_ent).expect("uh oh").clone(); + let current_state = states_q + .get_mut(seq_ent) + .expect("Failed to find action state for concurrent sequence entity") + .clone(); #[cfg(feature = "trace")] let _guard = _span.span.enter(); @@ -332,97 +341,105 @@ pub fn concurrent_system( "Initializing Concurrently action with {} children.", concurrent_action.actions.len() ); - let mut current_state = states_q.get_mut(seq_ent).expect("uh oh"); + let mut current_state = states_q + .get_mut(seq_ent) + .expect("Failed to find action state component for concurrent action entity"); *current_state = Executing; for action in concurrent_action.actions.iter() { let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("uh oh"); + let mut child_state = states_q.get_mut(child_ent).expect( + "Failed to find action state component for concurrent action entity", + ); *child_state = Requested; } } - Executing => match concurrent_action.mode { - ConcurrentMode::Join => { - let mut all_success = true; - let mut failed_idx = None; - for (idx, action) in concurrent_action.actions.iter().enumerate() { - let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("uh oh"); - match *child_state { - Failure => { - failed_idx = Some(idx); - all_success = false; - #[cfg(feature = "trace")] - trace!( - "Join action has failed. Cancelling all other actions that haven't completed yet." - ); + Executing => { + match concurrent_action.mode { + ConcurrentMode::Join => { + let mut all_success = true; + let mut failed_idx = None; + for (idx, action) in concurrent_action.actions.iter().enumerate() { + let child_ent = action.entity(); + let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); + match *child_state { + Failure => { + failed_idx = Some(idx); + all_success = false; + #[cfg(feature = "trace")] + trace!( + "Join action has failed. Cancelling all other actions that haven't completed yet." + ); + } + Success => {} + _ => { + all_success = false; + if failed_idx.is_some() { + *child_state = Cancelled; + } + } } - Success => {} - _ => { - all_success = false; - if failed_idx.is_some() { + } + if all_success { + *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Success; + } else if let Some(idx) = failed_idx { + for action in concurrent_action.actions.iter().take(idx) { + let child_ent = action.entity(); + let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); + if !matches!(*child_state, Failure | Success) { *child_state = Cancelled; } } + *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Failure; } } - if all_success { - *states_q.get_mut(seq_ent).expect("uh oh") = Success; - } else if let Some(idx) = failed_idx { - for action in concurrent_action.actions.iter().take(idx) { + ConcurrentMode::Race => { + let mut all_failure = true; + let mut succeed_idx = None; + for (idx, action) in concurrent_action.actions.iter().enumerate() { let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("uh oh"); - if !matches!(*child_state, Failure | Success) { - *child_state = Cancelled; + let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); + match *child_state { + Failure => {} + Success => { + succeed_idx = Some(idx); + all_failure = false; + #[cfg(feature = "trace")] + trace!( + "Race action has succeeded. Cancelling all other actions that haven't completed yet." + ); + } + _ => { + all_failure = false; + if succeed_idx.is_some() { + *child_state = Cancelled; + } + } } } - *states_q.get_mut(seq_ent).expect("uh oh") = Failure; - } - } - ConcurrentMode::Race => { - let mut all_failure = true; - let mut succeed_idx = None; - for (idx, action) in concurrent_action.actions.iter().enumerate() { - let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("uh oh"); - match *child_state { - Failure => {} - Success => { - succeed_idx = Some(idx); - all_failure = false; - #[cfg(feature = "trace")] - trace!( - "Race action has succeeded. Cancelling all other actions that haven't completed yet." - ); - } - _ => { - all_failure = false; - if succeed_idx.is_some() { + if all_failure { + *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Failure; + } else if let Some(idx) = succeed_idx { + for action in concurrent_action.actions.iter().take(idx) { + let child_ent = action.entity(); + let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); + if !matches!(*child_state, Failure | Success) { *child_state = Cancelled; } } + *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Success; } } - if all_failure { - *states_q.get_mut(seq_ent).expect("uh oh") = Failure; - } else if let Some(idx) = succeed_idx { - for action in concurrent_action.actions.iter().take(idx) { - let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("uh oh"); - if !matches!(*child_state, Failure | Success) { - *child_state = Cancelled; - } - } - *states_q.get_mut(seq_ent).expect("uh oh") = Success; - } } - }, + } Cancelled => { let mut all_done = true; let mut any_failed = false; let mut any_success = false; for action in concurrent_action.actions.iter() { let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("uh oh"); + let mut child_state = states_q.get_mut(child_ent).expect( + "Failed to find action state component for concurrent action entity", + ); match *child_state { Init => {} Success => any_success = true, @@ -434,7 +451,9 @@ pub fn concurrent_system( } } if all_done { - let mut state_var = states_q.get_mut(seq_ent).expect("uh oh"); + let mut state_var = states_q.get_mut(seq_ent).expect( + "Failed to find action state component for concurrent action entity", + ); match concurrent_action.mode { ConcurrentMode::Race => { if any_success { diff --git a/crates/systems/ai/src/core/choices.rs b/crates/systems/ai/src/core/choices.rs index 10de274..0909c09 100644 --- a/crates/systems/ai/src/core/choices.rs +++ b/crates/systems/ai/src/core/choices.rs @@ -22,7 +22,7 @@ impl Choice { pub fn calculate(&self, scores: &Query<&Score>) -> f32 { scores .get(self.scorer.0) - .expect("Where did the score go?") + .expect("Failed to find score component for choice scorer") .0 } } diff --git a/crates/systems/ai/src/core/scorers.rs b/crates/systems/ai/src/core/scorers.rs index 0dbc454..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` @@ -290,13 +285,17 @@ pub fn sum_of_scorers_system( { let mut sum = 0.0; for Scorer(child) in children.iter() { - let score = scores.get_mut(*child).expect("where is it?"); + let score = scores + .get_mut(*child) + .expect("Failed to find score component for SumOfScorers child"); sum += score.0; } if sum < *threshold { sum = 0.0; } - let mut score = scores.get_mut(sos_ent).expect("where did it go?"); + let mut score = scores + .get_mut(sos_ent) + .expect("Failed to find score component for SumOfScorers entity"); score.set(crate::core::evaluators::clamp(sum, 0.0, 1.0)); #[cfg(feature = "trace")] { @@ -400,7 +399,9 @@ pub fn product_of_scorers_system( let mut num_scorers = 0; for Scorer(child) in children.iter() { - let score = scores.get_mut(*child).expect("where is it?"); + let score = scores + .get_mut(*child) + .expect("Failed to find score component for ProductOfScorers child"); product *= score.0; num_scorers += 1; } @@ -417,7 +418,9 @@ pub fn product_of_scorers_system( product = 0.0; } - let mut score = scores.get_mut(sos_ent).expect("where did it go?"); + let mut score = scores + .get_mut(sos_ent) + .expect("Failed to find score component for ProductOfScorers entity"); score.set(product.clamp(0.0, 1.0)); #[cfg(feature = "trace")] { @@ -515,23 +518,20 @@ pub fn winning_scorer_system( ) { for (sos_ent, mut winning_scorer, _span) in query.iter_mut() { let (threshold, children) = (winning_scorer.threshold, &mut winning_scorer.scorers); - let mut all_scores = children + let winning_score_or_zero = children .iter() - .map(|Scorer(e)| scores.get(*e).expect("where is it?")) - .collect::>(); - - all_scores.sort_by(|a, b| a.get().partial_cmp(&b.get()).unwrap_or(Ordering::Equal)); - let winning_score_or_zero = match all_scores.last() { - Some(s) => { - if s.get() < threshold { - 0.0 - } else { - s.get() - } - } - None => 0.0, - }; - let mut score = scores.get_mut(sos_ent).expect("where did it go?"); + .map(|Scorer(e)| { + scores + .get(*e) + .expect("Failed to find score component for WinningScorer child") + }) + .map(|score| score.get()) + .max_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)) + .filter(|&score| score >= threshold) + .unwrap_or(0.0); + let mut score = scores + .get_mut(sos_ent) + .expect("Failed to find score component for WinningScorer entity"); score.set(crate::core::evaluators::clamp( winning_score_or_zero, 0.0, @@ -631,10 +631,12 @@ pub fn evaluating_scorer_system( // Get the inner score let inner_score = scores .get(eval_scorer.scorer.0) - .expect("where did it go?") + .expect("Failed to find score component for TrustScorer inner scorer") .get(); // Get composite score - let mut score = scores.get_mut(sos_ent).expect("where did it go?"); + let mut score = scores + .get_mut(sos_ent) + .expect("Failed to find score component for TrustScorer entity"); score.set(crate::core::evaluators::clamp( eval_scorer.evaluator.evaluate(inner_score), 0.0, @@ -723,10 +725,19 @@ pub fn measured_scorers_system( let measured_score = measure.calculate( children .iter() - .map(|(scorer, weight)| (scores.get(scorer.0).expect("where is it?"), *weight)) + .map(|(scorer, weight)| { + ( + scores + .get(scorer.0) + .expect("Failed to find score component for WeightedScorer child"), + *weight, + ) + }) .collect::>(), ); - let mut score = scores.get_mut(sos_ent).expect("where did it go?"); + let mut score = scores + .get_mut(sos_ent) + .expect("Failed to find score component for WeightedScorer entity"); if measured_score < *threshold { score.set(0.0); diff --git a/crates/systems/ai/src/core/thinkers.rs b/crates/systems/ai/src/core/thinkers.rs index e4aa5f2..0c7d694 100644 --- a/crates/systems/ai/src/core/thinkers.rs +++ b/crates/systems/ai/src/core/thinkers.rs @@ -285,7 +285,7 @@ pub fn thinker_system( let thinker_state = action_states .get_mut(thinker_ent) - .expect("Where is it?") + .expect("Failed to find action state component for thinker") .clone(); let thinker_span = thinker.span.clone(); @@ -293,12 +293,16 @@ pub fn thinker_system( match thinker_state { ActionState::Init => { - let mut act_state = action_states.get_mut(thinker_ent).expect("???"); + let mut act_state = action_states + .get_mut(thinker_ent) + .expect("Failed to find action state component for thinker entity"); debug!("Initializing thinker."); *act_state = ActionState::Requested; } ActionState::Requested => { - let mut act_state = action_states.get_mut(thinker_ent).expect("???"); + let mut act_state = action_states + .get_mut(thinker_ent) + .expect("Failed to find action state component for thinker entity"); debug!("Starting execution."); *act_state = ActionState::Executing; } @@ -306,7 +310,9 @@ pub fn thinker_system( ActionState::Cancelled => { debug!("Cleaning up."); if let Some(current) = &mut thinker.current_action { - let action_span = action_spans.get(current.0.0).expect("Where is it?"); + let action_span = action_spans + .get(current.0.0) + .expect("Failed to find action span for current action"); debug!("Cancelling current action."); let state = action_states .get_mut(current.0.0) @@ -334,7 +340,9 @@ pub fn thinker_system( } } } else { - let mut act_state = action_states.get_mut(thinker_ent).expect("???"); + let mut act_state = action_states + .get_mut(thinker_ent) + .expect("Failed to find action state component for thinker entity"); debug!("No current action. Completing as Success."); *act_state = ActionState::Success; } @@ -347,7 +355,9 @@ pub fn thinker_system( trace!("Action picked. Executing picked action."); let action = choice.action.clone(); let scorer = choice.scorer; - let score = scores.get(choice.scorer.0).expect("Where is it?"); + let score = scores + .get(choice.scorer.0) + .expect("Failed to find score component for chosen scorer"); exec_picked_action( &mut cmd, *actor, @@ -382,7 +392,9 @@ pub fn thinker_system( false, ); } else if let Some((action_ent, _)) = &thinker.current_action { - let action_span = action_spans.get(action_ent.0).expect("Where is it?"); + let action_span = action_spans + .get(action_ent.0) + .expect("Failed to find action span for action entity"); let _guard = action_span.span.enter(); let mut curr_action_state = action_states .get_mut(action_ent.0) @@ -469,7 +481,9 @@ fn exec_picked_action( *curr_action_state, ActionState::Success | ActionState::Failure ); - let action_span = action_spans.get(action_ent.0).expect("Where is it?"); + let action_span = action_spans + .get(action_ent.0) + .expect("Failed to find action span for action entity"); let _guard = action_span.span.enter(); if (!Arc::ptr_eq(current_id, &picked_action.0) && override_current) || previous_done { if !previous_done { @@ -492,7 +506,9 @@ fn exec_picked_action( ent.despawn(); } if let Some((Scorer(ent), score)) = scorer_info { - let scorer_span = scorer_spans.get(*ent).expect("Where is it?"); + let scorer_span = scorer_spans + .get(*ent) + .expect("Failed to find scorer span for scorer entity"); let _guard = scorer_span.span.enter(); debug!("Winning score: {}", score.get()); } @@ -518,7 +534,9 @@ fn exec_picked_action( trace!("Falling back to `otherwise` clause.",); if let Some((Scorer(ent), score)) = scorer_info { - let scorer_span = scorer_spans.get(*ent).expect("Where is it?"); + let scorer_span = scorer_spans + .get(*ent) + .expect("Failed to find scorer span for scorer entity"); let _guard = scorer_span.span.enter(); debug!("Winning score: {}", score.get()); } diff --git a/examples/basic_ai.rs b/examples/basic_ai.rs index ff4c533..3381fa5 100644 --- a/examples/basic_ai.rs +++ b/examples/basic_ai.rs @@ -19,10 +19,9 @@ fn main() { ( update_creatures_and_perception, handle_food_consumption, - respawn_food, - update_visuals, - update_personality_labels, - ), + (respawn_food, update_visuals, update_personality_labels), + ) + .chain(), ) .run(); } diff --git a/examples/basic_forces.rs b/examples/basic_forces.rs index ae21dbb..8d4fe48 100644 --- a/examples/basic_forces.rs +++ b/examples/basic_forces.rs @@ -17,12 +17,12 @@ fn main() { Update, ( reset_forces, - calculate_gravitational_attraction.after(reset_forces), - apply_forces.after(calculate_gravitational_attraction), - integrate_positions.after(apply_forces), - update_sprites.after(integrate_positions), - keep_in_bounds.after(integrate_positions), - ), + calculate_gravitational_attraction, + apply_forces, + integrate_positions, + (update_sprites, keep_in_bounds), + ) + .chain(), ) .run(); } diff --git a/examples/basic_save.rs b/examples/basic_save.rs index f0db785..a195053 100644 --- a/examples/basic_save.rs +++ b/examples/basic_save.rs @@ -20,10 +20,7 @@ impl Default for GameData { fn main() { let path = "save.json"; - let mut data = match load::(path) { - Ok(data) => data, - Err(_) => GameData::default(), - }; + let mut data = load::(path).unwrap_or_default(); data.score += 1; println!("Score: {}", data.score); From b0c8f7c6b6dd018365fcbb616ee258292c95df0a Mon Sep 17 00:00:00 2001 From: M1thieu Date: Sat, 20 Sep 2025 21:05:00 +0200 Subject: [PATCH 6/8] Reduce DRY violations and improve AI system maintainability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Add UtilityScore constants (ZERO, HALF, MAX) for common values • Consolidate repeated error messages in concurrent action systems • Extract trait value clamping to shared helper function • Replace magic numbers with named constants for configuration • Revert Score::set() to maintain compatibility • Remove external library references from documentation --- crates/systems/ai/src/core/actions.rs | 148 +++++++++--------- crates/systems/ai/src/core/mod.rs | 4 +- crates/systems/ai/src/core/scorers.rs | 9 +- crates/systems/ai/src/core/utility.rs | 12 +- crates/systems/ai/src/drives/needs.rs | 7 +- crates/systems/ai/src/personality/traits.rs | 26 +-- crates/systems/ai/src/relationships/social.rs | 7 +- .../systems/ai/src/trackers/needs_tracker.rs | 4 +- 8 files changed, 117 insertions(+), 100 deletions(-) diff --git a/crates/systems/ai/src/core/actions.rs b/crates/systems/ai/src/core/actions.rs index a035616..18395b3 100644 --- a/crates/systems/ai/src/core/actions.rs +++ b/crates/systems/ai/src/core/actions.rs @@ -4,6 +4,10 @@ use crate::core::thinkers::{Action, ActionSpan, Actor}; use bevy::prelude::*; use std::sync::Arc; +// Error message constants to reduce repetition +const ACTION_STATE_ERROR: &str = + "Failed to find action state component for concurrent action entity"; + /// The current state for an Action. These states are changed by a combination /// of the Thinker that spawned it, and the actual Action system executing the /// Action itself. @@ -341,105 +345,101 @@ pub fn concurrent_system( "Initializing Concurrently action with {} children.", concurrent_action.actions.len() ); - let mut current_state = states_q - .get_mut(seq_ent) - .expect("Failed to find action state component for concurrent action entity"); + let mut current_state = states_q.get_mut(seq_ent).expect(ACTION_STATE_ERROR); *current_state = Executing; for action in concurrent_action.actions.iter() { let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect( - "Failed to find action state component for concurrent action entity", - ); + let mut child_state = states_q.get_mut(child_ent).expect(ACTION_STATE_ERROR); *child_state = Requested; } } - Executing => { - match concurrent_action.mode { - ConcurrentMode::Join => { - let mut all_success = true; - let mut failed_idx = None; - for (idx, action) in concurrent_action.actions.iter().enumerate() { - let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); - match *child_state { - Failure => { - failed_idx = Some(idx); - all_success = false; - #[cfg(feature = "trace")] - trace!( - "Join action has failed. Cancelling all other actions that haven't completed yet." - ); - } - Success => {} - _ => { - all_success = false; - if failed_idx.is_some() { - *child_state = Cancelled; - } - } + Executing => match concurrent_action.mode { + ConcurrentMode::Join => { + let mut all_success = true; + let mut failed_idx = None; + for (idx, action) in concurrent_action.actions.iter().enumerate() { + let child_ent = action.entity(); + let mut child_state = + states_q.get_mut(child_ent).expect(ACTION_STATE_ERROR); + match *child_state { + Failure => { + failed_idx = Some(idx); + all_success = false; + #[cfg(feature = "trace")] + trace!( + "Join action has failed. Cancelling all other actions that haven't completed yet." + ); } - } - if all_success { - *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Success; - } else if let Some(idx) = failed_idx { - for action in concurrent_action.actions.iter().take(idx) { - let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); - if !matches!(*child_state, Failure | Success) { + Success => {} + _ => { + all_success = false; + if failed_idx.is_some() { *child_state = Cancelled; } } - *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Failure; } } - ConcurrentMode::Race => { - let mut all_failure = true; - let mut succeed_idx = None; - for (idx, action) in concurrent_action.actions.iter().enumerate() { + if all_success { + *states_q.get_mut(seq_ent).expect(ACTION_STATE_ERROR) = Success; + } else if let Some(idx) = failed_idx { + for action in concurrent_action.actions.iter().take(idx) { let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); - match *child_state { - Failure => {} - Success => { - succeed_idx = Some(idx); - all_failure = false; - #[cfg(feature = "trace")] - trace!( - "Race action has succeeded. Cancelling all other actions that haven't completed yet." - ); - } - _ => { - all_failure = false; - if succeed_idx.is_some() { - *child_state = Cancelled; - } - } + let mut child_state = + states_q.get_mut(child_ent).expect(ACTION_STATE_ERROR); + if !matches!(*child_state, Failure | Success) { + *child_state = Cancelled; } } - if all_failure { - *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Failure; - } else if let Some(idx) = succeed_idx { - for action in concurrent_action.actions.iter().take(idx) { - let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect("Failed to find action state component for concurrent action entity"); - if !matches!(*child_state, Failure | Success) { + *states_q.get_mut(seq_ent).expect(ACTION_STATE_ERROR) = Failure; + } + } + ConcurrentMode::Race => { + let mut all_failure = true; + let mut succeed_idx = None; + for (idx, action) in concurrent_action.actions.iter().enumerate() { + let child_ent = action.entity(); + let mut child_state = + states_q.get_mut(child_ent).expect(ACTION_STATE_ERROR); + match *child_state { + Failure => {} + Success => { + succeed_idx = Some(idx); + all_failure = false; + #[cfg(feature = "trace")] + trace!( + "Race action has succeeded. Cancelling all other actions that haven't completed yet." + ); + } + _ => { + all_failure = false; + if succeed_idx.is_some() { *child_state = Cancelled; } } - *states_q.get_mut(seq_ent).expect("Failed to find action state component for concurrent action entity") = Success; } } + if all_failure { + *states_q.get_mut(seq_ent).expect(ACTION_STATE_ERROR) = Failure; + } else if let Some(idx) = succeed_idx { + for action in concurrent_action.actions.iter().take(idx) { + let child_ent = action.entity(); + let mut child_state = + states_q.get_mut(child_ent).expect(ACTION_STATE_ERROR); + if !matches!(*child_state, Failure | Success) { + *child_state = Cancelled; + } + } + *states_q.get_mut(seq_ent).expect(ACTION_STATE_ERROR) = Success; + } } - } + }, Cancelled => { let mut all_done = true; let mut any_failed = false; let mut any_success = false; for action in concurrent_action.actions.iter() { let child_ent = action.entity(); - let mut child_state = states_q.get_mut(child_ent).expect( - "Failed to find action state component for concurrent action entity", - ); + let mut child_state = states_q.get_mut(child_ent).expect(ACTION_STATE_ERROR); match *child_state { Init => {} Success => any_success = true, @@ -451,9 +451,7 @@ pub fn concurrent_system( } } if all_done { - let mut state_var = states_q.get_mut(seq_ent).expect( - "Failed to find action state component for concurrent action entity", - ); + let mut state_var = states_q.get_mut(seq_ent).expect(ACTION_STATE_ERROR); match concurrent_action.mode { ConcurrentMode::Race => { if any_success { diff --git a/crates/systems/ai/src/core/mod.rs b/crates/systems/ai/src/core/mod.rs index c09dbc3..ab9bbc8 100644 --- a/crates/systems/ai/src/core/mod.rs +++ b/crates/systems/ai/src/core/mod.rs @@ -1,7 +1,7 @@ //! Core AI module for LP //! -//! Provides a utility AI system with composable scorers, actions, and thinkers. -//! Based on the Big Brain architecture but integrated into LP's ecosystem. +//! Provides a utility AI system with composable scorers, actions, and thinkers +//! for sophisticated agent behavior in dynamic ecosystems. pub mod actions; pub mod choices; diff --git a/crates/systems/ai/src/core/scorers.rs b/crates/systems/ai/src/core/scorers.rs index 8f39696..3a41fc3 100644 --- a/crates/systems/ai/src/core/scorers.rs +++ b/crates/systems/ai/src/core/scorers.rs @@ -24,9 +24,14 @@ impl Score { /// Set the `Score`'s value. /// - /// Values outside `0.0..=1.0` will be automatically clamped to the valid range. + /// ### Panics + /// + /// Panics if `value` isn't within `0.0..=1.0`. pub fn set(&mut self, value: f32) { - self.0 = value.clamp(0.0, 1.0); + if !(0.0..=1.0).contains(&value) { + panic!("Score value must be between 0.0 and 1.0"); + } + self.0 = value; } /// Set the `Score`'s value. Allows values outside the range `0.0..=1.0` diff --git a/crates/systems/ai/src/core/utility.rs b/crates/systems/ai/src/core/utility.rs index 9cc7f56..0a22b8e 100644 --- a/crates/systems/ai/src/core/utility.rs +++ b/crates/systems/ai/src/core/utility.rs @@ -9,6 +9,11 @@ use std::collections::HashMap; pub struct UtilityScore(f32); impl UtilityScore { + /// Common utility score constants + pub const ZERO: Self = Self(0.0); + pub const HALF: Self = Self(0.5); + pub const MAX: Self = Self(1.0); + /// Create a new utility score, clamping to valid range pub fn new(value: f32) -> Self { Self(value.clamp(0.0, 1.0)) @@ -19,6 +24,11 @@ impl UtilityScore { self.0 } + /// Helper function for clamping trait values to valid range + pub fn clamp_trait_value(value: f32) -> f32 { + value.clamp(0.0, 1.0) + } + /// Normalize a collection of scores to sum to 1.0 pub fn normalize_scores(scores: &mut [UtilityScore]) { let total: f32 = scores.iter().map(|score| score.value()).sum(); @@ -107,7 +117,7 @@ pub fn determine_behavior( modules: &[(&dyn AIModule, UtilityScore, Behavior)], ) -> (Behavior, UtilityScore) { if modules.is_empty() { - return (Behavior::Idle, UtilityScore::new(0.0)); + return (Behavior::Idle, UtilityScore::ZERO); } // Create normalized version of the scores for better decision making diff --git a/crates/systems/ai/src/drives/needs.rs b/crates/systems/ai/src/drives/needs.rs index 7947171..9849b46 100644 --- a/crates/systems/ai/src/drives/needs.rs +++ b/crates/systems/ai/src/drives/needs.rs @@ -1,3 +1,4 @@ +use crate::core::utility::UtilityScore; use crate::prelude::*; use bevy::prelude::*; @@ -27,9 +28,9 @@ impl Need { pub fn new(need_type: NeedType, satisfaction: f32, depletion_rate: f32, priority: f32) -> Self { Self { need_type, - satisfaction: satisfaction.clamp(0.0, 1.0), + satisfaction: UtilityScore::clamp_trait_value(satisfaction), depletion_rate: depletion_rate.max(0.0), - priority: priority.clamp(0.0, 1.0), + priority: UtilityScore::clamp_trait_value(priority), } } @@ -57,7 +58,7 @@ pub fn get_most_urgent_need( needs: Query<&Need>, ) -> Option<(NeedType, UtilityScore)> { let mut most_urgent = None; - let mut highest_urgency = UtilityScore::new(0.0); + let mut highest_urgency = UtilityScore::ZERO; for need in needs.iter_many(std::iter::once(entity)) { let urgency = need.urgency(); diff --git a/crates/systems/ai/src/personality/traits.rs b/crates/systems/ai/src/personality/traits.rs index fe1503d..da85610 100644 --- a/crates/systems/ai/src/personality/traits.rs +++ b/crates/systems/ai/src/personality/traits.rs @@ -1,7 +1,11 @@ +use crate::core::utility::UtilityScore; use crate::prelude::*; use bevy::prelude::*; // Removed direct energy dependency - use trait-based interface instead +// Constants for social influence calculations +const MAX_INFLUENCE_DISTANCE: f32 = 100.0; // Universal influence range + /// Universal life adaptation traits for all organisms (plants through animals) #[derive(Component, Debug, Clone, Reflect)] pub struct Personality { @@ -34,9 +38,9 @@ impl Personality { competitive_strength: f32, ) -> Self { Self { - resource_assertiveness: resource_assertiveness.clamp(0.0, 1.0), - stress_tolerance: stress_tolerance.clamp(0.0, 1.0), - competitive_strength: competitive_strength.clamp(0.0, 1.0), + resource_assertiveness: UtilityScore::clamp_trait_value(resource_assertiveness), + stress_tolerance: UtilityScore::clamp_trait_value(stress_tolerance), + competitive_strength: UtilityScore::clamp_trait_value(competitive_strength), } } @@ -82,7 +86,7 @@ impl AIModule for Personality { fn utility(&self) -> UtilityScore { // Return a base utility score for personality-driven behaviors - UtilityScore::new(0.5) + UtilityScore::HALF } } @@ -123,7 +127,7 @@ impl Altruistic { if self.should_be_altruistic(hunger_level) { UtilityScore::new(self.strength) } else { - UtilityScore::new(0.0) + UtilityScore::ZERO } } } @@ -142,11 +146,11 @@ pub struct ContextAwareUtilities { impl Default for ContextAwareUtilities { fn default() -> Self { Self { - resource_competition: UtilityScore::new(0.5), - stress_retreat: UtilityScore::new(0.5), - competitive_behavior: UtilityScore::new(0.5), - cooperation: UtilityScore::new(0.5), - collective_influence: UtilityScore::new(0.0), + resource_competition: UtilityScore::HALF, + stress_retreat: UtilityScore::HALF, + competitive_behavior: UtilityScore::HALF, + cooperation: UtilityScore::HALF, + collective_influence: UtilityScore::ZERO, } } } @@ -190,8 +194,6 @@ pub fn update_collective_influence( relations_query: Query<&SocialRelation>, positions_query: Query<&Transform, Without>, ) { - const MAX_INFLUENCE_DISTANCE: f32 = 100.0; // Universal influence range - for (entity, transform, mut utilities) in &mut utilities_query { let mut total_collective_influence = 0.0; let position = transform.translation.truncate(); diff --git a/crates/systems/ai/src/relationships/social.rs b/crates/systems/ai/src/relationships/social.rs index 744f970..475563b 100644 --- a/crates/systems/ai/src/relationships/social.rs +++ b/crates/systems/ai/src/relationships/social.rs @@ -1,3 +1,4 @@ +use crate::core::utility::UtilityScore; use crate::prelude::*; use bevy::prelude::*; use std::collections::HashMap; @@ -11,7 +12,7 @@ pub struct RelationshipStrength(f32); impl RelationshipStrength { pub fn new(value: f32) -> Self { - Self(value.clamp(0.0, 1.0)) + Self(UtilityScore::clamp_trait_value(value)) } pub fn value(&self) -> f32 { @@ -25,7 +26,7 @@ impl RelationshipStrength { /// Modify relationship by amount (can be positive or negative) pub fn adjust(&mut self, amount: f32) { - self.0 = (self.0 + amount).clamp(0.0, 1.0); + self.0 = UtilityScore::clamp_trait_value(self.0 + amount); } } @@ -179,7 +180,7 @@ pub fn social_behavior_utility(relationships: &SocialNetwork) -> UtilityScore { if count > 0 { UtilityScore::new(total_utility / count as f32) } else { - UtilityScore::new(0.0) + UtilityScore::ZERO } } diff --git a/crates/systems/ai/src/trackers/needs_tracker.rs b/crates/systems/ai/src/trackers/needs_tracker.rs index a5ac4da..c6d8900 100644 --- a/crates/systems/ai/src/trackers/needs_tracker.rs +++ b/crates/systems/ai/src/trackers/needs_tracker.rs @@ -41,7 +41,7 @@ impl AIModule for NeedsTracker { // Find most urgent need let mut most_urgent = None; - let mut highest_urgency = UtilityScore::new(0.0); + let mut highest_urgency = UtilityScore::ZERO; for need in &self.needs { let urgency = need.urgency(); @@ -58,6 +58,6 @@ impl AIModule for NeedsTracker { // Return the urgency of the most urgent need, or zero if no needs self.most_urgent_need .map(|(_, urgency)| urgency) - .unwrap_or(UtilityScore::new(0.0)) + .unwrap_or(UtilityScore::ZERO) } } From c3b9a3aa5d1a14d72b7f6ee0e91ac4e56496fbaa Mon Sep 17 00:00:00 2001 From: M1thieu Date: Sun, 21 Sep 2025 12:01:31 +0200 Subject: [PATCH 7/8] Small Codebase Polish --- crates/energy/src/conservation.rs | 12 ++++++++++++ crates/energy/src/thermodynamics/thermal.rs | 12 ++++++++++-- crates/forces/src/core/gravity.rs | 5 +++-- crates/forces/src/core/newton_laws.rs | 8 ++++++++ crates/forces/src/lib.rs | 10 +++------- crates/systems/ai/src/lib.rs | 6 ++---- crates/systems/src/lib.rs | 6 ++---- 7 files changed, 40 insertions(+), 19 deletions(-) diff --git a/crates/energy/src/conservation.rs b/crates/energy/src/conservation.rs index 6b4ed79..5c76e42 100644 --- a/crates/energy/src/conservation.rs +++ b/crates/energy/src/conservation.rs @@ -26,6 +26,18 @@ pub struct EnergyQuantity { impl EnergyQuantity { /// Create a new energy quantity pub fn new(value: f32, energy_type: EnergyType, max_capacity: Option) -> Self { + debug_assert!( + value >= 0.0, + "Energy cannot be negative (violates conservation)" + ); + debug_assert!( + value < 1e20, + "Energy exceeds realistic bounds (nuclear scale ~1e20 J)" + ); + if let Some(max) = max_capacity { + debug_assert!(max > 0.0, "Energy capacity must be positive"); + } + let clamped_value = max_capacity.map(|max| value.min(max)).unwrap_or(value); Self { diff --git a/crates/energy/src/thermodynamics/thermal.rs b/crates/energy/src/thermodynamics/thermal.rs index c28e810..46aa6aa 100644 --- a/crates/energy/src/thermodynamics/thermal.rs +++ b/crates/energy/src/thermodynamics/thermal.rs @@ -1,7 +1,7 @@ use bevy::prelude::*; -/// Stefan-Boltzmann constant (W/(m²·K⁴)) -pub const STEFAN_BOLTZMANN: f32 = 5.67e-8; +// Physical constants +pub const STEFAN_BOLTZMANN: f32 = 5.67e-8; // W/(m²·K⁴) /// Temperature component for thermal systems #[derive(Component, Debug, Clone, Copy, Reflect, Default)] @@ -12,6 +12,14 @@ pub struct Temperature { impl Temperature { pub fn new(kelvin: f32) -> Self { + debug_assert!( + kelvin >= 0.0, + "Temperature below absolute zero violates thermodynamics" + ); + debug_assert!( + kelvin < 1e8, + "Temperature exceeds realistic stellar core bounds (~1e8 K)" + ); Self { value: kelvin.max(0.0), } diff --git a/crates/forces/src/core/gravity.rs b/crates/forces/src/core/gravity.rs index 93cccc6..282cb94 100644 --- a/crates/forces/src/core/gravity.rs +++ b/crates/forces/src/core/gravity.rs @@ -1,7 +1,7 @@ use super::newton_laws::{AppliedForce, Mass}; use bevy::prelude::*; -/// Modified gravitational constant for simulation scale +// Simulation constants pub const GRAVITATIONAL_CONSTANT: f32 = 0.1; /// Resource for gravity simulation parameters @@ -47,10 +47,11 @@ pub struct GravitySource; #[derive(Component, Debug, Clone, Copy, Reflect)] pub struct MassiveBody; -// Spatial partitioning structures for Barnes-Hut algorithm +// Barnes-Hut spatial partitioning mod spatial { use bevy::prelude::*; + // Algorithm parameters const MAX_DEPTH: usize = 8; const MAX_BODIES_PER_NODE: usize = 8; diff --git a/crates/forces/src/core/newton_laws.rs b/crates/forces/src/core/newton_laws.rs index 97d798b..4b9b5b7 100644 --- a/crates/forces/src/core/newton_laws.rs +++ b/crates/forces/src/core/newton_laws.rs @@ -46,6 +46,14 @@ pub struct Mass { impl Mass { pub fn new(value: f32) -> Self { + debug_assert!( + value > 0.0, + "Mass cannot be negative or zero in real physics" + ); + debug_assert!( + value < 1e30, + "Mass exceeds realistic bounds (solar mass ~2e30 kg)" + ); Self { value: value.max(0.001), // Prevent zero or negative mass is_infinite: false, diff --git a/crates/forces/src/lib.rs b/crates/forces/src/lib.rs index 4ca4c3a..4ef14c0 100644 --- a/crates/forces/src/lib.rs +++ b/crates/forces/src/lib.rs @@ -3,19 +3,17 @@ pub mod core; use bevy::prelude::*; pub use core::newton_laws::NewtonLawsPlugin; -/// Force application interface for physical forces +/// Interface for applying forces to entities pub trait ForceApplicator: Send + Sync { /// Apply a force to an entity fn apply_force(&self, entity: Entity, force: Vec3); - /// Get the force magnitude fn get_magnitude(&self) -> f32; - /// Get the force direction fn get_direction(&self) -> Vec3; } -/// Main plugin for all force-related systems +/// Forces domain plugin #[derive(Default)] pub struct ForcesPlugin; @@ -33,9 +31,7 @@ impl Plugin for ForcesPlugin { } } -/// The forces prelude. -/// -/// This includes the most common types in this crate, re-exported for your convenience. +/// Common forces types and functions pub mod prelude { // Core interfaces from crate root pub use crate::{ForceApplicator, ForcesPlugin}; diff --git a/crates/systems/ai/src/lib.rs b/crates/systems/ai/src/lib.rs index 202ed1b..b83859f 100644 --- a/crates/systems/ai/src/lib.rs +++ b/crates/systems/ai/src/lib.rs @@ -5,12 +5,10 @@ pub mod personality; pub mod relationships; pub mod trackers; -/// Main AI plugin that provides the complete AI system +/// Core AI plugin with utility-based decision making pub use crate::core::LPAIPlugin; -/// The AI prelude. -/// -/// This includes the most common types in this crate, re-exported for your convenience. +/// Common AI types and plugins pub mod prelude { // Main plugins for easy access pub use crate::LPAIPlugin; diff --git a/crates/systems/src/lib.rs b/crates/systems/src/lib.rs index 8d40daa..8f60899 100644 --- a/crates/systems/src/lib.rs +++ b/crates/systems/src/lib.rs @@ -5,7 +5,7 @@ pub use save_system; use bevy::prelude::*; -/// Main plugin for all systems-related functionality +/// Systems domain plugin #[derive(Default)] pub struct SystemsPlugin; @@ -20,9 +20,7 @@ impl Plugin for SystemsPlugin { } } -/// The systems prelude. -/// -/// This includes all system plugins for easy importing. +/// Common systems plugins pub mod prelude { pub use super::SystemsPlugin; From bcfc58623a5e29ca35a2a65bf836b5c24be0d86c Mon Sep 17 00:00:00 2001 From: M1thieu Date: Sun, 21 Sep 2025 12:19:31 +0200 Subject: [PATCH 8/8] CI Typo Forgot to make a cargo check workspace though --- .github/workflows/ci.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 4ef6456..f243f63 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -36,9 +36,12 @@ jobs: - name: Install system dependencies run: sudo apt-get update && sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev clang lld + - name: Quick check + run: cargo check --workspace + - name: Run tests run: cargo test --workspace - + - name: Check documentation run: cargo doc --workspace --no-deps