Maker

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.






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.

2024.11.08 – Code Project – Python – Improved FreshRSS Link Lists

Today, I want to talk about recent improvements I have made on my FreshRSS to WordPress Digest Python script. And to make a note on what I would like to do next.

This is the script I used to produce these Link List Posts on [Blogging Intensifies] and Lameazoid. The Github Repository for it is here.

  • The first version was simple, it pulled from the sharded feed of FreshRSS, collected favorited articles, formatted them a bit, then posted a wordpress post of links.
  • Overtime, I wanted these posts to be prettier, so I added a bit more massaging to the formatting, and some HTML code so the links would show up in pretty little formatted boxes. I also decided some sort of summary would be useful, so it pulls the first 100 words or so from a feed item as a teaser.
  • Initially I was using the main share feed from FreshRSS, but I have two blogs, each with vague but disting “themes”. Sharing video game news to [BI] felt a bit silly. Not that it matters, no one reads those lists and its mostly for my reference. I found you could narrow things down by personal tags, so altered the Python script to handle any number of configured blogs and now both get seperate link lists.

So, whats new this round?

Not a lot on the externally visible end. A week or so ago, I found that the Raspberry Pi I had running the script on had died on me. Or, more likely, the SD card did. Whatever the case, the script was not running as scheduled. I have always had a bit of a love/hate with the scheduled run. Some days I barely share anything from the reader, so it makes for weird small posts. Also it ran at like, 10:30P, which was kind of late, and occasionally, I found myself rushing to get through everything to make sure I flagged anything relevant, because I was flipping through it at 5 minutes till.

Right now, I am running it manually. And at irregular intervals. This created a new problem, but its one I had already been planning to fix. The way the FreshRSS shared feed works, you can append a number, X, and get “everything in the last X hours.” When it ran on a cronjob schedule every 24 hours, this number was simply, hardcoded at 24.

When manually running things, I needed X to be “however many hours since it last ran.” So now, it writes out a simple file with a time stamp, after each run. It also pulls in that file, and calculated the time difference. If the time difference is less than an hour, it defaults to an hour, because “X=0”, just gives the default feed, which may be everything, or may be the last ten items. I am not sure on the limits. If there isn’t a timestamp file, most likely if its being first run, it sets the hours different to 0, and gets everything.

Something else I added this round, everytime I wanted to do modifications, I needed to comment out some lines and uncomment others, so the script would not spam my blog with the same post over and over. Also, I have a time stamp file now that I don’t want to overwrite when testing, since it will probably stop seeing feed items unless they were marked in the last hour.

So I added a flag variable at the top, and encapsulated the business end output in some conditional statements. Now, when I want to test, I just change the “runmode” variable at the tol to “False”, and it stops posting or editing the time stamp file.

This was also needed for my third new feature. In addition to posting to the blog, it spits everything out into a simple, dated markdown file. This way, I have a private record of everything shared, since a lot of the point is, “I want to keep these links for reference.” Initially I just spit out the post data, but that was ugly since it was full of HTML tags. So It now compiles together a second, markdown formatted, variable, that gets written to the file. Another key difference, in my private files, I dump the entire article, and not just the 100 word summary.

I don’t want to repost entire articles on my blog, its rude and ugly, hence the summary, but for a private archive of text data, dumping it all is preferable. Articles disappear ALL the time, this is literally 100% of why I save and archive shit like this in the first place.

Some changes I still want to make

  • Right now it dumps everything into one output file, I may split this across blogs/topics
  • Another advantage of the private archive, I can add any number of additional tags to pull, that don’t have to be posted anywhere. I can just, pull them to a text archive. I already have started a recipes tag, for example. I also added a to be used flag in the config for it a feed gets posted anywhere.
  • I am kind of down on the idea of AI, but I still may look into hooking the summary function to some sort of AI service to create actual summaries. In my very very vague testing, it had a hard time keeping it short, even when instructed to do so.
  • I kind of want to modify the script to also produce a queue of links, then maybe a second script on a schedule that posts any links out of a file on microblogging services like Mastodon.
  • I would love to find a way to share links I find elsewhere to FreshRSS or this script.
  • I kind of want to find a way to sort and group posts under categories (Music, Coding, Video Games, etc). I have ideas oh how, but they are not all very… Elegant.
  • I kind of dislike having the timestamp file, I would like to figure out a way to query the WordPress Blog itself for “The last post marked link list,” and go off of that as the “last run date.”

The “Holy Grail” want, is the ability to add comments to shares. I put in a suggestion on the Github page for a “Notes” feature. I am seriously just considering making my own plug in. This would be super useful for WHY I shared a link. I could use this later in the social sharing queue system as well. The idea would be, as an example, I tag a post for a new CHVRCHES album, then add a little note, “I am super looking forward to this!”, then on the digest, the comment would show up.