use crate::Problem;
use delhi_mb::State;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AgentView {
pub agent: String,
pub knows: Vec<String>,
pub believes: Vec<String>,
pub undecided: Vec<String>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct StateView {
pub facts: Vec<String>,
pub agents: Vec<AgentView>,
}
pub fn state_view(p: &mut Problem, state: &State) -> StateView {
let n_atoms = p.sig.n_atoms();
let n_agents = p.sig.n_agents();
let names: Vec<String> = (0..n_atoms).map(|a| p.sig.atom_name(a as u32).to_string()).collect();
let agent_names: Vec<String> =
(0..n_agents).map(|i| p.sig.agent_name(i as u32).to_string()).collect();
let signed = |a: usize, positive: bool| {
if positive {
names[a].clone()
} else {
format!("!{}", names[a])
}
};
let facts = (0..n_atoms).map(|a| signed(a, state.model.val[state.designated].get(a))).collect();
let mut agents = Vec::with_capacity(n_agents);
for (i, agent) in agent_names.iter().enumerate() {
let mut queries = Vec::with_capacity(n_atoms);
for a in 0..n_atoms {
let atom = p.store.atom(a as u32);
let neg = p.store.not(atom);
let kp = p.store.knows(i as u32, atom);
let kn = p.store.knows(i as u32, neg);
let bp = p.store.believes(i as u32, atom);
let bn = p.store.believes(i as u32, neg);
queries.push((kp, kn, bp, bn));
}
let mut view = AgentView {
agent: agent.clone(),
knows: Vec::new(),
believes: Vec::new(),
undecided: Vec::new(),
};
for (a, (kp, kn, bp, bn)) in queries.into_iter().enumerate() {
if state.entails(&p.store, kp) {
view.knows.push(signed(a, true));
} else if state.entails(&p.store, kn) {
view.knows.push(signed(a, false));
} else if state.entails(&p.store, bp) {
view.believes.push(signed(a, true));
} else if state.entails(&p.store, bn) {
view.believes.push(signed(a, false));
} else {
view.undecided.push(names[a].clone());
}
}
agents.push(view);
}
StateView { facts, agents }
}
#[cfg(test)]
mod tests {
use super::*;
fn view(src: &str) -> StateView {
let mut p = Problem::parse(src).unwrap_or_else(|e| panic!("{e}"));
let state = p.state.clone();
state_view(&mut p, &state)
}
const COIN: &str = r#"
types{ Actor - Object } objects{ a, b - Actor } agents{ a, b } props{ h }
initially { h, ?[a] h, B[a] h }
actions {}
"#;
#[test]
fn knowing_and_merely_believing_land_in_different_lists() {
let v = view(COIN);
assert_eq!(v.facts, vec!["h"]);
assert_eq!(v.agents[0].agent, "a");
assert_eq!(v.agents[0].believes, vec!["h"]);
assert!(v.agents[0].knows.is_empty(), "a does not know h");
assert_eq!(v.agents[1].agent, "b");
assert_eq!(v.agents[1].knows, vec!["h"]);
assert!(v.agents[1].believes.is_empty());
}
#[test]
fn a_flat_plausibility_order_reads_as_undecided() {
let v = view(
r#"
types{ Actor - Object } objects{ a - Actor } agents{ a } props{ h }
initially { h, ?[a] h }
actions {}
"#,
);
assert_eq!(v.agents[0].undecided, vec!["h"]);
assert!(v.agents[0].knows.is_empty() && v.agents[0].believes.is_empty());
}
#[test]
fn a_false_proposition_is_reported_negated_not_omitted() {
let v = view(
r#"
types{ Actor - Object } objects{ a - Actor } agents{ a } props{ h, d }
initially { h }
actions {}
"#,
);
assert!(v.facts.contains(&"!d".to_string()), "got {:?}", v.facts);
assert!(v.agents[0].knows.contains(&"!d".to_string()), "got {:?}", v.agents[0]);
}
#[test]
fn every_proposition_lands_in_exactly_one_list() {
const MUDDY: &str = r#"
types { Actor - Object }
objects { a, b, c - Actor }
agents { a, b, c }
props { m_a, m_b, m_c }
initially {
m_a m_b m_c
?[a] m_a K[b] m_a K[c] m_a
K[a] m_b ?[b] m_b K[c] m_b
K[a] m_c K[b] m_c ?[c] m_c
}
actions {}
"#;
for src in [COIN, MUDDY] {
let mut p = Problem::parse(src).unwrap_or_else(|e| panic!("{e}"));
let n = p.sig.n_atoms();
let state = p.state.clone();
let v = state_view(&mut p, &state);
assert_eq!(v.facts.len(), n);
for a in &v.agents {
assert_eq!(
a.knows.len() + a.believes.len() + a.undecided.len(),
n,
"{}'s attitudes must cover every proposition once: {a:?}",
a.agent
);
}
}
}
}