Python

Advent of Code 2024 – Day 06 – Guard Gallivant

It’s happening already. The slow crushing creep of laziness. I have not done part 2, yet, and I will, maybe get to it. I have ideas for how to solve part 2, its just… Kind of tedious and I want a respite.

Part 1 was fun though. Basically just, a simple pathfinding robot to trace the path of a guard through a maze of obstacles. I even gave it the ability to draw the map, but the dataset is bigger than my monitor so the refresh and drawing it’s stupid ugly with the real data set. I also turned it off because it drags the entire process to a crawl. A seconds-long calculation takes minutes and counting when its drawing nonsense.

The sample dataset looks neat though when drawn.

Part 2 is to find every place you could drop an object to create a loop for the guard. I thought I had it, I had the right idea, but I only ended up finding all of the existing loops. And I am not sure some were not in the middle of “walls”.

I got to thinking about what I did wrong, which is basically, checking for squares (loops) instead of finding them. I got to thinking about new algorithms to check all directions instead of just one, but what I need to do is to check along each side for walls and see if the gap is longer than the previous sides.  But that’s not going to work exactly, so I think I can use the distances traveled to see if there are places to drop objects based on if sides are shorter than the previous sides.

That probably made more sense in my head.

It probably isn’t that hard, I just, don’t feel like doing it right now. Occasionally there is a stupid simple one thrown in that takes 5 minutes to do both parts, maybe I can come back to today on that day.

Or maybe just sometime tomorrow, tomorrow is surprisingly pretty open.

Anyway, here is the code, I trimmed out the useless part 2 function.

import time
import os

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

lines = data.split("\n")
lines.pop()

guard = [0,0,0]
grid = []
guard_active = True
total = 0
total2 = 0
distances = []
cur_steps = 0

for each in lines:
  if "^" in each:
    guard[0] = each.index("^")
    guard[1] = lines.index(each)
#  print(guard)
  grid.append(list(each))

def print_grid(thegrid):
  for each in thegrid:
    print("".join(each))

#print_grid(grid)

def move_guard(thegrid, guard_pos):
# 0 - North, 1 - East, 2 - South, 3 - West
# Defind in guard[2]
# guard[0] = x coord (across rows), guard [1] = y coord (up and down lines)
  step = 1
  if (thegrid[guard_pos[1]-1][guard_pos[0]] != "#") and guard_pos[2] == 0:
    thegrid[guard_pos[1]][guard_pos[0]] = "X"
    thegrid[guard_pos[1]-1][guard_pos[0]] = "^"
    guard_pos[1] = guard_pos[1]-1
  elif (thegrid[guard_pos[1]][guard_pos[0]+1] != "#") and guard_pos[2] == 1:
    thegrid[guard_pos[1]][guard_pos[0]] = "X"
    thegrid[guard_pos[1]][guard_pos[0]+1] = "^"
    guard_pos[0] = guard_pos[0]+1
  elif (thegrid[guard_pos[1]+1][guard_pos[0]] != "#") and guard_pos[2] == 2:
    thegrid[guard_pos[1]][guard_pos[0]] = "X"
    thegrid[guard_pos[1]+1][guard_pos[0]] = "^"
    guard_pos[1] = guard_pos[1]+1
  elif (thegrid[guard_pos[1]][guard_pos[0]-1] != "#") and guard_pos[2] == 3:
    thegrid[guard_pos[1]][guard_pos[0]] = "X"
    thegrid[guard_pos[1]][guard_pos[0]-1] = "^"
    guard_pos[0] = guard_pos[0]-1
  else:
    guard_pos[2] = (guard_pos[2]+1) % 4
    step = 0
#  print(guard_pos)

  return thegrid, guard_pos, step


while guard_active:
  this_step = 0
  grid, guard, this_step = move_guard(grid, guard)
  if this_step == 0:
    distances.append(cur_steps)
    cur_steps = 0
  else:
    cur_steps += this_step
# Optionally Print the Map
#  print_grid(grid)
#  time.sleep(.001)
#  os.system('clear')
#  print(guard)
#  print(len(grid)-1)
  if guard[1] >= len(grid)-1 or guard[1] <= 0 or guard[0] >= len(grid[0])-1 or guard[0] <= 0:
    grid[guard[1]][guard[0]] = "X"
    guard_active = False
    print("The guard has left the area!")

for each in grid:
  total+= each.count("X")

#Print the final grid for fun
#print_grid(grid)

print(distances)

print(total)
# 5318
print(total2)

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 01 – Historian Hysteria

Apparently I only attempt do Advent of Code every other year.  I had this vague thought about doing this thing in JavaScript this year, but considering I am way better at Python than JS, and I have yet to actually complete one of these 100%, i should stick with what am good-ish at.  

The early day are always stupid easy.  It’s usually around halfway they have some super goofy and hard problem that I get stuck on.  The main change this year is doing it through the Bash terminal.  Though I think I did it this way in 2020.  I may swap later, who knows.  The only real annoyance is opening and closing files to test run.

I also have not done a lot of coding in a while, so I had to search up on how to do a few things.  It was a good starter scenario though, it used a lot of things I have forgotten.

I don’t do anything super fancy with my code on these, I have seen some annoyingly unreadable one liners a few times.  I always prefer clean code.  The thing runs in essentially zero time anyway, I don’t need to be “milliseconds more efficient”.

Anyway, here is my solution for part 1 and 2 of Day 1.

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

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

list1 = []
list2 = []

for each in sets:
   list1.append(int(each[0]))
   list2.append(int(each[1]))

list1.sort()
list2.sort()

total = 0
total2 = 0
for i in range(len(list1)):
    total += abs(list1[i]-list2[i])
    total2 += list1[i]*list2.count(list1[i])

print(total)
print(total2)

Iterating Again, Already

A few days ago I posted about updates to my little FreshRSS script. I already did some fairly quick simple updates, though some didn’t quite work out as hoped initially.

Firstly, I didn’t care for the big blob daily article file it pushed out to the archive. So now it spits out a bunch of individual articles, formatted “YYYY-MM-DD-Article Title.md.” this created my first issue. I got a “Filename too long error.” So I set up a check to truncate the article title to 100 characters, if its longer than 100 characters. I can’t just always slice it, because if its short, I will get an “index out of range” error. I then ran into a second issue. One of the articles I tagged was about the new Ranma 1/2 series. The system, totally choked on the / in 1/2.

I tried a few things to replace any /?%$&#@ symbols, because they all felt like potential catches. Working with slashes is a pain though since its “the escape character”. I ended up just untagging the article so the problem would go away for now.

The second change I made was to add a conditional for if the feed has a “Yes” under “Publish”. This allows me to add more feeds that simply, write articles, without posting links anywhere.

After running it, I noticed something though. None of the new non posting Notes or Recipes were making files. It seems they were all older than the “last run” time. Since I only needed them to work once, I just, force the feed to look at the last 1000+ hours.

This created another error with the WordPress module. These files only feeds don’t have URLs or usernames etc, so when the wp module called for this information, it just craps out. I moved the call to create the connection to inside the “should I post” conditional, which fixed the issue.

Another, thing I tried to do, though this is less coding and more convenience. the script runs in a virtual environment right now. Which is “good practice” but annoying to use. I’ve been trying to work out how to run it without all the hassle, though so far nothing I have found is real definitive. I have tried running the “version of python in the virtual environment, ie “venv/bin/python3 main.py”, but that doesn’t seem to work. I saw a suggestion fo using a hashang in the top line, which I tried, but that didn’t work. So I am just, back to “cd Scripts/Python/FreshRSS-Poster/” then “source venv/bin/activate” then “python3 main.py”, which is annoying, but it works.

There are still a few things I want to work out.

  • When I save files in other methods, it embeds images in them. I would like to adjust the formatting to mimic that.
  • The dates on the files are all the date run, though they should not be. I need to look into that.
  • I should sort the file outputs into directories based on the blog or topic tag.