diff options
author | Federico Igne <undyamon@disroot.org> | 2023-12-01 09:21:16 +0100 |
---|---|---|
committer | Federico Igne <undyamon@disroot.org> | 2023-12-01 09:21:16 +0100 |
commit | 64e22194d39d2914db1e3bf93c90de9e0791d9b3 (patch) | |
tree | 6acdf81dad39cd7c009764b4538beddb0a6d860a /2021/day04/src/main.rs | |
parent | 120d53c0ff20574866ce10fa0538fb8b0dd2ef82 (diff) | |
download | aoc-64e22194d39d2914db1e3bf93c90de9e0791d9b3.tar.gz aoc-64e22194d39d2914db1e3bf93c90de9e0791d9b3.zip |
chore: reorganize project structure
Diffstat (limited to '2021/day04/src/main.rs')
-rw-r--r-- | 2021/day04/src/main.rs | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/2021/day04/src/main.rs b/2021/day04/src/main.rs new file mode 100644 index 0000000..fd74866 --- /dev/null +++ b/2021/day04/src/main.rs | |||
@@ -0,0 +1,147 @@ | |||
1 | use std::fs; | ||
2 | use std::fmt; | ||
3 | use std::path::Path; | ||
4 | |||
5 | struct Bingo { | ||
6 | cards: Vec<BingoCard> | ||
7 | } | ||
8 | |||
9 | impl Bingo { | ||
10 | fn update(&mut self, draw: u32) -> Vec<(usize,u32)> { | ||
11 | self.cards.iter_mut().enumerate().filter_map( | ||
12 | |(i,c)| { | ||
13 | if let Some(s) = c.mark(draw) { | ||
14 | if c.ingame { return Some((i,s)) } | ||
15 | } | ||
16 | None | ||
17 | } | ||
18 | ).collect() | ||
19 | } | ||
20 | |||
21 | fn simulate(&mut self, draws: &[u32]) -> u32 { | ||
22 | if draws.len() > 0 { | ||
23 | if let Some((_,score)) = self.update(draws[0]).first() { | ||
24 | *score | ||
25 | } else { | ||
26 | self.simulate(&draws[1..]) | ||
27 | } | ||
28 | } else { | ||
29 | unreachable!() | ||
30 | } | ||
31 | } | ||
32 | |||
33 | fn simulate_all(&mut self, draws: &[u32]) -> u32 { | ||
34 | if draws.len() > 0 { | ||
35 | for (index,score) in self.update(draws[0]) { | ||
36 | if self.cards.iter().filter(|c| c.ingame).count() == 1 { | ||
37 | return score | ||
38 | } | ||
39 | self.cards[index].ingame = false; | ||
40 | } | ||
41 | self.simulate_all(&draws[1..]) | ||
42 | } else { | ||
43 | unreachable!() | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | #[derive(Clone)] | ||
49 | struct BingoCard { | ||
50 | ingame: bool, | ||
51 | nums: Vec<(u32,bool)> | ||
52 | } | ||
53 | |||
54 | impl BingoCard { | ||
55 | fn mark(&mut self, n: u32) -> Option<u32> { | ||
56 | if let Some(pos) = self.nums.iter().position(|c| c.0 == n) { | ||
57 | self.nums[pos].1 = true; | ||
58 | if self.check(pos) { | ||
59 | Some(self.score() * n) | ||
60 | } else { None } | ||
61 | } else { None } | ||
62 | } | ||
63 | |||
64 | fn check(&self, pos: usize) -> bool { | ||
65 | let row = pos / 5; | ||
66 | let col = pos % 5; | ||
67 | (5*row..5*row+5).all(|c| self.nums[c].1) | ||
68 | || (0usize..5).all(|c| self.nums[c*5+col].1) | ||
69 | } | ||
70 | |||
71 | fn score(&self) -> u32 { | ||
72 | self.nums.iter().filter_map(|c| if c.1 { None } else { Some(c.0) }).sum() | ||
73 | } | ||
74 | } | ||
75 | |||
76 | impl fmt::Display for BingoCard { | ||
77 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
78 | let str = self.nums.iter().enumerate().map( | ||
79 | |(n,c)| format!("{:>2}{}{}", c.0, if c.1 { '*' } else { ' ' }, if (n+1)%5 == 0 { '\n' } else { ' ' }) | ||
80 | ).collect::<Vec<String>>().join(""); | ||
81 | write!(f, "{}", str) | ||
82 | } | ||
83 | } | ||
84 | |||
85 | impl From<&str> for BingoCard { | ||
86 | fn from(s: &str) -> Self { | ||
87 | let mut nums = Vec::with_capacity(25); | ||
88 | s.lines().for_each( | ||
89 | |l| l.split_whitespace().for_each( | ||
90 | |n| nums.push((n.parse().expect("Malformed input"), false)) | ||
91 | ) | ||
92 | ); | ||
93 | BingoCard { ingame: true, nums } | ||
94 | } | ||
95 | } | ||
96 | |||
97 | /* AOC21 Day 4: https://adventofcode.com/2021/day/4 */ | ||
98 | fn main() { | ||
99 | let input = Path::new("resources").join("input.txt"); | ||
100 | let content = fs::read_to_string(input).expect("Unable to read input file"); | ||
101 | let report: Vec<&str> = content.split("\n\n").collect(); | ||
102 | let draws: Vec<u32> = report[0].split(",").map(|x| x.parse().expect("Malformed input")).collect(); | ||
103 | let cards: Vec<BingoCard> = report[1..].iter().map(|&c| BingoCard::from(c)).collect(); | ||
104 | println!("Ex1: The result is {}", Bingo { cards: cards.clone() }.simulate(&draws)); | ||
105 | println!("Ex1: The result is {}", Bingo { cards }.simulate_all(&draws)); | ||
106 | } | ||
107 | |||
108 | #[cfg(test)] | ||
109 | mod tests { | ||
110 | use super::*; | ||
111 | |||
112 | const INPUT: &str = "7,4,9,5,11,17,23,2,0,14,21,24,10,16,13,6,15,25,12,22,18,20,8,19,3,26,1 | ||
113 | |||
114 | 22 13 17 11 0 | ||
115 | 8 2 23 4 24 | ||
116 | 21 9 14 16 7 | ||
117 | 6 10 3 18 5 | ||
118 | 1 12 20 15 19 | ||
119 | |||
120 | 3 15 0 2 22 | ||
121 | 9 18 13 17 5 | ||
122 | 19 8 7 25 23 | ||
123 | 20 11 10 24 4 | ||
124 | 14 21 16 12 6 | ||
125 | |||
126 | 14 21 17 24 4 | ||
127 | 10 16 15 9 19 | ||
128 | 18 8 23 26 20 | ||
129 | 22 11 13 6 5 | ||
130 | 2 0 12 3 7"; | ||
131 | |||
132 | #[test] | ||
133 | fn winning_score() { | ||
134 | let report: Vec<&str> = INPUT.split("\n\n").collect(); | ||
135 | let draws: Vec<u32> = report[0].split(",").map(|x| x.parse().expect("Malformed input")).collect(); | ||
136 | let cards: Vec<BingoCard> = report[1..].iter().map(|&c| BingoCard::from(c)).collect(); | ||
137 | assert_eq!(4512, Bingo { cards }.simulate(&draws)) | ||
138 | } | ||
139 | |||
140 | #[test] | ||
141 | fn last_winning_score() { | ||
142 | let report: Vec<&str> = INPUT.split("\n\n").collect(); | ||
143 | let draws: Vec<u32> = report[0].split(",").map(|x| x.parse().expect("Malformed input")).collect(); | ||
144 | let cards: Vec<BingoCard> = report[1..].iter().map(|&c| BingoCard::from(c)).collect(); | ||
145 | assert_eq!(1924, Bingo { cards }.simulate_all(&draws)) | ||
146 | } | ||
147 | } | ||