Advent of Code 2020 day 3
My Python solution for Advent of Code 2020 day 3. Nested lists and row/column coordinates always make my brain hurt.
I took a gamble for part one and decided not to build the repeating pattern when needed, instead using the mod/wraparound approach. Got lucky that part two didn’t build on this component of the puzzle!
Grid
could do with a little love, e.g. why does it have an ‘x’ getter but not a ‘y’ getter?
Ooh one other change I made is to the way I parse the puzzle input. In past years (and in this years previous challenges) I’ve usually had a get_input
function that reads the input file and parses it. That has meant that my unit tests - with their hardcoded “example” inputs - have had to use pre-parsed inputs (or I’ve had to parse the example inputs manually before sticking it in the unit test).
I changed that today so that the Grid
objects themselves parse raw input, and I can therefore feed them either text files or sample strings, which in turn means the parsing itself is now getting tested. Much better - will continue doing that.
from math import prod
from pathlib import Path
import unittest
def get_input():
return (Path(__file__).parent / 'day_03_input.txt').read_text()
class Grid():
def __init__(self, input):
self.grid = self.parse_input(input)
self.position = {'x': 0, 'y': 0}
@classmethod
def parse_input(self, input):
return [ [ char for char in line.strip() ] for line in input.splitlines() ]
@property
def grid_height(self):
return len(self.grid)
@property
def grid_width(self):
"""Return width of slope
Assumes all rows have the same width
"""
return len(self.grid[0])
@property
def x(self):
"""Return effective x position
Accounts for arboreal genetics and biome stability
"""
return self.position['x'] % self.grid_width
def past_bottom(self):
if self.position['y'] > ( self.grid_height - 1 ):
return True
else:
return False
def slope(self, delta_x, delta_y):
obstacles = []
while not self.past_bottom():
obstacles.append(self.grid[self.position['y']][self.x])
self.position['y'] += delta_y
self.position['x'] += delta_x
return obstacles
def part_one(input):
return Grid(input).slope(3,1).count('#')
def part_two(input):
slope_results = []
slopes_to_check = [[1,1],[3,1],[5,1],[7,1],[1,2]]
for slope in slopes_to_check:
grid = Grid(input)
slope_results.append(grid.slope(slope[0], slope[1]).count('#'))
return prod(slope_results)
class TestExamples(unittest.TestCase):
def setUp(self):
self.example_grid = """..##.......
#...#...#..
.#....#..#.
..#.#...#.#
.#...##..#.
..#.##.....
.#.#.#....#
.#........#
#.##...#...
#...##....#
.#..#...#.#"""
def test_part_one(self):
self.assertEqual(part_one(self.example_grid), 7)
def test_part_two(self):
self.assertEqual(part_two(self.example_grid), 336)
if __name__ == '__main__':
print(f'Part one: {part_one(get_input())}')
print(f'Part two: {part_two(get_input())}')