change directory names
This commit is contained in:
parent
3cff0fc5cf
commit
78b8881016
90 changed files with 0 additions and 0 deletions
4
20/.formatter.exs
Normal file
4
20/.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
19
20/lib/days/day1.ex
Normal file
19
20/lib/days/day1.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule AOC.Day1 do
|
||||
def part1 do
|
||||
l = AOC.Util.input_integers(1, 1)
|
||||
[x, y] = Enum.filter(l, &((2020 - &1) in l))
|
||||
x * y
|
||||
end
|
||||
|
||||
def find_three([h | tl]) do
|
||||
case Enum.filter(tl, &((2020 - h - &1) in tl)) do
|
||||
[x, y] -> h * x * y
|
||||
_ -> find_three(tl)
|
||||
end
|
||||
end
|
||||
|
||||
def part2 do
|
||||
AOC.Util.input_integers(1, 1)
|
||||
|> find_three()
|
||||
end
|
||||
end
|
48
20/lib/days/day10.ex
Normal file
48
20/lib/days/day10.ex
Normal file
|
@ -0,0 +1,48 @@
|
|||
defmodule AOC.Day10 do
|
||||
def find_diffs(xs), do: find_diffs(xs, %{})
|
||||
def find_diffs([_], acc), do: acc
|
||||
|
||||
def find_diffs([x1, x2 | xs], acc) do
|
||||
acc = Map.update(acc, x2 - x1, 1, &(&1 + 1))
|
||||
|
||||
find_diffs([x2 | xs], acc)
|
||||
end
|
||||
|
||||
def count_walks([x | _] = xs) do
|
||||
memo =
|
||||
Enum.into(xs, %{}, &{&1, 0})
|
||||
|> Map.put(x, 1)
|
||||
|
||||
count_walks(xs, memo)
|
||||
end
|
||||
|
||||
def count_walks([x], memo), do: memo[x]
|
||||
|
||||
def count_walks([x | xs], memo) do
|
||||
memo =
|
||||
Enum.take(xs, 3)
|
||||
|> Enum.reduce(memo, fn
|
||||
next, memo when next - x <= 3 ->
|
||||
Map.update!(memo, next, &(&1 + memo[x]))
|
||||
|
||||
_, memo ->
|
||||
memo
|
||||
end)
|
||||
|
||||
count_walks(xs, memo)
|
||||
end
|
||||
|
||||
def parts do
|
||||
input =
|
||||
AOC.Util.input_integers(10, 1)
|
||||
|> Enum.sort()
|
||||
|
||||
input = [0 | input] ++ [Enum.max(input) + 3]
|
||||
|
||||
%{1 => ones, 3 => threes} = find_diffs(input)
|
||||
part1 = ones * threes
|
||||
part2 = count_walks(input)
|
||||
|
||||
{part1, part2}
|
||||
end
|
||||
end
|
91
20/lib/days/day11.ex
Normal file
91
20/lib/days/day11.ex
Normal file
|
@ -0,0 +1,91 @@
|
|||
defmodule AOC.Day11 do
|
||||
@x_size 95
|
||||
@y_size 93
|
||||
|
||||
def parse_input do
|
||||
AOC.Util.input_lines(11, 1)
|
||||
|> Enum.with_index()
|
||||
|> Enum.reduce(%{}, fn {line, y}, acc ->
|
||||
line
|
||||
|> String.split("", trim: true)
|
||||
|> Enum.with_index()
|
||||
|> Enum.reject(&(elem(&1, 0) == "."))
|
||||
|> Enum.map(fn {_, i} -> {false, i} end)
|
||||
|> Enum.into(acc, fn {c, x} -> {{x, y}, c} end)
|
||||
end)
|
||||
end
|
||||
|
||||
def count_direct_adj(seats, {x, y}) do
|
||||
[
|
||||
{x - 1, y - 1},
|
||||
{x - 1, y},
|
||||
{x - 1, y + 1},
|
||||
{x, y - 1},
|
||||
{x, y + 1},
|
||||
{x + 1, y - 1},
|
||||
{x + 1, y},
|
||||
{x + 1, y + 1}
|
||||
]
|
||||
|> Enum.count(fn coords -> Map.get(seats, coords) end)
|
||||
end
|
||||
|
||||
def look_direction(seats, coords, next_fun) do
|
||||
{x, y} = coords = next_fun.(coords)
|
||||
|
||||
if x not in 0..@x_size or y not in 0..@y_size do
|
||||
false
|
||||
else
|
||||
case Map.get(seats, coords) do
|
||||
nil -> look_direction(seats, coords, next_fun)
|
||||
x -> x
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def count_sight_adj(seats, {x, y}) do
|
||||
[
|
||||
fn {x, y} -> {x + 1, y + 1} end,
|
||||
fn {x, y} -> {x + 1, y - 1} end,
|
||||
fn {x, y} -> {x - 1, y + 1} end,
|
||||
fn {x, y} -> {x - 1, y - 1} end,
|
||||
fn {x, y} -> {x + 1, y} end,
|
||||
fn {x, y} -> {x - 1, y} end,
|
||||
fn {x, y} -> {x, y + 1} end,
|
||||
fn {x, y} -> {x, y - 1} end
|
||||
]
|
||||
|> Enum.count(&look_direction(seats, {x, y}, &1))
|
||||
end
|
||||
|
||||
def simulate_round(seats, count_adj, min) do
|
||||
Enum.reduce(seats, {%{}, false}, fn {coords, x}, {acc, changed} ->
|
||||
new =
|
||||
case {x, count_adj.(seats, coords)} do
|
||||
{false, 0} -> true
|
||||
{true, count} when count >= min -> false
|
||||
_ -> x
|
||||
end
|
||||
|
||||
{Map.put(acc, coords, new), changed or new != x}
|
||||
end)
|
||||
end
|
||||
|
||||
def find_stable(seats, count_adj, min) do
|
||||
{seats, changed} = simulate_round(seats, count_adj, min)
|
||||
|
||||
if changed do
|
||||
find_stable(seats, count_adj, min)
|
||||
else
|
||||
seats
|
||||
end
|
||||
end
|
||||
|
||||
def parts do
|
||||
input = parse_input()
|
||||
stable = find_stable(input, &count_direct_adj/2, 4)
|
||||
part1 = Enum.count(stable, &elem(&1, 1))
|
||||
stable = find_stable(input, &count_sight_adj/2, 5)
|
||||
part2 = Enum.count(stable, &elem(&1, 1))
|
||||
|
||||
{part1, part2}
|
||||
end
|
||||
end
|
93
20/lib/days/day12.ex
Normal file
93
20/lib/days/day12.ex
Normal file
|
@ -0,0 +1,93 @@
|
|||
defmodule AOC.Day12 do
|
||||
@turn_right %{
|
||||
N: :E,
|
||||
E: :S,
|
||||
S: :W,
|
||||
W: :N
|
||||
}
|
||||
|
||||
def parse_input do
|
||||
AOC.Util.input_lines(12, 1)
|
||||
|> Enum.map(fn line ->
|
||||
{action, value} = String.split_at(line, 1)
|
||||
action = String.to_atom(action)
|
||||
value = String.to_integer(value)
|
||||
|
||||
{action, value}
|
||||
end)
|
||||
end
|
||||
|
||||
def handle_instruction1({:N, value}, acc), do: Map.update!(acc, :y, &(&1 - value))
|
||||
def handle_instruction1({:E, value}, acc), do: Map.update!(acc, :x, &(&1 + value))
|
||||
def handle_instruction1({:S, value}, acc), do: Map.update!(acc, :y, &(&1 + value))
|
||||
def handle_instruction1({:W, value}, acc), do: Map.update!(acc, :x, &(&1 - value))
|
||||
|
||||
def handle_instruction1({:F, value}, %{dir: dir} = acc),
|
||||
do: handle_instruction1({dir, value}, acc)
|
||||
|
||||
def handle_instruction1({:L, value}, acc), do: handle_instruction1({:R, -value}, acc)
|
||||
|
||||
def handle_instruction1({:R, value}, %{dir: dir} = acc) do
|
||||
value = rem(div(value, 90), 4)
|
||||
|
||||
value =
|
||||
if value < 0 do
|
||||
value + 4
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
dir =
|
||||
List.duplicate(nil, value)
|
||||
|> Enum.reduce(dir, fn _, d -> Map.get(@turn_right, d) end)
|
||||
|
||||
Map.put(acc, :dir, dir)
|
||||
end
|
||||
|
||||
def handle_instruction2({:N, value}, acc), do: Map.update!(acc, :wpy, &(&1 - value))
|
||||
def handle_instruction2({:E, value}, acc), do: Map.update!(acc, :wpx, &(&1 + value))
|
||||
def handle_instruction2({:S, value}, acc), do: Map.update!(acc, :wpy, &(&1 + value))
|
||||
def handle_instruction2({:W, value}, acc), do: Map.update!(acc, :wpx, &(&1 - value))
|
||||
|
||||
def handle_instruction2({:F, value}, %{x: x, y: y, wpx: wpx, wpy: wpy} = acc),
|
||||
do: %{acc | x: x + value * wpx, y: y + value * wpy}
|
||||
|
||||
def handle_instruction2({:L, value}, acc), do: handle_instruction2({:R, -value}, acc)
|
||||
|
||||
def handle_instruction2({:R, value}, %{wpx: wpx, wpy: wpy} = acc) do
|
||||
value = rem(div(value, 90), 4)
|
||||
|
||||
value =
|
||||
if value < 0 do
|
||||
value + 4
|
||||
else
|
||||
value
|
||||
end
|
||||
|
||||
{wpx, wpy} =
|
||||
case value do
|
||||
0 -> {wpx, wpy}
|
||||
1 -> {-wpy, wpx}
|
||||
2 -> {-wpx, -wpy}
|
||||
3 -> {wpy, -wpx}
|
||||
end
|
||||
|
||||
%{acc | wpx: wpx, wpy: wpy}
|
||||
end
|
||||
|
||||
def handle_instructions(instructions, instruction_handler) do
|
||||
acc = %{x: 0, y: 0, dir: :E, wpx: 10, wpy: -1}
|
||||
Enum.reduce(instructions, acc, instruction_handler)
|
||||
end
|
||||
|
||||
def parts do
|
||||
input = parse_input()
|
||||
%{x: x, y: y} = handle_instructions(input, &handle_instruction1/2)
|
||||
part1 = abs(x) + abs(y)
|
||||
|
||||
%{x: x, y: y} = handle_instructions(input, &handle_instruction2/2)
|
||||
part2 = abs(x) + abs(y)
|
||||
|
||||
{part1, part2}
|
||||
end
|
||||
end
|
45
20/lib/days/day13.ex
Normal file
45
20/lib/days/day13.ex
Normal file
|
@ -0,0 +1,45 @@
|
|||
defmodule AOC.Day13 do
|
||||
def parse_input do
|
||||
[depart, buses] = AOC.Util.input_lines(13, 1)
|
||||
depart = String.to_integer(depart)
|
||||
|
||||
buses =
|
||||
buses
|
||||
|> String.split(",")
|
||||
|> Enum.map(fn
|
||||
bus when bus == "x" -> :x
|
||||
bus -> String.to_integer(bus)
|
||||
end)
|
||||
|
||||
{depart, buses}
|
||||
end
|
||||
|
||||
def find_earliest_bus(depart, buses), do: find_earliest_bus(depart, buses, {nil, :infinity})
|
||||
def find_earliest_bus(_, [], earliest), do: earliest
|
||||
|
||||
def find_earliest_bus(depart, [bus | tl], {_, earliest} = acc) do
|
||||
rounds = div(depart, bus)
|
||||
rounds = if rounds * bus < depart, do: rounds + 1, else: rounds
|
||||
wait = rounds * bus - depart
|
||||
|
||||
acc = if wait < earliest, do: {bus, wait}, else: acc
|
||||
|
||||
find_earliest_bus(depart, tl, acc)
|
||||
end
|
||||
|
||||
def find_offsets(buses) do
|
||||
buses
|
||||
|> Enum.with_index()
|
||||
|> Enum.reject(&(elem(&1, 0) |> is_atom()))
|
||||
end
|
||||
|
||||
def parts do
|
||||
{depart, buses} = parse_input()
|
||||
{bus, earliest} = find_earliest_bus(depart, Enum.reject(buses, &is_atom/1))
|
||||
part1 = bus * earliest
|
||||
|
||||
offsets = find_offsets(buses)
|
||||
|
||||
{part1, offsets}
|
||||
end
|
||||
end
|
36
20/lib/days/day2.ex
Normal file
36
20/lib/days/day2.ex
Normal file
|
@ -0,0 +1,36 @@
|
|||
defmodule AOC.Day2 do
|
||||
def parse_line(line) do
|
||||
[min, max, char, password] = String.split(line, [" ", "-", ":"], trim: true)
|
||||
password = String.to_charlist(password)
|
||||
[char] = String.to_charlist(char)
|
||||
min = String.to_integer(min)
|
||||
max = String.to_integer(max)
|
||||
[password, char, min, max]
|
||||
end
|
||||
|
||||
def validate_password1(password, char, min, max) do
|
||||
count = Enum.count(password, &(&1 == char))
|
||||
count in min..max
|
||||
end
|
||||
|
||||
def part1 do
|
||||
AOC.Util.input_lines(2, 1)
|
||||
|> Enum.map(&parse_line/1)
|
||||
|> Enum.map(&apply(__MODULE__, :validate_password1, &1))
|
||||
|> Enum.count(&Function.identity/1)
|
||||
end
|
||||
|
||||
def validate_password2(password, char, pos1, pos2) do
|
||||
m1 = Enum.at(password, pos1 - 1) == char
|
||||
m2 = Enum.at(password, pos2 - 1) == char
|
||||
|
||||
(m1 or m2) and not (m1 and m2)
|
||||
end
|
||||
|
||||
def part2 do
|
||||
AOC.Util.input_lines(2, 1)
|
||||
|> Enum.map(&parse_line/1)
|
||||
|> Enum.map(&apply(__MODULE__, :validate_password2, &1))
|
||||
|> Enum.count(&Function.identity/1)
|
||||
end
|
||||
end
|
33
20/lib/days/day3.ex
Normal file
33
20/lib/days/day3.ex
Normal file
|
@ -0,0 +1,33 @@
|
|||
defmodule AOC.Day3 do
|
||||
@line_length 31
|
||||
|
||||
def count_trees(lines, jump), do: count_trees(lines, jump, 0, 0)
|
||||
def count_trees([], _, _, acc), do: acc
|
||||
|
||||
def count_trees([hd | tl], jump, x, acc) do
|
||||
acc =
|
||||
acc +
|
||||
if String.at(hd, x) == "#" do
|
||||
1
|
||||
else
|
||||
0
|
||||
end
|
||||
|
||||
x = rem(x + jump, @line_length)
|
||||
count_trees(tl, jump, x, acc)
|
||||
end
|
||||
|
||||
def part1 do
|
||||
AOC.Util.input_lines(3, 1)
|
||||
|> count_trees(3)
|
||||
end
|
||||
|
||||
def part2 do
|
||||
lines = AOC.Util.input_lines(3, 1)
|
||||
|
||||
count_down2 = Enum.take_every(lines, 2) |> count_trees(1)
|
||||
tree_counts = Enum.map([1, 3, 5, 7], &count_trees(lines, &1))
|
||||
|
||||
Enum.reduce([count_down2 | tree_counts], &*/2)
|
||||
end
|
||||
end
|
82
20/lib/days/day4.ex
Normal file
82
20/lib/days/day4.ex
Normal file
|
@ -0,0 +1,82 @@
|
|||
defmodule AOC.Day4 do
|
||||
def parse_input(lines), do: parse_input(lines, [], %{})
|
||||
def parse_input([], acc, el), do: [el | acc]
|
||||
def parse_input(["" | tl], acc, el), do: parse_input(tl, [el | acc], %{})
|
||||
|
||||
def parse_input([hd | tl], acc, el) do
|
||||
el =
|
||||
String.split(hd)
|
||||
|> Enum.map(&String.split(&1, ":"))
|
||||
|> Enum.map(fn [k, v] -> {k, v} end)
|
||||
|> Enum.into(el)
|
||||
|
||||
parse_input(tl, acc, el)
|
||||
end
|
||||
|
||||
def validate_field_presence(%{
|
||||
"byr" => _,
|
||||
"iyr" => _,
|
||||
"eyr" => _,
|
||||
"hgt" => _,
|
||||
"hcl" => _,
|
||||
"ecl" => _,
|
||||
"pid" => _
|
||||
}),
|
||||
do: true
|
||||
|
||||
def validate_field_presence(_), do: false
|
||||
|
||||
def part1 do
|
||||
AOC.Util.input_lines(4, 1)
|
||||
|> parse_input()
|
||||
|> Enum.filter(&validate_field_presence/1)
|
||||
|> Enum.count()
|
||||
end
|
||||
|
||||
def validate_height(height) do
|
||||
if String.ends_with?(height, ["cm", "in"]) do
|
||||
{height, measure} = String.split_at(height, -2)
|
||||
height = String.to_integer(height)
|
||||
|
||||
case measure do
|
||||
"cm" -> height >= 150 and height <= 193
|
||||
"in" -> height >= 59 and height <= 76
|
||||
end
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def validate_fields(%{
|
||||
"byr" => byr,
|
||||
"iyr" => iyr,
|
||||
"eyr" => eyr,
|
||||
"hgt" => hgt,
|
||||
"hcl" => hcl,
|
||||
"ecl" => ecl,
|
||||
"pid" => pid
|
||||
}) do
|
||||
byr = String.to_integer(byr)
|
||||
iyr = String.to_integer(iyr)
|
||||
eyr = String.to_integer(eyr)
|
||||
|
||||
[
|
||||
byr >= 1920 and byr <= 2002,
|
||||
iyr >= 2010 and iyr <= 2020,
|
||||
eyr >= 2020 and eyr <= 2030,
|
||||
validate_height(hgt),
|
||||
hcl =~ ~r/^#([a-f]|[0-9]){6}$/,
|
||||
ecl in ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"],
|
||||
pid =~ ~r/^[0-9]{9}$/
|
||||
]
|
||||
|> Enum.reduce(&and/2)
|
||||
end
|
||||
|
||||
def part2 do
|
||||
AOC.Util.input_lines(4, 1)
|
||||
|> parse_input()
|
||||
|> Enum.filter(&validate_field_presence/1)
|
||||
|> Enum.filter(&validate_fields/1)
|
||||
|> Enum.count()
|
||||
end
|
||||
end
|
42
20/lib/days/day5.ex
Normal file
42
20/lib/days/day5.ex
Normal file
|
@ -0,0 +1,42 @@
|
|||
defmodule AOC.Day5 do
|
||||
@upper 'BR'
|
||||
@lower 'FL'
|
||||
|
||||
def search_range([], x, x), do: x
|
||||
|
||||
def search_range([hd | tl], low, high) when hd in @lower do
|
||||
search_range(tl, low, div(high + 1 + low, 2) - 1)
|
||||
end
|
||||
|
||||
def search_range([hd | tl], low, high) when hd in @upper do
|
||||
search_range(tl, div(low + high + 1, 2), high)
|
||||
end
|
||||
|
||||
def search_seat({row_search, col_search}) do
|
||||
row = search_range(row_search, 0, 127)
|
||||
column = search_range(col_search, 0, 7)
|
||||
{row, column}
|
||||
end
|
||||
|
||||
def part1 do
|
||||
AOC.Util.input_lines(5, 1)
|
||||
|> Enum.map(&String.to_charlist/1)
|
||||
|> Enum.map(&Enum.split(&1, 7))
|
||||
|> Enum.map(&search_seat/1)
|
||||
|> Enum.map(fn {r, c} -> r * 8 + c end)
|
||||
|> Enum.max()
|
||||
end
|
||||
|
||||
def find_missing_seat([l, r | _]) when r != l + 1, do: l + 1
|
||||
def find_missing_seat([_ | tl]), do: find_missing_seat(tl)
|
||||
|
||||
def part2 do
|
||||
AOC.Util.input_lines(5, 1)
|
||||
|> Enum.map(&String.to_charlist/1)
|
||||
|> Enum.map(&Enum.split(&1, 7))
|
||||
|> Enum.map(&search_seat/1)
|
||||
|> Enum.map(fn {r, c} -> r * 8 + c end)
|
||||
|> Enum.sort()
|
||||
|> find_missing_seat()
|
||||
end
|
||||
end
|
29
20/lib/days/day6.ex
Normal file
29
20/lib/days/day6.ex
Normal file
|
@ -0,0 +1,29 @@
|
|||
defmodule AOC.Day6 do
|
||||
def part1 do
|
||||
AOC.Util.input_file(6, 1)
|
||||
|> String.split("\n\n")
|
||||
|> Enum.map(fn group ->
|
||||
group
|
||||
|> String.replace("\n", "")
|
||||
|> String.to_charlist()
|
||||
|> Enum.uniq()
|
||||
|> Enum.count()
|
||||
end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def part2 do
|
||||
AOC.Util.input_file(6, 1)
|
||||
|> String.split("\n\n")
|
||||
|> Enum.map(fn group ->
|
||||
count = String.split(group, "\n") |> Enum.count()
|
||||
|
||||
group
|
||||
|> String.replace("\n", "")
|
||||
|> String.to_charlist()
|
||||
|> Enum.frequencies()
|
||||
|> Enum.count(fn {_, v} -> v == count end)
|
||||
end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
end
|
61
20/lib/days/day7.ex
Normal file
61
20/lib/days/day7.ex
Normal file
|
@ -0,0 +1,61 @@
|
|||
defmodule AOC.Day7 do
|
||||
def parse_input(lines), do: parse_input(lines, %{})
|
||||
def parse_input([], acc), do: acc
|
||||
|
||||
def parse_input([str | tl], acc) do
|
||||
[container, str] = String.split(str, " bags contain ")
|
||||
|
||||
inner =
|
||||
str
|
||||
|> String.split(", ")
|
||||
|> Enum.map(fn
|
||||
"no other bags." ->
|
||||
[]
|
||||
|
||||
bags ->
|
||||
[num, adj1, adj2, _] = String.split(bags)
|
||||
{String.to_integer(num), "#{adj1} #{adj2}"}
|
||||
end)
|
||||
|> List.flatten()
|
||||
|
||||
parse_input(tl, Map.put(acc, container, inner))
|
||||
end
|
||||
|
||||
def find_golden_containers(map) do
|
||||
map
|
||||
|> Map.keys()
|
||||
|> Enum.filter(fn
|
||||
"shiny gold" -> false
|
||||
bag -> find_golden_containers(map, bag)
|
||||
end)
|
||||
end
|
||||
|
||||
def find_golden_containers(_, "shiny gold"), do: true
|
||||
|
||||
def find_golden_containers(map, bag) do
|
||||
map[bag]
|
||||
|> Enum.map(fn {_, x} -> find_golden_containers(map, x) end)
|
||||
|> Enum.any?()
|
||||
end
|
||||
|
||||
def part1 do
|
||||
AOC.Util.input_lines(7, 1)
|
||||
|> parse_input()
|
||||
|> find_golden_containers()
|
||||
|> Enum.count()
|
||||
end
|
||||
|
||||
def count_bags(map), do: count_bags(map, "shiny gold")
|
||||
|
||||
def count_bags(map, bag) do
|
||||
map[bag]
|
||||
|> Enum.map(fn {n, x} -> n * (count_bags(map, x) + 1) end)
|
||||
|> Enum.sum()
|
||||
end
|
||||
|
||||
def part2 do
|
||||
AOC.Util.input_lines(7, 1)
|
||||
|> parse_input()
|
||||
|> count_bags()
|
||||
end
|
||||
end
|
60
20/lib/days/day8.ex
Normal file
60
20/lib/days/day8.ex
Normal file
|
@ -0,0 +1,60 @@
|
|||
defmodule AOC.Day8 do
|
||||
def get_input do
|
||||
AOC.Util.input_lines(8, 1)
|
||||
|> Enum.map(fn line ->
|
||||
[opcode, arg] = String.split(line)
|
||||
{String.to_atom(opcode), String.to_integer(arg)}
|
||||
end)
|
||||
|> Enum.with_index()
|
||||
|> Enum.into(%{}, fn {v, k} -> {k, v} end)
|
||||
end
|
||||
|
||||
def find_loop(program), do: find_loop(program, 0, MapSet.new(), 0)
|
||||
|
||||
def find_loop(program, acc, seen, ip) do
|
||||
cond do
|
||||
MapSet.member?(seen, ip) ->
|
||||
acc
|
||||
|
||||
ip >= length(Map.keys(program)) ->
|
||||
{true, acc}
|
||||
|
||||
true ->
|
||||
seen = MapSet.put(seen, ip)
|
||||
|
||||
{acc, ip} =
|
||||
case program[ip] do
|
||||
{:acc, arg} -> {acc + arg, ip + 1}
|
||||
{:nop, _} -> {acc, ip + 1}
|
||||
{:jmp, arg} -> {acc, ip + arg}
|
||||
end
|
||||
|
||||
find_loop(program, acc, seen, ip)
|
||||
end
|
||||
end
|
||||
|
||||
def part1 do
|
||||
get_input()
|
||||
|> find_loop()
|
||||
end
|
||||
|
||||
def perturb_program(program) do
|
||||
program
|
||||
|> Stream.reject(fn {_, {opcode, _}} -> opcode == :acc end)
|
||||
|> Stream.map(fn
|
||||
{ip, {:nop, arg}} -> {ip, {:jmp, arg}}
|
||||
{ip, {:jmp, arg}} -> {ip, {:nop, arg}}
|
||||
end)
|
||||
|> Stream.map(fn {ip, v} ->
|
||||
Map.put(program, ip, v)
|
||||
end)
|
||||
end
|
||||
|
||||
def part2 do
|
||||
get_input()
|
||||
|> perturb_program()
|
||||
|> Stream.map(&find_loop/1)
|
||||
|> Stream.filter(&is_tuple/1)
|
||||
|> Enum.to_list()
|
||||
end
|
||||
end
|
46
20/lib/days/day9.ex
Normal file
46
20/lib/days/day9.ex
Normal file
|
@ -0,0 +1,46 @@
|
|||
defmodule AOC.Day9 do
|
||||
@preamble_len 25
|
||||
|
||||
def find_invalid(queue, [hd | tl]) do
|
||||
sums =
|
||||
:queue.to_list(queue)
|
||||
|> Combination.combine(2)
|
||||
|> Enum.map(&Enum.sum/1)
|
||||
|
||||
queue =
|
||||
:queue.in(hd, queue)
|
||||
|> :queue.drop()
|
||||
|
||||
if hd in sums do
|
||||
find_invalid(queue, tl)
|
||||
else
|
||||
hd
|
||||
end
|
||||
end
|
||||
|
||||
def find_weakness(invalid, [_ | tl] = nums) do
|
||||
result =
|
||||
Enum.reduce_while(nums, {0, []}, fn num, {sum, l} ->
|
||||
sum = sum + num
|
||||
if sum < invalid, do: {:cont, {sum, [num | l]}}, else: {:halt, {sum, l}}
|
||||
end)
|
||||
|
||||
case result do
|
||||
{sum, l} when sum == invalid -> Enum.min_max(l)
|
||||
_ -> find_weakness(invalid, tl)
|
||||
end
|
||||
end
|
||||
|
||||
def parts do
|
||||
input = AOC.Util.input_integers(9, 1)
|
||||
{preamble, nums} = Enum.split(input, @preamble_len)
|
||||
|
||||
invalid =
|
||||
preamble
|
||||
|> :queue.from_list()
|
||||
|> find_invalid(nums)
|
||||
|
||||
{min, max} = find_weakness(invalid, input)
|
||||
{invalid, min + max}
|
||||
end
|
||||
end
|
21
20/lib/util.ex
Normal file
21
20/lib/util.ex
Normal file
|
@ -0,0 +1,21 @@
|
|||
defmodule AOC.Util do
|
||||
def input_file_name(day, input) do
|
||||
Path.join([File.cwd!(), "inputs", "day#{day}", "input#{input}.txt"])
|
||||
end
|
||||
|
||||
def input_file(day, input) do
|
||||
input_file_name(day, input)
|
||||
|> File.read!()
|
||||
end
|
||||
|
||||
def input_lines(day, input) do
|
||||
input_file_name(day, input)
|
||||
|> File.stream!()
|
||||
|> Enum.map(&String.trim/1)
|
||||
end
|
||||
|
||||
def input_integers(day, input) do
|
||||
input_lines(day, input)
|
||||
|> Enum.map(&String.to_integer/1)
|
||||
end
|
||||
end
|
27
20/mix.exs
Normal file
27
20/mix.exs
Normal file
|
@ -0,0 +1,27 @@
|
|||
defmodule Aoc2020.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
def project do
|
||||
[
|
||||
app: :aoc2020,
|
||||
version: "0.1.0",
|
||||
elixir: "~> 1.12-dev",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps()
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help compile.app" to learn about applications.
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger]
|
||||
]
|
||||
end
|
||||
|
||||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:combination, "~> 0.0.3"}
|
||||
]
|
||||
end
|
||||
end
|
3
20/mix.lock
Normal file
3
20/mix.lock
Normal file
|
@ -0,0 +1,3 @@
|
|||
%{
|
||||
"combination": {:hex, :combination, "0.0.3", "746aedca63d833293ec6e835aa1f34974868829b1486b1e1cb0685f0b2ae1f41", [:mix], [], "hexpm", "72b099f463df42ef7dc6371d250c7070b57b6c5902853f69deb894f79eda18ca"},
|
||||
}
|
1
20/test/test_helper.exs
Normal file
1
20/test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
|||
ExUnit.start()
|
Loading…
Add table
Add a link
Reference in a new issue