commit 49b32ed7252e5edd87e0a4b426787193bcd5442f
parent 621c531824bbfdf326c1c129cd2296fb05be21a7
Author: Eamon Caddigan <eamon.caddigan@gmail.com>
Date: Thu, 2 Dec 2021 12:06:26 -0500
Solution to day 2, part 1
Diffstat:
A | day02_part1.py | | | 72 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 72 insertions(+), 0 deletions(-)
diff --git a/day02_part1.py b/day02_part1.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+"""Day 2, Part 1: we need to parse submarine commands and figure out where the
+submarine winds up. I am very confident that I wrote code to solve this exact
+problem, using Lisp, decades ago in undergrad."""
+
+# Once again I'm going to take a pandas-heavy approach even though it's not
+# strictly necessary here. I'm going to make some tweaks to the coding
+# conventions I used yesterday; I'd like to make it easier to develop different
+# implementations and compare their performance.
+
+from io import StringIO
+import numpy as np
+import pandas as pd
+from utils import get_puzzle_input
+
+def convert_input_to_df(input_string):
+ """Given a string representation of the input data, return a single-column
+ pandas data frame with the column `commands`"""
+ return pd.read_csv(StringIO(input_string),
+ names=('commands',))
+
+def split_command_parts(command_df):
+ """Given a pandas data frame with a column `commands`, returns a new data
+ frame with the columns `direction` and `distance`"""
+ return (
+ command_df['commands']
+ .str.split(n=2, expand=True)
+ .rename(columns={0:'direction', 1:'distance'})
+ .assign(distance=lambda x: pd.to_numeric(x['distance']))
+ )
+
+def convert_commands_to_offsets(command_df):
+ """Given a pandas data frame with the columns `direction` (in "forward",
+ "up", and "down") and `distance`, returns a new data frame with the columns
+ `axis` (either "horizontal" or "vertical") and `offset`"""
+ return pd.DataFrame(
+ {'axis': np.where(command_df['direction'].isin(('up', 'down')),
+ 'vertical', 'horizontal'),
+ 'offset': np.where(command_df['direction'] == 'up',
+ -command_df['distance'], command_df['distance'])}
+ )
+
+def find_total_offsets(offset_df):
+ """Given a pandas data frame with the column `axis` and any others, find
+ the sum of other columns grouped by `axis`. (Note that this doesn't
+ restrict itself to the axes 'horizontal' or 'vertical' and it doesn't check
+ the other column names"""
+ # You know, in retrospect it was maybe confusing to call a column 'axis' in
+ # pandas world. Ah well!
+ return offset_df.groupby('axis').sum()
+
+def calculate_puzzle_solution(axis_summary_df):
+ """Given a pandas data frame with the sum of `offset` values along each
+ `axis`, multiply the 'horizontal' and 'vertical' offsets."""
+ return axis_summary_df.loc['horizontal', 'offset'] * \
+ axis_summary_df.loc['vertical', 'offset']
+
+def solve_puzzle():
+ """Return the numeric solution to the puzzle"""
+ return calculate_puzzle_solution(
+ find_total_offsets(
+ convert_commands_to_offsets(
+ split_command_parts(
+ convert_input_to_df(get_puzzle_input(2))
+ )
+ )
+ )
+ )
+
+if __name__ == "__main__":
+ print("Product of horizontal and vertical offsets:",
+ solve_puzzle())