Advent of Code: Day 4

This is earlier than I expected but we're already at the point where I no longer know if I'm making a great solution... but it works, and in less than a second, and I'll take that as a win for now.

Link to problem: https://adventofcode.com/2024/day/4

import numpy as np
grid = []
with open("input.txt") as file:
for line in file:
grid.append(line.strip())
grid = np.array(grid)
rows = len(grid)
cols = len(grid[0])

I just wanted to make myself a getLetter function, mostly just to take away the code checking for out of bounds errors from my actual solution code... no real reason to do this though.

def getLetter(i, j) -> str:
if i < 0 or i >= rows or j < 0 or j >= cols:
return '-'
return grid[i][j]

Then... I initially started of by writing code to like-- have a conditional for each direction. Stopped within 10 seconds, I hate writing out a lot of repetitive code, and like to try and generalize. Then I realized that since we're essentially just checking strings going out in 8 directions, like the ones on a compass, I could probably just create an array to iterate through those 8 directions somehow. I'm not going to lie, my instincts did not lead me to the most efficient solution.

I wanted to represent each direction as an offset from i and j (and then this would be repeated since the string only goes in that direction.

import itertools as it
directions = set(it.permutations([0, 1, -1, 1, -1], 2))

That works :)

Now here's the probably silly inefficient part:

def check(i, j) -> bool:
compass = list(map(lambda p : [True, p[0], p[1], i, j], directions))

for letter in "MAS":
for direction in compass:
if not direction[0]:
continue
direction[3] += direction[1]
direction[4] += direction[2]
if getLetter(direction[3], direction[4]) != letter:
direction[0] = False
return sum(map(lambda t : 1 if t[0] else 0, compass))

I think the compass is silly in terms of memory usage + the unnecessary time it would take to create it each time... but then again, I was doing this in class, and was just going by my instincts-- so I'll leave it there for now. I'll do better when I come back to do this in a different language at some point.

count = 0
for i in range(len(grid)):
for j in range(len(grid[i])):
if grid[i][j] == 'X':
count += check(i, j)

That's part 1. Let me know if there's anything inefficient at a high level? I don't think there is... just some silly choices along the way. Also I realized that Numpy has a diagonals feature after this, probably would not have done all this if I knew that.

Part 2 seems to be... a simpler version of the same problem. Here, I did decide to just use the conditionals, since it's just two.

def check2(i, j) -> bool:
other = {
'S' : 'M',
'M' : 'S'
}
if not getLetter(i + 1, j + 1) == other.get(getLetter(i - 1, j - 1)):
return False
if not getLetter(i + 1, j - 1) == other.get(getLetter(i - 1, j + 1)):
return False
return True

I do, at some point, want to explore using convolutions for these problems. I don't think they'd work out to be any faster asymptotically, but it'd be cool to try out!

count = 0
for i in range(len(grid)):
for j in range(len(grid[i])):
if grid[i][j] == 'A':
if check2(i, j):
count += 1
print(count)



Comments