2024

Advent of Code 2024 – Day 05 – Print Queue

Well, it’s an easy and hard-ish day.  I kind of hate the part 2s on these because they are almost always annoying.  It was a bit more interesting on the input because it was a two part input.  I’m still getting weird empty spaces at the end too, which I have just been trimming with a .pop(), but that’s a bit sloppy.  Today’s puzzle is verifying sorting of sets of numbers.

Part 1 was pretty simple, mostly because I forced myself not to “overthink it”.  I just, compared every pair set to the valid list.  Then if they were valid, added them into the total.  It feels like it’s going to miss some things, but it didn’t.  I got the correct answer in one try.

Part 2 was a pain.  It was, “take the wrong answers and fix them”.  What I did initially was just, swap the numbers if they were wrong.  This would put them one step closer to correct, afterwards, it fed them back through the check, this time flagged as bad, since the two sets need to be answered separately.  This code worked for the sample input for both parts. 

When I ran it through my actual data, this resulted in an answer of “5692”, which was incorrect.  Everything seemed correct in the code.  I even created a copy of the sorting loop with a “total3”, this time just running through bad pages.  It matched the answer of 5692.  So I started trying to guess a bit to see if I was even close I was within 100 for sure.  I went off to Reddit and pulled someone else’s code, ran my data set through it, and got the answer “5770”, which is the correct answer.

It also gave me a direction on where to look.  I tried a different sort where I just, slapped the first number at the end and sorted it back through.  It returned the same 5692.  Which would be a good sign, that both sorts were getting the same results, if the answer was correct.  On a bit of a lark, I decided to see just how off I was, 5770-5692 is 78.  In the data, on the last line, is a 78.  This was the key to solving the problem.  I uncommented some of my print statements and, sure enough, it wasn’t running the last line.

You know that .pop() I mentioned back at the start of this?  Well, it turns out that the sample data had an extra blank.  My actual data, did not, so it was just, dropping the last line of data.

Remember when I said it was a bad idea?

with open("Day05Input.txt") as file:
    data = file.read()


rules = data.split("\n\n")[0].split("\n")
pages = data.split("\n\n")[1].split("\n")
#pages.pop()
bad_pages = []

#Verify Inputs
#for each in pages:
#for each in rules:
#  print(each)

total = 0
total2 = 0
total3 = 0

for each in pages:
  these_pages = each.split(",")
  goodset = True
  running = True
#  print(these_pages)
  while running:
#    print(these_pages)
    running = False
    restart = False 
    for p1 in these_pages:
      for p2 in these_pages[these_pages.index(p1):]:
        if p1 != p2:
          pair = f"{p1}|{p2}"
          if pair in rules:
            #print("Good")
            pass
          else:
#            print(these_pages.index(p1))
            p1pos = these_pages.index(p1)
            p2pos = these_pages.index(p2)
            these_pages[p1pos] = p2
            these_pages[p2pos] = p1
#            print(these_pages.index(p1))
            #print("Bad")
            #print(pair)
            running = True
            goodset = False
 #           print("---")
 #           print(these_pages)
  if goodset:
    #print(f"Set {pages.index(each)} is good")
    total += int(these_pages[int((len(these_pages) - 1)/2)])
  else:
    bad_pages.append(these_pages)
    total2 += int(these_pages[int((len(these_pages) - 1)/2)])



for these_pages in bad_pages:
  goodset = True
  running = True
#  print(these_pages)
  while running:
#    print(these_pages)
    running = False
    restart = False 
    for p1 in these_pages:
      for p2 in these_pages[these_pages.index(p1):]:
        if p1 != p2:
          pair = f"{p1}|{p2}"
          if pair in rules:
            #print("Good")
            pass
          else:
            these_pages.append(these_pages.pop(p1))
#            print(these_pages.index(p1))
#            print(these_pages.index(p1))
            #print("Bad")
            #print(pair)
            running = True
            goodset = False
 #           print("---")
 #           print(these_pages)
  total3 += int(these_pages[int((len(these_pages) - 1)/2)])




print(total)
print(total2)
# Part 1 - 7365
#5692 Low
#5700 Low
#5800 Too High
#5750 - Wrong
#5730 - Wrong
print(total3)
## Its not working but the answer for future reference is 5770

Advent of Code 2024 – Part 04 – Ceres Search

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

Advent of Code 2024 – Day 03 – Mull it Over

This was so much easier than yesterday, since I wasn’t splitting my focus between trying to be clever and trying to write shitty code.

Day 03’s problem involved digging through a block of “corrupted code” to pull out important and usable data.  I ended up forcing myself to use the logical method here, and built a Regex string.  Despite that I write a lot of simple scripts to process and convert data for automation, I don’t really use Regex ever.  It’s been something I keep telling myself to do and use and learn better.  A lot of these challenges are actually greatly helped if you use Regex searches.

So that’s what I did.

I found this online Regex builder/tester, stuck the sample code into it, and went to work until I got what I intended out of it.  I learned two things. 

  • Adding a * to “find a digit of any size” only sometimes works, and in this case, using “+” was better.   
  • You can search on multiple strings using a pipe ( | )

