diff --git a/src/day3.rs b/src/day3.rs new file mode 100644 index 0000000..62b95c9 --- /dev/null +++ b/src/day3.rs @@ -0,0 +1,135 @@ +/// I thought this would be useful for both parts but I was very wrong +fn bit_count(input: &String) -> (Vec, usize) { + let byte_len = input.lines().next().unwrap().len(); + let mut count = vec![0i64; byte_len]; + + for line in input.lines() { + let mut i = 0; + for c in line.chars() { + if c == '1' { + count[i] += 1; + } else if c == '0' { + count[i] -= 1; + } else { + panic! {"Malformed input!"}; + } + i += 1; + } + } + + (count, byte_len) +} + +/// Returns the product of gamma and epsilon. +/// +/// Gamma is determined by the most common bit values at each position in all of the +/// input. +/// +/// Epsilon is the inverse, determined by the least common bit values. +pub fn part1(input: String) { + let (count, byte_len) = bit_count(&input); + + let mut gamma = 0u64; + let mut epsilon = 0u64; + for i in 0..byte_len { + // ERROR: wrong with even counts + gamma |= ((count[i] > 0) as u64) << (byte_len - (i + 1)); + epsilon |= ((count[i] <= 0) as u64) << (byte_len - (i + 1)); + } + + println!("{}", gamma * epsilon); +} + +/// Counts high/low bits in +fn count_bits(data: &Vec, bit_index: usize, mask: u64, bit_diag: u64) -> (u64, i64) { + let mut bit_prevalance = 0i64; + let mut valid_count = 0u64; + + for byte in data { + //println!("byte: {}", bit_diag ^ (byte & mask)); + if bit_diag ^ (byte & mask) == 0 { + valid_count += 1; + if (byte & (1 << bit_index)) > 0 { + bit_prevalance += 1; + } else { + bit_prevalance -= 1; + } + } + } + + //println!("bit_index: {}, mask: {}, bit_diag: {}, valid_count: {}, bit_prevalance: {}", bit_index, mask, bit_diag, valid_count, bit_prevalance); + (valid_count, bit_prevalance) +} + +/// I *could* document this, but why would I want to? :P +/// AOC itself serves as documentation. +pub fn part2(input: String) { + let byte_len = input.lines().next().unwrap().len(); + let data: Vec = input + .lines() + .map(|l| u64::from_str_radix(l, 2).unwrap()) + .collect(); + + let mut generator_mask = 0u64; + let mut generator_diag = 0u64; + let mut generator_value = None; + let mut scrubber_mask = 0u64; + let mut scrubber_diag = 0u64; + let mut scrubber_value = None; + + // Diagnostic bit sequence will be different for generator and scrubber + // Logic for counting bits will be the same, just need to keep diagnostic bits seperate + for i in 0..byte_len { + let bit_index = byte_len - i - 1; + + // Set diagnostic bit for each piece of data individually + if generator_value.is_none() { + let (valid_count, bit_prevalance) = + count_bits(&data, bit_index, generator_mask, generator_diag); + + // We want to match against the most common bit, which is 1 when prevalance is positive. + // We also want to match on one when they are equal. + if bit_prevalance >= 0 { + generator_diag |= 1 << bit_index; + } + generator_mask |= 1 << bit_index; + + // Only one valid solution, grab it and get out + if valid_count <= 2 || i == byte_len - 1 { + generator_value = Some( + data.iter() + .filter(|d| generator_diag ^ (*d & generator_mask) == 0) + .next() + .unwrap(), + ); + } + } + + if scrubber_value.is_none() { + let (valid_count, bit_prevalance) = + count_bits(&data, bit_index, scrubber_mask, scrubber_diag); + + // We want to match against the least common bit, which is 1 when prevalance is negative. + // When equal we match against 0. + if bit_prevalance < 0 { + scrubber_diag |= 1 << bit_index; + } + scrubber_mask |= 1 << bit_index; + + // Only one valid solution, grab it and get out + if valid_count <= 2 || i == byte_len - 1 { + scrubber_value = Some( + data.iter() + .filter(|d| scrubber_diag ^ (*d & scrubber_mask) == 0) + .next() + .unwrap(), + ) + } + } + } + + let gen = generator_value.unwrap(); + let scrub = scrubber_value.unwrap(); + + println!("{}", gen * scrub); +} diff --git a/src/main.rs b/src/main.rs index 9437bda..65dd063 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use clap::{App, Arg}; mod day1; mod day2; +mod day3; fn main() { let matches = App::new("AOC 2021 Code") @@ -44,6 +45,11 @@ fn main() { 2 => day2::part2(input), _ => (), }, + 3 => match part { + 1 => day3::part1(input), + 2 => day3::part2(input), + _ => (), + }, _ => println!("Day {} not completed yet!", day), } }