Docker Images a la carte with Elixir and RabbitMQ

Upcoming conferences

Start booking your calendar with more Code Sync conferences happening across the globe. We will be slowly releasing more dates, in the meantime here is what we’ve planned already:

All conferences

ARTICLES: 9

Day 11: Chronal Charge - Advent of Code 2018

Article by

READ MORE

Day 10: The Stars Align - Advent of Code 2018

Article by

READ MORE

Day 7: The Sum of Its Parts - Advent of Code 2018

Article by

I did the Advent of Code 2018 day 7 challenge in Elixir! Parts one and two are as follows:

defmodule Day7 do
  @instruction_info ~r/Step (\w+) must be finished before step (\w+) can begin/
  def part1(input) do
    input
    |> read_input()
    |> parse_input()
    |> discover_dependencies([])
  end

  defp read_input(input) do
    input
    |> File.read!()
    |> String.split("\n", trim: true)
  end

  defp parse_input(lines) do
    Enum.reduce(lines, %{}, fn line, acc ->
      [step, dependency] = Regex.run(@instruction_info, line, capture: :all_but_first)

      acc
      |> Map.put_new(step, [])
      |> Map.update(dependency, [step], fn dependencies ->
        Enum.sort([step | dependencies])
      end)
    end)
    |> Enum.to_list()
  end

  defp discover_dependencies(deps_tree, acc) when map_size(deps_tree) == 0do
    acc |> Enum.reverse() |> Enum.join()
  end

  defp discover_dependencies(deps_tree, acc) do
    {step, []} = Enum.find(deps_tree, fn {_, deps} -> Enum.empty?(deps) end)

    new_dependencies =
      Enum.reduce(deps_tree, %{}, fn
        {^step, _}, acc -> Map.delete(acc, step)
        {other_step, deps}, acc -> Map.put_new(acc, other_step, List.delete(deps, step))
      end)

    discover_dependencies(new_dependencies, [step | acc])
  end
end

# r Day7; :aoc2018 |> :code.priv_dir() |> Path.join("day7.txt") |> Day7.part1()
# :aoc2018 |> :code.priv_dir() |> Path.join("day7.txt") |> Day7.part2()

input = "Step D must be finished before step E can begin.
Step F must be finished before step E can begin.
Step C must be finished before step A can begin.
Step B must be finished before step E can begin.
Step A must be finished before step D can begin.
Step C must be finished before step F can begin.
Step A must be finished before step B can begin."

# r Day7; Day7.part1(input)
 

 

READ MORE

Day 6: Chronal Coordinates - Advent of Code 2018

Article by

I did the Advent of Code 2018 day 6 challenge in Elixir! Parts one and two are as follows:

defmodule Day6 do
  def part1(input) do
    {_infinites, finites} =
      input
      |> read_input()
      |> create_grid()
      |> group_by_infinites()

    {_coordinates, layout} =
      finites
      |> Enum.group_by(fn {x, y, _dist} -> {x, y} end)
      |> Enum.max_by(fn {_, v} -> length(v) end)

    length(layout)
  end

  def part2(input) do
    {max_x, max_y, coordinates} =
      input
      |> read_input()

    for x <- 0..max_x,
        y <- 0..max_y do
      coordinates
      |> Enum.reduce(0, fn {point_x, point_y}, acc ->
        distance = abs(x - point_x) + abs(y - point_y)
        distance + acc
      end)
    end
    |> Enum.filter(&(&1 < 10_000))
    |> Enum.count()
  end

  defp read_input(input) do
    {max_x, max_y, coordinates} =
      input
      |> File.read!()
      |> String.trim()
      |> String.split("\n", trim: true)
      |> Enum.reduce({-1, -1, []}, fn line, {max_x, max_y, coordinates} ->
        [x, y] = String.split(line, ", ")
        x = String.to_integer(x)
        y = String.to_integer(y)
        coordinate = {x, y}
        max_x = if x > max_x, do: x, else: max_x
        max_y = if y > max_y, do: y, else: max_y
        {max_x, max_y, [coordinate | coordinates]}
      end)

    {max_x, max_y, Enum.reverse(coordinates)}
  end

  defp create_grid({max_x, max_y, coordinates}) do
    for x <- 0..max_x,
        y <- 0..max_y do
      [{px, py, dist1}, {_, _, dist2} | _] =
        Enum.map(coordinates, fn {point_x, point_y} ->
          distance = abs(x - point_x) + abs(y - point_y)
          {point_x, point_y, distance}
        end)
        |> Enum.sort_by(fn {_, _, distance} -> distance end)

      if dist1 == dist2 do
        nil
      else
        cond do
          x == 0 ->
            {:infinite, px, py}

          x == max_x ->
            {:infinite, px, py}

          y == 0 ->
            {:infinite, px, py}

          y == max_y ->
            {:infinite, px, py}

          true ->
            {px, py, dist1}
        end
      end
    end
  end

  defp group_by_infinites(grid) do
    Enum.reduce(grid, {MapSet.new(), []}, fn
      nil, acc ->
        acc

      {:infinite, px, py}, {infinites, finites} ->
        {MapSet.put(infinites, {px, py}), finites}

      {px, py, dist}, {infinites, finites} ->
        if MapSet.member?(infinites, {px, py}) do
          {infinites, finites}
        else
          {infinites, [{px, py, dist} | finites]}
        end
    end)
  end
