Programming Update: September / October 2021


Across both September and October I got a decent amount of programming done. I just about finished the Learn You Haskell for Great Good book (got up to just before Monads) and started Head First Go. Once I had enough Go language to get started on AoC, I knew I’d be ready for the 2016 problem set. Let’s get into the nitty gritty!

Python (not Advent of Code)

Impractical Python

I finished chapter 11 which contained my old friend the Monty Hall Paradox. I’ve had a version up on my site for a very long time based on a PHP tutorial in Linux Format Magazine. This time I was able to code it in Python as well as creating a GUI version. Chapter 11 also touched upon The Birthday Paradox

Civilization VI Webhooks with FastAPI

I added a few more unit tests to my Civ VI play by cloud webhook service. FastAPI makes this incredibly easy and painless. I never thought writing unit tests for a web framework could be so easy!

DonorDrivePython and Extra Life Donation Tracker

After having laid the groundwork a few months ago, I finally got DonorDrivePython (an API framework that should allow anyone to build an app that works with the Donor Drive API) stood up and ripped the code out of the Extra Life Donation Tracker. I also had a few bugfix releases for the latter to deal with some issues folks were having over the Halloween weekend. The code is in a great place for me to put the final layer of polish on it in the next year. 

Advent of Code

I finished the 2015 problem set in September. I felt a great sense of accomplishment because I hadn’t been able to finish the 2020 problem set during December last year. In addition to that, I’d done it in 3 different programming languages! You can read my write-up about it here. I wasn’t yet ready to move on to 2016, so I went back to 2020 to try and solve some of the problems I wasn’t able to solve last December. I was able to solve part 2 of Day 7. Essentially I used my newly gained knowledge of how to do caching to be able to quickly find the solution. For Day 13 I was able to make use of my better programming skills to come up with a solution for part 2 that would finish in a reasonable time for the test input. But I was unable to figure out the main input on my own. I looked through the AoC subreddit and couldn’t really understand any of the Chinese Remainder Theorem solutions. Eventually I found a solution I could reason my way through and use that.

Then it was finally time to begin 2016! Before the end of October I got though Day 4 Part 1 with Python. I finished up to Day 3 in Perl, Ruby, and Go. And I finished Day 1 in Haskell. Haskell was a huge change in the way I look at problems. You’re not supposed save state or reassign variables so you essentially have to set up a chain of functions to get your answer. Here’s my Day 1 Haskell code:

import Data.Complex
import qualified Data.Text as T

-- Using the polar math turn one direction if L and the other way if R (or anything, but we know it'll be R)
figureOutDirection :: Num a => (Char, b) -> (Complex a, b)
figureOutDirection (dir, distance)
    | dir == 'L' = ((0:+1), distance)
    | otherwise = ((0:+ (-1)), distance)

-- This splits the letter from the number in the input
splitDirDistance :: [a] -> (a, [a])
splitDirDistance x = (head x, tail x)

-- this converts the number from a string into a Float number
fixString x = (fst x, read (snd x):: Float)

-- First convert the string into a Data.Text, split on the commas, then turn back into a string.
splitStringOnCommas :: String -> [String]
splitStringOnCommas x = map T.unpack (map T.strip (T.split (==',') (T.pack x)))

-- Basically we convert the puzzle input into a series of complex numbers and distances
getComplexDirections :: Num a => String -> [(Complex a, Float)]
getComplexDirections x = map figureOutDirection (map fixString ((map splitDirDistance (splitStringOnCommas x))))

--move :: Num b => (b, b) -> (b, b) -> (b, b)
-- does the actual movement along the blocks one block at a time.
move (current_direction, location) (rotate, distance) = (current_direction * rotate, location + (current_direction * rotate * (distance:+0)))

-- foldl to apply the move across the entire series of directions
finalLocation j = foldl move (0:+1, 0:+0) (getComplexDirections j)

-- now that we know where we ended up, take the absolute values of the final coordinates and sum. 
finalAnswer x = abs (realPart (snd x)) + abs (imagPart (snd x))

-- finalAnswer (finalLocation "R2, L3")
    
main = do 
    let puzzleInput = "R4, R4, L1, R3, L5, R2, R5, R1, L4, R3, L5, R2, L3, L4, L3, R1, R5, R1, L3, L1, R3, L1, R2, R2, L2, R5, L3, L4, R4, R4, R2, L4, L1, R5, L1, L4, R4, L1, R1, L2, R5, L2, L3, R2, R1, L194, R2, L4, R49, R1, R3, L5, L4, L1, R4, R2, R1, L5, R3, L5, L4, R4, R4, L2, L3, R78, L5, R4, R191, R4, R3, R1, L2, R1, R3, L1, R3, R4, R2, L2, R1, R4, L5, R2, L2, L4, L2, R1, R2, L3, R5, R2, L3, L3, R3, L1, L1, R5, L4, L4, L2, R5, R1, R4, L3, L5, L4, R5, L4, R5, R4, L3, L2, L5, R4, R3, L3, R1, L5, R5, R1, L3, R2, L5, R5, L3, R1, R4, L5, R4, R2, R3, L4, L5, R3, R4, L5, L5, R4, L4, L4, R1, R5, R3, L1, L4, L3, L4, R1, L5, L1, R2, R2, R4, R4, L5, R4, R1, L1, L1, L3, L5, L2, R4, L3, L5, L4, L1, R3"
    print (finalAnswer (finalLocation puzzleInput))

Go has been mostly easy to use. It’s a bit more verbose than the other languages, but the only real problem (or rather the thing that has caused changed in my solution) is that arrays can only contain one type of thing. Unlike Python, Ruby, and Perl – you can’t have an array with both strings and integers. So I’ve had to get a bit more creative with my solutions. Still, it’s been fun learning the ins and outs of Go. I look forward to learning more and, as I go further in Head First Go getting even more idiomatic in my solutions.