diff --git a/source/day_03/mod.rs b/source/day_03/mod.rs new file mode 100644 index 0000000..3363efa --- /dev/null +++ b/source/day_03/mod.rs @@ -0,0 +1,106 @@ +use std::collections::HashMap; + +use color_eyre::{eyre::eyre, Result}; +use itertools::Itertools; + +pub fn solve() -> Result<()> { + let input_data = include_str!("../../data/day_03.txt").trim(); + println!("Day 03 Part 1: {}", part_1(input_data)?); + println!("Day 03 Part 2: {}", part_2(input_data)?); + Ok(()) +} + +fn count_bits(input: &str) -> Result> { + let mut bits = HashMap::::new(); + + for line in input.lines() { + for (index, bit) in line.char_indices() { + let entry = bits.entry(index).or_default(); + + match bit { + '0' => *entry -= 1, + '1' => *entry += 1, + _ => return Err(eyre!("Unknown character in line: {}", line)), + } + } + } + + Ok( + bits + .into_iter() + .sorted_by(|(a, _), (b, _)| a.cmp(b)) + .collect(), + ) +} + +fn part_1(input: &str) -> Result { + let bits = count_bits(input)?; + + let gamma_rate = i32::from_str_radix( + &bits + .clone() + .into_iter() + .map(|(_, bit)| if bit > 0 { '1' } else { '0' }) + .collect::(), + 2, + )?; + + let epsilon_rate = i32::from_str_radix( + &bits + .into_iter() + .map(|(_, bit)| if bit < 0 { '1' } else { '0' }) + .collect::(), + 2, + )?; + + Ok(gamma_rate * epsilon_rate) +} + +fn part_2(input: &str) -> Result { + let mut most_common_lines = input.lines().collect::>(); + let mut least_common_lines = input.lines().collect::>(); + + let mut most_common_bits = count_bits(input)?; + let mut least_common_bits = count_bits(input)?; + + for index in 0..most_common_bits.len() { + if most_common_lines.len() > 1 { + let (index, bit) = most_common_bits + .get(index) + .ok_or(eyre!("Could not find most common bit"))?; + let most_common = if bit >= &0 { '1' } else { '0' }; + most_common_lines = most_common_lines + .into_iter() + .filter(|line| line.chars().nth(*index) == Some(most_common)) + .collect(); + most_common_bits = count_bits(&most_common_lines.join("\n"))?; + } + + if least_common_lines.len() > 1 { + let (index, bit) = least_common_bits + .get(index) + .ok_or(eyre!("Could not find least common bit"))?; + let least_common = if bit < &0 { '1' } else { '0' }; + least_common_lines = least_common_lines + .into_iter() + .filter(|line| line.chars().nth(*index) == Some(least_common)) + .collect(); + least_common_bits = count_bits(&least_common_lines.join("\n"))?; + } + } + + let oxygen_generator_rating = i32::from_str_radix( + most_common_lines + .first() + .ok_or(eyre!("Didn't find an oxygen generator rating"))?, + 2, + )?; + let co2_scrubber_rating = i32::from_str_radix( + least_common_lines + .first() + .ok_or(eyre!("Didn't find a CO2 scrubber rating"))?, + 2, + )?; + + Ok(oxygen_generator_rating * co2_scrubber_rating) +} diff --git a/source/main.rs b/source/main.rs index 6313b77..b263835 100644 --- a/source/main.rs +++ b/source/main.rs @@ -1,5 +1,6 @@ mod day_01; mod day_02; +mod day_03; fn main() -> color_eyre::Result<()> { color_eyre::install()?; @@ -7,6 +8,7 @@ fn main() -> color_eyre::Result<()> { println!("Advent of Code 2021"); day_01::solve()?; day_02::solve()?; + day_03::solve()?; Ok(()) }