Powered by AppSignal & Oban Pro

Day 7

2023/day_7.livemd

Day 7

Mix.install([:kino])

Get input

input =
  Kino.Input.textarea("Paste input here",
    default: """
    32T3K 765
    T55J5 684
    KK677 28
    KTJJT 220
    QQQJA 483
    """
  )
parsed_input =
  Kino.Input.read(input)
  |> String.split("\n", trim: true)
  |> Stream.map(&String.split(&1, " "))

Part 1

defmodule CamelPoker.NormalRules do
  def to_card_value(card) do
    case card do
      "T" -> "10"
      "J" -> "11"
      "Q" -> "12"
      "K" -> "13"
      "A" -> "14"
      n -> n
    end
    |> then(&String.to_integer/1)
  end

  def compare({hand_1, _}, {hand_2, _}) do
    type_1 = hand_type(hand_1)
    type_2 = hand_type(hand_2)

    cond do
      type_1 > type_2 ->
        :lt

      type_1 < type_2 ->
        :gt

      true ->
        Stream.zip(hand_1, hand_2)
        |> Enum.reduce_while(:eq, fn
          {c, c}, acc -> {:cont, acc}
          {c1, c2}, _acc when c1 > c2 -> {:halt, :lt}
          {c1, c2}, _acc when c1 < c2 -> {:halt, :gt}
        end)
    end
  end

  def hand_type(hand) do
    case Enum.sort(hand) do
      # five of a kind
      [a, a, a, a, a] -> 6
      # four of a kind
      [a, a, a, a, _b] -> 5
      [_b, a, a, a, a] -> 5
      # full house
      [a, a, a, b, b] -> 4
      [a, a, b, b, b] -> 4
      # three of a kind
      [a, a, a, _b, _c] -> 3
      [_a, b, b, b, _c] -> 3
      [_a, _b, c, c, c] -> 3
      # two pair
      [a, a, b, b, _c] -> 2
      [_a, b, b, c, c] -> 2
      [a, a, _b, c, c] -> 2
      # one pair
      [a, a, _b, _c, _d] -> 1
      [_a, b, b, _c, _d] -> 1
      [_a, _b, c, c, _d] -> 1
      [_a, _b, _c, d, d] -> 1
      # high card
      [_a, _b, _c, _d, _e] -> 0
    end
  end
end
parsed_input
|> Enum.map(fn [hand, bid] ->
  parsed_hand =
    hand
    |> String.split("", trim: true)
    |> Stream.map(&CamelPoker.NormalRules.to_card_value/1)

  {parsed_hand, String.to_integer(bid)}
end)
|> Enum.sort(CamelPoker.NormalRules)
|> Enum.reverse()
|> Stream.with_index()
|> Stream.map(fn {{_, bid}, rank} -> (rank + 1) * bid end)
|> Enum.sum()

Part 2

defmodule CamelPoker.JokerRules do
  def to_card_value(card) do
    case card do
      "T" -> "10"
      "J" -> "1"
      "Q" -> "12"
      "K" -> "13"
      "A" -> "14"
      n -> n
    end
    |> then(&String.to_integer/1)
  end

  def compare({hand_1, _}, {hand_2, _}) do
    type_1 = hand_type(hand_1)
    type_2 = hand_type(hand_2)

    cond do
      type_1 > type_2 ->
        :lt

      type_1 < type_2 ->
        :gt

      true ->
        Stream.zip(hand_1, hand_2)
        |> Enum.reduce_while(:eq, fn
          {c, c}, acc -> {:cont, acc}
          {c1, c2}, _acc when c1 > c2 -> {:halt, :lt}
          {c1, c2}, _acc when c1 < c2 -> {:halt, :gt}
        end)
    end
  end

  def hand_type(hand) do
    case Enum.sort(hand) do
      # five of a kind
      [a, a, a, a, a] -> 6
      [1, a, a, a, a] -> 6
      [1, 1, a, a, a] -> 6
      [1, 1, 1, a, a] -> 6
      [1, 1, 1, 1, _a] -> 6
      # four of a kind
      [a, a, a, a, _b] -> 5
      [1, a, a, a, _b] -> 5
      [1, _a, b, b, b] -> 5
      [1, 1, a, a, _b] -> 5
      [1, 1, _a, b, b] -> 5
      [1, 1, 1, _a, _b] -> 5
      [_b, a, a, a, a] -> 5
      # full house
      [a, a, a, b, b] -> 4
      [1, a, a, b, b] -> 4
      [1, 1, _a, b, b] -> 4
      [a, a, b, b, b] -> 4
      [1, _a, b, b, b] -> 4
      # three of a kind
      [a, a, a, _b, _c] -> 3
      [1, a, a, _b, _c] -> 3
      [1, 1, _a, _b, _c] -> 3
      [1, _a, b, b, _c] -> 3
      [_a, b, b, b, _c] -> 3
      [_a, _b, c, c, c] -> 3
      [1, _a, _b, c, c] -> 3
      # two pair
      [a, a, b, b, _c] -> 2
      [1, _a, b, b, _c] -> 2
      [_a, b, b, c, c] -> 2
      [a, a, _b, c, c] -> 2
      [1, _a, _b, c, c] -> 2
      # one pair
      [a, a, _b, _c, _d] -> 1
      [1, _a, _b, _c, _d] -> 1
      [_a, b, b, _c, _d] -> 1
      [_a, _b, c, c, _d] -> 1
      [_a, _b, _c, d, d] -> 1
      # high card
      [_a, _b, _c, _d, _e] -> 0
    end
  end
end
parsed_input
|> Stream.map(fn [hand, bid] ->
  parsed_hand =
    hand
    |> String.split("", trim: true)
    |> Enum.map(&CamelPoker.JokerRules.to_card_value/1)

  {parsed_hand, String.to_integer(bid)}
end)
|> Enum.sort(CamelPoker.JokerRules)
|> Enum.reverse()
|> Stream.with_index()
|> Stream.map(fn {{_, bid}, rank} -> (rank + 1) * bid end)
|> Enum.sum()