Last active
December 5, 2018 05:34
-
-
Save gjastrab/a86871cdad8e76911009c1adb7122064 to your computer and use it in GitHub Desktop.
Advent of Code 2018 - Elixir
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Advent2018.Frequencies do | |
@input_file "frequencies" | |
def calibrate do | |
{:ok, data} = File.read(@input_file) | |
data | |
|> split_lines() | |
|> Enum.map(&line_to_number/1) | |
|> Enum.sum() | |
end | |
defp split_lines(data) do | |
data | |
|> String.trim() | |
|> String.split("\n") | |
end | |
defp sum(lines) do | |
lines | |
|> Enum.reduce(0, fn num, total -> | |
total + line_to_number(num) | |
end) | |
end | |
defp line_to_number(line), do: line |> String.trim() |> String.to_integer() | |
def calibrate_repeated do | |
{:ok, data} = File.read(@input_file) | |
data | |
|> split_lines() | |
|> Stream.cycle() | |
|> find_repeated_frequency() | |
end | |
defp find_repeated_frequency(stream) do | |
stream | |
|> Enum.reduce_while({0, MapSet.new()}, fn num, {total, set} -> | |
new_total = total + line_to_number(num) | |
if MapSet.member?(set, new_total) do | |
{:halt, new_total} | |
else | |
{:cont, {new_total, MapSet.put(set, new_total)}} | |
end | |
end) | |
end | |
end | |
Advent2018.Frequencies.calibrate() |> IO.puts() | |
Advent2018.Frequencies.calibrate_repeated() |> IO.puts() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule Advent2018.Day2 do | |
def part1(stream) do | |
stream | |
|> Stream.map(&to_freq_tuple/1) | |
|> Enum.reduce({0,0}, fn {x,y}, {twos, threes} -> | |
{x+twos, y+threes} | |
end) | |
|> Tuple.to_list() | |
|> Enum.reduce(&*/2) | |
end | |
@spec to_freq_tuple(String.t()) :: {number(), number()} | |
defp to_freq_tuple(line) do | |
import Bitwise, only: [bor: 2] | |
line | |
|> String.graphemes() | |
|> Enum.reduce(%{}, fn char, map -> | |
Map.update(map, char, 1, &(&1 + 1)) | |
end) | |
|> Enum.reduce({0, 0}, fn {_,v}, {twos, threes} -> | |
{twos,threes} | |
{bor(twos, bool_to_i(v == 2)), | |
bor(threes, bool_to_i(v == 3))} | |
end) | |
end | |
defp bool_to_i(expr), do: expr && 1 || 0 | |
def part2(stream) do | |
stream | |
|> Stream.map(&(&1 |> String.trim |> String.to_charlist())) | |
# -> [ [1,2,3,4,5,6], [2,1,2,1,2,3], ... ] | |
|> Enum.to_list() | |
|> find_lists_with_single_diff() | |
# -> {[1,2,3], [1,3,3]} | |
|> combine_common_chars() | |
end | |
def combine_common_chars({list1, list2}) do | |
Enum.zip(list1, list2) | |
|> Enum.reduce([], fn pair, acc -> | |
case pair do | |
{x,x} -> [x | acc] | |
_ -> acc | |
end | |
end) | |
|> Enum.reverse() | |
end | |
def find_lists_with_single_diff([_ | rest] = list) do | |
Enum.reduce_while( | |
list, | |
rest, | |
fn cur_list, remaining -> | |
case Enum.find(remaining, fn x -> diff_between(cur_list, x) == 1 end) do | |
nil -> | |
[_ | new_remaining] = remaining | |
{:cont, new_remaining} | |
diff -> | |
{:halt, {cur_list, diff}} | |
end | |
end | |
) | |
end | |
@doc """ | |
Given 2 lists of equal length returns the number of | |
positional differences between the lists. | |
""" | |
def diff_between(list1, list2) when is_list(list1) and is_list(list2) | |
and length(list1) == length(list2) do | |
Enum.zip(list1, list2) | |
|> Enum.reduce(0, | |
fn pair, acc -> | |
case pair do | |
{x,x} -> acc | |
_ -> acc + 1 | |
end | |
end | |
) | |
end | |
def diff_between(_, _), do: raise "diff_between must be called with 2 equal length lists" | |
end | |
# Thanks Jose for Day1 pattern to easily switch | |
# between test running and real input! | |
case System.argv() do | |
["--test"] -> | |
ExUnit.start() | |
defmodule Advent2018.Day2Test do | |
use ExUnit.Case | |
import Advent2018.Day2 | |
@testdata """ | |
abcdef | |
bababc | |
abbcde | |
abcccd | |
aabcdd | |
abcdee | |
ababab | |
""" | |
test "part1 example IDs checksum is 12" do | |
{:ok, io} = StringIO.open(@testdata) | |
stream = IO.stream(io, :line) | |
assert part1(stream) == 12 | |
end | |
test "part2 common letters" do | |
{:ok, io} = StringIO.open(""" | |
abcde | |
fghij | |
klmno | |
pqrst | |
fguij | |
axcye | |
wvxyz | |
""") | |
stream = IO.stream(io, :line) | |
assert part2(stream) == String.to_charlist("fgij") | |
end | |
test "diff_between/2 returns the number of items which differ position-by-position between 2 lists" do | |
assert diff_between([1,1], [1,1]) == 0 | |
assert diff_between([1,0], [1,1]) == 1 | |
assert diff_between([1,0], [0,1]) == 2 | |
end | |
test "diff_between/2 only works between 2 equal lists" do | |
assert_raise RuntimeError, fn -> | |
diff_between([1], [2,3]) | |
end | |
end | |
test "find_lists_with_single_diff/1 returns our matching tuple" do | |
assert find_lists_with_single_diff([ [1,2,1], [0,1,2], [0,1,3] ]) == | |
{[0,1,2], [0,1,3]} | |
end | |
end | |
["-p2 "<>filename] -> | |
filename | |
|> File.stream!([], :line) | |
|> Advent2018.Day2.part2() | |
|> IO.inspect() | |
[filename] -> | |
filename | |
|> File.stream!([], :line) | |
|> Advent2018.Day2.part1() | |
|> IO.inspect() | |
_ -> | |
IO.puts(:stderr, "must be called with --test or a filename") | |
System.halt(1) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment