advent_of_code_2021

My attempts to work through the 2021 Advent of Code problems.
git clone https://git.eamoncaddigan.net/advent_of_code_2021.git
Log | Files | Refs | README | LICENSE

commit 993f9555566d97b99f9853aa741fb62f494dc9a1
parent 2a907e61f669b35fe9260f01175bc8e4b8ed8c46
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date:   Wed,  8 Dec 2021 14:34:48 -0500

(Finally implemented a) solution to day 8, part 1

Diffstat:
Mday08_part1.py | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 74 insertions(+), 1 deletion(-)

diff --git a/day08_part1.py b/day08_part1.py @@ -26,12 +26,16 @@ displays""" # each line of puzzle input! But... # I'm not going to implement it yet, because "good programmers are lazy", and # maybe part 2 will throw me for a loop? Regardless, figuring this out has -# influenced my decision about how to represent the data; i.e., we're doing a +# influenced my decision about how to represent the data; e.g., we're doing a # lot of set operations, so I'm going to use sets for everything, and since # it's easy to imagine wanting to use these sets of lit segments as dictionary # keys, we'll use immutable `frozenset`s specifically (which can be `dict` # keys). + +import pandas as pd +from utils import get_puzzle_input, convert_lines_to_series + EXAMPLE_INPUT = \ """be cfbegad cbdgef fgaecd cgeb fdcge agebfd fecdb fabcd edb | fdgacbe cefdb cefbgd gcbe edbfga begcd cbg gc gcadebf fbgde acbgfd abcde gfcbed gfec | fcgedb cgb dgebacf gc @@ -44,3 +48,72 @@ bdfegc cbegaf gecbf dfcage bdacg ed bedf ced adcbefg gebcd | ed bcgafe cdgba cbg egadfb cdbfeg cegd fecab cgb gbdefca cg fgcdab egfdb bfceg | gbdfcae bgc cg cgb gcafb gcf dcaebfg ecagb gf abcdeg gaef cafbge fdbac fegbdc | fgae cfgab fg bagce """ + +def convert_input_to_df(input_string): + """Convert the puzzle input to a five-column df: the first a string of + unique segment sets, and the next four `frozenset` objects representing the + display digits""" + display_df= ( + convert_lines_to_series(input_string) + .str.rsplit(' ', 4, expand=True) + .rename(columns=dict(enumerate(('segments', 'digit_1', 'digit_2', + 'digit_3', 'digit_4')))) + ) + display_df.loc[:, 'digit_1':'digit_4'] = ( + display_df.loc[:, 'digit_1':'digit_4'].applymap(frozenset) + ) + return display_df + +def create_mapper_from_segments(segment_sequence): + """Given the observed sequence of segment sets (which isn't actually used + for anything here), return a function that maps a `frozenset` of segments + to a digit""" + # Another example of getting ready for what part 2 might look like: this is + # more complicated than necessary but if we do need to decode every line + # then this function is one of the few that would need to be changed. + def part_1_mapper(segments): + """This is the simple mapping function that we need for part 1, that + identifies the digits '1', '4', '7', and '8' based on the number of + segments that are lit. Returns the matching digit as a string or `None` + if its not one of these""" + return ( + {2: '1', 3: '7', 4: '4', 7: '8'} + .get(len(segments), None) + ) + return part_1_mapper + +def apply_mappers_to_digits(display_df, mapper_creator): + """Given a df that contains the unique segment sequence and four columns of + digits, use the `mapper_creator` function to make a mapping function for + each row of segment sequences and apply that function to the digits from + the same row, then return a df with the digits""" + # I don't love how this is coded, not one bit + digits_dict = {} + for col in [f"digit_{i}" for i in range(1, 5)]: + digits_dict[col] = [f(x) for f, x in \ + zip(display_df['segments'].map(mapper_creator), + display_df[col])] + return pd.DataFrame(digits_dict) + +def count_visible_digits(digits_df): + """Given a four-column df of digit displays, return the number of 'visible' + digits (one of '1', '4', '7', or '8') present""" + return int(digits_df.count().sum()) + +def solve_puzzle(input_string): + """Return the numeric solution to the puzzle""" + return count_visible_digits( + apply_mappers_to_digits( + convert_input_to_df(input_string), + create_mapper_from_segments + ) + ) + +def main(): + """Run when the file is called as a script""" + assert solve_puzzle(EXAMPLE_INPUT) == 26 + print("Number of easily decoded digits:", + solve_puzzle(get_puzzle_input(8))) + +if __name__ == "__main__": + main()