1use crate::Prng;
4
5#[derive(Clone, PartialEq, Eq, Debug, Default)]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct Pile<T> {
15 items: Vec<T>,
16}
17
18impl<T> Pile<T> {
19 #[must_use]
21 pub const fn new() -> Self {
22 Self { items: Vec::new() }
23 }
24
25 #[must_use]
27 pub const fn from_items(items: Vec<T>) -> Self {
28 Self { items }
29 }
30
31 #[must_use]
33 pub const fn len(&self) -> usize {
34 self.items.len()
35 }
36
37 #[must_use]
39 pub const fn is_empty(&self) -> bool {
40 self.items.is_empty()
41 }
42
43 pub fn draw(&mut self) -> Option<T> {
45 self.items.pop()
46 }
47
48 pub fn draw_n(&mut self, n: usize) -> Vec<T> {
50 let mut drawn = Vec::new();
51 for _ in 0..n {
52 match self.items.pop() {
53 Some(item) => drawn.push(item),
54 None => break,
55 }
56 }
57 drawn
58 }
59
60 pub fn put(&mut self, item: T) {
62 self.items.push(item);
63 }
64
65 pub fn put_bottom(&mut self, item: T) {
67 self.items.insert(0, item);
68 }
69
70 pub fn insert(&mut self, index: usize, item: T) {
74 self.items.insert(index.min(self.items.len()), item);
75 }
76
77 pub fn remove(&mut self, index: usize) -> Option<T> {
79 if index < self.items.len() {
80 Some(self.items.remove(index))
81 } else {
82 None
83 }
84 }
85
86 pub fn shuffle(&mut self, rng: &mut Prng) {
88 rng.shuffle(&mut self.items);
89 }
90
91 #[must_use]
93 pub fn as_slice(&self) -> &[T] {
94 &self.items
95 }
96
97 pub fn iter(&self) -> std::slice::Iter<'_, T> {
99 self.items.iter()
100 }
101}
102
103impl<T: PartialEq> Pile<T> {
104 #[must_use]
106 pub fn contains(&self, item: &T) -> bool {
107 self.items.contains(item)
108 }
109
110 #[must_use]
112 pub fn position(&self, item: &T) -> Option<usize> {
113 self.items.iter().position(|candidate| candidate == item)
114 }
115
116 pub fn remove_item(&mut self, item: &T) -> Option<T> {
118 self.position(item).and_then(|index| self.remove(index))
119 }
120}
121
122impl<T> FromIterator<T> for Pile<T> {
123 fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
124 Self {
125 items: iter.into_iter().collect(),
126 }
127 }
128}
129
130impl<'a, T> IntoIterator for &'a Pile<T> {
131 type Item = &'a T;
132 type IntoIter = std::slice::Iter<'a, T>;
133
134 fn into_iter(self) -> Self::IntoIter {
135 self.items.iter()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::Pile;
142 use crate::Prng;
143
144 #[test]
145 fn draw_takes_from_the_top() {
146 let mut pile = Pile::from_items(vec![1, 2, 3]);
147 assert_eq!(pile.draw(), Some(3));
148 assert_eq!(pile.draw(), Some(2));
149 assert_eq!(pile.len(), 1);
150 }
151
152 #[test]
153 fn draw_n_stops_at_empty() {
154 let mut pile = Pile::from_items(vec![1, 2]);
155 assert_eq!(pile.draw_n(5), vec![2, 1]);
156 assert!(pile.is_empty());
157 }
158
159 #[test]
160 fn remove_then_insert_restores_order() {
161 let mut pile = Pile::from_items(vec!['a', 'b', 'c', 'd']);
162 let removed = pile.remove(1).unwrap();
163 assert_eq!(removed, 'b');
164 pile.insert(1, removed);
165 assert_eq!(pile.as_slice(), &['a', 'b', 'c', 'd']);
166 }
167
168 #[test]
169 fn shuffle_is_a_deterministic_permutation() {
170 let base: Vec<u32> = (0..50).collect();
171 let mut a = Pile::from_items(base.clone());
172 let mut b = Pile::from_items(base.clone());
173 a.shuffle(&mut Prng::new(7));
174 b.shuffle(&mut Prng::new(7));
175 assert_eq!(a, b, "same seed shuffles identically");
176
177 let mut sorted: Vec<u32> = a.iter().copied().collect();
178 sorted.sort_unstable();
179 assert_eq!(sorted, base, "shuffle is a permutation");
180 }
181
182 #[test]
183 fn contains_and_remove_item_use_equality() {
184 let mut pile = Pile::from_items(vec![10, 20, 30]);
185 assert!(pile.contains(&20));
186 assert_eq!(pile.position(&30), Some(2));
187 assert_eq!(pile.remove_item(&20), Some(20));
188 assert!(!pile.contains(&20));
189 }
190}