1use crate::*;
2use rbp_core::*;
3
4#[derive(Debug, Default, Clone, Copy, Eq, Hash, PartialEq, Ord, PartialOrd)]
22pub struct Path(u64);
23
24impl Path {
25 const SEPARATOR: &'static str = "/";
26 pub fn length(&self) -> usize {
28 (67 - self.0.leading_zeros() as usize) / 4
29 }
30 pub fn aggression(&self) -> usize {
33 self.into_iter()
34 .rev()
35 .take_while(|e| e.is_choice())
36 .filter(|e| e.is_aggro())
37 .count()
38 }
39 pub fn street(&self) -> rbp_cards::Street {
42 match self.into_iter().filter(|e| e.is_chance()).count() {
43 0 => rbp_cards::Street::Pref,
44 1 => rbp_cards::Street::Flop,
45 2 => rbp_cards::Street::Turn,
46 _ => rbp_cards::Street::Rive,
47 }
48 }
49}
50
51impl Arbitrary for Path {
52 fn random() -> Self {
53 Self::from(rand::random::<u64>())
54 }
55}
56
57impl From<Path> for Vec<Edge> {
60 fn from(path: Path) -> Self {
61 path.into_iter().collect()
62 }
63}
64
65impl From<Vec<Edge>> for Path {
66 fn from(edges: Vec<Edge>) -> Self {
67 edges.into_iter().collect()
68 }
69}
70
71impl From<u64> for Path {
74 fn from(value: u64) -> Self {
75 Self(value)
76 }
77}
78impl From<Path> for u64 {
79 fn from(path: Path) -> Self {
80 path.0
81 }
82}
83impl From<Path> for i64 {
84 fn from(path: Path) -> Self {
85 path.0 as i64
86 }
87}
88impl From<i64> for Path {
89 fn from(value: i64) -> Self {
90 Self(value as u64)
91 }
92}
93
94impl TryFrom<&str> for Path {
95 type Error = anyhow::Error;
96 fn try_from(s: &str) -> Result<Self, Self::Error> {
97 s.split(Self::SEPARATOR)
98 .map(Edge::try_from)
99 .collect::<Result<Vec<_>, _>>()
100 .map(Self::from)
101 }
102}
103
104impl std::fmt::Display for Path {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 write!(
107 f,
108 "{}",
109 self.clone()
110 .into_iter()
111 .map(|e| e.to_string())
112 .collect::<Vec<_>>()
113 .join(Self::SEPARATOR)
114 )
115 }
116}
117
118impl Iterator for Path {
119 type Item = Edge;
120 fn next(&mut self) -> Option<Self::Item> {
121 let x = (self.0 & 0xF) as u8;
122 if self.0 == 0 {
123 None
124 } else if x == 0 {
125 None
126 } else {
127 self.0 >>= 4;
128 Some(Edge::from(x))
129 }
130 }
131}
132
133impl DoubleEndedIterator for Path {
134 fn next_back(&mut self) -> Option<Self::Item> {
135 let shift = ((63u32.saturating_sub(self.0.leading_zeros())) / 4) * 4;
136 let bloop = (self.0 >> shift) & 0xF;
137 if self.0 == 0 {
138 None
139 } else if bloop == 0 {
140 None
141 } else {
142 self.0 &= !(0xF << shift);
143 Some(Edge::from(bloop as u8))
144 }
145 }
146}
147
148impl std::iter::FromIterator<Edge> for Path {
149 fn from_iter<T>(iter: T) -> Self
150 where
151 T: IntoIterator<Item = Edge>,
152 {
153 iter.into_iter()
154 .take(rbp_core::MAX_DEPTH_SUBGAME)
155 .map(u8::from)
156 .map(|byte| byte as u64)
157 .enumerate()
158 .map(|(i, byte)| byte << (i * 4))
159 .fold(0u64, |acc, bits| acc | bits)
160 .into()
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 #[test]
168 fn bijective_path_empty() {
169 let edges = vec![];
170 let paths = Vec::<Edge>::from(Path::from(edges.clone()));
171 assert_eq!(edges, paths);
172 }
173
174 #[test]
175 fn bijective_path_edges() {
176 let edges = (0..)
177 .map(|_| Edge::random())
178 .take(rbp_core::MAX_DEPTH_SUBGAME)
179 .collect::<Vec<Edge>>();
180 let paths = Vec::<Edge>::from(Path::from(edges.clone()));
181 assert_eq!(edges, paths);
182 }
183
184 #[test]
185 fn bijective_path_collect() {
186 let edges = (0..).map(|_| Edge::random()).take(5).collect::<Vec<Edge>>();
187 let collected = Path::from(edges.clone()).into_iter().collect::<Vec<Edge>>();
188 assert_eq!(edges, collected);
189 }
190
191 #[test]
192 fn length() {
193 let n = rand::random::<u64>() % (rbp_core::MAX_DEPTH_SUBGAME + 1) as u64;
194 let n = n as usize;
195 let path = (0..).map(|_| Edge::random()).take(n).collect::<Path>();
196 assert_eq!(path.length(), n);
197 }
198
199 #[test]
200 fn double_ended_iterator() {
201 let path = (0..).map(|_| Edge::random()).take(5).collect::<Path>();
202 let forward = path.clone();
203 let reverse = path
204 .into_iter()
205 .rev()
206 .collect::<Vec<Edge>>()
207 .into_iter()
208 .rev()
209 .collect::<Path>();
210 assert_eq!(forward, reverse);
211 }
212
213 #[test]
214 fn subgame_aggression() {
215 let path = [
216 Edge::Draw,
218 Edge::Raise(Odds::new(1, 2)),
219 Edge::Call,
220 Edge::Call,
221 Edge::Draw,
223 Edge::Check,
224 Edge::Check,
225 Edge::Check,
226 Edge::Draw,
228 Edge::Raise(Odds::new(1, 1)),
229 Edge::Shove,
230 Edge::Fold,
231 ]
232 .into_iter()
233 .collect::<Path>();
234 assert_eq!(path.aggression(), 2);
235 let path = [
236 Edge::Draw,
238 Edge::Check,
239 Edge::Check,
240 Edge::Check,
241 ]
242 .into_iter()
243 .collect::<Path>();
244 assert_eq!(path.aggression(), 0);
245 }
246}