character-traits-warrior-capability 0.1.0

A Rust crate for modeling and simulating a broad spectrum of warrior capabilities, aimed at enhancing intricate game mechanics and strategic simulations.
Documentation
// ---------------- [ File: character-traits-warrior-capability/src/warrior_profile_sampler.rs ]
crate::ix!();

#[derive(Debug, Clone, Builder, Getters)]
#[builder(pattern = "owned", setter(into, strip_option), default)]
#[getset(get = "pub")]
pub struct WarriorProfileSampler {
    /// Soft‑max temperature. Lower values sharpen distribution.
    temperature:   f64,

    /// Explicit minimum similarity for inclusion.
    /// Variants below this similarity are excluded from sampling.
    min_similarity: f64,
}

impl Default for WarriorProfileSampler {
    fn default() -> Self {
        Self {
            temperature:   0.25,
            min_similarity: 0.75,
        }
    }
}

impl WarriorProfileSampler {

    /// Sample **one** `WarriorCapabilityProfile` from the probability space
    /// defined by `space_ratings`.
    ///
    /// Leaf enums **must** implement `CapabilityVariant`; otherwise this
    /// will not compile.  (Add those impls alongside each enum.)
    pub fn sample_profile<R>(
        &self,
        space_ratings: &WarriorCapabilityIntrinsicDimensionRatings,
        rng: &mut R,
    ) -> WarriorCapabilityProfile
    where
        R: rand::Rng + ?Sized,
    {
        trace!("sampling profile from intrinsic‑dimension space");

        WarriorCapabilityProfileBuilder::default()
            .bravery(self.sample_set::<WarriorBraveryCapability, R>(space_ratings, rng))
            .leadership(self.sample_set::<WarriorLeadershipCapability, R>(space_ratings, rng))
            .strategy(self.sample_set::<WarriorStrategicCapability, R>(space_ratings, rng))
            .protection(self.sample_set::<WarriorProtectiveCapability, R>(space_ratings, rng))
            .physical(self.sample_set::<WarriorPhysicalCapability, R>(space_ratings, rng))
            .resilience(self.sample_set::<WarriorResilienceCapability, R>(space_ratings, rng))
            .responsiveness(self.sample_set::<WarriorResponsivenessCapability, R>(space_ratings, rng))
            .exploration(self.sample_set::<WarriorExplorationCapability, R>(space_ratings, rng))
            .threat_adaptation(self.sample_set::<WarriorThreatAdaptationCapability, R>(space_ratings, rng))
            .conflict_mitigation(self.sample_set::<WarriorConflictMitigationCapability, R>(space_ratings, rng))
            .approach(self.sample_set::<WarriorApproachCapability, R>(space_ratings, rng))
            .embodiment(self.sample_set::<WarriorEmbodimentCapability, R>(space_ratings, rng))
            .meta(self.sample_set::<WarriorMetaCapability, R>(space_ratings, rng))
            .offensive_magic(self.sample_set::<WarriorOffensiveMagicCapability, R>(space_ratings, rng))
            .defensive_magic(self.sample_set::<WarriorDefensiveMagicCapability, R>(space_ratings, rng))
            .illusionary_magic(self.sample_set::<WarriorIllusionaryMagicCapability, R>(space_ratings, rng))
            .cyber_defense(self.sample_set::<WarriorCyberDefenseCapability, R>(space_ratings, rng))
            .tech_adaptation(self.sample_set::<WarriorTechAdaptationCapability, R>(space_ratings, rng))
            .build()
            .unwrap()
    }

    /// Fast helper for 115‑dimensional dot product.
    #[inline(always)]
    fn dot(a: &[f64], b: &[f64]) -> f64 {
        debug_assert_eq!(a.len(), b.len());
        a.iter().zip(b).map(|(x, y)| x * y).sum()
    }

    #[instrument(level = "trace", skip(self, space_ratings, rng))]
    fn sample_set<V, R>(
        &self,
        space_ratings: &WarriorCapabilityIntrinsicDimensionRatings,
        rng: &mut R,
    ) -> HashSet<V>
    where
        V: CapabilityVariant + Clone + Eq + std::hash::Hash + std::fmt::Debug + 'static,
        R: rand::Rng + ?Sized,
    {
        let variants = V::all_variants();
        let space_vec = space_ratings.to_vec();

        // Compute raw dot-product similarities.
        let sims: Vec<(V, f64)> = variants
            .into_iter()
            .map(|v| {
                let sim = Self::dot(&space_vec, &v.intrinsic_ratings().to_vec());
                (v.clone(), sim)
            })
        .collect();

        // Identify max similarity for logging and baseline shifting.
        let max_sim = sims.iter().map(|(_, sim)| sim).fold(f64::MIN, |a, &b| a.max(b));

        // Apply baseline subtraction and gating.
        let weights: Vec<f64> = sims
            .iter()
            .map(|(_, sim)| {
                if *sim <= 0.0 || *sim < self.min_similarity {
                    0.0
                } else {
                    // Subtract 1 from exponentiation (baseline removal).
                    (sim / self.temperature).exp() - 1.0
                }
            })
        .collect();

        // Filter out variants with zero weight.
        let valid_variants: Vec<(V, f64)> = sims
            .into_iter()
            .zip(weights.into_iter())
            .filter(|(_, weight)| *weight > 0.0)
            .map(|((variant, _), weight)| (variant, weight))
            .collect();

        // No valid variants after filtering.
        if valid_variants.is_empty() {
            trace!(
                category = std::any::type_name::<V>(),
                max_similarity = max_sim,
                "category left empty (no variants with sufficient similarity)"
            );
            return HashSet::new();
        }

        // Re-extract filtered weights for WeightedIndex.
        let filtered_weights: Vec<f64> = valid_variants.iter().map(|(_, w)| *w).collect();

        // Sample using WeightedIndex.
        let dist = WeightedIndex::new(&filtered_weights).expect("weights positive & finite");
        let choice = valid_variants[dist.sample(rng)].0.clone();

        trace!(
            category = std::any::type_name::<V>(),
            ?choice,
            max_similarity = max_sim,
            "selected capability variant"
        );

        HashSet::from([choice])
    }
}