AoC2021/src/day03.rs

161 lines
4.0 KiB
Rust

use crate::Day;
use anyhow::Result;
pub struct Day03(Vec<Vec<bool>>);
impl Day for Day03 {
fn init(content: String) -> Result<Self> {
let v = content.lines().map(split_lines).collect();
Ok(Self(v))
}
fn part1(&self) -> Result<String> {
Ok(format!("{}", self.gamma() * self.epsilon()))
}
fn part2(&self) -> Result<String> {
Ok(format!("{}", self.ox() * self.co2()))
}
}
impl Day03 {
pub fn gamma_calc(&self) -> Vec<bool> {
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<bool> {
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<bool> {
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<bool> {
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<bool>], 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<bool>], 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<bool> {
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");
}