1use crate::error::DeckCodeError;
2use crate::format::Format;
3
4#[derive(PartialEq, Debug)]
5pub struct Deck {
7 version: u8,
8 pub format: Format,
9 pub heroes: Vec<u32>,
11 single_cards: Vec<u32>,
13 double_cards: Vec<u32>,
15 #[allow(clippy::doc_markdown)]
16 multi_cards: Vec<(u8, u32)>,
18 #[allow(clippy::doc_markdown)]
19 sideboard_cards: Vec<(u32, u8, u32)>,
21}
22
23impl Deck {
24 #[must_use]
26 pub fn total_cards(&self) -> usize {
27 let mut card_count = self.single_cards.len() + self.double_cards.len() * 2;
28 card_count += self.multi_cards.iter().fold(0, |acc, t| acc + t.0) as usize;
29 card_count
30 }
31
32 fn total_card_slots(&self) -> usize {
34 self.single_cards.len() + self.double_cards.len() + self.multi_cards.len()
35 }
36
37 #[must_use]
41 pub fn cards(&self) -> Vec<(u8, u32, Option<u32>)> {
42 let mut cards: Vec<(u8, u32, Option<u32>)> = Vec::new();
43
44 for card in &self.single_cards {
47 cards.push((1, *card, None));
48 }
49
50 for card in &self.double_cards {
51 cards.push((2, *card, None));
52 }
53
54 for (amount, card) in &self.multi_cards {
55 cards.push((*amount, *card, None));
56 }
57
58 for (card, amount, owner_dbfid) in &self.sideboard_cards {
59 cards.push((*amount, *card, Some(*owner_dbfid)));
60 }
61
62 cards
63 }
64
65 #[allow(clippy::too_many_lines)]
70 pub fn new(bytes: &[u32]) -> Result<Self, DeckCodeError> {
71 let total_bytes = bytes.len();
72
73 if total_bytes < 7 {
74 return Err(DeckCodeError::InvalidDeckEncoding {
75 encoding_type: String::from("Length is too small"),
76 });
77 }
78
79 if bytes[0] != 0 {
80 return Err(DeckCodeError::InvalidDeckEncoding {
81 encoding_type: String::from("No leading 0 byte found"),
82 });
83 }
84
85 let version = std::convert::TryInto::<u8>::try_into(bytes[1]).map_err(|_| {
86 DeckCodeError::InvalidDeckEncoding {
87 encoding_type: "Could not read deck code version.".to_owned(),
88 }
89 })?;
90
91 if version != 1 {
92 return Err(DeckCodeError::UnknownVersion {
93 version: u32::from(version),
94 });
95 }
96
97 let format: Format = Format::from_u32(bytes[2])?;
98
99 let hero_count = bytes[3] as usize;
101 let last_hero_byte: usize = 3 + hero_count;
102 let single_card_count = bytes[last_hero_byte + 1];
103 let last_single_card_byte = next_end(last_hero_byte, single_card_count);
104 if last_single_card_byte > total_bytes {
105 return Err(DeckCodeError::InvalidDeckEncoding {
106 encoding_type: String::from(
107 "Length of card sections does not match number of bytes",
108 ),
109 });
110 }
111
112 let double_card_count = bytes[last_single_card_byte + 1];
113 let last_double_card_byte = next_end(last_single_card_byte, double_card_count);
114 if last_double_card_byte > total_bytes {
115 return Err(DeckCodeError::InvalidDeckEncoding {
116 encoding_type: String::from(
117 "Length of card sections does not match number of bytes",
118 ),
119 });
120 }
121
122 let multi_card_count = bytes[last_double_card_byte + 1];
123 let last_multi_card_byte = next_end(last_double_card_byte, multi_card_count * 2); if last_multi_card_byte > total_bytes {
125 return Err(DeckCodeError::InvalidDeckEncoding {
126 encoding_type: String::from(
127 "Length of card sections does not match number of bytes",
128 ),
129 });
130 }
131
132 let sideboard_card_count = if last_multi_card_byte + 2 >= total_bytes {
135 0
136 } else {
137 bytes[last_multi_card_byte + 2]
138 };
139
140 let last_sideboard_card_byte = next_end(last_multi_card_byte, sideboard_card_count * 2 + 1); if sideboard_card_count > 0 && last_sideboard_card_byte > total_bytes {
143 return Err(DeckCodeError::InvalidDeckEncoding {
144 encoding_type: String::from(
145 "Length of card sections does not match number of bytes",
146 ),
147 });
148 }
149
150 let first_hero_byte: usize = 4;
152 let mut heroes: Vec<u32> = Vec::new();
153 for i in first_hero_byte..=last_hero_byte {
154 heroes.push(bytes[i]);
155 }
156
157 let mut single_cards: Vec<u32> = Vec::with_capacity(single_card_count as usize);
159 let first_single_card_byte: usize = last_hero_byte + 2;
160 for i in first_single_card_byte..=last_single_card_byte {
161 single_cards.push(bytes[i]);
162 }
163
164 let mut double_cards: Vec<u32> = Vec::with_capacity(double_card_count as usize);
165 let first_double_card_byte: usize = last_single_card_byte + 2;
166 for i in first_double_card_byte..=last_double_card_byte {
167 double_cards.push(bytes[i]);
168 }
169
170 let mut multi_cards: Vec<(u8, u32)> = Vec::with_capacity(multi_card_count as usize);
172 let mut index = last_double_card_byte + 2;
173 while index < last_multi_card_byte {
174 let card = bytes[index];
175 let number_of_card =
176 u8::try_from(bytes[index + 1]).map_err(|_| DeckCodeError::InvalidDeckEncoding {
177 encoding_type: String::from("Amount of single card exceeded 255"),
178 })?;
179 multi_cards.push((number_of_card, card));
180 index += 2;
181 }
182
183 let mut sideboard_cards: Vec<(u32, u8, u32)> =
185 Vec::with_capacity(sideboard_card_count as usize);
186 if sideboard_card_count > 0 {
187 let mut index = last_multi_card_byte + 3;
188 while index < last_sideboard_card_byte {
189 let card_id = bytes[index];
190 let sideboard_for_card_id = bytes[index + 1];
191 sideboard_cards.push((card_id, 1, sideboard_for_card_id));
192 index += 2;
193 }
194 }
195
196 single_cards.sort_unstable();
197 double_cards.sort_unstable();
198 multi_cards.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
199 sideboard_cards.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
200
201 Ok(Self {
202 version,
203 format,
204 heroes,
205 single_cards,
206 double_cards,
207 multi_cards,
208 sideboard_cards,
209 })
210 }
211
212 #[must_use]
217 pub fn to_byte_array(&self) -> Vec<u32> {
218 let mut byte_array: Vec<u32> =
220 Vec::with_capacity(7 + self.heroes.len() + self.total_card_slots());
221 let mut vec = vec![
222 0,
223 u32::from(self.version),
224 u32::from(self.format.to_u8()),
225 u32::try_from(self.heroes.len()).expect("More heroes provided than expected"),
226 ];
227 byte_array.append(&mut vec);
228 byte_array.extend(&self.heroes.clone());
229 byte_array.push(
230 u32::try_from(self.single_cards.len())
231 .expect("More single cards provided than expected"),
232 );
233 byte_array.extend(&self.single_cards.clone());
234
235 byte_array.push(
236 u32::try_from(self.double_cards.len())
237 .expect("More double cards provided than expected"),
238 );
239 byte_array.extend(&self.double_cards.clone());
240
241 byte_array.push(
242 u32::try_from(self.multi_cards.len()).expect("More multi-cards provided than expected"),
243 );
244 byte_array.extend(&flatten_multi_cards(&self.multi_cards));
245
246 if self.sideboard_cards.is_empty() {
247 byte_array.push(0);
248 } else {
249 byte_array.push(1);
250 byte_array.push(
251 u32::try_from(self.sideboard_cards.len())
252 .expect("More sideboard cards provided than expected"),
253 );
254 byte_array.extend(&flatten_sideboard(&self.sideboard_cards));
255 }
256
257 byte_array
258 }
259}
260
261fn flatten_multi_cards(intervals: &[(u8, u32)]) -> Vec<u32> {
262 use std::iter::once;
263
264 intervals
265 .iter()
266 .flat_map(|tup| once(tup.1).chain(once(u32::from(tup.0))))
267 .collect()
268}
269
270fn flatten_sideboard(intervals: &[(u32, u8, u32)]) -> Vec<u32> {
271 use std::iter::once;
272
273 intervals
274 .iter()
275 .flat_map(|tup| once(tup.0).chain(once(tup.2)))
276 .chain(once(0))
278 .chain(once(0))
279 .collect()
280}
281
282fn next_end(previous_end: usize, total_number: u32) -> usize {
283 previous_end + total_number as usize + 1
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn new_returns_err_if_there_is_a_small_number_of_bytes_than_7() {
292 let input = vec![0, 1, 2];
293 let result = Deck::new(&input);
294 assert!(result.is_err());
295 }
296
297 #[test]
298 fn new_returns_err_if_there_is_no_leading_0_byte() {
299 let input = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
300 let result = Deck::new(&input);
301 assert!(result.is_err());
302 }
303
304 #[test]
305 fn new_returns_err_if_there_is_an_unexpected_version() {
306 let input = vec![0, 2, 0, 0, 0, 0, 0, 0, 0, 0];
307 let result = Deck::new(&input);
308 assert!(result.is_err());
309 }
310
311 #[test]
312 fn new_returns_err_if_there_is_a_larger_suggested_number_of_bytes_than_total_bytes() {
313 let input = vec![
314 0, 1, 1, 1, 7, 7, 0, 0,
322 ];
323 let result = Deck::new(&input);
324 assert!(result.is_err());
325 }
326
327 #[test]
328 fn new_matches_simple_example() {
329 let input = vec![
330 0, 1, 1, 1, 7, 0, 0, 4, 1, 3, 2, 3, 3, 3, 4, 3, ];
347
348 let result = Deck::new(&input);
349
350 let expected = Deck {
351 format: Format::Wild,
352 version: 1,
353 heroes: vec![7],
354 single_cards: Vec::new(),
355 double_cards: Vec::new(),
356 multi_cards: vec![(3, 1), (3, 2), (3, 3), (3, 4)],
357 sideboard_cards: Vec::new(),
358 };
359 assert_eq!(result.unwrap(), expected);
360 }
361
362 #[test]
363 fn to_byte_array_matches_simple_example() {
364 let expected = vec![
365 0, 1, 1, 1, 7, 0, 0, 4, 1, 3, 2, 3, 3, 3, 4, 3, 0, ];
384
385 let input = Deck {
386 format: Format::Wild,
387 version: 1,
388 heroes: vec![7],
389 single_cards: Vec::new(),
390 double_cards: Vec::new(),
391 multi_cards: vec![(3, 1), (3, 2), (3, 3), (3, 4)],
392 sideboard_cards: Vec::new(),
393 };
394 let result = input.to_byte_array();
395 assert_eq!(result, expected);
396 }
397
398 #[test]
399 fn to_byte_array_matches_complex_example() {
400 let expected = vec![
401 0, 1, 2, 1, 637, 4, 192, 39841, 42718, 42790, 13, 113, 195, 315, 405, 555, 662, 748, 39715, 39767, 40297, 40583, 41153, 41496, 0, 0, ];
432
433 let input = Deck {
434 format: Format::Standard,
435 version: 1,
436 heroes: vec![637],
437 single_cards: vec![192, 39841, 42718, 42790],
438 double_cards: vec![
439 113, 195, 315, 405, 555, 662, 748, 39715, 39767, 40297, 40583, 41153, 41496,
440 ],
441 multi_cards: Vec::new(),
442 sideboard_cards: Vec::new(),
443 };
444 let result = input.to_byte_array();
445 assert_eq!(result, expected);
446 }
447
448 #[test]
449 fn total_cards() {
450 let input = Deck {
451 format: Format::Wild,
452 version: 1,
453 heroes: vec![7],
454 single_cards: vec![1, 2, 3, 4], double_cards: vec![1, 2, 3, 4], multi_cards: vec![(3, 1), (3, 2), (3, 3), (3, 4)], sideboard_cards: Vec::new(),
458 };
459 assert_eq!(24, input.total_cards());
460 }
461
462 #[test]
463 fn total_card_slots() {
464 let input = Deck {
465 format: Format::Wild,
466 version: 1,
467 heroes: vec![7],
468 single_cards: vec![1, 2, 3, 4], double_cards: vec![1, 2, 3, 4], multi_cards: vec![(3, 1), (3, 2), (3, 3), (3, 4)], sideboard_cards: Vec::new(),
472 };
473 assert_eq!(12, input.total_card_slots());
474 }
475}