summaryrefslogtreecommitdiff
path: root/2021/day04/src/main.rs
diff options
context:
space:
mode:
authorFederico Igne <undyamon@disroot.org>2023-12-01 09:21:16 +0100
committerFederico Igne <undyamon@disroot.org>2023-12-01 09:21:16 +0100
commit64e22194d39d2914db1e3bf93c90de9e0791d9b3 (patch)
tree6acdf81dad39cd7c009764b4538beddb0a6d860a /2021/day04/src/main.rs
parent120d53c0ff20574866ce10fa0538fb8b0dd2ef82 (diff)
downloadaoc-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.rs147
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 @@
1use std::fs;
2use std::fmt;
3use std::path::Path;
4
5struct Bingo {
6 cards: Vec<BingoCard>
7}
8
9impl 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)]
49struct BingoCard {
50 ingame: bool,
51 nums: Vec<(u32,bool)>
52}
53
54impl 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
76impl 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
85impl 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 */
98fn 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)]
109mod 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
11422 13 17 11 0
115 8 2 23 4 24
11621 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
12219 8 7 25 23
12320 11 10 24 4
12414 21 16 12 6
125
12614 21 17 24 4
12710 16 15 9 19
12818 8 23 26 20
12922 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}