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 f567407457ff31632cd8475b6a414d19ccafcc6f
parent 6d614d6d159f60c4698556c9f413e417973c07a1
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date:   Fri, 10 Dec 2021 11:39:20 -0500

Added type hints!

Diffstat:
Mday10_part1.py | 17+++++++++--------
Mday10_part2.py | 17+++++++++--------
Mutils.py | 10++++++----
3 files changed, 24 insertions(+), 20 deletions(-)

diff --git a/day10_part1.py b/day10_part1.py @@ -5,6 +5,7 @@ For part 1, we are finding 'corrupted' lines from the navigation subsystem # I'm not going to bother trying to use pandas for this one. +from typing import List from utils import get_puzzle_input EXAMPLE_INPUT = \ @@ -20,30 +21,30 @@ EXAMPLE_INPUT = \ <{([{{}}[<[[[<>{}]]]>[]] """ -def convert_input_to_list(input_string): +def convert_input_to_list(input_string: str) -> List[str]: """Convert the puzzle input to a list of strings""" return input_string.rstrip('\n').split('\n') -def is_open_bracket(bracket): +def is_open_bracket(bracket: str) -> bool: """Returns True iff the character is an open bracket""" return bracket in '([{<' -def match_bracket(bracket): +def match_bracket(bracket: str) -> str: """Given an open bracket, return the matching close bracket""" return {'(': ')', '[': ']', '{': '}', '<': '>'}[bracket] -def score_bracket(bracket): +def score_bracket(bracket: str) -> int: """Given a close bracket, return its associated score""" return {')': 3, ']': 57, '}': 1197, '>': 25137}[bracket] -def score_line(line): +def score_line(line: str) -> int: """Return the 'score' of a corrupted line, or 0 for lines that are not corrupt""" # Going with a straightforward solution here: using a stack, pushing open # brackets and popping when we find close brackets. If we have the wrong # close bracket, return a score for the bracket we have, otherwise we # return 0. - bracket_stack = [] + bracket_stack: List[str] = [] for bracket in line: if is_open_bracket(bracket): bracket_stack.append(bracket) @@ -52,11 +53,11 @@ def score_line(line): return score_bracket(bracket) return 0 -def solve_puzzle(input_string): +def solve_puzzle(input_string: str) -> int: """Return the numeric solution to the puzzle""" return sum([score_line(l) for l in convert_input_to_list(input_string)]) -def main(): +def main() -> None: """Run when the file is called as a script""" assert solve_puzzle(EXAMPLE_INPUT) == 26397 print("Score for corrupted lines:", diff --git a/day10_part2.py b/day10_part2.py @@ -3,17 +3,18 @@ For part 2, we are completing the 'incomplete' lines and finding the score associated with their completion.""" +from typing import List from day10_part1 import (EXAMPLE_INPUT, convert_input_to_list, is_open_bracket, match_bracket) from utils import get_puzzle_input -def score_bracket(bracket): +def score_bracket(bracket: str) -> int: """Given a close bracket, return its associated score""" return {')': 1, ']': 2, '}': 3, '>': 4}[bracket] -def score_line(line): +def score_line(line: str) -> int: """Return the 'score' of an incomplete line, or 0 for lines that are not incomplete or are corrupt""" # The problem description text implies that a line must be incomplete or @@ -21,8 +22,8 @@ def score_line(line): # I'm going to return a score of 0 for any line that is neither incomplete # nor corrupt (even though there are none in the example input and probably # none in my puzzle input). Just using a loop and stack again. - bracket_stack = [] - line_score = 0 + bracket_stack: List[str] = [] + line_score: int = 0 # line_score = line_score * 5 + score_bracket(bracket) for bracket in line: if is_open_bracket(bracket): @@ -43,23 +44,23 @@ def score_line(line): return line_score -def filter_zeros(scores): +def filter_zeros(scores: List[int]) -> List[int]: """Remove zeros from the list of scores""" return [s for s in scores if s != 0] -def middle_score(scores): +def middle_score(scores: List[int]) -> int: """Sort and return the middle item of the list""" # I could use `median` from numpy, but since the puzzle prompt promises an # odd number of lines, it's easy to implement ourselves return sorted(scores)[len(scores)//2] -def solve_puzzle(input_string): +def solve_puzzle(input_string: str) -> int: """Return the numeric solution to the puzzle""" return middle_score( filter_zeros([score_line(l) for l in convert_input_to_list(input_string)]) ) -def main(): +def main() -> None: """Run when the file is called as a script""" assert solve_puzzle(EXAMPLE_INPUT) == 288957 print("Middle score of incomplete lines:", diff --git a/utils.py b/utils.py @@ -1,21 +1,22 @@ """A collection of utilities that should be useful for more than one puzzle.""" import os.path +from typing import Any import requests import numpy as np import pandas as pd -def convert_lines_to_series(input_string): +def convert_lines_to_series(input_string: str) -> pd.Series: """Return a pandas Series consisting of the lines in the (newline-delimited) input string""" return pd.Series(input_string.rstrip('\n').split('\n')) -def convert_int_line_to_series(input_string): +def convert_int_line_to_series(input_string: str) -> pd.Series: """Converts one (optionally newline-terminated) string of comma-separated integers to a pandas Series""" return pd.Series(input_string.rstrip().split(',')).astype(np.int64) -def get_puzzle_input(day_number, binary=False): +def get_puzzle_input(day_number: int, binary: bool = False) -> Any: """Downloads and returns the input data for the puzzle on a given day. Note: requires a 'session' cookie string to be stored in '~/.config/advent_of_code/session' and will raise an error if it's not @@ -23,7 +24,8 @@ def get_puzzle_input(day_number, binary=False): if binary: raise RuntimeError('binary file output not supported yet') - with open(os.path.expanduser('~/.config/advent_of_code/session'), 'rt') \ + with open(os.path.expanduser('~/.config/advent_of_code/session'), + 'rt', encoding='utf8') \ as f_in: session = f_in.readline().strip()