Advent of code: Setting up each day programmatically

Each year I like to do the advent of code (or attempt to...). This year I have the added bonus of being on paternity leave so no work to get in the way, so during nap times its advent of code time! I go through a repetitive process of creating a new directory each day, getting my puzzle input, saving it to a file in the new directory and creating a file for the day. This year I thought I would see if I could automate this process (There is a good xkcd about automating but Im not paying attention to that as this is for fun 😄)

First things first, I wanted to work out the api that I wanted for this command. The only thing that changes is the day number each day so I decided I would like it to be ruby day_generator.rb <day-number>

The steps that the script is going to have to take are;
  1. Read the day number
  2. Create the directory if it doesn't already exist
  3. Create a new file with some templated code
  4. Fetch the puzzle input and write it to a file.

First part is very easy in ruby you have access to the arguments passed to the script via ARGV. To get the day number will just be day_num = ARGV[0].

Second part is almost as easy thanks to the API's provided by ruby we will use File.directory?(dir_name) to check for the directory and if not we will use Dir.mkdir(dir_name) to create the directory. If the directory does exist we will raise a helpful error (we dont want to overwrite a solution).

For the third part I could only think of adding inputs = File.read("input.txt") as a template for something that I would want in the solution every day. Maybe as the month goes on I will think of something else. For now, I'm just using a Heredoc and then File.write to create the file for the day.

content = <<~CONTENT
  inputs = File.read("input.txt")
CONTENT

File.write("#{dir_name}/day_#{day_num}.rb", content)

Now the trickest part, fetching the puzzle input. I didnt want any dependencies so I decided to use the net/http library that is build into ruby. Normally when working with rails I would use a gem for this as I dont find the net/http library to have the most intuitive api. I also had to fetch my session cookie from the browser, there isn't an api key for advent of code but the session cookie lasts for the month so as long as you dont log out this will remain valid. I saved the session cookie to an env var and started to work out how to make the request with net/http. This is what I ended up with;

url = URI.parse("https://adventofcode.com/2024/day/#{day_num}/input")
session_cookie = ENV["AOC_SESSION_COOKIE"]

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true if url.scheme == 'https'

request = Net::HTTP::Get.new(url)
request['Cookie'] = "session=#{session_cookie}"

response = http.request(request)

if response.is_a?(Net::HTTPSuccess)
  File.write("#{dir_name}/input.txt", response.body)
else
  raise "HTTP Error: #{response.code} - #{response.message}"
end

So putting it all together my script looks like the following;

require 'net/http'
require 'uri'

day_num = ARGV[0]

dir_name = "day_#{day_num}"
raise "Day already exists" if File.directory?(dir_name)

Dir.mkdir(dir_name)

content = <<~CONTENT
  inputs = File.read("input.txt")
CONTENT

File.write("#{dir_name}/day_#{day_num}.rb", content)

url = URI.parse("https://adventofcode.com/2024/day/#{day_num}/input")
session_cookie = ENV["AOC_SESSION_COOKIE"]

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true if url.scheme == 'https'

request = Net::HTTP::Get.new(url)
request['Cookie'] = "session=#{session_cookie}"

response = http.request(request)

if response.is_a?(Net::HTTPSuccess)
  File.write("#{dir_name}/input.txt", response.body)
else
  raise "HTTP Error: #{response.code} - #{response.message}"
end

The script has been serving me well for the first seven days 😄. Its taken the only repetitive toil (not that it was much) out of advent of code. Now to just solve the problems...