MENU

Latest Posts


Advent of Code Day 5

Decided to try to go with pure PostgreSQL for this one.

Part 1 was easy. You are given a list of available ingredient id’s from the elven cafeteria, and a list of ranges of “fresh” ingredients. Determine which available ingredients are fresh.

Fresh Ranges:
3-5
10-14
16-20
12-18
Available:
1
5
8
11
17
32

I made 2 tables, fresh_ingredients and available_ingredients, and from there it’s a simple query:

SELECT ai.* FROM available_ingredients ai WHERE
(SELECT COUNT(*) FROM fresh_ingredients
WHERE ingredient_id_range_min <= ai.ingredient_id
AND ingredient_id_range_max >= ai.ingredient_id) > 0

Part 2 asks you to disregard the available ingredients and get a count of all fresh ingredients from the given ranges. Also a simple query, right?

SELECT COUNT(DISTINCT gs.ingredient_id)
FROM GENERATE_SERIES(
(SELECT MIN(ingredient_id_range_min) FROM fresh_ingredients),
(SELECT MAX(ingredient_id_range_max) FROM fresh_ingredients)) as gs(ingredient_id)
WHERE (SELECT COUNT(*) FROM fresh_ingredients
WHERE ingredient_id_range_min <= gs.ingredient_id
AND ingredient_id_range_max >= gs.ingredient_id) > 0

Wrong!

Theoretically, that query can solve it. Except the ranges they give you are things like: 377649615919740 – 379699191722247, which gives you 2,049,575,802,507 fresh id’s. For one range.

I ran out of disk space on my 1TB laptop.

NEW PLAN: Need to order the ranges starting with the lowest starting id, loop through each one and merge them. Also, have a calculate column figure out the difference in each range (we don’t care about each ingredient, just how many).

ALTER TABLE fresh_ingredients_merged
ADD COLUMN ingredient_id_range_diff BIGINT
GENERATED ALWAYS AS (ingredient_id_range_max - ingredient_id_range_min + 1) STORED;

Now the “merging” logic. This is what we’re aiming for:

-- Loop through ranges
-- If the new start number falls within an existing range,
-- Alter the existing end number to be the max of the new end number and existing
--
-- If the new end number falls within an existing range,
-- Alter the existing start number to be the min of the new start number and existing
--
-- if the new start AND end number fall within existing range, do nothing
--
-- if neither new start or end numbers fall within existing range, add a new row

Here’s the final working query. PS. I can’t fucking believe this worked. PPS. I know this could have been much simpler in Python or something but this is the life we’ve chosen!

DO
$do$
DECLARE
  i record;
BEGIN 
  FOR i IN SELECT * FROM fresh_ingredients ORDER BY ingredient_id_range_min LOOP
	-- If the new start number falls within an existing range,
	-- Alter the existing end number to be the max of the new end number and existing
	--
	-- If the new end number falls within an existing range,
	-- Alter the existing start number to be the min of the new start number and existing
	--
	--   if the new start AND end number fall within existing range, do nothing
	--
	-- if neither new start or end numbers fall within existing range, add a new row
	IF (SELECT COUNT(*) FROM fresh_ingredients_merged fim
		WHERE fim.ingredient_id_range_min <= i.ingredient_id_range_min
		AND fim.ingredient_id_range_max >= i.ingredient_id_range_max) > 0 THEN -- If BOTH fall in range
		-- Do nothing
	ELSIF (SELECT COUNT(*) FROM fresh_ingredients_merged fim
		WHERE fim.ingredient_id_range_min <= i.ingredient_id_range_min
		AND fim.ingredient_id_range_max >= i.ingredient_id_range_min) > 0 THEN -- If the min false in range
		    UPDATE fresh_ingredients_merged
		    SET ingredient_id_range_max = GREATEST(ingredient_id_range_max, i.ingredient_id_range_max)
		    WHERE ingredient_id_range_min <= i.ingredient_id_range_min 
			AND ingredient_id_range_max >= i.ingredient_id_range_min;
	ELSEIF (SELECT COUNT(*) FROM fresh_ingredients_merged fim
		WHERE fim.ingredient_id_range_min <= i.ingredient_id_range_max
		AND fim.ingredient_id_range_max >= i.ingredient_id_range_max) > 0 THEN -- If the max falls in range
			UPDATE fresh_ingredients_merged
		    SET ingredient_id_range_min = LEAST(ingredient_id_range_min, i.ingredient_id_range_min)
		    WHERE ingredient_id_range_min <= i.ingredient_id_range_max 
			AND ingredient_id_range_max >= i.ingredient_id_range_max;
	ELSE -- NOTHING falls in range
		INSERT INTO fresh_ingredients_merged(ingredient_id_range_min, ingredient_id_range_max)
		VALUES (i.ingredient_id_range_min, i.ingredient_id_range_max);
	END IF;
   END LOOP;
