Reference: (https://adventofcode.com/2020/day/3)
Data Preparation
As with the previous days, I will be starting with getting my input into a good format before I’ve even taken a real pass at the problem…
real_run = False
file_name = "day3-input.txt" if real_run else "day3-test.txt"
# create a list from the file, removing any '\n' characters
data = [line.rstrip('\n') for line in open(file_name)]
# print data to check it's what we want it to be
print(data)
['..##.......', '#...#...#..', '.#....#..#.', '..#.#...#.#', '.#...##..#.', '..#.##.....', '.#.#.#....#', '.#........#', '#.##...#...', '#...##....#', '.#..#...#.#']
Part One
Each line of our data is a layer in a toboggan slope… It repeats inifinitely out to either side and ‘#’ are trees and ‘.’ are open spaces.
We have a route given to us (down 1, right 3) and we want to return the amount of trees we are given. If we start from (0,0), we will go down hitting (1,3) (2,6) (3,9) etc.
length = len(data)
for row in range(length):
col = row * 3
item = data[row][col]
print(item)
.
.
#
.
---------------------------------------------------------------------------
IndexError Traceback (most recent call last)
<ipython-input-2-cfdbe1fab67a> in <module>
4 col = row * 3
5
----> 6 item = data[row][col]
7
8 print(item)
IndexError: string index out of range
Since the length of each row is less than 3x the length we will get a string index out of range exception. Since we are starting at a 0 index and we know the length of each row, we can use modulus.
length = len(data)
row_length = len(data[0])
for row in range(length):
col = row * 3 % row_length
item = data[row][col]
print(item)
.
.
#
.
#
#
.
#
#
#
#
Now we make sure to keep track of the count as it increases.
length = len(data)
row_length = len(data[0])
tree_count = 0
for row in range(length):
col = (row * 3) % row_length
item = data[row][col]
if item == '#':
tree_count += 1
print(tree_count)
7
Part Two
Now, we do the same for a group of:
- Right 1, down 1.
- Right 3, down 1. (This is the slope we already checked.)
- Right 5, down 1.
- Right 7, down 1.
- Right 1, down 2.
So we can generalise and pass in a parameter for what the column position. We can address 4/5 requirements by just making this one change!
def traverse_path(path_data, right):
length = len(path_data)
row_length = len(path_data[0])
tree_count = 0
for row in range(length):
col = (row * right) % row_length
item = data[row][col]
if item == '#':
tree_count += 1
return tree_count
# Test with the one we already know...
r3_d1 = traverse_path(data, 3)
print(r3_d1)
7
And to figure the “down” motion of right 1, down 2 we can make some adjustments to the row number and divide the length so we don’t go down beyond the rows.
import math
def traverse_path(path_data, right, down=1):
# Use ceil as to make sure we get the final rows! (due to range taking us up to strictly less than the total)
length = math.ceil(len(path_data) / down)
row_length = len(path_data[0])
tree_count = 0
for row in range(length):
col = (row * right) % row_length
row_num = row * down
item = data[row_num][col]
if item == '#':
tree_count += 1
return tree_count
# Test with the one we already know...
r3_d1 = traverse_path(data, 3)
print(r3_d1)
# and do all the others:
r1_d1 = traverse_path(data, 1)
r5_d1 = traverse_path(data, 5)
r7_d1 = traverse_path(data, 7)
r1_d2 = traverse_path(data, 1, 2)
product = r3_d1 * r1_d1 * r5_d1 * r7_d1 * r1_d2
print(product)
7
336
There we have it…
But perhaps we can go one step further, reduce that repeated code and give the instructions as a dict…
instructions = [
{'right':1, 'down':1},
{'right':3, 'down':1},
{'right':5, 'down':1},
{'right':7, 'down':1},
{'right':1, 'down':2}
]
def prod_trav_paths(instrs, path_data):
results = []
for instr in instrs:
res = traverse_path(path_data, instr['right'], instr['down'])
results.append(res)
return math.prod(results)
prod_trav_paths(instructions, data)
336
Made utilising jupyter notebook