diff --git a/2021/src/day3.rs b/2021/src/day3.rs index 51189e2..1800580 100644 --- a/2021/src/day3.rs +++ b/2021/src/day3.rs @@ -1,41 +1,71 @@ use color_eyre::eyre; -fn most_common_bits(numbers: &Vec) -> u16 { - let mut bit_diffs: [isize; 12] = [0; 12]; - for num in numbers { - for i in 0..11 { - if num & (1 << i) > 0 { - bit_diffs[i] += 1 - } - else { - bit_diffs[i] -= 1 - } - } - } +fn most_common_bit(numbers: &Vec, pos: u8) -> u16 { + // number of times the bit in question is 1, + // minus the number of times it's 0 + let mask: u16 = 1 << pos; + let total: isize = numbers + .iter() + .map(|n| if n & mask > 0 {1} else {-1}) + .sum(); - let mut result = 0u16; - for (i, sum) in bit_diffs.iter().enumerate() { - let bit = if *sum >= 0 {1} else {0}; - result |= bit << i; - } - - result -} - - -fn invert_12bit(n: u16) -> u16 { - !n & (65535 >> 4) // since these are really only 12-bit numbers we have to clear the 4 spurious 1's that come from inverting the original + // if the total is positive, then 1 is more frequent than 0 + if total >= 0 {1} else {0} } fn part1(numbers: &Vec) -> u64 { - let gamma = most_common_bits(numbers); - let epsilon = invert_12bit(gamma); + let mut gamma = 0u16; + for i in 0..12 { + let mask = most_common_bit(numbers, i) << i; + gamma |= mask; + } + + // alternate solution: it feels super cool, but it's not any fewer lines than the above, and way less clear :( + // let gamma = (0..12) + // .fold( + // 0u16, + // |g, i| g | (most_common_bit(numbers, i) << i) + // ); + + // epsilon is just the bit inverse of gamma, but since these are really 12-bit numbers + // we have to clear the 4 spurious 1's that come from inverting the original + let epsilon = !gamma & (65535 >> 4); epsilon as u64 * gamma as u64 } +fn filter_bit_criteria(numbers: Vec, bit_pos: u8, bit_value: u16) -> Vec { + numbers + .into_iter() + .filter(|n| (n >> bit_pos) & 1 == bit_value) + .collect() +} + + +fn part2(numbers: &Vec) -> u64 { + let mut oxy = numbers.clone(); + let mut co2 = numbers.clone(); + for pos in (0..12).rev() { + if oxy.len() > 1 { + let oxy_bit = most_common_bit(&oxy, pos); + oxy = filter_bit_criteria(oxy, pos, oxy_bit); + } + + if co2.len() > 1 { + let co2_bit = most_common_bit(&co2, pos) ^ 1; // flip the rightmost bit, giving us the least common instead + co2 = filter_bit_criteria(co2, pos, co2_bit); + } + } + + // assume both vecs have len == 1 now, since otherwise it means + // AoC gave us an invalid problem input + oxy[0] as u64 * co2[0] as u64 +} + + + pub fn run(data: &str) -> eyre::Result<()> { let mut nums = Vec::new(); for line in data.lines() { @@ -43,5 +73,6 @@ pub fn run(data: &str) -> eyre::Result<()> { } println!("One: {}", part1(&nums)); + println!("Two: {}", part2(&nums)); Ok(()) } \ No newline at end of file