173 lines
4.3 KiB
Rust
173 lines
4.3 KiB
Rust
|
use crate::Day;
|
||
|
|
||
|
pub struct Day03(Vec<Vec<bool>>);
|
||
|
|
||
|
impl Day for Day03 {
|
||
|
fn init(content: String) -> Self {
|
||
|
let v = content.lines().map(split_lines).collect();
|
||
|
Self(v)
|
||
|
}
|
||
|
|
||
|
fn part1(&self) -> String {
|
||
|
format!("{}", self.gamma() * self.epsilon())
|
||
|
}
|
||
|
|
||
|
fn part2(&self) -> String {
|
||
|
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 std::fs::read_to_string;
|
||
|
|
||
|
use crate::Day03;
|
||
|
use crate::day::Day;
|
||
|
|
||
|
const INPUT: &str = r"00100
|
||
|
11110
|
||
|
10110
|
||
|
10111
|
||
|
10101
|
||
|
01111
|
||
|
00111
|
||
|
11100
|
||
|
10000
|
||
|
11001
|
||
|
00010
|
||
|
01010";
|
||
|
|
||
|
#[test]
|
||
|
fn part1_test() {
|
||
|
let t = Day03::init(String::from(INPUT));
|
||
|
let p1 = t.part1();
|
||
|
assert_eq!("198", p1)
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn part2_test() {
|
||
|
let t = Day03::init(String::from(INPUT));
|
||
|
let p2 = t.part2();
|
||
|
assert_eq!("230", p2)
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn part1_real() {
|
||
|
let f = read_to_string("./input/day03").expect("Could not load input");
|
||
|
let t = Day03::init(f);
|
||
|
let p1 = t.part1();
|
||
|
assert_eq!("4118544", p1)
|
||
|
}
|
||
|
|
||
|
#[test]
|
||
|
fn part2_real() {
|
||
|
let f = read_to_string("./input/day03").expect("Could not load input");
|
||
|
let t = Day03::init(f);
|
||
|
let p2 = t.part2();
|
||
|
assert_eq!("3832770", p2)
|
||
|
}
|
||
|
}
|