use crate::Day; use anyhow::Result; pub struct Day03(Vec>); impl Day for Day03 { fn init(content: String) -> Result { let v = content.lines().map(split_lines).collect(); Ok(Self(v)) } fn part1(&self) -> Result { Ok(format!("{}", self.gamma() * self.epsilon())) } fn part2(&self) -> Result { Ok(format!("{}", self.ox() * self.co2())) } } impl Day03 { pub fn gamma_calc(&self) -> Vec { let mut result = Vec::new(); for i in 0..self.0[0].len() { let bit = !matches!(most_common_bit(&self.0.clone(), i), CommonBit::Zero); result.push(bit); } result } pub fn eps_calc(&self) -> Vec { let mut result = Vec::new(); for i in 0..self.0[0].len() { let bit = !matches!(most_common_bit(&self.0.clone(), i), CommonBit::Zero); result.push(!bit); } result } pub fn get_oxygen_generator_rating_binary(&self) -> Vec { let mut working_set = self.0.clone(); for i in 0..self.0[0].len() { let common_bit = !matches!(most_common_bit(&working_set.clone(), i), CommonBit::Zero); working_set = working_set .into_iter() .filter(|x| x[i] == common_bit) .collect(); if working_set.len() == 1 { break; } } working_set[0].clone() } pub fn get_co2_scrubber_rating_binary(&self) -> Vec { let mut working_set = self.0.clone(); for i in 0..self.0[0].len() { let common_bit = matches!(least_common_bit(&working_set.clone(), i), CommonBit::One); working_set = working_set .into_iter() .filter(|x| x[i] == common_bit) .collect(); if working_set.len() == 1 { break; } } working_set[0].clone() } pub fn gamma(&self) -> u32 { convert(&self.gamma_calc()) } pub fn epsilon(&self) -> u32 { convert(&self.eps_calc()) } pub fn ox(&self) -> u32 { convert(&self.get_oxygen_generator_rating_binary()) } pub fn co2(&self) -> u32 { convert(&self.get_co2_scrubber_rating_binary()) } } enum CommonBit { Zero, One, Equal, } fn most_common_bit(set: &[Vec], bit_position: usize) -> CommonBit { let ones_count = set.iter().fold( 0, |ones, code| if code[bit_position] { ones + 1 } else { ones }, ); let len = (set.len() + 1) / 2; match ones_count.cmp(&len) { std::cmp::Ordering::Less => CommonBit::Zero, std::cmp::Ordering::Equal => CommonBit::Equal, std::cmp::Ordering::Greater => CommonBit::One, } } fn least_common_bit(set: &[Vec], bit_position: usize) -> CommonBit { let ones_count = set.iter().fold( 0, |ones, code| if code[bit_position] { ones + 1 } else { ones }, ); let len = (set.len() + 1) / 2; match ones_count.cmp(&len) { std::cmp::Ordering::Less => CommonBit::One, std::cmp::Ordering::Equal => CommonBit::Equal, std::cmp::Ordering::Greater => CommonBit::Zero, } } pub fn convert(binary: &[bool]) -> u32 { binary.iter().fold(0, |result, &bit| (result << 1) ^ bit as u32) } fn split_lines(content: &str) -> Vec { content.chars().map(|x| x == '1').collect() } #[cfg(test)] mod tests { use crate::{Day03, day_tests}; use crate::day::Day; use anyhow::Result; const INPUT: &str = r"00100 11110 10110 10111 10101 01111 00111 11100 10000 11001 00010 01010"; #[test] fn part1_test() -> Result<()>{ let t = Day03::init(String::from(INPUT))?; let p1 = t.part1()?; assert_eq!("198", p1); Ok(()) } #[test] fn part2_test() -> Result<()>{ let t = Day03::init(String::from(INPUT))?; let p2 = t.part2()?; assert_eq!("230", p2); Ok(()) } day_tests!(Day03, "03", "4118544", "3832770"); }