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.
frommathimportprodfrompathlibimportPathimportunittestdefget_input():return(Path(__file__).parent/'day_03_input.txt').read_text()classGrid():def__init__(self,input):self.grid=self.parse_input(input)self.position={'x':0,'y':0}@classmethoddefparse_input(self,input):return[[charforcharinline.strip()]forlineininput.splitlines()]@propertydefgrid_height(self):returnlen(self.grid)@propertydefgrid_width(self):"""Return width of slope Assumes all rows have the same width """returnlen(self.grid[0])@propertydefx(self):"""Return effective x position Accounts for arboreal genetics and biome stability """returnself.position['x']%self.grid_widthdefpast_bottom(self):ifself.position['y']>(self.grid_height-1):returnTrueelse:returnFalsedefslope(self,delta_x,delta_y):obstacles=[]whilenotself.past_bottom():obstacles.append(self.grid[self.position['y']][self.x])self.position['y']+=delta_yself.position['x']+=delta_xreturnobstaclesdefpart_one(input):returnGrid(input).slope(3,1).count('#')defpart_two(input):slope_results=[]slopes_to_check=[[1,1],[3,1],[5,1],[7,1],[1,2]]forslopeinslopes_to_check:grid=Grid(input)slope_results.append(grid.slope(slope[0],slope[1]).count('#'))returnprod(slope_results)classTestExamples(unittest.TestCase):defsetUp(self):self.example_grid="""..##....... #...#...#.. .#....#..#. ..#.#...#.# .#...##..#. ..#.##..... .#.#.#....# .#........# #.##...#... #...##....# .#..#...#.#"""deftest_part_one(self):self.assertEqual(part_one(self.example_grid),7)deftest_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())}')