Programming Update for July and August 2021


I didn’t do much coding in July, so I decided to combine the July and August wrap-ups. 

Python

Dreamhost Dynamic DNS

I decided to clean up and update some code that uses the Dreamhost API to allow for Dynamic DNS. I cleaned up the logging output so that I could figure out why it was sometimes deleting one of my domains rather than just updating the IP. The original programmer had it printing out to the terminal. I used the logging package to send logs out to a file. While I was improving the logging, I also decided to use f-strings to make it more obvious what was going into the output string. 

Then I changed some of the logic to hopefully prevent the domains from being deleted just because an update didn’t work. But, just in case (as well as a bonus for when adding new subdomains or domains) I told it at the end to add in any domains in the list. Since making these changes I haven’t had any more issues.

Repo is here.

Extra Life Donation Tracker

I was really busy with this project in July and August with 3 releases (v6.2, v6.2.1, v6.2.2). Some of my changes were fixes to make life easier for users. For example, having the program know if you’ve successfully run it before so that it won’t overwrite your values with the defaults if it can’t reach the API. Other changes involved backend things like moving from urlopen to requests. Lots of the changes came from bug reports from my users. (Always nice to know I have users who care enough to file a bug report)

In September I’m hoping to rip out the API code and move that to its own repo so that it can be used for any DonorDrive projects. We’ll see how that goes.

Project Page.

Repo.

Prophecy Practicum

The Django project I’ve been developing for a friend reached its v4.0 milestone and continues to have quality of life improvements. I’ve learned quite a bit on this project and it’s a great testament to the idea that the best way to learn how to program in a new framework is to use it to solve a problem. 

Repo.

CircuitPython

The most recent Adabox turned out to be the RP2040 Macropad. I’d been wanting to make one of these after the success of my QTPy Streamdeck. The coolest part is that, with its screen it’s even more useful as it can have shortcuts for many programs. I haven’t set it up for that yet, but it’ll come with time. I may eventually get a second one for my Linux computer. I had to modify the code from the Adafruit tutorial a little so that it would work with OBS when it didn’t have focus. But mostly what you’ll want to see in my repo are any macros I add. 

Repo.

Advent of Code

After taking most of July off, I got back the urge to work on Advent of Code 2015. A flurry of coding at the end of August got me up to 36 stars, surpassing my 2020 total. (I finished Days 14-18 in Python and Ruby and all but the last two in Perl) Part of this is due to not being constrained by trying to do each day’s problem. The other part is due to what I learned in the aftermath of 2020 – such as the fact that I usually want to use a dictionary (or hash or map, depending on what the language calls it) rather than a list (or array or vector) for performance and simplicity reasons. See my Day 18 code for a great example of this. 

Before I get to my code, I wanted to share this blog post from the folks over at JetBrains.It discusses using Advent of Code as a way to learn idiomatic Kotlin. (Kotlin is a Java successor language created by JetBrains) It was neat to see someone else advocating AoC for the same purpose I’m using it – as a way to learn a new programming language by using it to solve problems.

Day 15

I originally had some plans to try and find the maximum values and figure out if I was in a local maxima. But someone on the AoC subreddit pointed out that with a modern computer we weren’t looking at too many permutations so it wasn’t too bad to brute-force it. 

from sys import path
from itertools import permutations, combinations_with_replacement
import re
path.insert(0, '../../input_parsing')
import parse_input


def ingredient_score(teaspoon_list, ingredient_list):
    combined_list = zip(teaspoon_list, ingredient_list)
    total_ingredients = []
    for multiplication_tuple in combined_list:
        temp_list = [
            int(item) * multiplication_tuple[0] for item in multiplication_tuple[1]
        ]
        total_ingredients.append(temp_list)
    properties = zip(*total_ingredients)
    final_score = 1
    property_count = 1  # this is to ignore calories for part 1
    for cookie_property in properties:
        if sum(cookie_property) > 0:
            final_score *= sum(cookie_property)
        property_count += 1
        if property_count == 5:
            break
    return final_score


def parse_ingredients(ingredient_inputs):
    pattern = re.compile(r'(-*\d)')
    return [re.findall(pattern, ingredient) for ingredient in ingredient_inputs]

def brute_force_cookie_score(ingredient_list):
    ingredient_combos = [
        element
        for element in permutations(
            range(1, 100), len(ingredient_list)
        )
        if sum(element) == 100
    ]
    score = 0
    for ingredient_combination in ingredient_combos:
        combo_score = ingredient_score(ingredient_combination, ingredient_list)
        if combo_score > score:
            score = combo_score
    return score


if __name__ == "__main__":
    cookie_list = parse_input.input_per_line('../input.txt')
    ingredients = parse_ingredients(cookie_list)
    cookie_score = brute_force_cookie_score(ingredients)
    print(f"The cookie score is {cookie_score}")

It was taking a lot longer in my Ruby code and so I asked on the Ruby subreddit and ended up with a few examples of ways to use Enumerators which are more similar to Python’s generators. Go to the Ruby Day 15 readme to see those examples.

Day 16

I know figuring out this problem this should help me understand how to solve Day 21 of AoC 2020, but I can’t quite make the leap. That said, this was not too hard to figure out. Essentially, I just went through a dictionary of Sues and eliminated each Sue who didn’t match. The important thing was that my input didn’t contain info for each and every attribute. So I had to have guards for not eliminating an Aunt Sue just because she didn’t have an entry for one of the attributes. See how I solved for each language here. Each one had a slight difference in the way I had to handle those None values. 

Day 17

Day 17 turned out to be extremely easy after the day 15 experience taught me how to do permutations where they add up to a specific number. Ruby was slightly more complicated than Python, but once I got rid of empty arrays (I think due to the fact that Ruby doesn’t do list comprehensions), it was pretty easy. 

require "../../input_parsing/parse_input"

container_sizes = input_per_line("../input.txt")
container_combinations = []
container_sizes = container_sizes.map{|x| x.to_i}
(1..container_sizes.length).each do|x|
  container_combinations.append(container_sizes.combination(x)
                                 .map{|y| y if y.sum == 150}
                                 .compact)
end
container_combinations = container_combinations.compact.reject(&:empty?).flatten(1)
puts container_combinations.length

As of the day I write this, I haven’t been able to write Perl code that’s as efficient as the Python and Ruby solutions. Yesterday after running for hours, it crashed Konsole. We’ll see if I have a proper answer before I post this.

Day 18

This is where it REALLY paid off that I had learned the lesson of using dictionaries/hashes rather than lists in most places. AoC 2020 had at least 2 days with Conways’ Game of Life. On day 11 there was a modified 2D version. On day 17 there was a 3D version. My code for day 11 was a mess and I couldn’t figure out how to do day 17. For this 2015 Day 18 problem: by using dictionaries, I didn’t have to worry about guard clauses. For Python I just used .get and in Ruby it would just return “nil” if it wasn’t there. So I was able to have a much simplified code and I didn’t need to fear Conway as much as I thought I did. See my code here.

Because I’m an info geek, I’ll mention that at the time I write this (mid-day Aug 31 so there may be slight changes if I do more coding this evening, it may change a little), the code in my Advent of Code repo is:

  • 69.7% Python
  • 18.0% Ruby
  • 12.2% Perl
  • 0.1% Shell Code

My Advent of Code repo.