Day 4
Mix.install([
{:kino, "~> 0.11.3"}
])
Solutions
input = Kino.Input.textarea("Please paste your input file:")
Day 4: Scratchcards
For example:
Card 1: 41 48 83 86 17 | 83 86 6 31 17 9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3: 1 21 53 59 44 | 69 82 63 72 16 21 14 1
Card 4: 41 92 73 84 69 | 59 84 76 51 58 5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11
tldr; find winning number count for each card double for the second, double again for third, etc. add up points.
So, in this example, the Elf’s pile of scratchcards is worth 13
points.
Part Two
tldr; number of winning numbers on card 1 gives you free cards:
- Card 1 has four matching numbers, so you win one copy each of the next four cards: cards 2, 3, 4, and 5.
- Repeat for card 2, etc.
In total, this example pile of scratchcards causes you to ultimately have 30
scratchcards!
input = Kino.Input.read(input)
defmodule Y2023.D4 do
@moduledoc """
https://adventofcode.com/2023/day/4
"""
def p1(input) do
input
|> parse_input()
|> Enum.map(&find_match_count/1)
|> Enum.map(&calculate_points/1)
|> Enum.sum()
end
def parse_input(input) do
input
|> String.split("\n", trim: true)
|> Enum.map(&String.trim/1)
end
def find_match_count(card) do
[_, winning_numbers, owned_numbers] = Regex.run(~r/^.+:([\s\d]+)\|([\d\s]+)/, card)
parse_numbers(winning_numbers)
|> MapSet.intersection(parse_numbers(owned_numbers))
|> MapSet.size()
end
defp calculate_points(winning_count) do
(2 ** (winning_count - 1)) |> trunc()
end
def parse_numbers(numbers_string) do
numbers_string
|> String.split(" ", trim: true)
|> MapSet.new()
end
def p2(input) do
input
|> parse_input()
|> Enum.map(&find_match_count/1)
|> Enum.map(&initialize_card/1)
|> claim_winning_cards()
|> Enum.reduce(0, fn {_bonus, count}, acc -> acc + count end)
end
def initialize_card(bonus) do
{bonus, 1}
end
def claim_winning_cards([]), do: []
def claim_winning_cards([{bonus, count} = card | rest]) do
{bonus_cards, remaining_cards} = Enum.split(rest, bonus)
rest_cards = apply_bonus(bonus_cards, count) ++ remaining_cards
[card | claim_winning_cards(rest_cards)]
end
def apply_bonus(cards, count) do
Enum.map(cards, fn {bonus, card_count} -> {bonus, card_count + count} end)
end
end
Y2023.D4.p1(input) |> IO.puts()
Y2023.D4.p2(input) |> IO.puts()
Observations
The part 1 and part 2 of each exercise help you think about how the code you write might be used in the future when requirements change. Yesterday, I had almost no reuse between parts 1 and 2. Today, I was able to reuse a good chunk of it.