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

Day 6: Tuning Trouble

2022/day-06.livemd

Day 6: Tuning Trouble

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

Day 6

real_input = Kino.Input.textarea("Paste Real Input")
samples = [
  "mjqjpqmgbljsphdztnvjfqwrcgsmlb",
  "bvwbjplbgvbhsrlpgdmjqwftvncz",
  "nppdvjthqldpwncqszvftbrmjlhg",
  "nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg",
  "zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw"
]
signal = Kino.Input.read(real_input)
defmodule ParserState do
  defstruct count: 0, buffer: "", size: 4, status: :processing

  def update_buffer(
        %__MODULE__{
          size: size,
          buffer: buffer
        } = state,
        char
      ) do
    if String.length(buffer) === size do
      <<_first::binary-size(1)>> <> rest = buffer
      %{state | buffer: rest <> char}
    else
      %{state | buffer: buffer <> char}
    end
  end

  def advance_count(%__MODULE__{count: count} = state), do: %{state | count: count + 1}

  def update_status(%__MODULE__{buffer: buffer, size: size} = state) do
    distinct_count = buffer |> String.codepoints() |> MapSet.new() |> MapSet.size()

    if distinct_count == size do
      %{state | status: :done}
    else
      state
    end
  end

  def process_char(%__MODULE__{} = state, char) do
    state
    |> update_buffer(char)
    |> advance_count()
    |> update_status()
  end
end

parse_input = fn input, size ->
  input
  |> String.codepoints()
  |> Enum.reduce_while(%ParserState{size: size}, fn char, prev_state ->
    case ParserState.process_char(prev_state, char) do
      %{status: :done} = state -> {:halt, state}
      state -> {:cont, state}
    end
  end)
  |> Map.get(:count)
end
samples |> Enum.map(fn sample -> parse_input.(sample, 4) end)
parse_input.(signal, 4)
samples |> Enum.map(fn sample -> parse_input.(sample, 14) end)
parse_input.(signal, 14)