↤ go back, or return home

jack's advent of code 2024 ramblings

day 5

day five, lets get this bread 🍞


click to view my solution

given the sample input:

47|53
97|13
97|61
97|47
75|29
61|13
75|53
29|13
97|29
53|29
61|53
97|53
61|29
47|13
75|47
97|75
47|61
75|61
47|29
75|13
53|13

75,47,61,53,29
97,61,53,29,13
75,29,13
75,97,47,61,53
61,13,29
97,13,75,29,47

assuming input is the above as one big string,

[rules, updates] = input |> String.split("\n\n")

split the input into the rules as one string, and the updates as another

rules = rules
  |> String.split("\n")
  |> Enum.map(fn rule ->
    rule
    |> String.split("|")
    |> Enum.map(&(String.to_integer(&1)))
  end)

basic newline split, map, split on |, then map to integer

[
  [47, 53],
  [97, 13],
  [97, 61],
  [97, 47],
  ..
]

we get the rules

updates = updates
  |> String.split("\n")
  |> Enum.map(fn update ->
    update
    |> String.split(",")
    |> Enum.map(&(String.to_integer(&1)))
  end)

same as above, but we split on ,

[
  [75, 47, 61, 53, 29],
  [97, 61, 53, 29, 13],
  [75, 29, 13],
  [75, 97, 47, 61, 53],
  [61, 13, 29],
  [97, 13, 75, 29, 47]
]

we get the updates

rule_lookup = rules
  |> Enum.group_by(fn [a, _b] -> a end)
  |> Enum.map(fn {k, v} ->
    {k, v |> Enum.map(fn [_a, b] -> b end) |> MapSet.new()}
  end)
  |> Map.new()

lastly, i create a map to use a lookup table, from value, to what values should come before it

%{
  29 => MapSet.new([13]),
  47 => MapSet.new([13, 29, 53, 61]),
  53 => MapSet.new([13, 29]),
  61 => MapSet.new([13, 29, 53]),
  75 => MapSet.new([13, 29, 47, 53, 61]),
  97 => MapSet.new([13, 29, 47, 53, 61, 75])
}

so rules from the input, like these:

47|53
47|13
47|61
47|29

become:

47 => MapSet.new([13, 29, 53, 61]),

nice little lookup table


check_valid = fn update ->
  for {num, index} <- Enum.with_index(update) do
    {before_updates, _after_updates} = update |> Enum.split(index)
    for before_update <- before_updates do
      Enum.member?(Map.get(rule_lookup, before_update, []), num)
    end
  end
  |> List.flatten()
  |> Enum.all?()
end

lastly, here’s a little function that checks to see if a given update is valid

i iterate through each number in the list, with its index:

for {num, index} <- Enum.with_index(update) do

i then split the list to get every number before the number we’re on:

{before_updates, _after_updates} = update |> Enum.split(index)

then, go through each item before, and ensure that the number we have is a member of the rule lookup:

for before_update <- before_updates do
    Enum.member?(Map.get(rule_lookup, before_update, []), num)
end

then, flatten this list, and expect all of the values to be true

if any value isn’t true, we’ve broken a rule


part 1

for update <- updates do
  is_valid = check_valid.(update)
  
  if is_valid do
    Enum.at(update, floor(length(update) / 2))
  else
    nil
  end
end
|> Enum.filter(&(&1))
|> Enum.sum()

with the handy valid function, we go through each update, and if its valid, we calculate the midpoint, fetch it, and sum all midpoints


part 2

for update <- updates do
  is_valid = check_valid.(update)

  if is_valid do
    nil
  else
    update
    |> Enum.sort(fn a, b ->
      Enum.member?(Map.get(rule_lookup, a, []), b)
    end)
    |> Enum.at(floor(length(update) / 2))
  end
end
|> Enum.filter(&(&1))
|> Enum.sum()

part 2 took me a bit longer than expected, and i didn’t first know the trick to it

thankfully riley gave me a tip that a sort function might be all you need, which did pass my mind as i was considering this problem, but i thought wouldn’t be enough

low and behold, you can indeed just Enum.sort

so yeah, we use check_valid again, but this time, we find things that are not valid, use Enum.sort with a lookup into the rule_lookup like before, get the midpoint, throw in a filter on the identity function to filter out nil, then sum

amazin


the full solution can be found [here]



others

[ericthomasca/adventofcode2024] golang

200 line long go solution, quite lengthy!

i must say though, good function usage

func getCorrectOrders(filename string) ([][]int, error)
func splitInput(filename string) ([][]int, [][]int, error)
func isOrderCorrect(order []int, rules [][]int) bool
func reorderPages(order []int, rules [][]int) []int
func stringToSlices(line string, appendingSlice [][]int, splitChar string) ([][]int, error)

as well as nice variable names

quite the hand rolled sorting function, but it does the thing

nice

[M-ArafatZaman/advent-of-code-2024] python