end

# r Day6; :aoc2018 |> :code.priv_dir() |> Path.join("day6.txt") |> Day6.part1()
# r Day6; :aoc2018 |> :code.priv_dir() |> Path.join("day6.txt") |> Day6.part2()
 

 

READ MORE

Day 5: Alchemical Reduction - Advent of Code 2018

Article by

I did the Advent of Code 2018 day 5 challenge in Elixir! Parts one and two are as follows:

defmodule Day5 do
  def part1(input) do
    input
    |> read_input()
    |> String.codepoints()
    |> reduce_polymer()
  end

  def part2(input) do
    chars =
      input
      |> read_input()
      |> String.codepoints()

    unit_types =
      chars
      |> Enum.map(&String.downcase/1)
      |> Enum.uniq()

    Enum.reduce(unit_types, 1_000_000, fn unit_type, min_size ->
      without_unit =
        Enum.reject(chars, fn char ->
          String.downcase(char) == unit_type
        end)

      min(min_size, reduce_polymer(without_unit))
    end)
  end

  defp read_input(input) do
    input
    |> File.read!()
    |> String.trim()
  end

  defp reduce_polymer(chars) do
    Enum.reduce(chars, [], fn
      char, [] ->
        [char]

      char, [last_seen | rest] = acc ->
        result =
          if char != last_seen &&
               (char == String.upcase(last_seen) || char == String.downcase(last_seen)) do
            rest
          else
            [char | acc]
          end

        result
    end)
    |> length()
  end
end

# :aoc2018 |> :code.priv_dir() |> Path.join("day5.txt") |> Day5.part1()
# :aoc2018 |> :code.priv_dir() |> Path.join("day5.txt") |> Day5.part2()

# input = "dabAcCaCBAcCcaDA"
# r Day5; Day5.part2(input)

 

READ MORE

Day 4: Repose Record - Advent of Code 2018

Article by

I did the Advent of Code 2018 day 4 challenge in Elixir! Parts one and two are as follows:

