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

Day 19

day19.livemd

Day 19

# Note: when making the next template, something like this works well:
#   `cat day04.livemd | sed 's/19/04/' > day04.livemd`
# When inspecting lists of numbers, use "charlists: :as_lists"
#
Mix.install([
  # Join the string so a copy of dayN to dayM doesn't destroy it.
  {:kino, "~> 0.1" <> "4.2"}
])

# Join the string so a copy of dayN to dayM doesn't destroy it.
IEx.Helpers.c("/Users/johnb/dev/2" <> "0" <> "2" <> "4adventOfCode/advent_of_code.ex")
alias AdventOfCode, as: AOC
alias Kino.Input

Installation and Data

input_example = Kino.Input.textarea("Example Data", monospace: true)
input_puzzleInput = Kino.Input.textarea("Puzzle Input", monospace: true)
input_source_select =
  Kino.Input.select("Source", [{:example, "example"}, {:puzzle_input, "puzzle input"}])
data = fn ->
  (Kino.Input.read(input_source_select) == :example &amp;&amp;
     Kino.Input.read(input_example)) ||
    Kino.Input.read(input_puzzleInput)
end

Solution

defmodule Day19 do
  def parse(text) do
    [patterns, request] = AOC.as_doublespaced_paragraphs(text)
    patterns = String.split(patterns, ", ")
      # |> Enum.reduce(%{}, fn pattern, acc ->
      #   {_, map} = get_and_update_in(acc, [String.slice(pattern, 0..0)], fn current ->
      #     {current, (current || []) ++ [pattern]}
      #   end)
      #   map
      # end)
    requests = AOC.as_single_lines(request)

    [patterns, requests]
  end

  def can_be_made_with?(request, patterns) do
    patterns
    |> Enum.any?(fn pattern ->
      cond do
        request == pattern -> true
        String.starts_with?(request, pattern) ->
          can_be_made_with?(String.slice(request, String.length(pattern)..-1//1), patterns)
        true -> false
      end
    end)
  end

  def solve1(text) do
    [patterns, requests] = parse(text)
    # patterns = Map.values(patterns) |> List.flatten()

    requests
    |> Enum.filter(fn request ->
      can_be_made_with?(request, patterns)
    end)
    |> Enum.count()
  end

  def how_many_can_be_made_with?("", _patmap), do: 1
  def how_many_can_be_made_with?(request, patmap) do
    patmap.minmax
    |> Enum.map(fn length ->
      if String.length(request) < length do
        0
      else
        request_front = String.slice(request, 0..(length - 1))
        if patmap[request_front] do
          # AOC.inspect([request, length, request_front, patmap[request_front], length..-1//1])
          how_many_can_be_made_with?(String.slice(request, length..-1//1), patmap)     
        else
          0
        end
      end
    end)
  end

  def solve2(text) do
    [patterns, requests] = parse(text)
    AOC.inspect([Enum.sort(patterns)])

    patmap = patterns
      |> Enum.reduce(%{}, fn pattern, acc -> put_in(acc, [pattern], 1) end)
    sorted_pattern_lengths = Enum.map(patterns, &amp;String.length/1) |> Enum.sort()
    patmap = put_in(patmap, [:minmax], 
      List.first(sorted_pattern_lengths)..List.last(sorted_pattern_lengths)
      |> AOC.inspect(label: "patmap.minmax")
    )
    # |> AOC.inspect(label: "patmap")

    requests
    |> Enum.map(fn request ->
      # IO.puts("Checking #{request}")
      how_many_can_be_made_with?(request, patmap)
      |> List.flatten()
      # |> AOC.inspect(label: request)
      |> Enum.sum()
      |> AOC.inspect(label: "sum for #{request}")
    end)
    |> AOC.inspect()
    |> List.flatten()
    |> Enum.sum()
  end
end

# Example:

data.()
|> Day19.solve1()
|> IO.inspect(label: "\n*** Part 1 solution (example: 6)")
# 233

IO.puts(Time.utc_now())
data.()
|> Day19.solve2()
|> IO.inspect(label: "\n*** Part 2 solution (example: 16)")
IO.puts(Time.utc_now())
# 4799 is too low, as is 264216358