use crate::error::*;
use crate::format::Format;
#[derive(PartialEq, Debug)]
pub struct Deck {
version: u8,
pub format: Format,
pub heroes: Vec<u32>,
single_cards: Vec<u32>,
double_cards: Vec<u32>,
multi_cards: Vec<(u8, u32)>,
}
impl Deck {
pub fn total_cards(&self) -> usize {
let mut card_count = self.single_cards.len() + self.double_cards.len() * 2;
card_count = card_count + self.multi_cards.iter().fold(0, |acc, t| acc + t.0) as usize;
card_count
}
fn total_card_slots(&self) -> usize {
self.single_cards.len() + self.double_cards.len() + self.multi_cards.len()
}
pub fn cards(&self) -> Vec<(u8, u32)> {
let mut cards: Vec<(u8, u32)> = Vec::new();
for card in &self.single_cards {
cards.push((1, *card));
}
for card in &self.double_cards {
cards.push((2, *card));
}
for (amount, card) in &self.multi_cards {
cards.push((*amount, *card));
}
cards.sort_by(|a, b| {
let initial_ordering =
a.0.partial_cmp(&b.0)
.expect("Error comparing card count whilst sorting");
match initial_ordering {
std::cmp::Ordering::Equal => {
a.1.partial_cmp(&b.1)
.expect("Error comparing card ids whilst sorting")
}
_ => initial_ordering,
}
});
cards
}
pub fn new(bytes: &Vec<u32>) -> Result<Self, DeckCodeError> {
let total_bytes = bytes.len();
if total_bytes < 7 {
return Err(DeckCodeError::InvalidDeckEncoding {
encoding_type: String::from("Length is too small"),
});
}
if bytes[0] != 0 {
return Err(DeckCodeError::InvalidDeckEncoding {
encoding_type: String::from("No leading 0 byte found"),
});
}
let version = bytes[1] as u8;
if version != 1 {
return Err(DeckCodeError::InvalidDeckEncoding {
encoding_type: String::from("Invalid or unsupported version"),
});
}
let format: Format = Format::from_u32(bytes[2]);
let hero_count = bytes[3] as usize;
let last_hero_byte: usize = 3 + hero_count;
let single_card_count = bytes[last_hero_byte + 1];
let last_single_card_byte = next_end(last_hero_byte, single_card_count);
if last_single_card_byte as usize > total_bytes {
return Err(DeckCodeError::InvalidDeckEncoding {
encoding_type: String::from(
"Length of card sections does not match number of bytes",
),
});
}
let double_card_count = bytes[last_single_card_byte + 1];
let last_double_card_byte = next_end(last_single_card_byte, double_card_count);
if last_double_card_byte as usize > total_bytes {
return Err(DeckCodeError::InvalidDeckEncoding {
encoding_type: String::from(
"Length of card sections does not match number of bytes",
),
});
}
let multi_card_count = bytes[last_double_card_byte + 1];
let last_multi_card_byte = next_end(last_double_card_byte, multi_card_count * 2);
if last_multi_card_byte as usize > total_bytes {
return Err(DeckCodeError::InvalidDeckEncoding {
encoding_type: String::from(
"Length of card sections does not match number of bytes",
),
});
}
let first_hero_byte: usize = 4;
let mut heroes: Vec<u32> = Vec::new();
for i in first_hero_byte..(last_hero_byte + 1) {
heroes.push(bytes[i]);
}
let mut single_cards: Vec<u32> = Vec::with_capacity(single_card_count as usize);
let first_single_card_byte: usize = last_hero_byte + 2;
for i in first_single_card_byte..(last_single_card_byte + 1) {
single_cards.push(bytes[i]);
}
let mut double_cards: Vec<u32> = Vec::with_capacity(double_card_count as usize);
let first_double_card_byte: usize = last_single_card_byte + 2;
for i in first_double_card_byte..(last_double_card_byte + 1) {
double_cards.push(bytes[i]);
}
let mut multi_cards: Vec<(u8, u32)> = Vec::with_capacity(multi_card_count as usize);
let mut index = last_double_card_byte + 2;
while index < last_multi_card_byte {
let card = bytes[index];
let number_of_card = bytes[index + 1] as u8;
multi_cards.push((number_of_card, card));
index = index + 2;
}
single_cards.sort();
double_cards.sort();
multi_cards.sort_by(|a, b| {
a.1.partial_cmp(&b.1)
.expect("Error comparing values whilst sorting")
});
Ok(Deck {
version: version,
format: format,
heroes: heroes,
single_cards: single_cards,
double_cards: double_cards,
multi_cards: multi_cards,
})
}
pub fn to_byte_array(&self) -> Vec<u32> {
let mut byte_array: Vec<u32> =
Vec::with_capacity(7 + self.heroes.len() + self.total_card_slots());
byte_array.append(&mut vec![
0,
(self.version as u32),
(self.format.to_u8() as u32),
(self.heroes.len() as u32),
]);
byte_array.extend(&self.heroes.clone());
byte_array.push(self.single_cards.len() as u32);
byte_array.extend(&self.single_cards.clone());
byte_array.push(self.double_cards.len() as u32);
byte_array.extend(&self.double_cards.clone());
byte_array.push((self.multi_cards.len()) as u32);
byte_array.extend(&flatten(&self.multi_cards));
byte_array
}
}
fn flatten(intervals: &[(u8, u32)]) -> Vec<u32> {
use std::iter::once;
intervals
.iter()
.flat_map(|tup| once(tup.1).chain(once(tup.0 as u32)))
.collect()
}
fn next_end(previous_end: usize, total_number: u32) -> usize {
return previous_end + total_number as usize + 1;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn new_returns_err_if_there_is_a_small_number_of_bytes_than_7() {
let input = vec![0, 1, 2];
let result = Deck::new(&input);
assert!(result.is_err());
}
#[test]
fn new_returns_err_if_there_is_no_leading_0_byte() {
let input = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
let result = Deck::new(&input);
assert!(result.is_err());
}
#[test]
fn new_returns_err_if_there_is_an_unexpected_version() {
let input = vec![0, 2, 0, 0, 0, 0, 0, 0, 0, 0];
let result = Deck::new(&input);
assert!(result.is_err());
}
#[test]
fn new_returns_err_if_there_is_a_larger_suggested_number_of_bytes_than_total_bytes() {
let input = vec![
0, 1, 1,
1, 7,
7,
0,
0,
];
let result = Deck::new(&input);
assert!(result.is_err());
}
#[test]
fn new_matches_simple_example() {
let input = vec![
0,
1,
1,
1,
7,
0,
0,
4,
1, 3,
2, 3,
3, 3,
4, 3,
];
let result = Deck::new(&input);
let expected = Deck {
format: Format::Wild,
version: 1,
heroes: vec![7],
single_cards: Vec::new(),
double_cards: Vec::new(),
multi_cards: vec![(3, 1), (3, 2), (3, 3), (3, 4)],
};
assert!(result.is_ok());
assert_eq!(result.unwrap(), expected);
}
#[test]
fn to_byte_array_matches_simple_example() {
let expected = vec![
0,
1,
1,
1,
7,
0,
0,
4,
1, 3,
2, 3,
3, 3,
4, 3,
];
let input = Deck {
format: Format::Wild,
version: 1,
heroes: vec![7],
single_cards: Vec::new(),
double_cards: Vec::new(),
multi_cards: vec![(3, 1), (3, 2), (3, 3), (3, 4)],
};
let result = input.to_byte_array();
assert_eq!(result, expected);
}
#[test]
fn to_byte_array_matches_complex_example() {
let expected = vec![
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,
];
let input = Deck {
format: Format::Standard,
version: 1,
heroes: vec![637],
single_cards: vec![192, 39841, 42718, 42790],
double_cards: vec![
113, 195, 315, 405, 555, 662, 748, 39715, 39767, 40297, 40583, 41153, 41496,
],
multi_cards: Vec::new(),
};
let result = input.to_byte_array();
assert_eq!(result, expected);
}
#[test]
fn total_cards() {
let input = Deck {
format: Format::Wild,
version: 1,
heroes: vec![7],
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)],
};
assert_eq!(24, input.total_cards())
}
#[test]
fn total_card_slots() {
let input = Deck {
format: Format::Wild,
version: 1,
heroes: vec![7],
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)],
};
assert_eq!(12, input.total_card_slots())
}
}