seeplusplus
Hello all, hopefully I post this before someone else does and I don’t dupe.
IMO Day 4 was much easier than Day 3 (yay, I can sleep before work!)
My set-up:
defmodule Card do
defp to_list_numbers(str) do
str
|> String.split(" ")
|> Enum.filter(fn s -> String.length(s) !== 0 end)
|> Enum.map(fn u ->
{i, _} = Integer.parse(u)
i
end)
end
def parse("Card " <> rest) do
[_, numbers] = rest |> String.split(":")
[winning, player] = numbers |> String.split("|")
[winning |> String.trim() |> to_list_numbers(), player |> String.trim() |> to_list_numbers()]
end
end
cards = for card <- input |> String.split("\n"),
[winning_numbers, our_numbers] = Card.parse(card) do
[winning_numbers, our_numbers]
end
Then part 1 was really simple (10 minutes from start for me to get here):
for [winning_numbers, our_numbers] <- cards
do
case count_winning_cards.(our_numbers, winning_numbers) do
n when n > 0 -> 2**(n-1)
0 -> 0
end
end |> Enum.sum()
Part 2 slightly less so (40 minutes from start to get here):
cards
|> Enum.with_index()
|> Enum.reduce(
List.duplicate(1, cards |> Enum.count()),
fn {[winning, player], idx}, copies ->
self_copies = Enum.at(copies, idx)
won = count_winning_cards.(player, winning)
slice = if won > 0, do: (idx+1)..(idx+won), else: ..
copies |> Enum.with_index() |> Enum.map(
fn {count, idx} ->
if idx in slice, do: count + self_copies, else: count
end
)
end
) |> Enum.sum()
The biggest time sink for me was remembering that 1..1 is a non-empty range in Elixir, so I needed to write the slice to be:
slice = if won > 0, do: (idx+1)..(idx+won), else: ..
Trending in Challenges
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself.
My main conc...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
Aetherus
You don’t have to convert those numeric strings to integers.
Here’s my code:
https://github.com/Aetherus/advent-of-code/blob/master/2023/day-04.livemd
seeplusplus
good point, probably just muscle memory from the previous days
bjorng
I also found today’s puzzle much easier than yesterday’s.
https://github.com/bjorng/advent-of-code-2023/blob/main/day04/lib/day04.ex
seeplusplus
Wow, today I learned
--, that’s a really clean way to get the winning numbers!christhekeele
Part Two really took me a minute, here.
Input
Processing
A simple map of id to map of mapsets of winners and picks helped me out, meaning that determining the number of winning picks was a
MapSet.intersection/2away. This is strictly more efficient than--/2, but probably doesn’t make a difference at these sizes.Types
Example Input
Source code available here.
Part One
Solution
Scoring a card is counting the number of winning picks, and then doing some
:math.pow. I’m getting better at breaking calculations in part one apart such that some functions prove useful later in part two.Source code available here.
Part Two
Solution
This took a while. The insights that got me over the finish line was that
This means if you start at the end of the cards, you can walk backwards through the card list determining each one’s value without recursive operations or actually generating copies of anything.
First I count how many wins each card has, then I generate a map of
id => [id]to reference what each card creates a copy of.Then I generate a map of how many copies each id must create, starting from the
last_id.list_idmust always haveMap.get(copies, last_id) == [], so its count is1;Map.get(copies, last_id -1)must be either[]or[last_id], and can reliably look up the number of copies sincelast_idmust have already been computed, etcera.Source code available here.
stevensonmt
Once again parsing the input was the biggest challenge. Once I had that both parts went quickly. I kept thinking the card IDs would be necessary in Part 2 but when they weren’t I didn’t bother cleaning them out of the code.
Full code here.
I’m sure there’s something really clever to be done with bit shifting for part 1 but I didn’t bother trying.
stevensonmt
I must be tired. I can’t seem to figure out how reversing the list helps you here. The thing that made it click for me was that for each copy operation you make as many copies as the number of current cards you have.
christhekeele
My solution skips creating any copies—instead I generate a map of what card ids would get copied. Then I start with the base case of “the last card cannot generate any copies as there is nothing left to copy, it must have the value of
1” and work backwards, translating each list of “these cards would get copied” to their innate value.The value of later cards must be computed by the time we get to any given earlier card going backwards. The resulting
countsmap is kind of odd—for the example set,Later cards like the final
6is only worth one point, but then the value of earlier cards factor in the repetition of their reference to later ones.I’m tired as well though, so I can’t tell if it’s clever or not.
mexicat
I just woke up so the main obstacle for me was figuring out what it wanted me to do for part 2
I couldn’t think about an efficient “math-y” way to do it for part 2, so I just pre-save the intersections in a map and check them all the time. I thought it was going to be super slow, but it finishes in ~140ms, which is good enough for me.
https://github.com/mexicat/aoc-2023/blob/main/lib/aoc/day_04.ex
Aetherus
I heard that when running
a -- b, the Erlang runtime will first convertbto a red-black tree ifbis long enough (maybe whenlength(b) > 32). If that’s the case, thena -- a -- bcan be as performant asMapSet.intersection(set1, set2).