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

Day10

livebook/day-10.livemd

Day10

Setup

Mix.install(
  [
    {:kino, "~> 0.4.1"}
  ],
  consolidate_protocols: false
)

input = Kino.Input.textarea("Please paste your input:")
chunks =
  input
  |> Kino.Input.read()
  |> String.split(["\n"], trim: true)

# |> (fn parsed -> ["999999999999"] ++ parsed ++ ["999999999999"] end).()
# |> Enum.map(&String.to_charlist/1)

Part 1

defmodule Stack do
  defstruct elements: []

  def new, do: %Stack{}

  def push(stack, element) do
    %Stack{stack | elements: [element | stack.elements]}
  end

  def pop(%Stack{elements: []}), do: raise("Stack is empty!")

  def pop(%Stack{elements: [top | rest]}) do
    {top, %Stack{elements: rest}}
  end

  def top(%Stack{elements: [top | _rest]}) do
    top
  end

  def top(%Stack{elements: []}) do
    0
  end

  def elements(%Stack{elements: elements}) do
    elements
  end

  def depth(%Stack{elements: elements}), do: length(elements)
end

{last, st} = Stack.new() |> Stack.push(1) |> Stack.push(2) |> Stack.push(2) |> Stack.pop()
# Stack.new
# start_end =  %{
#     '{' => '}',
#     '[' => ']',
#     '<' => '>',
#     '(' => ')'
#   }
#   Map.get(start_end, '{')
Stack.top(st)
defmodule Part1 do
  @start_chunk '{[(<'
  @start_end %{
    '{' => '}',
    '[' => ']',
    '<' => '>',
    '(' => ')'
  }
  def find_bug(_chunk = [], {last_started, started_so_far}) do
    # {:ok, nil}
    {false, [last_started], started_so_far}
  end

  def find_bug(chunk, {last_started, started_so_far}) do
    [char | chunk] = chunk
    # IO.puts("#{chunk} #{[char]} #{[last_started]} #{inspect(started_so_far)}")
    if char in @start_chunk do
      find_bug(chunk, {char, Stack.push(started_so_far, char)})
    else
      expected = Map.get(@start_end, [last_started])

      if [char] == expected do
        {_, started_so_far} = Stack.pop(started_so_far)
        # IO.puts("Closing #{[last_started]} remaining #{inspect(started_so_far)}")
        find_bug(chunk, {Stack.top(started_so_far), started_so_far})
      else
        # {:bug, [last_started], [char]}
        {true, [char], started_so_far}
      end
    end
  end
end

# [chunk | _ ] = chunks
# Part1.validate_chunk(chunk, {0, Stack.new})
chunks
|> Enum.map(&amp;String.to_charlist/1)
|> Enum.map(fn chunk -> Part1.find_bug(chunk, {0, Stack.new()}) end)
|> Enum.filter(fn {is_buggy, _bug, _incompleted} -> is_buggy end)
|> Enum.reduce(0, fn {_, bug_char, _}, score ->
  case bug_char do
    ')' -> score + 3
    ']' -> score + 57
    '}' -> score + 1197
    '>' -> score + 25137
    _ -> 99999
  end
end)

Part 2

defmodule Part2 do
  def score_incomplete({_, _, incompleted}) do
    Stack.elements(incompleted)
    |> Enum.reduce(0, fn char, score ->
      case [char] do
        '(' -> 5 * score + 1
        '[' -> 5 * score + 2
        '{' -> 5 * score + 3
        '<' -> 5 * score + 4
        _ -> 99999
      end
    end)
  end
end

scores =
  chunks
  |> Enum.map(&amp;String.to_charlist/1)
  |> Enum.map(fn chunk -> Part1.find_bug(chunk, {0, Stack.new()}) end)
  |> Enum.filter(fn {is_buggy, _bug, _incompleted} -> is_buggy == false end)
  |> Enum.map(&amp;Part2.score_incomplete/1)
  |> Enum.sort()

Enum.fetch!(scores, trunc(Enum.count(scores) / 2))

Jose

defmodule Syntax do
  def parse(line), do: parse(line, [])

  # Opening
  defp parse(<rest::binary>, stack), do: parse(rest, [?) | stack])
  defp parse(<rest::binary>, stack), do: parse(rest, [?] | stack])
  defp parse(<rest::binary>, stack), do: parse(rest, [?} | stack])
  defp parse(<rest::binary>, stack), do: parse(rest, [?> | stack])

  # Closing
  defp parse(<>, [char | stack]), do: parse(rest, stack)

  # Base/error cases
  defp parse(<>, _stack), do: {:corrupted, char}
  defp parse(<<>>, []), do: :ok
  defp parse(<<>>, stack), do: {:incomplete, stack}

  def test(<>) do
    IO.puts(rest)
  end
end
points = %{
  ?) => 3,
  ?] => 57,
  ?} => 1197,
  ?> => 25137
}

# Enum.sum(
for line <- chunks,
    {:corrupted, char} <- [Syntax.parse(line)],
    do: Map.fetch!(points, char)

# )
points = %{
  ?) => 1,
  ?] => 2,
  ?} => 3,
  ?> => 4
}

scores =
  Enum.sort(
    for line <- chunks,
        {:incomplete, chars} <- [Syntax.parse(line)],
        do: Enum.reduce(chars, 0, fn char, acc -> acc * 5 + Map.fetch!(points, char) end)
  )

# Enum.fetch!(scores, div(length(scores), 2))