extensive_form/
information.rs1use crate::node::NodeId;
4use crate::tree::GameTree;
5use std::collections::HashMap;
6
7#[derive(Clone, Debug)]
9pub struct InformationSet {
10 pub id: usize,
12 pub player: usize,
14 pub nodes: Vec<NodeId>,
16 pub actions: Vec<String>,
18}
19
20#[derive(Clone, Debug, Default)]
22pub struct InformationStructure {
23 pub sets: Vec<InformationSet>,
25 pub node_to_set: HashMap<NodeId, usize>,
27}
28
29impl InformationStructure {
30 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn add_set(
37 &mut self,
38 player: usize,
39 nodes: Vec<NodeId>,
40 actions: Vec<String>,
41 ) -> usize {
42 let id = self.sets.len();
43 for &node in &nodes {
44 self.node_to_set.insert(node, id);
45 }
46 self.sets.push(InformationSet {
47 id,
48 player,
49 nodes,
50 actions,
51 });
52 id
53 }
54
55 pub fn set_for_node(&self, node: NodeId) -> Option<&InformationSet> {
57 self.node_to_set.get(&node).map(|&id| &self.sets[id])
58 }
59
60 pub fn same_set(&self, a: NodeId, b: NodeId) -> bool {
62 match (self.node_to_set.get(&a), self.node_to_set.get(&b)) {
63 (Some(sa), Some(sb)) => sa == sb,
64 _ => false,
65 }
66 }
67
68 pub fn validate(&self, tree: &GameTree) -> Result<(), String> {
75 for info_set in &self.sets {
76 let mut player = None;
78 for &nid in &info_set.nodes {
79 let node = tree.get_node(nid)
80 .ok_or_else(|| format!("Node {} not found", nid))?;
81 match player {
82 None => player = Some(node.player()),
83 Some(p) => {
84 if node.player() != p {
85 return Err(format!(
86 "Info set {} has nodes with different players", info_set.id
87 ));
88 }
89 }
90 }
91 }
92
93 for &nid in &info_set.nodes {
95 let node = tree.get_node(nid).unwrap();
96 if node.actions.len() != info_set.actions.len() {
97 return Err(format!(
98 "Node {} has {} actions but info set has {}",
99 nid, node.actions.len(), info_set.actions.len()
100 ));
101 }
102 }
103 }
104 Ok(())
105 }
106}
107
108pub fn simultaneous_game_info_sets(n_actions_p0: usize, n_actions_p1: usize) -> InformationStructure {
112 let mut info = InformationStructure::new();
113 let p1_nodes: Vec<NodeId> = (1..=n_actions_p0).collect();
115 let p1_actions: Vec<String> = (0..n_actions_p1).map(|i| format!("A{}", i)).collect();
116 info.add_set(1, p1_nodes, p1_actions);
117 info
118}
119
120#[cfg(test)]
121mod tests {
122 use super::*;
123 use crate::tree::GameTree;
124
125 #[test]
126 fn test_info_set_creation() {
127 let mut info = InformationStructure::new();
128 let id = info.add_set(0, vec![0, 1], vec!["L".into(), "R".into()]);
129 assert_eq!(id, 0);
130 assert_eq!(info.sets.len(), 1);
131 }
132
133 #[test]
134 fn test_same_set() {
135 let mut info = InformationStructure::new();
136 info.add_set(0, vec![0, 1, 2], vec!["L".into(), "R".into()]);
137 assert!(info.same_set(0, 1));
138 assert!(info.same_set(0, 2));
139 assert!(!info.same_set(0, 3));
140 }
141
142 #[test]
143 fn test_set_for_node() {
144 let mut info = InformationStructure::new();
145 info.add_set(0, vec![0, 1], vec!["L".into(), "R".into()]);
146 let s = info.set_for_node(0).unwrap();
147 assert_eq!(s.player, 0);
148 assert!(info.set_for_node(99).is_none());
149 }
150
151 #[test]
152 fn test_validate_consistent() {
153 let mut tree = GameTree::new("Test");
154 let root = tree.add_decision(0, "P0");
155 let n0 = tree.add_decision(1, "P1-a");
156 let n1 = tree.add_decision(1, "P1-b");
157 let t0 = tree.add_terminal(vec![1.0, 2.0], "End0");
158 let t1 = tree.add_terminal(vec![3.0, 4.0], "End1");
159 let t2 = tree.add_terminal(vec![5.0, 6.0], "End2");
160 let t3 = tree.add_terminal(vec![7.0, 8.0], "End3");
161 tree.add_action(root, "L", n0);
162 tree.add_action(root, "R", n1);
163 tree.add_action(n0, "U", t0);
164 tree.add_action(n0, "D", t1);
165 tree.add_action(n1, "U", t2);
166 tree.add_action(n1, "D", t3);
167
168 let mut info = InformationStructure::new();
169 info.add_set(1, vec![n0, n1], vec!["U".into(), "D".into()]);
170 assert!(info.validate(&tree).is_ok());
171 }
172}