diff --git a/2021/day3/src/diagnostics.rs b/2021/day3/src/diagnostics.rs index b5c639d..ffbbb98 100644 --- a/2021/day3/src/diagnostics.rs +++ b/2021/day3/src/diagnostics.rs @@ -1,28 +1,9 @@ pub fn diagnostics_report(readings: &[&str]) -> (isize, isize) { - let reading_length = if readings.is_empty() { - 0 - } else { - readings.get(0).unwrap().len() - }; - - let column_bits: Vec = - (0..reading_length).fold(vec![String::new(); reading_length], |mut acc, index| { - readings.iter().for_each(|line| { - acc.get_mut(index) - .unwrap() - .push(line.chars().nth(index).unwrap()); - }); - - acc - }); - let most_common = - column_bits + column_bits(readings) .iter() .fold((String::from("0"), String::from("0")), |acc, column| { - let count_zeroes = column.chars().filter(|char| *char == '0').count(); - - if count_zeroes >= (column.len() / 2) { + if most_common_bit(column, '0') == '0' { (acc.0 + "0", acc.1 + "1") } else { (acc.0 + "1", acc.1 + "0") @@ -31,17 +12,99 @@ pub fn diagnostics_report(readings: &[&str]) -> (isize, isize) { match most_common { (b_gamma, b_epsilon) => { - let gamma = isize::from_str_radix(&b_gamma, 2) - .expect(&format!("Expected a binary input but got: {}", b_gamma)[..]); + let gamma = binary_to_decimal(&b_gamma); - let epsilon = isize::from_str_radix(&b_epsilon, 2) - .expect(&format!("Expected a binary input but got: {}", b_epsilon)[..]); + let epsilon = binary_to_decimal(&b_epsilon); (gamma, epsilon) } } } +pub fn life_support_rating(readings: &[&str]) -> (isize, isize) { + let oxygen_rating = find_by_bit_criteria(readings, true, '1'); + let co2_rating = find_by_bit_criteria(readings, false, '0'); + + (oxygen_rating, co2_rating) +} + +fn binary_to_decimal(binary: &str) -> isize { + isize::from_str_radix(binary, 2) + .expect(&format!("Expected a binary input but got: {}", binary)[..]) +} + +fn find_by_bit_criteria(readings: &[&str], most_common: bool, on_match: char) -> isize { + let readings_length = readings_length(readings); + + let reduced_readings = (0..readings_length).fold(readings.to_owned(), |acc, index| { + let bit_to_match = find_common_bit( + most_common, + column_bits(&acc[..]).get(index).unwrap(), + on_match, + ); + + if acc.len() > 1 { + acc.iter() + .filter(|line| line.chars().nth(index).unwrap() == bit_to_match) + .map(|x| *x) + .collect::>() + } else { + acc + } + }); + + match reduced_readings[..] { + [binary] => binary_to_decimal(binary), + _ => 0, + } +} + +fn readings_length(readings: &[&str]) -> usize { + if readings.is_empty() { + 0 + } else { + readings.get(0).unwrap().len() + } +} + +fn most_common_bit(column: &str, on_match: char) -> char { + find_common_bit(true, column, on_match) +} + +fn find_common_bit(most_common: bool, column: &str, on_match: char) -> char { + let count_zeroes = column.chars().filter(|char| *char == '0').count(); + + if count_zeroes == (column.len() / 2) { + on_match + } else if count_zeroes > (column.len() / 2) { + if most_common { + '0' + } else { + '1' + } + } else { + if most_common { + '1' + } else { + '0' + } + } +} + +fn column_bits(readings: &[&str]) -> Vec { + let readings_length = readings_length(readings); + + (0..readings_length).fold(vec![String::new(); readings_length], |mut acc, index| { + readings.iter().for_each(|line| { + acc.get_mut(index) + .unwrap() + .push(line.chars().nth(index).unwrap()); + }); + + acc + }) +} + #[cfg(test)] mod tests { @@ -67,4 +130,25 @@ mod tests { assert_eq!(actual_report, (22, 9)) } + + #[test] + fn empty_life_support_rating() { + let readings = vec![]; + + let actual_ratings = life_support_rating(&readings); + + assert_eq!(actual_ratings, (0, 0)) + } + + #[test] + fn sample_life_support_rating() { + let readings = vec![ + "00100", "11110", "10110", "10111", "10101", "01111", "00111", "11100", "10000", + "11001", "00010", "01010", + ]; + + let actual_ratings = life_support_rating(&readings); + + assert_eq!(actual_ratings, (23, 10)) + } } diff --git a/2021/day3/src/main.rs b/2021/day3/src/main.rs index 7a1e2d1..b130174 100644 --- a/2021/day3/src/main.rs +++ b/2021/day3/src/main.rs @@ -12,6 +12,8 @@ fn main() { .collect(); let (gamma, epsilon) = diagnostics::diagnostics_report(&readings); - println!("gamma = {}, epsilon = {}", gamma, epsilon); + + let (oxygen, co2) = diagnostics::life_support_rating(&readings); + println!("oxygen = {}, co2 = {}", oxygen, co2); }