END;
$do$;

Advent of Code Day 4

This was a fun one. I’ve been reading tutorials for QB64 (the unauthorized successor to QBasic) the past week or so, because I have that kind of time. Decided to add a bit of visualization too.

Quickly relearned the importance of Option Explicit! IYKYK.

Option Base 0 'Arrays zero-indexed for sanity
Option _Explicit ' Also for sanity

' Declare Variables
Dim CurrentRow%, CurrentCol%, AccessibleRolls%, InaccessibleRolls%, AdjacentRolls%
Dim InnerRow%, InnerCol%, Ch$, i%, RemovedRolls%
Dim FileName$
Dim Shared InputRowCount%
ReDim Shared InputArray$(200) 'create dynamic array

' Configure output screen
Screen 0
Width 138, 138

' Get the input file and read it into array
ChDir "C:\Users\burgd\source\repos\Advent2025\day4"
Print "Enter the input filename:"
Input FileName$

InputRowCount% = 0
If _FileExists(FileName$) Then
    Print "FIle Found"
    i% = 0
    Open FileName$ For Input As #1
    Do Until EOF(1)
        Line Input #1, InputArray$(i%)
        i% = i% + 1
        InputRowCount% = InputRowCount% + 1
    Loop
    Close #1
Else
    Print "File Not FOUND!"
End If
Print "Press any key to continue"
Sleep

Cls

'Main logic for determining movable rolls
CurrentRow% = 0
AccessibleRolls% = 1
InaccessibleRolls% = 0
RemovedRolls% = 0

While AccessibleRolls% > 0
    AccessibleRolls% = 0
    CurrentRow% = 0
    InaccessibleRolls% = 0
    While CurrentRow% < InputRowCount%
        CurrentCol% = 1 ' Mid function starts at 1
        While CurrentCol% <= Len(InputArray$(CurrentRow%))
            Ch$ = Mid$(InputArray$(CurrentRow%), CurrentCol%, 1)
            If Ch$ = "@" Or Ch$ = "x" Then
                Cls
                AdjacentRolls% = 0
                For InnerRow% = Max(CurrentRow% - 1, 0) To Min(CurrentRow% + 1, InputRowCount% - 1)
                    For InnerCol% = Max(CurrentCol% - 1, 1) To Min(CurrentCol% + 1, Len(InputArray$(InnerRow%)))
                        If Not (InnerRow% = CurrentRow% And InnerCol% = CurrentCol%) Then
                            Ch$ = Mid$(InputArray$(InnerRow%), InnerCol%, 1)
                            If Ch$ = "@" Or Ch$ = "x" Then
                                AdjacentRolls% = AdjacentRolls% + 1
                            End If
                        End If
                    Next InnerCol%
                Next InnerRow%
                If AdjacentRolls% < 4 Then
                    Mid$(InputArray$(CurrentRow%), CurrentCol%, 1) = "x"
                    Cls
                    PrintDiagram
                    AccessibleRolls% = AccessibleRolls% + 1
                End If
            End If
            CurrentCol% = CurrentCol% + 1
        Wend
        CurrentRow% = CurrentRow% + 1
    Wend
    Cls
    RemovedRolls% = RemovedRolls% + AccessibleRolls%
    ClearRemovedRolls
Wend

Cls
Print "Total rolls removed!: " + Str$(RemovedRolls%)
Sleep

Sub PrintDiagram ()
    Dim i%, j%, ch$
    For i% = 0 To InputRowCount%
        For j% = 1 To Len(InputArray$(i%))
            ch$ = Mid$(InputArray$(i%), j%, 1)
            If ch$ = "x" Then
                Color 11
            Else
                Color 15
            End If
            Print ch$;
        Next j%
        Print
    Next i%
    Color 15
    _Display
End Sub

Sub ClearRemovedRolls ()
    Dim i%, j%, ch$
    For i% = 0 To InputRowCount%
        For j% = 1 To Len(InputArray$(i%))
            ch$ = Mid$(InputArray$(i%), j%, 1)
            If ch$ = "x" Then
                Mid$(InputArray$(i%), j%, 1) = "."
            End If
        Next j%
    Next i%
End Sub

Function Min% (Number1%, Number2%)
    If Number1% <= Number2% Then
        Min% = Number1%
    Else
        Min% = Number2%
    End If
End Function

Function Max% (Number1%, Number2%)
    If Number1% >= Number2% Then
        Max% = Number1%
    Else
        Max% = Number2%
    End If
End Function

Advent of Code Day 3

Went with React today: https://github.com/ChrisBurgdorff/advent-2025-day-3

Part 2 reminded me what these challenges always remind me on their part 2’s and that is choosing the right level of abstraction can save future you extra work.

Part 1 is to choose 2 batteries to turn on. The first battery’s number becomes the tens digit of your “joltage” and the second battery becomes the ones digit.

