--- /dev/null
+use std::str::FromStr;\r
+\r
+#[derive(Debug, PartialEq)]\r
+pub struct Card {\r
+ pub id: u32,\r
+ pub count: u32,\r
+ pub winning: Vec<u32>,\r
+ pub numbers: Vec<u32>,\r
+}\r
+\r
+#[derive(Debug, PartialEq, Eq)]\r
+pub struct ParseCardError;\r
+\r
+impl Card {\r
+ // part 1: calculate the number of points for this card.\r
+ pub fn points(&self) -> u32 {\r
+ let mut points: u32 = 0;\r
+ for _vals in self.numbers.iter().filter(|x| self.winning.contains(x)) {\r
+ if points == 0 {\r
+ points = 1;\r
+ } else {\r
+ points *= 2;\r
+ }\r
+ }\r
+ return points;\r
+ }\r
+\r
+ // returns the number of winning numbers\r
+ pub fn matches(&self) -> u32 {\r
+ self.numbers.iter()\r
+ .filter(|x| self.winning.contains(x)).count() as u32\r
+ }\r
+\r
+ pub fn match_nums(&self) -> Vec<u32> {\r
+ let mut result = Vec::<u32>::new();\r
+ for num in &self.numbers {\r
+ if self.winning.contains(num) {\r
+ result.push(*num);\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+}\r
+\r
+impl FromStr for Card {\r
+ type Err = ParseCardError;\r
+\r
+ fn from_str(s: &str) -> Result<Self, Self::Err> {\r
+ let (name, nums) = s.trim().split_once(':').ok_or(ParseCardError)?;\r
+ let (_junk, idstr) = name.split_once(" ").unwrap();\r
+ let id = idstr.trim().parse().unwrap();\r
+ let (wins, vals) = nums.split_once("|").unwrap();\r
+ let winning: Vec<u32> = wins.trim().split_whitespace().map(|x| x.parse::<u32>().unwrap()).collect();\r
+ let values: Vec<u32> = vals.trim().split_whitespace().map(|x| x.parse::<u32>().unwrap()).collect();\r
+ Ok( Card {id: id, count: 1, winning: winning, numbers: values} )\r
+ }\r
+}\r
+\r
+#[cfg(test)]\r
+mod test {\r
+ use super::*;\r
+\r
+ #[test]\r
+ fn test_parse() {\r
+ let card = "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53".parse::<Card>().unwrap();\r
+ assert_eq!(card.id, 1);\r
+ assert_eq!(card.winning, vec![41, 48, 83, 86, 17]);\r
+ assert_eq!(card.numbers, vec![83, 86, 6, 31, 17, 9, 48, 53]);\r
+ }\r
+\r
+ #[test]\r
+ fn test_points() {\r
+ let card = "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53".parse::<Card>().unwrap();\r
+ assert_eq!(card.points(), 8);\r
+ }\r
+\r
+ #[test]\r
+ fn test_matches() {\r
+ let card = "Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53".parse::<Card>().unwrap();\r
+ assert_eq!(card.matches(), 4);\r
+ }\r
+\r
+ #[test]\r
+ fn test_count_change() {\r
+ let mut card = Card {id: 1, count: 1, winning: vec![], numbers: vec![] };\r
+ card.count += 1;\r
+ assert_eq!(card.count, 2);\r
+ }\r
+}
\ No newline at end of file
--- /dev/null
+pub mod card;\r
+\r
+use card::Card;\r
+\r
+use std::error::Error;\r
+use std::env;\r
+use std::fs::File;\r
+use std::io::{prelude::*, BufReader};\r
+\r
+fn main() -> Result<(), Box<dyn Error>> {\r
+ let args: Vec<String> = env::args().collect();\r
+ if args.len() > 1 {\r
+ let input = File::open(&args[1]).expect("no such file");\r
+ let buffered = BufReader::new(input);\r
+ let cards: Vec<Card> = buffered.lines()\r
+ .map(|x| x.expect("something").parse::<Card>().unwrap())\r
+ .collect();\r
+ for card in &cards {\r
+ println!("{:?}", card);\r
+ }\r
+ let points: u32 = cards.iter().map(|c| c.points()).sum();\r
+ println!("part 1: {}", points);\r
+\r
+ }\r
+ Ok(())\r
+}\r