use crate::context::EcologicalContext;
use crate::entity::{AffectiveState, PhysiologicalState};
use crate::enums::{
ContextPath, DispositionPath, Emotion, HexacoPath, LifeStage, MentalHealthPath, MoodPath,
NeedsPath, PersonCharacteristicsPath, SocialCognitionPath, Species, StatePath,
};
use crate::memory::{EmotionalSnapshot, MemoryEntry, MemoryLayer, MemoryLayers, MemoryTag};
use crate::state::{EntityModelConfig, IndividualState};
use crate::types::{Alert, Duration, EntityId, MicrosystemId, RelationshipSlot, Timestamp};
use std::collections::HashMap;
pub const MAX_RELATIONSHIP_SLOTS: usize = 8;
#[derive(Debug, Clone, PartialEq)]
pub struct Entity {
id: EntityId,
species: Species,
age: Duration,
birth_date: Option<Timestamp>,
life_stage: LifeStage,
individual_state: IndividualState,
relationship_slots: [RelationshipSlot; MAX_RELATIONSHIP_SLOTS],
memories: MemoryLayers,
context: EcologicalContext,
pending_alerts: Vec<Alert>,
config: EntityModelConfig,
}
impl Entity {
pub(crate) fn new(
id: EntityId,
species: Species,
age: Duration,
birth_date: Option<Timestamp>,
life_stage: LifeStage,
individual_state: IndividualState,
) -> Self {
let config = EntityModelConfig::for_species(&species);
Entity {
id,
species,
age,
birth_date,
life_stage,
individual_state,
relationship_slots: Default::default(),
memories: MemoryLayers::new(),
context: EcologicalContext::default(),
pending_alerts: Vec::new(),
config,
}
}
pub(crate) fn new_with_context(
id: EntityId,
species: Species,
age: Duration,
birth_date: Option<Timestamp>,
life_stage: LifeStage,
individual_state: IndividualState,
context: EcologicalContext,
) -> Self {
let config = EntityModelConfig::for_species(&species);
Entity {
id,
species,
age,
birth_date,
life_stage,
individual_state,
relationship_slots: Default::default(),
memories: MemoryLayers::new(),
context,
pending_alerts: Vec::new(),
config,
}
}
#[must_use]
pub fn id(&self) -> &EntityId {
&self.id
}
#[must_use]
pub fn species(&self) -> &Species {
&self.species
}
#[must_use]
pub fn age(&self) -> Duration {
self.age
}
#[must_use]
pub fn birth_date(&self) -> Option<Timestamp> {
self.birth_date
}
#[must_use]
pub fn life_stage(&self) -> LifeStage {
self.life_stage
}
#[must_use]
pub fn individual_state(&self) -> &IndividualState {
&self.individual_state
}
pub fn individual_state_mut(&mut self) -> &mut IndividualState {
&mut self.individual_state
}
#[must_use]
pub fn get_effective(&self, path: StatePath) -> Option<f64> {
let value = match path {
StatePath::Hexaco(p) => self.get_hexaco_effective(p),
StatePath::Mood(p) => self.get_mood_effective(p),
StatePath::Needs(p) => self.get_needs_effective(p),
StatePath::SocialCognition(p) => self.get_social_cognition_effective(p),
StatePath::MentalHealth(p) => self.get_mental_health_effective(p),
StatePath::Disposition(p) => self.get_disposition_effective(p),
StatePath::PersonCharacteristics(p) => self.get_person_characteristics_effective(p),
};
Some(f64::from(value))
}
#[must_use]
pub fn get_base(&self, path: StatePath) -> Option<f64> {
let value = match path {
StatePath::Hexaco(p) => Some(self.get_hexaco_base(p)),
StatePath::Mood(p) => Some(self.get_mood_base(p)),
StatePath::Needs(p) => Some(self.get_needs_base(p)),
StatePath::SocialCognition(p) => Some(self.get_social_cognition_base(p)),
StatePath::MentalHealth(p) => self.get_mental_health_base(p),
StatePath::Disposition(p) => Some(self.get_disposition_base(p)),
StatePath::PersonCharacteristics(p) => self.get_person_characteristics_base(p),
};
value.map(f64::from)
}
#[must_use]
pub fn get_delta(&self, path: StatePath) -> Option<f64> {
let value = match path {
StatePath::Hexaco(_) => None, StatePath::Mood(p) => Some(self.get_mood_delta(p)),
StatePath::Needs(p) => Some(self.get_needs_delta(p)),
StatePath::SocialCognition(p) => Some(self.get_social_cognition_delta(p)),
StatePath::MentalHealth(p) => self.get_mental_health_delta(p),
StatePath::Disposition(p) => Some(self.get_disposition_delta(p)),
StatePath::PersonCharacteristics(p) => self.get_person_characteristics_delta(p),
};
value.map(f64::from)
}
#[must_use]
pub fn query_affective_state(&self) -> AffectiveState {
let state = self.individual_state();
AffectiveState {
valence: state.mood().valence_effective(),
arousal: state.mood().arousal_effective(),
dominance: state.mood().dominance_effective(),
}
}
#[must_use]
pub fn emotion_membership(&self) -> HashMap<Emotion, f64> {
self.individual_state().mood().emotion_membership()
}
#[must_use]
pub fn get_baseline_affect(&self) -> AffectiveState {
let state = self.individual_state();
AffectiveState {
valence: state.mood().valence_base(),
arousal: state.mood().arousal_base(),
dominance: state.mood().dominance_base(),
}
}
#[must_use]
pub fn query_physiological_state(&self) -> PhysiologicalState {
let state = self.individual_state();
PhysiologicalState {
fatigue: state.needs().fatigue_effective(),
stress: state.needs().stress_effective(),
}
}
#[must_use]
pub fn get_baseline_physiological(&self) -> PhysiologicalState {
let state = self.individual_state();
PhysiologicalState {
fatigue: state.needs().fatigue_base(),
stress: state.needs().stress_base(),
}
}
#[must_use]
pub fn relationship_slots(&self) -> &[RelationshipSlot; MAX_RELATIONSHIP_SLOTS] {
&self.relationship_slots
}
#[allow(dead_code)]
pub(crate) fn relationship_slots_mut(
&mut self,
) -> &mut [RelationshipSlot; MAX_RELATIONSHIP_SLOTS] {
&mut self.relationship_slots
}
#[must_use]
pub fn context(&self) -> &EcologicalContext {
&self.context
}
pub fn context_mut(&mut self) -> &mut EcologicalContext {
&mut self.context
}
#[must_use]
pub fn config(&self) -> &EntityModelConfig {
&self.config
}
pub fn config_mut(&mut self) -> &mut EntityModelConfig {
&mut self.config
}
#[must_use]
pub fn get_context(&self, path: &ContextPath) -> Option<f64> {
self.context.get(path)
}
pub fn set_context(&mut self, path: &ContextPath, value: f64) -> bool {
self.context.set(path, value)
}
#[must_use]
pub fn memories(&self) -> &MemoryLayers {
&self.memories
}
pub fn memories_mut(&mut self) -> &mut MemoryLayers {
&mut self.memories
}
#[must_use]
pub fn mood_snapshot(&self) -> EmotionalSnapshot {
EmotionalSnapshot::from_mood(self.individual_state.mood())
}
pub fn create_memory(
&mut self,
summary: impl Into<String>,
participants: Vec<EntityId>,
tags: Vec<MemoryTag>,
salience: f32,
microsystem_context: Option<MicrosystemId>,
) -> MemoryEntry {
let layer = Self::layer_for_salience(salience);
self.create_memory_in_layer(
layer,
summary,
participants,
tags,
salience,
microsystem_context,
)
}
pub fn create_memory_in_layer(
&mut self,
layer: MemoryLayer,
summary: impl Into<String>,
participants: Vec<EntityId>,
tags: Vec<MemoryTag>,
salience: f32,
microsystem_context: Option<MicrosystemId>,
) -> MemoryEntry {
let snapshot = self.mood_snapshot();
let mut entry = MemoryEntry::new(self.age, summary)
.with_participants(participants)
.with_tags(tags)
.with_salience(salience)
.with_emotional_snapshot(snapshot);
if let Some(context) = microsystem_context {
entry = entry.with_microsystem_context(context);
}
let result = entry.clone();
self.memories.add(layer, entry);
result
}
#[must_use]
fn layer_for_salience(salience: f32) -> MemoryLayer {
if salience < 0.3 {
MemoryLayer::Immediate
} else if salience < 0.5 {
MemoryLayer::ShortTerm
} else if salience < 0.7 {
MemoryLayer::LongTerm
} else {
MemoryLayer::Legacy
}
}
#[must_use]
pub fn recall_mood_congruent(&self, min_congruence: f32) -> Vec<&MemoryEntry> {
self.memories
.retrieve_mood_congruent(self.individual_state.mood(), min_congruence)
}
#[must_use]
pub fn alerts(&self) -> &[Alert] {
&self.pending_alerts
}
pub fn push_alert(&mut self, alert: Alert) {
self.pending_alerts.push(alert);
}
pub fn clear_alerts(&mut self) {
self.pending_alerts.clear();
}
#[must_use]
pub fn take_alerts(&mut self) -> Vec<Alert> {
std::mem::take(&mut self.pending_alerts)
}
fn get_hexaco_effective(&self, path: HexacoPath) -> f32 {
let h = self.individual_state.hexaco();
match path {
HexacoPath::Openness => h.openness(),
HexacoPath::Conscientiousness => h.conscientiousness(),
HexacoPath::Extraversion => h.extraversion(),
HexacoPath::Agreeableness => h.agreeableness(),
HexacoPath::Neuroticism => h.neuroticism(),
HexacoPath::HonestyHumility => h.honesty_humility(),
}
}
fn get_hexaco_base(&self, path: HexacoPath) -> f32 {
self.get_hexaco_effective(path)
}
fn get_mood_effective(&self, path: MoodPath) -> f32 {
let m = self.individual_state.mood();
match path {
MoodPath::Valence => m.valence_effective(),
MoodPath::Arousal => m.arousal_effective(),
MoodPath::Dominance => m.dominance_effective(),
}
}
fn get_mood_base(&self, path: MoodPath) -> f32 {
let m = self.individual_state.mood();
match path {
MoodPath::Valence => m.valence_base(),
MoodPath::Arousal => m.arousal_base(),
MoodPath::Dominance => m.dominance_base(),
}
}
fn get_mood_delta(&self, path: MoodPath) -> f32 {
let m = self.individual_state.mood();
match path {
MoodPath::Valence => m.valence_delta(),
MoodPath::Arousal => m.arousal_delta(),
MoodPath::Dominance => m.dominance_delta(),
}
}
fn get_needs_effective(&self, path: NeedsPath) -> f32 {
let n = self.individual_state.needs();
match path {
NeedsPath::Fatigue => n.fatigue_effective(),
NeedsPath::Stress => n.stress_effective(),
NeedsPath::Purpose => n.purpose_effective(),
}
}
fn get_needs_base(&self, path: NeedsPath) -> f32 {
let n = self.individual_state.needs();
match path {
NeedsPath::Fatigue => n.fatigue_base(),
NeedsPath::Stress => n.stress_base(),
NeedsPath::Purpose => n.purpose_base(),
}
}
fn get_needs_delta(&self, path: NeedsPath) -> f32 {
let n = self.individual_state.needs();
match path {
NeedsPath::Fatigue => n.fatigue().delta(),
NeedsPath::Stress => n.stress().delta(),
NeedsPath::Purpose => n.purpose().delta(),
}
}
fn get_social_cognition_effective(&self, path: SocialCognitionPath) -> f32 {
let social = self.individual_state.social_cognition();
match path {
SocialCognitionPath::Loneliness => social.loneliness_effective(),
SocialCognitionPath::PerceivedReciprocalCaring => {
social.perceived_reciprocal_caring_effective()
}
SocialCognitionPath::PerceivedLiability => social.perceived_liability_effective(),
SocialCognitionPath::SelfHate => social.self_hate_effective(),
SocialCognitionPath::PerceivedCompetence => social.perceived_competence_effective(),
}
}
fn get_social_cognition_base(&self, path: SocialCognitionPath) -> f32 {
let social = self.individual_state.social_cognition();
match path {
SocialCognitionPath::Loneliness => social.loneliness_base(),
SocialCognitionPath::PerceivedReciprocalCaring => {
social.perceived_reciprocal_caring_base()
}
SocialCognitionPath::PerceivedLiability => social.perceived_liability_base(),
SocialCognitionPath::SelfHate => social.self_hate_base(),
SocialCognitionPath::PerceivedCompetence => social.perceived_competence_base(),
}
}
fn get_social_cognition_delta(&self, path: SocialCognitionPath) -> f32 {
let social = self.individual_state.social_cognition();
match path {
SocialCognitionPath::Loneliness => social.loneliness().delta(),
SocialCognitionPath::PerceivedReciprocalCaring => {
social.perceived_reciprocal_caring().delta()
}
SocialCognitionPath::PerceivedLiability => social.perceived_liability().delta(),
SocialCognitionPath::SelfHate => social.self_hate().delta(),
SocialCognitionPath::PerceivedCompetence => social.perceived_competence().delta(),
}
}
fn get_mental_health_effective(&self, path: MentalHealthPath) -> f32 {
let mh = self.individual_state.mental_health();
let social = self.individual_state.social_cognition();
match path {
MentalHealthPath::Depression => mh.depression_effective(),
MentalHealthPath::SelfWorth => mh.self_worth_effective(),
MentalHealthPath::Hopelessness => mh.hopelessness_effective(),
MentalHealthPath::InterpersonalHopelessness => {
mh.interpersonal_hopelessness_effective()
}
MentalHealthPath::AcquiredCapability => mh.acquired_capability_effective(),
MentalHealthPath::ThwartedBelongingness => mh.compute_thwarted_belongingness(social),
MentalHealthPath::PerceivedBurdensomeness => {
mh.compute_perceived_burdensomeness(social)
}
MentalHealthPath::SuicidalDesire => mh.compute_suicidal_desire(social),
MentalHealthPath::AttemptRisk => mh.compute_attempt_risk(social),
}
}
fn get_mental_health_base(&self, path: MentalHealthPath) -> Option<f32> {
let mh = self.individual_state.mental_health();
match path {
MentalHealthPath::Depression => Some(mh.depression().base()),
MentalHealthPath::SelfWorth => Some(mh.self_worth().base()),
MentalHealthPath::Hopelessness => Some(mh.hopelessness().base()),
MentalHealthPath::InterpersonalHopelessness => {
Some(mh.interpersonal_hopelessness().base())
}
MentalHealthPath::AcquiredCapability => Some(mh.acquired_capability().base()),
MentalHealthPath::ThwartedBelongingness
| MentalHealthPath::PerceivedBurdensomeness
| MentalHealthPath::SuicidalDesire
| MentalHealthPath::AttemptRisk => None,
}
}
fn get_mental_health_delta(&self, path: MentalHealthPath) -> Option<f32> {
let mh = self.individual_state.mental_health();
match path {
MentalHealthPath::Depression => Some(mh.depression().delta()),
MentalHealthPath::SelfWorth => Some(mh.self_worth().delta()),
MentalHealthPath::Hopelessness => Some(mh.hopelessness().delta()),
MentalHealthPath::InterpersonalHopelessness => {
Some(mh.interpersonal_hopelessness().delta())
}
MentalHealthPath::AcquiredCapability => Some(mh.acquired_capability().delta()),
MentalHealthPath::ThwartedBelongingness
| MentalHealthPath::PerceivedBurdensomeness
| MentalHealthPath::SuicidalDesire
| MentalHealthPath::AttemptRisk => None,
}
}
fn get_disposition_effective(&self, path: DispositionPath) -> f32 {
let d = self.individual_state.disposition();
match path {
DispositionPath::ImpulseControl => d.impulse_control_effective(),
DispositionPath::Empathy => d.empathy_effective(),
DispositionPath::Aggression => d.aggression_effective(),
DispositionPath::Grievance => d.grievance_effective(),
DispositionPath::Reactance => d.reactance_effective(),
DispositionPath::TrustorPropensity => d.trustor_propensity_effective(),
}
}
fn get_disposition_base(&self, path: DispositionPath) -> f32 {
let d = self.individual_state.disposition();
match path {
DispositionPath::ImpulseControl => d.impulse_control().base(),
DispositionPath::Empathy => d.empathy().base(),
DispositionPath::Aggression => d.aggression().base(),
DispositionPath::Grievance => d.grievance().base(),
DispositionPath::Reactance => d.reactance().base(),
DispositionPath::TrustorPropensity => d.trustor_propensity().base(),
}
}
fn get_disposition_delta(&self, path: DispositionPath) -> f32 {
let d = self.individual_state.disposition();
match path {
DispositionPath::ImpulseControl => d.impulse_control().delta(),
DispositionPath::Empathy => d.empathy().delta(),
DispositionPath::Aggression => d.aggression().delta(),
DispositionPath::Grievance => d.grievance().delta(),
DispositionPath::Reactance => d.reactance().delta(),
DispositionPath::TrustorPropensity => d.trustor_propensity().delta(),
}
}
fn get_person_characteristics_effective(&self, path: PersonCharacteristicsPath) -> f32 {
let pc = self.individual_state.person_characteristics();
match path {
PersonCharacteristicsPath::CognitiveAbility => pc.cognitive_ability_effective(),
PersonCharacteristicsPath::EmotionalRegulationAssets => {
pc.emotional_regulation_assets_effective()
}
PersonCharacteristicsPath::SocialCapital => pc.social_capital_effective(),
PersonCharacteristicsPath::MaterialSecurity => pc.material_security_effective(),
PersonCharacteristicsPath::ExperienceDiversity => pc.experience_diversity_effective(),
PersonCharacteristicsPath::BaselineMotivation => pc.baseline_motivation_effective(),
PersonCharacteristicsPath::PersistenceTendency => pc.persistence_tendency_effective(),
PersonCharacteristicsPath::CuriosityTendency => pc.curiosity_tendency_effective(),
PersonCharacteristicsPath::Resource => pc.resource(),
PersonCharacteristicsPath::Force => pc.force(),
}
}
fn get_person_characteristics_base(&self, path: PersonCharacteristicsPath) -> Option<f32> {
let pc = self.individual_state.person_characteristics();
match path {
PersonCharacteristicsPath::CognitiveAbility => Some(pc.cognitive_ability().base()),
PersonCharacteristicsPath::EmotionalRegulationAssets => {
Some(pc.emotional_regulation_assets().base())
}
PersonCharacteristicsPath::SocialCapital => Some(pc.social_capital().base()),
PersonCharacteristicsPath::MaterialSecurity => Some(pc.material_security().base()),
PersonCharacteristicsPath::ExperienceDiversity => {
Some(pc.experience_diversity().base())
}
PersonCharacteristicsPath::BaselineMotivation => Some(pc.baseline_motivation().base()),
PersonCharacteristicsPath::PersistenceTendency => {
Some(pc.persistence_tendency().base())
}
PersonCharacteristicsPath::CuriosityTendency => Some(pc.curiosity_tendency().base()),
PersonCharacteristicsPath::Resource | PersonCharacteristicsPath::Force => None,
}
}
fn get_person_characteristics_delta(&self, path: PersonCharacteristicsPath) -> Option<f32> {
let pc = self.individual_state.person_characteristics();
match path {
PersonCharacteristicsPath::CognitiveAbility => Some(pc.cognitive_ability().delta()),
PersonCharacteristicsPath::EmotionalRegulationAssets => {
Some(pc.emotional_regulation_assets().delta())
}
PersonCharacteristicsPath::SocialCapital => Some(pc.social_capital().delta()),
PersonCharacteristicsPath::MaterialSecurity => Some(pc.material_security().delta()),
PersonCharacteristicsPath::ExperienceDiversity => {
Some(pc.experience_diversity().delta())
}
PersonCharacteristicsPath::BaselineMotivation => Some(pc.baseline_motivation().delta()),
PersonCharacteristicsPath::PersistenceTendency => {
Some(pc.persistence_tendency().delta())
}
PersonCharacteristicsPath::CuriosityTendency => Some(pc.curiosity_tendency().delta()),
PersonCharacteristicsPath::Resource | PersonCharacteristicsPath::Force => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::entity::EntityBuilder;
#[test]
fn human_entity_creation() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
assert_eq!(entity.species(), &Species::Human);
}
#[test]
fn entity_has_individual_state() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let state = entity.individual_state();
let _ = state.mood();
let _ = state.needs();
let _ = state.mental_health();
}
#[test]
fn entity_has_species() {
let entity = EntityBuilder::new().species(Species::Dog).age(crate::types::Duration::years(30)).build().unwrap();
assert_eq!(entity.species(), &Species::Dog);
}
#[test]
fn entity_has_life_stage() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.life_stage(LifeStage::Adult)
.build()
.unwrap();
assert_eq!(entity.life_stage(), LifeStage::Adult);
}
#[test]
fn entity_has_person_characteristics() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let pc = entity.individual_state().person_characteristics();
let _ = pc.resource();
let _ = pc.force();
}
#[test]
fn entity_emotion_membership_accessible() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let membership = entity.emotion_membership();
let total: f64 = membership.values().copied().sum();
assert!((total - 1.0).abs() < 1e-6);
}
#[test]
fn entity_age_query() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(Duration::years(25))
.build()
.unwrap();
assert_eq!(entity.age().as_years(), 25);
}
#[test]
fn entity_get_effective_mood_valence() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let valence = entity
.get_effective(StatePath::Mood(MoodPath::Valence))
.unwrap();
assert!(valence.abs() < 0.1); }
#[test]
fn entity_get_effective_mood_arousal() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let arousal = entity
.get_effective(StatePath::Mood(MoodPath::Arousal))
.unwrap();
assert!(arousal.abs() < 0.1); }
#[test]
fn entity_get_effective_mood_dominance() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let dominance = entity
.get_effective(StatePath::Mood(MoodPath::Dominance))
.unwrap();
assert!(dominance.abs() < 0.1); }
#[test]
fn entity_get_effective_needs_stress() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let stress = entity
.get_effective(StatePath::Needs(NeedsPath::Stress))
.unwrap();
assert!(stress >= 0.0 && stress <= 1.0);
}
#[test]
fn entity_get_effective_needs_fatigue() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let fatigue = entity
.get_effective(StatePath::Needs(NeedsPath::Fatigue))
.unwrap();
assert!(fatigue >= 0.0 && fatigue <= 1.0);
}
#[test]
fn entity_get_effective_mental_health_path() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let depression = entity
.get_effective(StatePath::MentalHealth(MentalHealthPath::Depression))
.unwrap();
assert!(depression >= 0.0 && depression <= 1.0);
}
#[test]
fn entity_get_effective_hexaco_path() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let openness = entity
.get_effective(StatePath::Hexaco(HexacoPath::Openness))
.unwrap();
assert!(openness >= -1.0 && openness <= 1.0);
}
#[test]
fn entity_get_effective_disposition_path() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let empathy = entity
.get_effective(StatePath::Disposition(DispositionPath::Empathy))
.unwrap();
assert!(empathy >= 0.0 && empathy <= 1.0);
}
#[test]
fn entity_get_effective_person_characteristics_path() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let cognitive = entity
.get_effective(StatePath::PersonCharacteristics(
PersonCharacteristicsPath::CognitiveAbility,
))
.unwrap();
assert!(cognitive >= 0.0 && cognitive <= 1.0);
}
#[test]
fn entity_get_base_returns_base_only() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.3);
let base = entity.get_base(StatePath::Mood(MoodPath::Valence)).unwrap();
let effective = entity
.get_effective(StatePath::Mood(MoodPath::Valence))
.unwrap();
assert!((effective - base - 0.3).abs() < 1e-6);
}
#[test]
fn entity_get_delta_returns_delta_only() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.5);
let delta = entity
.get_delta(StatePath::Mood(MoodPath::Valence))
.unwrap();
assert!((delta - 0.5).abs() < 1e-6);
}
#[test]
fn entity_get_computed_path_calls_compute() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let tb = entity
.get_effective(StatePath::MentalHealth(
MentalHealthPath::ThwartedBelongingness,
))
.unwrap();
assert!(tb >= 0.0 && tb <= 1.0);
}
#[test]
fn entity_get_base_on_computed_returns_none() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let base = entity.get_base(StatePath::MentalHealth(
MentalHealthPath::ThwartedBelongingness,
));
assert!(base.is_none());
}
#[test]
fn entity_has_relationship_slots() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let slots = entity.relationship_slots();
assert_eq!(slots.len(), MAX_RELATIONSHIP_SLOTS);
}
#[test]
fn entity_relationship_slot_empty_initially() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for slot in entity.relationship_slots() {
assert!(slot.is_empty());
}
}
#[test]
fn entity_relationship_slot_get_attached_none() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for slot in entity.relationship_slots() {
assert!(slot.get_attached().is_none());
}
}
#[test]
fn entity_clone() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(Duration::years(30))
.build()
.unwrap();
let cloned = entity.clone();
assert_eq!(entity.id(), cloned.id());
assert_eq!(entity.species(), cloned.species());
assert_eq!(entity.age(), cloned.age());
}
#[test]
fn entity_debug() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let debug = format!("{:?}", entity);
assert!(debug.contains("Entity"));
}
#[test]
fn entity_id() {
let entity = EntityBuilder::new()
.id("test_id")
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
assert_eq!(entity.id().as_str(), "test_id");
}
#[test]
fn entity_individual_state_mutable() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.3);
assert!((entity.individual_state().mood().valence_delta() - 0.3).abs() < f32::EPSILON);
}
#[test]
fn hexaco_has_no_delta() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let delta = entity.get_delta(StatePath::Hexaco(HexacoPath::Openness));
assert!(delta.is_none());
}
#[test]
fn all_needs_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for path in NeedsPath::all() {
let effective = entity.get_effective(StatePath::Needs(path)).unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::Needs(path));
assert!(base.is_some());
let delta = entity.get_delta(StatePath::Needs(path));
assert!(delta.is_some());
}
}
#[test]
fn all_disposition_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for path in DispositionPath::all() {
let effective = entity.get_effective(StatePath::Disposition(path)).unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::Disposition(path));
assert!(base.is_some());
let delta = entity.get_delta(StatePath::Disposition(path));
assert!(delta.is_some());
}
}
#[test]
fn all_mental_health_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for path in MentalHealthPath::all() {
let effective = entity.get_effective(StatePath::MentalHealth(path)).unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
if path.is_computed() {
assert!(entity.get_base(StatePath::MentalHealth(path)).is_none());
assert!(entity.get_delta(StatePath::MentalHealth(path)).is_none());
} else {
assert!(entity.get_base(StatePath::MentalHealth(path)).is_some());
assert!(entity.get_delta(StatePath::MentalHealth(path)).is_some());
}
}
}
#[test]
fn all_person_characteristics_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for path in PersonCharacteristicsPath::all() {
let effective = entity
.get_effective(StatePath::PersonCharacteristics(path))
.unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
if path.is_composite() {
assert!(entity
.get_base(StatePath::PersonCharacteristics(path))
.is_none());
assert!(entity
.get_delta(StatePath::PersonCharacteristics(path))
.is_none());
} else {
assert!(entity
.get_base(StatePath::PersonCharacteristics(path))
.is_some());
assert!(entity
.get_delta(StatePath::PersonCharacteristics(path))
.is_some());
}
}
}
#[test]
fn dog_entity_creation() {
let entity = EntityBuilder::new().species(Species::Dog).age(crate::types::Duration::years(30)).build().unwrap();
assert_eq!(entity.species(), &Species::Dog);
}
#[test]
fn cat_entity_creation() {
let entity = EntityBuilder::new().species(Species::Cat).age(crate::types::Duration::years(30)).build().unwrap();
assert_eq!(entity.species(), &Species::Cat);
}
#[test]
fn all_hexaco_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for path in HexacoPath::all() {
let effective = entity.get_effective(StatePath::Hexaco(path)).unwrap();
assert!(effective >= -1.0 && effective <= 1.0);
let base = entity.get_base(StatePath::Hexaco(path));
assert!(base.is_some());
let delta = entity.get_delta(StatePath::Hexaco(path));
assert!(delta.is_none());
}
}
#[test]
fn all_mood_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
for path in MoodPath::all() {
let effective = entity.get_effective(StatePath::Mood(path)).unwrap();
assert!(effective >= -1.0 && effective <= 1.0);
let base = entity.get_base(StatePath::Mood(path));
assert!(base.is_some());
let delta = entity.get_delta(StatePath::Mood(path));
assert!(delta.is_some());
}
}
#[test]
fn hexaco_paths_base_and_effective() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let openness = entity
.get_effective(StatePath::Hexaco(HexacoPath::Openness))
.unwrap();
let conscientiousness = entity
.get_effective(StatePath::Hexaco(HexacoPath::Conscientiousness))
.unwrap();
let extraversion = entity
.get_effective(StatePath::Hexaco(HexacoPath::Extraversion))
.unwrap();
let agreeableness = entity
.get_effective(StatePath::Hexaco(HexacoPath::Agreeableness))
.unwrap();
let neuroticism = entity
.get_effective(StatePath::Hexaco(HexacoPath::Neuroticism))
.unwrap();
let honesty = entity
.get_effective(StatePath::Hexaco(HexacoPath::HonestyHumility))
.unwrap();
for v in [
openness,
conscientiousness,
extraversion,
agreeableness,
neuroticism,
honesty,
] {
assert!(v >= -1.0 && v <= 1.0);
}
for path in HexacoPath::all() {
let base = entity.get_base(StatePath::Hexaco(path)).unwrap();
let effective = entity.get_effective(StatePath::Hexaco(path)).unwrap();
assert!((base - effective).abs() < 1e-6);
}
}
#[test]
fn mood_paths_base_delta_effective() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.2);
entity
.individual_state_mut()
.mood_mut()
.add_arousal_delta(0.3);
entity
.individual_state_mut()
.mood_mut()
.add_dominance_delta(0.4);
let valence_delta = entity
.get_delta(StatePath::Mood(MoodPath::Valence))
.unwrap();
assert!((valence_delta - 0.2).abs() < 1e-6);
let arousal_delta = entity
.get_delta(StatePath::Mood(MoodPath::Arousal))
.unwrap();
assert!((arousal_delta - 0.3).abs() < 1e-6);
let dominance_delta = entity
.get_delta(StatePath::Mood(MoodPath::Dominance))
.unwrap();
assert!((dominance_delta - 0.4).abs() < 1e-6);
let valence_base = entity.get_base(StatePath::Mood(MoodPath::Valence)).unwrap();
let arousal_base = entity.get_base(StatePath::Mood(MoodPath::Arousal)).unwrap();
let dominance_base = entity
.get_base(StatePath::Mood(MoodPath::Dominance))
.unwrap();
assert!(valence_base.abs() < 1e-6);
assert!(arousal_base.abs() < 1e-6);
assert!(dominance_base.abs() < 1e-6);
}
#[test]
fn needs_paths_base_delta_effective() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let paths = [NeedsPath::Fatigue, NeedsPath::Stress, NeedsPath::Purpose];
for path in paths {
let effective = entity.get_effective(StatePath::Needs(path)).unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::Needs(path)).unwrap();
assert!(base >= 0.0 && base <= 1.0);
let delta = entity.get_delta(StatePath::Needs(path)).unwrap();
assert!(delta.abs() < 1e-6); }
}
#[test]
fn social_cognition_paths_base_delta_effective() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let paths = [
SocialCognitionPath::Loneliness,
SocialCognitionPath::PerceivedReciprocalCaring,
SocialCognitionPath::PerceivedLiability,
SocialCognitionPath::SelfHate,
SocialCognitionPath::PerceivedCompetence,
];
for path in paths {
let effective = entity
.get_effective(StatePath::SocialCognition(path))
.unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::SocialCognition(path)).unwrap();
assert!(base >= 0.0 && base <= 1.0);
let delta = entity.get_delta(StatePath::SocialCognition(path)).unwrap();
assert!(delta.abs() < 1e-6);
}
}
#[test]
fn query_affective_state_returns_pure_pad_dimensions() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let affective = entity.query_affective_state();
assert!(affective.valence >= -1.0 && affective.valence <= 1.0);
assert!(affective.arousal >= -1.0 && affective.arousal <= 1.0);
assert!(affective.dominance >= -1.0 && affective.dominance <= 1.0);
}
#[test]
fn query_physiological_state_returns_fatigue_and_stress() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let physiological = entity.query_physiological_state();
assert!(physiological.fatigue >= 0.0 && physiological.fatigue <= 1.0);
assert!(physiological.stress >= 0.0 && physiological.stress <= 1.0);
}
#[test]
fn get_baseline_affect_returns_base_values_only() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
{
let state = entity.individual_state_mut();
state.mood_mut().valence_mut().set_base(0.1);
state.mood_mut().arousal_mut().set_base(0.2);
state.mood_mut().dominance_mut().set_base(-0.1);
state.mood_mut().add_valence_delta(0.5);
}
let baseline = entity.get_baseline_affect();
assert!((baseline.valence - 0.1).abs() < f32::EPSILON);
assert!((baseline.arousal - 0.2).abs() < f32::EPSILON);
assert!((baseline.dominance - (-0.1)).abs() < f32::EPSILON);
}
#[test]
fn get_baseline_physiological_returns_base_values_only() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
{
let state = entity.individual_state_mut();
state.needs_mut().fatigue_mut().set_base(0.3);
state.needs_mut().stress_mut().set_base(0.4);
state.needs_mut().add_stress_delta(0.2);
}
let baseline = entity.get_baseline_physiological();
assert!((baseline.fatigue - 0.3).abs() < f32::EPSILON);
assert!((baseline.stress - 0.4).abs() < f32::EPSILON);
}
#[test]
fn baseline_vs_effective_differ_when_deltas_present() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.4);
entity
.individual_state_mut()
.needs_mut()
.add_fatigue_delta(0.2);
let baseline = entity.get_baseline_affect();
let effective = entity.query_affective_state();
assert!((effective.valence - baseline.valence).abs() > f32::EPSILON);
let phys_baseline = entity.get_baseline_physiological();
let phys_effective = entity.query_physiological_state();
assert!((phys_effective.fatigue - phys_baseline.fatigue).abs() > f32::EPSILON);
}
#[test]
fn affective_state_is_pure_pad() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
{
let state = entity.individual_state_mut();
state.mood_mut().valence_mut().set_base(0.2);
state.mood_mut().arousal_mut().set_base(0.1);
state.mood_mut().dominance_mut().set_base(0.0);
state.mood_mut().add_valence_delta(0.1);
}
let affective = entity.query_affective_state();
assert!((affective.valence - 0.3).abs() < 0.001);
assert!((affective.arousal - 0.1).abs() < 0.001);
assert!((affective.dominance - 0.0).abs() < 0.001);
}
#[test]
fn physiological_state_separate_from_pad() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
{
let state = entity.individual_state_mut();
state.needs_mut().fatigue_mut().set_base(0.5);
state.needs_mut().stress_mut().set_base(0.4);
state.needs_mut().add_stress_delta(-0.2);
}
let physiological = entity.query_physiological_state();
assert!((physiological.fatigue - 0.5).abs() < 0.001);
assert!((physiological.stress - 0.2).abs() < 0.001);
}
#[test]
fn mental_health_all_stored_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let stored_paths = [
MentalHealthPath::Depression,
MentalHealthPath::SelfWorth,
MentalHealthPath::Hopelessness,
MentalHealthPath::InterpersonalHopelessness,
MentalHealthPath::AcquiredCapability,
];
for path in stored_paths {
let effective = entity.get_effective(StatePath::MentalHealth(path)).unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::MentalHealth(path));
assert!(base.is_some());
let delta = entity.get_delta(StatePath::MentalHealth(path));
assert!(delta.is_some());
}
}
#[test]
fn mental_health_all_computed_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let computed_paths = [
MentalHealthPath::ThwartedBelongingness,
MentalHealthPath::PerceivedBurdensomeness,
MentalHealthPath::SuicidalDesire,
MentalHealthPath::AttemptRisk,
];
for path in computed_paths {
let effective = entity.get_effective(StatePath::MentalHealth(path)).unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::MentalHealth(path));
assert!(base.is_none());
let delta = entity.get_delta(StatePath::MentalHealth(path));
assert!(delta.is_none());
}
}
#[test]
fn disposition_paths_base_delta_effective() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let paths = [
DispositionPath::ImpulseControl,
DispositionPath::Empathy,
DispositionPath::Aggression,
DispositionPath::Grievance,
DispositionPath::Reactance,
DispositionPath::TrustorPropensity,
];
for path in paths {
let effective = entity.get_effective(StatePath::Disposition(path)).unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::Disposition(path)).unwrap();
assert!(base >= 0.0 && base <= 1.0);
let delta = entity.get_delta(StatePath::Disposition(path)).unwrap();
let _ = delta;
}
}
#[test]
fn person_characteristics_all_stored_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let stored_paths = [
PersonCharacteristicsPath::CognitiveAbility,
PersonCharacteristicsPath::EmotionalRegulationAssets,
PersonCharacteristicsPath::SocialCapital,
PersonCharacteristicsPath::MaterialSecurity,
PersonCharacteristicsPath::ExperienceDiversity,
PersonCharacteristicsPath::BaselineMotivation,
PersonCharacteristicsPath::PersistenceTendency,
PersonCharacteristicsPath::CuriosityTendency,
];
for path in stored_paths {
let effective = entity
.get_effective(StatePath::PersonCharacteristics(path))
.unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::PersonCharacteristics(path));
assert!(base.is_some());
let delta = entity.get_delta(StatePath::PersonCharacteristics(path));
assert!(delta.is_some());
}
}
#[test]
fn person_characteristics_all_composite_paths() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let composite_paths = [
PersonCharacteristicsPath::Resource,
PersonCharacteristicsPath::Force,
];
for path in composite_paths {
let effective = entity
.get_effective(StatePath::PersonCharacteristics(path))
.unwrap();
assert!(effective >= 0.0 && effective <= 1.0);
let base = entity.get_base(StatePath::PersonCharacteristics(path));
assert!(base.is_none());
let delta = entity.get_delta(StatePath::PersonCharacteristics(path));
assert!(delta.is_none());
}
}
#[test]
fn relationship_slots_mut_internal() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let slots = entity.relationship_slots_mut();
assert_eq!(slots.len(), MAX_RELATIONSHIP_SLOTS);
assert!(slots[0].is_empty());
}
#[test]
fn entity_memories_empty_initially() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
assert!(entity.memories().is_empty());
assert_eq!(entity.memories().total_count(), 0);
}
#[test]
fn entity_memories_mut_add_to_layer() {
use crate::memory::{MemoryEntry, MemoryLayer};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let entry = MemoryEntry::new(Duration::days(10), "Test memory");
entity.memories_mut().add(MemoryLayer::Immediate, entry);
assert_eq!(entity.memories().immediate_count(), 1);
assert_eq!(entity.memories().total_count(), 1);
}
#[test]
fn entity_mood_snapshot_captures_current_mood() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.5);
entity
.individual_state_mut()
.mood_mut()
.add_arousal_delta(0.3);
entity
.individual_state_mut()
.mood_mut()
.add_dominance_delta(-0.2);
let snapshot = entity.mood_snapshot();
assert!((snapshot.valence() - 0.5).abs() < 0.01);
assert!((snapshot.arousal() - 0.3).abs() < 0.01);
assert!((snapshot.dominance() - (-0.2)).abs() < 0.01);
}
#[test]
fn entity_create_memory_selects_layer_by_salience() {
use crate::memory::MemoryTag;
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let participant = crate::types::EntityId::new("other_001").unwrap();
let memory = entity.create_memory(
"Test memory",
vec![participant],
vec![MemoryTag::Personal],
0.7,
None,
);
assert_eq!(entity.memories().legacy_count(), 1);
assert_eq!(memory.summary(), "Test memory");
}
#[test]
fn entity_create_memory_in_layer_explicit() {
use crate::memory::{MemoryLayer, MemoryTag};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let participant = crate::types::EntityId::new("other_001").unwrap();
let memory = entity.create_memory_in_layer(
MemoryLayer::ShortTerm,
"Test memory",
vec![participant],
vec![MemoryTag::Personal],
0.7,
None,
);
assert_eq!(entity.memories().short_term_count(), 1);
assert_eq!(memory.summary(), "Test memory");
}
#[test]
fn entity_create_memory_captures_mood() {
use crate::memory::MemoryTag;
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.6);
let memory = entity.create_memory(
"Happy memory",
vec![],
vec![MemoryTag::Achievement],
0.8,
None,
);
assert!((memory.emotional_snapshot().valence() - 0.6).abs() < 0.01);
let memories = entity.memories().legacy();
assert_eq!(memories.len(), 1);
assert!((memories[0].emotional_snapshot().valence() - 0.6).abs() < 0.01);
}
#[test]
fn entity_create_memory_with_context() {
use crate::memory::MemoryTag;
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let context = crate::types::MicrosystemId::new("work_001").unwrap();
let memory = entity.create_memory(
"Work memory",
vec![],
vec![MemoryTag::Mission],
0.5,
Some(context.clone()),
);
assert_eq!(memory.microsystem_context(), Some(&context));
let results = entity.memories().retrieve_by_context(&context);
assert_eq!(results.len(), 1);
}
#[test]
fn layer_for_salience_immediate() {
use crate::memory::MemoryLayer;
assert_eq!(Entity::layer_for_salience(0.0), MemoryLayer::Immediate);
assert_eq!(Entity::layer_for_salience(0.1), MemoryLayer::Immediate);
assert_eq!(Entity::layer_for_salience(0.29), MemoryLayer::Immediate);
}
#[test]
fn layer_for_salience_short_term() {
use crate::memory::MemoryLayer;
assert_eq!(Entity::layer_for_salience(0.3), MemoryLayer::ShortTerm);
assert_eq!(Entity::layer_for_salience(0.4), MemoryLayer::ShortTerm);
assert_eq!(Entity::layer_for_salience(0.49), MemoryLayer::ShortTerm);
}
#[test]
fn layer_for_salience_long_term() {
use crate::memory::MemoryLayer;
assert_eq!(Entity::layer_for_salience(0.5), MemoryLayer::LongTerm);
assert_eq!(Entity::layer_for_salience(0.6), MemoryLayer::LongTerm);
assert_eq!(Entity::layer_for_salience(0.69), MemoryLayer::LongTerm);
}
#[test]
fn layer_for_salience_legacy() {
use crate::memory::MemoryLayer;
assert_eq!(Entity::layer_for_salience(0.7), MemoryLayer::Legacy);
assert_eq!(Entity::layer_for_salience(0.8), MemoryLayer::Legacy);
assert_eq!(Entity::layer_for_salience(0.9), MemoryLayer::Legacy);
assert_eq!(Entity::layer_for_salience(1.0), MemoryLayer::Legacy);
}
#[test]
fn entity_recall_mood_congruent() {
use crate::memory::{EmotionalSnapshot, MemoryEntry, MemoryLayer};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let entry = MemoryEntry::new(Duration::days(10), "Happy memory")
.with_emotional_snapshot(EmotionalSnapshot::new(0.8, 0.3, 0.2))
.with_salience(0.7);
entity.memories_mut().add(MemoryLayer::ShortTerm, entry);
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(0.8);
entity
.individual_state_mut()
.mood_mut()
.add_arousal_delta(0.3);
entity
.individual_state_mut()
.mood_mut()
.add_dominance_delta(0.2);
let results = entity.recall_mood_congruent(0.9);
assert_eq!(results.len(), 1);
}
#[test]
fn entity_recall_mood_congruent_empty_when_no_match() {
use crate::memory::{EmotionalSnapshot, MemoryEntry, MemoryLayer};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let entry = MemoryEntry::new(Duration::days(10), "Happy memory")
.with_emotional_snapshot(EmotionalSnapshot::new(1.0, 1.0, 1.0))
.with_salience(0.7);
entity.memories_mut().add(MemoryLayer::ShortTerm, entry);
entity
.individual_state_mut()
.mood_mut()
.add_valence_delta(-0.8);
entity
.individual_state_mut()
.mood_mut()
.add_arousal_delta(-0.8);
entity
.individual_state_mut()
.mood_mut()
.add_dominance_delta(-0.8);
let results = entity.recall_mood_congruent(0.9);
assert!(results.is_empty());
}
#[test]
fn entity_starts_with_no_alerts() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
assert!(entity.alerts().is_empty());
}
#[test]
fn entity_can_accumulate_alerts() {
use crate::enums::{AlertSeverity, AlertTrigger, SpiralType};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let alert1 = Alert::new(
AlertSeverity::Warning,
AlertTrigger::spiral(SpiralType::Stress),
Duration::days(1),
"First alert",
);
entity.push_alert(alert1);
assert_eq!(entity.alerts().len(), 1);
let alert2 = Alert::new(
AlertSeverity::Critical,
AlertTrigger::spiral(SpiralType::Depression),
Duration::days(2),
"Second alert",
);
entity.push_alert(alert2);
assert_eq!(entity.alerts().len(), 2);
}
#[test]
fn entity_clear_alerts() {
use crate::enums::{AlertSeverity, AlertTrigger, SpiralType};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity.push_alert(Alert::new(
AlertSeverity::Warning,
AlertTrigger::spiral(SpiralType::Stress),
Duration::days(1),
"Test alert",
));
assert!(!entity.alerts().is_empty());
entity.clear_alerts();
assert!(entity.alerts().is_empty());
}
#[test]
fn entity_take_alerts() {
use crate::enums::{AlertSeverity, AlertTrigger, SpiralType};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
entity.push_alert(Alert::new(
AlertSeverity::Warning,
AlertTrigger::spiral(SpiralType::Stress),
Duration::days(1),
"Test alert",
));
let alerts = entity.take_alerts();
assert_eq!(alerts.len(), 1);
assert!(entity.alerts().is_empty());
}
#[test]
fn entity_context_accessor() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let ctx = entity.context();
assert_eq!(ctx.microsystem_count(), 0);
}
#[test]
fn entity_context_mut_accessor() {
use crate::context::{Microsystem, WorkContext};
use crate::types::MicrosystemId;
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let work_id = MicrosystemId::new("work").unwrap();
entity
.context_mut()
.add_microsystem(work_id, Microsystem::new_work(WorkContext::default()));
assert_eq!(entity.context().microsystem_count(), 1);
}
#[test]
fn entity_get_context_method() {
use crate::enums::{ContextPath, MacrosystemPath};
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let value = entity.get_context(&ContextPath::Macrosystem(MacrosystemPath::PowerDistance));
assert!(value.is_some());
assert!(value.unwrap() >= 0.0 && value.unwrap() <= 1.0);
}
#[test]
fn entity_set_context_method() {
use crate::enums::{ContextPath, MacrosystemPath};
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let result = entity.set_context(
&ContextPath::Macrosystem(MacrosystemPath::CulturalStress),
0.7,
);
assert!(result);
let value = entity
.get_context(&ContextPath::Macrosystem(MacrosystemPath::CulturalStress))
.unwrap();
assert!((value - 0.7).abs() < f64::EPSILON);
}
#[test]
fn entity_has_config() {
let entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
assert!(entity.config().mental_health_enabled());
}
#[test]
fn entity_config_derived_from_species() {
let human = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
let dog = EntityBuilder::new().species(Species::Dog).age(crate::types::Duration::years(30)).build().unwrap();
assert!(human.config().mental_health_enabled());
assert!(!dog.config().mental_health_enabled());
}
#[test]
fn entity_config_mut_modifies_config() {
let mut entity = EntityBuilder::new()
.species(Species::Human)
.age(crate::types::Duration::years(30))
.build()
.unwrap();
assert!(entity.config().mental_health_enabled());
entity.config_mut().set_mental_health_enabled(false);
assert!(!entity.config().mental_health_enabled());
}
}