getting both answers at once, nice

always like seeing defaultdict being pulled out, for anyone who doesn’t know about it, [you should check it out], it sparks joy

getting both the ruleset backwards and forwards for validation, mine don’t do that, but yours does, and does counts and such with some comparisons?

interesting

nice

[mynameisgump/advent-of-code] python

defaultdict would come in handy here to make code like this:

rulebook = {}

for rule in rules:
    if rule[0] in rulebook:
        rulebook[rule[0]].append(rule[1])
    else:
        rulebook[rule[0]] = [rule[1]]

not need to check the dict when inserting

nice and simple rules though, tidy

[terales/advent-of-code] python

defaultdict usage! this is all i seem to have to say today!

good variable names, good funcs, very nice and epic

the logic is less easy to follow with a day like today, i can’t tell if people have similar solutions, or i can’t tell the difference between them very well

[ShevinuM/Advent-of-Code-2024] golang

sort is giving hadouken code

nice check function though, pretty compact:

func check(pageOrder map[int][]int, update []int) (bool, int) {
    for i := 0; i < len(update); i++ {
        val := update[i]
        for j := i; j < len(update); j++ {
           val2 := update[j]
           if postVals, exists := pageOrder[val2]; exists {
               for _, postVal := range postVals {
                   if postVal == val {
                       return false, j
                   }
               }
           }
        }
    }
    return true, -1
}

tidy

[Mudkip/AdventOfCode] elixir

mudkip pulling out good ol reduc for page cecking, with a seen lookup

you could use [Enum.reduce_while], so once you know something is no longer valid, you can just {:halt, false} to break out of iteration

since i myself have written this in elixir, who knew, i have stuff to actually say about this one!

sort function, more busy than mine, but also more readable

tidy, thank you mudkip

[ncashin/aoc2024] elixir

hello other elixir do-er

a very sad

defmodule Main do
end

at the top of your code

mudkip had all the defmodule, you have none!

you could also do with a Enum.reduce_while

but wait… you don’t break out your validation logic for part 1 for use in part 2, and your part 2 does sorting on everything, including things that are already valid, and you Enum.sum() all of them…

but… you minus the result from part 2, from the result of everything, to get your part 2 answer

clever bugger

[DanielPower/AdventOfCode] rust

much more input parsing than actual code for part 2

also a sort, but woah, lets take a look at this sort:

update.sort_by(|a, b| match ordering_rules.get(a) {
    Some(rules) => {
        if rules.contains(b) {
            std::cmp::Ordering::Less
        } else {
            std::cmp::Ordering::Greater
        }
    }
    None => std::cmp::Ordering::Equal,
});

std::cmp, tidy

[evaan/AdventOfCode] golang
if one == two {

nice code evan

evan out here doing maths

one of the more compact solutions, i like the approach to use a labeled jump to the start after fixing the invalids, and if you make it to the end of the loop, you add it to list of correct things

tidy

[djrideout/advent2024] rust

nice get_matching_updates function for either spitting out correct or invalid updataes

and, tidy sort function, looks similar to mine, i think?

love rusts Ordering, so tidy

[nint8835/advent-of-code] f#

41 lines of f#, nice

let originallySorted, originallyUnsorted =
    updates
    |> Array.map (fun update ->
        (update,
         update
         |> Array.sortWith (fun a b ->
             rules |> Map.tryFind (a, b) |> Option.defaultValue 0)))
    |> Array.partition (fun (original, sorted) -> original = sorted)

this is clever, sort everything, and then split apart the list depending on if the original and sorted versions are theh same, nice!

[GirishVerm/aoc2024] python

some dicts, some sets, some appends, some code

its python, not much to see, to be honest

regardless, nice

[ThatGravyBoat/Advent-of-Code-2024] rust

nice usage of Result, your validation function returns the indexes of a problem once it bumps into it, and just swaps the values, until there is no more error values returned by the finction

for mut pages in unordered_pages {
    loop {
        if let Err((one, two)) = is_abiding_by_rules(&pages, &before) {
            pages.swap(one, two)
        } else {
            ordered_pages.push(pages);
            break
        }
    }
}

tidy

[GreyGrisGrey] python

[PEP 3136] is rejected, sad, was about to be like um aktually use labled loops like others, but not a python feature it seems

line 14, is quite the line:

orderings[len(orderings)-1][len(i.split(","))-1] = orderings[len(orderings)-1][len(i.split(","))-1].strip()

its giving jack the beanstalk

[STollenaar/AdventOfCode] golang

stephen out here with sneaky advent of code internal tooling for input parsing, clean

import (
    ...

    "github.com/STollenaar/AdventOfCode/internal"
)

func main() {
    lines := internal.Reader()
    ...
}

looks like go does have the ability to [jump to labels], nice


finishing this up on day 11, before i’ve actually started my own day 11 solution…





any thoughts about any of the above?

reach out:




we totally got this bread 🥪