Day 7
Mix.install([
{:req, "~> 0.4.5"},
{:kino, "~> 0.11.3"}
])
Input
session = System.fetch_env!("LB_AOC_SESSION")
input =
Req.get!(
"https://adventofcode.com/2023/day/7/input",
headers: [{"Cookie", ~s"session=#{session}"}]
).body
Kino.Text.new(input, terminal: true)
Part 1
hands =
for line <- String.split(input, "\n", trim: true) do
[hand, bid] = String.split(line)
{String.graphemes(hand), String.to_integer(bid)}
end
defmodule Part1 do
@powers (2..9 |> Enum.map(&Integer.to_string/1)) ++ ["T", "J", "Q", "K", "A"]
def card_power(card), do: Enum.find_index(@powers, &(&1 == card))
def hand_power(cards) do
hand = Enum.frequencies(cards)
size = map_size(hand)
sets = Map.values(hand)
cond do
size == 1 -> 6
size == 2 and Enum.any?(sets, &(&1 == 1 or &1 == 4)) -> 5
size == 2 and Enum.any?(sets, &(&1 == 2 or &1 == 3)) -> 4
size == 3 and Enum.any?(sets, &(&1 == 3)) -> 3
size == 3 and Enum.any?(sets, &(&1 == 2)) -> 2
size == 4 -> 1
true -> 0
end
end
def total_winnings(hands, hp_fun, cp_fun) do
hands
|> Enum.map(fn {cards, bid} ->
{hp_fun.(cards), Enum.map(cards, cp_fun), bid}
end)
|> Enum.sort(fn {hp1, cp1, _}, {hp2, cp2, _} ->
if hp1 == hp2, do: cp1 <= cp2, else: hp1 < hp2
end)
|> Enum.with_index(1)
|> Enum.map(fn {{_, _, bid}, rank} -> bid * rank end)
|> Enum.sum()
end
end
Part1.total_winnings(hands, &Part1.hand_power/1, &Part1.card_power/1)
Part 2
defmodule Part2 do
@five_of_akind 6
@four_of_a_kind 5
@full_house 4
@three_of_a_kind 3
@two_pair 2
@one_pair 1
@high_card 0
@joker_powers ["J"] ++ (2..9 |> Enum.map(&Integer.to_string/1)) ++ ["T", "Q", "K", "A"]
def joker_card_power(card), do: Enum.find_index(@joker_powers, &(&1 == card))
def joker_hand_power(cards) do
hand = Enum.frequencies(cards)
size = map_size(hand)
sets = Map.values(hand)
jokers = Map.get(hand, "J", 0)
cond do
size == 1 ->
@five_of_akind
size == 2 and Enum.any?(sets, &(&1 == 1 or &1 == 4)) ->
if jokers > 0, do: @five_of_akind, else: @four_of_a_kind
size == 2 and Enum.any?(sets, &(&1 == 2 or &1 == 3)) ->
if jokers > 0, do: @five_of_akind, else: @full_house
size == 3 and Enum.any?(sets, &(&1 == 3)) ->
if jokers > 0, do: @four_of_a_kind, else: @three_of_a_kind
size == 3 and Enum.any?(sets, &(&1 == 2)) ->
case jokers do
2 -> @four_of_a_kind
1 -> @full_house
_ -> @two_pair
end
size == 4 ->
if jokers > 0, do: @three_of_a_kind, else: @one_pair
true ->
if jokers == 1, do: @one_pair, else: @high_card
end
end
end
Part1.total_winnings(hands, &Part2.joker_hand_power/1, &Part2.joker_card_power/1)