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

Day 16: Aunt Sue

2015/16.livemd

Day 16: Aunt Sue

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

Modules

defmodule Sue do
  @moduledoc """
  Aunt Sue
  """

  defstruct [
    :id,
    :children,
    :cats,
    :samoyeds,
    :pomeranians,
    :akitas,
    :vizslas,
    :goldfish,
    :trees,
    :cars,
    :perfumes
  ]

  @type t :: %__MODULE__{
          id: pos_integer(),
          children: non_neg_integer() | nil,
          cats: non_neg_integer() | nil,
          samoyeds: non_neg_integer() | nil,
          pomeranians: non_neg_integer() | nil,
          akitas: non_neg_integer() | nil,
          vizslas: non_neg_integer() | nil,
          goldfish: non_neg_integer() | nil,
          trees: non_neg_integer() | nil,
          cars: non_neg_integer() | nil,
          perfumes: non_neg_integer() | nil
        }

  @doc """
  Finds potential matches from a list of Sues, given some properties.
  """
  def find(sues, properties, rules \\ :part1) do
    sues
    |> Enum.filter(&match?(&1, properties, rules))
  end

  # compares Aunt Sue with a set of given properties, to see if they 
  # potentially match.
  defp match?(sue, props, rules)

  # part 1 matches when each property is equal or nil
  defp match?(sue, props, :part1) do
    props
    |> Enum.all?(fn
      {key, value} ->
        sue_value = Map.get(sue, key)
        is_nil(sue_value) || sue_value == value
    end)
  end

  # part 2 matches with slightly different rules
  defp match?(sue, props, :part2) do
    props
    |> Enum.all?(fn
      {key, value} ->
        sue_value = Map.get(sue, key)

        case key do
          :cats -> is_nil(sue_value) || sue_value > value
          :trees -> is_nil(sue_value) || sue_value > value
          :pomeranians -> is_nil(sue_value) || sue_value < value
          :goldfish -> is_nil(sue_value) || sue_value < value
          _key -> is_nil(sue_value) || sue_value == value
        end
    end)
  end
end
defmodule Parser do
  @moduledoc """
  Enables parsing text into Aunt Sue structs
  """

  @doc """

  """
  def parse(input) do
    input
    |> String.split("\n")
    |> Enum.map(&amp;parse_line/1)
  end

  def parse_line(line) do
    id =
      Regex.run(~r/\d+/, line)
      |> Enum.map(&amp;String.to_integer/1)
      |> then(fn [id] -> id end)

    props =
      Regex.scan(~r/\w+: \d+/, line)
      |> List.flatten()
      |> Enum.map(&amp;parse_props/1)

    struct(Sue, [id: id] ++ props)
  end

  def parse_props(props) do
    props
    |> String.split(": ")
    |> then(fn
      [key, value] -> {String.to_atom(key), String.to_integer(value)}
    end)
  end
end

Input

input = Kino.Input.textarea("Please paste your puzzle input:")

Part 1

props = [
  children: 3,
  cats: 7,
  samoyeds: 2,
  pomeranians: 3,
  akitas: 0,
  vizslas: 0,
  goldfish: 5,
  trees: 3,
  cars: 2,
  perfumes: 1
]

input
|> Kino.Input.read()
|> Parser.parse()
|> Sue.find(props)

Part 2

props = [
  children: 3,
  cats: 7,
  samoyeds: 2,
  pomeranians: 3,
  akitas: 0,
  vizslas: 0,
  goldfish: 5,
  trees: 3,
  cars: 2,
  perfumes: 1
]

input
|> Kino.Input.read()
|> Parser.parse()
|> Sue.find(props, :part2)