defmodule Day4_1 do
  # 1518-11-01 00:00
  @log_info ~r/\[(\d+)-(\d+)-(\d+) (\d+):(\d+)\] (.*)/
  @guard_entry ~r/Guard #(\d+) begins shift/

  def part1(input) do
    {_, _, guards} =
      input
      |> read_input()
      |> extract_logs()

    {guard_id, _time_asleep} = most_asleep(guards)
    {minute, _size} = get_most_asleep_minutes(guards[guard_id])
    minute * String.to_integer(guard_id)
  end

  def part2(input) do
    {_, _, guards} =
      input
      |> read_input()
      |> extract_logs()

    {guard_id, minute, _size} = most_times_asleep(guards)
    minute * String.to_integer(guard_id)
  end

  defp read_input(input) do
    input
    |> File.read!()
    |> String.split("\n", trim: true)
  end

  def extract_logs(lines) do
    lines
    |> Enum.sort()
    |> Enum.map(fn line ->
      [_year, _month, _day, _hour, minute, message] =
        Regex.run(@log_info, line, capture: :all_but_first)

      case message do
        "falls asleep" ->
          {:sleep, String.to_integer(minute)}

        "wakes up" ->
          {:wake, String.to_integer(minute)}

        _ ->
          [guard_id] = Regex.run(@guard_entry, message, capture: :all_but_first)
          {:guard, guard_id}
      end
    end)
    |> Enum.reduce({nil, nil, %{}}, fn log, {current_guard_id, last_minute, guards} ->
      case log do
        {:guard, guard_id} ->
          {guard_id, nil, Map.put_new(guards, guard_id, [])}

        {:sleep, minute} ->
          {current_guard_id, minute, guards}

        {:wake, minute} ->
          new_range = for min <- last_minute..(minute - 1), do: min
          {current_guard_id, nil, Map.update!(guards, current_guard_id, &(&1 ++ new_range))}
      end
    end)
  end

  defp most_asleep(guards) do
    Enum.reduce(guards, {nil, 0}, fn {guard_id, sleep_range}, {_, current_bigger_time} = acc ->
      time_asleep = length(sleep_range)

      if time_asleep > current_bigger_time do
        {guard_id, time_asleep}
      else
        acc
      end
    end)
  end

  defp most_times_asleep(guards) do
    Enum.reduce(guards, {nil, 0, 0}, fn {guard_id, asleep_range},
                                        {_current_guard, _minute, max_size} = acc ->
      {minute, size} = get_most_asleep_minutes(asleep_range)

      if size > max_size do
        {guard_id, minute, size}
      else
        acc
      end
    end)
  end

  defp get_minutes_of(guard_intervals) do
    {asleep_range, []} =
      guard_intervals
      |> Enum.reduce({[], []}, fn
        {datetime, _}, {range, []} ->
          {range, [datetime.minute]}

        {datetime, _}, {range, [asleep_time]} ->
          new_range = for min <- asleep_time..(datetime.minute - 1), do: min
          {[new_range | range], []}
      end)

    {minute, _size} = get_most_asleep_minutes(asleep_range)
    minute
  end

  defp get_most_asleep_minutes(asleep_range) do
    asleep_range
    |> Enum.group_by(& &1)
    |> Enum.reduce({nil, 0}, fn {minute, occurrences}, {_, times} = acc ->
      size = length(occurrences)

      if size > times do
        {minute, size}
      else
        acc
      end
    end)
  end
end

# :aoc2018 |> :code.priv_dir() |> Path.join("day4.txt") |> Day4_1.part1()
# :aoc2018 |> :code.priv_dir() |> Path.join("day4.txt") |> Day4_1.part2()

 

READ MORE

Day 3: No matter how you slice it - Advent of Code 2018

Article by

I did the Advent of Code 2018 day 3 challenge in Elixir! Parts one and two are as follows:

defmodule Day3 do
  @grid_info ~r/#(\d+) @ (\d+),(\d+): (\d+)x(\d+)/

  def part1(input) do
    {overlaps, _} =
      input
      |> read_input()
      |> extract_grid()
      |> build_map()
      |> Enum.reduce({MapSet.new(), MapSet.new()}, fn {x, y, _id}, {overlap, no_overlap} ->
        if MapSet.member?(no_overlap, {x, y}) do
          {MapSet.put(overlap, {x, y}), no_overlap}
        else
          {overlap, MapSet.put(no_overlap, {x, y})}
        end
      end)

    MapSet.size(overlaps)
  end

  def part2(input) do
    map =
      input
      |> read_input()
      |> extract_grid()
      |> build_keyed_map()

    {id, _} =
      for {id1, coordinates1} <- map,
          {id2, coordinates2} <- map,
          id1 != id2 do
        overlaps = MapSet.intersection(coordinates1, coordinates2)

        if MapSet.size(overlaps) != 0 do
          {id1, id2}
        else
          {id1, nil}
        end
      end
      |> Enum.group_by(fn {key, _} -> key end, fn {_, value} -> value end)
      |> Enum.find(fn {_key, overlaps} ->
        Enum.all?(overlaps, &Kernel.==(&1, nil))
      end)

    id
  end

  defp read_input(input) do
    input
    |> File.read!()
    |> String.split("\n", trim: true)
  end

  defp extract_grid(lines) do
    Enum.map(lines, fn line ->
      [id, x, y, width, height] = Regex.run(@grid_info, line, capture: :all_but_first)

      [
        id,
        String.to_integer(x),
        String.to_integer(y),
        String.to_integer(width),
        String.to_integer(height)
      ]
    end)
  end

  defp build_map(grid) do
    Enum.flat_map(grid, fn [id, x, y, width, height] ->
      for x <- x..(x + width - 1),
          y <- y..(y + height - 1) do
        {x, y, id}
      end
    end)
  end

  defp build_keyed_map(grid) do
    Enum.reduce(grid, %{}, fn [id, x, y, width, height], acc ->
      coordinates =
        for x <- x..(x + width - 1),
            y <- y..(y + height - 1) do
          {x, y}
        end

      Map.put_new(acc, id, MapSet.new(coordinates))
    end)
  end
