commit 5cdf049c8cef9b85f2fe770d010fd681632297f8
parent 319d828636c9f02a9c265fa471de4d3695e701ee
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date: Fri, 3 Dec 2021 15:48:17 -0500
Solution to day 3, part 2
Diffstat:
A | day03_part2.py | | | 67 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 67 insertions(+), 0 deletions(-)
diff --git a/day03_part2.py b/day03_part2.py
@@ -0,0 +1,67 @@
+#!/usr/bin/env python
+"""Get the 'life support rating' by finding the 'oxygen generator rating' and
+'CO2 scrubber tating' from the same stream of binary numbers used for part
+1."""
+
+import functools
+from utils import get_puzzle_input
+from day03_part1 import (convert_input_to_df,
+ split_report_bits,
+ bit_series_to_int)
+
+def filter_by_bit_commonality_at(bits_df, column_label, least_common=False):
+ """Helper function: return the rows for which the given `column_label`
+ has the most common bit (or least common). Ties go to the `1`s for most
+ common (`0` for least common)"""
+ # If we select all rows for which the value of the given column equals
+ # the integer value of the boolean corresponding to the test of whether
+ # twice the sum of the column is greater than or equal to the column
+ # length, we'll follow the rules for selecting the most common bit, and
+ # if we invert that, the least common
+ if len(bits_df) == 1:
+ return bits_df
+ rows_to_get = bits_df.loc[:, column_label] == \
+ int(2 * bits_df.loc[:, column_label].sum() >= bits_df.loc[:, 1].size)
+ if least_common:
+ rows_to_get = ~rows_to_get
+ return bits_df.loc[rows_to_get]
+
+def filter_by_bit_commonality(bits_df, least_common=False):
+ """Filter the df from left to right, selecting only rows that have the most
+ common bit (or least common, if `least_common` == `True`), until only a
+ single row remains, which is returned as a Series"""
+ # Using `reduce` instead of writing a loop prevents us from stopping as
+ # early as possible (with a break) and from checking that we have a unique
+ # solution, but it's just so CUTE I'm sorry
+ return functools.reduce(
+ lambda x, y: filter_by_bit_commonality_at(x, y, least_common),
+ bits_df.columns,
+ bits_df
+ ).iloc[0]
+
+def calculate_oxygen_co2(bits_df):
+ """Given a df find the "oxygen generator rating" and "CO2 scrubber rating"
+ by successively filtering the df by the most (least) common bit from left
+ to right, and return the values as a tuple"""
+ return (bit_series_to_int(filter_by_bit_commonality(bits_df, False)),
+ bit_series_to_int(filter_by_bit_commonality(bits_df, True)))
+
+def calculate_life_support_rating(oxygen_generator_rating, co2_scrubber_rating):
+ """Return the product of the oxygen generator rating and co2 scrubber
+ rating"""
+ return oxygen_generator_rating * co2_scrubber_rating
+
+def solve_puzzle(input_string):
+ """Return the numeric solution to the puzzle"""
+ # 5.47 ms ± 43.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+ return calculate_life_support_rating(
+ *calculate_oxygen_co2(
+ split_report_bits(
+ convert_input_to_df(input_string)
+ )
+ )
+ )
+
+if __name__ == "__main__":
+ print("Life support rating:",
+ solve_puzzle(get_puzzle_input(3)))