Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 7: Camel Cards

2023/day_07.livemd

Day 7: Camel Cards

Mix.install([:kino])

input = Kino.Input.textarea("Please paste your input:")

Part 1

Run in Livebook

https://adventofcode.com/2023/day/7

data =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)

sets =
  data
  |> Enum.map(&String.split(&1, " "))
  |> Enum.map(fn [hand_str, bid_str] ->
    {hand_str |> String.split("", trim: true), bid_str |> String.to_integer()}
  end)
label_value =
  ~w(A K Q J T 9 8 7 6 5 4 3 2)
  |> Enum.reverse()
  |> Enum.with_index()
  |> Map.new()

type_value =
  ~w(five_of_a_kind four_of_a_kind full_house three_of_a_kind two_pair one_pair high_card)a
  |> Enum.reverse()
  |> Enum.with_index()
  |> Map.new()
defmodule Day07.Part1 do
  def type(hand) do
    hand
    |> Enum.frequencies()
    |> Map.values()
    |> Enum.sort(:desc)
    |> do_type()
  end

  defp do_type([5]), do: :five_of_a_kind
  defp do_type([4, 1]), do: :four_of_a_kind
  defp do_type([3, 2]), do: :full_house
  defp do_type([3 | _]), do: :three_of_a_kind
  defp do_type([2, 2 | _]), do: :two_pair
  defp do_type([2 | _]), do: :one_pair
  defp do_type(_), do: :high_card
end
sets
|> Enum.map(fn {hand, bid} ->
  type = Day07.Part1.type(hand)

  {hand, bid, type}
end)
|> Enum.sort_by(fn {labels, _bid, type} ->
  label_values = labels |> Enum.map(&label_value[&1]) |> List.to_tuple()
  {type_value[type], label_values}
end)
|> Enum.with_index(1)
|> Enum.map(fn {{_hand, bid, _}, rank} ->
  bid * rank
end)
|> Enum.sum()

Part 2

https://adventofcode.com/2023/day/7#part2

label_value =
  ~w(A K Q T 9 8 7 6 5 4 3 2 J)
  |> Enum.reverse()
  |> Enum.with_index()
  |> Map.new()
defmodule Day07.Part2 do
  def type(hand) do
    hand
    |> Enum.frequencies()
    |> Enum.sort_by(fn {_label, count} -> count end, :desc)
    |> then(fn label_count_list ->
      j_index = label_count_list |> Enum.find_index(fn {label, _count} -> label == "J" end)

      case j_index do
        nil ->
          label_count_list

        j_index ->
          {{"J", j_count}, poped_label_count_list} = label_count_list |> List.pop_at(j_index)

          case j_count do
            5 ->
              label_count_list

            j_count ->
              poped_label_count_list
              |> List.update_at(0, fn {label, count} -> {label, count + j_count} end)
          end
      end
    end)
    |> Enum.map(fn {_label, count} -> count end)
    |> do_type()
  end

  defp do_type([5]), do: :five_of_a_kind
  defp do_type([4, 1]), do: :four_of_a_kind
  defp do_type([3, 2]), do: :full_house
  defp do_type([3 | _]), do: :three_of_a_kind
  defp do_type([2, 2, 1]), do: :two_pair
  defp do_type([2 | _]), do: :one_pair
  defp do_type(_), do: :high_card
end
sets
|> Enum.map(fn {hand, bid} ->
  type = Day07.Part2.type(hand)

  {hand, bid, type}
end)
|> Enum.sort_by(fn {labels, _bid, type} ->
  label_values = labels |> Enum.map(&label_value[&1]) |> List.to_tuple()
  {type_value[type], label_values}
end)
|> Enum.with_index(1)
|> Enum.map(fn {{_hand, bid, _}, rank} ->
  bid * rank
end)
|> Enum.sum()