use super::types::{FactId, FactionId, PerceivedFact, Timestamp};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct KnowledgeBoard {
perceived_facts: HashMap<FactId, PerceivedFact>,
confidence_levels: HashMap<FactId, f32>,
last_updated: HashMap<FactId, Timestamp>,
}
impl KnowledgeBoard {
pub fn new() -> Self {
Self::default()
}
pub fn update_fact(
&mut self,
fact_id: FactId,
perceived: PerceivedFact,
confidence: f32,
timestamp: Timestamp,
) {
let confidence = confidence.clamp(0.0, 1.0);
self.perceived_facts.insert(fact_id.clone(), perceived);
self.confidence_levels.insert(fact_id.clone(), confidence);
self.last_updated.insert(fact_id, timestamp);
}
pub fn get_fact(&self, fact_id: &FactId) -> Option<&PerceivedFact> {
self.perceived_facts.get(fact_id)
}
pub fn get_confidence(&self, fact_id: &FactId) -> Option<f32> {
self.confidence_levels.get(fact_id).copied()
}
pub fn get_last_updated(&self, fact_id: &FactId) -> Option<Timestamp> {
self.last_updated.get(fact_id).copied()
}
pub fn remove_fact(&mut self, fact_id: &FactId) {
self.perceived_facts.remove(fact_id);
self.confidence_levels.remove(fact_id);
self.last_updated.remove(fact_id);
}
pub fn has_fact(&self, fact_id: &FactId) -> bool {
self.perceived_facts.contains_key(fact_id)
}
pub fn all_facts(&self) -> impl Iterator<Item = (&FactId, &PerceivedFact)> {
self.perceived_facts.iter()
}
pub fn fact_count(&self) -> usize {
self.perceived_facts.len()
}
pub fn clear(&mut self) {
self.perceived_facts.clear();
self.confidence_levels.clear();
self.last_updated.clear();
}
pub fn facts_above_confidence(&self, threshold: f32) -> Vec<(&FactId, &PerceivedFact, f32)> {
self.perceived_facts
.iter()
.filter_map(|(fact_id, perceived)| {
self.confidence_levels
.get(fact_id)
.filter(|&&confidence| confidence >= threshold)
.map(|&confidence| (fact_id, perceived, confidence))
})
.collect()
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct KnowledgeBoardRegistry {
boards: HashMap<FactionId, KnowledgeBoard>,
}
impl KnowledgeBoardRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register_faction(&mut self, faction_id: FactionId) {
self.boards.entry(faction_id).or_default();
}
pub fn get_board(&self, faction_id: &FactionId) -> Option<&KnowledgeBoard> {
self.boards.get(faction_id)
}
pub fn get_board_mut(&mut self, faction_id: &FactionId) -> Option<&mut KnowledgeBoard> {
self.boards.get_mut(faction_id)
}
pub fn has_faction(&self, faction_id: &FactionId) -> bool {
self.boards.contains_key(faction_id)
}
pub fn all_boards(&self) -> impl Iterator<Item = (&FactionId, &KnowledgeBoard)> {
self.boards.iter()
}
pub fn all_boards_mut(&mut self) -> impl Iterator<Item = (&FactionId, &mut KnowledgeBoard)> {
self.boards.iter_mut()
}
pub fn faction_count(&self) -> usize {
self.boards.len()
}
pub fn remove_faction(&mut self, faction_id: &FactionId) -> Option<KnowledgeBoard> {
self.boards.remove(faction_id)
}
pub fn clear(&mut self) {
self.boards.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::plugin::subjective_reality::types::*;
use std::time::Duration;
#[test]
fn test_knowledge_board_creation() {
let board = KnowledgeBoard::new();
assert_eq!(board.fact_count(), 0);
}
#[test]
fn test_knowledge_board_update_fact() {
let mut board = KnowledgeBoard::new();
let perceived = PerceivedFact::new(
FactType::MilitaryStrength {
faction: "enemy".into(),
strength: 850,
},
0.85,
Duration::from_secs(5),
Some("fact_001".into()),
);
board.update_fact("fact_001".into(), perceived.clone(), 0.85, 100);
assert_eq!(board.fact_count(), 1);
assert!(board.has_fact(&"fact_001".into()));
assert_eq!(board.get_confidence(&"fact_001".into()), Some(0.85));
assert_eq!(board.get_last_updated(&"fact_001".into()), Some(100));
let retrieved = board.get_fact(&"fact_001".into()).unwrap();
assert_eq!(retrieved.accuracy, 0.85);
}
#[test]
fn test_knowledge_board_remove_fact() {
let mut board = KnowledgeBoard::new();
let perceived = PerceivedFact::new(
FactType::MarketPrice {
item: "wheat".into(),
price: 15.5,
},
0.9,
Duration::from_secs(0),
None,
);
board.update_fact("fact_002".into(), perceived, 0.9, 200);
assert_eq!(board.fact_count(), 1);
board.remove_fact(&"fact_002".into());
assert_eq!(board.fact_count(), 0);
assert!(!board.has_fact(&"fact_002".into()));
}
#[test]
fn test_knowledge_board_confidence_threshold() {
let mut board = KnowledgeBoard::new();
board.update_fact(
"fact_high".into(),
PerceivedFact::new(
FactType::Custom {
fact_type: "test".into(),
data: serde_json::json!({}),
},
0.9,
Duration::from_secs(0),
None,
),
0.9,
100,
);
board.update_fact(
"fact_low".into(),
PerceivedFact::new(
FactType::Custom {
fact_type: "test".into(),
data: serde_json::json!({}),
},
0.3,
Duration::from_secs(0),
None,
),
0.3,
100,
);
let high_confidence_facts = board.facts_above_confidence(0.5);
assert_eq!(high_confidence_facts.len(), 1);
assert_eq!(high_confidence_facts[0].0, &"fact_high".to_string());
}
#[test]
fn test_registry_faction_management() {
let mut registry = KnowledgeBoardRegistry::new();
assert_eq!(registry.faction_count(), 0);
registry.register_faction("faction_a".into());
registry.register_faction("faction_b".into());
assert_eq!(registry.faction_count(), 2);
assert!(registry.has_faction(&"faction_a".into()));
assert!(registry.has_faction(&"faction_b".into()));
assert!(!registry.has_faction(&"faction_c".into()));
}
#[test]
fn test_registry_board_access() {
let mut registry = KnowledgeBoardRegistry::new();
registry.register_faction("faction_a".into());
{
let board = registry.get_board_mut(&"faction_a".into()).unwrap();
board.update_fact(
"fact_001".into(),
PerceivedFact::new(
FactType::InfectionStatus {
location: "district_a".into(),
infected: 100,
},
0.8,
Duration::from_secs(0),
None,
),
0.8,
100,
);
}
let board = registry.get_board(&"faction_a".into()).unwrap();
assert_eq!(board.fact_count(), 1);
assert!(board.has_fact(&"fact_001".into()));
}
#[test]
fn test_registry_remove_faction() {
let mut registry = KnowledgeBoardRegistry::new();
registry.register_faction("faction_a".into());
let removed = registry.remove_faction(&"faction_a".into());
assert!(removed.is_some());
assert_eq!(registry.faction_count(), 0);
assert!(!registry.has_faction(&"faction_a".into()));
}
#[test]
fn test_confidence_clamping() {
let mut board = KnowledgeBoard::new();
board.update_fact(
"fact_over".into(),
PerceivedFact::new(
FactType::Custom {
fact_type: "test".into(),
data: serde_json::json!({}),
},
0.5,
Duration::from_secs(0),
None,
),
1.5, 100,
);
assert_eq!(board.get_confidence(&"fact_over".into()), Some(1.0));
board.update_fact(
"fact_under".into(),
PerceivedFact::new(
FactType::Custom {
fact_type: "test".into(),
data: serde_json::json!({}),
},
0.5,
Duration::from_secs(0),
None,
),
-0.5, 100,
);
assert_eq!(board.get_confidence(&"fact_under".into()), Some(0.0));
}
#[test]
fn test_serialization() {
let mut board = KnowledgeBoard::new();
board.update_fact(
"fact_001".into(),
PerceivedFact::new(
FactType::FinancialStatus {
faction: "corp_a".into(),
budget: 50000.0,
},
0.75,
Duration::from_secs(10),
Some("ground_truth_1".into()),
),
0.75,
100,
);
let json = serde_json::to_string(&board).unwrap();
let deserialized: KnowledgeBoard = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.fact_count(), 1);
assert_eq!(deserialized.get_confidence(&"fact_001".into()), Some(0.75));
}
}