day 1
its december, so that means, advent of code time, lets go
im hyped to give another go at rambles this year, i started working on rambles in 2023, but didn’t actually make it to publishing my work-in-progress day 1 post, oh well
for context of those that didn’t see my 2022 rambles, this is a spot where i discuss advent of code solutions, starting with my own (that i attempt to break down step by step), then digging into solutions by others, giving commentary on them, sometimes throwing in some suggestions
as expected, day 1 is cozy, and the breakdown of how to come to the final answer in the example, provided in the question, sort of explains the steps to write the program in the language of your choice
im returning to my beloved, [elixir], this year, and it seems like i’m not the only this time around!
both in mine, and probably other functional language solutions, you’ll likely see the pipe operator, |>
,
if you want a little elixir-focused primer on said operation, check out elixir schools [Pipe Operator] lesson
not much activity on the [proposal to add this operator to js], but a man can wish…
click to view my solution
given the sample input:
3 4
4 3
2 5
1 3
3 9
3 3
assuming input
is the above as one big string, lets do some input parsing:
{left_list, right_list} =
input
|> String.split("\n")
|> Enum.map(fn line ->
[l, r] = line
|> String.split(" ")
|> Enum.map(&(String.to_integer(String.trim(&1))))
{l,r}
end)
|> Enum.unzip()
first, split string on newline character, returns a list of lines
map over each line, and then, split on that line by two spaces to break up 3 4
to ["3", "4"]
then, map over the trimmed value of each of those strings (trimmed since i think for some reason i had spaces left in my input? skill issue), and convert them from string into integer
in this map statement, i return a tuple of the left value, and the right value
this means, before the final Enum.unzip
, the data structure looks like:
[{3, 4}, {4, 3}, {2, 5}, {1, 3}, {3, 9}, {3, 3}]
since i want a list of all of the left values, and all of the right values, the structure i’ve created is what Enum.zip
returns,
so, Enum.unzip
, gives me:
{[3, 4, 2, 1, 3, 3], [4, 3, 5, 3, 9, 3]}
nice
part 1
Enum.zip(Enum.sort(left_list), Enum.sort(right_list))
|> Enum.map(fn {l, r} ->
abs(l - r)
end)
|> Enum.sum()
its day 1, therefore part 1 fits into a small footprint, no need to even break it up into multiple explanatory chunks
since the question is asking to compare smallest numbers in both right and left lists, ascending,
sorting both lists, then re-Enum.zip
-ing them, gives us the numbers that need to be
compared together in tuple pairs
so, map over tuple pairs, and take the absolute value of the l - r
, to get the distances
then, sum the result via Enum.sum
, boom, we get the 11
total distance the example asks for, and i get a star!
part 2
right_counts = right_list
|> Enum.group_by(&(&1))
|> Enum.map(fn {k, v} ->
{k, Enum.count(v)}
end)
|> Map.new()
for n <- left_list do
n * Map.get(right_counts, n, 0)
end
|> Enum.sum()
part 2 is also nice and compact
first, lets discuss the creation of right_counts
right_counts = right_list
|> Enum.group_by(&(&1))
|> Enum.map(fn {k, v} ->
{k, Enum.count(v)}
end)
|> Map.new()
here, i take the right list, and use Enum.group_by
(passing in an identity anonymous function), which gives me:
%{3 => [3, 3, 3], 4 => [4], 5 => [5], 9 => [9]}
i map over that to return the values of this map as the count rather than a list of each value, which gives me:
[{3, 3}, {4, 1}, {5, 1}, {9, 1}]
i then pipe that to Map.new()
, since iterating over the Map
returned by Enum.group_by
gets mucked up by the map, giving me the map:
%{3 => 3, 4 => 1, 5 => 1, 9 => 1}
so a lookup from value ->
how many times in the right list it exists
paired with this list, the last section is quite easy to grok:
for n <- left_list do
n * Map.get(right_counts, n, 0)
end
|> Enum.sum()
iterate over the left list, use the value in the right list (or 0 if no value) to multiply with the value in the left list we’re on
then, do a little bit of Enum.sum
we get the example answer, and after running my test input, i get a second star!
the full solution can be found [here]
others
time to peep others solutions!
i’ve dug through people who i checked out in 2022, and seen people post links to their advent of code solutions in the [MUN Computer Science Society] discord, and the [CTSNL] discord, so i’ve compiled a list of people to peep for solutions and give some rambles here
if you’ve ended up being included and would rather not be, reach out to my via the message box at the bottom of the page, sorry!
likely not much to say today here since its day 1 and all, but likely the most to say from a sheer volume perspective since day 1 means many solutions!
[DanielPower/AdventOfCode] rust
[Mudkip/AdventOfCode] elixirdan out here with separate rust projects for part 1 and 2 respectively, an interesting choice that seems to permit for zero code sharing between parts other than either a registry being used, or good ol’
ctrl+c
ctrl-v
:Dthe solution is good, lots of
.unwrap().unwrap().unwrap()
but that seems kosher for advent of code rust solutionsa.sort(); b.sort();
seems like rusts built in sort method on a
Vec
modifies in-place? probably a better representation of whats actually going on under the hood that other languages hide away, but obviously from my functional-leaning-at-the-moment brain, this feels not so greatits a good rust solution though, thank u dan
[Keenan-Nicholson/AdventOfCode] golangmudkip out here with both a python solution (that was seemingly built quickly sometime after the problem coming out, since mudkip was high up on the leaderboard the morning i woke up on the 1st) and a elixir solution
i also have context that you wouldn’t get from looking at the repo, but after doing it in python, mudkip wanted to reach for something different, and originally also tried rust, before giving up due to fighting with the borrow checker, which is honestly quite understandable if you’re used to quick dynamic languages for quick iteration
the solution i want to talk about, is an elixir solution!
{l, r} = File.stream!("1.in") |> Enum.map(&String.trim/1) |> Enum.map(&String.split(&1, " ")) |> Enum.map(fn list -> Enum.map(list, &String.to_integer/1) end) |> Enum.map(&List.to_tuple/1) |> Enum.unzip() r_counts = Enum.frequencies(r) part1 = Enum.zip(Enum.sort(l), Enum.sort(r)) |> Enum.map(fn {x, y} -> abs(x - y) end) |> Enum.sum() part2 = l |> Enum.map(fn x -> x * Map.get(r_counts, x, 0) end) |> Enum.sum()
small enough to just include inline here, this is a nice solution, similar to mine in many ways, honestly more pipeline-heavy initially, with a great usage of [Enum.frequencies] that seems to do the count logic i was calculating manually with a
group_by
, niceonly tip would be for maybe using the [& Shorthand] for a bit more code-golfying :)
[ncashin/aoc2024] elixir“When you have to import 14 things to write a loop”
- Mudkip
first of go code from keenan, looking swell
classic walrus operator error catching
nitpick, but variables list
location_IDs_txt
could probably just belocation_ids_txt
, mid-variable capitalization does not feel like a good style79 lines feels like a bit much for a day 1 solution, but it is go which is usually a super verbose lang, and it is first keenan go code, so i must respect jumping into the deep end and the overall grind
[mynameisgump/advent-of-code] pythonnatalie out here, on my recommendation, also pulling out the elixir, yay
{:ok, input} = File.read("input.txt") split_ints = String.split(input) |> Enum.map(&String.to_integer/1) left_array = Enum.take_every(split_ints, 2) |> Enum.sort() right_array = Enum.drop_every(split_ints, 2) |> Enum.sort() part1 = Enum.zip(left_array, right_array) |> Enum.map(fn {x, y} -> abs(x - y) end) |> Enum.sum() right_number_of_occurences = Enum.reduce(right_array, %{}, fn x, accumulator -> Map.update(accumulator, x, 1, &(&1 + 1)) end) part2 = Enum.map(left_array, fn x -> x * Map.get(right_number_of_occurences, x, 0) end) |> Enum.sum()
another solution smol enough i might as well include it here as-is
i think the pipeline operators here could be newline’d, it would increase amount of lines, but bring line length down to a small amount
take_every
/drop_every
to split up the input, followed by a sort, very cozya reducer being pulled out for day 1! doing a similar thing i was doing with group_by by hand, my bet is not knowing the group_by operator exists, which is totally fair for someone writing elixir for the first time (but mudkip did pull out frequencies, so you have competition on who is the best elixir freshie!)
then a map sum
good job nat
[M-ArafatZaman/advent-of-code-2024] pythongump solution gump solution
i am a [pathlib] stan, so whenever i see
open
code, i must say, use pathlib instead its so much cleaner with a nicer apisolutions starting to feel a little déjà vu-ey, feels quite similar to dans, but there is only so much room for differences this early on
argparse being used for a lil runner, nice
[STollenaar/AdventOfCode] golangNOTE: when i review’d this solution, it was go, but arafat has since gone on to do the rest of the solutions in python, so it says python, and links to python, but you will find a
.go
file in the same dir :Da more concise go solution from arafat (sorry keenan <3)
i like having the input parsing in its own func here
google seems to indicate go does not have
abs
, but it looks like it was added at a later date? [math.Abs] seems to be a thingaha! it is, just checked keenans solution, it is using
math.Abs
, maybe this keenan guy is alright after all…good go solution here!
[ThatGravyBoat/Advent-of-Code-2024] rusttaking the lead for the least amount of line solution is stephen, coming in at 53 lines (5 less than arafat)
this one gets the count in the right list every time it parses a value in the left list, which solves the problem sure, but does seem like a waste of computation, but also i guess needs little memory to keep around other than the lists themselves?
stephen also found
math.Abs
, niceanotha good go solution here, even if interesting un-optimization
[hkings google sheets] sheetsgravy boat out here with a interesting repo that seems to permit for all problems to live in the same spot, just with the entrypoint
main.rs
needing to be switched out to pull in the correct crate by editing source code, which honestly isn’t half bad, a lot less busy than pulling something in like [cargo-aoc], that i’m not dissing in anyway, but nice to see a decently clean zero dep (other than regex, at the moment) rust project!some unwraps, as expected
but here we go, part one and two in different functions, using the same input parsing, good to see
HashMap
being used as a counter lookup herenot much else to say, is rust, is good
[djrideout/advent2024] rustHAMZAH!
back with some sheets! he pulled them out before and here he goes pulling them out again!
love to see some sheets in action, row a and b are the input lists already cleaned up,
- row d:
=SORT(A:A)
- row e:
=SORT(B:B)
- row f:
=ABS(D2-E2)
- row g:
=SUM(F:F)
->
the madman gets part 1- row i:
=A*COUNTIF($B$2:B,A)
the madman uses his existing part 1 sorted lists andCOUNTIF
to do some countin’- row j:
=SUM(I:I)
->
the madman just sums again
[nint8835/advent-of-code] f#dj rust time, this time someone using an off-the-shelf runner, [aoc-runner], which honestly means this looks nice and clean
more of the same approaches i’ve seen before, a little bit of sneaky pattern matching on part 2 is cool to see, but as mentioned before, early days don’t leave a lot of room for self expression
this statement goes for dj, but obviously everyone here, excited for this stuff to get a bit more meaty so i can see more varied solutions!
[ranguli/advent-of-code] cppits riley, so you KNOW its
f#
unctionalfeels very similar in vibes to my elixir solution, plenty of pipes, good usage of stdlib, sparking joy
Array.countBy
cool, and cursed thatid
in f# is a builtin for the identity function…like as someone who has done enough category theory learning to know its an important concept in functional programming, its interesting to have it as a easily accessible builtin, i assume something that can be shadowed quite easily as there will be many-a-cases people use
id
the variable not the top-level functionexcited to see more nint f#
[briannamcdonald/advent-of-code-2024] pythonranguli out here with c++, i’d expect to be seeing two less characters in that file extension, but i’ll accept it
some
std:ifstream
iteration, pipe operations galore, pretty standard c++ code (i think, i am not c++ brained)the type of code that i just know [makes keith proud]
[omega7379/Advent-of-Code] pythonnice and compact part 1 & 2, after input parsing, both parts are just one-liners:
total_dist = sum([abs(left_num - right_num) for left_num, right_num in zip(sorted(left_list), sorted(right_list))])
a good part 1, cool seeing zip usage
similarity_score = sum([(num * right_list.count(num)) for num in left_list])
a part 2 smaller than part 1! very nice, count usage sparks joy
v-nice, could probs just have a single
day1.py
that does both things, so input parsing code could be shared
[terales/advent-of-code] pythonneiro time
very nice and simple part 1, same as brianna with a smaller part 2!
only tidbit would be not having variables names
LikeThis
, should always belike_this
[zcvaters/adventofcode2024] swiftsome python from alex, feeling very familiar at this point after reviewing python a few times in a row
interesting seeing your sample input included inside the python file, i guess one less step to worry about removing the need for file parsing! (should probably revert that to the smaller input shown in the example rather than your puzzle input however)
[evaan/AdventOfCode] golangthe one, and only, swift solution, from the one, and only, zach
interesting approach in part 1 of not sorting, but doing min on the two lists to find the smallest, calculating, and then removing the values from the list
my brain says this isn’t as efficient as just sorting and then calculating, but its interesting to see a very non expected approach to this question
[ericthomasca/adventofcode2024] golanganother go solution oh yeah, hello evan
doing the list creation and the count lookup creation in the input parsing, nice
the for loop for i range to 1000 is a bit cursed, but yeah, you got the stars, you did the thing
[ShevinuM/Advent-of-Code-2024] golangmore go, this time from eric
comments galore, bit more verbose than other solutions, but sparking joy and doing the stuff
i like
and last but not least, even more go, this time from shevinu
nothing really to add, its more go, this one does not take cake for being smallest but does feel nice and concise
epic
overall, a nice day 1, good set of people to go over solutions for this it feels as well! a lot more go than expected, and glad to have some people alongside me for elixir :)
lets see how long i can keep these rambles up for, i’m currently finishing up this post at 1:14 AM
on day 4, so i have a backlog of two more days to get to, and then to do todays,
but hopefully i get into a roll here (and people fall off so i have less to review :D)
eventually the problems become meaty to the point i need more time to focus on them than i have to write about, so that will liiikely be the end
but at least for the next few days, lets do this
any thoughts about any of the above?
reach out: