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

Advent of Code 2016 - Day 07

2016/day07.livemd

Advent of Code 2016 - Day 07

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

Part 01

While snooping around the local network of EBHQ, you compile a list of IP addresses (they’re IPv7, of course; IPv6 is much too limited). You’d like to figure out which IPs support TLS (transport-layer snooping).

An IP supports TLS if it has an Autonomous Bridge Bypass Annotation, or ABBA. An ABBA is any four-character sequence which consists of a pair of two different characters followed by the reverse of that pair, such as xyyx or abba. However, the IP also must not have an ABBA within any hypernet sequences, which are contained by square brackets.

For example:

  • zabba[mnop]qrst supports TLS (abba outside square brackets).
  • abcd[bddb]xyyx does not support TLS (bddb is within square brackets, even though xyyx is outside square brackets).
  • aaaa[qwer]tyui does not support TLS (aaaa is invalid; the interior characters must be different).
  • ioxxoj[asdfgh]zxcvbn supports TLS (oxxo is outside square brackets, even though it’s within a larger string).

How many IPs in your puzzle input support TLS?

test_input = Kino.Input.textarea("Paste input here:")
input = Kino.Input.textarea("Paste input here:")
defmodule PartOne do
  @spec solve(input :: String.t()) :: [bool()]
  def solve(input) do
    input
    |> Kino.Input.read()
    |> String.split("\n", trim: true)
    |> Enum.map(fn ip -> split_ip(ip, "", []) end)
    |> Enum.map(fn ip_parts -> supports_tls?(ip_parts) end)
  end

  def split_ip(<<>>, curr, parts) do
    curr
    |> IO.iodata_to_binary()
    |> then(fn curr -> [curr | parts] end)
    |> Enum.reverse()
  end

  def split_ip(<<"[", rest::binary>>, curr, parts) do
    {part, [_rbrace | rest]} =
      Enum.split_while(
        String.split(rest, "", trim: true),
        &amp;(!is_rbrace?(&amp;1))
      )

    part = Enum.join(part, "")
    rest = IO.iodata_to_binary(rest)
    curr = IO.iodata_to_binary(curr)
    parts = [curr | parts]
    parts = [[part] | parts]
    split_ip(rest, <<>>, parts)
  end

  def split_ip(<>, curr, parts) do
    split_ip(rest, [curr | <>], parts)
  end

  def supports_tls?(ip_parts) when is_list(ip_parts) do
    ip_parts
    |> Enum.map(fn ip_part ->
      case is_list(ip_part) do
        true -> ip_part |> hd() |> eval_part(:inside_brackets)
        false -> ip_part |> eval_part(:outside_brackets)
      end
    end)
    |> Enum.reduce_while(
      false,
      fn
        {:inside_brackets, true}, _acc -> {:halt, false}
        {:inside_brackets, false}, acc -> {:cont, acc}
        {:outside_brackets, result}, acc -> {:cont, acc or result}
      end
    )
  end

  def eval_part(_ip_part, :inside_brackets) do
    {:inside_brackets, false}
  end

  def eval_part(ip_part, :outside_brackets) do
    ip_part
    |> String.split("", trim: true)
    |> Enum.chunk_every(4, 1, :discard)
    |> Enum.reject(fn chars ->
      [a, b, c, d] = chars
      all_same_characters = a != b and a != c and a != d
      is_abba_notation = a == d and b == c
      all_same_characters or !is_abba_notation
    end)
    |> then(fn l -> {:outside_brackets, !Enum.empty?(l)} end)
  end

  defp is_rbrace?(ch), do: ch == <<"]">>
end
{:module, PartOne, <<70, 79, 82, 49, 0, 0, 23, ...>>, {:is_rbrace?, 1}}
PartOne.supports_tls?([
  "zabba",
  ["mnop"],
  "qrst"
])
true
PartOne.supports_tls?([
  "wysextplwqpvipxdv",
  ["srzvtwbfzqtspxnethm"],
  "syqbzgtboxxzpwr",
  ["kljvjjkjyojzrstfgrw"],
  "obdhcczonzvbfby",
  ["svotajtpttohxsh"],
  "cooktbyumlpxostt"
])
false
PartOne.solve(test_input)
[true, true, true, true]