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

Day 07

day07.livemd

Day 07

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

IEx.Helpers.c("/Users/johnb/dev/2023adventOfCode/advent_of_code.ex")
alias AdventOfCode, as: AOC
alias Kino.Input

# Note: when making the next template, something like this works well:
#   `cat day04.livemd | sed 's/03/04/' > day04.livemd`
#

Installation and Data

input_p1example = Kino.Input.textarea("Example Data")
input_p1puzzleInput = Kino.Input.textarea("Puzzle Input")
input_source_select =
  Kino.Input.select("Source", [{:example, "example"}, {:puzzle_input, "puzzle input"}])
p1data = fn ->
  (Kino.Input.read(input_source_select) == :example &&
     Kino.Input.read(input_p1example)) ||
    Kino.Input.read(input_p1puzzleInput)
end

Part 1

defmodule Day07 do
  # Plan: sort by text sort 
  # Enum.sort(~w/b6QQQQ c2KK22 a44444 d8J8Q8 eA2323 f97AJ9 g3K86T/)

  # We can only do this because we know they're pre-sorted
  # and card-ordering doesn't matter ([no straights])
  # 5 of a kind
  def _score([aaa, aaa, aaa, aaa, aaa]), do: "1"
  # 4 of a kind
  def _score([aaa, aaa, aaa, aaa, _bb]), do: "2"
  def _score([_bb, aaa, aaa, aaa, aaa]), do: "2"
  # full house
  def _score([aaa, aaa, aaa, bb, bb]), do: "3"
  def _score([bb, bb, aaa, aaa, aaa]), do: "3"
  # 3 of a kind
  def _score([aaa, aaa, aaa, _b, _c]), do: "4"
  def _score([_b, aaa, aaa, aaa, _c]), do: "4"
  def _score([_b, _c, aaa, aaa, aaa]), do: "4"
  # Two Pair
  def _score([aaa, aaa, bb, bb, _c]), do: "5"
  def _score([aaa, aaa, _c, bb, bb]), do: "5"
  def _score([_c, aaa, aaa, bb, bb]), do: "5"
  # One Pair
  def _score([aaa, aaa, _b, _c, _d]), do: "6"
  def _score([_b, aaa, aaa, _c, _d]), do: "6"
  def _score([_b, _c, aaa, aaa, _d]), do: "6"
  def _score([_b, _c, _d, aaa, aaa]), do: "6"
  # High Card?
  def _score([_a, _b, _c, _d, _e]), do: "7"

  def score(hand) do
    hand
    |> String.split("", trim: true)
    |> Enum.sort()
    |> _score()
  end

  def sortable_hand(hand) do
    hand
    |> String.replace(~r/A/, "a")
    |> String.replace(~r/K/, "b")
    |> String.replace(~r/Q/, "c")
    |> String.replace(~r/J/, "d")
    |> String.replace(~r/T/, "e")
    |> String.replace(~r/9/, "f")
    |> String.replace(~r/8/, "g")
    |> String.replace(~r/7/, "h")
    |> String.replace(~r/6/, "i")
    |> String.replace(~r/5/, "j")
    |> String.replace(~r/4/, "k")
    |> String.replace(~r/3/, "l")
    |> String.replace(~r/2/, "m")
  end

  def solve(text) do
    _hands =
      text
      |> AOC.as_single_lines()
      |> Enum.map(&AOC.delimited_by_spaces/1)
      |> Enum.map(fn [hand, bid] ->
        score = score(hand)

        %{
          hand: hand,
          bid: String.to_integer(bid),
          score: score,
          comparable: score <> sortable_hand(hand)
        }
      end)
      |> Enum.sort_by(fn hand -> hand.comparable end)
      |> Enum.reverse()
      # |> IO.inspect()
      |> Enum.with_index(fn %{bid: bid}, rank ->
        (rank + 1) * bid
        # |> IO.inspect(label: "#{bid}*#{rank + 1}") 
      end)
      |> Enum.sum()
  end

  ##################################################

  def sortable_hand2(hand) do
    hand
    |> String.replace(~r/A/, "a")
    |> String.replace(~r/K/, "b")
    |> String.replace(~r/Q/, "c")
    |> String.replace(~r/T/, "d")
    |> String.replace(~r/9/, "e")
    |> String.replace(~r/8/, "f")
    |> String.replace(~r/7/, "g")
    |> String.replace(~r/6/, "h")
    |> String.replace(~r/5/, "i")
    |> String.replace(~r/4/, "j")
    |> String.replace(~r/3/, "k")
    |> String.replace(~r/2/, "l")
    |> String.replace(~r/J/, "m")
  end

  # We can only do this because we know they're pre-sorted
  # and card-ordering doesn't matter ([no straights])
  # JOKERS have been translated to "m" and will sort to the end.
  # 5 of a kind
  def _score2([aaa, aaa, aaa, aaa, aaa]), do: "1"
  def _score2([_aaa, "m", "m", "m", "m"]), do: "1"
  def _score2([aaa, aaa, "m", "m", "m"]), do: "1"
  def _score2([aaa, aaa, aaa, "m", "m"]), do: "1"
  def _score2([aaa, aaa, aaa, aaa, "m"]), do: "1"
  # 4 of a kind
  def _score2([aaa, aaa, aaa, aaa, _bb]), do: "2"
  def _score2([_bb, aaa, aaa, aaa, aaa]), do: "2"
  def _score2([aaa, aaa, aaa, _bb, "m"]), do: "2"
  def _score2([_bb, aaa, aaa, aaa, "m"]), do: "2"
  def _score2([_bb, aaa, aaa, "m", "m"]), do: "2"
  def _score2([aaa, aaa, _bb, "m", "m"]), do: "2"
  def _score2([_aaa, _bb, "m", "m", "m"]), do: "2"
  # full house - cannot have two jokers
  def _score2([aaa, aaa, aaa, bb, bb]), do: "3"
  def _score2([bb, bb, aaa, aaa, aaa]), do: "3"
  def _score2([bb, bb, aaa, aaa, "m"]), do: "3"
  # 3 of a kind
  def _score2([aaa, aaa, aaa, _b, _c]), do: "4"
  def _score2([_b, aaa, aaa, aaa, _c]), do: "4"
  def _score2([_b, _c, aaa, aaa, aaa]), do: "4"
  def _score2([aaa, aaa, _b, _c, "m"]), do: "4"
  def _score2([_b, aaa, aaa, _c, "m"]), do: "4"
  def _score2([_b, _c, aaa, aaa, "m"]), do: "4"
  def _score2([_a, _b, _c, "m", "m"]), do: "4"
  # Two Pair - cannot have a joker
  def _score2([aaa, aaa, bb, bb, _c]), do: "5"
  def _score2([aaa, aaa, _c, bb, bb]), do: "5"
  def _score2([_c, aaa, aaa, bb, bb]), do: "5"
  # One Pair
  def _score2([aaa, aaa, _b, _c, _d]), do: "6"
  def _score2([_b, aaa, aaa, _c, _d]), do: "6"
  def _score2([_b, _c, aaa, aaa, _d]), do: "6"
  def _score2([_b, _c, _d, aaa, aaa]), do: "6"
  def _score2([_a, _b, _c, _d, "m"]), do: "6"
  # High Card?
  def _score2([_a, _b, _c, _d, _e]), do: "7"

  def score2(hand) do
    hand
    |> sortable_hand2()
    |> String.split("", trim: true)
    |> Enum.sort()
    |> _score2()
  end

  def solve2(text) do
    _hands =
      text
      |> AOC.as_single_lines()
      |> Enum.map(&amp;AOC.delimited_by_spaces/1)
      |> Enum.map(fn [hand, bid] ->
        score = score2(hand)

        %{
          hand: hand,
          bid: String.to_integer(bid),
          score: score,
          comparable: score <> sortable_hand2(hand)
        }

        # |> IO.inspect()
      end)
      |> Enum.sort_by(fn hand -> hand.comparable end)
      |> Enum.reverse()
      # |> IO.inspect()
      |> Enum.with_index(fn %{bid: bid} = _item, rank ->
        # if item.score == "7", do: IO.inspect(item, label: "#{rank + 1} * #{bid}") 
        (rank + 1) * bid
      end)
      |> Enum.sum()
  end
end

# Day07.score("32T3K") |> IO.inspect(label: "32T3K")

p1data.()
|> Day07.solve()
|> IO.inspect(label: "\n*** Part 1 solution (example: 6440)")

# 250453939

p1data.()
|> Day07.solve2()
|> IO.inspect(label: "\n*** Part 2 solution (example: 5905)")

# 248553970 is too low
# 248652697 is correct after adding [_aaa, _bb, "m", "m", "m"] 4-of-a-kind