
September 18th, 2025
Day 2.0
I enjoyed the challenge of day 1, so today I'm using my copious amounts of free time to work on day 2 of Advent of Code 2024. Today's assignment contains "reports" (lines of the input file), each containing lists of 5 integers separated by spaces, representing "levels". The goal of this assignment is to figure out the number of reports that are "safe"; reports are safe if 2 things are true:
- The levels are either all increasing or all decreasing.
- Any two adjacent levels differ by at least one and at most three.
function isSafe(distances::AbstractVector{<:Integer})::Bool isempty(diffs) && return false req1 = all(>(0), diffs) || all(<(0), diffs) req1 || return false return all(1 .<= abs.(diffs) .<= 3) end
After I got this working, I combined it with the basic boiler plate for activating the package, readlines for reading the input file, and a basic int parsing script for creating a Vector of ints for each level, then created a Vector of ints to represent the distances of each level to its neighbor. This, in a for loop, allowed me to calculate the number of safe levels:
using Pkg Pkg.activate(".") lines = readlines("input/day2.txt") function main()::Int64 safeReports = 0 for line in lines nums = [parse(Int64, strNum) for strNum in split(line)] distances = [nums[i] - nums[i + 1] for i in 1:length(nums) - 1] if isSafe(distances) safeReports += 1 end end return safeReports end safe1, safe2 = main() println("Day2.0: $safe1\nDay2.5: $safe2")
Day 2.5
One of the cool things with the advent of code problems is that occasionally, the second half of the problem makes you completely rethink how you solved the first half of the problem. That's what happened today.
The instructions for this half of the day include a so called "Problem Dampener", which allows the tolerance of one bad level. What this means in practice is that the same rules from earlier apply, but we also can declare a report safe if removal of one level. This means that my old strategy of using all won't fly anymore, which lead me to doing some more research on the Julia language.
First thing I found were two changes I could make to help the verbosity of my main function. Firstly, I found that there was diff()
built-in function in Julia that was exactly what I was looking for. I replaced distances = [nums[i] - nums[i + 1] for i in 1:length(nums) - 1]
with diff(nums)
(you can kinda tell I use Python all day from my first approach)/ Secondly, I turned my line parsing code from nums = [parse(Int64, strNum) for strNum in split(line)]
into a one-line function parse_levels(line::AbstractString)::Vector{Int} = parse.(Int, split(line))
(pretty cool feature Julia imo, reminds me of lambda but cooler).
After getting those small tweaks out of the way, I got started on a separate function for checking if is_safe_with_dampener
, that would only activate when isSafe returns false for a report. My approach to this was to iterate through the indices of a report, and for each index, create a new Vector of integers with all the levels besides the one at the index. Then I check to see if this new Vector of level safe using is_safe(diff(cand)) && return true
, returning true if this new Vector is safe. If none of the newly created Vectors are safe, I return false. The code for this, plus my new main method is below:
function is_safe_with_dampener(levels::AbstractVector{<:Integer})::Bool n = length(levels) for i in 1:n cand = Vector{eltype(levels)}(undef, n - 1) if i > 1 cand[1:i-1] = @view levels[1:i-1] end if i < n cand[i:n-1] = @view levels[i+1:n] end is_safe(diff(cand)) && return true end return false end function main(path::AbstractString)::Tuple{Int,Int} part1::Int = 0 part2::Int = 0 for line in eachline(path) levels = parse_levels(line) if is_safe(diff(levels)) part1 += 1 part2 += 1 elseif is_safe_with_dampener(levels) part2 += 1 end end return (part1, part2) end
Conclusions
Even with how busy PhD, TA & Kartchaser is keeping me, I'm having a blast working on these, and plan on continuing this grind. My code can be found on my GitHub in this repo.