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

2023 - day 7

2023/elixir/day-7.livemd

2023 - day 7

Mix.install([
  {:kino, "~> 0.11.3"}
])

Section

input = Kino.Input.textarea("input")
input = Kino.Input.read(input)

sample_input = """
32T3K 765
T55J5 684
KK677 28
KTJJT 220
QQQJA 483
"""

Section

defmodule Day7 do
  defmodule Part1 do
    @card_order ~c"AKQJT98765432"
                |> Enum.reverse()
                |> Enum.with_index(0)
                |> Enum.into(%{})

    def solve(input_str) do
      input_str
      |> Day7.parse()
      |> Enum.sort(&Day7.card_sort(&1, &2, fn cards -> cards_label(cards) end, @card_order))
      |> Enum.with_index(1)
      |> Enum.map(fn {{_, bid}, rank} -> bid * rank end)
      |> Enum.sum()
    end

    defp cards_label(cards) do
      cards_counter = Enum.frequencies(cards)

      counts = Map.values(cards_counter) |> Enum.sort(:desc)
      determine_card_label(counts)
    end

    def determine_card_label(counts)
    def determine_card_label([5]), do: {1, :five_of_a_kind}
    def determine_card_label([4, 1]), do: {2, :four_of_a_kind}
    def determine_card_label([3, 2]), do: {3, :full_house}
    def determine_card_label([3, 1, 1]), do: {4, :three_of_a_kind}
    def determine_card_label([2, 2, 1]), do: {5, :two_pair}
    def determine_card_label([2, 1, 1, 1]), do: {6, :one_pair}
    def determine_card_label(_), do: {7, :high_card}
  end

  def card_sort({cards1, _bid1}, {cards2, _bid2}, label_fn, card_order_map) do
    cards1_label = label_fn.(cards1)
    cards2_label = label_fn.(cards2)

    cond do
      cards1_label < cards2_label ->
        false

      cards1_label > cards2_label ->
        true

      true ->
        Day7.compare_cards(cards1, cards2, card_order_map)
    end
  end

  def parse(input_str) do
    input_str
    |> String.split(["\n", " "], trim: true)
    |> Enum.chunk_every(2)
    |> Enum.map(fn [cards, bid] ->
      {String.to_charlist(cards), String.to_integer(bid)}
    end)
  end

  def compare_cards(cards1, cards2, card_order_map) do
    Enum.zip(cards1, cards2)
    |> Enum.reduce_while(true, fn {c1, c2}, lt? ->
      cond do
        card_order_map[c1] < card_order_map[c2] -> {:halt, true}
        card_order_map[c1] > card_order_map[c2] -> {:halt, false}
        true -> {:cont, lt?}
      end
    end)
  end
end
Day7.Part1.solve(input)

Part 2

defmodule Day7.Part2 do
  @card_order2 ~c"AKQT98765432J" |> Enum.reverse() |> Enum.with_index(0) |> Enum.into(%{})

  def solve(input_str) do
    input_str
    |> Day7.parse()
    |> Enum.sort(&amp;Day7.card_sort(&amp;1, &amp;2, fn cards -> cards_label(cards) end, @card_order2))
    |> Enum.with_index(1)
    |> Enum.map(fn {{_, bid}, rank} -> bid * rank end)
    |> Enum.sum()
  end

  def cards_label(cards) do
    cards_counter = Enum.frequencies(cards)

    counts = Map.values(cards_counter) |> Enum.sort(:desc)
    determine_card_label(counts, cards_counter)
  end

  def determine_card_label([5], _), do: {1, :five_of_a_kind}
  def determine_card_label([4, 1], %{?J => _}), do: {1, :five_of_a_kind}
  def determine_card_label([3, 2], %{?J => _}), do: {1, :five_of_a_kind}
  def determine_card_label([3, 1, 1], %{?J => _}), do: {2, :four_of_a_kind}
  def determine_card_label([2, 2, 1], %{?J => 2}), do: {2, :four_of_a_kind}
  def determine_card_label([2, 2, 1], %{?J => 1}), do: {3, :full_house}
  def determine_card_label([2, 1, 1, 1], %{?J => _}), do: {4, :three_of_a_kind}
  def determine_card_label([1, 1, 1, 1, 1], %{?J => 1}), do: {6, :one_pair}
  def determine_card_label(counts, _), do: Day7.Part1.determine_card_label(counts)
end
Day7.Part2.solve(input)