Once I had the Regex working and I extracted all of the valid “mul(###,###)” instances, I used a second Regex search, mostly for practice, to extract the digits from each.

Then part two added an on off trigger.  Which was fairly simple to add in, except that I left my first Regex statement in the code, for posterity, but forgot to actually use the new one, so I was getting the same answer twice.

Anyway, here is the code.

import re
# https://www.w3schools.com/python/python_regex.asp
# https://regex101.com/

with open("Day03Input.txt") as file:
    data = file.read()

rxstring = "mul\(\d+,\d+\)"
rxstring2 = "mul\(\d+,\d+\)|do\(\)|don't\(\)"
total = 0
total2 = 0
enabler = True

def multiplier(a,b):
   return int(a)*int(b)

valid = re.findall(rxstring2,data)

for each in valid:
#   print(each)
   if "don't()" in each:
     enabler = False
   elif "do()" in each:
     enabler = True
   else:
     digits = re.findall('\d+',each)
     value = multiplier(digits[0],digits[1])
     total += value
     if enabler:
       total2 += value

print(total)
print(total2)

#Part1 - 179571322
#Part2 - 103811193

Advent of Code 2024 – Day 02 – Red-Nosed Reports

This entire Post is one big code block, because I did a stupid thing that felt like a good idea at the time.

#Lets comment the heck out of this one and flow with the process.  So it may look a little ugly.

# I am essentially just... blogging in real time, in the comments now....

#Until the end, I am using the sample data set
# 7 6 4 2 1
# 1 2 7 8 9
# 9 7 6 2 1
# 1 3 2 4 5
# 8 6 4 4 1
# 1 3 6 7 9

# I started by copying Day01, then modifying the top lines to read my Day02Input.txt file, because, lazy.
# Then just deleting everything else below the set processing, because none of it is real useful.
with open("Day02Input.txt") as file:
    data = file.read()

sets = [each.split(" ") for each in data.split("\n")]

# Then I want to test it so I add a print of the result, just to make sure it looks right against the sample data set. 
##### NOTE Removed code will just be commented out, to follow the comments.  I'll mark these as ##
# Tehre will be a lot of prints removed.  These are used gratuitously to verify things are working.

##print(sets)

# This gives
# [['7', '6', '4', '2', '1'], ['1', '2', '7', '8', '9'], ['9', '7', '6', '2', '1'], ['1', '3', '2', '4', '5'], ['8', '6', '4', '4', '1'], ['1', '3', '6', '7', '9'], ['']]
# For some reason I keep getting black sets on these, so I'm just gonna do like day one and clip it.
sets.pop()

##print(sets)

# Part of the test on this one is if the digits are ascending or descending, I have an idea to test this out quickly
# Lets see if it works.

##for each in sets:
##   print(each)
##   if each == each.sort():
##      print("Ascending")
##   print(each.sort())
# Ok, I ran into a prolem here, because it's reading everything in as strings and not integers.

# There is probably a more elegant way to do this but this will work.
int_sets = []

for each in sets:
   int_sets.append(list(map(int, each)))

## print(int_sets)

# [[7, 6, 4, 2, 1], [1, 2, 7, 8, 9], [9, 7, 6, 2, 1], [1, 3, 2, 4, 5], [8, 6, 4, 4, 1], [1, 3, 6, 7, 9]]
# Perfect, let's try again.
# Also it looks like I needed to use a different sort, for reasons.
# https://stackoverflow.com/questions/403421/how-do-i-sort-a-list-of-objects-based-on-an-attribute-of-the-objects

##for each in int_sets:
##   if each == sorted(each):
##      print("Ascending")
##   else:
##      print("Bad")

# Bad
# Ascending
# Bad
# Bad
# Bad
# Ascending

# Ok, but I also need Descending Lists

##for each in int_sets:
##   if each == sorted(each):
##      print("Ascending")
##   elif each == sorted(each, reverse=True):
##      print("Descending")
##   else:
##      print("Bad")

# Descending
# Ascending
# Descending
# Bad
# Descending
# Ascending

# Ok, things are working well, I probably need a counter for the number of good lists though.
# Despite these tess and comments, I am still "at the top" of the pure code so.

good_lists = 0

# I also need to check for the other condition in the test
# Any two adjacent levels differ by at least one and at most three.

# Since I am still "at the top", I will go ahead and just make a function to check this here.
# Note to self, post a clean version of the code below.

def check_range(a,b):
##  print(f"{a},{b}")
##  print(abs(b-a))
##  if abs(b-a) >= 3 and abs(b-a) <= 1:
  if abs(b-a) <= 3 and abs(b-a) >= 1:
##    print("OK")
    return True
  return False

# I am also going to clean up my ascending/Descending loop a bit.

##for each in int_sets:
##   if each == sorted(each) or each == sorted(each, reverse=True):
##      for i in range(len(each)-1):
##         print(i)
##         print(f"{each[i]},{each[i+1]}")
##         check_range(each[i], each[i+1])
##      print("Good")
##   else:
##      print("Bad")

# Something is not working right, so I'm modifying the defined function above a bit and adding some prints to test.

# I found it, my Greater than less than signs were reversed, oops.
# Also, all this commenting is seriously slowing this whole process

# I'm copying the above loop though, for posterity, before finding a good way to track good loops.

for each in int_sets:
   if each == sorted(each) or each == sorted(each, reverse=True):
# Lets assume a set is true
      good_set = True
      for i in range(len(each)-1):
# If the set is still good, keep checking, otherwise, don't.
         if good_set:
           good_set = check_range(each[i], each[i+1])
# I hate having these conditionals like this, but this is starting to get exhausting typing this, and it's easy.
# If the set is still good after checking the digits, add one to the total number of good sets.
      if good_set:
         good_lists +=1
# I removed the else, because I don't care if it's bad.

print(good_lists)
# This gives 2, as expected, for the sample dataset.
# Time to adjust for my personal data set as input.

# This gives 326 (data sets and answers very by person).
# This is the correct answer, on to Part 2.

# For Part 2, the system can tolorate a single bad level.  Those are what I am checking for in "check_range'.
# Normally I would modify my code to do both tests at once, but for TODAY, i am just going to make a second version.
# Basically, if a bad pair happens, it needs to try matching with the next higher number.  But it only needs to try it once.

good_lists_p2 = 0

##for each in int_sets:
##   if each == sorted(each) or each == sorted(each, reverse=True):
##      print(each)
##      good_set = True
##      dampened = False
##      for i in range(len(each)-1):
##         if good_set:
##           good_set = check_range(each[i], each[i+1])
##           if not good_set and not dampened:
##              print(f"Trying {each[i]} and {each[i+2]}")
##              print(good_set)
##              good_set = check_range(each[i], each[i+2])
##              print(good_set)
##              dampened = True
##      print(good_set)
##      if good_set:
##         good_lists_p2 +=1
##      print(good_lists_p2)

# So, this is not working out.  It's mostly working, but it's failing my Ascending/Descending check.
# Specifically, this one in the sample data fails. "1 3 2 4 5"

# What I need, is a new way to check Ascending and Descending lists.  An annoying way, that is ugly.
# I could build this in to the existing checks, but at this point, I don't want to, I just need a yes/no check
# And all these notes and comments are getting annoying.

def check_order(list):
   if list[-1] > list[0]:
     direction = "Asc"
   elif list[-1] < list[0]:
     direction = "Desc"
   else:
     direction = "Equal"
#   print(direction)

   for n in range(len(list)-1):
      if list[n+1] > list[n] and direction == "Desc":
        if n+2 < len(list):
          if list[n+2] > list[n]:
             return False
      if list[n+1] < list[n] and direction == "Asc":
        if n+2 < len(list):
          if list[n+2] < list[n]:
             return False
   return True

for each in int_sets:
#   print(each)
   if check_order(each):
      good_set = True
      dampened = False
      for i in range(len(each)-1):
         if good_set:
           good_set = check_range(each[i], each[i+1])
           if not good_set and not dampened and i+2 < len(each):
#             print(f"Trying {each[i]} and {each[i+2]}")
#             print(good_set)
              good_set = check_range(each[i], each[i+2])
#              print(good_set)
              dampened = True
#      print(good_set)
      if good_set:
         good_lists_p2 +=1
#      print(good_lists_p2)






print(good_lists_p2)


# 415 too high
# 414 too high
# 400 too high
# Ok, I started Guessing
# I got it
# 381 for my data set.






Weekly Wrap Up – 11.24.2024 – 12.01.2024

When I write these in my local copies, I don’t put the date ranges on them, just the date they were written. Its one of the most pain in the butt parts of this because it means opening some sort of calendar. I mean, its not hard, but I really do not look at calendars a lot. At least not beyond “Day of the week”.

While I am rambling about things I don’t follow, I also don’t really follow the weather. Somehow, despite decades of data, and AI and all the magic pf technology, weather predicting still seems like a complete and utter toss up 90% of the time. I prefer to just, decide when I walk put the door, or over dress with hat and gloves or whatever, that can easily just be discarded.

On to this last week. It was of course, Thanksgiving week. We went down to my mother in law’s for the day, though no real fancy dinner or anything, just sandwiches, and doing various tasks around her house that needed done.

Speaking of Thanksgiving, I lied last week, I caught, part of, the Macy’s parade. It starts earlier than I thought. The antenna hook up went way better than expected. Despite that we sort of sit “downhill” because we are nearish the lake here, and the antenna itself isn’t even above the roof line, I can receive almost all of the channels I should be here. CBS which os very far is a little iffy at time, and PBS, which is probably the farthest, shows up but never locks in. But Fox, ABC, CW, the religeous channel, all tune in with all their subchannels.

The Saturday after Thanksgiving was very busy at the shop, which was nice. The town had a holiday event and it was “Small Business Saturday” so we actually had a regular run of customers. Many first timers and many who really liked the shop. It was a good sign, hopefully.