Advent of Code’s inputs are usually provided in a standardised format where the input file can be processed line by line. The last few years I’ve used a custom built function that reads the input file and transforms its lines into a usable format. This allows me to focus on solving the puzzle and not worry about how to read the input each morning.
import os
import sys
def read_input(day, map_fn=str, example=False):
"""
Read puzzle input for file `/inputs/day_{day}.txt'
and apply transformer function to each line.
:param int day: number of the day
:param func map_fn: a function that is run for each line in the input
:param boolean example: if True, read example input instead
"""
try:
if example:
filename = f'day_{day}_example.txt'
else:
filename = f'day_{day}.txt'
with open(os.path.join('..', 'inputs', filename)) as input_file:
return [map_fn(line.strip()) for line in input_file]
except FileNotFoundError as e:
print(e)
sys.exit(1)
My read_input
function takes in two three arguments:
day
is the number of the day which is used to define which input file is read. A side effect of this is that it documents at the start of the code file which day’s solution it is.map_fn
is a function (defaulting tostr
which is effectively a no-op) that accepts a line of input as a string and returns a desired data format.example
is a flag that allows me to easily switch between reading the provided example input and the actual puzzle input.
For example, if the input for day 1 is a list of numbers per line like this:
10
15
20
22
15
17
23
I would read it in as
from utils import read_input
numbers = read_input(1, int)
print(numbers)
# [10, 15, 20, 22, 15, 17, 23]
I can use built-in functions or write my own as long. I often use named tuples as my data structure in these puzzles so for a more complex example, I could do the following:
a,5
b,7
c,1
d,5
from utils import read_input
from collections import namedtuple
Entry = namedtuple('Entry', ['id', 'value'])
def map_fn(line):
id, value = line.split(',')
return Entry(id=id, value=value)
entries = read_input(2, map_fn)
print(entries)
# [Entry(id='a', value='5'),
# Entry(id='b', value='7'),
# Entry(id='c', value='1'),
# Entry(id='d', value='5')]
Writing these input mapper functions as the first thing of the day gives me structure in designing how to map the input and puzzle description into a data structure that would work best. I often go back and change things around as I learn more about the solution once I actually start writing the code.