FEATURED ARTICLES

ALL

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

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

    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

      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

        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

          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

            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

              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