Weee, the first real “pain in the ass” day and it’s only day 4!
Ok, it wasn’t awful, I had the right idea, for the most part. I wanted to do something better for Part 2, but it wasn’t working out, but I’ll get there. For this puzzle, you get a blog of text with the letters of XMAS in them, randomly, scattered. Sometimes it actually spells XMAS, it’s a “crossword” puzzle. The ides is to find all of the occurrences of XMAS, crossword style, front, back, up down, and diagonally. I used Numpy here to do some rotations on the letter matrix, which was fun.
First was a simple search for “XMAS” and “SAMX” on every line of the matrix. Then I looked into how to rotate the matrix, 45 degrees. Thankfully, there was a way. I did it twice, one clockwise and one counter clockwise, which gave me all of the “diagonals” as single lines of texts. I appended this to the normal lines and did the search again. Then I realized, I needed to also search the verticals. This meant, another, different, Numpy rotation. I appended this to the big blob of lines as well, and ran the search again.
This gave the correct answer, easy.
I started out with a good but failed approach on Part 2. Some of the code is still there, commented out, for posterity.
For Part two, the search is modified, you need to find all occurrences of “X-MAS”. That is, places where the letters “MAS” make an “X” shape. They can go forward or backwards, and must occur twice. I started out by making a second copy of my diagonals lines. I wanted to construct these back into “square” shapes. I looked into doing some sort of fancy fill methods, but instead just, did a simple algorithm based on line lengths. The idea was, to search for the As in one rotation block, then check if it’s a MAS or a SAM, then check the matching A on the opposite rotation block of text. These coordinates should be opposites. If point 2,4 is an “A”, then on the opposite rotation, that same A should be at point (1,2).
Except… it’s not. There is some funny doubling going on with one of the coordinates. I spent, more time than I care to admit trying to puzzle it out. I finally gave up.
Instead I did the old fashioned “brute force” method. I took the original block of text, checked each character to see if it’s an A, if it was an A, I built the two cross words, and checked if they were SAM or MAS. This all occurs before modifying the block of text for part 1.
I got stuck a bit on it again, because I made a stupid typo and used “i” instead of “j” when making my check words.
Anyway, here is my code below for Day 4 in Python.
with open("Day04Input.txt") as file:
data = file.read()
import numpy as np
lines = data.split("\n")
lines.pop()
diaglines = []
total = 0
total2 = 0
grid = []
gridsize = len(lines[1])
for each in lines:
grid.append(list(each))
a = np.array(grid)
rotated_a = np.rot90(a)
# Part 2 visa the shitty Brute Force Way
for i in range(len(lines)-1):
for j in range(len(lines[i])-1):
if i != 0 and j != 0:
# print(lines[i][j])
if lines[i][j] == 'A':
check1 = lines[i+1][j+1]+lines[i][j]+lines[i-1][j-1]
check2 = lines[i+1][j-1]+lines[i][j]+lines[i-1][j+1]
if (check1 == "MAS" or check1 == "SAM") and (check2 == "MAS" or check2 == "SAM"):
total2 += 1
# print(check1)
# print(check2)
# print("Match")
else:
print(check1)
print(check2)
print(lines[i-1])
print(lines[i])
print(lines[i+1])
print("------")
# Rotate the array 90 and add the lines to our overall lines
for row in rotated_a:
# print(''.join(row))
lines.append(''.join(row))
# Rotate the array diagonally
# https://stackoverflow.com/questions/6313308/get-all-the-diagonals-in-a-matrix-list-of-lists-in-python
diags = [a[::-1,:].diagonal(i) for i in range(-a.shape[0]+1,a.shape[1])]
diags.extend(a.diagonal(i) for i in range(a.shape[1]-1,-a.shape[0],-1))
#######################################################################################################
## Join the rotated numpy array and add it to our total lines to ge the diagonals
for n in diags:
new_string = f"{''.join(str(i) for i in n.tolist())}"
# print(new_string)
# padding = int((gridsize - len(new_string))/2)
# diag_string = ('o'*padding) + new_string + ('o'*padding)
# if len(diag_string) % 2 == 0:
# diag_string = diag_string[:-1]
lines.append(new_string)
# diaglines.append(diag_string)
#print(diaglines)
# Count up for Part 1
for each in lines:
# print(each)
total+= each.count("XMAS")
total+= each.count("SAMX")
#dlist1 = diaglines[:len(diaglines)//2]
#dlist2 = diaglines[len(diaglines)//2:]
# Print Totals
print(total)
print(total2)
#1771 = Too low
#2003 = Too Low
#Part 1 - 2427
#3085 Too High
#1312 Too Low
#1900 - Part 2
Josh Miller aka “Ramen Junkie”. I write about my various hobbies here. Mostly coding, photography, and music. Sometimes I just write about life in general. I also post sometimes about toy collecting and video games at Lameazoid.com.