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

AoC 2023 - Day 07

2023/lang-elixir/07.livemd

AoC 2023 - Day 07

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

Section

input_e1 =
  "day07-e1.txt"
  |> Kino.FS.file_path()
  |> File.read!()
  |> String.trim()

input =
  "day07.txt"
  |> Kino.FS.file_path()
  |> File.read!()
  |> String.trim()

[input_e1, input]
defmodule Solution do
  @cards ~w(2 3 4 5 6 7 8 9 T J Q K A)
         |> Enum.with_index(2)
         |> Enum.reduce(%{}, fn {v, k}, a -> Map.put(a, v, k) end)

  def part_1(input) do
    input
    |> String.split("\n")
    |> Enum.map(&parse_line/1)
    |> Enum.sort(&sort/2)
    |> Enum.with_index(1)
    |> Enum.map(fn {{_cards, _hand, bid}, k} ->
      k * bid
    end)
    |> Enum.sum()
  end

  def part_2(input) do
    input
    |> String.split("\n")
    |> Enum.map(&parse_line_2/1)
    |> Enum.sort(&sort/2)
    |> IO.inspect()
    |> Enum.with_index(1)
    |> Enum.map(fn {{_cards, _hand, bid}, k} ->
      k * bid
    end)
    |> Enum.sum()
  end

  def sort(
        {[a1, b1, c1, d1, e1], rank_1, _},
        {[a2, b2, c2, d2, e2], rank_2, _}
      ) do
    cond do
      rank_1 < rank_2 ->
        true

      rank_1 == rank_2 and a1 < a2 ->
        true

      rank_1 == rank_2 and a1 == a2 and b1 < b2 ->
        true

      rank_1 == rank_2 and a1 == a2 and b1 == b2 and c1 < c2 ->
        true

      rank_1 == rank_2 and a1 == a2 and b1 == b2 and c1 == c2 and d1 < d2 ->
        true

      rank_1 == rank_2 and a1 == a2 and b1 == b2 and c1 == c2 and d1 == d2 and e1 < e2 ->
        true

      true ->
        false
    end
  end

  defp parse_line(input) do
    [cards, bids] = String.split(input, " ")
    cards = cards |> String.codepoints() |> Enum.map(&amp;to_card/1)
    {cards, rank_hand(cards), String.to_integer(bids)}
  end

  defp parse_line_2(input) do
    [cards, bids] = String.split(input, " ")
    cards = cards |> String.codepoints() |> Enum.map(&amp;to_card_2/1)
    {cards, rank_hand_with_joker(cards), String.to_integer(bids)}
  end

  def to_card(card), do: Map.get(@cards, card)
  def to_card_2("J"), do: 1
  def to_card_2(card), do: to_card(card)

  def rank_hand(hand), do: hand |> Enum.frequencies() |> Map.values() |> Enum.sort() |> rank()

  def rank_hand_with_joker(hand) do
    rank = rank_hand(hand)
    wild = hand |> Enum.frequencies() |> Map.get(1)

    case {rank, wild} do
      {_, nil} -> rank
      # [1, 1, 1, 1, 1] -> [1, 1, 1, 2]
      {1, _} -> 2
      # [1, 1, 1, 2] -> [1, 1, 3]
      {2, _} -> 4
      # [1, 2, 2] -> [1, 3]
      {3, 1} -> 5
      # [1, 2, 2] -> [1, 4] if 2J
      {3, 2} -> 6
      # [1, 1, 3] -> [1, 4]
      {4, _} -> 6
      # [2, 3] - both can be J
      {5, _} -> 7
      {6, _} -> 7
      {7, _} -> rank
    end
  end

  def rank([1, 1, 1, 1, 1]), do: 1
  def rank([1, 1, 1, 2]), do: 2
  def rank([1, 2, 2]), do: 3
  def rank([1, 1, 3]), do: 4
  def rank([2, 3]), do: 5
  def rank([1, 4]), do: 6
  def rank([5]), do: 7
end
Solution.part_1(input)
Solution.part_2(input)