Undoubtedly, you will write something like this:

const getMaximumJoltage = (bank: string) => {
  let maxTensVal = 0;
  let maxTensIndex = 0;
  let maxOnesVal = 0;
  for (let i = 0; i < bank.length - 1; i++) {
    if (Number(bank[i]) > maxTensVal) {
      maxTensIndex = i;
      maxTensVal = Number(bank[i]);
    }
  }
  for (let j = maxTensIndex + 1; j < bank.length; j++) {
    if (Number(bank[j]) > maxOnesVal) {
      maxOnesVal = Number(bank[j]);
    }
  }
  return (maxTensVal * 10) + maxOnesVal;
};

Notice how the ones and tens shit is hardcoded. Part 2 is to do the same, but choose 12 batteries to turn on.

Now, this is super unrealistic because we all know that real clients and PM’s never change their minds. Humor me though. If you had written part 1 as:

const getMaximumJoltage = (bank: string, numberOfBatteries: number) => {
...

You would be done. If not, you either need to edit the first function with 20 extra variables and loops, or write the abstraction that you should have written in the first place..


Advent of Code Day 2

So far so good. Finding invalid id’s (sequences of repeated numbers, eg. 123123) using brute force and awkward string manipulation.

Remembered that “awkward string manipulation” is usually a good flag for regex. Fired up my regex generator (AKA ChatGPT). Boom. Done.

def getInvalidIdSum(rangeBounds, part2Flag):
  invalidIdSum = 0
  for id in range(int(rangeBounds[0]), int(rangeBounds[1]) + 1):
    if (re.fullmatch(r"^(\d+)\1+$" if part2Flag else r"^(.+?)\1$", str(id))):
      invalidIdSum += int(str(id))
  return invalidIdSum

Advent of Code Day 1

Well it’s that time of year again. I’ll inevitably get really into this thing for a few nights, and then around day 5 or 6, full on ragequit after an exhausting 3 AM troubleshooting session.

My overly ambitious plan is to try to mix in a few different languages/technologies.. I’ve heard of people using a different language every day, but that’s 12 days, or like 10 more languages than I could possibly manage.

Anyway, here is day 1 though, in Excel VBA:

Option Explicit

Function GetNewDialPosition(instruction As String, currentDialPosition As Integer) As Integer
    Dim direction As String
    Dim amount As Long
    Dim rawPosition As Integer 'Can be negative
    Dim actualPosition As Integer 'Always between 0 and 99
    
    direction = Left(instruction, 1)
    amount = Int(Right(instruction, Len(instruction) - 1))
    
    If direction = "R" Then
        rawPosition = (currentDialPosition + amount) Mod 100
    Else
        rawPosition = (currentDialPosition - amount) Mod 100
    End If
    If rawPosition < 0 Then
        actualPosition = rawPosition + 100
    Else
        actualPosition = rawPosition
    End If
    GetNewDialPosition = actualPosition
End Function

Function GetPassingZeroes(instruction As String, currentDialPosition As Integer) As Long
    Dim direction As String
    Dim amount As Long
    Dim passingZeroes As Long
    
    direction = Left(instruction, 1)
    amount = Int(Right(instruction, Len(instruction) - 1))
    
    If direction = "R" Then
        passingZeroes = Abs(Int((currentDialPosition + amount) / 100))
        If (currentDialPosition + amount) Mod 100 = 0 Then
            passingZeroes = passingZeroes - 1
        End If
    Else
        passingZeroes = Abs(Int((currentDialPosition - amount) / 100)) - Abs(Int(currentDialPosition / 100))
        If currentDialPosition = 0 Then
            passingZeroes = passingZeroes - 1
        End If
    End If
    GetPassingZeroes = passingZeroes
End Function


Sub GetPassword()
    Dim currentRow As Long
    Dim currentDialPosition As Integer
    Dim currentInstruction As String
    Dim lastRow As Long
    Dim numberOfZeroes As Long
    Dim numberOfZeroesPart2 As Long
    
    currentDialPosition = 50
    numberOfZeroes = 0
    numberOfZeroesPart2 = 0
    lastRow = 4780
    
    For currentRow = 1 To lastRow
        numberOfZeroesPart2 = numberOfZeroesPart2 + GetPassingZeroes(Cells(currentRow, 1).Value, currentDialPosition)
        currentDialPosition = GetNewDialPosition(Cells(currentRow, 1).Value, currentDialPosition)
        If currentDialPosition = 0 Then
            numberOfZeroes = numberOfZeroes + 1
            numberOfZeroesPart2 = numberOfZeroesPart2 + 1
        End If
    Next
    
    MsgBox "The password is: " & CStr(numberOfZeroes) & " for part 1 and " & CStr(numberOfZeroesPart2) & " for part 2"
End Sub