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()
ARTICLES: 9
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