end

# :aoc2018 |> :code.priv_dir() |> Path.join("day3.txt") |> Day3.part1()
# :aoc2018 |> :code.priv_dir() |> Path.join("day3.txt") |> Day3.part2()
 

 

READ MORE

Day 2: Inventory Management System - Advent of Code 2018

Article by

I did the Advent of Code 2018 day 2 challenge in Elixir! Parts one and two are as follows:

defmodule Day2 do
  def checksum(input) do
    {two, three} =
      input
      |> read_input()
      |> Enum.reduce({0, 0}, fn line, {appears_two_times, appears_three_times} ->
        {two_times, three_times, _} =
          line
          |> String.graphemes()
          |> Enum.group_by(& &1)
          |> get_checksum_values()

        {appears_two_times + two_times, appears_three_times + three_times}
      end)

    two * three
  end

  def differ(input) do
    lines = input |> read_input()

    # length(diff) == 4 # [eq: "_", del: "_", ins: "_", eq: "_"]
    for line1 <- lines,
        line2 <- lines,
        line1 != line2,
        diff = String.myers_difference(line1, line2),
        length(diff) == 4 do
      diff
      |> Keyword.get_values(:eq)
      |> Enum.join()
    end
    |> Enum.uniq()
  end

  defp read_input(input) do
    input
    |> File.read!()
    |> String.split("\n", trim: true)
  end

  defp get_checksum_values(groups) do
    Enum.reduce(groups, {0, 0, MapSet.new()}, fn {_key, chars},
                                                 {two_times, three_times, seen} = acc ->
      value = length(chars)

      if MapSet.member?(seen, value) do
        acc
      else
        new_seen = MapSet.put(seen, value)

        case value do
          2 -> {two_times + 1, three_times, new_seen}
          3 -> {two_times, three_times + 1, new_seen}
          _ -> {two_times, three_times, new_seen}
        end
      end
    end)
  end
end

:aoc2018 |> :code.priv_dir() |> Path.join("day2.txt") |> Day2.checksum()
:aoc2018 |> :code.priv_dir() |> Path.join("day2.txt") |> Day2.differ()

 

READ MORE

Day 1: Chronal Calibration - Advent of Code 2018

Article by

I did the Advent of Code 2018 day 1 challenge in Elixir! Parts one and two are as follows:

defmodule Day1 do
  def frequency(input) do
    input
    |> read_input()
    |> Enum.reduce(0, fn line, acc ->
      String.to_integer(line) + acc
    end)
  end

  def repeated_frequency(input) do
      input
      |> read_input()
      |> Stream.cycle()
      |> Enum.reduce_while({0, MapSet.new([0])}, fn line, {acc, seen} ->
        value = String.to_integer(line) + acc
        if MapSet.member?(seen, value) do
          {:halt, value}
        else
          {:cont, {value, MapSet.put(seen, value)}}
        end
      end)
  end

  defp read_input(input) do
    input
    |> File.read!()
    |> String.split("\n", trim: true)
  end
end

:aoc2018 |> :code.priv_dir() |> Path.join("day1.txt") |> Day1.frequency()
:aoc2018 |> :code.priv_dir() |> Path.join("day1.txt") |> Day1.repeated_frequency()

 

 

READ MORE
si